diff --git a/addressing-modes.go b/addressing-modes.go new file mode 100644 index 0000000..a0e3c0a --- /dev/null +++ b/addressing-modes.go @@ -0,0 +1,36 @@ +package i6502 + +// Addressing modes +const ( + _ = iota + absolute + absoluteX + absoluteY + accumulator + immediate + implied + indirect + indirectX + indirectY + relative + zeropage + zeropageX + zoerpageY +) + +var addressingNames = [...]string{ + "", + "absolute", + "absoluteX", + "absoluteY", + "accumulator", + "immediate", + "implied", + "(indirect)", + "(indirect,X)", + "(indirect),Y", + "relative", + "zeropage", + "zeropageX", + "zeropageY", +} diff --git a/cpu.go b/cpu.go index d02d2f4..c150de2 100644 --- a/cpu.go +++ b/cpu.go @@ -1,5 +1,7 @@ package i6502 +import "fmt" + type Cpu struct { bus *AddressBus // The address bus @@ -35,3 +37,14 @@ func (c *Cpu) LoadProgram(data []byte, location uint16) { c.PC = location } + +// Execute the instruction pointed to by the Program Counter (PC) +func (c *Cpu) Step() { + instruction := c.readNextInstruction() + c.PC += uint16(instruction.Size) + fmt.Println(instruction) + c.execute(instruction) +} + +func (c *Cpu) execute(instruction Instruction) { +} diff --git a/cpu_test.go b/cpu_test.go index cab31a0..29a3d43 100644 --- a/cpu_test.go +++ b/cpu_test.go @@ -63,3 +63,10 @@ func TestProgramLoading(t *testing.T) { assert.Equal(0x0300, cpu.PC) } + +func TestNOP(t *testing.T) { + cpu, _, _ := NewRamMachine() + cpu.LoadProgram([]byte{0xEA}, 0x0300) + cpu.Step() + assert.Equal(t, 0x0301, cpu.PC) +} diff --git a/instruction.go b/instruction.go new file mode 100644 index 0000000..52bdd0a --- /dev/null +++ b/instruction.go @@ -0,0 +1,53 @@ +package i6502 + +import ( + "fmt" +) + +type Instruction struct { + // Embed OpType + OpType + + // 8-bit operand for 2-byte instructions + Op8 byte + + // 16-bit operand for 3-byte instructions + Op16 uint16 + + // Address location where this instruction got read + Address uint16 +} + +func (i Instruction) String() (output string) { + switch i.Size { + case 1: + output = fmt.Sprintf("~~ 0x%04X: 0x%02X - %s [%s] {%d}\n", i.Address, i.Opcode, instructionNames[i.opcodeId], addressingNames[i.addressingId], i.Cycles) + case 2: + output = fmt.Sprintf("~~ 0x%04X: 0x%02X - %s 0x%02X [%s] {%d}\n", i.Address, i.Opcode, instructionNames[i.opcodeId], i.Op8, addressingNames[i.addressingId], i.Cycles) + case 3: + output = fmt.Sprintf("~~ 0x%04X: 0x%02X - %s 0x%04X [%s] {%d}\n", i.Address, i.Opcode, instructionNames[i.opcodeId], i.Op16, addressingNames[i.addressingId], i.Cycles) + } + + return +} + +func (c *Cpu) readNextInstruction() Instruction { + // Read the opcode + opcode := c.bus.Read(c.PC) + + optype, ok := opTypes[opcode] + if !ok { + panic(fmt.Sprintf("Unknown or unimplemented opcde 0x%02X", opcode)) + } + + instruction := Instruction{OpType: optype} + switch instruction.Size { + case 1: // Zero operand instruction + case 2: // 8-bit operand + instruction.Op8 = c.bus.Read(c.PC + 1) + case 3: // 16-bit operand + instruction.Op16 = c.bus.Read16(c.PC + 1) + } + + return instruction +} diff --git a/opcodes.go b/opcodes.go new file mode 100644 index 0000000..911d784 --- /dev/null +++ b/opcodes.go @@ -0,0 +1,146 @@ +package i6502 + +// OpCode table +const ( + _ = iota + adc + and + asl + bcc + bcs + beq + bit + bmi + bne + bpl + brk + bvc + bvs + clc + cld + cli + clv + cmp + cpx + cpy + dec + dex + dey + eor + inc + inx + iny + jmp + jsr + lda + ldx + ldy + lsr + nop + ora + pha + php + pla + plp + rol + ror + rti + rts + sbc + sec + sed + sei + sta + stx + sty + tax + tay + tsx + txa + txs + tya +) + +var instructionNames = [...]string{ + "", + "ADC", + "AND", + "ASL", + "BCC", + "BCS", + "BEQ", + "BIT", + "BMI", + "BNE", + "BPL", + "BRK", + "BVC", + "BVS", + "CLC", + "CLD", + "CLI", + "CLV", + "CMP", + "CPX", + "CPY", + "DEC", + "DEX", + "DEY", + "EOR", + "INC", + "INX", + "INY", + "JMP", + "JSR", + "LDA", + "LDX", + "LDY", + "LSR", + "NOP", + "ORA", + "PHA", + "PHP", + "PLA", + "PLP", + "ROL", + "ROR", + "RTI", + "RTS", + "SBC", + "SEC", + "SED", + "SEI", + "STA", + "STX", + "STY", + "TAX", + "TAY", + "TSX", + "TXA", + "TXS", + "TYA", +} + +// OpType is the operation type, it includes the instruction and +// addressing mode. It also includes some extra information for the +// emulator, like number of cycles +type OpType struct { + // The actual Opcode byte read from memory + Opcode byte + + // Opcode ID + opcodeId uint8 + + // Addressing Mode ID + addressingId uint8 + + // Size of this instruction, either 1, 2 or 3 bytes + Size uint8 + + // Number of clock cycles this instruction needs + Cycles uint8 +} + +var opTypes = map[uint8]OpType{ + 0xEA: OpType{0xEA, nop, implied, 1, 2}, +}