Foreword
In the previous post we implemented the stack and related operations.
In this post we will be implementing the remaining 6502 instructions which are the following:
- Logical instructions
- Instructions for setting and clearing flags.
- Transfer Instructions
- The Bit instruction
- Bit shifting instructions
For each of these instructions I will be giving a very brief description.
In this post I will not be giving a test program since we will anyway be running a comprehensive Test Suite on our emulator in the next post.
The Logical Instructions
The 6502 provides instructions for the following logical operations: AND, OR and XOR.Let us quickly implement them:
/*AND AND Memory with Accumulator
A AND M -> A N Z C I D V
+ + - - - -
addressing assembler opc bytes cyles
--------------------------------------------
immediate AND #oper 29 2 2
zeropage AND oper 25 2 3
zeropage,X AND oper,X 35 2 4
absolute AND oper 2D 3 4
absolute,X AND oper,X 3D 3 4*
absolute,Y AND oper,Y 39 3 4*
(indirect,X) AND (oper,X) 21 2 6
(indirect),Y AND (oper),Y 31 2 5* */
case 0x29: acc = acc & arg1;
zeroFlag = (acc == 0) ? 1 : 0;
negativeFlag = ((acc & 0x80) != 0) ? 1 : 0;
break;
case 0x25:
case 0x35:
case 0x2D:
case 0x3D:
case 0x39:
case 0x21:
case 0x31: acc = acc & memory_read(effectiveAdrress);
zeroFlag = (acc == 0) ? 1 : 0;
negativeFlag = ((acc & 0x80) != 0) ? 1 : 0;
break;
/*EOR Exclusive-OR Memory with Accumulator
A EOR M -> A N Z C I D V
+ + - - - -
addressing assembler opc bytes cyles
--------------------------------------------
immediate EOR #oper 49 2 2
zeropage EOR oper 45 2 3
zeropage,X EOR oper,X 55 2 4
absolute EOR oper 4D 3 4
absolute,X EOR oper,X 5D 3 4*
absolute,Y EOR oper,Y 59 3 4*
(indirect,X) EOR (oper,X) 41 2 6
(indirect),Y EOR (oper),Y 51 2 5* */
case 0x49: acc = acc ^ arg1;
zeroFlag = (acc == 0) ? 1 : 0;
negativeFlag = ((acc & 0x80) != 0) ? 1 : 0;
break;
case 0x45:
case 0x55:
case 0x4D:
case 0x5D:
case 0x59:
case 0x41:
case 0x51: acc = acc ^ memory_read(effectiveAdrress);
zeroFlag = (acc == 0) ? 1 : 0;
negativeFlag = ((acc & 0x80) != 0) ? 1 : 0;
break;
/*ORA OR Memory with Accumulator
A OR M -> A N Z C I D V
+ + - - - -
addressing assembler opc bytes cyles
--------------------------------------------
immediate ORA #oper 09 2 2
zeropage ORA oper 05 2 3
zeropage,X ORA oper,X 15 2 4
absolute ORA oper 0D 3 4
absolute,X ORA oper,X 1D 3 4*
absolute,Y ORA oper,Y 19 3 4*
(indirect,X) ORA (oper,X) 01 2 6
(indirect),Y ORA (oper),Y 11 2 5* */
case 0x09: acc = acc | arg1;
zeroFlag = (acc == 0) ? 1 : 0;
negativeFlag = ((acc & 0x80) != 0) ? 1 : 0;
break;
case 0x05:
case 0x15:
case 0x0D:
case 0x1D:
case 0x19:
case 0x01:
case 0x11: acc = acc | memory_read(effectiveAdrress);
zeroFlag = (acc == 0) ? 1 : 0;
negativeFlag = ((acc & 0x80) != 0) ? 1 : 0;
break;
Setting clearing/flags
The 6502 provides a bunch of instructions for setting and clearing the flags.Here is their implementation:
/*CLC Clear Carry Flag
0 -> C N Z C I D V
- - 0 - - -
addressing assembler opc bytes cyles
--------------------------------------------
implied CLC 18 1 2 */
case 0x18:
carryFlag = 0;
break;
/*CLV Clear Overflow Flag
0 -> V N Z C I D V
- - - - - 0
addressing assembler opc bytes cyles
--------------------------------------------
implied CLV B8 1 2 */
case 0xB8:
overflowFlag = 0;
break;
/*SEC Set Carry Flag
1 -> C N Z C I D V
- - 1 - - -
addressing assembler opc bytes cyles
--------------------------------------------
implied SEC 38 1 2 */
case 0x38:
carryFlag = 1;
break;
Transfer Instructions
The 6502 provides you with a couple of instructions that enables you to transfer the contents of register to another register.Here is their implementation:
/*TAX Transfer Accumulator to Index X
A -> X N Z C I D V
+ + - - - -
addressing assembler opc bytes cyles
--------------------------------------------
implied TAX AA 1 2 */
case 0xAA:
xReg = acc;
zeroFlag = (xReg == 0) ? 1 : 0;
negativeFlag = ((xReg & 0x80) != 0) ? 1 : 0;
break;
/*TAY Transfer Accumulator to Index Y
A -> Y N Z C I D V
+ + - - - -
addressing assembler opc bytes cyles
--------------------------------------------
implied TAY A8 1 2 */
case 0xA8:
yReg = acc;
zeroFlag = (yReg == 0) ? 1 : 0;
negativeFlag = ((yReg & 0x80) != 0) ? 1 : 0;
break;
/*TSX Transfer Stack Pointer to Index X
SP -> X N Z C I D V
+ + - - - -
addressing assembler opc bytes cyles
--------------------------------------------
implied TSX BA 1 2 */
case 0xBA:
xReg = sp;
zeroFlag = (xReg == 0) ? 1 : 0;
negativeFlag = ((xReg & 0x80) != 0) ? 1 : 0;
break;
/*TXA Transfer Index X to Accumulator
X -> A N Z C I D V
+ + - - - -
addressing assembler opc bytes cyles
--------------------------------------------
implied TXA 8A 1 2 */
case 0x8A:
acc = xReg;
zeroFlag = (acc == 0) ? 1 : 0;
negativeFlag = ((acc & 0x80) != 0) ? 1 : 0;
break;
/*TXS Transfer Index X to Stack Register
X -> SP N Z C I D V
+ + - - - -
addressing assembler opc bytes cyles
--------------------------------------------
implied TXS 9A 1 2 */
case 0x9A:
sp = xReg;
break;
/*TYA Transfer Index Y to Accumulator
Y -> A N Z C I D V
+ + - - - -
addressing assembler opc bytes cyles
--------------------------------------------
implied TYA 98 1 2 */
case 0x98:
acc = yReg;
zeroFlag = (acc == 0) ? 1 : 0;
negativeFlag = ((acc & 0x80) != 0) ? 1 : 0;
break;
The Bit Instruction
The Bit instruction provides you with a way to quickly test bits 6 and 7 of a memory location and then provide a subsequent branch instruction to branch on the result of the test.Here is the implementation of the BIT instruction:
/*BIT Test Bits in Memory with Accumulator
bits 7 and 6 of operand are transfered to bit 7 and 6 of SR (N,V);
the zeroflag is set to the result of operand AND accumulator.
A AND M, M7 -> N, M6 -> V N Z C I D V
M7 + - - - M6
addressing assembler opc bytes cyles
--------------------------------------------
zeropage BIT oper 24 2 3
absolute BIT oper 2C 3 4 */
case 0x24:
case 0x2C:
tempVal = memory_read(effectiveAdrress);
negativeFlag = ((tempVal & 0x80) != 0) ? 1 : 0;
overflowFlag = ((tempVal & 0x40) != 0) ? 1 : 0;
zeroFlag = ((acc & tempVal) == 0) ? 1 : 0;
break;
Bit Shifting Instructions
The 6502 provides you with a set of instructions allowing you to rotate the bits of the Accumulator or a particular memory location in a given direction.Here is the implementation of the Bit Shifting Instructions:
/*ASL Shift Left One Bit (Memory or Accumulator)
C <- [76543210] <- 0 N Z C I D V
+ + + - - -
addressing assembler opc bytes cyles
--------------------------------------------
accumulator ASL A 0A 1 2
zeropage ASL oper 06 2 5
zeropage,X ASL oper,X 16 2 6
absolute ASL oper 0E 3 6
absolute,X ASL oper,X 1E 3 7 */
case 0x0A:
acc = acc << 1;
carryFlag = ((acc & 0x100) != 0) ? 1 : 0;
acc = acc & 0xff;
negativeFlag = ((acc & 0x80) != 0) ? 1 : 0;
zeroFlag = (acc == 0) ? 1 : 0;
break;
case 0x06:
case 0x16:
case 0x0E:
case 0x1E:
tempVal = memory_read(effectiveAdrress);
tempVal = tempVal << 1;
carryFlag = ((tempVal & 0x100) != 0) ? 1 : 0;
tempVal = tempVal & 0xff;
negativeFlag = ((tempVal & 0x80) != 0) ? 1 : 0;
zeroFlag = (tempVal == 0) ? 1 : 0;
memory_write(effectiveAdrress, tempVal);
break;
/*LSR Shift One Bit Right (Memory or Accumulator)
0 -> [76543210] -> C N Z C I D V
- + + - - -
addressing assembler opc bytes cyles
--------------------------------------------
accumulator LSR A 4A 1 2
zeropage LSR oper 46 2 5
zeropage,X LSR oper,X 56 2 6
absolute LSR oper 4E 3 6
absolute,X LSR oper,X 5E 3 7 */
case 0x4A:
carryFlag = ((acc & 0x1) != 0) ? 1 : 0;
acc = acc >> 1;
acc = acc & 0xff;
zeroFlag = (acc == 0) ? 1 : 0;
break;
case 0x46:
case 0x56:
case 0x4E:
case 0x5E:
tempVal = memory_read(effectiveAdrress);
carryFlag = ((tempVal & 0x1) != 0) ? 1 : 0;
tempVal = tempVal >> 1;
tempVal = tempVal & 0xff;
zeroFlag = (tempVal == 0) ? 1 : 0;
memory_write(effectiveAdrress, tempVal);
break;
/*ROL Rotate One Bit Left (Memory or Accumulator)
C <- [76543210] <- C N Z C I D V
+ + + - - -
addressing assembler opc bytes cyles
--------------------------------------------
accumulator ROL A 2A 1 2
zeropage ROL oper 26 2 5
zeropage,X ROL oper,X 36 2 6
absolute ROL oper 2E 3 6
absolute,X ROL oper,X 3E 3 7 */
case 0x2A:
acc = acc << 1;
acc = acc | carryFlag;
carryFlag = ((acc & 0x100) != 0) ? 1 : 0;
acc = acc & 0xff;
zeroFlag = (acc == 0) ? 1 : 0;
negativeFlag = ((acc & 0x80) != 0) ? 1 : 0;
break;
case 0x26:
case 0x36:
case 0x2E:
case 0x3E:
tempVal = memory_read(effectiveAdrress);
tempVal = tempVal << 1;
tempVal = tempVal | carryFlag;
carryFlag = ((tempVal & 0x100) != 0) ? 1 : 0;
tempVal = tempVal & 0xff;
zeroFlag = (tempVal == 0) ? 1 : 0;
negativeFlag = ((tempVal & 0x80) != 0) ? 1 : 0;
memory_write(effectiveAdrress,tempVal);
break;
/*ROR Rotate One Bit Right (Memory or Accumulator)
C -> [76543210] -> C N Z C I D V
+ + + - - -
addressing assembler opc bytes cyles
--------------------------------------------
accumulator ROR A 6A 1 2
zeropage ROR oper 66 2 5
zeropage,X ROR oper,X 76 2 6
absolute ROR oper 6E 3 6
absolute,X ROR oper,X 7E 3 7 */
case 0x6A:
acc = acc | (carryFlag << 8);
carryFlag = ((acc & 0x1) != 0) ? 1 : 0;
acc = acc >> 1;
acc = acc & 0xff;
zeroFlag = (acc == 0) ? 1 : 0;
negativeFlag = ((acc & 0x80) != 0) ? 1 : 0;
break;
case 0x66:
case 0x76:
case 0x6E:
case 0x7E:
tempVal = memory_read(effectiveAdrress);
tempVal = tempVal | (carryFlag << 8);
carryFlag = ((tempVal & 0x1) != 0) ? 1 : 0;
tempVal = tempVal >> 1;
tempVal = tempVal & 0xff;
zeroFlag = (tempVal == 0) ? 1 : 0;
negativeFlag = ((tempVal & 0x80) != 0) ? 1 : 0;
memory_write(effectiveAdrress, tempVal);
break;
In Summary
In this post we have implemented the remaining 6502 Instruction.In the next post we will putting our emulator through its paces with a Test Suite.
Till next time!
No comments:
Post a Comment