VOGONS


First post, by superfury

User metadata
Rank l33t++
Rank
l33t++

I'm trying to get the (I)MUL test to pass it's flags test, but for some reason it won't give the correct results.

Different MUL instructions:
80186+ IMUL:

void CPU186_OP69()
{
if (modrm_check16(&params,1,1)) return; //Abort on fault!
temp1.val32 = modrm_read16(&params,1);
temp2.val32 = immw;
modrm_decode16(&params,&info,0);
debugger_setcommand("IMUL %s,%04X",info.text,temp2);
if ((temp1.val32 &0x8000)==0x8000) temp1.val32 |= 0xFFFF0000;
if ((temp2.val32 &0x8000)==0x8000) temp2.val32 |= 0xFFFF0000;
temp3.val32 = ((temp1.val32*temp2.val32)&0xFFFFFFFF);
REG_AX = temp3.val16;
REG_DX = temp3.val16high;
if (((temp1.val32>>15)==0) || ((temp1.val32>>15)==0x1FFFF)) FLAGW_OF(0);
else FLAGW_OF(1);
FLAGW_CF(FLAG_OF); //OF=CF!
}

8086 MULB:

	case 4: //MULB
tempAL = REG_AL; //Save a backup for calculating cycles!
temp1.val32 = (uint32_t)oper1b * (uint32_t)REG_AL;
REG_AX = temp1.val16 & 0xFFFF;
if (((temp1.val16&0xFF80)==0) || ((temp1.val16&0xFF80)==0xFF80))
{
FLAGW_OF(0); //Both zeroed!
}
else FLAGW_OF(1); //Set due to overflow!

FLAGW_CF(FLAG_OF); //Same!
tempAL = FLAG_ZF; //Backup!
flag_szp8(REG_AL);
if (EMULATED_CPU==CPU_8086) //8086 only?
{
FLAGW_ZF(tempAL); //Restore Zero flag!
if (REG_AX) FLAGW_ZF(0); //8086/8088 clears the Zero flag when not zero only. Undocumented bug!
}
if (MODRM_EA(params)) //Memory?
{
CPU[activeCPU].cycles_OP = 76+MODRM_EA(params); //Mem max!
}
else //Register?
{
CPU[activeCPU].cycles_OP = 70; //Reg!
}
if (NumberOfSetBits(tempAL)>1) //More than 1 bit set?
{
CPU[activeCPU].cycles_OP += NumberOfSetBits(tempAL) - 1; //1 cycle for all bits more than 1 bit set!
}
break;

8086 IMULB:

	case 5: //IMULB
oper1 = oper1b;
temp1.val32 = REG_AL;
temp2.val32 = oper1b;
//Sign extend!
if ((temp1.val8 & 0x80) == 0x80) temp1.val32 |= 0xFFFFFF00;
if ((temp2.val8 & 0x80) == 0x80) temp2.val32 |= 0xFFFFFF00;
//Multiply and convert to 16-bit!
temp3.val32s = temp1.val32s; //Load and...
temp3.val32s *= temp2.val32s; //Multiply!
REG_AX = temp3.val16; //Load into AX!
if (((temp1.val16&0xFF80)==0) || ((temp1.val16&0xFF80)==0xFF80))
{
FLAGW_OF(0); //Both zeroed!
}
else FLAGW_OF(1); //Set due to overflow!

FLAGW_CF(FLAG_OF); //Same!
if (EMULATED_CPU==CPU_8086)
{
FLAGW_ZF(0); //Clear ZF!
}
if (MODRM_EA(params)) //Memory?
{
CPU[activeCPU].cycles_OP = 86 + MODRM_EA(params); //Mem max!
}
else //Register?
{
CPU[activeCPU].cycles_OP = 80; //Reg!
}
break;

8086 MULW:

	case 4: //MULW
tempAX = REG_AX; //Save a backup for calculating cycles!
temp1.val32 = (uint32_t)oper1 * (uint32_t)REG_AX;
REG_AX = temp1.val16;
REG_DX = temp1.val16high;
if (((temp1.val32>>15)==0) || ((temp1.val32>>15)==0x1FFFF)) FLAGW_OF(0);
else FLAGW_OF(1);
FLAGW_CF(FLAG_OF); //OF=CF!

tempAL = FLAG_ZF; //Backup!
flag_szp16(REG_AX);
if (EMULATED_CPU==CPU_8086)
{
FLAGW_ZF(tempAL); //Restore!
if ((EMULATED_CPU==CPU_8086) && temp1.val32) FLAGW_ZF(0); //8086/8088 clears the Zero flag when not zero only.
}
if (MODRM_EA(params)) //Memory?
{
CPU[activeCPU].cycles_OP = 124 + MODRM_EA(params); //Mem max!
}
else //Register?
{
CPU[activeCPU].cycles_OP = 118; //Reg!
}
if (NumberOfSetBits(tempAX)>1) //More than 1 bit set?
{
CPU[activeCPU].cycles_OP += NumberOfSetBits(tempAX) - 1; //1 cycle for all bits more than 1 bit set!
}
break;

8086 IMULW:

	case 5: //IMULW
temp1.val32 = REG_AX;
temp2.val32 = oper1;
//Sign extend!
if (temp1.val16 & 0x8000) temp1.val32 |= 0xFFFF0000;
if (temp2.val16 & 0x8000) temp2.val32 |= 0xFFFF0000;
temp3.val32s = temp1.val32s; //Load and...
temp3.val32s *= temp2.val32s; //Signed multiplication!
REG_AX = temp3.val16; //into register ax
REG_DX = temp3.val16high; //into register dx
if (((temp1.val32>>15)==0) || ((temp1.val32>>15)==0x1FFFF)) FLAGW_OF(0);
else FLAGW_OF(1);
FLAGW_CF(FLAG_OF); //OF=CF!
if (EMULATED_CPU==CPU_8086)
{
FLAGW_ZF(0); //Clear ZF!
}
if (MODRM_EA(params)) //Memory?
{
CPU[activeCPU].cycles_OP = 128 + MODRM_EA(params); //Mem max!
}
else //Register?
{
CPU[activeCPU].cycles_OP = 134; //Reg max!
}
break;

I'm getting the strangest flag errors, as can be seen in the log within the ZIP file(there's two files: debugger.log contains the disassembly, ROM_log.log contains the surrounding wrapper test results).

Filename
ROM_log_20161119_1153.zip
File size
15.19 KiB
Downloads
39 downloads
File comment
Logs of the testing MUL&arithmetic tests.
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 1 of 12, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've managed to get the invalid list smaller by rewriting some instruction checks and fixing one instruction I've missed on the 80186+ emulation:

First 80186+ IMUL instruction:

void CPU186_OP69()
{
if (modrm_check16(&params,1,1)) return; //Abort on fault!
temp1.val32 = modrm_read16(&params,1);
temp2.val32 = immw;
modrm_decode16(&params,&info,0);
debugger_setcommand("IMULW %s,%04X",info.text,temp2);
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!
REG_AX = temp3.val16;
REG_DX = temp3.val16high;
if (((temp1.val32>>15)==0) || ((temp1.val32>>15)==0x1FFFF)) FLAGW_OF(0);
else FLAGW_OF(1);
FLAGW_CF(FLAG_OF); //OF=CF!
}

Second 80186+ IMUL instruction:

void CPU186_OP6B()
{
if (modrm_check16(&params,1,1)) return; //Abort on fault!
temp1.val32 = (uint_32)modrm_read16(&params,1); //Read R/M!
temp2.val32 = (uint_32)immb; //Read unsigned parameter!
modrm_decode16(&params,&info,1); //Store the address!
if (temp1.val32&0x8000) temp1.val32 |= 0xFFFF0000;//Sign extend to 32 bits!
if (temp2.val32&0x80) temp2.val32 |= 0xFFFFFF00; //Sign extend to 32 bits!
debugger_setcommand("IMULW %s,%02X",info.text,temp2.val32&0xFF); //Command!

temp3.val32s = temp1.val32s * temp2.val32s;
modrm_write16(&params,0, temp3.val16,0); //Write to register!
if (((temp1.val32>>15)==0) || ((temp1.val32>>15)==0x1FFFF)) FLAGW_OF(0); //Overflow occurred?
else FLAGW_OF(1);
FLAGW_CF(FLAG_OF); //Same!
}

8086 (I)MULB (GRP3 8-bit Opcode):

	case 4: //MULB
tempAL = REG_AL; //Save a backup for calculating cycles!
temp1.val32 = (uint32_t)oper1b * (uint32_t)REG_AL;
REG_AX = temp1.val16 & 0xFFFF;
if ((temp1.val16&0xFF00)==0)
{
FLAGW_OF(0); //Both zeroed!
}
else FLAGW_OF(1); //Set due to overflow!

FLAGW_CF(FLAG_OF); //Same!
tempAL = FLAG_ZF; //Backup!
flag_szp8(REG_AL);
if (EMULATED_CPU==CPU_8086) //8086 only?
{
FLAGW_ZF(tempAL); //Restore Zero flag!
if (REG_AX) FLAGW_ZF(0); //8086/8088 clears the Zero flag when not zero only. Undocumented bug!
}
if (MODRM_EA(params)) //Memory?
{
CPU[activeCPU].cycles_OP = 76+MODRM_EA(params); //Mem max!
}
else //Register?
{
CPU[activeCPU].cycles_OP = 70; //Reg!
}
if (NumberOfSetBits(tempAL)>1) //More than 1 bit set?
{
CPU[activeCPU].cycles_OP += NumberOfSetBits(tempAL) - 1; //1 cycle for all bits more than 1 bit set!
}
break;

case 5: //IMULB
oper1 = oper1b;
temp1.val32 = REG_AL;
temp2.val32 = oper1b;
//Sign extend!
if ((temp1.val8 & 0x80) == 0x80) temp1.val32 |= 0xFFFFFF00;
if ((temp2.val8 & 0x80) == 0x80) temp2.val32 |= 0xFFFFFF00;
//Multiply and convert to 16-bit!
temp3.val32s = temp1.val32s; //Load and...
temp3.val32s *= temp2.val32s; //Multiply!
REG_AX = temp3.val16; //Load into AX!
if (((temp1.val16&0xFF80)==0) || ((temp1.val16&0xFF80)==0xFF80))
{
FLAGW_OF(0); //Both zeroed!
}
else FLAGW_OF(1); //Set due to overflow!

FLAGW_CF(FLAG_OF); //Same!
if (EMULATED_CPU==CPU_8086)
{
FLAGW_ZF(0); //Clear ZF!
}
if (MODRM_EA(params)) //Memory?
{
CPU[activeCPU].cycles_OP = 86 + MODRM_EA(params); //Mem max!
}
else //Register?
{
Show last 3 lines
			CPU[activeCPU].cycles_OP = 80; //Reg!
}
break;

8086+ (I)MULW (8086 GRP3 16-bit opcode):

	case 4: //MULW
tempAX = REG_AX; //Save a backup for calculating cycles!
temp1.val32 = (uint32_t)oper1 * (uint32_t)REG_AX;
REG_AX = temp1.val16;
REG_DX = temp1.val16high;
if (temp1.val16high==0) FLAGW_OF(0);
else FLAGW_OF(1);
FLAGW_CF(FLAG_OF); //OF=CF!

tempAL = FLAG_ZF; //Backup!
flag_szp16(REG_AX);
if (EMULATED_CPU==CPU_8086)
{
FLAGW_ZF(tempAL); //Restore!
if ((EMULATED_CPU==CPU_8086) && temp1.val32) FLAGW_ZF(0); //8086/8088 clears the Zero flag when not zero only.
}
if (MODRM_EA(params)) //Memory?
{
CPU[activeCPU].cycles_OP = 124 + MODRM_EA(params); //Mem max!
}
else //Register?
{
CPU[activeCPU].cycles_OP = 118; //Reg!
}
if (NumberOfSetBits(tempAX)>1) //More than 1 bit set?
{
CPU[activeCPU].cycles_OP += NumberOfSetBits(tempAX) - 1; //1 cycle for all bits more than 1 bit set!
}
break;
case 5: //IMULW
temp1.val32 = REG_AX;
temp2.val32 = oper1;
//Sign extend!
if (temp1.val16 & 0x8000) temp1.val32 |= 0xFFFF0000;
if (temp2.val16 & 0x8000) temp2.val32 |= 0xFFFF0000;
temp3.val32s = temp1.val32s; //Load and...
temp3.val32s *= temp2.val32s; //Signed multiplication!
REG_AX = temp3.val16; //into register ax
REG_DX = temp3.val16high; //into register dx
if (((temp1.val32>>15)==0) || ((temp1.val32>>15)==0x1FFFF)) FLAGW_OF(0);
else FLAGW_OF(1);
FLAGW_CF(FLAG_OF); //OF=CF!
if (EMULATED_CPU==CPU_8086)
{
FLAGW_ZF(0); //Clear ZF!
}
if (MODRM_EA(params)) //Memory?
{
CPU[activeCPU].cycles_OP = 128 + MODRM_EA(params); //Mem max!
}
else //Register?
{
CPU[activeCPU].cycles_OP = 134; //Reg max!
}
break;

The log has become somewhat smaller due to these changes.

Filename
ROM_log_20161120_1650.zip
File size
15.65 KiB
Downloads
31 downloads
File comment
Log of UniPCemu commit 2016/11/20 16:50
File license
Fair use/fair dealing exception
00:00:19:21.04394: START FLAG_OF VERIFICATION PROCEDURE!
00:00:19:22.09998: Start verifying ROM/debug/999.mul.bin!
00:00:19:71.02550: Starting verification ROM emulator...
00:00:19:98.02792: Emulator terminated OK.
00:00:19:99.00160: Verifying output...
00:00:20:00.06718: Error address: 00000090, expected: 03, in memory: 02
00:00:20:02.07564: Error address: 00000091, expected: 08, in memory: 00
00:00:20:09.05394: Error address: 00000092, expected: 46, in memory: 02
00:00:20:14.05076: Error address: 00000096, expected: 86, in memory: 02
00:00:20:15.00306: Error address: 00000098, expected: 82, in memory: 02
00:00:20:16.02850: Error address: 0000009A, expected: 83, in memory: 02
00:00:20:19.00062: Error address: 0000009B, expected: 08, in memory: 00
00:00:20:28.09922: Error address: 0000009C, expected: 87, in memory: 02
00:00:20:31.03270: Error address: 0000009D, expected: 08, in memory: 00
00:00:20:35.04660: Error address: 0000009E, expected: 46, in memory: 02
00:00:20:38.00112: Error address: 000000A2, expected: 86, in memory: 02
00:00:20:46.03968: ROM Success: 0...
00:00:20:53.02670: ROM/debug/999.mul.bin has gone wrong!

Anyone can see what's going wrong?

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

Reply 2 of 12, by superfury

User metadata
Rank l33t++
Rank
l33t++

Eventually, I managed to make it give the full correct results(no errors in the resulting flags or calculated values in memory):

8086 (I)MULB(GRP3 8-bit):

	case 4: //MULB
tempAL = REG_AL; //Save a backup for calculating cycles!
temp1.val32 = (uint32_t)oper1b * (uint32_t)REG_AL;
REG_AX = temp1.val16 & 0xFFFF;
if ((temp1.val16&0xFF00)==0)
{
FLAGW_OF(0); //Both zeroed!
}
else FLAGW_OF(1); //Set due to overflow!

FLAGW_CF(FLAG_OF); //Same!
tempAL = FLAG_ZF; //Backup!
flag_szp8(REG_AL);
if (EMULATED_CPU==CPU_8086) //8086 only?
{
FLAGW_ZF(tempAL); //Restore Zero flag!
if (REG_AX) FLAGW_ZF(0); //8086/8088 clears the Zero flag when not zero only. Undocumented bug!
}
if (MODRM_EA(params)) //Memory?
{
CPU[activeCPU].cycles_OP = 76+MODRM_EA(params); //Mem max!
}
else //Register?
{
CPU[activeCPU].cycles_OP = 70; //Reg!
}
if (NumberOfSetBits(tempAL)>1) //More than 1 bit set?
{
CPU[activeCPU].cycles_OP += NumberOfSetBits(tempAL) - 1; //1 cycle for all bits more than 1 bit set!
}
break;

case 5: //IMULB
oper1 = oper1b;
temp1.val32 = REG_AL;
temp2.val32 = oper1b;
//Sign extend!
if ((temp1.val8 & 0x80) == 0x80) temp1.val32 |= 0xFFFFFF00;
if ((temp2.val8 & 0x80) == 0x80) temp2.val32 |= 0xFFFFFF00;
//Multiply and convert to 16-bit!
temp3.val32s = temp1.val32s; //Load and...
temp3.val32s *= temp2.val32s; //Multiply!
REG_AX = temp3.val16; //Load into AX!
FLAGW_SF((temp3.val16&0x80)>>7); //Sign!
FLAGW_PF(parity[temp3.val16&0xFF]); //Parity flag!
if (((temp3.val16&0xFF80)==0) || ((temp3.val16&0xFF80)==0xFF80))
{
FLAGW_OF(0); //Both zeroed!
}
else FLAGW_OF(1); //Set due to overflow!

FLAGW_CF(FLAG_OF); //Same!
FLAGW_ZF((temp3.val16==0)?1:0); //Set the zero flag!
if (EMULATED_CPU==CPU_8086)
{
FLAGW_ZF(0); //Clear ZF!
}
if (MODRM_EA(params)) //Memory?
{
CPU[activeCPU].cycles_OP = 86 + MODRM_EA(params); //Mem max!
Show last 7 lines
		}
else //Register?
{
CPU[activeCPU].cycles_OP = 80; //Reg!
}
break;

8086 (I)MULW(GRP3 16-bit):

	case 4: //MULW
tempAX = REG_AX; //Save a backup for calculating cycles!
temp1.val32 = (uint32_t)oper1 * (uint32_t)REG_AX;
REG_AX = temp1.val16;
REG_DX = temp1.val16high;
if (temp1.val16high==0) FLAGW_OF(0);
else FLAGW_OF(1);
FLAGW_CF(FLAG_OF); //OF=CF!

tempAL = FLAG_ZF; //Backup!
flag_szp16(REG_AX);
if (EMULATED_CPU==CPU_8086)
{
FLAGW_ZF(tempAL); //Restore!
if ((EMULATED_CPU==CPU_8086) && temp1.val32) FLAGW_ZF(0); //8086/8088 clears the Zero flag when not zero only.
}
if (MODRM_EA(params)) //Memory?
{
CPU[activeCPU].cycles_OP = 124 + MODRM_EA(params); //Mem max!
}
else //Register?
{
CPU[activeCPU].cycles_OP = 118; //Reg!
}
if (NumberOfSetBits(tempAX)>1) //More than 1 bit set?
{
CPU[activeCPU].cycles_OP += NumberOfSetBits(tempAX) - 1; //1 cycle for all bits more than 1 bit set!
}
break;
case 5: //IMULW
temp1.val32 = REG_AX;
temp2.val32 = oper1;
//Sign extend!
if (temp1.val16 & 0x8000) temp1.val32 |= 0xFFFF0000;
if (temp2.val16 & 0x8000) temp2.val32 |= 0xFFFF0000;
temp3.val32s = temp1.val32s; //Load and...
temp3.val32s *= temp2.val32s; //Signed multiplication!
REG_AX = temp3.val16; //into register ax
REG_DX = temp3.val16high; //into register dx
if (((temp3.val32>>15)==0) || ((temp3.val32>>15)==0x1FFFF)) FLAGW_OF(0);
else FLAGW_OF(1);
FLAGW_CF(FLAG_OF); //OF=CF!
FLAGW_SF((temp3.val32&0x80000000)>>31); //Sign!
FLAGW_PF(parity[temp3.val32&0xFF]); //Parity flag!
FLAGW_ZF((temp3.val32==0)?1:0); //Set the zero flag!
if (EMULATED_CPU==CPU_8086)
{
FLAGW_ZF(0); //Clear ZF!
}
if (MODRM_EA(params)) //Memory?
{
CPU[activeCPU].cycles_OP = 128 + MODRM_EA(params); //Mem max!
}
else //Register?
{
CPU[activeCPU].cycles_OP = 134; //Reg max!
}
break;

80186 IMULW:

void CPU186_OP69()
{
if (modrm_check16(&params,1,1)) return; //Abort on fault!
temp1.val32 = modrm_read16(&params,1);
temp2.val32 = immw;
modrm_decode16(&params,&info,0);
debugger_setcommand("IMULW %s,%04X",info.text,temp2);
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!
REG_AX = temp3.val16;
REG_DX = temp3.val16high;
if (((temp3.val32>>15)==0) || ((temp3.val32>>15)==0x1FFFF)) FLAGW_OF(0);
else FLAGW_OF(1);
FLAGW_CF(FLAG_OF); //OF=CF!
FLAGW_SF((temp3.val32&0x80000000)>>31); //Sign!
FLAGW_PF(parity[temp3.val32&0xFF]); //Parity flag!
FLAGW_ZF((temp3.val32==0)?1:0); //Set the zero flag!
}

void CPU186_OP6B()
{
if (modrm_check16(&params,1,1)) return; //Abort on fault!
temp1.val32 = (uint_32)modrm_read16(&params,1); //Read R/M!
temp2.val32 = (uint_32)immb; //Read unsigned parameter!
modrm_decode16(&params,&info,1); //Store the address!
if (temp1.val32&0x8000) temp1.val32 |= 0xFFFF0000;//Sign extend to 32 bits!
if (temp2.val32&0x80) temp2.val32 |= 0xFFFFFF00; //Sign extend to 32 bits!
debugger_setcommand("IMULW %s,%02X",info.text,temp2.val32&0xFF); //Command!

temp3.val32s = temp1.val32s * temp2.val32s;
modrm_write16(&params,0, temp3.val16,0); //Write to register!
if (((temp3.val32>>7)==0) || ((temp3.val32>>7)==0x1FFFFFF)) FLAGW_OF(0); //Overflow occurred?
else FLAGW_OF(1);
FLAGW_CF(FLAG_OF); //Same!
FLAGW_SF((temp3.val16&0x8000)>>31); //Sign!
FLAGW_PF(parity[temp3.val32&0xFF]); //Parity flag!
FLAGW_ZF((temp3.val16==0)?1:0); //Set the zero flag!
}

Anyone can verify this functionality?

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

Reply 3 of 12, by vladstamate

User metadata
Rank Oldbie
Rank
Oldbie

How did you get this ROM that tests different instructions? I am interested in something like this too.

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 4 of 12, by superfury

User metadata
Rank l33t++
Rank
l33t++

I simply use the ROMs that are posted on a topic on the fake86 emulator.

It's the EPC 186 testsuite in this topic:
http://forum.osdev.org/viewtopic.php?f=13&t=23739&start=15
By Artlav.

Artlav wrote:
Nice work. […]
Show full quote

Nice work.

If you're interested, here is my collection of testsuites for EPC's 80186:
http://orbides.1gb.ru/80186_tests.zip
res_*.bin contains the results expected at the offset 0x0 when you run *.bin test placed as a BIOS.

Although, if you have win 3.0 booting these are a little late.

About the timing - do you want to get precise real-time experience or something?
You won't get away from CPU overuse without many tricks. There is nothing better then scheduling to free the CPU (like sleep(1);), but it's hard to predict the duration.
Best results will be with rdtsc and active waiting, but you're still subjected to task switching.
Why bother?

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

Reply 5 of 12, by superfury

User metadata
Rank l33t++
Rank
l33t++

There's also something strange about the 80186+ IMUL instructions(Opcodes 6B&69). The documentation says it uses two different forms(2-operand and 3-operand forms), but I can't find any indication which one is used when? Does this depend on the Mod bits of the ModR/M byte? So register(11b) is mapped to the two-operand version, while memory operands (00b, 01b, 10b) are mapped to the three-operand version? How does the CPU know which one it is?

Looking at https://courses.engr.illinois.edu/ece390/reso … es/opcodes.html seems to imply that the mod 3 version indeed only uses two parameters (RM as register and immediate byte/word), while the other variant uses the RM, register and immediate word?

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

Reply 6 of 12, by superfury

User metadata
Rank l33t++
Rank
l33t++

This is what I've made of the information said at http://x86.renejeschke.de/html/file_module_x86_id_138.html :

void CPU186_OP69()
{
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!
temp1.val32 = modrm_read16(&params,1); //Read R/M!
}
else
{
temp1.val32 = (uint_32)modrm_read16(&params,0); //Read reg instead! Word register = Word register * imm16!
}
temp2.val32 = immw; //Immediate word is second/third parameter!
modrm_decode16(&params,&info,0); //Reg!
modrm_decode16(&params,&info2,1); //Second parameter(R/M)!
if (MODRM_MOD(params.modrm)==3) //Two-operand version?
{
debugger_setcommand("IMULW %s,%04X",info.text,immw); //IMUL reg,imm16
}
else //Three-operand version?
{
debugger_setcommand("IMULW %s,%s,%04X",info.text,info2.text,immw); //IMUL reg,r/m16,imm16
}
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!
modrm_write16(&params,0,temp3.val16,0); //Write to the destination(register)!
if (((temp3.val32>>15)==0) || ((temp3.val32>>15)==0x1FFFF)) FLAGW_OF(0);
else FLAGW_OF(1);
FLAGW_CF(FLAG_OF); //OF=CF!
FLAGW_SF((temp3.val32&0x80000000)>>31); //Sign!
FLAGW_PF(parity[temp3.val32&0xFF]); //Parity flag!
FLAGW_ZF((temp3.val32==0)?1:0); //Set the zero flag!
}

void CPU186_OP6B()
{
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!
temp1.val32 = modrm_read16(&params,1); //Read R/M!
}
else
{
temp1.val32 = (uint_32)modrm_read16(&params,0); //Read reg instead! Word register = Word register * imm8 sign extended!
}
temp2.val32 = (uint_32)immb; //Read unsigned parameter!
modrm_decode16(&params,&info,0); //Store the address!
modrm_decode16(&params,&info2,1); //Store the address(R/M)!
if (MODRM_MOD(params.modrm)==3) //Two-operand version?
{
debugger_setcommand("IMULW %s,%02X",info.text,immb); //IMUL reg,imm8
}
else //Three-operand version?
{
debugger_setcommand("IMULW %s,%s,%02X",info.text,info2.text,immb); //IMUL reg,r/m16,imm8
}

if (temp1.val32&0x8000) temp1.val32 |= 0xFFFF0000;//Sign extend to 32 bits!
if (temp2.val32&0x80) temp2.val32 |= 0xFFFFFF00; //Sign extend to 32 bits!
Show last 9 lines
	temp3.val32s = temp1.val32s * temp2.val32s;
modrm_write16(&params,0,temp3.val16,0); //Write to register!
if (((temp3.val32>>7)==0) || ((temp3.val32>>7)==0x1FFFFFF)) FLAGW_OF(0); //Overflow occurred?
else FLAGW_OF(1);
FLAGW_CF(FLAG_OF); //Same!
FLAGW_SF((temp3.val16&0x8000)>>31); //Sign!
FLAGW_PF(parity[temp3.val32&0xFF]); //Parity flag!
FLAGW_ZF((temp3.val16==0)?1:0); //Set the zero flag!
}

Anyone can confirm this behaviour on a 186+ CPU?

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

Reply 7 of 12, by vladstamate

User metadata
Rank Oldbie
Rank
Oldbie

Oh those tests are great!! Thank you for the link!

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 8 of 12, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've just tested Wolfenstein 3D's wolf3d.exe(instead of the 8088 wolf8088.exe). It runs without problems (the 8088 version messes up graphics a bit, mainly menus and surrounding game areas). It only gives a red screen and hangs when I'm trying to quit Wolfenstein 3D back to the MS-DOS prompt. Is this correct behaviour on the 80286? (It's probably the demo version of the game, as trying to select other levels than level 1 gives me the message to buy the game?)

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

Reply 9 of 12, by peterferrie

User metadata
Rank Oldbie
Rank
Oldbie

The "two-operand" version is when one of the registers is AX. Then it looks like "IMUL CX, val" when it's really "IMUL AX, CX, val".
It's a disassembler artifact, not an actual separate CPU instruction.

Reply 10 of 12, by superfury

User metadata
Rank l33t++
Rank
l33t++

So the instruction is always IMUL reg,modr/m,val, which is essentially "int reg=modr/m*signextend(val);" in C/C++? That two-operand version doesn't need to be implemented, just the three-operand version?

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

Reply 11 of 12, by vladstamate

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote:

I've just tested Wolfenstein 3D's wolf3d.exe(instead of the 8088 wolf8088.exe). It runs without problems (the 8088 version messes up graphics a bit, mainly menus and surrounding game areas). It only gives a red screen and hangs when I'm trying to quit Wolfenstein 3D back to the MS-DOS prompt. Is this correct behaviour on the 80286? (It's probably the demo version of the game, as trying to select other levels than level 1 gives me the message to buy the game?)

I doubt that is the correct behaviour. Is is possible that you are not hanged, but rather that you are not out of VGA graphics mode? In other words the command prompt could be alive and running just the display is wrong? You can type a command like "dir" and see if INT 13H gets called.

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 12 of 12, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've done what you said (typing DIR and pressing enter during the red screen). I then turned on the logging of the executed commands and notice that the hard disk BIOS (which resides at segment C600h(NO this isn't the VGA BIOS, since the VGA BIOS ROM is 24kB in size, so the hard disk BIOS is at C600:0000)). I see it's in the hard disk BIOS(AT ROM of the latest XT-IDE BIOS), reading port 7 and waiting for bit 7 to set(which doesn't seem to happen for some reason)? This is the DMA channel 3 counter? The hard disk BIOS shouldn't use ISA DMA, since it uses PCI Bus Mastering DMA(UDMA?) of the hard disk instead? The hard disk emulation is supposed to support polling mode only?

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