VOGONS


First post, by superfury

User metadata
Rank l33t++
Rank
l33t++

I'm trying to get my 8/16/32-bit shift/rotate(GRP2 opcodes) instructions result in fully correct flags, but for some reason, it doesn't check out in the EPC 80186 testsuite(ran on the UniPCemu 80286 emulation).

GRP2 8-bit

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

case 1: //ROR r/m8
for (shift = 1; shift <= cnt; shift++) {
FLAGW_CF(s & 1);
s = (s >> 1) | (FLAG_CF << 7);
}
if (cnt==1) FLAGW_OF((s >> 7) ^ ((s >> 6) & 1));
break;

case 2: //RCL r/m8
for (shift = 1; shift <= cnt; shift++) {
oldCF = FLAG_CF;
if (s & 0x80) FLAGW_CF(1); else FLAGW_CF(0);
s = s << 1;
s = s | oldCF;
}
if (cnt==1) FLAGW_OF(FLAG_CF ^ ((s >> 7) & 1));
break;

case 3: //RCR r/m8
for (shift = 1; shift <= cnt; shift++) {
oldCF = FLAG_CF;
FLAGW_CF(s & 1);
s = (s >> 1) | (oldCF << 7);
}
if (cnt==1) FLAGW_OF((s >> 7) ^ ((s >> 6) & 1));
break;

case 4: case 6: //SHL r/m8
//FLAGW_AF(0);
for (shift = 1; shift <= cnt; shift++) {
if (s & 0x80) FLAGW_CF(1); else FLAGW_CF(0);
//if (s & 0x8) FLAGW_AF(1); //Auxiliary carry?
s = (s << 1) & 0xFF;
}
if (cnt==1) FLAGW_OF((FLAG_CF ^ (s >> 7)));
flag_szp8((uint8_t)(s&0xFF)); break;

case 5: //SHR r/m8
if (cnt == 1) FLAGW_OF((s & 0x80) ? 1 : 0); else FLAGW_OF(0);
//FLAGW_AF(0);
for (shift = 1; shift <= cnt; shift++) {
FLAGW_CF(s & 1);
Show last 41 lines
			backup = s; //Save backup!
s = s >> 1;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
flag_szp8((uint8_t)(s & 0xFF)); break;

case 7: //SAR r/m8
msb = s & 0x80;
//FLAGW_AF(0);
for (shift = 1; shift <= cnt; 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 (!cnt) //Nothing done?
{
FLAGW_SF(tempSF); //We don't update when nothing's done!
}
else if (cnt==1) //Overflow is cleared on all 1-bit shifts!
{
flag_s8(s); //Affect sign as well!
FLAGW_OF(0); //Cleared!
}
else if (cnt) //Anything shifted at all?
{
flag_s8(s); //Affect sign as well!
}
if ((EMULATED_CPU>=CPU_NECV30) && cnt) //NECV20+ affected?
{
flag_p8(s); //Affect parity as well!
}
break;
}
op_grp2_cycles(cnt, varshift);
return(s & 0xFF);
}

GRP2 16-bit

word op_grp2_16(byte cnt, byte varshift) {
//uint32_t d,
INLINEREGISTER uint_32 s, shift, oldCF, msb;
//if (cnt>0x10) return(oper1); //NEC V20/V30+ limits shift count
if (EMULATED_CPU >= CPU_NECV30) cnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
word backup;
s = oper1;
oldCF = FLAG_CF;
switch (thereg) {
case 0: //ROL r/m16
for (shift = 1; shift <= cnt; shift++) {
if (s & 0x8000) FLAGW_CF(1); else FLAGW_CF(0);
s = s << 1;
s = s | FLAG_CF;
}
if (cnt==1) FLAGW_OF(FLAG_CF ^ ((s >> 15) & 1));
break;

case 1: //ROR r/m16
for (shift = 1; shift <= cnt; shift++) {
FLAGW_CF(s & 1);
s = (s >> 1) | (FLAG_CF << 15);
}
if (cnt==1) FLAGW_OF((s >> 15) ^ ((s >> 14) & 1));
break;

case 2: //RCL r/m16
for (shift = 1; shift <= cnt; shift++) {
oldCF = FLAG_CF;
if (s & 0x8000) FLAGW_CF(1); else FLAGW_CF(0);
s = s << 1;
s = s | oldCF;
//oldCF = ((s&0x8000)>>15)&1; //Save FLAG_CF!
//s = (s<<1)+FLAG_CF;
//FLAG_CF = oldCF;
}
if (cnt==1) FLAGW_OF(FLAG_CF ^ ((s >> 15) & 1));
break;

case 3: //RCR r/m16
if (cnt==1) FLAGW_OF(((s >> 15) & 1) ^ FLAG_CF);
for (shift = 1; shift <= cnt; shift++) {
oldCF = FLAG_CF;
FLAGW_CF(s & 1);
s = (s >> 1) | (oldCF << 15);
//oldCF = s&1;
//s = (s<<1)+(FLAG_CF<<16);
//FLAG_CF = oldCF;
}
if (cnt==1) FLAGW_OF((s >> 15) ^ ((s >> 14) & 1));
break;

case 4: case 6: //SHL r/m16
//FLAGW_AF(0);
for (shift = 1; shift <= cnt; shift++) {
if (s & 0x8000) FLAGW_CF(1); else FLAGW_CF(0);
//if (s & 0x8) FLAGW_AF(1); //Auxiliary carry?
s = (s << 1) & 0xFFFF;
}
if ((cnt) && (FLAG_CF == (s >> 15))) FLAGW_OF(0); else FLAGW_OF(1);
Show last 49 lines
		flag_szp16(s); break;

case 5: //SHR r/m16
if (cnt) FLAGW_OF((s & 0x8000) ? 1 : 0);
//FLAGW_AF(0);
for (shift = 1; shift <= cnt; shift++) {
FLAGW_CF(s & 1);
backup = s; //Save backup!
s = s >> 1;
//if (((backup^s)&0x10)) FLAGW_AF(1); //Auxiliary carry?
}
flag_szp16(s); break;

case 7: //SAR r/m16
msb = s & 0x8000; //Read the MSB!
//FLAGW_AF(0);
for (shift = 1; shift <= cnt; 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_szp16(s);*/
//http://www.electronics.dit.ie/staff/tscarff/8086_instruction_set/8086_instruction_set.html#SAR says only C and O flags!
if (!cnt) //Nothing done?
{
FLAGW_SF(tempSF); //We don't update when nothing's done!
}
else if (cnt==1) //Overflow is cleared on all 1-bit shifts!
{
flag_s16(s); //Affect sign as well!
FLAGW_OF(0); //Cleared!
}
else if (cnt) //Anything shifted at all?
{
flag_s16(s); //Affect sign as well!
}
if ((EMULATED_CPU>=CPU_NECV30) && cnt) //NECV20+ affected?
{
flag_p16(s); //Affect parity as well!
flag_s16(s); //Affect sign as well!
}
break;
}
op_grp2_cycles(cnt, varshift|4);
return(s & 0xFFFF);
}

GRP2 32-bit:

uint_32 op_grp2_32(byte cnt, byte varshift) {
//uint32_t d,
INLINEREGISTER uint_32 s, shift, oldCF, msb;
//if (cnt>0x10) return(oper1d); //NEC V20/V30+ limits shift count
if (EMULATED_CPU >= CPU_NECV30) cnt &= 0x1F; //Clear the upper 3 bits to become a NEC V20/V30+!
s = oper1d;
oldCF = FLAG_CF;
switch (thereg) {
case 0: //ROL r/m32
for (shift = 1; shift <= cnt; shift++) {
if (s & 0x80000000) FLAGW_CF(1); else FLAGW_CF(0);
s = s << 1;
s = s | FLAG_CF;
}
if (cnt==1) FLAGW_OF(FLAG_CF ^ ((s >> 31) & 1));
break;

case 1: //ROR r/m32
for (shift = 1; shift <= cnt; shift++) {
FLAGW_CF(s & 1);
s = (s >> 1) | (FLAG_CF << 31);
}
if (cnt==1) FLAGW_OF((s >> 31) ^ ((s >> 30) & 1));
break;

case 2: //RCL r/m32
for (shift = 1; shift <= cnt; shift++) {
oldCF = FLAG_CF;
if (s & 0x80000000) FLAGW_CF(1); else FLAGW_CF(0);
s = s << 1;
s = s | oldCF;
//oldCF = ((s&0x8000)>>15)&1; //Save FLAG_CF!
//s = (s<<1)+FLAG_CF;
//FLAG_CF = oldCF;
}
if (cnt==1) FLAGW_OF(FLAG_CF ^ ((s >> 31) & 1));
break;

case 3: //RCR r/m32
if (cnt==1) FLAGW_OF(((s >> 31) & 1) ^ FLAG_CF);
for (shift = 1; shift <= cnt; shift++) {
oldCF = FLAG_CF;
FLAGW_CF(s & 1);
s = (s >> 1) | (oldCF << 31);
//oldCF = s&1;
//s = (s<<1)+(FLAG_CF<<32);
//FLAG_CF = oldCF;
}
if (cnt==1) FLAGW_OF((s >> 31) ^ ((s >> 30) & 1));
break;

case 4: case 6: //SHL r/m32
//FLAGW_AF(0);
for (shift = 1; shift <= cnt; shift++) {
if (s & 0x80000000) FLAGW_CF(1); else FLAGW_CF(0);
s = (s << 1) & 0xFFFFFFFF;
//FLAGW_AF(1); //Auxiliary carry?
}
if ((cnt==1) && (FLAG_CF == (s >> 31))) FLAGW_OF(0); else FLAGW_OF(1);
flag_szp32(s); break;
Show last 46 lines

case 5: //SHR r/m32
if (cnt==1) FLAGW_OF((s & 0x80000000) ? 1 : 0);
//FLAGW_AF(0);
for (shift = 1; shift <= cnt; shift++) {
FLAGW_CF(s & 1);
s = s >> 1;
//FLAGW_AF(1); //Auxiliary carry?
}
flag_szp32(s); break;

case 7: //SAR r/m32
msb = s & 0x80000000; //Read the MSB!
//FLAGW_AF(0);
for (shift = 1; shift <= cnt; shift++) {
FLAGW_CF(s & 1);
s = (s >> 1) | msb;
//FLAGW_AF(1); //Auxiliary carry?
}
byte tempSF;
tempSF = FLAG_SF; //Save the SF!
/*flag_szp32(s);*/
//http://www.electronics.dit.ie/staff/tscarff/8086_instruction_set/8086_instruction_set.html#SAR says only C and O flags!
if (!cnt) //Nothing done?
{
FLAGW_SF(tempSF); //We don't update when nothing's done!
}
else if (cnt==1) //Overflow is cleared on all 1-bit shifts!
{
flag_s32(s); //Affect sign as well!
FLAGW_OF(0); //Cleared!
}
else if (cnt) //Anything shifted at all?
{
flag_s32(s); //Affect sign as well!
}
if ((EMULATED_CPU>=CPU_NECV30) && cnt) //NECV20+ affected?
{
flag_p32(s); //Affect parity as well!
flag_s32(s); //Affect sign as well!
}
break;
}
op_grp2_cycles32(cnt, varshift|4);
return(s & 0xFFFFFFFF);
}

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

User metadata
Rank l33t++
Rank
l33t++

I'm wondering about one thing, though: why is there a theoretical sal instruction? If there's a sar instruction(which shifts right, while keeping the sign bit), shouldn't there be a sal instruction for the same purpose? So each shift left shifts left, setting the most significant bit to the original sign(original bit 7, 15 or 31), thus making an effective SAL variant like SAR? Or is this impossible, due to the binary value being incompatible(needing to be negated+1 before and after shifting to be effective)?

Edit: So SAL X,n equals:
NEG X
SHL X,n
NEG X

Is that correct, or is that not needed for SAL X,1?

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

Reply 2 of 3, by Jepael

User metadata
Rank Oldbie
Rank
Oldbie
superfury wrote:

I'm wondering about one thing, though: why is there a theoretical sal instruction?

That's because it's identical to shl, and both shl and sal gets assembled to same opcode, both assembler mnemonics exist for human convenience.

The second highest bit already matches the sign, unless the value is too big to shift and fit into result anyway.

Reply 3 of 3, by superfury

User metadata
Rank l33t++
Rank
l33t++

There would be a problem when shifting left signed numbers: the sign bit(bit 7/15/31) would be shifted off (into carry(shifting 1) or nothingness with shifts larger than 1). So shifting left negative numbers might cause data loss? Or is this simply not the case(seeing as the negative number stays the same due to two's complement)? So FFFE(-2) becomes -4 because the high bits stay set(until the high bits overflow incorrectly when shifted off?)?

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