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!

No comments:

Post a Comment