Foreword
In the previous post we added some basic plumbing to our emulator for single stepping through instructions and showing a dump of memory and registers at each step.
We ended off the post by implementing the instructions Load and store Accumulator (LDA) immediate and Store Accumulator (STA) absolute.
In this post we will go forth and implement every single load and store instruction, with every associated address mode.
In this exercise we will also be developing a generic way of resolving addresses in the different address modes, not having to do it with every single instruction.
Hope you enjoy this post!
Lookup tables
I mentioned that I want to create a generic way for dealing with address modes. Considering that there is over 100 instructions on the 6502 CPU, this sounds like quite an intimidating task!
But fear not. We can use lookup tables to lookup the addressing mode for each opcode. 😀
However, despite using a lookup table, there is still the daunting task of creating table by hand and is very error prone, considering the volume of instructions.
I will try and make this task less daunting by automating this table generation, and supplying this process a text file of all the instructions. The following website from 6502.org gives it to us:
http://www.6502.org/tutorials/6502opcodes.html
Lets have a look at how this info of the instructions is laid out:
ADC (ADd with Carry) Affects Flags: N V Z C MODE SYNTAX HEX LEN TIM Immediate ADC #$44 $69 2 2 Zero Page ADC $44 $65 2 3 Zero Page,X ADC $44,X $75 2 4 Absolute ADC $4400 $6D 3 4 Absolute,X ADC $4400,X $7D 3 4+ Absolute,Y ADC $4400,Y $79 3 4+ Indirect,X ADC ($44,X) $61 2 6 Indirect,Y ADC ($44),Y $71 2 5+ + add 1 cycle if page boundary crossed ADC results are dependant on the setting of the decimal flag. In decimal mode, addition is carried out on the assumption that the values involved are packed BCD (Binary Coded Decimal). There is no way to add without carry. AND (bitwise AND with accumulator) Affects Flags: N Z MODE SYNTAX HEX LEN TIM Immediate AND #$44 $29 2 2 Zero Page AND $44 $25 2 3 Zero Page,X AND $44,X $35 2 4 Absolute AND $4400 $2D 3 4 Absolute,X AND $4400,X $3D 3 4+ Absolute,Y AND $4400,Y $39 3 4+ Indirect,X AND ($44,X) $21 2 6 Indirect,Y AND ($44),Y $31 2 5+ + add 1 cycle if page boundary crossedWe see the actual info we need is in table format, which is nice. We can easily extract the info we need from that.
What would complicate the process is preceding text for each table, which we need to remove. A mindset we can apply for that, would be to look for the word MODE in the beginning of the line, then we can assume the following lines are instruction data, until we hit a blank line.
sed -n '/^MODE/{:a;N;/\n$/!ba;s/\n//gp}' lodandstore.txtRunning this, we get the following text output:
MODE SYNTAX HEX LEN TIM Immediate ADC #$44 $69 2 2 Zero Page ADC $44 $65 2 3 Zero Page,X ADC $44,X $75 2 4 Absolute ADC $4400 $6D 3 4 Absolute,X ADC $4400,X $7D 3 4+ Absolute,Y ADC $4400,Y $79 3 4+ Indirect,X ADC ($44,X) $61 2 6 Indirect,Y ADC ($44),Y $71 2 5+ MODE SYNTAX HEX LEN TIM Immediate AND #$44 $29 2 2 Zero Page AND $44 $25 2 3 Zero Page,X AND $44,X $35 2 4 Absolute AND $4400 $2D 3 4 Absolute,X AND $4400,X $3D 3 4+ Absolute,Y AND $4400,Y $39 3 4+ Indirect,X AND ($44,X) $21 2 6 Indirect,Y AND ($44),Y $31 2 5+ MODE SYNTAX HEX LEN TIM Accumulator ASL A $0A 1 2 Zero Page ASL $44 $06 2 5 Zero Page,X ASL $44,X $16 2 6 Absolute ASL $4400 $0E 3 6 Absolute,X ASL $4400,X $1E 3 7This resembles more what I am looking for. Lets pipe this output to another sed command, so we are only left with instruction lines:
sed -n '/^MODE/{:a;N;/\n$/!ba;s/\n/\n/g;p}' lodandstore.txt | sed '/^MODE/d; /^\s*$/d'In the second sed command the /d basically tells sed if you find that match delete that line.
The output of this command is as follows:
Immediate ADC #$44 $69 2 2 Zero Page ADC $44 $65 2 3 Zero Page,X ADC $44,X $75 2 4 Absolute ADC $4400 $6D 3 4 Absolute,X ADC $4400,X $7D 3 4+ Absolute,Y ADC $4400,Y $79 3 4+ Indirect,X ADC ($44,X) $61 2 6 Indirect,Y ADC ($44),Y $71 2 5+ Immediate AND #$44 $29 2 2 Zero Page AND $44 $25 2 3 Zero Page,X AND $44,X $35 2 4 Absolute AND $4400 $2D 3 4 Absolute,X AND $4400,X $3D 3 4+ Absolute,Y AND $4400,Y $39 3 4+ Indirect,X AND ($44,X) $21 2 6 Indirect,Y AND ($44),Y $31 2 5+ Accumulator ASL A $0A 1 2 Zero Page ASL $44 $06 2 5 Zero Page,X ASL $44,X $16 2 6 Absolute ASL $4400 $0E 3 6 Absolute,X ASL $4400,X $1E 3 7So, now we have a text file only containing the instruction data. Now we just need to extract the required data from each line and build the table.
Using awk, here is the code to generate the Instruction Length array:
awk ' BEGIN {for (i = 0; i < 256; i++) instruction_lengths[i] = 0;} { mode = substr($0, 1, 14) format = substr($0, 15, 14) opcode = substr($0, 30, 3) hex_index = strtonum("0x" opcode) insLen = substr($0, 34, 1) gsub(/[ ]+$/, "", mode) # Remove trailing spaces from the substring gsub(/[ ]+$/, "", format) # Remove trailing spaces from the substring hex_index = strtonum("0x" opcode) instruction_lengths[hex_index] = insLen } END { # Print the array, with values separated by commas for (i = 0; i < length(instruction_lengths); i++) { printf "%s, ", instruction_lengths[i] if ((i % 16) == 15) print ""; } print "}" } ' processed.txtPlease note that processed.txt is the path to our text file we created previously containing only the instruction rows.
We then have a middle block which awk invokes for each row, and thus $0 always contains the text of the row we are currently busy with.
1, 2, 0, 0, 0, 2, 2, 0, 0, 2, 1, 0, 0, 3, 3, 0, 0, 2, 0, 0, 0, 2, 2, 0, 0, 3, 0, 0, 0, 3, 3, 0, 3, 2, 0, 0, 2, 2, 2, 0, 0, 2, 1, 0, 3, 3, 3, 0, 0, 2, 0, 0, 0, 2, 2, 0, 0, 3, 0, 0, 0, 3, 3, 0, 1, 2, 0, 0, 0, 2, 2, 0, 0, 2, 1, 0, 3, 3, 3, 0, 0, 2, 0, 0, 0, 2, 2, 0, 0, 3, 0, 0, 0, 3, 3, 0, 1, 2, 0, 0, 0, 2, 2, 0, 0, 2, 1, 0, 3, 3, 3, 0, 0, 2, 0, 0, 0, 2, 2, 0, 0, 3, 0, 0, 0, 3, 3, 0, 0, 2, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 2, 0, 0, 0, 2, 2, 0, 0, 3, 0, 0, 0, 3, 0, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 2, 0, 0, 3, 3, 3, 0, 0, 2, 0, 0, 2, 2, 2, 0, 0, 3, 0, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 0, 2, 0, 0, 3, 3, 3, 0, 0, 2, 0, 0, 0, 2, 2, 0, 0, 3, 0, 0, 0, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 0, 2, 1, 0, 3, 3, 3, 0, 0, 2, 0, 0, 0, 2, 2, 0, 0, 3, 0, 0, 0, 3, 3, 0,So, now we have an array, where if we have an opcode, we can quickly find the length of it.
Here is the code:
awk ' BEGIN {for (i = 0; i < 256; i++) instruction_cycles[i] = 0;} { mode = substr($0, 1, 14) format = substr($0, 15, 14) opcode = substr($0, 30, 3) hex_index = strtonum("0x" opcode) insLen = substr($0, 34, 1) insCycles = substr($0, 38, 1) gsub(/[ ]+$/, "", mode) # Remove trailing spaces from the substring gsub(/[ ]+$/, "", format) # Remove trailing spaces from the substring hex_index = strtonum("0x" opcode) instruction_cycles[hex_index] = insCycles } END { # Print the array, with values separated by commas for (i = 0; i < length(instruction_cycles); i++) { printf "%s, ", instruction_cycles[i] if ((i % 16) == 15) print ""; } print "}" } ' processed.txtThe resulting array look like this:
7, 6, 0, 0, 0, 3, 5, 0, 0, 2, 2, 0, 0, 4, 6, 0, 0, 5, 0, 0, 0, 4, 6, 0, 0, 4, 0, 0, 0, 4, 7, 0, 6, 6, 0, 0, 3, 3, 5, 0, 0, 2, 2, 0, 4, 4, 6, 0, 0, 5, 0, 0, 0, 4, 6, 0, 0, 4, 0, 0, 0, 4, 7, 0, 6, 6, 0, 0, 0, 3, 5, 0, 0, 2, 2, 0, 3, 4, 6, 0, 0, 5, 0, 0, 0, 4, 6, 0, 0, 4, 0, 0, 0, 4, 7, 0, 6, 6, 0, 0, 0, 3, 5, 0, 0, 2, 2, 0, 5, 4, 6, 0, 0, 5, 0, 0, 0, 4, 6, 0, 0, 4, 0, 0, 0, 4, 7, 0, 0, 6, 0, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0, 6, 0, 0, 0, 4, 4, 0, 0, 5, 0, 0, 0, 5, 0, 0, 2, 6, 2, 0, 3, 3, 3, 0, 0, 2, 0, 0, 4, 4, 4, 0, 0, 5, 0, 0, 4, 4, 4, 0, 0, 4, 0, 0, 4, 4, 4, 0, 2, 6, 0, 0, 3, 3, 5, 0, 0, 2, 0, 0, 4, 4, 6, 0, 0, 5, 0, 0, 0, 4, 6, 0, 0, 4, 0, 0, 0, 4, 7, 0, 2, 6, 0, 0, 3, 3, 5, 0, 0, 2, 2, 0, 4, 4, 6, 0, 0, 5, 0, 0, 0, 4, 6, 0, 0, 4, 0, 0, 0, 4, 7, 0,Finally, let us get to the address mode array. Here is the code:
awk ' BEGIN {for (i = 0; i < 256; i++) addr_modes[i] = 0;} { mode = substr($0, 1, 14) format = substr($0, 15, 14) opcode = substr($0, 30, 3) hex_index = strtonum("0x" opcode) insLen = substr($0, 34, 1) gsub(/[ ]+$/, "", mode) # Remove trailing spaces from the substring gsub(/[ ]+$/, "", format) # Remove trailing spaces from the substring hex_index = strtonum("0x" opcode) if (mode == "Implied") { addr_mode = 0; } else if (mode == "Accumulator") { addr_mode = 1; } else if (mode == "Immediate") { addr_mode = 2; } else if (node == "Zero Page") { addr_mode = 3; } else if (mode == "Zero Page,X") { addr_mode = 4; } else if (mode == "Zero Page,Y") { addr_mode = 5; } else if (mode == "Absolute,X") { addr_mode = 7; } else if (mode == "Absolute,X") { addr_mode = 8; } else if (mode == "Absolute,Y") { addr_mode = 9; } else if (mode == "Indirect") { addr_mode = 10; } else if (mode == "Indirect,X") { addr_mode = 11; } else if (mode == "Indirect,Y") { addr_mode = 12; } addr_modes[hex_index] = addr_mode } END { # Print the array, with values separated by commas for (i = 0; i < length(addr_modes); i++) { printf "%s, ", addr_modes[i] if ((i % 16) == 15) print ""; } print "}" } ' processed.txtAnd the resulting array looks like this:
0, 11, 0, 0, 0, 2, 1, 0, 0, 2, 1, 0, 0, 4, 4, 0, 0, 12, 0, 0, 0, 4, 4, 0, 0, 9, 0, 0, 0, 7, 7, 0, 10, 11, 0, 0, 7, 2, 1, 0, 0, 2, 1, 0, 7, 4, 4, 0, 0, 12, 0, 0, 0, 4, 4, 0, 0, 9, 0, 0, 0, 7, 7, 0, 0, 11, 0, 0, 0, 2, 1, 0, 0, 2, 1, 0, 7, 4, 4, 0, 0, 12, 0, 0, 0, 4, 4, 0, 0, 9, 0, 0, 0, 7, 7, 0, 0, 11, 0, 0, 0, 2, 1, 0, 0, 2, 1, 0, 10, 4, 4, 0, 0, 12, 0, 0, 0, 4, 4, 0, 0, 9, 0, 0, 0, 7, 7, 0, 0, 11, 0, 0, 0, 12, 12, 0, 0, 0, 0, 0, 0, 4, 5, 0, 0, 12, 0, 0, 0, 4, 5, 0, 0, 9, 0, 0, 0, 7, 0, 0, 2, 11, 2, 0, 2, 2, 2, 0, 0, 2, 0, 0, 4, 4, 5, 0, 0, 12, 0, 0, 4, 4, 5, 0, 0, 9, 0, 0, 7, 7, 9, 0, 2, 11, 0, 0, 2, 2, 2, 0, 0, 2, 0, 0, 2, 4, 4, 0, 0, 12, 0, 0, 0, 4, 4, 0, 0, 9, 0, 0, 0, 7, 7, 0, 2, 11, 0, 0, 2, 2, 12, 0, 0, 2, 0, 0, 2, 4, 4, 0, 0, 12, 0, 0, 0, 4, 4, 0, 0, 9, 0, 0, 0, 7, 7, 0,At this point, many people will wonder why I don't use enums for the address mode array. I probably could, but the the names of the address modes is quite lengthy, so you will end up with very long lines for you array definition, and you need to scroll back and forth horizontally which is an unpleasant experience.
... Branches are dependant on the status of the flag bits when the op code is encountered. A branch not taken requires two machine cycles. Add one if the branch is taken and add one more if the branch crosses a page boundary. MNEMONIC HEX BPL (Branch on PLus) $10 BMI (Branch on MInus) $30 BVC (Branch on oVerflow Clear) $50 BVS (Branch on oVerflow Set) $70 BCC (Branch on Carry Clear) $90 BCS (Branch on Carry Set) $B0 BNE (Branch on Not Equal) $D0 BEQ (Branch on EQual) $F0 ... These instructions are implied mode, have a length of one byte and require two machine cycles. MNEMONIC HEX CLC (CLear Carry) $18 SEC (SEt Carry) $38 CLI (CLear Interrupt) $58 SEI (SEt Interrupt) $78 CLV (CLear oVerflow) $B8 CLD (CLear Decimal) $D8 SED (SEt Decimal) $F8 ... These instructions are implied mode, have a length of one byte and require two machine cycles. MNEMONIC HEX TAX (Transfer A to X) $AA TXA (Transfer X to A) $8A DEX (DEcrement X) $CA INX (INcrement X) $E8 TAY (Transfer A to Y) $A8 TYA (Transfer Y to A) $98 DEY (DEcrement Y) $88 INY (INcrement Y) $C8 ...For these instructions, we need to manually adjust the lookup tables. I will only do these once I get to the relevant sections.
class CpuTables { static const List<int> addressModes = [ 0, 11, 0, 0, 0, 2, 1, 0, 0, 2, 1, 0, 0, 4, 4, 0, 0, 12, 0, 0, 0, 4, 4, 0, 0, 9, 0, 0, 0, 7, 7, 0, 10, 11, 0, 0, 7, 2, 1, 0, 0, 2, 1, 0, 7, 4, 4, 0, 0, 12, 0, 0, 0, 4, 4, 0, 0, 9, 0, 0, 0, 7, 7, 0, 0, 11, 0, 0, 0, 2, 1, 0, 0, 2, 1, 0, 7, 4, 4, 0, 0, 12, 0, 0, 0, 4, 4, 0, 0, 9, 0, 0, 0, 7, 7, 0, 0, 11, 0, 0, 0, 2, 1, 0, 0, 2, 1, 0, 10, 4, 4, 0, 0, 12, 0, 0, 0, 4, 4, 0, 0, 9, 0, 0, 0, 7, 7, 0, 0, 11, 0, 0, 0, 12, 12, 0, 0, 0, 0, 0, 0, 4, 5, 0, 0, 12, 0, 0, 0, 4, 5, 0, 0, 9, 0, 0, 0, 7, 0, 0, 2, 11, 2, 0, 2, 2, 2, 0, 0, 2, 0, 0, 4, 4, 5, 0, 0, 12, 0, 0, 4, 4, 5, 0, 0, 9, 0, 0, 7, 7, 9, 0, 2, 11, 0, 0, 2, 2, 2, 0, 0, 2, 0, 0, 2, 4, 4, 0, 0, 12, 0, 0, 0, 4, 4, 0, 0, 9, 0, 0, 0, 7, 7, 0, 2, 11, 0, 0, 2, 2, 12, 0, 0, 2, 0, 0, 2, 4, 4, 0, 0, 12, 0, 0, 0, 4, 4, 0, 0, 9, 0, 0, 0, 7, 7, 0, ]; static const List<int> instructionLen = [ 1, 2, 0, 0, 0, 2, 2, 0, 0, 2, 1, 0, 0, 3, 3, 0, 0, 2, 0, 0, 0, 2, 2, 0, 0, 3, 0, 0, 0, 3, 3, 0, 3, 2, 0, 0, 2, 2, 2, 0, 0, 2, 1, 0, 3, 3, 3, 0, 0, 2, 0, 0, 0, 2, 2, 0, 0, 3, 0, 0, 0, 3, 3, 0, 1, 2, 0, 0, 0, 2, 2, 0, 0, 2, 1, 0, 3, 3, 3, 0, 0, 2, 0, 0, 0, 2, 2, 0, 0, 3, 0, 0, 0, 3, 3, 0, 1, 2, 0, 0, 0, 2, 2, 0, 0, 2, 1, 0, 3, 3, 3, 0, 0, 2, 0, 0, 0, 2, 2, 0, 0, 3, 0, 0, 0, 3, 3, 0, 0, 2, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 2, 0, 0, 0, 2, 2, 0, 0, 3, 0, 0, 0, 3, 0, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 2, 0, 0, 3, 3, 3, 0, 0, 2, 0, 0, 2, 2, 2, 0, 0, 3, 0, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 0, 2, 0, 0, 3, 3, 3, 0, 0, 2, 0, 0, 0, 2, 2, 0, 0, 3, 0, 0, 0, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 0, 2, 1, 0, 3, 3, 3, 0, 0, 2, 0, 0, 0, 2, 2, 0, 0, 3, 0, 0, 0, 3, 3, 0, ]; static const List<int> instructionCycles = [ 7, 6, 0, 0, 0, 3, 5, 0, 0, 2, 2, 0, 0, 4, 6, 0, 0, 5, 0, 0, 0, 4, 6, 0, 0, 4, 0, 0, 0, 4, 7, 0, 6, 6, 0, 0, 3, 3, 5, 0, 0, 2, 2, 0, 4, 4, 6, 0, 0, 5, 0, 0, 0, 4, 6, 0, 0, 4, 0, 0, 0, 4, 7, 0, 6, 6, 0, 0, 0, 3, 5, 0, 0, 2, 2, 0, 3, 4, 6, 0, 0, 5, 0, 0, 0, 4, 6, 0, 0, 4, 0, 0, 0, 4, 7, 0, 6, 6, 0, 0, 0, 3, 5, 0, 0, 2, 2, 0, 5, 4, 6, 0, 0, 5, 0, 0, 0, 4, 6, 0, 0, 4, 0, 0, 0, 4, 7, 0, 0, 6, 0, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 4, 4, 0, 0, 6, 0, 0, 0, 4, 4, 0, 0, 5, 0, 0, 0, 5, 0, 0, 2, 6, 2, 0, 3, 3, 3, 0, 0, 2, 0, 0, 4, 4, 4, 0, 0, 5, 0, 0, 4, 4, 4, 0, 0, 4, 0, 0, 4, 4, 4, 0, 2, 6, 0, 0, 3, 3, 5, 0, 0, 2, 0, 0, 4, 4, 6, 0, 0, 5, 0, 0, 0, 4, 6, 0, 0, 4, 0, 0, 0, 4, 7, 0, 2, 6, 0, 0, 3, 3, 5, 0, 0, 2, 2, 0, 4, 4, 6, 0, 0, 5, 0, 0, 0, 4, 6, 0, 0, 4, 0, 0, 0, 4, 7, 0 ]; }
Implementing Address resolution
enum AddressMode { implied, accumulator, immediate, zeroPage, zeroPageX, zeroPageY, relative, absolute, absoluteX, absoluteY, indirect, indexedIndirect, indirectIndexed }Something to note here is that we use the same ordinal order here as which is used in addressModes array defined in the previous section.
int calculateEffectiveAddress(int mode, int operand1, int operand2) { var modeAsEnum = AddressMode.values[mode]; switch (modeAsEnum) { case AddressMode.zeroPage: return operand1; case AddressMode.implied: // TODO: Handle this case. case AddressMode.accumulator: // TODO: Handle this case. case AddressMode.immediate: // TODO: Handle this case. case AddressMode.zeroPageX: return (operand1 + _x) & 0xff; case AddressMode.zeroPageY: return (operand1 + _y) & 0xff; case AddressMode.relative: // TODO: Handle this case. case AddressMode.absolute: return (operand2 << 8) | operand1; case AddressMode.absoluteX: var add = (operand2 << 8) | operand1; return (add + _x) & 0xffff; case AddressMode.absoluteY: var add = (operand2 << 8) | operand1; return (add + _y) & 0xffff; case AddressMode.indirect: // TODO: Handle this case. case AddressMode.indexedIndirect: // LDA ($40,X) var add = operand1 + _x; var readByte0 = memory.getMem(add & 0xff); var readByte1 = memory.getMem((add + 1) & 0xff); return (readByte1 << 8) | readByte0; case AddressMode.indirectIndexed: // LDA ($40),Y var readByte0 = memory.getMem(operand1 & 0xff); var readByte1 = memory.getMem((operand1 + 1) & 0xff); var result = (readByte1 << 8) | readByte0; return (result + _y) & 0xffff; } return 0; }Something to note here is that we receive the mode as an ordinal position, then we convert it to the enum via var modeAsEnum = AddressMode.values[mode]
step() { var opCode = memory.getMem(pc); pc++; var insLen = CpuTables.instructionLen[opCode]; var arg0 = 0; var arg1 = 0; if (insLen > 1) { arg0 = memory.getMem(pc); pc++; } if (insLen > 2) { arg1 = memory.getMem(pc); pc++; } var resolvedAddress = calculateEffectiveAddress( CpuTables.addressModes[opCode], arg0, arg1); switch (opCode) { ... } }You will also see some simplifications here from the previous post. The instruction length lookup table now helps us find the argument bytes before hand, whereas in the previous post he had to do it with in the opCode switch within the case statement of the particular opcode. With this way, our opCode switch statement will not grow so drastically.
Implementing the Load and Store instructions
... /* Zero Page LDA $44 $A5 2 3 Zero Page,X LDA $44,X $B5 2 4 Absolute LDA $4400 $AD 3 4 Absolute,X LDA $4400,X $BD 3 4+ Absolute,Y LDA $4400,Y $B9 3 4+ Indirect,X LDA ($44,X) $A1 2 6 Indirect,Y LDA ($44),Y $B1 2 5+ */ case 0xa9: _a = arg0; _n = (_a & 0x80) != 0; _z = _a == 0; case 0xB5: case 0xAD: case 0xBD: case 0xB9: case 0xA1: case 0xB1: _a = memory.getMem(resolvedAddress); _n = (_a & 0x80) != 0; _z = _a == 0; ...You will see I have introduced two new variables, _n and _z. These are boolean variables for the Negative and zero flags. To keep the discussion simple, I am not going to show you what is required to implement these variables. All I am going to mention, is that you need to follow the same process as we have implemented the accumulator (e.g. _a).
... /* LDX (LoaD X register) Affects Flags: N Z MODE SYNTAX HEX LEN TIM Immediate LDX #$44 $A2 2 2 Zero Page LDX $44 $A6 2 3 Zero Page,Y LDX $44,Y $B6 2 4 Absolute LDX $4400 $AE 3 4 Absolute,Y LDX $4400,Y $BE 3 4+ */ case 0xA2: _x = arg0; _n = (_x & 0x80) != 0; _z = _x == 0; case 0xA6: case 0xB6: case 0xAE: case 0xBE: _x = memory.getMem(resolvedAddress); _n = (_x & 0x80) != 0; _z = _x == 0; /* LDY (LoaD Y register) Affects Flags: N Z MODE SYNTAX HEX LEN TIM Immediate LDY #$44 $A0 2 2 Zero Page LDY $44 $A4 2 3 Zero Page,X LDY $44,X $B4 2 4 Absolute LDY $4400 $AC 3 4 Absolute,X LDY $4400,X $BC 3 4+ */ case 0xA0: _y = arg0; _n = (_y & 0x80) != 0; _z = _y == 0; case 0xA4: case 0xB4: case 0xAC: case 0xBC: _y = memory.getMem(resolvedAddress); _n = (_y & 0x80) != 0; _z = _y == 0; ...Finally, what remains for this post is to implement, STA, STX and STY:
... /* STA (STore Accumulator) Affects Flags: none MODE SYNTAX HEX LEN TIM Zero Page STA $44 $85 2 3 Zero Page,X STA $44,X $95 2 4 Absolute STA $4400 $8D 3 4 Absolute,X STA $4400,X $9D 3 5 Absolute,Y STA $4400,Y $99 3 5 Indirect,X STA ($44,X) $81 2 6 Indirect,Y STA ($44),Y $91 2 6 */ case 0x85: case 0x95: case 0x8D: case 0x9D: case 0x99: case 0x81: case 0x91: memory.setMem(_a, resolvedAddress); /* STX (STore X register) Affects Flags: none MODE SYNTAX HEX LEN TIM Zero Page STX $44 $86 2 3 Zero Page,Y STX $44,Y $96 2 4 Absolute STX $4400 $8E 3 4 */ case 0x86: case 0x96: case 0x8E: memory.setMem(_x, resolvedAddress); /* STY (STore Y register) Affects Flags: none MODE SYNTAX HEX LEN TIM Zero Page STY $44 $84 2 3 Zero Page,X STY $44,X $94 2 4 Absolute STY $4400 $8C 3 4 */ case 0x84: case 0x94: case 0x8C: memory.setMem(_y, resolvedAddress); } } ...
The Test Program
LDA #$40 A9 40 LDY #$06 A0 06 STA ($10),Y 91 10 LDX #$05 A2 05 STA ($12,X) 81 12 LDA #$F0A binary dump of the program will look as follows:
The End Result
In summary
In the next post we will continue to some more instructions to our emulator.
No comments:
Post a Comment