test386.asm CPU tester

Emulation of old PCs, PC hardware, or PC peripherals.

Re: test386.asm CPU tester

Postby superfury » 2017-11-05 @ 15:45

This is what I've gotten so far:

16-bit:
Code: Select all
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;
      }
      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:
Code: Select all
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); }
      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)?
superfury
l33t
 
Posts: 2282
Joined: 2014-3-08 @ 11:25
Location: Netherlands

Re: test386.asm CPU tester

Postby superfury » 2017-11-05 @ 18:25

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+):
Code: Select all
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;
      }
      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:
Code: Select all
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;
      }
      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/unipcem ... ?at=master
Opcodes 69/6B 16-bit and Shift extension opcodes C0/C1: https://bitbucket.org/superfury/unipcem ... ?at=master
32-bit extensions: https://bitbucket.org/superfury/unipcem ... ?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)).
superfury
l33t
 
Posts: 2282
Joined: 2014-3-08 @ 11:25
Location: Netherlands

Re: test386.asm CPU tester

Postby superfury » 2017-11-06 @ 14:07

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:
Code: Select all
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:
Code: Select all
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:
Code: Select all
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:
Code: Select all
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:
Code: Select all
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! :S After having fixed those, all IMUL tests check out correctly without bugs. :D

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?

porte9.log
Current POST EE output with UniPCemu.
(2.57 MiB) Downloaded 6 times
superfury
l33t
 
Posts: 2282
Joined: 2014-3-08 @ 11:25
Location: Netherlands

Re: test386.asm CPU tester

Postby peterferrie » 2017-11-06 @ 23:24

It might help if you gave some specific examples of values that are failing tests. Then I can look at the code and follow the flow to find the problem.
peterferrie
Oldbie
 
Posts: 607
Joined: 2008-5-08 @ 21:54

Re: test386.asm CPU tester

Postby superfury » 2017-11-07 @ 06:17

Simply diff the porte9.log against the EE reference log of test386.asm?
superfury
l33t
 
Posts: 2282
Joined: 2014-3-08 @ 11:25
Location: Netherlands

Re: test386.asm CPU tester

Postby superfury » 2017-11-07 @ 10:32

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

test386-EE-reference.diff
UniPCemu stripped port E9 output diff against test386-EE-reference.txt.
(68 KiB) Downloaded 7 times


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).

Code: Select all
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;
      }
      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)).
superfury
l33t
 
Posts: 2282
Joined: 2014-3-08 @ 11:25
Location: Netherlands

Re: test386.asm CPU tester

Postby superfury » 2017-11-08 @ 13:53

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?

debugger.log
Log of the executed instructions.
(568.42 KiB) Downloaded 5 times


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?
superfury
l33t
 
Posts: 2282
Joined: 2014-3-08 @ 11:25
Location: Netherlands

Re: test386.asm CPU tester

Postby peterferrie » 2017-11-08 @ 18:41

Your registers contain text strings "0012", "C7CA", "=00(". I suggest that you go back a bit to see how they acquired such values.
peterferrie
Oldbie
 
Posts: 607
Joined: 2008-5-08 @ 21:54

Re: test386.asm CPU tester

Postby vladstamate » 2017-11-08 @ 19:20

Maybe they are trying to speak and tell us something.... :)))
User avatar
vladstamate
Oldbie
 
Posts: 801
Joined: 2015-8-23 @ 01:43

Re: test386.asm CPU tester

Postby superfury » 2017-11-08 @ 21:14

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?
superfury
l33t
 
Posts: 2282
Joined: 2014-3-08 @ 11:25
Location: Netherlands

Re: test386.asm CPU tester

Postby superfury » 2017-11-09 @ 19:35

One thing that still goes awry is the ROR instruction?
Code: Select all
RORi D EAX=00000001 EDX=00000000 PS=0044 EAX=FE000000 EDX=00000000 PS=0044


Anyone? EAX is supposed to be 02000000?
superfury
l33t
 
Posts: 2282
Joined: 2014-3-08 @ 11:25
Location: Netherlands

Re: test386.asm CPU tester

Postby vladstamate » 2017-11-09 @ 19:43

I finally got CAPE to reach 0xE0 test step in test386.asm. Once I reach where you are now, I can compare our results.
User avatar
vladstamate
Oldbie
 
Posts: 801
Joined: 2015-8-23 @ 01:43

Re: test386.asm CPU tester

Postby superfury » 2017-11-09 @ 23:33

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:
Code: Select all
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((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:

Code: Select all
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;
      }
      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);
}
superfury
l33t
 
Posts: 2282
Joined: 2014-3-08 @ 11:25
Location: Netherlands

Re: test386.asm CPU tester

Postby superfury » 2017-11-10 @ 01:01

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?
superfury
l33t
 
Posts: 2282
Joined: 2014-3-08 @ 11:25
Location: Netherlands

Re: test386.asm CPU tester

Postby superfury » 2017-11-10 @ 01:28

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). :D The only bugs still left are (oddly enough) the carry flag and overflow flag not always matching up with an actual CPU?

porte9.log
POST EE log of UniPCemu's latest commit.
(2.57 MiB) Downloaded 3 times
superfury
l33t
 
Posts: 2282
Joined: 2014-3-08 @ 11:25
Location: Netherlands

Re: test386.asm CPU tester

Postby superfury » 2017-11-10 @ 09:17

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?
superfury
l33t
 
Posts: 2282
Joined: 2014-3-08 @ 11:25
Location: Netherlands

Re: test386.asm CPU tester

Postby Jepael » 2017-11-10 @ 13:32

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/why-does-unsigned-char0x80-24-get-sign-extended-to-0xffffffff80000000-64?noredirect=1&lq=1
Jepael
Oldbie
 
Posts: 1195
Joined: 2005-6-15 @ 19:28
Location: Finland

Re: test386.asm CPU tester

Postby superfury » 2017-11-10 @ 14:55

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?

porte9.log
Currently generated POST EE log.
(2.57 MiB) Downloaded 3 times


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_mod ... d_273.html
https://pdos.csail.mit.edu/6.828/2014/r ... 86/RCL.htm
http://x86.renejeschke.de/html/file_mod ... d_285.html

8-bit shifts/rotates:
Code: Select all
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;
      //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:
Code: Select all
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;
      //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:
Code: Select all
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+!
      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?
superfury
l33t
 
Posts: 2282
Joined: 2014-3-08 @ 11:25
Location: Netherlands

Re: test386.asm CPU tester

Postby peterferrie » 2017-11-10 @ 18:36

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).
peterferrie
Oldbie
 
Posts: 607
Joined: 2008-5-08 @ 21:54

Re: test386.asm CPU tester

Postby superfury » 2017-11-10 @ 19:05

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?
superfury
l33t
 
Posts: 2282
Joined: 2014-3-08 @ 11:25
Location: Netherlands

PreviousNext

Return to PC Emulation

Who is online

Users browsing this forum: istellabot [Bot] and 4 guests