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!



No comments:

Post a Comment