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