Monday, 26 September 2016

Part 8: The Remaining instructions

Foreword

In the previous post we implemented the stack and related operations.

In this post we will be implementing the remaining 6502 instructions which are the following:
  • Logical instructions
  • Instructions for setting and clearing flags.
  • Transfer Instructions
  • The Bit instruction
  • Bit shifting instructions
For each of these instructions I will be giving a very brief description.

In this post I will not be giving a test program since we will anyway be running a comprehensive Test Suite on our emulator in the next post.

The Logical Instructions

The 6502 provides instructions for the following logical operations: AND, OR and XOR.

Let us quickly 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 & memory_read(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 ^ memory_read(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 | memory_read(effectiveAdrress);
              zeroFlag = (acc == 0) ? 1 : 0;
              negativeFlag = ((acc & 0x80) != 0) ? 1 : 0;
              break;

Setting clearing/flags

The 6502 provides a bunch of instructions for setting and clearing the flags.

Here is their implementation:

/*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 6502 provides you with a couple of instructions that enables you to transfer the contents of register to another register.

Here is their implementation:

/*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:
          xReg = acc;
              zeroFlag = (xReg == 0) ? 1 : 0;
              negativeFlag = ((xReg & 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:
          yReg = acc;
              zeroFlag = (yReg == 0) ? 1 : 0;
              negativeFlag = ((yReg & 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:
          xReg = sp;
              zeroFlag = (xReg == 0) ? 1 : 0;
              negativeFlag = ((xReg & 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 = xReg;
              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 = xReg;
              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 = yReg;
              zeroFlag = (acc == 0) ? 1 : 0;
              negativeFlag = ((acc & 0x80) != 0) ? 1 : 0;
              break;

The Bit Instruction

The Bit instruction provides you with a way to quickly test bits 6 and 7 of a memory location and then provide a subsequent branch instruction to branch on the result of the test.

Here is the implementation of the BIT instruction:

/*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:
              tempVal = memory_read(effectiveAdrress);
              negativeFlag = ((tempVal & 0x80) != 0) ? 1 : 0;
              overflowFlag = ((tempVal & 0x40) != 0) ? 1 : 0;
              zeroFlag = ((acc & tempVal) == 0) ? 1 : 0;
              break;

Bit Shifting Instructions

The 6502 provides you with a set of instructions allowing you to rotate the bits of the Accumulator or a particular memory location in a given direction.

Here is the implementation of the Bit Shifting 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:
             tempVal = memory_read(effectiveAdrress);
              tempVal = tempVal << 1;
              carryFlag = ((tempVal & 0x100) != 0) ? 1 : 0;
              tempVal = tempVal & 0xff;
              negativeFlag = ((tempVal & 0x80) != 0) ? 1 : 0;
              zeroFlag = (tempVal == 0) ? 1 : 0;
              memory_write(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:
             tempVal = memory_read(effectiveAdrress);
              carryFlag = ((tempVal & 0x1) != 0) ? 1 : 0;
              tempVal = tempVal >> 1;
              tempVal = tempVal & 0xff;
              zeroFlag = (tempVal == 0) ? 1 : 0;
              memory_write(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:
              tempVal = memory_read(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;
              memory_write(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:
              tempVal = memory_read(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;
              memory_write(effectiveAdrress, tempVal);
              break;

In Summary

In this post we have implemented the remaining 6502 Instruction.

In the next post we will putting our emulator through its paces with a Test Suite.

Till next time!

No comments:

Post a Comment