Tuesday, 10 May 2016

Part 7: The Remaining Instructions

Foreword

In the previous Blog I covered the Stack and related operations.

In this post I will cover the remaining instructions that needs to be implemented for our 6502 emulator.

So, we are almost done implementing the CPU part of our emulator! This is very exciting since blog posts after this one I will cover running running the Klaus Test Suite on the emulator and after that booting the C64 system with its ROM's.

Logical operations


The 6502 supports the following logical operations: AND, XOR and OR.

Lets implement them:

/*AND  AND Memory with Accumulator

     A AND M -> A                     N Z C I D V
                                      + + - - - -

     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     immediate     AND #oper     29    2     2
     zeropage      AND oper      25    2     3
     zeropage,X    AND oper,X    35    2     4
     absolute      AND oper      2D    3     4
     absolute,X    AND oper,X    3D    3     4*
     absolute,Y    AND oper,Y    39    3     4*
     (indirect,X)  AND (oper,X)  21    2     6
     (indirect),Y  AND (oper),Y  31    2     5* */


      case 0x29:  acc = acc & arg1;
          zeroflag = (acc == 0) ? 1 : 0;
          negativeflag = ((acc & 0x80) != 0) ? 1 : 0;
        break;
      case 0x25:
      case 0x35:
      case 0x2D:
      case 0x3D:
      case 0x39:
      case 0x21:
      case 0x31: acc = acc & localMem.readMem(effectiveAdrress);
          zeroflag = (acc == 0) ? 1 : 0;
          negativeflag = ((acc & 0x80) != 0) ? 1 : 0;
        break;



/*EOR  Exclusive-OR Memory with Accumulator

     A EOR M -> A                     N Z C I D V
                                      + + - - - -

     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     immediate     EOR #oper     49    2     2
     zeropage      EOR oper      45    2     3
     zeropage,X    EOR oper,X    55    2     4
     absolute      EOR oper      4D    3     4
     absolute,X    EOR oper,X    5D    3     4*
     absolute,Y    EOR oper,Y    59    3     4*
     (indirect,X)  EOR (oper,X)  41    2     6
     (indirect),Y  EOR (oper),Y  51    2     5* */

      case 0x49:  acc = acc ^ arg1;
          zeroflag = (acc == 0) ? 1 : 0;
          negativeflag = ((acc & 0x80) != 0) ? 1 : 0;
        break;
      case 0x45:
      case 0x55:
      case 0x4D:
      case 0x5D:
      case 0x59:
      case 0x41:
      case 0x51: acc = acc ^ localMem.readMem(effectiveAdrress);
          zeroflag = (acc == 0) ? 1 : 0;
          negativeflag = ((acc & 0x80) != 0) ? 1 : 0;
        break;


/*ORA  OR Memory with Accumulator

     A OR M -> A                      N Z C I D V
                                      + + - - - -

     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     immediate     ORA #oper     09    2     2
     zeropage      ORA oper      05    2     3
     zeropage,X    ORA oper,X    15    2     4
     absolute      ORA oper      0D    3     4
     absolute,X    ORA oper,X    1D    3     4*
     absolute,Y    ORA oper,Y    19    3     4*
     (indirect,X)  ORA (oper,X)  01    2     6
     (indirect),Y  ORA (oper),Y  11    2     5* */

      case 0x09:  acc = acc | arg1;
          zeroflag = (acc == 0) ? 1 : 0;
          negativeflag = ((acc & 0x80) != 0) ? 1 : 0;
        break;
      case 0x05:
      case 0x15:
      case 0x0D:
      case 0x1D:
      case 0x19:
      case 0x01:
      case 0x11: acc = acc | localMem.readMem(effectiveAdrress);
          zeroflag = (acc == 0) ? 1 : 0;
          negativeflag = ((acc & 0x80) != 0) ? 1 : 0;
        break;

Set and Clear Flag Instructions

The instructions for setting and clearing flags is as follows:

/*CLC  Clear Carry Flag

     0 -> C                           N Z C I D V
                                      - - 0 - - -

     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     implied       CLC           18    1     2 */

      case 0x18: 
          carryflag = 0;
        break;



/*CLV  Clear Overflow Flag

     0 -> V                           N Z C I D V
                                      - - - - - 0

     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     implied       CLV           B8    1     2 */

      case 0xB8: 
          overflowflag = 0;
        break;


/*SEC  Set Carry Flag

     1 -> C                           N Z C I D V
                                      - - 1 - - -

     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     implied       SEC           38    1     2 */

      case 0x38: 
          carryflag = 1;
        break;

Transfer Instructions


The implementation of the Transfer instructions is as follows:

/*TAX  Transfer Accumulator to Index X

     A -> X                           N Z C I D V
                                      + + - - - -

     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     implied       TAX           AA    1     2 */

      case 0xAA: 
          x = acc;
          zeroflag = (x == 0) ? 1 : 0;
          negativeflag = ((x & 0x80) != 0) ? 1 : 0;
        break;


/*TAY  Transfer Accumulator to Index Y

     A -> Y                           N Z C I D V
                                      + + - - - -

     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     implied       TAY           A8    1     2 */

      case 0xA8: 
          y = acc;
          zeroflag = (y == 0) ? 1 : 0;
          negativeflag = ((y & 0x80) != 0) ? 1 : 0;
        break;


/*TSX  Transfer Stack Pointer to Index X

     SP -> X                          N Z C I D V
                                      + + - - - -

     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     implied       TSX           BA    1     2 */

      case 0xBA: 
          x = sp;
          zeroflag = (x == 0) ? 1 : 0;
          negativeflag = ((x & 0x80) != 0) ? 1 : 0;
        break;



/*TXA  Transfer Index X to Accumulator

     X -> A                           N Z C I D V
                                      + + - - - -

     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     implied       TXA           8A    1     2 */

      case 0x8A: 
          acc = x;
          zeroflag = (acc == 0) ? 1 : 0;
          negativeflag = ((acc & 0x80) != 0) ? 1 : 0;
        break;


/*TXS  Transfer Index X to Stack Register

     X -> SP                          N Z C I D V
                                      + + - - - -

     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     implied       TXS           9A    1     2 */

      case 0x9A: 
          sp = x;
          zeroflag = (sp == 0) ? 1 : 0;
          negativeflag = ((sp & 0x80) != 0) ? 1 : 0;
        break;



/*TYA  Transfer Index Y to Accumulator

     Y -> A                           N Z C I D V
                                      + + - - - -

     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     implied       TYA           98    1     2 */

      case 0x98: 
          acc = y;
          zeroflag = (acc == 0) ? 1 : 0;
          negativeflag = ((acc & 0x80) != 0) ? 1 : 0;
        break;


The Bit Instruction

The BIT instruction can be described by the three flags it effects:

  • Negative flag: Gets set by the value of bit 7 of the contents at the effective memory address 
  • Overflow flag: Gets set by the value of bit 6 of the contents at the effective memory address 
  • Zero Flag: The result of Anding the accumulator and the memory location 
 Here is the implementation:

/*BIT  Test Bits in Memory with Accumulator

     bits 7 and 6 of operand are transfered to bit 7 and 6 of SR (N,V);
     the zeroflag is set to the result of operand AND accumulator.

     A AND M, M7 -> N, M6 -> V        N Z C I D V
                                     M7 + - - - M6

     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     zeropage      BIT oper      24    2     3
     absolute      BIT oper      2C    3     4 */

      case 0x24: 
      case 0x2C: 
          var tempVal = localMem.readMem(effectiveAdrress);
          negativeflag = ((tempVal & 0x80) != 0) ? 1 : 0;
          overflowflag = ((tempVal & 0x40) != 0) ? 1 : 0;
          zeroflag = ((acc & tempVal) == 0) ? 1 : 0;
        break;


Shifting Instructions

Here is the implementation of all the shift Instructions:

/*ASL  Shift Left One Bit (Memory or Accumulator)

     C <- [76543210] <- 0             N Z C I D V
                                      + + + - - -

     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     accumulator   ASL A         0A    1     2
     zeropage      ASL oper      06    2     5
     zeropage,X    ASL oper,X    16    2     6
     absolute      ASL oper      0E    3     6
     absolute,X    ASL oper,X    1E    3     7 */

      case 0x0A: 
          acc = acc << 1;
          carryflag = ((acc & 0x100) != 0) ? 1 : 0;
          acc = acc & 0xff;
          negativeflag = ((acc & 0x80) != 0) ? 1 : 0;
          zeroflag = (acc == 0) ? 1 : 0;
         break;
      case 0x06: 
      case 0x16: 
      case 0x0E: 
      case 0x1E: 
          var tempVal = localMem.readMem(effectiveAdrress);
          tempVal = tempVal << 1;
          carryflag = ((tempVal & 0x100) != 0) ? 1 : 0;
          tempVal = tempVal & 0xff;
          negativeflag = ((tempVal & 0x80) != 0) ? 1 : 0;
          zeroflag = (tempVal == 0) ? 1 : 0;
          localMem.writeMem(effectiveAdrress, tempVal);
        break;



/*LSR  Shift One Bit Right (Memory or Accumulator)

     0 -> [76543210] -> C             N Z C I D V
                                      - + + - - -

     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     accumulator   LSR A         4A    1     2
     zeropage      LSR oper      46    2     5
     zeropage,X    LSR oper,X    56    2     6
     absolute      LSR oper      4E    3     6
     absolute,X    LSR oper,X    5E    3     7 */

      case 0x4A: 
          carryflag = ((acc & 0x1) != 0) ? 1 : 0;
          acc = acc >> 1;
          acc = acc & 0xff;
          zeroflag = (acc == 0) ? 1 : 0;
        break;
      case 0x46: 
      case 0x56: 
      case 0x4E: 
      case 0x5E: 
          var tempVal = localMem.readMem(effectiveAdrress);
          carryflag = ((tempVal & 0x1) != 0) ? 1 : 0;
          tempVal = tempVal >> 1;
          tempVal = tempVal & 0xff;
          zeroflag = (tempVal == 0) ? 1 : 0;
          localMem.writeMem(effectiveAdrress, tempVal);
        break;


/*ROL  Rotate One Bit Left (Memory or Accumulator)

     C <- [76543210] <- C             N Z C I D V
                                      + + + - - -

     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     accumulator   ROL A         2A    1     2
     zeropage      ROL oper      26    2     5
     zeropage,X    ROL oper,X    36    2     6
     absolute      ROL oper      2E    3     6
     absolute,X    ROL oper,X    3E    3     7 */


      case 0x2A: 
          acc = acc << 1;
          acc = acc | carryflag;
          carryflag = ((acc & 0x100) != 0) ? 1 : 0;
          acc = acc & 0xff;
          zeroflag = (acc == 0) ? 1 : 0;
          negativeflag = ((acc & 0x80) != 0) ? 1 : 0;
      break;

      case 0x26: 
      case 0x36: 
      case 0x2E: 
      case 0x3E: 
          var tempVal = localMem.readMem(effectiveAdrress);
          tempVal = tempVal << 1;
          tempVal = tempVal | carryflag;
          carryflag = ((tempVal & 0x100) != 0) ? 1 : 0;
          tempVal = tempVal & 0xff;
          zeroflag = (tempVal == 0) ? 1 : 0;
          negativeflag = ((tempVal & 0x80) != 0) ? 1 : 0;
          localMem.writeMem(effectiveAdrress,tempVal);
      break;

/*ROR  Rotate One Bit Right (Memory or Accumulator)

     C -> [76543210] -> C             N Z C I D V
                                      + + + - - -

     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     accumulator   ROR A         6A    1     2
     zeropage      ROR oper      66    2     5
     zeropage,X    ROR oper,X    76    2     6
     absolute      ROR oper      6E    3     6
     absolute,X    ROR oper,X    7E    3     7  */

      case 0x6A: 
          acc = acc | (carryflag << 8);
          carryflag = ((acc & 0x1) != 0) ? 1 : 0;
          acc = acc >> 1;
          acc = acc & 0xff;
          zeroflag = (acc == 0) ? 1 : 0;
          negativeflag = ((acc & 0x80) != 0) ? 1 : 0;
      break;
      case 0x66: 
      case 0x76: 
      case 0x6E: 
      case 0x7E: 
          var tempVal = localMem.readMem(effectiveAdrress);
          tempVal = tempVal | (carryflag << 8);
          carryflag = ((tempVal & 0x1) != 0) ? 1 : 0;
          tempVal = tempVal >> 1;
          tempVal = tempVal & 0xff;
          zeroflag = (tempVal == 0) ? 1 : 0;
          negativeflag = ((tempVal & 0x80) != 0) ? 1 : 0;
          localMem.writeMem(effectiveAdrress, tempVal);
      break;


This concludes the development for this blog post.

I will not be writing a test program for this blog post since I will be running the Klaus Test Suite in my next Blog. I'm sure we will find a couple of bugs when running this Test Suite!

One Final thought

I am just about done with this Blog Post. We have implemented all instructions we planned for and are ready to run a Test Suite.

One thing that would be nice is to know if we missed an instruction. For this purpose I am adding the following default selector in our switch construct:

      default: alert("Op code "+opcode+" not implemented. PC = "+pc.toString(16));

So, if the switch statement doesn't find any case statements matching the given opcode, it will execute the default label, which will pop up a message.

In Summary

In this Blog we covered implementing the remaining instructions for our emulator.

In the next Post I will give a detailed account on what went right and what went wrong when I tried to run the Klaus Test Suite on the emulator.

Till Next Time!


No comments:

Post a Comment