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!

Sunday 25 September 2016

Part 7: The stack and related operations

Foreword

In the previous post we implemented comparison and branching instructions.

In this post we will be implementing the stack and related operations.

Introduction to the stack

You can view the stack as a kind of a Last-In-First-Out (LIFO) data structure.

A typical real world example of a stack is a spike with pieces of paper on it. It is always easier to get hold of the last piece of paper you pushed onto it, than the initial piece of paper you pushed onto it.

On the 6502 CPU you can use the stack for a couple of things. For instance, when you run the instruction JSR to jump to a subroutine, the 6502 will save the current address (to be more specific: the address of the last byte of the JSR instruction) on the stack. So, when you return from the subroutine with RTS, the 6502 will retrieve the saved address from the stack, so it can return to the address just after the JSR instruction you have just called.

You are also free to push your own data on the stack, like the current value of the Accumulator. Later on you can then restore the accumulator with the saved value from the stack, after doing some temporary operations with the Accumulator.

How is the stack implemented on the 6502? First of all, page 1 of memory is used solely for the stack storage area (e.g. address 0x100-0x1ff). This means that the 6502 stack has a limit of 256 bytes.

Secondly, it is important to note that the stack grows downwards on the 6502. That is, it starts at address 0x1ff and as you push stuff on the stack, it will grow towards address 0x100.

How does the 6502 keeps track of the current position on the stack? The 6502 actually have a register for this purpose, called the Stack pointer, or SP for short. The initial value of this register is 0xff which in effect point to memory address 0x1ff, e.g. the preceding 1 of the resulting address is implied. So, every time you push a byte on the stack, the Stack Pointer get decremented by one. Likewise, when you pop a byte from the stack, the Stack Pointer gets incremented by one.

Implementing the Stack

It it time to implement the stack within our emulator.

First of all we need to add the stack pointer as a variable to cpu.c:

    jchar sp = 0xff;

As you can see, we initialise the stackpointer with 0xff since the stack starts at 0x1ff and then grow downwards to 0x100.

Next, we need to define two atomic stack operation methods: Push and Pop. All 6502 instructions manipulating the stack within our emulator, will need to call these methods.

Here is the implementation for Push and Pop:

  void Push(char value) {
    memory_write((sp | 0x100), value);
    sp--;
    sp = sp & 0xff;
  }

  char Pop() {
    sp++;
    sp = sp & 0xff;
    char result = memory_read(sp | 0x100);
    return result;
  }

For the Push method you supply the value you want to push onto the stack. The value gets written to memory in the stack area and the stack pointer updated accordingly.

The Pop method read from memory the value on top of the stack and return it, updating the stackpointer afterwards.

Implementing 6502 Push and Pop instructions

As mentioned earlier, the 6502 provides you with instructions to directly Push and Pop values from the stack. These direct Push and Pop instructions can only be performed on two sources: The Accumulator and the Status Register.

Let us start to implement the Push and Pop instructions for the Accumulator:

/*PHA  Push Accumulator on Stack
     push A                           N Z C I D V
                                      - - - - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     implied       PHA           48    1     3 */

      case 0x48:
        Push(acc);
      break;


/*PLA  Pull Accumulator from Stack
     pull A                           N Z C I D V
                                      + + - - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     implied       PLA           68    1     4 */

      case 0x68:
        acc = Pop();
        zeroFlag = (acc == 0) ? 1 : 0;
        negativeFlag = ((acc & 0x80) != 0) ? 1 : 0;
      break;

Next, let us implement the Push and Pop instructions for the Status Register.

I briefly touched the service of the Status Register in the previous post, mentioning that each flag is represented by particular bit within the Status Register. This is a bit of a contrast in how we currently have implemented flags within our emulator: Currently we do store all the flags within a single byte, but each flag has its own variable.

In order to implement the instructions for Pushing and Popping the Status register, we need to create the following methods to collate the separate flag variables into a single byte and vice-versa:

  char getStatusFlagsAsByte() {
    char result = (negativeFlag << 7) | (overflowFlag << 6) | (zeroFlag << 1) |
    (carryFlag);
    return result;
  }

  void setStatusFlagsAsByte(char value) {
    negativeFlag = (value >> 7) & 1;
    overflowFlag = (value >> 6) & 1;
    zeroFlag = (value >> 1) & 1;
    carryFlag = (value) & 1;
  }

For now I am only working with a subset of the flags. We will move to a complete implementation when put our emulator through a Test Suite.

With all this implemented, we can now implement the Push and Pop instructions for the Status Register:

/*PHP  Push Processor Status on Stack
     push SR                          N Z C I D V
                                      - - - - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     implied       PHP           08    1     3 */

      case 0x08:
        Push(getStatusFlagsAsByte());
      break;

/*PLP  Pull Processor Status from Stack
     pull SR                          N Z C I D V
                                      from stack
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     implied       PHP           28    1     4 */

      case 0x28:
        setStatusFlagsAsByte(Pop());
      break;

Implementing JSR and RTS

Let us now implement the JSR and RTS instructions:

/*JSR  Jump to New Location Saving Return Address
     push (PC+2),                     N Z C I D V
     (PC+1) -> PCL                    - - - - - -
     (PC+2) -> PCH
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     absolute      JSR oper      20    3     6 */

      case 0x20:
        tempVal = pc - 1;
        Push((tempVal >> 8) & 0xff);
        Push(tempVal & 0xff);
        pc = effectiveAdrress;
      break;

/*RTS  Return from Subroutine
     pull PC, PC+1 -> PC              N Z C I D V
                                      - - - - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     implied       RTS           60    1     6 */

      case 0x60:
        tempVal = Pop();
        tempVal = tempVal + Pop() * 256;
        pc = tempVal + 1;
      break;

Maybe worthwhile to mention that with JSR instruction, the 6502 pushes the address of the third byte of the JSR instruction on the stack. At the point when the push happens, the pc register within our emulator points to the next instruction. For that reason we subract one pc and pushes the result when we execute a JSR instruction.

Testing

Time to test the code we wrote for emulator in this post.

Again, we will be using a test program:

0000 LDA #$52 A9 52
0002 PHA      48
0003 LDA #$07 A9 07
0005 JSR $000A 20 0A 00
0008 PLA       68
0009 00        00
000A SBC #$06  E9 06
000C RTS       60

This program will translate to the following array declaration within memory .c:

jchar my_program[] = {0xA9, 0x52,
                      0x48,
                      0xA9, 0x07,
                      0x20, 0x0A, 0x00,
                      0x68,
                      0x00,
                      0xE9, 0x06,
                      0x60 };

Before we jump in and run the test program, I just would like to focus on the fact we have added a new register: The Stack Register. It would rather be nice if we could also show the contents of this register while stepping through the program.

So, let us quickly adjust the GUI. First thing to do is to add a method to cpu.c so MainActivity can access the Stack Pointer:

jchar Java_com_johan_emulator_MainActivity_getSP(JNIEnv* pEnv, jobject pObj)
{
  return sp;
}


Similarly, we need to add a stub for this method to MainActivity:

public native char getSP();

Before we continue, we need to adjust the getRegisterDump() method to accept the StackPointer value as a parameter:

    public String getRegisterDump(char acc, char xReg, char yReg, char SP) {
        String accStr = "00"+Integer.toHexString(acc);
        accStr = accStr.substring(accStr.length() - 2);

        String xRegStr = "00"+Integer.toHexString(xReg);
        xRegStr = xRegStr.substring(xRegStr.length() - 2);

        String yRegStr = "00"+Integer.toHexString(yReg);
        yRegStr = yRegStr.substring(yRegStr.length() - 2);

        String spStr = "00"+Integer.toHexString(SP);
        spStr = spStr.substring(spStr.length() - 2);

        return accStr + xRegStr + yRegStr + spStr;

    }


And finally we modify refreshControls to the native getSP() method and pass it as a parameter to getRegisterDump:

...
        TextView viewReg = (TextView) findViewById(R.id.Registers);
        viewReg.setText(getRegisterDump(getAcc(), getXreg(), getYreg(), getSP()));
...

It is is now time to build our Android Project again and run it on an Android device.

When stepping through this application, we are particularly interested in keeping an eye on the stack to see how our application behaves.So, before we start to step, specify 1f0 as the memory dump address. If you now click on Refresh Dump, our screen will look as follows:


I have indicated in red the new register, which is the Stack Pointer.

If you now click STEP, the LDA instruction will execute. The next instruction in line to execute, is PHA. When this instruction executes, some interesting stuff will happen on the stack. So, once this instruction has executed, the screen will look as follows:


A couple of things has happened here. First, the Stack Pointer has been reduced by one. You will also see, the value 52 has been pushed onto the stack, which happens to be at memory location 0x1ff.

Click STEP once more to execute the LDA instruction as in the above screen.

The next interesting to execute is JSR $000A. So hit STEP once more, so we can see what happens:


You will see something interesting has happened again on the stack. The value 0 was pushed, and then the value 7. This is the return address. Talking of return address, one might have expected that the second value pushed should have been an 8 instead of a 7. Remember, however that the JSR pushes the address of byte three of the JSR instruction.

One further instruction gets executed, then we get to RTS. When this instruction executes, our emulator jumps to the memory location just after the preceding JSR instruction we executed a while ago:


If we now execute this PLA instruction, our Accumulator will be restored to 52, the value we previously pushed onto the stack.

In Summary

In this post we have implemented the stack and related operations into our emulator.

In the next post we will implement the remaining 6502 instructions into our emulator, after which we will be ready to run a Test Suite on our emulator.

Till next time!

Friday 23 September 2016

Part 6: Comparisons and Branching

Foreword

In the previous post we covered the ADC and SBC instructions and implemented it within our emulator.

In this post we will be covering the Comparison and Branching instructions and implement it within our emulator.

Comparison and branching is the basic functionality that is equivalent to if-then statements in a High Level language.

Up to this point in time we had just been adding assembly instructions to our emulator. So, we haven't really been doing exciting stuff at least stretching the capabilities of Android. So, the question might be when will we be doing some more exciting stuff in our emulator?

The answer is after a about two more posts. After this post, I plan to do two further post in which our all the instructions will be implemented within our emulator. The remaining two posts will basically cover the stack and related operations, and finally all other instructions.

Once we have implemented all instructions the real fun will start to begin. We will need to run a Test Suite to test that we have implemented all the instructions correctly. Obviously the Test Suite will be a big bulk of instructions to execute, so single stepping wouldn't do. Running the Test Suite will therefore be the first time we will let our emulator run on its own. Making the GUI responsive during this run, will definitely be fun!

But enough of the future. Let us start with comparisons and branching...

Display order of Flags

Before we start, I just want to mention something from my previous post.

BigEd from 6502.org actually make a suggestion regarding the display of the flags.

In a sense, each flag is represented by an appropriate bit within a byte called the status register.

BigEd's suggestion basically was that the flags should be listed in the same order (e.g. Most significant bit to least significant bit) as they appear in this register.

This just makes debugging a bit easier later on. To change the display order of the flags, we just need to modify the method within MainActivity:

  public String getFlagDump() {
        String result ="";
        result = result + ((getNegativeFlag() == 1) ? "N" : "-");
        result = result + ((getOverflowFlag() == 1) ? "V" : "-");
        result = result + "-";
        result = result + "B";
        result = result + ((getDecimalFlag() == 1) ? "D" : "-");
        result = result + ((getInterruptFlag() == 1) ? "I" : "-");
        result = result + ((getZeroFlag() == 1) ? "Z" : "-");
        result = result + ((getCarryFlag() == 1) ? "C" : "-");

        return result;
    }


This will show the status of all eight bits in  the status register. You will notice that we hard code two bit positions. First of all, bit 5 of the status register is never used, so for this bit position we will always show a minus (-).

Secondly you will see that we hardcode the letter B for bit 4 of the status register. This is called the break flag. We will only implement functionality for setting and clearing this flag in later posts. For now we will just default it to B (This flag is set like 90% of the time).

Coding the Comparison Instructions

On the 6502 you use the comparison instructions to test for equality between two numbers. This equality relates to the following operators in a High Level Language: Less than, Greater than and equal to.

In each compare instruction the first number is implied by the Mnemonic (e.g. With CMP first operand is the accumulator, with CPX first operand is the X-Register and with CPY first operand is the Y-Register). The second number for the comparison is supplied by the operand.

The 6502 achieves the comparison by subtracting the two numbers and then setting the applicable flags. By inspecting the flags after the comparison you would be able to tell what the result of the comparison was.

The following table, as taken from http://www.6502.org, gives an idea how the different flags will be effected depending on the equality result:

Compare Result NZC
A, X, or Y < Memory *00
A, X, or Y = Memory 011
A, X, or Y > Memory *01
* The N flag will be bit 7 of A, X, or Y - Memory

It is important to note that a Compare instruction doesn't alter the contents of a register or memory in any way. A Compare instruction just effects the flags.

Having said all this, let us start to code the compare instructions into out emulator.

First we need to create a method that will do the work of the comparing for us:

  void CMP(int operand1, int operand2) {
    operand2 = ~operand2 & 0xff;
    operand2 = operand2 + 1;
    int temp = operand1 + operand2;
    carryFlag = ((temp & 0x100) == 0x100) ? 1 : 0;
    temp = temp & 0xff;
    zeroFlag = (temp == 0) ? 1 : 0;
    negativeFlag = ((temp & 0x80) != 0) ? 1 : 0;
  }

This is a generic method that we can use for all the compare instruction. You just can this method with the two number to be compared as parameters. The method starts off by changing the second number into twos complement form. This is just a form that enables you to do subtraction via addition. We could have potentially just have used the minus operator that C provides us, and C would have taken care of all the twos complement stuff under cover. However, this introduces some difficulties like the fact that you cannot get hold of the carry. So, manual two's complement it will be.

Let us implement all the compare instructions now within the instruction case statement in cpu.c:

/*CMP  Compare Memory with Accumulator
     A - M                            N Z C I D V
                                    + + + - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     immediate     CMP #oper     C9    2     2
     zeropage      CMP oper      C5    2     3
     zeropage,X    CMP oper,X    D5    2     4
     absolute      CMP oper      CD    3     4
     absolute,X    CMP oper,X    DD    3     4*
     absolute,Y    CMP oper,Y    D9    3     4*
     (indirect,X)  CMP (oper,X)  C1    2     6
     (indirect),Y  CMP (oper),Y  D1    2     5* */

        case 0xc9:
          CMP(acc, arg1);
              break;
        case 0xc5:
        case 0xd5:
        case 0xcd:
        case 0xdD:
        case 0xd9:
        case 0xc1:
        case 0xd1:
          CMP(acc, memory_read(effectiveAdrress));
              break;

/*CPX  Compare Memory and Index X
     X - M                            N Z C I D V
                                      + + + - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     immediate     CPX #oper     E0    2     2
     zeropage      CPX oper      E4    2     3
     absolute      CPX oper      EC    3     4
*/

        case 0xe0:
          CMP(xReg, arg1);
              break;
        case 0xe4:
        case 0xec:
          CMP(xReg, memory_read(effectiveAdrress));
              break;

/*CPY  Compare Memory and Index Y
     Y - M                            N Z C I D V
                                      + + + - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     immediate     CPY #oper     C0    2     2
     zeropage      CPY oper      C4    2     3
     absolute      CPY oper      CC    3     4*/

        case 0xc0:
          CMP(yReg, arg1);
              break;
        case 0xc4:
        case 0xcc:
          CMP(yReg, memory_read(effectiveAdrress));
              break;

As you can see all variants of the Compare instruction we just invoke the CMP with applicable parameters.

Implementing the branch instructions

In the previous section we have implemented the Compare instructions. On its own Compare instructions is not that useful. If we look again at an if-then statement in a High Level language, we see that an if-then statement does two things: Do the comparison, and then branch to the applicable block of code depending on the result of the comparison. And this what we are missing within our current emulator: Branching instructions.

The 6502 provide us with a couple of branching instructions, branching on the state of a particular flag.

Take for example the instruction BCS (e.g. Branch on Carry set). This instruction will branch to the supplied address if the carry flag is set. The inverse of the BCS instruction also exist: BCC (Branch if carry clear). The BCC instruction will branch to the supplied address if the Carry flag is cleared.

There is similar instructions for most of the flags, as you will see in a moment.

There is something special about the address you supply with a branch instruction. The address you specify with a branch instruction is not a absolute address, but a relative one.

What is a relative address? A relative address is in fact an offset you need to add to the current value of the program counter to get to an actual address that we should jump to. A special note: The address of the next instruction after the current branch instruction is the address that will be used in the addition.

This offset address is a single byte. What is special about this offset is that it allows you to jump in a forward direction, as well as in a reverse direction. The reverse direction is indicated by the use of twos complement. The use of two's complement limits the jumping range as follows: -128...127.

We haven't implemented the relative address mode yet within the calculateEffevtiveAdd method, so let us add a case statement to the this method's switch statement:

   case ADDRESS_MODE_RELATIVE:
        tempAddress = (argbyte1 > 127) ? (argbyte1 - 256) : argbyte1;
        tempAddress = tempAddress + pc;
        return tempAddress;
      break;

We can now implement the different branch instructions:

/*BCC  Branch on Carry Clear
     branch on C = 0                  N Z C I D V
                                      - - - - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     relative      BCC oper      90    2     2** */

        case 0x90:
          if (carryFlag == 0)
            pc = effectiveAdrress;
              break;


/*BCS  Branch on Carry Set
     branch on C = 1                  N Z C I D V
                                      - - - - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     relative      BCS oper      B0    2     2** */

        case 0xB0:
          if (carryFlag == 1)
            pc = effectiveAdrress;
              break;


/*BEQ  Branch on Result Zero
     branch on Z = 1                  N Z C I D V
                                      - - - - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     relative      BEQ oper      F0    2     2** */

        case 0xF0:
          if (zeroFlag == 1)
            pc = effectiveAdrress;
              break;



/*BMI  Branch on Result Minus
     branch on N = 1                  N Z C I D V
                                      - - - - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     relative      BMI oper      30    2     2** */

        case 0x30:
          if (negativeFlag == 1)
            pc = effectiveAdrress;
              break;


/*BNE  Branch on Result not Zero
     branch on Z = 0                  N Z C I D V
                                      - - - - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     relative      BNE oper      D0    2     2**/

        case 0xD0:
          if (zeroFlag == 0)
            pc = effectiveAdrress;
              break;



/*BPL  Branch on Result Plus
     branch on N = 0                  N Z C I D V
                                      - - - - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     relative      BPL oper      10    2     2** */

        case 0x10:
          if (negativeFlag == 0)
            pc = effectiveAdrress;
              break;



/*BVC  Branch on Overflow Clear
     branch on V = 0                  N Z C I D V
                                      - - - - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     relative      BVC oper      50    2     2** */

        case 0x50:
          if (overflowFlag == 0)
            pc = effectiveAdrress;
              break;

/*BVS  Branch on Overflow Set
     branch on V = 1                  N Z C I D V
                                      - - - - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     relative      BVC oper      70    2     2** */

        case 0x70:
          if (overflowFlag == 1)
            pc = effectiveAdrress;
              break;

/*JMP  Jump to New Location
     (PC+1) -> PCL                    N Z C I D V
     (PC+2) -> PCH                    - - - - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     absolute      JMP oper      4C    3     3
     indirect      JMP (oper)    6C    3     5 */

        case 0x4C:
        case 0x6C:
          pc = effectiveAdrress;
              break;

The comments makes this code fairly self explanatory.

You will notice that I have also added the jump instruction. Although not strictly speaking a branch instruction as will always do a jump regardless if a certain condition is met or not. Also take note of the address modes available for jump. Jump doesn't have a relative mode instruction, but absolute and indirect.

Testing

Time to do some testing.

We will again use a test program from my previous JavaScript emulator series, Part 5 : Comparisons and Branching

For convenience I am showing the assembly version of the program:

0000 LDA #$FB A9 FB
0002 CMP #$05 C9 05
0004 LDA #$05 A9 05
0006 CMP #$FB C9 FB
0008 LDA #$E2 A9 E2
000A CMP #$64 C9 64
000C LDX #$40 A2 40
000E DEX          CA
000F CPX #$3A  E0 3A
0011 BNE $000E D0 FB
0013 LDY #$10 A0 10
0015 CPY #$23 C0 23
0017 BCS $001D B0 04
0019 INY              C8
001A JMP #$0015 4C 15 00
001D 00

Here is the array declaration of the program:

jchar my_program[] = {0xA9, 0xFB, 0xC9, 0x05, 0xA9, 0x05, 0xC9, 0xFB, 0xA9,
                      0xE2, 0xC9, 0x64, 0xA2, 0x40, 0xCA, 0xE0, 0x3A, 0xD0,
                      0xFB, 0xA0, 0x10, 0xC0, 0x23, 0xB0, 0x04, 0xC8, 0x4C,
                      0x15, 0x00, 0x00 };

The program starts off by doing a couple of comparisons. One can check the flags to see if the comparisons gives the expected results.

Finally the program will do two loops, looping a fixed number of times.

In Summary

In this post we implemented all variants of the Comapre and branching instructions.

As mentioned at the beginning of this post, we have about two posts left in order to implement all the instructions within our emulator, after which the real fun will begin.

In the next post we will be covering the Stack and related operations.

Till next time!

Tuesday 13 September 2016

Part 5: Adding ADC and SBC instructions

Foreword

In the previous post we implemented all the variants of the LOAD and STORE instructions.

In this post we will be doing the same with the ADC and SBC instructions (e.g. Add with Carry and Subtract with carry).

We will again be working from one of my previous JavScript emulator posts. This time we will be using Part 4: The ADC and SBC instuctions

We will also be extending the front end of our emulator to show the following state:

  • State of the registers
  • State of the flags
  • Disassembled version of next instruction to be executed

Viewing complete emulator state

Up to this point in time, the only way to see what is going on within our emulator, is via the memory dump. This only gives us a partial view on what is going on within our emulator.

It would rather be nice if we could see the following as well as part of the emulator state:
  • The Accumulator, X-Register and Y register
  • The Flags
  • A disassembled version of the next instruction to executed.
In this section we will change our application so that the above mentioned will also show as part of the state visible to the user.

Changes to the front end

We start by doing some GUI changes for our application.

As you might remember, the GUI of application has a GridLayout. This means that every visible component has an explicit layout_row and a layout_column property.

When adding extra visual components to this GUI it is important to keep an eye on these two properties to ensure the visual components gets displayed in the correct order.

With these changes in mind open up content_main.xml and adjust it so that it looks as follows:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.johan.emulator.MainActivity"
    android:background="#000000"
    tools:showIn="@layout/activity_main">

    <GridLayout
        android:layout_width="match_parent"
        android:layout_height="fill_parent"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true"
        android:columnCount="2"
        android:rowCount="9">
    <EditText
        android:id="@+id/inputSearchEditText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"

        android:ellipsize="start"

        android:hint="Address"
        android:inputType="text"
        android:background="@drawable/edittextstyle"
        android:layout_row="0"
        android:layout_column="0"/>

     <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Refresh Dump"
        android:id="@+id/button"
        android:layout_row="0"
        android:minHeight="0dp"

        android:layout_column="1"
        android:onClick="onClick" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:textColor="#00ff00"
            android:text="00000000"
            android:id="@+id/Registers"
            android:layout_row="1"
            android:layout_column="0" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:textColor="#00ff00"
            android:text="------"
            android:id="@+id/Flags"
            android:layout_row="1"
            android:layout_column="1" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:text=""
            android:id="@+id/instruction"
            android:layout_columnSpan="2"
            android:textColor="#00ff00"
            android:layout_row="2"
            android:layout_column="0" />


        <!--android:minHeight="0dp"-->



        <Space android:layout_height="10dp"
        android:layout_width="wrap_content"
        android:layout_row="3"
        android:layout_column="0"

        />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="@string/welcomestring"
        android:id="@+id/memoryDump"
        android:background="@drawable/textview_border"

        android:lines="10"
        android:typeface="monospace"
        android:textColor="#00ff00"
        android:textSize="12dp"
        android:layout_row="4"
        android:layout_column="0"
        android:layout_columnSpan="2" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="STEP"
            android:id="@+id/button2"
            android:layout_row="5"
            android:layout_column="0"
            android:onClick="onStepClick" />


    </GridLayout>
    <![CDATA[


    TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="Large Text"
        android:id="@+id/textView2"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_above="@+id/textView" />

    TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="New Text"
        android:id="@+id/textView3"
        android:layout_above="@+id/textView2"
        android:layout_centerHorizontal="true"
        android:editable="true"
        android:lines="2" />
]]>
</RelativeLayout>


In this snippet I have highlighted the new visual elements to be added. Please take note that I have adjusted the layout_row and a layout_column properties of the surrounding elements as well, though not highlighted.

With these changes applied, our GUI will look as follow within the Design View of AndroidStudio:


The zero's will show the content of the registers, two hex digits per register. The first two hex digits the Accumulator, the next two the X-Register and the next two the Y-Register.

Next to the set of zero's, you will a set of dashes (-). These dashes will indicate the state of the flags. Each position correspond to a specific flag. The flags will be displayed in the following order:
  • Negative (N)
  • Zero (Z)
  • Carry (C)
  • Interrupt (I)
  • Decimal (D)
  • Overflow (V)
In the above list I have indicted within brackets the character that will show if the corresponding flag is set. The basic idea is that if a flag is set, the specific character will show, otherwise just a dash (-) will show.

So, for example, if the Carry flag and Negative flag is set, the following string will display: N-C---

The last visual element we added was one showing a disassembled version of the next instruction to be executed. This element will be shown just below above mentioned two elements. I haven't specified default text to display within content_main.xml. So, within the AndroidStudio designer view this element will be shown as a blank line.

Coding behind the register view

Let us now start to add the code behind the register view.

Ultimately the value of the registers lives in cpu.c, so we need to add some native methods to this file that MainActivity can call to get to the value of the registers.

So, within cpu.c, add the following methods:

jchar Java_com_johan_emulator_MainActivity_getAcc(JNIEnv* pEnv, jobject pObj)
{
  return acc;
}

jchar Java_com_johan_emulator_MainActivity_getXreg(JNIEnv* pEnv, jobject pObj)
{
  return xReg;
}

jchar Java_com_johan_emulator_MainActivity_getYreg(JNIEnv* pEnv, jobject pObj)
{
  return yReg;
}


Now, within MainActivity add matching stubs:

    public native char getAcc();
    public native char getXreg();
    public native char getYreg();

Next, also within MainActivity, we create the following method:

    public String getRegisterDump(char acc, char xReg, char yReg) {
        String accStr = "00"+Integer.toHexString(acc);
        accStr = accStr.substring(accStr.length() - 2);

        String xRegStr = "00"+Integer.toHexString(xReg);
        xRegStr = xRegStr.substring(xRegStr.length() - 2);

        String yRegStr = "00"+Integer.toHexString(yReg);
        yRegStr = yRegStr.substring(yRegStr.length() - 2);

        return accStr + xRegStr + yRegStr;

    }


This method takes as parameters the contents of the registers and returns a formatted string.

Now, we need to adjust the refreshControls method to call getRegisterDump:

    private void refreshControls() {

        char[] memDump = dump();

        TextView view = (TextView) findViewById(R.id.memoryDump);
        view.setText(getMemDumpAsString(memDump));

        TextView viewReg = (TextView) findViewById(R.id.Registers);
        viewReg.setText(getRegisterDump(getAcc(), getXreg(), getYreg()));

    }


As you can see we follow more or less the same convention as with memoryDump to get the visual element and setting its text. In setText we invoke getRegisterDump. The parameters of getRegisterDump are calls to our native methods getAcc(), getXreg() and getYreg().


All this code will take care of displaying the contents of the registers during stepping.

Coding behind the flags view

Next, let us write the code for populating the flags view.

Also, as with the registers view, we need to surface the values of the flags in cpu.c with native methods:

jchar Java_com_johan_emulator_MainActivity_getZeroFlag(JNIEnv* pEnv, jobject pObj)
{
  return zeroFlag;
}

jchar Java_com_johan_emulator_MainActivity_getNegativeFlag(JNIEnv* pEnv, jobject pObj)
{
  return negativeFlag;
}

jchar Java_com_johan_emulator_MainActivity_getCarryFlag(JNIEnv* pEnv, jobject pObj)
{
  return carryFlag;
}

jchar Java_com_johan_emulator_MainActivity_getInterruptFlag(JNIEnv* pEnv, jobject pObj)
{
  return 0;
}

jchar Java_com_johan_emulator_MainActivity_getDecimalFlag(JNIEnv* pEnv, jobject pObj)
{
  return 0;
}

jchar Java_com_johan_emulator_MainActivity_getOverflowFlag(JNIEnv* pEnv, jobject pObj)
{
  return overflowFlag;
}


You might pick up that for the Interrupt flag and the Decimal flag we are returning just a zero. This is because we haven't implemented any logic for these flags yet, so we we just added the methods for these flags so long as a place holder.

Also, we need to create native stubs for above methods within MainActivity:

    public native char getZeroFlag();
    public native char getNegativeFlag();
    public native char getCarryFlag();
    public native char getInterruptFlag();
    public native char getDecimalFlag();
    public native char getOverflowFlag();


Next, we should also create a dump method for returning the contents of the flags as a formatted string:

    public String getFlagDump() {
        String result ="";
        result = result + ((getNegativeFlag() == 1) ? "N" : "-");
        result = result + ((getZeroFlag() == 1) ? "Z" : "-");
        result = result + ((getCarryFlag() == 1) ? "C" : "-");
        result = result + ((getInterruptFlag() == 1) ? "I" : "-");
        result = result + ((getDecimalFlag() == 1) ? "N" : "-");
        result = result + ((getOverflowFlag() == 1) ? "V" : "-");

        return result;
    }


In this case we are calling the native content methods from within the method, instead of passing them as parameters.

Finally, we also need to invoke this method within refreshControls:

    private void refreshControls() {

        char[] memDump = dump();

        TextView view = (TextView) findViewById(R.id.memoryDump);
        view.setText(getMemDumpAsString(memDump));

        TextView viewReg = (TextView) findViewById(R.id.Registers);
        viewReg.setText(getRegisterDump(getAcc(), getXreg(), getYreg()));

        TextView viewFlags = (TextView) findViewById(R.id.Flags);
        viewFlags.setText(getFlagDump());

    }


Coding behind Disassembled Instruction View

The final view we will coding for this post, the next disassembled Instruction view.

The code around this view will have a lot more meat than the code behind the previous two views. The reason for this will become very clear as you read through this subsection.

First of all, in order for this view to do its work, we will need to add some lookup tables to MainActivity, some of which are a duplicate from cpu.c:

    private static final int ADDRESS_MODE_ACCUMULATOR = 0;
    private static final int ADDRESS_MODE_ABSOLUTE = 1;
    private static final int ADDRESS_MODE_ABSOLUTE_X_INDEXED = 2;
    private static final int ADDRESS_MODE_ABSOLUTE_Y_INDEXED = 3;
    private static final int ADDRESS_MODE_IMMEDIATE = 4;
    private static final int ADDRESS_MODE_IMPLIED = 5;
    private static final int ADDRESS_MODE_INDIRECT = 6;
    private static final int ADDRESS_MODE_X_INDEXED_INDIRECT = 7;
    private static final int ADDRESS_MODE_INDIRECT_Y_INDEXED = 8;
    private static final int ADDRESS_MODE_RELATIVE = 9;
    private static final int ADDRESS_MODE_ZERO_PAGE = 10;
    private static final int ADDRESS_MODE_ZERO_PAGE_X_INDEXED = 11;
    private static final int ADDRESS_MODE_ZERO_PAGE_Y_INDEXED = 12;

    public static final int[] ADDRESS_MODES = {
            5, 7, 0, 0, 0, 10, 10, 0, 5, 4, 0, 0, 0, 1, 1, 0,
            9, 8, 0, 0, 0, 11, 11, 0, 5, 3, 0, 0, 0, 2, 2, 0,
            1, 7, 0, 0, 10, 10, 10, 0, 5, 4, 0, 0, 1, 1, 1, 0,
            9, 8, 0, 0, 0, 11, 11, 0, 5, 3, 0, 0, 0, 2, 2, 0,
            5, 7, 0, 0, 0, 10, 10, 0, 5, 4, 0, 0, 1, 1, 1, 0,
            9, 8, 0, 0, 0, 11, 11, 0, 5, 3, 0, 0, 0, 2, 2, 0,
            5, 7, 0, 0, 0, 10, 10, 0, 5, 4, 0, 0, 6, 1, 1, 0,
            9, 8, 0, 0, 0, 11, 11, 0, 5, 3, 0, 0, 0, 2, 2, 0,
            0, 7, 0, 0, 10, 10, 10, 0, 5, 0, 5, 0, 1, 1, 1, 0,
            9, 8, 0, 0, 11, 11, 12, 0, 5, 3, 5, 0, 0, 2, 0, 0,
            4, 7, 4, 0, 10, 10, 10, 0, 5, 4, 5, 0, 1, 1, 1, 0,
            9, 8, 0, 0, 11, 11, 12, 0, 5, 3, 5, 0, 2, 2, 3, 0,
            4, 7, 0, 0, 10, 10, 10, 0, 5, 4, 5, 0, 1, 1, 1, 0,
            9, 8, 0, 0, 0, 11, 11, 0, 5, 3, 0, 0, 0, 2, 2, 0,
            4, 7, 0, 0, 10, 10, 10, 0, 5, 4, 5, 0, 1, 1, 1, 0,
            9, 8, 0, 0, 0, 11, 11, 0, 5, 3, 0, 0, 0, 2, 2, 0
    };

    public static final int[] INSTRUCTION_LEN = {
            1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 0, 3, 3, 0,
            2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
            3, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
            2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
            1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
            2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
            1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
            2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
            0, 2, 0, 0, 2, 2, 2, 0, 1, 0, 1, 0, 3, 3, 3, 0,
            2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 0, 3, 0, 0,
            2, 2, 2, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
            2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0,
            2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
            2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
            2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
            2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0
    };

    public static final String[] opCodeDesc =
            {"BRK", "ORA", "", "", "", "ORA", "ASL", "", "PHP", "ORA", "ASL", "", "", "ORA", "ASL", "",
            "BPL", "ORA", "", "", "", "ORA", "ASL", "", "CLC", "ORA", "", "", "", "ORA", "ASL", "",
            "JSR", "AND", "", "", "BIT", "AND", "ROL", "", "PLP", "AND", "ROL", "", "BIT", "AND", "ROL", "",
            "BMI", "AND", "", "", "", "AND", "ROL", "", "SEC", "AND", "", "", "", "AND", "ROL", "",
            "RTI", "EOR", "", "", "", "EOR", "LSR", "", "PHA", "EOR", "LSR", "", "JMP", "EOR", "LSR", "",
            "BVC", "EOR", "", "", "", "EOR", "LSR", "", "CLI", "EOR", "", "", "", "EOR", "LSR", "",
            "RTS", "ADC", "", "", "", "ADC", "ROR", "", "PLA", "ADC", "ROR", "", "JMP", "ADC", "ROR", "",
            "BVC", "ADC", "", "", "", "ADC", "ROR", "", "SEI", "ADC", "", "", "", "ADC", "ROR", "",
            "", "STA", "", "", "STY", "STA", "STX", "", "DEY", "", "TXA", "", "STY", "STA", "STX", "",
            "BCC", "STA", "", "", "STY", "STA", "STX", "", "TYA", "STA", "TXS", "", "", "STA", "", "",
            "LDY", "LDA", "LDX", "", "LDY", "LDA", "LDX", "", "TAY", "LDA", "TAX", "", "LDY", "LDA", "LDX", "",
            "BCS", "LDA", "", "", "LDY", "LDA", "LDX", "", "CLV", "LDA", "TSX", "", "LDY", "LDA", "LDX", "",
            "CPY", "CMP", "", "", "CPY", "CMP", "DEC", "", "INY", "CMP", "DEX", "", "CPY", "CMP", "DEC", "",
            "BNE", "CMP", "", "", "", "CMP", "DEC", "", "CLD", "CMP", "", "", "", "CMP", "DEC", "",
            "CPX", "SBC", "", "", "CPX", "SBC", "INC", "", "INX", "SBC", "NOP", "", "CPX", "SBC", "INC", "",
            "BEQ", "SBC", "", "", "", "SBC", "INC", "", "SED", "SBC", "", "", "", "SBC", "INC", ""};


Also, we will be needing a handle to the pc register of cpu.c so that we know which part in memory to disassemble as the next instruction. So, within cpu.c add the following method:

jchar Java_com_johan_emulator_MainActivity_getPc(JNIEnv* pEnv, jobject pObj)
{
  return pc;
}


Don't forget the native stub in MainActivity:

public native char getPc();

Next up, we create our dump method:

    protected String getDisassembled(char[] memContents, int pc) {
        int opCode = memContents[pc];
        int mode = ADDRESS_MODES[opCode];
        int numArgs = INSTRUCTION_LEN[opCode] - 1;
        int argbyte1 = 0;
        int argbyte2 = 0;
        if (numArgs > 0) {
            argbyte1 = memContents[pc + 1];
        }

        if (numArgs > 1) {
            argbyte2 = memContents[pc + 2];
        }

        String addrStr = "";
        String result = getAsFourDigit(pc);
        result = result + " " + opCodeDesc[opCode] + " ";
        switch (mode) {
            case ADDRESS_MODE_ACCUMULATOR: result = result + " A";
            break;

            case ADDRESS_MODE_ABSOLUTE: addrStr = getAsFourDigit(argbyte2 * 256 + argbyte1);
                result = result + "$" + addrStr;
            break;

            case ADDRESS_MODE_ABSOLUTE_X_INDEXED: addrStr = getAsFourDigit(argbyte2 * 256 + argbyte1);
                result = result + "$" + addrStr + ",X";

            break;

            case ADDRESS_MODE_ABSOLUTE_Y_INDEXED: addrStr = getAsFourDigit(argbyte2 * 256 + argbyte1);
                result = result + "$" + addrStr + ",Y";

            break;

            case ADDRESS_MODE_IMMEDIATE: addrStr = getAsTwoDigit(argbyte1);
                result = result + "#$" + addrStr;

            break;

            case ADDRESS_MODE_IMPLIED:
                //return result;
            break;

            //case ADDRESS_MODE_INDIRECT:
            //    tempAddress = (argbyte2 * 256 + argbyte1);
            //    return (localMem.readMem(tempAddress + 1) * 256 + localMem.readMem(tempAddress));
            //break;

            case ADDRESS_MODE_X_INDEXED_INDIRECT:
                addrStr = getAsTwoDigit(argbyte2 * 256 + argbyte1);
                result = result + "($" + addrStr + ",X)";
                     break;

            case ADDRESS_MODE_INDIRECT_Y_INDEXED:
                addrStr = getAsTwoDigit(argbyte1);
                result = result + "($" + addrStr + "),Y";

            break;

            //case ADDRESS_MODE_RELATIVE:
            //    addrStr = getAsFourDigit(((argbyte1 > 127) ? (argbyte1 - 256) : argbyte1) + pc + 2);
            //    result = result + "$" + addrStr;
            //    return result;
            //break;

            case ADDRESS_MODE_ZERO_PAGE:
                addrStr = getAsTwoDigit(argbyte1);
                result = result + "$" + addrStr;
                //return result;
            break;

            case ADDRESS_MODE_ZERO_PAGE_X_INDEXED:
                addrStr = getAsTwoDigit(argbyte1);
                result = result + "$" + addrStr + ",X";
                //return result;
            break;

            case ADDRESS_MODE_ZERO_PAGE_Y_INDEXED:
                addrStr = getAsTwoDigit(argbyte1);
                result = result + "$" + addrStr + ",Y";
               // return result;
            break;

        }
        return result;
    }


As you can see this method very closely resembles the step() method within cpu.c.

Within this method you will see mention to the methods getAsFourDigit and getAsTwoDigit. These are helper methods returning the contents of number either as a four hex digit string or a two digit hex string. For completeness, here is the declaration of these two methods:

    private String getAsTwoDigit(int number) {
        String numStr = "00"+Integer.toHexString(number);
        numStr = numStr.substring(numStr.length() - 2);
        return numStr;
    }

    private String getAsFourDigit(int number) {
        String numStr = "0000"+Integer.toHexString(number);
        numStr = numStr.substring(numStr.length() - 4);
        return numStr;
    }


And finally, we need to add a call to the getDisassembled() method within refreshControls():

    private void refreshControls() {

        char[] memDump = dump();

        TextView view = (TextView) findViewById(R.id.memoryDump);
        view.setText(getMemDumpAsString(memDump));

        TextView viewReg = (TextView) findViewById(R.id.Registers);
        viewReg.setText(getRegisterDump(getAcc(), getXreg(), getYreg()));

        TextView viewFlags = (TextView) findViewById(R.id.Flags);
        viewFlags.setText(getFlagDump());

        TextView viewDiss = (TextView) findViewById(R.id.instruction);
        viewDiss.setText(getDisassembled(memDump, getPc()));


    }


Adding new Instructions

It is finally time to add the new instructions ADC and SBC.

Within the different address modes of these two instructions, there is a big bulk of repeating code, so it would be worthwhile for us to move this repeating code into two methods. So, within cpu.c add the following code:

  char ADC(char operand1, char operand2) {
    int temp = operand1 + operand2 + carryFlag;
    carryFlag = ((temp & 0x100) == 0x100) ? 1 : 0;
    overflowFlag = (((operand1^temp) & (operand2^temp) & 0x80) == 0x80) ? 1 : 0;
    temp = temp & 0xff;
    return temp;
  }

  char SBC(char operand1, char operand2) {
    operand2 = ~operand2 & 0xff;
    operand2 = operand2 + (1 - carryFlag);
    int temp = operand1 + operand2;
    carryFlag = ((temp & 0x100) == 0x100) ? 1 : 0;
    overflowFlag = (((operand1^temp) & (operand2^temp) & 0x80) == 0x80) ? 1 : 0;
    temp = temp & 0xff;
    return temp;
  }

The bulk of the code in these methods is responsible for updating effected flags.

With these methods coded, we can now add the new instructions to the end of the switch statement within the step method:

*ADC  Add Memory to Accumulator with Carry
     A + M + C -> A, C                N Z C I D V
                                      + + + - - +
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     immediate     ADC #oper     69    2     2
     zeropage      ADC oper      65    2     3
     zeropage,X    ADC oper,X    75    2     4
     absolute      ADC oper      6D    3     4
     absolute,X    ADC oper,X    7D    3     4*
     absolute,Y    ADC oper,Y    79    3     4*
     (indirect,X)  ADC (oper,X)  61    2     6
     (indirect),Y  ADC (oper),Y  71    2     5* */

          case 0x69:
              acc = ADC (acc, arg1);
              updateFlags(acc);
              break;
          case 0x65:
          case 0x75:
          case 0x6D:
          case 0x7D:
          case 0x79:
          case 0x61:
          case 0x71:
              acc = ADC (acc, memory_read(effectiveAdrress));
              updateFlags(acc);
              break;

/*SBC  Subtract Memory from Accumulator with Borrow
     A - M - C -> A                   N Z C I D V
                                      + + + - - +
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     immediate     SBC #oper     E9    2     2
     zeropage      SBC oper      E5    2     3
     zeropage,X    SBC oper,X    F5    2     4
     absolute      SBC oper      ED    3     4
     absolute,X    SBC oper,X    FD    3     4*
     absolute,Y    SBC oper,Y    F9    3     4*
     (indirect,X)  SBC (oper,X)  E1    2     6
     (indirect),Y  SBC (oper),Y  F1    2     5*  */

          case 0xE9:
              acc = SBC (acc, arg1);
              updateFlags(acc);
              break;
          case 0xE5:
          case 0xF5:
          case 0xED:
          case 0xFD:
          case 0xF9:
          case 0xE1:
          case 0xF1:
              acc = SBC (acc, memory_read(effectiveAdrress));
              updateFlags(acc);
              break;
/*INC  Increment Memory by One
     M + 1 -> M                       N Z C I D V
                                      + + - - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     zeropage      INC oper      E6    2     5
     zeropage,X    INC oper,X    F6    2     6
     absolut      INC oper      EE    3     6
     absolute,X    INC oper,X    FE    3     7 */

          case 0xE6:
          case 0xF6:
          case 0xEE:
          case 0xFE:
              tempVal = memory_read(effectiveAdrress);
              tempVal++; tempVal = tempVal & 0xff;
              memory_write(effectiveAdrress, tempVal);
              updateFlags(tempVal);
              break;

/*INX  Increment Index X by One
     X + 1 -> X                       N Z C I D V
                                      + + - - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     implied       INX           E8    1     2*/

          case 0xE8:

              xReg++; xReg = xReg & 0xff;
              updateFlags(xReg);
              break;

/*INY  Increment Index Y by One
     Y + 1 -> Y                       N Z C I D V
                                      + + - - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     implied       INY           C8    1     2*/

          case 0xC8:

              yReg++; yReg = yReg & 0xff;
              updateFlags(yReg);
              break;

/*DEC  Decrement Memory by One
     M - 1 -> M                       N Z C I D V
                                      + + - - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     zeropage      DEC oper      C6    2     5
     zeropage,X    DEC oper,X    D6    2     6
     absolute      DEC oper      CE    3     3
     absolute,X    DEC oper,X    DE    3     7 */

          case 0xC6:
          case 0xD6:
          case 0xCE:
          case 0xDE:
              tempVal = memory_read(effectiveAdrress);
              tempVal--; tempVal = tempVal & 0xff;
              memory_write(effectiveAdrress, tempVal);
              updateFlags(tempVal);
              break;

/*DEX  Decrement Index X by One
     X - 1 -> X                       N Z C I D V
                                      + + - - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     implied       DEC           CA    1     2*/

          case 0xCA:

              xReg--; xReg = xReg & 0xff;
              updateFlags(xReg);
              break;

/*DEY  Decrement Index Y by One
     Y - 1 -> Y                       N Z C I D V
                                      + + - - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     implied       DEC           88    1     2*/

          case 0x88:

              yReg--; yReg = yReg & 0xff;
              updateFlags(yReg);
              break;

We have just implemented the ADC and SBC instructions with all its address mode variants.

If you closely at the case statements in the code above, you will realise that I also have implemented the following the following non-mentioned instructions: INC, INX, INY, DEC, DEX, DEY. These are instructions for incrementing and decrementing the X/Y register or memory locations by 1. Since These type of instructions are very closely related to ADC and SBC, I have decided to also include it in this section.

Testing the code changes

Time to test our code changes. For this purpose we also use the following 6502 test program as used in our JavaScript series:

LDA #$0a  a9 0a
ADC #0f   69 0f
LDA #$32  a9 32
ADC #$32  69 32
LDA #E7   a9 e7
ADC #$CE  69 ce
LDA #$88  a9 88
ADC #$EC  69 ec
LDA #$43  a9 43
ADC #$3C  69 3c

LDA #$0f  a9 0f
SBC #$0a  e9 0a
LDA #$0a  a9 0a
SBC #$0d  e9 0d
LDA #$78  a9 78
SBC #$F9  e9 f9
LDA #$88  a9 88
SBC #$F8  e9 f8
LDX #$FE  a2 fe
INX       e8
INX       e8
DEX       ca
DEX       ca
LDA #$FE  a9 fe
STA $64   85 64
INC $64   e6 64
INC $64   e6 64
DEC $64   c6 64
DEC $64   c6 64

As with the previous post, we need to replace the contents of the my_program array within memory.c with our new program:

jchar my_program[] = {0xa9, 0x0a, 0x69, 0x0f, 0xa9, 0x32, 0x69, 0x32,
                      0xa9, 0xe7, 0x69, 0xce, 0xa9, 0x88, 0x69, 0xec,
                      0xa9, 0x43, 0x69, 0x3c, 0xa9, 0x0f, 0xe9, 0x0a,
                      0xa9, 0x0a, 0xe9, 0x0d, 0xa9, 0x78, 0xe9, 0xf9,
                      0xa9, 0x88, 0xe9, 0xf8, 0xa2, 0xfe, 0xe8, 0xe8,
                      0xca, 0xca, 0xa9, 0xfe, 0x85, 0x64, 0xe6, 0x64,
                      0xe6, 0x64, 0xc6, 0x64, 0xc6, 0x64};


To give you an idea what to expect when stepping through the program, I made the following screen snippets:



































You will realise that something interesting happens only after after the fifth step regarding the flags.

In Summary

In this post we added all address mode variants of the ADC and SBC instructions.

We also extended our front end to show the following as part of the state:

  • Registers
  • Flags
  • Disassembled version of net intruction
In the next post we will be tackling the comparison and branching instructions.

Till Next time!



Wednesday 7 September 2016

Part 4: Adding the LOAD and STORE instructions

Foreword

In the previous post we rewrote the back-end of our emulator to C and compiled it into a native module with the help of the Android NDK.

In this post we will be adding more instructions to our emulator.

The instructions I will be adding will be the LOAD and STORE instructions. These instructions are the following (including all Address Mode variants): LDA, LDX, LDY, STA, STX, STY.

In this post we will be following more or less the same approach as Part 2 of my JavaScript emulator series: 6502 address modes. The syntax of JavaScript and C is very close to each other, so I will basically copying and pasting most of the JavaScript emulator code for this series. Of course there is quite a number of differences between the two languages, which I will cover in this post and in coming posts.

Preliminary Planning

Before we start with this post, let us do some quick planning on what we want to achieve.

We start off by looking at the 6502 test program that we use in Part 2 of our JavaScript emulator series:

lda #$d0    a9 d0
ldx #$01    a2 01
sta $96     85 96
lda #$07    a9 07
sta $96,X   95 96 
sta $99     85 99
lda #$d1    a9 d1
sta $98     85 98
lda #$55    a9 55
ldx #$02    a2 02
sta ($96,X) ;  81 96
ldy #$02       a0 02
lda #$56       a9 56
sta ($96),y ;  91 96
lda $07d1      ad d1 07
sta $07d1,x ;  9d d1 07

As stated in the blog post, when this program has executed, we expect that memory location 2001 will contain the hexadecimal value 55, 2002 hexadecimal 56 and location 2003 the hexadecimal value 55.

So, in order for our us to check whether this program has executed successfully, our emulator will need to provide us with a dump of the memory at location 2000.

Currently, our emulator is hardcoded to only show a dump starting at memory location 0, even though an edit box is provided for specifying a different address.

Therefore, as part of our goals of this post is to change our emulator to actually use the contents of the address box to know which part of memory should be dumped.

Also,  currently our memory array is only as long as our machine language program we assign to it within memory.c. This means, for example, that with the above mentioned assembly program, our memory will only be 34 bytes.

This is a problem because this assembly program access the memory locations 2001, 2002 and 2003, well out of reach of 34 byte memory array!

We will get around this limitation by creating an empty 64kb memory array on startup, and then copying our program array array to it.

Extending Memory functionality

Let start to implement the mentioned new memory functionality.

The first file we will visit is memory.c.

Within this file we make the following changes:

jchar my_program[] = {0xa9, 0xd0, 0xa2, 0x01, 0x85, 0x96, 0xa9, 0x07, 0x95, 0x96,
                    0x85, 0x99, 0xa9, 0xd1, 0x85, 0x98, 0xa9, 0x55, 0xa2, 0x02, 
                    0x81, 0x96, 0xa0, 0x02, 0xa9, 0x56, 0x91, 0x96, 0xad, 0xd1, 
                    0x07, 0x9d, 0xd1, 0x07};

jchar mainMem[65536];


As you can see the definition of the mainMem array changed a bit. We don't assign our machine language program directly to this array any more. We rather define our mainMem an 64k element array without assigning any value. Such an array declaration will automatically initialised as an all zero array on startup.

You will also see that we added a new array declaration my_program, which contains our actual machine language program.

What should happen in principle is that the contents of my_program should be copied to the first couple of locations within mainMem.

The trick comes in with the fact that C doesn't provide you with functionality like static initialisers that would have perform the task for us.

So, we need to create a method that the static initialiser of MainActivity.java can invoke to kick off the process.

So, first we create the following method within memory.c:

void
Java_com_johan_emulator_MainActivity_memoryInit(JNIEnv* pEnv, jobject pObj)
{
  memcpy(mainMem, my_program, sizeof(my_program));
}

Next, we need to create native stub within MainActivity.java:

...
    public native char[] dump();
    public native void step();
    public static native void memoryInit();
...

And we invoke this method within the static initialiser of MainActivity:

    static {
        System.loadLibrary("native_emu");
        memoryInit();
    }

One thing to take note of is that this code executes within a static context, e.g. no object instance exist and for that matter this doesn't exist. It is therefore important that the native method stub should contain the keyword static.

An interesting question, would be is that if this doesn't exist for this method, what does your native method receive as parameter pObj? And the answer is a Class instance. In this case the class instance is MainActivity.class.

The final memory-related task we need to do is let our emulator make use of the address edit box to know from which address to display the dump. For this purpose we modify the getMemDumpAsString of MainActivity a bit:

    public String getMemDumpAsString(char[] memContents) {
        EditText editText = (EditText) findViewById(R.id.inputSearchEditText);
        String editAddress = "0"+editText.getText().toString();

        int intaddress = Integer.parseInt(editAddress,16);        String result = "";
        byte[] temp = new byte[48];
        for (int i=0; i < temp.length; i++) {
            temp[i] = (byte) memContents[i+intaddress];
        }

        for (int i = 0; i < temp.length; i++) {
            if ((i % 16) == 0) {
                String numberStr = Integer.toHexString(i+intaddress);
                numberStr = "0000" + numberStr;
                result = result + "\n" + numberStr.substring(numberStr.length() - 4);
            }
            String number = "0" + Integer.toHexString(temp[i] & 0xff);
            number = number.substring(number.length() - 2);
            result = result + " " + number;
        }
        return result;
    }

We get the contents of the editbox and add a zero in front of it. This is just to cater for the scenario when the editbox is empty, the parsing process will yield a zero. We then convert the string to a integer and add the resulting address to different variables in the process.

Adding extra instructions

Now it is time to add all the different LOAD and STORE instructions to our emulator.

There is basically 6 assembly mnemonics for the LOAD and STORE instructions: LDA, LDX, LDY, STA, STX and STY.

What makes life complicated with these mnemonics is that each mnemonic can have a couple of different flavours called address modes.

To simplify the landscape one just needs to remember that the different flavours of a mnemonic are just different ways to obtain a effective address to act on.

So, if one could move all this address resolution code into a method of its own, one can simplify the writing of your emulator a lot.

Also, to simplify code, we need to move the properties of the different properties of the different instructions into lookup tables. We will add these lookup tables to the beginning of the file cpu.c:

  #define ADDRESS_MODE_ACCUMULATOR 0
  #define ADDRESS_MODE_ABSOLUTE 1
  #define ADDRESS_MODE_ABSOLUTE_X_INDEXED 2
  #define ADDRESS_MODE_ABSOLUTE_Y_INDEXED 3
  #define ADDRESS_MODE_IMMEDIATE 4
  #define ADDRESS_MODE_IMPLIED 5
  #define ADDRESS_MODE_INDIRECT 6
  #define ADDRESS_MODE_X_INDEXED_INDIRECT 7
  #define ADDRESS_MODE_INDIRECT_Y_INDEXED 8
  #define ADDRESS_MODE_RELATIVE 9
  #define ADDRESS_MODE_ZERO_PAGE 10
  #define ADDRESS_MODE_ZERO_PAGE_X_INDEXED 11
  #define ADDRESS_MODE_ZERO_PAGE_Y_INDEXED 12

  const unsigned char addressModes[] = {5, 7, 0, 0, 0, 10, 10, 0, 5, 4, 0, 0, 0, 1, 1, 0, 
  9, 8, 0, 0, 0, 11, 11, 0, 5, 3, 0, 0, 0, 2, 2, 0, 
  1, 7, 0, 0, 10, 10, 10, 0, 5, 4, 0, 0, 1, 1, 1, 0, 
  9, 8, 0, 0, 0, 11, 11, 0, 5, 3, 0, 0, 0, 2, 2, 0, 
  5, 7, 0, 0, 0, 10, 10, 0, 5, 4, 0, 0, 1, 1, 1, 0, 
  9, 8, 0, 0, 0, 11, 11, 0, 5, 3, 0, 0, 0, 2, 2, 0, 
  5, 7, 0, 0, 0, 10, 10, 0, 5, 4, 0, 0, 6, 1, 1, 0, 
  9, 8, 0, 0, 0, 11, 11, 0, 5, 3, 0, 0, 0, 2, 2, 0, 
  0, 7, 0, 0, 10, 10, 10, 0, 5, 0, 5, 0, 1, 1, 1, 0, 
  9, 8, 0, 0, 11, 11, 12, 0, 5, 3, 5, 0, 0, 2, 0, 0, 
  4, 7, 4, 0, 10, 10, 10, 0, 5, 4, 5, 0, 1, 1, 1, 0, 
  9, 8, 0, 0, 11, 11, 12, 0, 5, 3, 5, 0, 2, 2, 3, 0, 
  4, 7, 0, 0, 10, 10, 10, 0, 5, 4, 5, 0, 1, 1, 1, 0, 
  9, 8, 0, 0, 0, 11, 11, 0, 5, 3, 0, 0, 0, 2, 2, 0, 
  4, 7, 0, 0, 10, 10, 10, 0, 5, 4, 5, 0, 1, 1, 1, 0, 
  9, 8, 0, 0, 0, 11, 11, 0, 5, 3, 0, 0, 0, 2, 2, 0};

  const unsigned char instructionLengths[] = {1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 0, 3, 3, 0, 
  2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 
  3, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 
  2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 
  1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 
  2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 
  1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 
  2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 
  0, 2, 0, 0, 2, 2, 2, 0, 1, 0, 1, 0, 3, 3, 3, 0, 
  2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 0, 3, 0, 0, 
  2, 2, 2, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 
  2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, 
  2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 
  2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 
  2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 
  2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0};

  const unsigned char instructionCycles[] = {7, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 0, 4, 6, 0, 
  2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, 
  6, 6, 0, 0, 3, 3, 5, 0, 4, 2, 2, 0, 4, 4, 6, 0, 
  2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, 
  6, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 3, 4, 6, 0, 
  2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, 
  6, 6, 0, 0, 0, 3, 5, 0, 4, 2, 2, 0, 5, 4, 6, 0, 
  2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, 
  0, 6, 0, 0, 3, 3, 3, 0, 2, 0, 2, 0, 4, 4, 4, 0, 
  2, 6, 0, 0, 4, 4, 4, 0, 2, 5, 2, 0, 0, 5, 0, 0, 
  2, 6, 2, 0, 3, 3, 3, 0, 2, 2, 2, 0, 4, 4, 4, 0, 
  2, 5, 0, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0, 
  2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 3, 0, 
  2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, 
  2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, 
  2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0};


This was also more or less a copy and paste exercise from my previous JavaScript emulator.

You will see see for all this constant information we declare by use of the keyword const, effectively declaring these information as read-only. The exception to the rule is the address mode constants in which each of them is declared as #define.

The reason for this is that we will be using these address mode constants as case labels within a switch statement. In switch statements C is a bit fuzzy and will only literals as case labels and constants simply wouldn't do.

Defines is our way out of this issue. When the C-preprocessor run during compilation, all instances where you use the define will be replaced by a physical literal.

We now need to define the method that will calculate the effective address for us for the relevant address mode:

  int calculateEffevtiveAdd(unsigned char mode, int argbyte1, int argbyte2) {

    int tempAddress = 0;
    switch (mode)
    {
      case ADDRESS_MODE_ACCUMULATOR: return 0; 
      break;

      case ADDRESS_MODE_ABSOLUTE: return ((argbyte2 << 8) + argbyte1);
      break;

      case ADDRESS_MODE_ABSOLUTE_X_INDEXED: tempAddress = ((argbyte2 << 8) + argbyte1);
        tempAddress = tempAddress + xReg;
        return tempAddress;
      break;

      case ADDRESS_MODE_ABSOLUTE_Y_INDEXED: tempAddress = ((argbyte2 << 8) + argbyte1);
        tempAddress = tempAddress + yReg;
        return tempAddress;
      break;

      case ADDRESS_MODE_IMMEDIATE: 
      break;

      case ADDRESS_MODE_IMPLIED:
      break;

      case ADDRESS_MODE_INDIRECT:
        tempAddress = ((argbyte2 << 8) + argbyte1);
        return ((memory_read(tempAddress + 1) << 8) + memory_read(tempAddress));
      break;

      case ADDRESS_MODE_X_INDEXED_INDIRECT:
        tempAddress = (argbyte1 + xReg) & 0xff;
        return ((memory_read(tempAddress + 1) << 8) + memory_read(tempAddress));
      break;

      case ADDRESS_MODE_INDIRECT_Y_INDEXED:
        tempAddress = (memory_read(argbyte1 + 1) << 8) + memory_read(argbyte1) + yReg;
        return tempAddress;
      break;

      case ADDRESS_MODE_RELATIVE:
      break;

      case ADDRESS_MODE_ZERO_PAGE:
         return argbyte1;
      break;

      case ADDRESS_MODE_ZERO_PAGE_X_INDEXED:
        return (argbyte1 + xReg) & 0xff;
      break;

      case ADDRESS_MODE_ZERO_PAGE_Y_INDEXED:
        return (argbyte1 + yReg) & 0xff;
      break;
    }
  }



Next, we need to modify the step method within cpu.c:

    void step() {
      int opcode = memory_read(pc);
      pc = pc + 1;
      int iLen = instructionLengths[opcode];
      jchar arg1 = 0;
      jchar arg2 = 0;
      int effectiveAdrress = 0;
      if (iLen > 1) {
        arg1 = memory_read(pc);
        pc = pc + 1;
      }    

      if (iLen > 2) {
        arg2 = memory_read(pc);
        pc = pc + 1;
      }    

      effectiveAdrress = calculateEffevtiveAdd(addressModes[opcode], arg1, arg2);

      switch (opcode)
      {
/*LDA  Load Accumulator with Memory
     M -> A                           N Z C I D V
                                      + + - - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     immediate     LDA #oper     A9    2     2
     zeropage      LDA oper      A5    2     3
     zeropage,X    LDA oper,X    B5    2     4
     absolute      LDA oper      AD    3     4
     absolute,X    LDA oper,X    BD    3     4*
     absolute,Y    LDA oper,Y    B9    3     4*
     (indirect,X)  LDA (oper,X)  A1    2     6
     (indirect),Y  LDA (oper),Y  B1    2     5* */

        case 0xa9:
          acc = arg1;
          updateFlags(acc);
        break;
        case 0xA5:
        case 0xB5:
        case 0xAD:
        case 0xBD:
        case 0xB9:
        case 0xA1:
        case 0xB1:
          acc = memory_read(effectiveAdrress);
          updateFlags(acc);
        break;

/*LDX  Load Index X with Memory
     M -> X                           N Z C I D V
                                      + + - - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     immediate     LDX #oper     A2    2     2
     zeropage      LDX oper      A6    2     3
     zeropage,Y    LDX oper,Y    B6    2     4
     absolute      LDX oper      AE    3     4
     absolute,Y    LDX oper,Y    BE    3     4**/

        case 0xA2:
          xReg = arg1;
          updateFlags(xReg);
        break;

        case 0xA6:
        case 0xB6:
        case 0xAE:
        case 0xBE:
          xReg = memory_read(effectiveAdrress);
          updateFlags(xReg);
        break;



/*LDY  Load Index Y with Memory
     M -> Y                           N Z C I D V
                                      + + - - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     immediate     LDY #oper     A0    2     2
     zeropage      LDY oper      A4    2     3
     zeropage,X    LDY oper,X    B4    2     4
     absolute      LDY oper      AC    3     4
     absolute,X    LDY oper,X    BC    3     4*/


        case 0xA0:
          yReg = arg1;
          updateFlags(yReg);
        break;

        case 0xA4:
        case 0xB4:
        case 0xAC:
        case 0xBC:
          yReg = memory_read(effectiveAdrress);
          updateFlags(yReg);
        break;
 
/*STA  Store Accumulator in Memory
     A -> M                           N Z C I D V
                                      - - - - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     zeropage      STA oper      85    2     3
     zeropage,X    STA oper,X    95    2     4
     absolute      STA oper      8D    3     4
     absolute,X    STA oper,X    9D    3     5
     absolute,Y    STA oper,Y    99    3     5
     (indirect,X)  STA (oper,X)  81    2     6
     (indirect),Y  STA (oper),Y  91    2     6  */

        case 0x85:
        case 0x95:
        case 0x8D:
        case 0x9D:
        case 0x99:
        case 0x81:
        case 0x91:
          memory_write(effectiveAdrress, acc);
        break;


/*STX  Store Index X in Memory
     X -> M                           N Z C I D V
                                      - - - - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     zeropage      STX oper      86    2     3
     zeropage,Y    STX oper,Y    96    2     4
     absolute      STX oper      8E    3     4  */

        case 0x86:
        case 0x96:
        case 0x8E:
          memory_write(effectiveAdrress, xReg);
        break;
/*STY  Sore Index Y in Memory
     Y -> M                           N Z C I D V
                                      - - - - - -
     addressing    assembler    opc  bytes  cyles
     --------------------------------------------
     zeropage      STY oper      84    2     3
     zeropage,X    STY oper,X    94    2     4
     absolute      STY oper      8C    3     4  */

        case 0x84:
        case 0x94:
        case 0x8C:
          memory_write(effectiveAdrress, yReg);
        break;
      }
    }


This cover all the code changes we need to do for this post.

Running the test program

Time for us to run the test 6502 program.

When stepping through the application we should keep an eye on memory locations 2001, 2002 and 2003, as mentioned earlier.

So, before stepping enter 7D0 (e.g. hex equalavent of 2000) in the address box.

Now click the step button about sixteen times. After sixteen times your display will look as follows:


This is in effect what we want. So we know that we are more or less on track with our emulator.

In Summary

In this post we have implemented all address mode variants of the LOAD and STORE instructions.

In the next blog we will adding all variants of the ADC and SBC instructions (Add with carry and Subtract with carry).

We will also be extending the functionality of our front end once more. This time around we will showing the contents of the CPU registers while stepping. We will also be showing the disassembled version of the next instruction to be executed.

Till next time!