VOGONS


test386.asm CPU tester

Topic actions

Reply 80 of 178, by superfury

User metadata
Rank l33t++
Rank
l33t++

This is what I've gotten so far:

16-bit:

byte op_grp2_8(byte cnt, byte varshift) {
//word d,
INLINEREGISTER word s, shift, tempCF, msb;
INLINEREGISTER byte numcnt;
//word backup;
//if (cnt>0x8) return(oper1b); //NEC V20/V30+ limits shift count
numcnt = cnt; //Save count!
s = oper1b;
switch (thereg) {
case 0: //ROL r/m8
if (EMULATED_CPU>=CPU_80386) numcnt &= 7; //Operand size wrap!
else if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
for (shift = 1; shift <= numcnt; shift++) {
tempCF = ((s&0x80)>>7); //Save MSB!
s = (s << 1)|tempCF;
}
FLAGW_CF(s&1); //Set carry flag!
if (numcnt==1) FLAGW_OF(((s >> 7) & 1)^FLAG_CF);
break;

case 1: //ROR r/m8
if (EMULATED_CPU>=CPU_80386) numcnt &= 7; //Operand size wrap!
else if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
for (shift = 1; shift <= numcnt; shift++) {
tempCF = (s&1); //Save LSB!
s = (s >> 1) | (tempCF << 7);
}
FLAGW_CF(((s&0x80)>>7)); //Set carry flag!
if (numcnt==1) FLAGW_OF((s >> 7) ^ ((s >> 6) & 1));
break;

case 2: //RCL r/m8
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (EMULATED_CPU>=CPU_80386) numcnt %= 9; //Operand size wrap!
for (shift = 1; shift <= numcnt; shift++) {
tempCF = ((s&0x80)>>7); //Save MSB!
s = (s << 1)|FLAG_CF; //Shift and set CF!
FLAGW_CF(tempCF); //Set CF!
}
if (numcnt==1) FLAGW_OF(((s >> 7) & 1)^FLAG_CF); //OF=MSB^CF
break;

case 3: //RCR r/m8
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (EMULATED_CPU>=CPU_80386) numcnt %= 9; //Operand size wrap!
if (numcnt==1) FLAGW_OF((s >> 7) ^ FLAG_CF);
for (shift = 1; shift <= numcnt; shift++) {
tempCF = (s&1); //Save LSB!
s = (s >> 1) | (FLAG_CF << 7);
FLAGW_CF(tempCF);
}
break;

case 4: case 6: //SHL r/m8
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
if (s & 0x80) FLAGW_CF(1); else FLAGW_CF(0);
//if (s & 0x8) FLAGW_AF(1); //Auxiliary carry?
s = (s << 1) & 0xFF;
Show last 165 lines
		}
if (numcnt==1) { if (FLAG_CF==(s>>7)) FLAGW_OF(0); else FLAGW_OF(1); }
if (numcnt) flag_szp8((uint8_t)(s&0xFF)); break;

case 5: //SHR r/m8
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (numcnt==1) { if (s&0x80) FLAGW_OF(1); else FLAGW_OF(0); }
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = s >> 1;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
if (numcnt) flag_szp8((uint8_t)(s & 0xFF)); break;

case 7: //SAR r/m8
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
msb = s & 0x80;
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = (s >> 1) | msb;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
byte tempSF;
tempSF = FLAG_SF; //Save the SF!
/*flag_szp8((uint8_t)(s & 0xFF));*/
//http://www.electronics.dit.ie/staff/tscarff/8086_instruction_set/8086_instruction_set.html#SAR says only C and O flags!
if (!numcnt) //Nothing done?
{
FLAGW_SF(tempSF); //We don't update when nothing's done!
}
else if (numcnt==1) //Overflow is cleared on all 1-bit shifts!
{
flag_szp8(s); //Affect sign as well!
FLAGW_OF(0); //Cleared!
}
else if (numcnt) //Anything shifted at all?
{
flag_szp8(s); //Affect sign as well!
if (EMULATED_CPU<=CPU_NECV30) //Valid to update OF?
{
FLAGW_OF(0); //Cleared with count as well?
}
}
break;
}
op_grp2_cycles(numcnt, varshift);
return(s & 0xFF);
}

word op_grp2_16(byte cnt, byte varshift) {
//word d,
INLINEREGISTER uint_32 s, shift, tempCF, msb;
INLINEREGISTER byte numcnt;
//word backup;
//if (cnt>0x8) return(oper1b); //NEC V20/V30+ limits shift count
numcnt = cnt; //Save count!
s = oper1;
switch (thereg) {
case 0: //ROL r/m16
if (EMULATED_CPU>=CPU_80386) numcnt &= 0xF; //Operand size wrap!
else if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
for (shift = 1; shift <= numcnt; shift++) {
tempCF = ((s&0x8000)>>15); //Save MSB!
s = (s << 1)|tempCF;
}
FLAGW_CF(s&1); //Set carry flag!
if (numcnt==1) FLAGW_OF(((s >> 15) & 1)^FLAG_CF);
break;

case 1: //ROR r/m16
if (EMULATED_CPU>=CPU_80386) numcnt &= 0xF; //Operand size wrap!
else if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
for (shift = 1; shift <= numcnt; shift++) {
tempCF = (s&1); //Save LSB!
s = (s >> 1) | (tempCF << 15);
}
FLAGW_CF(((s&0x8000)>>15)); //Set carry flag!
if (numcnt==1) FLAGW_OF((s >> 15) ^ ((s >> 14) & 1));
break;

case 2: //RCL r/m16
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (EMULATED_CPU>=CPU_80386) numcnt %= 17; //Operand size wrap!
for (shift = 1; shift <= numcnt; shift++) {
tempCF = ((s&0x8000)>>15); //Save MSB!
s = (s << 1)|FLAG_CF; //Shift and set CF!
FLAGW_CF(tempCF); //Set CF!
}
if (numcnt==1) FLAGW_OF(((s >> 15) & 1)^FLAG_CF); //OF=MSB^CF
break;

case 3: //RCR r/m16
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (EMULATED_CPU>=CPU_80386) numcnt %= 17; //Operand size wrap!
if (numcnt==1) FLAGW_OF((s >> 15) ^ FLAG_CF);
for (shift = 1; shift <= numcnt; shift++) {
tempCF = (s&1); //Save LSB!
s = (s >> 1) | (FLAG_CF << 15);
FLAGW_CF(tempCF);
}
break;

case 4: case 6: //SHL r/m16
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
if (s & 0x8000) FLAGW_CF(1); else FLAGW_CF(0);
//if (s & 0x8) FLAGW_AF(1); //Auxiliary carry?
s = (s << 1) & 0xFFFF;
}
if (numcnt==1) { if (FLAG_CF==(s>>15)) FLAGW_OF(0); else FLAGW_OF(1); }
if (numcnt) flag_szp16((uint16_t)(s&0xFFFF)); break;

case 5: //SHR r/m16
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (numcnt==1) { if (s&0x8000) FLAGW_OF(1); else FLAGW_OF(0); }
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = s >> 1;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
if (numcnt) flag_szp16((uint16_t)(s & 0xFFFF)); break;

case 7: //SAR r/m16
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
msb = s & 0x8000;
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = (s >> 1) | msb;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
byte tempSF;
tempSF = FLAG_SF; //Save the SF!
/*flag_szp8((uint8_t)(s & 0xFF));*/
//http://www.electronics.dit.ie/staff/tscarff/8086_instruction_set/8086_instruction_set.html#SAR says only C and O flags!
if (!numcnt) //Nothing done?
{
FLAGW_SF(tempSF); //We don't update when nothing's done!
}
else if (numcnt==1) //Overflow is cleared on all 1-bit shifts!
{
flag_szp16(s); //Affect sign as well!
FLAGW_OF(0); //Cleared!
}
else if (numcnt) //Anything shifted at all?
{
flag_szp16(s); //Affect sign as well!
if (EMULATED_CPU<=CPU_NECV30) //Valid to update OF?
{
FLAGW_OF(0); //Cleared with count as well?
}
}
break;
}
op_grp2_cycles(numcnt, varshift);
return(s & 0xFFFF);
}

32-bit:

uint_32 op_grp2_32(byte cnt, byte varshift) {
//word d,
INLINEREGISTER uint_64 s, shift, tempCF, msb;
INLINEREGISTER byte numcnt;
//word backup;
//if (cnt>0x8) return(oper1b); //NEC V20/V30+ limits shift count
numcnt = cnt; //Save count!
s = oper1d;
switch (thereg) {
case 0: //ROL r/m32
if (EMULATED_CPU>=CPU_80386) numcnt &= 0x1F; //Operand size wrap!
else if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
for (shift = 1; shift <= numcnt; shift++) {
tempCF = ((s&0x80000000)>>31); //Save MSB!
s = (s << 1)|tempCF;
}
FLAGW_CF(s&1); //Set carry flag!
if (numcnt==1) FLAGW_OF(((s >> 31) & 1)^FLAG_CF);
break;

case 1: //ROR r/m32
if (EMULATED_CPU>=CPU_80386) numcnt &= 0x1F; //Operand size wrap!
else if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
for (shift = 1; shift <= numcnt; shift++) {
tempCF = (s&1); //Save LSB!
s = (s >> 1) | (tempCF << 31);
}
FLAGW_CF(((s&0x80000000)>>31)); //Set carry flag!
if (numcnt==1) FLAGW_OF((s >> 31) ^ ((s >> 30) & 1));
break;

case 2: //RCL r/m32
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
for (shift = 1; shift <= numcnt; shift++) {
tempCF = ((s&0x80000000)>>31); //Save MSB!
s = (s << 1)|FLAG_CF; //Shift and set CF!
FLAGW_CF(tempCF); //Set CF!
}
if (numcnt==1) FLAGW_OF(((s >> 31) & 1)^FLAG_CF); //OF=MSB^CF
break;

case 3: //RCR r/m32
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (numcnt==1) FLAGW_OF((s >> 31) ^ FLAG_CF);
for (shift = 1; shift <= numcnt; shift++) {
tempCF = (s&1); //Save LSB!
s = (s >> 1) | (FLAG_CF << 31);
FLAGW_CF(tempCF);
}
break;

case 4: case 6: //SHL r/m32
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
if (s & 0x80000000) FLAGW_CF(1); else FLAGW_CF(0);
//if (s & 0x8) FLAGW_AF(1); //Auxiliary carry?
s = (s << 1) & 0xFFFFFFFF;
}
if (numcnt==1) { if (FLAG_CF==(s>>31)) FLAGW_OF(0); else FLAGW_OF(1); }
Show last 50 lines
		if (numcnt) flag_szp32((uint32_t)(s&0xFFFFFFFF)); break;

case 5: //SHR r/m32
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (numcnt==1) { if (s&0x80000000) FLAGW_OF(1); else FLAGW_OF(0); }
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = s >> 1;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
if (numcnt) flag_szp32((uint32_t)(s & 0xFFFFFFFF)); break;

case 7: //SAR r/m32
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
msb = s & 0x80000000;
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = (s >> 1) | msb;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
byte tempSF;
tempSF = FLAG_SF; //Save the SF!
/*flag_szp8((uint8_t)(s & 0xFF));*/
//http://www.electronics.dit.ie/staff/tscarff/8086_instruction_set/8086_instruction_set.html#SAR says only C and O flags!
if (!numcnt) //Nothing done?
{
FLAGW_SF(tempSF); //We don't update when nothing's done!
}
else if (numcnt==1) //Overflow is cleared on all 1-bit shifts!
{
flag_szp32(s); //Affect sign as well!
FLAGW_OF(0); //Cleared!
}
else if (numcnt) //Anything shifted at all?
{
flag_szp32(s); //Affect sign as well!
if (EMULATED_CPU<=CPU_NECV30) //Valid to update OF?
{
FLAGW_OF(0); //Cleared with count as well?
}
}
break;
}
op_grp2_cycles32(numcnt, varshift);
return(s & 0xFFFFFFFF);
}

There are still several things failing now(according to WinMerge, EDX/EAX is correct in all cases):
- ROLi B/W, ROLr, RORr: carry flag incorrectly set(set/not set)?
- RCLi B/W, RCRi B/W: missing Overflow flag set?
- RCRr: missing carry flag set(incorrectly)?

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

Reply 81 of 178, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've tried behaviour like http://www.felixcloutier.com/x86/RCL:RCR:ROL:ROR.html , but whatever I do, it doesn't match the EE dump you've made?

This is my current code (SHL/SAL/SHR/SAR already checks out without errors now):

8/16-bit(8086+):

byte op_grp2_8(byte cnt, byte varshift) {
//word d,
INLINEREGISTER word s, shift, tempCF, msb;
INLINEREGISTER byte numcnt;
//word backup;
//if (cnt>0x8) return(oper1b); //NEC V20/V30+ limits shift count
numcnt = cnt; //Save count!
s = oper1b;
switch (thereg) {
case 0: //ROL r/m8
if (EMULATED_CPU>=CPU_80386) numcnt &= 7; //Operand size wrap!
else if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
for (shift = 1; shift <= numcnt; shift++) {
tempCF = ((s&0x80)>>7); //Save MSB!
s = (s << 1)|tempCF;
}
FLAGW_CF(s&1); //Set carry flag!
if (numcnt) FLAGW_OF(((s >> 7) & 1)^FLAG_CF);
break;

case 1: //ROR r/m8
if (EMULATED_CPU>=CPU_80386) numcnt &= 7; //Operand size wrap!
else if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
for (shift = 1; shift <= numcnt; shift++) {
tempCF = (s&1); //Save LSB!
s = (s >> 1) | (tempCF << 7);
}
FLAGW_CF(((s&0x80)>>7)); //Set carry flag!
if (varshift==0) FLAGW_OF((s >> 7) ^ ((s >> 6) & 1));
break;

case 2: //RCL r/m8
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (EMULATED_CPU>=CPU_80386) numcnt %= 9; //Operand size wrap!
for (shift = 1; shift <= numcnt; shift++) {
tempCF = ((s&0x80)>>7); //Save MSB!
s = (s << 1)|FLAG_CF; //Shift and set CF!
FLAGW_CF(tempCF); //Set CF!
}
if (varshift==0) FLAGW_OF(((s >> 7) & 1)^FLAG_CF); //OF=MSB^CF
break;

case 3: //RCR r/m8
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (EMULATED_CPU>=CPU_80386) numcnt %= 9; //Operand size wrap!
if (varshift==0) FLAGW_OF((s >> 7) ^ FLAG_CF);
for (shift = 1; shift <= numcnt; shift++) {
tempCF = (s&1); //Save LSB!
s = (s >> 1) | (FLAG_CF << 7);
FLAGW_CF(tempCF);
}
break;

case 4: case 6: //SHL r/m8
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
if (s & 0x80) FLAGW_CF(1); else FLAGW_CF(0);
//if (s & 0x8) FLAGW_AF(1); //Auxiliary carry?
s = (s << 1) & 0xFF;
Show last 166 lines
		}
if (numcnt==1) { if (FLAG_CF==(s>>7)) FLAGW_OF(0); else FLAGW_OF(1); }
if (numcnt) flag_szp8((uint8_t)(s&0xFF)); break;

case 5: //SHR r/m8
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (numcnt==1) { if (s&0x80) FLAGW_OF(1); else FLAGW_OF(0); }
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = s >> 1;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
if (numcnt) flag_szp8((uint8_t)(s & 0xFF)); break;

case 7: //SAR r/m8
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
msb = s & 0x80;
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = (s >> 1) | msb;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
byte tempSF;
tempSF = FLAG_SF; //Save the SF!
/*flag_szp8((uint8_t)(s & 0xFF));*/
//http://www.electronics.dit.ie/staff/tscarff/8086_instruction_set/8086_instruction_set.html#SAR says only C and O flags!
if (!numcnt) //Nothing done?
{
FLAGW_SF(tempSF); //We don't update when nothing's done!
}
else if (numcnt==1) //Overflow is cleared on all 1-bit shifts!
{
flag_szp8(s); //Affect sign as well!
FLAGW_OF(0); //Cleared!
}
else if (numcnt) //Anything shifted at all?
{
flag_szp8(s); //Affect sign as well!
if (EMULATED_CPU<=CPU_NECV30) //Valid to update OF?
{
FLAGW_OF(0); //Cleared with count as well?
}
}
break;
}
op_grp2_cycles(numcnt, varshift);
return(s & 0xFF);
}

word op_grp2_16(byte cnt, byte varshift) {
//word d,
INLINEREGISTER uint_32 s, shift, tempCF, msb;
INLINEREGISTER byte numcnt;
//word backup;
//if (cnt>0x8) return(oper1b); //NEC V20/V30+ limits shift count
numcnt = cnt; //Save count!
s = oper1;
switch (thereg) {
case 0: //ROL r/m16
if (EMULATED_CPU>=CPU_80386) numcnt &= 0xF; //Operand size wrap!
else if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
tempCF = 0;
for (shift = 1; shift <= numcnt; shift++) {
tempCF = ((s&0x8000)>>15); //Save MSB!
s = (s << 1)|tempCF;
}
FLAGW_CF(tempCF); //Set carry flag!
if (numcnt) FLAGW_OF(((s >> 15) & 1)^FLAG_CF);
break;

case 1: //ROR r/m16
if (EMULATED_CPU>=CPU_80386) numcnt &= 0xF; //Operand size wrap!
else if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
for (shift = 1; shift <= numcnt; shift++) {
tempCF = (s&1); //Save LSB!
s = (s >> 1) | (tempCF << 15);
}
FLAGW_CF(((s&0x8000)>>15)); //Set carry flag!
if (varshift==0) FLAGW_OF((s >> 15) ^ ((s >> 14) & 1));
break;

case 2: //RCL r/m16
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (EMULATED_CPU>=CPU_80386) numcnt %= 17; //Operand size wrap!
for (shift = 1; shift <= numcnt; shift++) {
tempCF = ((s&0x8000)>>15); //Save MSB!
s = (s << 1)|FLAG_CF; //Shift and set CF!
FLAGW_CF(tempCF); //Set CF!
}
if (varshift==0) FLAGW_OF(((s >> 15) & 1)^FLAG_CF); //OF=MSB^CF
break;

case 3: //RCR r/m16
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (EMULATED_CPU>=CPU_80386) numcnt %= 17; //Operand size wrap!
if (varshift==0) FLAGW_OF((s >> 15) ^ FLAG_CF);
for (shift = 1; shift <= numcnt; shift++) {
tempCF = (s&1); //Save LSB!
s = (s >> 1) | (FLAG_CF << 15);
FLAGW_CF(tempCF);
}
break;

case 4: case 6: //SHL r/m16
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
if (s & 0x8000) FLAGW_CF(1); else FLAGW_CF(0);
//if (s & 0x8) FLAGW_AF(1); //Auxiliary carry?
s = (s << 1) & 0xFFFF;
}
if (numcnt==1) { if (FLAG_CF==(s>>15)) FLAGW_OF(0); else FLAGW_OF(1); }
if (numcnt) flag_szp16((uint16_t)(s&0xFFFF)); break;

case 5: //SHR r/m16
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (numcnt==1) { if (s&0x8000) FLAGW_OF(1); else FLAGW_OF(0); }
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = s >> 1;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
if (numcnt) flag_szp16((uint16_t)(s & 0xFFFF)); break;

case 7: //SAR r/m16
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
msb = s & 0x8000;
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = (s >> 1) | msb;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
byte tempSF;
tempSF = FLAG_SF; //Save the SF!
/*flag_szp8((uint8_t)(s & 0xFF));*/
//http://www.electronics.dit.ie/staff/tscarff/8086_instruction_set/8086_instruction_set.html#SAR says only C and O flags!
if (!numcnt) //Nothing done?
{
FLAGW_SF(tempSF); //We don't update when nothing's done!
}
else if (numcnt==1) //Overflow is cleared on all 1-bit shifts!
{
flag_szp16(s); //Affect sign as well!
FLAGW_OF(0); //Cleared!
}
else if (numcnt) //Anything shifted at all?
{
flag_szp16(s); //Affect sign as well!
if (EMULATED_CPU<=CPU_NECV30) //Valid to update OF?
{
FLAGW_OF(0); //Cleared with count as well?
}
}
break;
}
op_grp2_cycles(numcnt, varshift);
return(s & 0xFFFF);
}

32-bit:

uint_32 op_grp2_32(byte cnt, byte varshift) {
//word d,
INLINEREGISTER uint_64 s, shift, tempCF, msb;
INLINEREGISTER byte numcnt;
//word backup;
//if (cnt>0x8) return(oper1b); //NEC V20/V30+ limits shift count
numcnt = cnt; //Save count!
s = oper1d;
switch (thereg) {
case 0: //ROL r/m32
if (EMULATED_CPU>=CPU_80386) numcnt &= 0x1F; //Operand size wrap!
else if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
tempCF = 0;
for (shift = 1; shift <= numcnt; shift++) {
tempCF = ((s&0x80000000)>>31); //Save MSB!
s = (s << 1)|tempCF;
}
FLAGW_CF(tempCF); //Set carry flag!
if (numcnt) FLAGW_OF(((s >> 31) & 1)^FLAG_CF);
break;

case 1: //ROR r/m32
if (EMULATED_CPU>=CPU_80386) numcnt &= 0x1F; //Operand size wrap!
else if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
for (shift = 1; shift <= numcnt; shift++) {
tempCF = (s&1); //Save LSB!
s = (s >> 1) | (tempCF << 31);
}
FLAGW_CF(((s&0x80000000)>>31)); //Set carry flag!
if (varshift==0) FLAGW_OF((s >> 31) ^ ((s >> 30) & 1));
break;

case 2: //RCL r/m32
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
for (shift = 1; shift <= numcnt; shift++) {
tempCF = ((s&0x80000000)>>31); //Save MSB!
s = (s << 1)|FLAG_CF; //Shift and set CF!
FLAGW_CF(tempCF); //Set CF!
}
if (varshift==0) FLAGW_OF(((s >> 31) & 1)^FLAG_CF); //OF=MSB^CF
break;

case 3: //RCR r/m32
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (varshift==0) FLAGW_OF((s >> 31) ^ FLAG_CF);
for (shift = 1; shift <= numcnt; shift++) {
tempCF = (s&1); //Save LSB!
s = (s >> 1) | (FLAG_CF << 31);
FLAGW_CF(tempCF);
}
break;

case 4: case 6: //SHL r/m32
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
if (s & 0x80000000) FLAGW_CF(1); else FLAGW_CF(0);
//if (s & 0x8) FLAGW_AF(1); //Auxiliary carry?
s = (s << 1) & 0xFFFFFFFF;
}
Show last 51 lines
		if (numcnt==1) { if (FLAG_CF==(s>>31)) FLAGW_OF(0); else FLAGW_OF(1); }
if (numcnt) flag_szp32((uint32_t)(s&0xFFFFFFFF)); break;

case 5: //SHR r/m32
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (numcnt==1) { if (s&0x80000000) FLAGW_OF(1); else FLAGW_OF(0); }
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = s >> 1;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
if (numcnt) flag_szp32((uint32_t)(s & 0xFFFFFFFF)); break;

case 7: //SAR r/m32
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
msb = s & 0x80000000;
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = (s >> 1) | msb;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
byte tempSF;
tempSF = FLAG_SF; //Save the SF!
/*flag_szp8((uint8_t)(s & 0xFF));*/
//http://www.electronics.dit.ie/staff/tscarff/8086_instruction_set/8086_instruction_set.html#SAR says only C and O flags!
if (!numcnt) //Nothing done?
{
FLAGW_SF(tempSF); //We don't update when nothing's done!
}
else if (numcnt==1) //Overflow is cleared on all 1-bit shifts!
{
flag_szp32(s); //Affect sign as well!
FLAGW_OF(0); //Cleared!
}
else if (numcnt) //Anything shifted at all?
{
flag_szp32(s); //Affect sign as well!
if (EMULATED_CPU<=CPU_NECV30) //Valid to update OF?
{
FLAGW_OF(0); //Cleared with count as well?
}
}
break;
}
op_grp2_cycles32(numcnt, varshift);
return(s & 0xFFFFFFFF);
}

Can anyone help me getting this bug-free?

Edit: Implemented the version from fake86/cape, but still not checking out correctly? IMUL8/16/32 also still fails(wrong results?)?

Shift opcodes found at(16-bit/8-bit): https://bitbucket.org/superfury/unipcemu/src/ … 086.c?at=master
Opcodes 69/6B 16-bit and Shift extension opcodes C0/C1: https://bitbucket.org/superfury/unipcemu/src/ … V30.c?at=master
32-bit extensions: https://bitbucket.org/superfury/unipcemu/src/ … 386.c?at=master

Look for OP6B/OP69(NEC V30&386) and grp2_8/grp2_16/grp2_32(in 386, others in 8086(partial called by instruction set extensions in NEC V30(186+) as well)).

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

Reply 82 of 178, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've redirected all x86 IMUL instructions(except the GRP opcodes) to the following general function(since it's the same for opcodes 69/6B and 0FAF). I also fixed some stage problems(different execution stages(fetch phase(two subphases fetching r/m), execution phase, result phase)), which were causing part of the problems.

All IMUL C-style(69/6A/0FAF) execution phases are now handled by the following function:

void CPU_CIMUL(uint_32 base, byte basesize, uint_32 multiplicant, byte multiplicantsize, uint_32 *result, byte resultsize)
{
temp1.val64 = signextend64(base,basesize); //Read reg instead! Word register = Word register * imm16!
temp2.val64 = signextend64(multiplicant,multiplicantsize); //Immediate word is second/third parameter!
temp3.val64s = temp1.val64s; //Load and...
temp3.val64s *= temp2.val64s; //Signed multiplication!
temp2.val64 = signextend64(temp3.val64,resultsize); //For checking for overflow and giving the correct result!
if (temp3.val64s==temp2.val64s) FLAGW_OF(0); //Overflow flag is cleared when high word is a sign extension of the low word(values are equal)!
else FLAGW_OF(1);
FLAGW_CF(FLAG_OF); //OF=CF!
FLAGW_SF((temp3.val64&0x8000000000000000)>>63); //Sign!
FLAGW_PF(parity[temp3.val16&0xFF]); //Parity flag!
FLAGW_ZF((temp3.val64==0)?1:0); //Set the zero flag!
*result = (uint_32)temp2.val64; //Save the result, truncated to used size as 64-bit sign extension!
}

IMUL8 B/W execute correctly.
IMUL8 D fails with incorrect results?
IMUL16 W/D executes correctly.
IMUL32 D executes correctly.

FOr some reason, only the IMUL8 D fails? I only have four implementations of those IMUL instructions: r/m16,imm8; r/m32,imm16; r/m16,imm16 and r/m32,imm32. Am I supposed to believe there's another variant of that one I don't know of? There are supposed to be only four variants, which are made using the two opcodes(69/6B) and operand size(16-bit vs 32-bit)? What is the fifth variant?

Edit: These are my IMUL variants(16-bit as NECV30+, 32-bit as 386):
16-bit:
Opcode 69h 16-bit:

extern uint_32 IMULresult; //Buffer to use, general purpose!
extern word instructionbufferw, instructionbufferw2; //For 16-bit read storage!
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_internal_stepreadmodrmw(0,&instructionbufferw,MODRM_src1)) 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?
{
CPU_CIMUL(instructionbufferw,16,immw,16,&IMULresult,16); //Immediate word is second/third parameter!
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,(IMULresult&0xFFFF),0); //Write to the destination(register)!
}

Opcode 6Bh 16-bit:

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
//}

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_internal_stepreadmodrmw(0,&instructionbufferw,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?
{
CPU_CIMUL(instructionbufferw,16,immb,8,&IMULresult,16); //Immediate word is second/third parameter!
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,(IMULresult&0xFFFF),0); //Write to register!
}

Opcode 69h 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_internal_stepreadmodrmdw(0,&instructionbufferd,MODRM_src1)) 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?
{
CPU_CIMUL(instructionbufferd,32,imm32,32,&IMULresult,32); //Execute!
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,IMULresult); //Write to the destination(register)!
}

Opcode 6Bh 32-bit:

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
//}

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_internal_stepreadmodrmdw(0,&instructionbufferd,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?
{
CPU_CIMUL(instructionbufferd,32,immb,8,&IMULresult,32); //Execute!
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,IMULresult); //Write to register!
}

Edit: After having fixed the 32-bit instruction tables(which determine things like modR/M parameter order(MODRM_src0/1 in this case too)), I noticed that the ModR/M parameter order was correct in the 16-bit case, but REVERSED in the 32-bit case! 😖 After having fixed those, all IMUL tests check out correctly without bugs. 😁

Now the only problems left are (according to the testsuite) the rotate instructions(ROL/ROR/RCL/RCR)? It currently follows the logic like fake86 and CAPE, but it seems those are incorrect somehow? Both flags(carry flags and overflow flags) and results don't match up with the EE reference?

Filename
porte9.log
File size
2.57 MiB
Downloads
48 downloads
File comment
Current POST EE output with UniPCemu.
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 84 of 178, by superfury

User metadata
Rank l33t++
Rank
l33t++

Simply diff the porte9.log against the EE reference log of test386.asm?

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

Reply 85 of 178, by superfury

User metadata
Rank l33t++
Rank
l33t++

Just made a simple diff using diff from the diffutils for Windows:

Filename
test386-EE-reference.diff
File size
68 KiB
Downloads
48 downloads
File comment
UniPCemu stripped port E9 output diff against test386-EE-reference.txt.
File license
Fair use/fair dealing exception

Anyone can see what's going wrong? It's using the same algorithm as fake86 and CAPE(at least for the ROL/ROR/RCL/RCR instructions. SHL/SAL/SHR instructions are modfied to give correct results).

byte op_grp2_8(byte cnt, byte varshift) {
//word d,
INLINEREGISTER word s, shift, tempCF, msb;
INLINEREGISTER byte numcnt;
//word backup;
//if (cnt>0x8) return(oper1b); //NEC V20/V30+ limits shift count
numcnt = cnt; //Save count!
s = oper1b;
tempCF = FLAG_CF; //Save CF!
switch (thereg) {
case 0: //ROL r/m8
if (EMULATED_CPU>=CPU_80386) numcnt &= 7; //Operand size wrap!
else if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF((s&0x80)>>7); //Save MSB!
s = (s << 1)|FLAG_CF;
}
if (varshift==0) FLAGW_OF(FLAG_CF^((s >> 7) & 1)); //Only when not using CL?
break;

case 1: //ROR r/m8
if (EMULATED_CPU>=CPU_80386) numcnt &= 7; //Operand size wrap!
else if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
tempCF = FLAG_CF; //Default: unchanged!
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s&1); //Save LSB!
s = (s >> 1) | (FLAG_CF << 7);
}
if (varshift==0) FLAGW_OF((s >> 7) ^ ((s >> 6) & 1)); //Only when not using CL?
break;

case 2: //RCL r/m8
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (EMULATED_CPU>=CPU_80386) numcnt %= 9; //Operand size wrap!
for (shift = 1; shift <= numcnt; shift++) {
tempCF = FLAG_CF;
FLAGW_CF((s&0x80)>>7); //Save MSB!
s = (s << 1)|tempCF; //Shift and set CF!
}
if (varshift==0) FLAGW_OF(FLAG_CF^((s >> 7) & 1)); //OF=MSB^CF, only when not using CL?
break;

case 3: //RCR r/m8
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (EMULATED_CPU>=CPU_80386) numcnt %= 9; //Operand size wrap!
if (numcnt==1) FLAGW_OF( FLAG_CF^(s >> 7));
for (shift = 1; shift <= numcnt; shift++) {
tempCF = FLAG_CF;
FLAGW_CF(s&1); //Save LSB!
s = (s >> 1) | (tempCF << 7);
}
break;

case 4: case 6: //SHL r/m8
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
if (s & 0x80) FLAGW_CF(1); else FLAGW_CF(0);
//if (s & 0x8) FLAGW_AF(1); //Auxiliary carry?
s = (s << 1) & 0xFF;
Show last 275 lines
		}
if (numcnt==1) { if (FLAG_CF==(s>>7)) FLAGW_OF(0); else FLAGW_OF(1); }
if (numcnt) flag_szp8((uint8_t)(s&0xFF)); break;

case 5: //SHR r/m8
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (numcnt==1) { if (s&0x80) FLAGW_OF(1); else FLAGW_OF(0); }
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = s >> 1;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
if (numcnt) flag_szp8((uint8_t)(s & 0xFF)); break;

case 7: //SAR r/m8
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
msb = s & 0x80;
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = (s >> 1) | msb;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
byte tempSF;
tempSF = FLAG_SF; //Save the SF!
/*flag_szp8((uint8_t)(s & 0xFF));*/
//http://www.electronics.dit.ie/staff/tscarff/8086_instruction_set/8086_instruction_set.html#SAR says only C and O flags!
if (!numcnt) //Nothing done?
{
FLAGW_SF(tempSF); //We don't update when nothing's done!
}
else if (numcnt==1) //Overflow is cleared on all 1-bit shifts!
{
flag_szp8(s); //Affect sign as well!
FLAGW_OF(0); //Cleared!
}
else if (numcnt) //Anything shifted at all?
{
flag_szp8(s); //Affect sign as well!
if (EMULATED_CPU<=CPU_NECV30) //Valid to update OF?
{
FLAGW_OF(0); //Cleared with count as well?
}
}
break;
}
op_grp2_cycles(numcnt, varshift);
return(s & 0xFF);
}

word op_grp2_16(byte cnt, byte varshift) {
//word d,
INLINEREGISTER uint_32 s, shift, tempCF, msb;
INLINEREGISTER byte numcnt;
//word backup;
//if (cnt>0x8) return(oper1b); //NEC V20/V30+ limits shift count
numcnt = cnt; //Save count!
s = oper1;
tempCF = FLAG_CF; //Save CF!
switch (thereg) {
case 0: //ROL r/m16
if (EMULATED_CPU>=CPU_80386) numcnt &= 0xF; //Operand size wrap!
else if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF((s&0x8000)>>15); //Save MSB!
s = (s << 1)|FLAG_CF;
}
if (varshift==0) FLAGW_OF(FLAG_CF^((s >> 15) & 1)); //Only when not using CL?
break;

case 1: //ROR r/m16
if (EMULATED_CPU>=CPU_80386) numcnt &= 0xF; //Operand size wrap!
else if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
tempCF = FLAG_CF; //Default: unchanged!
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s&1); //Save LSB!
s = (s >> 1) | (FLAG_CF << 15);
}
if (varshift==0) FLAGW_OF((s >> 15) ^ ((s >> 14) & 1)); //Only when not using CL?
break;

case 2: //RCL r/m16
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (EMULATED_CPU>=CPU_80386) numcnt %= 17; //Operand size wrap!
for (shift = 1; shift <= numcnt; shift++) {
tempCF = FLAG_CF;
FLAGW_CF((s&0x8000)>>15); //Save MSB!
s = (s << 1)|tempCF; //Shift and set CF!
}
if (varshift==0) FLAGW_OF(FLAG_CF^((s >> 15) & 1)); //OF=MSB^CF, only when not using CL?
break;

case 3: //RCR r/m16
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (EMULATED_CPU>=CPU_80386) numcnt %= 17; //Operand size wrap!
if (numcnt==1) FLAGW_OF(FLAG_CF^(s >> 15));
for (shift = 1; shift <= numcnt; shift++) {
tempCF = FLAG_CF;
FLAGW_CF(s&1); //Save LSB!
s = (s >> 1) | (tempCF << 15);
}
break;

case 4: case 6: //SHL r/m16
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
if (s & 0x8000) FLAGW_CF(1); else FLAGW_CF(0);
//if (s & 0x8) FLAGW_AF(1); //Auxiliary carry?
s = (s << 1) & 0xFFFF;
}
if (numcnt==1) { if (FLAG_CF==(s>>15)) FLAGW_OF(0); else FLAGW_OF(1); }
if (numcnt) flag_szp16((uint16_t)(s&0xFFFF)); break;

case 5: //SHR r/m16
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (numcnt==1) { if (s&0x8000) FLAGW_OF(1); else FLAGW_OF(0); }
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = s >> 1;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
if (numcnt) flag_szp16((uint16_t)(s & 0xFFFF)); break;

case 7: //SAR r/m16
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
msb = s & 0x8000;
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = (s >> 1) | msb;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
byte tempSF;
tempSF = FLAG_SF; //Save the SF!
/*flag_szp8((uint8_t)(s & 0xFF));*/
//http://www.electronics.dit.ie/staff/tscarff/8086_instruction_set/8086_instruction_set.html#SAR says only C and O flags!
if (!numcnt) //Nothing done?
{
FLAGW_SF(tempSF); //We don't update when nothing's done!
}
else if (numcnt==1) //Overflow is cleared on all 1-bit shifts!
{
flag_szp16(s); //Affect sign as well!
FLAGW_OF(0); //Cleared!
}
else if (numcnt) //Anything shifted at all?
{
flag_szp16(s); //Affect sign as well!
if (EMULATED_CPU<=CPU_NECV30) //Valid to update OF?
{
FLAGW_OF(0); //Cleared with count as well?
}
}
break;
}
op_grp2_cycles(numcnt, varshift);
return(s & 0xFFFF);
}

uint_32 op_grp2_32(byte cnt, byte varshift) {
//word d,
INLINEREGISTER uint_64 s, shift, tempCF, msb;
INLINEREGISTER byte numcnt;
//word backup;
//if (cnt>0x8) return(oper1b); //NEC V20/V30+ limits shift count
numcnt = cnt; //Save count!
s = oper1d;
switch (thereg) {
case 0: //ROL r/m32
if (EMULATED_CPU>=CPU_80386) numcnt &= 0x1F; //Operand size wrap!
else if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF((s&0x80000000)>>31); //Save MSB!
s = (s << 1)|FLAG_CF;
}
if (varshift==0) FLAGW_OF(FLAG_CF^((s >> 31) & 1));
break;

case 1: //ROR r/m32
if (EMULATED_CPU>=CPU_80386) numcnt &= 0x1F; //Operand size wrap!
else if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
tempCF = FLAG_CF; //Default: unchanged!
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s&1); //Save LSB!
s = (s >> 1) | (FLAG_CF << 31);
}
if (varshift==0) FLAGW_OF((s >> 31) ^ ((s >> 30) & 1));
break;

case 2: //RCL r/m32
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
for (shift = 1; shift <= numcnt; shift++) {
tempCF = FLAG_CF;
FLAGW_CF((s&0x80000000)>>31); //Save MSB!
s = (s << 1)|tempCF; //Shift and set CF!
}
if (varshift==0) FLAGW_OF(FLAG_CF^((s >> 31) & 1)); //OF=MSB^CF
break;

case 3: //RCR r/m32
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (numcnt==1) FLAGW_OF(FLAG_CF^(s >> 31));
for (shift = 1; shift <= numcnt; shift++) {
tempCF = FLAG_CF;
FLAGW_CF(s&1); //Save LSB!
s = (s >> 1) | (tempCF << 31);
}
break;

case 4: case 6: //SHL r/m32
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
if (s & 0x80000000) FLAGW_CF(1); else FLAGW_CF(0);
//if (s & 0x8) FLAGW_AF(1); //Auxiliary carry?
s = (s << 1) & 0xFFFFFFFF;
}
if (numcnt==1) { if (FLAG_CF==(s>>31)) FLAGW_OF(0); else FLAGW_OF(1); }
if (numcnt) flag_szp32((uint32_t)(s&0xFFFFFFFF)); break;

case 5: //SHR r/m32
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (numcnt==1) { if (s&0x80000000) FLAGW_OF(1); else FLAGW_OF(0); }
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = s >> 1;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
if (numcnt) flag_szp32((uint32_t)(s & 0xFFFFFFFF)); break;

case 7: //SAR r/m32
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
msb = s & 0x80000000;
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = (s >> 1) | msb;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
byte tempSF;
tempSF = FLAG_SF; //Save the SF!
/*flag_szp8((uint8_t)(s & 0xFF));*/
//http://www.electronics.dit.ie/staff/tscarff/8086_instruction_set/8086_instruction_set.html#SAR says only C and O flags!
if (!numcnt) //Nothing done?
{
FLAGW_SF(tempSF); //We don't update when nothing's done!
}
else if (numcnt==1) //Overflow is cleared on all 1-bit shifts!
{
flag_szp32(s); //Affect sign as well!
FLAGW_OF(0); //Cleared!
}
else if (numcnt) //Anything shifted at all?
{
flag_szp32(s); //Affect sign as well!
if (EMULATED_CPU<=CPU_NECV30) //Valid to update OF?
{
FLAGW_OF(0); //Cleared with count as well?
}
}
break;
}
op_grp2_cycles32(numcnt, varshift);
return(s & 0xFFFFFFFF);
}

The cnt is CL or 1, varshift determines the instruction that's used(Opcodes D0/D1 uses 0(reg/mem with 1 shift), Opcodes D2/D3 uses 1(reg/mem with variable shift), Opcodes C0/C1 uses 2(reg/mem with immediate variable)).

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

Reply 86 of 178, by superfury

User metadata
Rank l33t++
Rank
l33t++

Just tried running MS-DOS 6.22's EMM386.EXE with HIMEM.SYS on MS-DOS 5.0a. I now see it's trying to execute a #PF handler due to some unpaged memory, which eventually causes a #GP handler due to EBX offset calculation overflowing(32-bit offset using 16-bit DS descriptor), which eventually runs into unpaged memory causing the previously #PF handler to fire again nested. This repeats infinitely?

Filename
debugger.log
File size
568.42 KiB
Downloads
47 downloads
File comment
Log of the executed instructions.
File license
Fair use/fair dealing exception

I also see something odd: lots of newlines in the debugger log that shouldn't actually be there? Strange... Also, the strange register state of that MOV instruction that's #GP faulting?

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

Reply 88 of 178, by vladstamate

User metadata
Rank Oldbie
Rank
Oldbie

Maybe they are trying to speak and tell us something.... 😀))

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

User metadata
Rank l33t++
Rank
l33t++

I know that the debugger unit in UniPCemu has such texts(Logging of memory accesses use "%08X=%02X(%c)" with values memaddr, value, value in a sprintf)? Maybe rogue memory(overflow) logs somehow?

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

Reply 90 of 178, by superfury

User metadata
Rank l33t++
Rank
l33t++

One thing that still goes awry is the ROR instruction?

RORi D EAX=00000001 EDX=00000000 PS=0044 EAX=FE000000 EDX=00000000 PS=0044 

Anyone? EAX is supposed to be 02000000?

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

Reply 91 of 178, by vladstamate

User metadata
Rank Oldbie
Rank
Oldbie

I finally got CAPE to reach 0xE0 test step in test386.asm. Once I reach where you are now, I can compare our results.

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

User metadata
Rank l33t++
Rank
l33t++

In the meanwhile fixed some bugs and some instruction debugging(opcodes 05h, 0Dh and C0h still were adding the operand size suffixes(now handled by the modr/m)). That should make it fully common log format compatible again.

Btw, the short method of memory accesses is currently used with UniPCemu's common log format register logs(see the post giving an example). Don't know if you've implemented that yet(since your webpage still doesn't mention memory transactions).

I've also fixed some compatibility differences between the GRP2 opcodes, which now handle the shifts/rotates in roughly the same way(still centered around the op_grp2_8/16/32, but the surrounding logic has been generalized for all those instructions(each opcode adjusted for the same method of handling data)).

One little warning, though: even in the compact common log format it still ammounts to a huge log when running that BIOS. Especially the POST EE logs take quite a few GB of logging data(last time it was about 19GB in total, making it practically impossible to open without hanging Windows 10, even with a good gaming PC as mine(with a i7-4790K@4.0GHz CPU and 8GB RAM)).

I've also rechecked my GRP2(shift/rotate) emulation, but looking at the logic to be executed no such strange behaviour should be present? The rotates with and without carry shouldn't give any strange data(EAX/EDX) results like 0xFE000000 instead of 0x02000000. It seems to have filled bits 31-30 with 1 bits for some odd reason?

My current GRP2 32-bit emulation:

uint_32 op_grp2_32(byte cnt, byte varshift) {
//word d,
INLINEREGISTER uint_64 s, shift, tempCF, msb;
INLINEREGISTER byte numcnt;
//word backup;
//if (cnt>0x8) return (oper1b); //NEC V20/V30+ limits shift count
numcnt = cnt; //Save count!
s = oper1d;
switch (thereg) {
case 0: //ROL r/m32
if (EMULATED_CPU>=CPU_80386) numcnt &= 0x1F; //Operand size wrap!
else if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF((s&0x80000000)>>31); //Save MSB!
s = (s << 1)|FLAG_CF;
}
if (varshift==0) FLAGW_OF(FLAG_CF^((s >> 31) & 1));
break;

case 1: //ROR r/m32
if (EMULATED_CPU>=CPU_80386) numcnt &= 0x1F; //Operand size wrap!
else if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
tempCF = FLAG_CF; //Default: unchanged!
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s&1); //Save LSB!
s = (s >> 1) | (FLAG_CF << 31);
}
if (varshift==0) FLAGW_OF((s >> 31) ^ ((s >> 30) & 1));
break;

case 2: //RCL r/m32
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
for (shift = 1; shift <= numcnt; shift++) {
tempCF = FLAG_CF;
FLAGW_CF((s&0x80000000)>>31); //Save MSB!
s = (s << 1)|tempCF; //Shift and set CF!
}
if (varshift==0) FLAGW_OF(FLAG_CF^((s >> 31) & 1)); //OF=MSB^CF
break;

case 3: //RCR r/m32
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (numcnt==1) FLAGW_OF(FLAG_CF^(s >> 31));
for (shift = 1; shift <= numcnt; shift++) {
tempCF = FLAG_CF;
FLAGW_CF(s&1); //Save LSB!
s = (s >> 1) | (tempCF << 31);
}
break;

case 4: case 6: //SHL r/m32
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
if (s & 0x80000000) FLAGW_CF(1); else FLAGW_CF(0);
//if (s & 0x8) FLAGW_AF(1); //Auxiliary carry?
s = (s << 1) & 0xFFFFFFFF;
}
if (numcnt==1) { if (FLAG_CF==(s>>31)) FLAGW_OF(0); else FLAGW_OF(1); }
if (numcnt) flag_szp32((uint32_t)(s&0xFFFFFFFF));
Show last 51 lines
		break;

case 5: //SHR r/m32
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (numcnt==1) { if (s&0x80000000) FLAGW_OF(1); else FLAGW_OF(0); }
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = s >> 1;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
if (numcnt) flag_szp32((uint32_t)(s & 0xFFFFFFFF));
break;

case 7: //SAR r/m32
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
msb = s & 0x80000000;
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = (s >> 1) | msb;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
byte tempSF;
tempSF = FLAG_SF; //Save the SF!
/*flag_szp8((uint8_t)(s & 0xFF));*/
//http://www.electronics.dit.ie/staff/tscarff/8086_instruction_set/8086_instruction_set.html#SAR says only C and O flags!
if (!numcnt) //Nothing done?
{
FLAGW_SF(tempSF); //We don't update when nothing's done!
}
else if (numcnt==1) //Overflow is cleared on all 1-bit shifts!
{
flag_szp32((uint32_t)s); //Affect sign as well!
FLAGW_OF(0); //Cleared!
}
else if (numcnt) //Anything shifted at all?
{
flag_szp32((uint32_t)s); //Affect sign as well!
if (EMULATED_CPU<=CPU_NECV30) //Valid to update OF?
{
FLAGW_OF(0); //Cleared with count as well?
}
}
break;
}
op_grp2_cycles32(numcnt, varshift);
return (s & 0xFFFFFFFF);
}

It should give correct results, but for some odd reason it doesn't seem to do so? The 8-bit and 16-bit variants are the same, just with smaller shifts and 0x80* being word or byte sized(0x8000(16-bit) and 0x80(8-bit)). Also they have different wrappings used on the shift counts, according to the 80386 user reference:

byte op_grp2_8(byte cnt, byte varshift) {
//word d,
INLINEREGISTER word s, shift, tempCF, msb;
INLINEREGISTER byte numcnt;
//word backup;
//if (cnt>0x8) return (oper1b); //NEC V20/V30+ limits shift count
numcnt = cnt; //Save count!
s = oper1b;
tempCF = FLAG_CF; //Save CF!
switch (thereg) {
case 0: //ROL r/m8
if (EMULATED_CPU>=CPU_80386) numcnt &= 7; //Operand size wrap!
else if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF((s&0x80)>>7); //Save MSB!
s = (s << 1)|FLAG_CF;
}
if (varshift==0) FLAGW_OF(FLAG_CF^((s >> 7) & 1)); //Only when not using CL?
break;

case 1: //ROR r/m8
if (EMULATED_CPU>=CPU_80386) numcnt &= 7; //Operand size wrap!
else if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
tempCF = FLAG_CF; //Default: unchanged!
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s&1); //Save LSB!
s = (s >> 1) | (FLAG_CF << 7);
}
if (varshift==0) FLAGW_OF((s >> 7) ^ ((s >> 6) & 1)); //Only when not using CL?
break;

case 2: //RCL r/m8
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (EMULATED_CPU>=CPU_80386) numcnt %= 9; //Operand size wrap!
for (shift = 1; shift <= numcnt; shift++) {
tempCF = FLAG_CF;
FLAGW_CF((s&0x80)>>7); //Save MSB!
s = (s << 1)|tempCF; //Shift and set CF!
}
if (varshift==0) FLAGW_OF(FLAG_CF^((s >> 7) & 1)); //OF=MSB^CF, only when not using CL?
break;

case 3: //RCR r/m8
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (EMULATED_CPU>=CPU_80386) numcnt %= 9; //Operand size wrap!
if (numcnt==1) FLAGW_OF( FLAG_CF^(s >> 7));
for (shift = 1; shift <= numcnt; shift++) {
tempCF = FLAG_CF;
FLAGW_CF(s&1); //Save LSB!
s = (s >> 1) | (tempCF << 7);
}
break;

case 4: case 6: //SHL r/m8
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
if (s & 0x80) FLAGW_CF(1); else FLAGW_CF(0);
//if (s & 0x8) FLAGW_AF(1); //Auxiliary carry?
s = (s << 1) & 0xFF;
Show last 169 lines
		}
if (numcnt==1) { if (FLAG_CF==(s>>7)) FLAGW_OF(0); else FLAGW_OF(1); }
if (numcnt) flag_szp8((uint8_t)(s&0xFF));
break;

case 5: //SHR r/m8
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (numcnt==1) { if (s&0x80) FLAGW_OF(1); else FLAGW_OF(0); }
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = s >> 1;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
if (numcnt) flag_szp8((uint8_t)(s & 0xFF));
break;

case 7: //SAR r/m8
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
msb = s & 0x80;
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = (s >> 1) | msb;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
byte tempSF;
tempSF = FLAG_SF; //Save the SF!
/*flag_szp8((uint8_t)(s & 0xFF));*/
//http://www.electronics.dit.ie/staff/tscarff/8086_instruction_set/8086_instruction_set.html#SAR says only C and O flags!
if (!numcnt) //Nothing done?
{
FLAGW_SF(tempSF); //We don't update when nothing's done!
}
else if (numcnt==1) //Overflow is cleared on all 1-bit shifts!
{
flag_szp8((uint8_t)s); //Affect sign as well!
FLAGW_OF(0); //Cleared!
}
else if (numcnt) //Anything shifted at all?
{
flag_szp8((uint8_t)s); //Affect sign as well!
if (EMULATED_CPU<=CPU_NECV30) //Valid to update OF?
{
FLAGW_OF(0); //Cleared with count as well?
}
}
break;
}
op_grp2_cycles(numcnt, varshift);
return (s & 0xFF);
}

word op_grp2_16(byte cnt, byte varshift) {
//word d,
INLINEREGISTER uint_32 s, shift, tempCF, msb;
INLINEREGISTER byte numcnt;
//word backup;
//if (cnt>0x8) return (oper1b); //NEC V20/V30+ limits shift count
numcnt = cnt; //Save count!
s = oper1;
tempCF = FLAG_CF; //Save CF!
switch (thereg) {
case 0: //ROL r/m16
if (EMULATED_CPU>=CPU_80386) numcnt &= 0xF; //Operand size wrap!
else if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF((s&0x8000)>>15); //Save MSB!
s = (s << 1)|FLAG_CF;
}
if (varshift==0) FLAGW_OF(FLAG_CF^((s >> 15) & 1)); //Only when not using CL?
break;

case 1: //ROR r/m16
if (EMULATED_CPU>=CPU_80386) numcnt &= 0xF; //Operand size wrap!
else if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
tempCF = FLAG_CF; //Default: unchanged!
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s&1); //Save LSB!
s = (s >> 1) | (FLAG_CF << 15);
}
if (varshift==0) FLAGW_OF((s >> 15) ^ ((s >> 14) & 1)); //Only when not using CL?
break;

case 2: //RCL r/m16
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (EMULATED_CPU>=CPU_80386) numcnt %= 17; //Operand size wrap!
for (shift = 1; shift <= numcnt; shift++) {
tempCF = FLAG_CF;
FLAGW_CF((s&0x8000)>>15); //Save MSB!
s = (s << 1)|tempCF; //Shift and set CF!
}
if (varshift==0) FLAGW_OF(FLAG_CF^((s >> 15) & 1)); //OF=MSB^CF, only when not using CL?
break;

case 3: //RCR r/m16
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (EMULATED_CPU>=CPU_80386) numcnt %= 17; //Operand size wrap!
if (numcnt==1) FLAGW_OF(FLAG_CF^(s >> 15));
for (shift = 1; shift <= numcnt; shift++) {
tempCF = FLAG_CF;
FLAGW_CF(s&1); //Save LSB!
s = (s >> 1) | (tempCF << 15);
}
break;

case 4: case 6: //SHL r/m16
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
if (s & 0x8000) FLAGW_CF(1); else FLAGW_CF(0);
//if (s & 0x8) FLAGW_AF(1); //Auxiliary carry?
s = (s << 1) & 0xFFFF;
}
if (numcnt==1) { if (FLAG_CF==(s>>15)) FLAGW_OF(0); else FLAGW_OF(1); }
if (numcnt) flag_szp16((uint16_t)(s&0xFFFF));
break;

case 5: //SHR r/m16
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (numcnt==1) { if (s&0x8000) FLAGW_OF(1); else FLAGW_OF(0); }
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = s >> 1;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
if (numcnt) flag_szp16((uint16_t)(s & 0xFFFF));
break;

case 7: //SAR r/m16
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
msb = s & 0x8000;
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = (s >> 1) | msb;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
byte tempSF;
tempSF = FLAG_SF; //Save the SF!
/*flag_szp8((uint8_t)(s & 0xFF));*/
//http://www.electronics.dit.ie/staff/tscarff/8086_instruction_set/8086_instruction_set.html#SAR says only C and O flags!
if (!numcnt) //Nothing done?
{
FLAGW_SF(tempSF); //We don't update when nothing's done!
}
else if (numcnt==1) //Overflow is cleared on all 1-bit shifts!
{
flag_szp16(s); //Affect sign as well!
FLAGW_OF(0); //Cleared!
}
else if (numcnt) //Anything shifted at all?
{
flag_szp16(s); //Affect sign as well!
if (EMULATED_CPU<=CPU_NECV30) //Valid to update OF?
{
FLAGW_OF(0); //Cleared with count as well?
}
}
break;
}
op_grp2_cycles(numcnt, varshift);
return (s & 0xFFFF);
}

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

Reply 93 of 178, by superfury

User metadata
Rank l33t++
Rank
l33t++

Just debugged a single 32-bit shift: it sets the CF, then shifts the original value(=1) and ors in the carry flag shifted left by 31, which results in an incorrect 0xffffffff80000000? Shifting more causes those 1-bits to slide into visible range, causing the invalid result? Can anyone tell me why that happens?

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

Reply 94 of 178, by superfury

User metadata
Rank l33t++
Rank
l33t++

I've just added some 0x7F, 0x7FFF and 0x7FFFFFFF masks to the "(s>>1)" parts (e.g. becoming "((s>>1)&0x7F)"). That was applied to the ROR and RCR instructions. Now the results all match up to the real CPU(all but the flags). 😁 The only bugs still left are (oddly enough) the carry flag and overflow flag not always matching up with an actual CPU?

Filename
porte9.log
File size
2.57 MiB
Downloads
45 downloads
File comment
POST EE log of UniPCemu's latest commit.
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 95 of 178, by superfury

User metadata
Rank l33t++
Rank
l33t++

One very important question: does the overflow flag depend on the shift count(needing to be 1), shift instruction(1-shift instruction, CL shift instruction or immediate shift instruction(186+)), the shift count(being any 8-bit number depending on the used value in instruction(1-shift), CL or immediate)) or both? Looking at cape's source code shows that only RCR depends on the instruction type(latter) but all others depend on the shift count only? What is correct?

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

Reply 96 of 178, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote:

Just debugged a single 32-bit shift: it sets the CF, then shifts the original value(=1) and ors in the carry flag shifted left by 31, which results in an incorrect 0xffffffff80000000? Shifting more causes those 1-bits to slide into visible range, causing the invalid result? Can anyone tell me why that happens?

https://stackoverflow.com/questions/29538935/ … redirect=1&lq=1

Reply 97 of 178, by superfury

User metadata
Rank l33t++
Rank
l33t++

So indeed, the sign extension is remedied by simply and-ing out the high 33/17/9 bits(sign bit and higher). Now those results are as required.

I've modified all instructions a bit, but for some odd reason the carry and overflow flag(even with ROLi, which shouldn't happen according to documentation!) bits aren't working properly, even though the results in the registers are correct? ROLi, RCLi and RCRi still have overflow bugs. The carry flag bugs appear on ROLr B/W/D, RORr B/W/D, RCRr B/W/D. All other instructions have no problems anymore.

It's strange that the carry bug appears, but the EAX/EDX registers have no differences?
It's also weird that the ROLi seems to set overflow sometimes, even though not a single documentation says it sets the overflow flag in any way?

Filename
porte9.log
File size
2.57 MiB
Downloads
45 downloads
File comment
Currently generated POST EE log.
File license
Fair use/fair dealing exception

Anyone knows what's going wrong there?

My documentation:
http://www.felixcloutier.com/x86/RCL:RCR:ROL:ROR.html
http://x86.renejeschke.de/html/file_module_x86_id_273.html
https://pdos.csail.mit.edu/6.828/2014/readings/i386/RCL.htm
http://x86.renejeschke.de/html/file_module_x86_id_285.html

8-bit shifts/rotates:

byte op_grp2_8(byte cnt, byte varshift) {
//word d,
INLINEREGISTER word s, shift, tempCF, msb;
INLINEREGISTER byte numcnt, maskcnt;
//word backup;
//if (cnt>0x8) return (oper1b); //NEC V20/V30+ limits shift count
numcnt = maskcnt = cnt; //Save count!
s = oper1b;
switch (thereg) {
case 0: //ROL r/m8
if (EMULATED_CPU >= CPU_NECV30) maskcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
numcnt = maskcnt;
if (EMULATED_CPU>=CPU_80386) numcnt &= 7; //Operand size wrap!
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF((s&0x80)>>7); //Save MSB!
s = (s << 1)|FLAG_CF;
}
FLAGW_CF(s); //Always sets CF, according to various sources?
if (numcnt==1) FLAGW_OF(FLAG_CF^((s >> 7) & 1)); //Only when not using CL?
break;

case 1: //ROR r/m8
if (EMULATED_CPU >= CPU_NECV30) maskcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
numcnt = maskcnt;
if (EMULATED_CPU>=CPU_80386) numcnt &= 7; //Operand size wrap!
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s&1); //Save LSB!
s = ((s >> 1)&0x7F) | (FLAG_CF << 7);
}
FLAGW_CF(s>>7); //Always sets CF, according to various sources?
if (numcnt==1) FLAGW_OF((s >> 7) ^ ((s >> 6) & 1)); //Only when not using CL?
break;

case 2: //RCL r/m8
if (EMULATED_CPU >= CPU_NECV30) maskcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
numcnt = maskcnt;
if (EMULATED_CPU>=CPU_80386) numcnt %= 9; //Operand size wrap!
for (shift = 1; shift <= numcnt; shift++) {
tempCF = FLAG_CF;
FLAGW_CF((s&0x80)>>7); //Save MSB!
s = (s << 1)|tempCF; //Shift and set CF!
}
if (cnt==1) FLAGW_OF(FLAG_CF^((s >> 7) & 1)); //OF=MSB^CF, only when not using CL?
break;

case 3: //RCR r/m8
if (EMULATED_CPU >= CPU_NECV30) maskcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
numcnt = maskcnt;
if (EMULATED_CPU>=CPU_80386) numcnt %= 9; //Operand size wrap!
if (cnt==1) FLAGW_OF( FLAG_CF^(s >> 7));
for (shift = 1; shift <= numcnt; shift++) {
tempCF = FLAG_CF;
FLAGW_CF(s&1); //Save LSB!
s = ((s >> 1)&0x7F) | (tempCF << 7);
}
break;

case 4: case 6: //SHL r/m8
if (EMULATED_CPU >= CPU_NECV30) maskcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
numcnt = maskcnt;
Show last 61 lines
		//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
if (s & 0x80) FLAGW_CF(1); else FLAGW_CF(0);
//if (s & 0x8) FLAGW_AF(1); //Auxiliary carry?
s = (s << 1) & 0xFF;
}
if (maskcnt==1) { if (FLAG_CF==(s>>7)) FLAGW_OF(0); else FLAGW_OF(1); }
if (numcnt) flag_szp8((uint8_t)(s&0xFF));
break;

case 5: //SHR r/m8
if (EMULATED_CPU >= CPU_NECV30) maskcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
numcnt = maskcnt;
if (maskcnt==1) { if (s&0x80) FLAGW_OF(1); else FLAGW_OF(0); }
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = s >> 1;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
if (numcnt) flag_szp8((uint8_t)(s & 0xFF));
break;

case 7: //SAR r/m8
if (EMULATED_CPU >= CPU_NECV30) maskcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
numcnt = maskcnt;
msb = s & 0x80;
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = (s >> 1) | msb;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
byte tempSF;
tempSF = FLAG_SF; //Save the SF!
/*flag_szp8((uint8_t)(s & 0xFF));*/
//http://www.electronics.dit.ie/staff/tscarff/8086_instruction_set/8086_instruction_set.html#SAR says only C and O flags!
if (!maskcnt) //Nothing done?
{
FLAGW_SF(tempSF); //We don't update when nothing's done!
}
else if (maskcnt==1) //Overflow is cleared on all 1-bit shifts!
{
flag_szp8((uint8_t)s); //Affect sign as well!
FLAGW_OF(0); //Cleared!
}
else if (numcnt) //Anything shifted at all?
{
flag_szp8((uint8_t)s); //Affect sign as well!
if (EMULATED_CPU<=CPU_NECV30) //Valid to update OF?
{
FLAGW_OF(0); //Cleared with count as well?
}
}
break;
}
op_grp2_cycles(numcnt, varshift);
return (s & 0xFF);
}

16-bit shifts/rotates:

word op_grp2_16(byte cnt, byte varshift) {
//word d,
INLINEREGISTER uint_32 s, shift, tempCF, msb;
INLINEREGISTER byte numcnt, maskcnt;
//word backup;
//if (cnt>0x8) return (oper1b); //NEC V20/V30+ limits shift count
numcnt = maskcnt = cnt; //Save count!
s = oper1;
switch (thereg) {
case 0: //ROL r/m16
if (EMULATED_CPU >= CPU_NECV30) maskcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
numcnt = maskcnt;
if (EMULATED_CPU>=CPU_80386) numcnt &= 0xF; //Operand size wrap!
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF((s&0x8000)>>15); //Save MSB!
s = (s << 1)|FLAG_CF;
}
FLAGW_CF(s); //Always sets CF, according to various sources?
if (numcnt==1) FLAGW_OF(FLAG_CF^((s >> 15) & 1)); //Only when not using CL?
break;

case 1: //ROR r/m16
if (EMULATED_CPU >= CPU_NECV30) maskcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
numcnt = maskcnt;
if (EMULATED_CPU>=CPU_80386) numcnt &= 0xF; //Operand size wrap!
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s&1); //Save LSB!
s = ((s >> 1)&0x7FFF) | (FLAG_CF << 15);
}
FLAGW_CF(s>>15); //Always sets CF, according to various sources?
if (numcnt==1) FLAGW_OF((s >> 15) ^ ((s >> 14) & 1)); //Only when not using CL?
break;

case 2: //RCL r/m16
if (EMULATED_CPU >= CPU_NECV30) maskcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
numcnt = maskcnt;
if (EMULATED_CPU>=CPU_80386) numcnt %= 17; //Operand size wrap!
for (shift = 1; shift <= numcnt; shift++) {
tempCF = FLAG_CF;
FLAGW_CF((s&0x8000)>>15); //Save MSB!
s = (s << 1)|tempCF; //Shift and set CF!
}
if (cnt==1) FLAGW_OF(FLAG_CF^((s >> 15) & 1)); //OF=MSB^CF, only when not using CL?
break;

case 3: //RCR r/m16
if (EMULATED_CPU >= CPU_NECV30) maskcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
numcnt = maskcnt;
if (EMULATED_CPU>=CPU_80386) numcnt %= 17; //Operand size wrap!
if (cnt==1) FLAGW_OF(FLAG_CF^(s >> 15));
for (shift = 1; shift <= numcnt; shift++) {
tempCF = FLAG_CF;
FLAGW_CF(s&1); //Save LSB!
s = ((s >> 1)&0x7FFF) | (tempCF << 15);
}
break;

case 4: case 6: //SHL r/m16
if (EMULATED_CPU >= CPU_NECV30) maskcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
numcnt = maskcnt;
Show last 61 lines
		//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
if (s & 0x8000) FLAGW_CF(1); else FLAGW_CF(0);
//if (s & 0x8) FLAGW_AF(1); //Auxiliary carry?
s = (s << 1) & 0xFFFF;
}
if (maskcnt==1) { if (FLAG_CF==(s>>15)) FLAGW_OF(0); else FLAGW_OF(1); }
if (numcnt) flag_szp16((uint16_t)(s&0xFFFF));
break;

case 5: //SHR r/m16
if (EMULATED_CPU >= CPU_NECV30) maskcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
numcnt = maskcnt;
if (maskcnt==1) { if (s&0x8000) FLAGW_OF(1); else FLAGW_OF(0); }
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = s >> 1;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
if (numcnt) flag_szp16((uint16_t)(s & 0xFFFF));
break;

case 7: //SAR r/m16
if (EMULATED_CPU >= CPU_NECV30) maskcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
numcnt = maskcnt;
msb = s & 0x8000;
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = (s >> 1) | msb;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
byte tempSF;
tempSF = FLAG_SF; //Save the SF!
/*flag_szp8((uint8_t)(s & 0xFF));*/
//http://www.electronics.dit.ie/staff/tscarff/8086_instruction_set/8086_instruction_set.html#SAR says only C and O flags!
if (!maskcnt) //Nothing done?
{
FLAGW_SF(tempSF); //We don't update when nothing's done!
}
else if (maskcnt==1) //Overflow is cleared on all 1-bit shifts!
{
flag_szp16(s); //Affect sign as well!
FLAGW_OF(0); //Cleared!
}
else if (numcnt) //Anything shifted at all?
{
flag_szp16(s); //Affect sign as well!
if (EMULATED_CPU<=CPU_NECV30) //Valid to update OF?
{
FLAGW_OF(0); //Cleared with count as well?
}
}
break;
}
op_grp2_cycles(numcnt, varshift);
return (s & 0xFFFF);
}

32-bit shifts/rotates:

uint_32 op_grp2_32(byte cnt, byte varshift) {
//word d,
INLINEREGISTER uint_64 s, shift, tempCF, msb;
INLINEREGISTER byte numcnt,maskcnt;
//word backup;
//if (cnt>0x8) return (oper1b); //NEC V20/V30+ limits shift count
numcnt = maskcnt = cnt; //Save count!
s = oper1d;
switch (thereg) {
case 0: //ROL r/m32
if (EMULATED_CPU >= CPU_NECV30) maskcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
numcnt = maskcnt;
if (EMULATED_CPU>=CPU_80386) numcnt &= 0x1F; //Operand size wrap!
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF((s&0x80000000)>>31); //Save MSB!
s = (s << 1)|FLAG_CF;
}
FLAGW_CF(s); //Always sets CF, according to various sources?
if (numcnt==1) FLAGW_OF(((s >> 31) & 1)^FLAG_CF);
break;

case 1: //ROR r/m32
if (EMULATED_CPU >= CPU_NECV30) maskcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
numcnt = maskcnt;
if (EMULATED_CPU>=CPU_80386) numcnt &= 0x1F; //Operand size wrap!
numcnt = maskcnt;
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s&1); //Save LSB!
s = ((s >> 1)&0x7FFFFFFF) | (FLAG_CF << 31);
}
FLAGW_CF(s>>31); //Always sets CF, according to various sources?
if (numcnt==1) FLAGW_OF((s >> 31) ^ ((s >> 30) & 1));
break;

case 2: //RCL r/m32
if (EMULATED_CPU >= CPU_NECV30) maskcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
numcnt = maskcnt;
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
for (shift = 1; shift <= numcnt; shift++) {
tempCF = FLAG_CF;
FLAGW_CF((s&0x80000000)>>31); //Save MSB!
s = (s << 1)|tempCF; //Shift and set CF!
}
if (cnt==1) FLAGW_OF(((s >> 31) & 1)^FLAG_CF); //OF=MSB^CF
break;

case 3: //RCR r/m32
if (EMULATED_CPU >= CPU_NECV30) maskcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
numcnt = maskcnt;
if (EMULATED_CPU >= CPU_NECV30) numcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
if (cnt==1) FLAGW_OF(((s >> 31)&1)^FLAG_CF);
for (shift = 1; shift <= numcnt; shift++) {
tempCF = FLAG_CF;
FLAGW_CF(s&1); //Save LSB!
s = ((s >> 1)&0x7FFFFFFF) | (tempCF << 31);
}
break;

case 4: case 6: //SHL r/m32
if (EMULATED_CPU >= CPU_NECV30) maskcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
Show last 62 lines
		numcnt = maskcnt;
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
if (s & 0x80000000) FLAGW_CF(1); else FLAGW_CF(0);
//if (s & 0x8) FLAGW_AF(1); //Auxiliary carry?
s = (s << 1) & 0xFFFFFFFF;
}
if (maskcnt==1) { if (FLAG_CF==(s>>31)) FLAGW_OF(0); else FLAGW_OF(1); }
if (numcnt) flag_szp32((uint32_t)(s&0xFFFFFFFF));
break;

case 5: //SHR r/m32
if (EMULATED_CPU >= CPU_NECV30) maskcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
numcnt = maskcnt;
if (maskcnt==1) { if (s&0x80000000) FLAGW_OF(1); else FLAGW_OF(0); }
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = s >> 1;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
if (numcnt) flag_szp32((uint32_t)(s & 0xFFFFFFFF));
break;

case 7: //SAR r/m32
if (EMULATED_CPU >= CPU_NECV30) maskcnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
numcnt = maskcnt;
msb = s & 0x80000000;
//FLAGW_AF(0);
for (shift = 1; shift <= numcnt; shift++) {
FLAGW_CF(s & 1);
//backup = s; //Save backup!
s = (s >> 1) | msb;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
byte tempSF;
tempSF = FLAG_SF; //Save the SF!
/*flag_szp8((uint8_t)(s & 0xFF));*/
//http://www.electronics.dit.ie/staff/tscarff/8086_instruction_set/8086_instruction_set.html#SAR says only C and O flags!
if (!maskcnt) //Nothing done?
{
FLAGW_SF(tempSF); //We don't update when nothing's done!
}
else if (maskcnt==1) //Overflow is cleared on all 1-bit shifts!
{
flag_szp32((uint32_t)s); //Affect sign as well!
FLAGW_OF(0); //Cleared!
}
else if (numcnt) //Anything shifted at all?
{
flag_szp32((uint32_t)s); //Affect sign as well!
if (EMULATED_CPU<=CPU_NECV30) //Valid to update OF?
{
FLAGW_OF(0); //Cleared with count as well?
}
}
break;
}
op_grp2_cycles32(numcnt, varshift);
return (s & 0xFFFFFFFF);
}

Anyone?

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

Reply 98 of 178, by peterferrie

User metadata
Rank Oldbie
Rank
Oldbie

Overflow happens as you would expect - when the top two bits differ before and after the rotate, using the same algorithm as for add, etc. It happens for all rotate counts, not just 1.
Carry is set to the value of the last bit rotated (e.g. al=0x40, cl=2, rol al,cl -> carry is set).

Reply 99 of 178, by superfury

User metadata
Rank l33t++
Rank
l33t++

So when what count is overflow (re)set? The original(cnt), the masked(maskcnt) or the actual shifts(numcnt)?

The setting of the carry bit is already done that way(see code), but still incorrect according to the reference?

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