VOGONS


test386.asm CPU tester

Topic actions

Reply 40 of 178, by hottobar

User metadata
Rank Newbie
Rank
Newbie
superfury wrote:

The list file is unchanging, though(only if source code is changed in the testsuite, which is only the base constants and the additional logging code):

test386.lst.zip

The #DE handler moves a new EIP to the stack (0010:C20C), so that the iretd at 0010:C213 can return to a ret, instead of the div that caused the #DE (see the comment in the source asm). The CPU then correctly executes a ret (0010:C214) but, instead of returning to 0010:2941, it jumps to 0010:0000.
Maybe a wrongly sized stack pop?

Reply 41 of 178, by superfury

User metadata
Rank l33t++
Rank
l33t++

You say a mov to [ESP], but according to https://pdos.csail.mit.edu/6.828/2016/reading … i386/s17_02.htm that doesn't exist? My emulator also uses 0 instead of ESP when addressed that way(base only, index still uses esp). Look at https://bitbucket.org/superfury/unipcemu/src/ … drm.c?at=master function modrm_SIB_reg for the 32-bit modr/m decoding of addresses(base=0 for index, 1 for base).

Edit: A wrongly sized stack pop occurred to me too, but the iret returns correctly and a 32-bit ret should pop and increase 32-bits afaik(esp+4, 32-bits having been read)? Also, earlier call/ret instructions worked?

And, the address you mentioned(0010:C20C isn't triggered by my breakpoint, so it's not ever reached). So maybe an earlier error?

Edit: I see one being raised at 0010:2BFC. It raises a div0 fault, no error code. CS, EIP and EFLAGS are pushed as dwords(Esp-0xC).

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 42 of 178, by vladstamate

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote:

You say a mov to [ESP], but according to https://pdos.csail.mit.edu/6.828/2016/reading … i386/s17_02.htm that doesn't exist? My emulator also uses 0 instead of ESP when addressed that way(base only, index still uses esp). Look at https://bitbucket.org/superfury/unipcemu/src/ … drm.c?at=master function modrm_SIB_reg for the 32-bit modr/m decoding of addresses(base=0 for index, 1 for base).

I found this that explains slightly better the ESP case. You can ignore the 64bit stuff.

YouTube channel: https://www.youtube.com/channel/UC7HbC_nq8t1S9l7qGYL0mTA
Collection: http://www.digiloguemuseum.com/index.html
Emulator: https://sites.google.com/site/capex86/
Raytracer: https://sites.google.com/site/opaqueraytracer/

Reply 43 of 178, by superfury

User metadata
Rank l33t++
Rank
l33t++

My bad, the scaled index on ESP uses displacement only(the base handles the base only). The base works normally, thus [ESP] is valid after all.

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 44 of 178, by hottobar

User metadata
Rank Newbie
Rank
Newbie
peterferrie wrote:

I opened an issue rather than forking the code because I haven't spent the time yet to understand how to add new tests by myself.
I hope that's acceptable.

I've updated test386.asm with a BCD undefined flags test (for the 80386 only though).
I've also validated the flags values against a 386SX hardware.

Reply 45 of 178, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've just tried running the current version(with my own patch which is posted at https://github.com/barotto/test386.asm/issues/2 for logging in UniPCemu(and Bochs)).

There are differences in the IMUL8 W section all the way to the DIVDL B section, which seems to still crash somehow before logging anything?

Filename
porte9.log
File size
1.51 MiB
Downloads
54 downloads
File comment
Full port E9 log of UniPCemu running test386.asm.
File license
Fair use/fair dealing exception

There's also one problem at:

das EAX=12340503 PS=0010 EAX=123405FD PS=0091 

This is in my emulator:

das EAX=12340503 PS=0010 EAX=123405FD PS=0090 

The only thing differing is that final PS value there? What does that mean? A wrong carry flag?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 46 of 178, by superfury

User metadata
Rank l33t++
Rank
l33t++

This is the code DAS executes:

byte CPU8086_internal_DAS()
{
INLINEREGISTER byte tempCF, tempAL;
INLINEREGISTER word bigAL;
bigAL = (word)(tempAL = REG_AL);
tempCF = FLAG_CF; //Save old values!
CPUPROT1
if (((bigAL&0xF)>9) || FLAG_AF)
{
oper1 = bigAL = REG_AL-6;
REG_AL = oper1&255;
FLAGW_CF(tempCF|((oper1&0xFF00)>0));
FLAGW_AF(1);
}
else FLAGW_AF(0);

if ((tempAL>0x99) || tempCF)
{
bigAL -= 0x60;
REG_AL = (byte)(bigAL&0xFF);
FLAGW_CF(1);
}
else
{
FLAGW_CF(0);
}
flag_szp8(REG_AL);
//if (bigAL&0xFF00) FLAGW_OF(1); else FLAGW_OF(0); //Undocumented: Overflow flag!
CPUPROT2
if (CPU_apply286cycles()==0) //No 80286+ cycles instead?
{
CPU[activeCPU].cycles_OP += 4; //Timings!
}
return 0;
}

That behaviour should be correct afaik?

Edit: Just looked at vladstamates emulation of the instruction: it should have the same result( https://vstamate@bitbucket.org/vstamate/cape- … U.cpp?at=master )? So why does the CPU set the carry flag anyway?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 48 of 178, by vladstamate

User metadata
Rank Oldbie
Rank
Oldbie

Why is that the bug? According to this we should clear the CF: http://css.csail.mit.edu/6.858/2014/readings/i386/DAS.htm

YouTube channel: https://www.youtube.com/channel/UC7HbC_nq8t1S9l7qGYL0mTA
Collection: http://www.digiloguemuseum.com/index.html
Emulator: https://sites.google.com/site/capex86/
Raytracer: https://sites.google.com/site/opaqueraytracer/

Reply 49 of 178, by hottobar

User metadata
Rank Newbie
Rank
Newbie

The old Intel dev manuals are wrong.
The correct algorithm is reported in the current IA-32 Software Dev's Manual (Volume 2A).

old_AL ← AL;
old_CF ← CF;
CF ← 0;
IF (((AL AND 0FH) > 9) or AF = 1)
THEN
AL ← AL - 6;
CF ← old_CF or (Borrow from AL ← AL − 6);
AF ← 1;
ELSE
AF ← 0;
FI;
IF ((old_AL > 99H) or (old_CF = 1))
THEN
AL ← AL − 60H;
CF ← 1;
FI;

Don't blindly trust old docs.
BTW, I've verified the results on a real 386.

Reply 50 of 178, by vladstamate

User metadata
Rank Oldbie
Rank
Oldbie

Oh nice. Thank you for the correction!

YouTube channel: https://www.youtube.com/channel/UC7HbC_nq8t1S9l7qGYL0mTA
Collection: http://www.digiloguemuseum.com/index.html
Emulator: https://sites.google.com/site/capex86/
Raytracer: https://sites.google.com/site/opaqueraytracer/

Reply 51 of 178, by superfury

User metadata
Rank l33t++
Rank
l33t++

There's something I notice about the test386.asm repository: it does seem to have a EE file(test386-EE-reference) for normal logs, but not another one for the logs for the undefined flags as well(the TEST_UNDEF enabled test)?

Also, looking at the normal log(the undefined logs HLTs when enabled?), by examining the softdebugger unit in UniPCemu, I see that it was last writing "#DE " block before crashing, so it was actually triggered and written to port E9. Maybe returning from that point crashes it somehow?

Edit: The bugs up until IMUL8 seems to have been fixed now. The IMUL8 instructions and onwardsI(all remaining IMUL instructions) seems to have problems with it's output, as well as input flags(PS)?

This is my current implementation of those IMUL instructions:

16-bit:

void CPU186_OP69()
{
memcpy(&info,&params.info[MODRM_src0],sizeof(info)); //Reg!
memcpy(&info2,&params.info[MODRM_src1],sizeof(info2)); //Second parameter(R/M)!
if (MODRM_MOD(params.modrm)==3) //Two-operand version?
{
debugger_setcommand("IMUL %s,%04X",info.text,immw); //IMUL reg,imm16
}
else //Three-operand version?
{
debugger_setcommand("IMUL %s,%s,%04X",info.text,info2.text,immw); //IMUL reg,r/m16,imm16
}
if (CPU[activeCPU].instructionstep==0) //First step?
{
if (MODRM_MOD(params.modrm)!=3) //Use R/M to calculate the result(Three-operand version)?
{
if (modrm_check16(&params,1,1)) return; //Abort on fault!
if (CPU8086_instructionstepreadmodrmw(0,&temp1.val16,1)) return; //Read R/M!
temp1.val16high = 0; //Clear high part by default!
}
else
{
if (CPU8086_instructionstepreadmodrmw(0,&temp1.val16,0)) return; //Read reg instead! Word register = Word register * imm16!
temp1.val16high = 0; //Clear high part by default!
}
++CPU[activeCPU].instructionstep; //Next step!
}
if (CPU[activeCPU].instructionstep==1) //Second step?
{
temp2.val32 = immw; //Immediate word is second/third parameter!
if ((temp1.val32 &0x8000)==0x8000) temp1.val32 |= 0xFFFF0000;
if ((temp2.val32 &0x8000)==0x8000) temp2.val32 |= 0xFFFF0000;
temp3.val32s = temp1.val32s; //Load and...
temp3.val32s *= temp2.val32s; //Signed multiplication!
CPU_apply286cycles(); //Apply the 80286+ cycles!
//We're writing to the register always, so no normal writeback!
++CPU[activeCPU].instructionstep; //Next step!
}
modrm_write16(&params,MODRM_src0,temp3.val16,0); //Write to the destination(register)!
if (((temp3.val32>>15)==0) || ((temp3.val32>>15)==0x1FFFF)) FLAGW_OF(0); //Overflow flag is cleared when high word is a sign extension of the low word!
else FLAGW_OF(1);
FLAGW_CF(FLAG_OF); //OF=CF!
FLAGW_SF((temp3.val16&0x8000)>>15); //Sign!
FLAGW_PF(parity[temp3.val16&0xFF]); //Parity flag!
FLAGW_ZF((temp3.val16==0)?1:0); //Set the zero flag!
}

void CPU186_OP6B()
{
memcpy(&info,&params.info[MODRM_src0],sizeof(info)); //Reg!
memcpy(&info2,&params.info[MODRM_src1],sizeof(info2)); //Second parameter(R/M)!
if (MODRM_MOD(params.modrm)==3) //Two-operand version?
{
debugger_setcommand("IMUL %s,%02X",info.text,immb); //IMUL reg,imm8
}
else //Three-operand version?
{
debugger_setcommand("IMUL %s,%s,%02X",info.text,info2.text,immb); //IMUL reg,r/m16,imm8
}

Show last 35 lines
	if (CPU[activeCPU].instructionstep==0) //First step?
{
if (MODRM_MOD(params.modrm)!=3) //Use R/M to calculate the result(Three-operand version)?
{
if (modrm_check16(&params,1,1)) return; //Abort on fault!
if (CPU8086_instructionstepreadmodrmw(0,&temp1.val16,MODRM_src1)) return; //Read R/M!
temp1.val16high = 0; //Clear high part by default!
}
else
{
if (CPU8086_instructionstepreadmodrmw(0,&temp1.val16,MODRM_src0)) return; //Read reg instead! Word register = Word register * imm16!
temp1.val16high = 0; //Clear high part by default!
}
++CPU[activeCPU].instructionstep; //Next step!
}
if (CPU[activeCPU].instructionstep==1) //Second step?
{
temp2.val32 = (uint_32)immb; //Read unsigned parameter!

if (temp1.val32&0x8000) temp1.val32 |= 0xFFFF0000;//Sign extend to 32 bits!
if (temp2.val32&0x80) temp2.val32 |= 0xFFFFFF00; //Sign extend to 32 bits!
temp3.val32s = temp1.val32s * temp2.val32s;
CPU_apply286cycles(); //Apply the 80286+ cycles!
//We're writing to the register always, so no normal writeback!
++CPU[activeCPU].instructionstep; //Next step!
}

modrm_write16(&params,MODRM_src0,temp3.val16,0); //Write to register!
if (((temp3.val32>>7)==0) || ((temp3.val32>>7)==0x1FFFFFF)) FLAGW_OF(0); //Overflow is cleared when the high byte is a sign extension of the low byte?
else FLAGW_OF(1);
FLAGW_CF(FLAG_OF); //Same!
FLAGW_SF((temp3.val16&0x8000)>>15); //Sign!
FLAGW_PF(parity[temp3.val16&0xFF]); //Parity flag!
FLAGW_ZF((temp3.val16==0)?1:0); //Set the zero flag!
}

32-bit:

void CPU386_OP69()
{
memcpy(&info,&params.info[MODRM_src0],sizeof(info)); //Reg!
memcpy(&info2,&params.info[MODRM_src1],sizeof(info2)); //Second parameter(R/M)!
if (MODRM_MOD(params.modrm)==3) //Two-operand version?
{
debugger_setcommand("IMUL %s,%08X",info.text,imm32); //IMUL reg,imm32
}
else //Three-operand version?
{
debugger_setcommand("IMUL %s,%s,%08X",info.text,info2.text,imm32); //IMUL reg,r/m32,imm32
}
if (CPU[activeCPU].instructionstep==0) //First step?
{
if (MODRM_MOD(params.modrm)!=3) //Use R/M to calculate the result(Three-operand version)?
{
if (modrm_check32(&params,1,1)) return; //Abort on fault!
if (CPU80386_instructionstepreadmodrmdw(0,&temp1.val32,1)) return; //Read R/M!
temp1.val32high = 0; //Clear high part by default!
}
else
{
if (CPU80386_instructionstepreadmodrmdw(0,&temp1.val32,0)) return; //Read reg instead! Word register = Word register * imm32!
temp1.val32high = 0; //Clear high part by default!
}
++CPU[activeCPU].instructionstep; //Next step!
}
if (CPU[activeCPU].instructionstep==1) //Second step?
{
temp2.val64 = imm32; //Immediate word is second/third parameter!
if ((temp1.val64 &0x80000000ULL)==0x80000000ULL) temp1.val64 |= 0xFFFFFFFF00000000ULL;
if ((temp2.val64 &0x80000000ULL)==0x80000000ULL) temp2.val64 |= 0xFFFFFFFF00000000ULL;
temp3.val64s = temp1.val64s; //Load and...
temp3.val64s *= temp2.val64s; //Signed multiplication!
CPU_apply286cycles(); //Apply the 80286+ cycles!
//We're writing to the register always, so no normal writeback!
++CPU[activeCPU].instructionstep; //Next step!
}
modrm_write32(&params,MODRM_src0,temp3.val32); //Write to the destination(register)!
if (((temp3.val64>>31)==0ULL) || ((temp3.val64>>31)==0x1FFFFFFFFULL)) FLAGW_OF(0); //Overflow flag is cleared when high word is a sign extension of the low word!
else FLAGW_OF(1);
FLAGW_CF(FLAG_OF); //OF=CF!
FLAGW_SF((temp3.val32&0x80000000U)>>31); //Sign!
FLAGW_PF(parity[temp3.val32&0xFF]); //Parity flag!
FLAGW_ZF((temp3.val32==0)?1:0); //Set the zero flag!
}

void CPU386_OP6B()
{
memcpy(&info,&params.info[MODRM_src0],sizeof(info)); //Reg!
memcpy(&info2,&params.info[MODRM_src1],sizeof(info2)); //Second parameter(R/M)!
if (MODRM_MOD(params.modrm)==3) //Two-operand version?
{
debugger_setcommand("IMUL %s,%02X",info.text,immb); //IMUL reg,imm8
}
else //Three-operand version?
{
debugger_setcommand("IMUL %s,%s,%02X",info.text,info2.text,immb); //IMUL reg,r/m32,imm8
}

Show last 35 lines
	if (CPU[activeCPU].instructionstep==0) //First step?
{
if (MODRM_MOD(params.modrm)!=3) //Use R/M to calculate the result(Three-operand version)?
{
if (modrm_check32(&params,1,1)) return; //Abort on fault!
if (CPU80386_instructionstepreadmodrmdw(0,&temp1.val32,MODRM_src1)) return; //Read R/M!
temp1.val32high = 0; //Clear high part by default!
}
else
{
if (CPU80386_instructionstepreadmodrmdw(0,&temp1.val32,MODRM_src0)) return; //Read reg instead! Word register = Word register * imm32!
temp1.val32high = 0; //Clear high part by default!
}
++CPU[activeCPU].instructionstep; //Next step!
}
if (CPU[activeCPU].instructionstep==1) //Second step?
{
temp2.val64 = (uint_64)immb; //Read unsigned parameter!

if (temp1.val64&0x80000000ULL) temp1.val64 |= 0xFFFFFFFF00000000ULL;//Sign extend to 64 bits!
if (temp2.val64&0x80ULL) temp2.val64 |= 0xFFFFFFFFFFFFFF00ULL; //Sign extend to 64 bits!
temp3.val64s = temp1.val64s * temp2.val64s;
CPU_apply286cycles(); //Apply the 80286+ cycles!
//We're writing to the register always, so no normal writeback!
++CPU[activeCPU].instructionstep; //Next step!
}

modrm_write32(&params,MODRM_src0,temp3.val32); //Write to register!
if (((temp3.val64>>15)==0ULL) || ((temp3.val64>>15)==0x1FFFFFFFFFFFFFFULL)) FLAGW_OF(0); //Overflow is cleared when the high byte is a sign extension of the low byte?
else FLAGW_OF(1);
FLAGW_CF(FLAG_OF); //Same!
FLAGW_SF((temp3.val32&0x80000000U)>>31); //Sign!
FLAGW_PF(parity[temp3.val32&0xFF]); //Parity flag!
FLAGW_ZF((temp3.val32==0)?1:0); //Set the zero flag!
}

Can you see what's going wrong there?

Attachments

  • Filename
    porte9.log
    File size
    1.51 MiB
    Downloads
    50 downloads
    File comment
    test386-EE output.
    File license
    Fair use/fair dealing exception

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 52 of 178, by hottobar

User metadata
Rank Newbie
Rank
Newbie
superfury wrote:

There's something I notice about the test386.asm repository: it does seem to have a EE file(test386-EE-reference) for normal logs, but not another one for the logs for the undefined flags as well(the TEST_UNDEF enabled test)?

Test 0xE0 (undefined behaviours) depends on the CPU family. I didn't want to create a ref file for each family, with only the initial 10 lines or so which are really different. I can't create an additional "log file" either, because it's not a file, it's a byte write to an output port.
So 0xE0 halts in case of error.

Reply 53 of 178, by superfury

User metadata
Rank l33t++
Rank
l33t++

I'm just asking to be sure: to the CPU, does a 2-operand opcode 69/6B(IMUL r16,imm8/16) even exist? Or does it always decode to 3 operands, with r/m and immediate being multiplied and stored into the reg operand?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 54 of 178, by hottobar

User metadata
Rank Newbie
Rank
Newbie
superfury wrote:

I'm just asking to be sure: to the CPU, does a 2-operand opcode 69/6B(IMUL r16,imm8/16) even exist? Or does it always decode to 3 operands, with r/m and immediate being multiplied and stored into the reg operand?

6B/69 are 3-operand only:

6B /r ib    IMUL r16, r/m16, imm8     word register ← r/m16 ∗ sign-extended immediate byte.
6B /r ib IMUL r32, r/m32, imm8 doubleword register ← r/m32 ∗ sign-extended immediate byte.
69 /r iw IMUL r16, r/m16, imm16 word register ← r/m16 ∗ immediate word.
69 /r id IMUL r32, r/m32, imm32 doubleword register ← r/m32 ∗ immediate doubleword.

Reply 55 of 178, by vladstamate

User metadata
Rank Oldbie
Rank
Oldbie

Man, the encoding for that is crazy.

IMUL EAX, [0x12345678], 0x12345678

encodes to something like this:

69 XX 78 56 34 12 78 56 34 12

10 bytes !! Plus 1 if you have a prefix (like 66). I can see why the 386 prefetch queue was made 12 bytes.

YouTube channel: https://www.youtube.com/channel/UC7HbC_nq8t1S9l7qGYL0mTA
Collection: http://www.digiloguemuseum.com/index.html
Emulator: https://sites.google.com/site/capex86/
Raytracer: https://sites.google.com/site/opaqueraytracer/

Reply 57 of 178, by superfury

User metadata
Rank l33t++
Rank
l33t++

@vladstamate: Add another one with SIB disp32 variant for the full 12 byte instruction length? Or another one for 32-bit 66h 0Fh OP modrm SIB disp32 imm32? Isn't that more than 12(13)? Although only newer CPUs have such 0F opcodes?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 58 of 178, by vladstamate

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote:

@vladstamate: Add another one with SIB disp32 variant for the full 12 byte instruction length? Or another one for 32-bit 66h 0Fh OP modrm SIB disp32 imm32? Isn't that more than 12(13)? Although only newer CPUs have such 0F opcodes?

Yes true. 12 with SIB. 386 does not have 0F IMULs AFAIK.

66 69 XX XX 78 56 34 12 78 56 34 12

12 bytes of instruction.

YouTube channel: https://www.youtube.com/channel/UC7HbC_nq8t1S9l7qGYL0mTA
Collection: http://www.digiloguemuseum.com/index.html
Emulator: https://sites.google.com/site/capex86/
Raytracer: https://sites.google.com/site/opaqueraytracer/