From 55ad73d01db7559a9d202562467f271481be553f Mon Sep 17 00:00:00 2001 From: Ivan Izaguirre Date: Thu, 28 Feb 2019 23:54:38 +0100 Subject: [PATCH] Reorg 6502 files. Count cycles. Pending extra cycles on page change. --- apple2/apple2.go | 10 +- core6502/6502functional_test.go | 19 +- core6502/addressing.go | 103 ++++++ core6502/execute.go | 567 ++------------------------------ core6502/execute_test.go | 544 +++++++++++++++--------------- core6502/opcodesNMOS6502.go | 184 +++++++++++ core6502/operations.go | 242 ++++++++++++++ 7 files changed, 838 insertions(+), 831 deletions(-) create mode 100644 core6502/addressing.go create mode 100644 core6502/opcodesNMOS6502.go create mode 100644 core6502/operations.go diff --git a/apple2/apple2.go b/apple2/apple2.go index fe4694a..932e44b 100644 --- a/apple2/apple2.go +++ b/apple2/apple2.go @@ -5,17 +5,15 @@ import "go6502/core6502" // Run instantiates an apple2 and start emulation func Run(romFile string, log bool) { mmu := newAddressSpace(romFile) - - var s core6502.State - s.Mem = mmu - + s := core6502.NewNMOS6502(mmu) fe := newAnsiConsoleFrontend(mmu) mmu.ioPage.setKeyboardProvider(fe) + go fe.textModeGoRoutine() // Start the processor - core6502.Reset(&s) + s.Reset() for { - core6502.ExecuteInstruction(&s, log) + s.ExecuteInstruction(log) } } diff --git a/core6502/6502functional_test.go b/core6502/6502functional_test.go index 8243f2d..5584cf7 100644 --- a/core6502/6502functional_test.go +++ b/core6502/6502functional_test.go @@ -6,18 +6,15 @@ import ( ) func TestFunctional(t *testing.T) { + m := new(FlatMemory) + s := NewNMOS6502(m) - //t.SkipNow() - - var s State - var m FlatMemory - s.Mem = &m // Test suite from https://github.com/Klaus2m5/6502_65C02_functional_tests m.loadBinary("testdata/6502_functional_test.bin") - s.Reg.setPC(0x0400) + s.reg.setPC(0x0400) for true { - testCase := s.Mem.Peek(0x0200) + testCase := s.mem.Peek(0x0200) if testCase >= 240 { break } @@ -25,10 +22,12 @@ func TestFunctional(t *testing.T) { if log { fmt.Printf("[ %d ] ", testCase) } - pc := s.Reg.getPC() - ExecuteInstruction(&s, log) - if pc == s.Reg.getPC() { + pc := s.reg.getPC() + s.ExecuteInstruction(log) + if pc == s.reg.getPC() { t.Errorf("Failure in test %v.", testCase) } } + + t.Errorf("Tests complited in %v megacycles.\n", s.cycles/1000000) } diff --git a/core6502/addressing.go b/core6502/addressing.go new file mode 100644 index 0000000..37bd7dc --- /dev/null +++ b/core6502/addressing.go @@ -0,0 +1,103 @@ +package core6502 + +const ( + modeImplicit = iota + 1 + modeImplicitX + modeImplicitY + modeAccumulator + modeImmediate + modeZeroPage + modeZeroPageX + modeZeroPageY + modeRelative + modeAbsolute + modeAbsoluteX + modeAbsoluteY + modeIndirect + modeIndexedIndirectX + modeIndirectIndexedY +) + +func getWordInLine(line []uint8) uint16 { + return uint16(line[1]) + 0x100*uint16(line[2]) +} + +func resolveValue(s *State, line []uint8, opcode opcode) uint8 { + getValue, _, _ := resolve(s, line, opcode) + return getValue() +} + +func resolveGetSetValue(s *State, line []uint8, opcode opcode) (value uint8, setValue func(uint8)) { + getValue, setValue, _ := resolve(s, line, opcode) + value = getValue() + return +} + +func resolveSetValue(s *State, line []uint8, opcode opcode) func(uint8) { + _, setValue, _ := resolve(s, line, opcode) + return setValue +} + +func resolveAddress(s *State, line []uint8, opcode opcode) uint16 { + _, _, address := resolve(s, line, opcode) + return address +} + +func resolve(s *State, line []uint8, opcode opcode) (getValue func() uint8, setValue func(uint8), address uint16) { + hasAddress := true + register := regNone + + switch opcode.addressMode { + case modeAccumulator: + getValue = func() uint8 { return s.reg.getA() } + hasAddress = false + register = regA + case modeImplicitX: + getValue = func() uint8 { return s.reg.getX() } + hasAddress = false + register = regX + case modeImplicitY: + getValue = func() uint8 { return s.reg.getY() } + hasAddress = false + register = regY + case modeImmediate: + getValue = func() uint8 { return line[1] } + hasAddress = false + case modeZeroPage: + address = uint16(line[1]) + case modeZeroPageX: + address = uint16(line[1] + s.reg.getX()) + case modeZeroPageY: + address = uint16(line[1] + s.reg.getY()) + case modeAbsolute: + address = getWordInLine(line) + case modeAbsoluteX: + address = getWordInLine(line) + uint16(s.reg.getX()) + case modeAbsoluteY: + address = getWordInLine(line) + uint16(s.reg.getY()) + case modeIndexedIndirectX: + addressAddress := uint8(line[1] + s.reg.getX()) + address = getZeroPageWord(s.mem, addressAddress) + case modeIndirect: + addressAddress := getWordInLine(line) + address = getWord(s.mem, addressAddress) + case modeIndirectIndexedY: + address = getZeroPageWord(s.mem, line[1]) + + uint16(s.reg.getY()) + } + + if hasAddress { + getValue = func() uint8 { return s.mem.Peek(address) } + } + + setValue = func(value uint8) { + if hasAddress { + s.mem.Poke(address, value) + } else if register != regNone { + s.reg.setRegister(register, value) + } else { + // Todo: assert impossible + } + } + return +} diff --git a/core6502/execute.go b/core6502/execute.go index 84b66e1..21e127c 100644 --- a/core6502/execute.go +++ b/core6502/execute.go @@ -2,124 +2,24 @@ package core6502 import "fmt" -// State represents the state of the simulated device -type State struct { - Reg registers - Mem Memory -} - -const ( - modeImplicit = iota + 1 - modeImplicitX - modeImplicitY - modeAccumulator - modeImmediate - modeZeroPage - modeZeroPageX - modeZeroPageY - modeRelative - modeAbsolute - modeAbsoluteX - modeAbsoluteY - modeIndirect - modeIndexedIndirectX - modeIndirectIndexedY -) - -const ( - vectorNMI uint16 = 0xfffa - vectorReset uint16 = 0xfffc - vectorBreak uint16 = 0xfffe -) - // https://www.masswerk.at/6502/6502_instruction_set.html // http://www.emulator101.com/reference/6502-reference.html // https://www.csh.rit.edu/~moffitt/docs/6502.html#FLAGS // https://ia800509.us.archive.org/18/items/Programming_the_6502/Programming_the_6502.pdf -func getWordInLine(line []uint8) uint16 { - return uint16(line[1]) + 0x100*uint16(line[2]) +// State represents the state of the simulated device +type State struct { + reg registers + mem Memory + cycles int64 + opcodes *[256]opcode } -func resolveValue(s *State, line []uint8, opcode opcode) uint8 { - getValue, _, _ := resolve(s, line, opcode) - return getValue() -} - -func resolveGetSetValue(s *State, line []uint8, opcode opcode) (value uint8, setValue func(uint8)) { - getValue, setValue, _ := resolve(s, line, opcode) - value = getValue() - return -} - -func resolveSetValue(s *State, line []uint8, opcode opcode) func(uint8) { - _, setValue, _ := resolve(s, line, opcode) - return setValue -} - -func resolveAddress(s *State, line []uint8, opcode opcode) uint16 { - _, _, address := resolve(s, line, opcode) - return address -} - -func resolve(s *State, line []uint8, opcode opcode) (getValue func() uint8, setValue func(uint8), address uint16) { - hasAddress := true - register := regNone - - switch opcode.addressMode { - case modeAccumulator: - getValue = func() uint8 { return s.Reg.getA() } - hasAddress = false - register = regA - case modeImplicitX: - getValue = func() uint8 { return s.Reg.getX() } - hasAddress = false - register = regX - case modeImplicitY: - getValue = func() uint8 { return s.Reg.getY() } - hasAddress = false - register = regY - case modeImmediate: - getValue = func() uint8 { return line[1] } - hasAddress = false - case modeZeroPage: - address = uint16(line[1]) - case modeZeroPageX: - address = uint16(line[1] + s.Reg.getX()) - case modeZeroPageY: - address = uint16(line[1] + s.Reg.getY()) - case modeAbsolute: - address = getWordInLine(line) - case modeAbsoluteX: - address = getWordInLine(line) + uint16(s.Reg.getX()) - case modeAbsoluteY: - address = getWordInLine(line) + uint16(s.Reg.getY()) - case modeIndexedIndirectX: - addressAddress := uint8(line[1] + s.Reg.getX()) - address = getZeroPageWord(s.Mem, addressAddress) - case modeIndirect: - addressAddress := getWordInLine(line) - address = getWord(s.Mem, addressAddress) - case modeIndirectIndexedY: - address = getZeroPageWord(s.Mem, line[1]) + - uint16(s.Reg.getY()) - } - - if hasAddress { - getValue = func() uint8 { return s.Mem.Peek(address) } - } - - setValue = func(value uint8) { - if hasAddress { - s.Mem.Poke(address, value) - } else if register != regNone { - s.Reg.setRegister(register, value) - } else { - // Todo: assert impossible - } - } - return -} +const ( + vectorNMI uint16 = 0xfffa + vectorReset uint16 = 0xfffc + vectorBreak uint16 = 0xfffe +) type opcode struct { name string @@ -131,424 +31,8 @@ type opcode struct { type opFunc func(s *State, line []uint8, opcode opcode) -func buildOpTransfer(regSrc int, regDst int) opFunc { - return func(s *State, line []uint8, opcode opcode) { - value := s.Reg.getRegister(regSrc) - s.Reg.setRegister(regDst, value) - if regDst != regSP { - s.Reg.updateFlagZN(value) - } - } -} - -func buildOpIncDec(inc bool) opFunc { - return func(s *State, line []uint8, opcode opcode) { - value, setValue := resolveGetSetValue(s, line, opcode) - if inc { - value++ - } else { - value-- - } - s.Reg.updateFlagZN(value) - setValue(value) - } -} - -func buildOpShift(isLeft bool, isRotate bool) opFunc { - return func(s *State, line []uint8, opcode opcode) { - value, setValue := resolveGetSetValue(s, line, opcode) - - oldCarry := s.Reg.getFlagBit(flagC) - var carry bool - if isLeft { - carry = (value & 0x80) != 0 - value <<= 1 - if isRotate { - value += oldCarry - } - } else { - carry = (value & 0x01) != 0 - value >>= 1 - if isRotate { - value += oldCarry << 7 - } - } - s.Reg.updateFlag(flagC, carry) - s.Reg.updateFlagZN(value) - setValue(value) - } -} - -func buildOpLoad(regDst int) opFunc { - return func(s *State, line []uint8, opcode opcode) { - value := resolveValue(s, line, opcode) - s.Reg.setRegister(regDst, value) - s.Reg.updateFlagZN(value) - } -} - -func buildOpStore(regSrc int) opFunc { - return func(s *State, line []uint8, opcode opcode) { - setValue := resolveSetValue(s, line, opcode) - value := s.Reg.getRegister(regSrc) - setValue(value) - } -} - -func buildOpUpdateFlag(flag uint8, value bool) opFunc { - return func(s *State, line []uint8, opcode opcode) { - s.Reg.updateFlag(flag, value) - } -} - -func buildOpBranch(flag uint8, value bool) opFunc { - return func(s *State, line []uint8, opcode opcode) { - if s.Reg.getFlag(flag) == value { - // This assumes that PC is already pointing to the next instruction - pc := s.Reg.getPC() - pc += uint16(int8(line[1])) - s.Reg.setPC(pc) - } - } -} - -func opBIT(s *State, line []uint8, opcode opcode) { - value := resolveValue(s, line, opcode) - acc := s.Reg.getA() - // Future note: The immediate addressing mode (65C02 or 65816 only) does not affect V. - s.Reg.updateFlag(flagZ, value&acc == 0) - s.Reg.updateFlag(flagN, value&(1<<7) != 0) - s.Reg.updateFlag(flagV, value&(1<<6) != 0) -} - -func buildOpCompare(reg int) opFunc { - return func(s *State, line []uint8, opcode opcode) { - value := resolveValue(s, line, opcode) - reference := s.Reg.getRegister(reg) - s.Reg.updateFlagZN(reference - value) - s.Reg.updateFlag(flagC, reference >= value) - } -} - -func operationAnd(a uint8, b uint8) uint8 { return a & b } -func operationOr(a uint8, b uint8) uint8 { return a | b } -func operationXor(a uint8, b uint8) uint8 { return a ^ b } - -func buildOpLogic(operation func(uint8, uint8) uint8) opFunc { - return func(s *State, line []uint8, opcode opcode) { - value := resolveValue(s, line, opcode) - result := operation(value, s.Reg.getA()) - s.Reg.setA(result) - s.Reg.updateFlagZN(result) - } -} - -func opADC(s *State, line []uint8, opcode opcode) { - value := resolveValue(s, line, opcode) - aValue := s.Reg.getA() - carry := s.Reg.getFlagBit(flagC) - - total := uint16(aValue) + uint16(value) + uint16(carry) - signedTotal := int16(int8(aValue)) + int16(int8(value)) + int16(carry) - truncated := uint8(total) - - if s.Reg.getFlag(flagD) { - totalBcdLo := int(aValue&0x0f) + int(value&0x0f) + int(carry) - totalBcdHi := int(aValue>>4) + int(value>>4) - if totalBcdLo >= 10 { - totalBcdHi++ - } - totalBcd := (totalBcdHi%10)<<4 + (totalBcdLo % 10) - s.Reg.setA(uint8(totalBcd)) - s.Reg.updateFlag(flagC, totalBcdHi > 9) - } else { - s.Reg.setA(truncated) - s.Reg.updateFlag(flagC, total > 0xFF) - } - - // ZNV flags behave for BCD as if the operation was binary? - s.Reg.updateFlagZN(truncated) - s.Reg.updateFlag(flagV, signedTotal < -128 || signedTotal > 127) -} - -func opSBC(s *State, line []uint8, opcode opcode) { - value := resolveValue(s, line, opcode) - aValue := s.Reg.getA() - carry := s.Reg.getFlagBit(flagC) - - total := 0x100 + uint16(aValue) - uint16(value) + uint16(carry) - 1 - signedTotal := int16(int8(aValue)) - int16(int8(value)) + int16(carry) - 1 - truncated := uint8(total) - - if s.Reg.getFlag(flagD) { - totalBcdLo := 10 + int(aValue&0x0f) - int(value&0x0f) + int(carry) - 1 - totalBcdHi := 10 + int(aValue>>4) - int(value>>4) - if totalBcdLo < 10 { - totalBcdHi-- - } - totalBcd := (totalBcdHi%10)<<4 + (totalBcdLo % 10) - s.Reg.setA(uint8(totalBcd)) - s.Reg.updateFlag(flagC, totalBcdHi >= 10) - } else { - s.Reg.setA(truncated) - s.Reg.updateFlag(flagC, total > 0xFF) - } - - // ZNV flags behave for SBC as if the operation was binary - s.Reg.updateFlagZN(truncated) - s.Reg.updateFlag(flagV, signedTotal < -128 || signedTotal > 127) -} - -const stackAddress uint16 = 0x0100 - -func pushByte(s *State, value uint8) { - adresss := stackAddress + uint16(s.Reg.getSP()) - s.Mem.Poke(adresss, value) - s.Reg.setSP(s.Reg.getSP() - 1) -} - -func pullByte(s *State) uint8 { - s.Reg.setSP(s.Reg.getSP() + 1) - adresss := stackAddress + uint16(s.Reg.getSP()) - return s.Mem.Peek(adresss) -} - -func pushWord(s *State, value uint16) { - pushByte(s, uint8(value>>8)) - pushByte(s, uint8(value)) -} - -func pullWord(s *State) uint16 { - return uint16(pullByte(s)) + - (uint16(pullByte(s)) << 8) - -} - -func opPLA(s *State, line []uint8, opcode opcode) { - value := pullByte(s) - s.Reg.setA(value) - s.Reg.updateFlagZN(value) -} - -func opPLP(s *State, line []uint8, opcode opcode) { - value := pullByte(s) - s.Reg.setP(value) -} - -func opPHA(s *State, line []uint8, opcode opcode) { - pushByte(s, s.Reg.getA()) -} - -func opPHP(s *State, line []uint8, opcode opcode) { - pushByte(s, s.Reg.getP()|(flagB+flag5)) -} - -func opJMP(s *State, line []uint8, opcode opcode) { - address := resolveAddress(s, line, opcode) - s.Reg.setPC(address) -} - -func opNOP(s *State, line []uint8, opcode opcode) {} - -func opJSR(s *State, line []uint8, opcode opcode) { - pushWord(s, s.Reg.getPC()-1) - address := resolveAddress(s, line, opcode) - s.Reg.setPC(address) -} - -func opRTI(s *State, line []uint8, opcode opcode) { - s.Reg.setP(pullByte(s)) - s.Reg.setPC(pullWord(s)) -} - -func opRTS(s *State, line []uint8, opcode opcode) { - s.Reg.setPC(pullWord(s) + 1) -} - -func opBRK(s *State, line []uint8, opcode opcode) { - pushWord(s, s.Reg.getPC()+1) - pushByte(s, s.Reg.getP()|(flagB+flag5)) - s.Reg.setFlag(flagI) - s.Reg.setPC(getWord(s.Mem, vectorBreak)) -} - -var opcodes = [256]opcode{ - 0x00: opcode{"BRK", 1, 7, modeImplicit, opBRK}, - 0x4C: opcode{"JMP", 3, 3, modeAbsolute, opJMP}, - 0x6C: opcode{"JMP", 3, 3, modeIndirect, opJMP}, - 0x20: opcode{"JSR", 3, 6, modeAbsolute, opJSR}, - 0x40: opcode{"RTI", 1, 6, modeImplicit, opRTI}, - 0x60: opcode{"RTS", 1, 6, modeImplicit, opRTS}, - - 0x48: opcode{"PHA", 1, 3, modeImplicit, opPHA}, - 0x08: opcode{"PHP", 1, 3, modeImplicit, opPHP}, - 0x68: opcode{"PLA", 1, 4, modeImplicit, opPLA}, - 0x28: opcode{"PLP", 1, 4, modeImplicit, opPLP}, - - 0x09: opcode{"ORA", 2, 2, modeImmediate, buildOpLogic(operationOr)}, - 0x05: opcode{"ORA", 2, 3, modeZeroPage, buildOpLogic(operationOr)}, - 0x15: opcode{"ORA", 2, 4, modeZeroPageX, buildOpLogic(operationOr)}, - 0x0D: opcode{"ORA", 3, 4, modeAbsolute, buildOpLogic(operationOr)}, - 0x1D: opcode{"ORA", 3, 4, modeAbsoluteX, buildOpLogic(operationOr)}, // Extra cycles - 0x19: opcode{"ORA", 3, 4, modeAbsoluteY, buildOpLogic(operationOr)}, // Extra cycles - 0x01: opcode{"ORA", 2, 6, modeIndexedIndirectX, buildOpLogic(operationOr)}, - 0x11: opcode{"ORA", 2, 5, modeIndirectIndexedY, buildOpLogic(operationOr)}, // Extra cycles - - 0x29: opcode{"AND", 2, 2, modeImmediate, buildOpLogic(operationAnd)}, - 0x25: opcode{"AND", 2, 3, modeZeroPage, buildOpLogic(operationAnd)}, - 0x35: opcode{"AND", 2, 4, modeZeroPageX, buildOpLogic(operationAnd)}, - 0x2D: opcode{"AND", 3, 4, modeAbsolute, buildOpLogic(operationAnd)}, - 0x3D: opcode{"AND", 3, 4, modeAbsoluteX, buildOpLogic(operationAnd)}, // Extra cycles - 0x39: opcode{"AND", 3, 4, modeAbsoluteY, buildOpLogic(operationAnd)}, // Extra cycles - 0x21: opcode{"AND", 2, 6, modeIndexedIndirectX, buildOpLogic(operationAnd)}, - 0x31: opcode{"AND", 2, 5, modeIndirectIndexedY, buildOpLogic(operationAnd)}, // Extra cycles - - 0x49: opcode{"EOR", 2, 2, modeImmediate, buildOpLogic(operationXor)}, - 0x45: opcode{"EOR", 2, 3, modeZeroPage, buildOpLogic(operationXor)}, - 0x55: opcode{"EOR", 2, 4, modeZeroPageX, buildOpLogic(operationXor)}, - 0x4D: opcode{"EOR", 3, 4, modeAbsolute, buildOpLogic(operationXor)}, - 0x5D: opcode{"EOR", 3, 4, modeAbsoluteX, buildOpLogic(operationXor)}, // Extra cycles - 0x59: opcode{"EOR", 3, 4, modeAbsoluteY, buildOpLogic(operationXor)}, // Extra cycles - 0x41: opcode{"EOR", 2, 6, modeIndexedIndirectX, buildOpLogic(operationXor)}, - 0x51: opcode{"EOR", 2, 5, modeIndirectIndexedY, buildOpLogic(operationXor)}, // Extra cycles - - 0x69: opcode{"ADC", 2, 2, modeImmediate, opADC}, - 0x65: opcode{"ADC", 2, 3, modeZeroPage, opADC}, - 0x75: opcode{"ADC", 2, 4, modeZeroPageX, opADC}, - 0x6D: opcode{"ADC", 3, 4, modeAbsolute, opADC}, - 0x7D: opcode{"ADC", 3, 4, modeAbsoluteX, opADC}, // Extra cycles - 0x79: opcode{"ADC", 3, 4, modeAbsoluteY, opADC}, // Extra cycles - 0x61: opcode{"ADC", 2, 6, modeIndexedIndirectX, opADC}, - 0x71: opcode{"ADC", 2, 5, modeIndirectIndexedY, opADC}, // Extra cycles - - 0xE9: opcode{"SBC", 2, 2, modeImmediate, opSBC}, - 0xE5: opcode{"SBC", 2, 3, modeZeroPage, opSBC}, - 0xF5: opcode{"SBC", 2, 4, modeZeroPageX, opSBC}, - 0xED: opcode{"SBC", 3, 4, modeAbsolute, opSBC}, - 0xFD: opcode{"SBC", 3, 4, modeAbsoluteX, opSBC}, // Extra cycles - 0xF9: opcode{"SBC", 3, 4, modeAbsoluteY, opSBC}, // Extra cycles - 0xE1: opcode{"SBC", 2, 6, modeIndexedIndirectX, opSBC}, - 0xF1: opcode{"SBC", 2, 5, modeIndirectIndexedY, opSBC}, // Extra cycles - - 0x24: opcode{"BIT", 2, 3, modeZeroPage, opBIT}, - 0x2C: opcode{"BIT", 3, 3, modeAbsolute, opBIT}, - - 0xC9: opcode{"CMP", 2, 2, modeImmediate, buildOpCompare(regA)}, - 0xC5: opcode{"CMP", 2, 3, modeZeroPage, buildOpCompare(regA)}, - 0xD5: opcode{"CMP", 2, 4, modeZeroPageX, buildOpCompare(regA)}, - 0xCD: opcode{"CMP", 3, 4, modeAbsolute, buildOpCompare(regA)}, - 0xDD: opcode{"CMP", 3, 4, modeAbsoluteX, buildOpCompare(regA)}, // Extra cycles - 0xD9: opcode{"CMP", 3, 4, modeAbsoluteY, buildOpCompare(regA)}, // Extra cycles - 0xC1: opcode{"CMP", 2, 6, modeIndexedIndirectX, buildOpCompare(regA)}, - 0xD1: opcode{"CMP", 2, 5, modeIndirectIndexedY, buildOpCompare(regA)}, // Extra cycles - - 0xE0: opcode{"CPX", 2, 2, modeImmediate, buildOpCompare(regX)}, - 0xE4: opcode{"CPX", 2, 3, modeZeroPage, buildOpCompare(regX)}, - 0xEC: opcode{"CPX", 3, 4, modeAbsolute, buildOpCompare(regX)}, - - 0xC0: opcode{"CPY", 2, 2, modeImmediate, buildOpCompare(regY)}, - 0xC4: opcode{"CPY", 2, 3, modeZeroPage, buildOpCompare(regY)}, - 0xCC: opcode{"CPY", 3, 4, modeAbsolute, buildOpCompare(regY)}, - - 0x2A: opcode{"ROL", 1, 2, modeAccumulator, buildOpShift(true, true)}, - 0x26: opcode{"ROL", 2, 5, modeZeroPage, buildOpShift(true, true)}, - 0x36: opcode{"ROL", 2, 6, modeZeroPageX, buildOpShift(true, true)}, - 0x2E: opcode{"ROL", 3, 6, modeAbsolute, buildOpShift(true, true)}, - 0x3E: opcode{"ROL", 3, 7, modeAbsoluteX, buildOpShift(true, true)}, - - 0x6A: opcode{"ROR", 1, 2, modeAccumulator, buildOpShift(false, true)}, - 0x66: opcode{"ROR", 2, 5, modeZeroPage, buildOpShift(false, true)}, - 0x76: opcode{"ROR", 2, 6, modeZeroPageX, buildOpShift(false, true)}, - 0x6E: opcode{"ROR", 3, 6, modeAbsolute, buildOpShift(false, true)}, - 0x7E: opcode{"ROR", 3, 7, modeAbsoluteX, buildOpShift(false, true)}, - - 0x0A: opcode{"ASL", 1, 2, modeAccumulator, buildOpShift(true, false)}, - 0x06: opcode{"ASL", 2, 5, modeZeroPage, buildOpShift(true, false)}, - 0x16: opcode{"ASL", 2, 6, modeZeroPageX, buildOpShift(true, false)}, - 0x0E: opcode{"ASL", 3, 6, modeAbsolute, buildOpShift(true, false)}, - 0x1E: opcode{"ASL", 3, 7, modeAbsoluteX, buildOpShift(true, false)}, - - 0x4A: opcode{"LSR", 1, 2, modeAccumulator, buildOpShift(false, false)}, - 0x46: opcode{"LSR", 2, 5, modeZeroPage, buildOpShift(false, false)}, - 0x56: opcode{"LSR", 2, 6, modeZeroPageX, buildOpShift(false, false)}, - 0x4E: opcode{"LSR", 3, 6, modeAbsolute, buildOpShift(false, false)}, - 0x5E: opcode{"LSR", 3, 7, modeAbsoluteX, buildOpShift(false, false)}, - - 0x38: opcode{"SEC", 1, 2, modeImplicit, buildOpUpdateFlag(flagC, true)}, - 0xF8: opcode{"SED", 1, 2, modeImplicit, buildOpUpdateFlag(flagD, true)}, - 0x78: opcode{"SEI", 1, 2, modeImplicit, buildOpUpdateFlag(flagI, true)}, - 0x18: opcode{"CLC", 1, 2, modeImplicit, buildOpUpdateFlag(flagC, false)}, - 0xD8: opcode{"CLD", 1, 2, modeImplicit, buildOpUpdateFlag(flagD, false)}, - 0x58: opcode{"CLI", 1, 2, modeImplicit, buildOpUpdateFlag(flagI, false)}, - 0xB8: opcode{"CLV", 1, 2, modeImplicit, buildOpUpdateFlag(flagV, false)}, - - 0xE6: opcode{"INC", 2, 5, modeZeroPage, buildOpIncDec(true)}, - 0xF6: opcode{"INC", 2, 6, modeZeroPageX, buildOpIncDec(true)}, - 0xEE: opcode{"INC", 3, 6, modeAbsolute, buildOpIncDec(true)}, - 0xFE: opcode{"INC", 3, 7, modeAbsoluteX, buildOpIncDec(true)}, - 0xC6: opcode{"DEC", 2, 5, modeZeroPage, buildOpIncDec(false)}, - 0xD6: opcode{"DEC", 2, 6, modeZeroPageX, buildOpIncDec(false)}, - 0xCE: opcode{"DEC", 3, 6, modeAbsolute, buildOpIncDec(false)}, - 0xDE: opcode{"DEC", 3, 7, modeAbsoluteX, buildOpIncDec(false)}, - 0xE8: opcode{"INX", 1, 2, modeImplicitX, buildOpIncDec(true)}, - 0xC8: opcode{"INY", 1, 2, modeImplicitY, buildOpIncDec(true)}, - 0xCA: opcode{"DEX", 1, 2, modeImplicitX, buildOpIncDec(false)}, - 0x88: opcode{"DEY", 1, 2, modeImplicitY, buildOpIncDec(false)}, - - 0xAA: opcode{"TAX", 1, 2, modeImplicit, buildOpTransfer(regA, regX)}, - 0xA8: opcode{"TAY", 1, 2, modeImplicit, buildOpTransfer(regA, regY)}, - 0x8A: opcode{"TXA", 1, 2, modeImplicit, buildOpTransfer(regX, regA)}, - 0x98: opcode{"TYA", 1, 2, modeImplicit, buildOpTransfer(regY, regA)}, - 0x9A: opcode{"TXS", 1, 2, modeImplicit, buildOpTransfer(regX, regSP)}, - 0xBA: opcode{"TSX", 1, 2, modeImplicit, buildOpTransfer(regSP, regX)}, - - 0xA9: opcode{"LDA", 2, 2, modeImmediate, buildOpLoad(regA)}, - 0xA5: opcode{"LDA", 2, 3, modeZeroPage, buildOpLoad(regA)}, - 0xB5: opcode{"LDA", 2, 4, modeZeroPageX, buildOpLoad(regA)}, - 0xAD: opcode{"LDA", 3, 4, modeAbsolute, buildOpLoad(regA)}, - 0xBD: opcode{"LDA", 3, 4, modeAbsoluteX, buildOpLoad(regA)}, // Extra cycles - 0xB9: opcode{"LDA", 3, 4, modeAbsoluteY, buildOpLoad(regA)}, // Extra cycles - 0xA1: opcode{"LDA", 2, 6, modeIndexedIndirectX, buildOpLoad(regA)}, - 0xB1: opcode{"LDA", 2, 5, modeIndirectIndexedY, buildOpLoad(regA)}, // Extra cycles - 0xA2: opcode{"LDX", 2, 2, modeImmediate, buildOpLoad(regX)}, - 0xA6: opcode{"LDX", 2, 3, modeZeroPage, buildOpLoad(regX)}, - 0xB6: opcode{"LDX", 2, 4, modeZeroPageY, buildOpLoad(regX)}, - 0xAE: opcode{"LDX", 3, 4, modeAbsolute, buildOpLoad(regX)}, - 0xBE: opcode{"LDX", 3, 4, modeAbsoluteY, buildOpLoad(regX)}, // Extra cycles - 0xA0: opcode{"LDY", 2, 2, modeImmediate, buildOpLoad(regY)}, - 0xA4: opcode{"LDY", 2, 3, modeZeroPage, buildOpLoad(regY)}, - 0xB4: opcode{"LDY", 2, 4, modeZeroPageX, buildOpLoad(regY)}, - 0xAC: opcode{"LDY", 3, 4, modeAbsolute, buildOpLoad(regY)}, - 0xBC: opcode{"LDY", 3, 4, modeAbsoluteX, buildOpLoad(regY)}, // Extra cycles - - 0x85: opcode{"STA", 2, 3, modeZeroPage, buildOpStore(regA)}, - 0x95: opcode{"STA", 2, 4, modeZeroPageX, buildOpStore(regA)}, - 0x8D: opcode{"STA", 3, 4, modeAbsolute, buildOpStore(regA)}, - 0x9D: opcode{"STA", 3, 5, modeAbsoluteX, buildOpStore(regA)}, - 0x99: opcode{"STA", 3, 5, modeAbsoluteY, buildOpStore(regA)}, - 0x81: opcode{"STA", 2, 6, modeIndexedIndirectX, buildOpStore(regA)}, - 0x91: opcode{"STA", 2, 6, modeIndirectIndexedY, buildOpStore(regA)}, - 0x86: opcode{"STX", 2, 3, modeZeroPage, buildOpStore(regX)}, - 0x96: opcode{"STX", 2, 4, modeZeroPageY, buildOpStore(regX)}, - 0x8E: opcode{"STX", 3, 4, modeAbsolute, buildOpStore(regX)}, - 0x84: opcode{"STY", 2, 3, modeZeroPage, buildOpStore(regY)}, - 0x94: opcode{"STY", 2, 4, modeZeroPageX, buildOpStore(regY)}, - 0x8C: opcode{"STY", 3, 4, modeAbsolute, buildOpStore(regY)}, - - 0x90: opcode{"BCC", 2, 2, modeRelative, buildOpBranch(flagC, false)}, // Extra cycles - 0xB0: opcode{"BCS", 2, 2, modeRelative, buildOpBranch(flagC, true)}, // Extra cycles - 0xD0: opcode{"BNE", 2, 2, modeRelative, buildOpBranch(flagZ, false)}, // Extra cycles - 0xF0: opcode{"BEQ", 2, 2, modeRelative, buildOpBranch(flagZ, true)}, // Extra cycles - 0x10: opcode{"BPL", 2, 2, modeRelative, buildOpBranch(flagN, false)}, // Extra cycles - 0x30: opcode{"BMI", 2, 2, modeRelative, buildOpBranch(flagN, true)}, // Extra cycles - 0x50: opcode{"BVC", 2, 2, modeRelative, buildOpBranch(flagV, false)}, // Extra cycles - 0x70: opcode{"BVS", 2, 2, modeRelative, buildOpBranch(flagV, true)}, // Extra cycles - - 0xEA: opcode{"NOP", 1, 2, modeImplicit, opNOP}, -} - -func executeLine(s *State, line []uint8) { - opcode := opcodes[line[0]] +func (s *State) executeLine(line []uint8) { + opcode := s.opcodes[line[0]] if opcode.cycles == 0 { panic(fmt.Sprintf("Unknown opcode 0x%02x\n", line[0])) } @@ -556,10 +40,10 @@ func executeLine(s *State, line []uint8) { } // ExecuteInstruction transforms the state given after a single instruction is executed. -func ExecuteInstruction(s *State, log bool) { - pc := s.Reg.getPC() - opcodeID := s.Mem.Peek(pc) - opcode := opcodes[opcodeID] +func (s *State) ExecuteInstruction(log bool) { + pc := s.reg.getPC() + opcodeID := s.mem.Peek(pc) + opcode := s.opcodes[opcodeID] if opcode.cycles == 0 { panic(fmt.Sprintf("Unknown opcode 0x%02x\n", opcodeID)) @@ -567,27 +51,28 @@ func ExecuteInstruction(s *State, log bool) { line := make([]uint8, opcode.bytes) for i := uint8(0); i < opcode.bytes; i++ { - line[i] = s.Mem.Peek(pc) + line[i] = s.mem.Peek(pc) pc++ } - s.Reg.setPC(pc) + s.reg.setPC(pc) if log { - fmt.Printf("%#04x %-12s: ", pc, lineString(s, line, opcode)) + fmt.Printf("%#04x %-12s: ", pc, lineString(line, opcode)) } opcode.action(s, line, opcode) + s.cycles += int64(opcode.cycles) if log { - fmt.Printf("%v, [%02x]\n", s.Reg, line) + fmt.Printf("%v, [%02x]\n", s.reg, line) } } // Reset resets the processor state. Moves the program counter to the vector in 0cfffc. -func Reset(s *State) { - startAddress := getWord(s.Mem, vectorReset) - s.Reg.setPC(startAddress) +func (s *State) Reset() { + startAddress := getWord(s.mem, vectorReset) + s.reg.setPC(startAddress) } -func lineString(s *State, line []uint8, opcode opcode) string { +func lineString(line []uint8, opcode opcode) string { t := opcode.name switch opcode.addressMode { case modeImplicit: diff --git a/core6502/execute_test.go b/core6502/execute_test.go index 5e9db1b..4a6ddd4 100644 --- a/core6502/execute_test.go +++ b/core6502/execute_test.go @@ -5,486 +5,482 @@ import ( ) func TestLoad(t *testing.T) { - var s State - s.Mem = new(FlatMemory) + s := NewNMOS6502(new(FlatMemory)) - executeLine(&s, []uint8{0xA9, 0x42}) - if s.Reg.getA() != 0x42 { + s.executeLine([]uint8{0xA9, 0x42}) + if s.reg.getA() != 0x42 { t.Error("Error in LDA #") } - executeLine(&s, []uint8{0xA9, 0x00}) - if s.Reg.getP() != flagZ { + s.executeLine([]uint8{0xA9, 0x00}) + if s.reg.getP() != flagZ { t.Error("Error in flags for LDA $0") } - executeLine(&s, []uint8{0xA9, 0xF0}) - if s.Reg.getP() != flagN { + s.executeLine([]uint8{0xA9, 0xF0}) + if s.reg.getP() != flagN { t.Error("Error in flags for LDA $F0") } - executeLine(&s, []uint8{0xA0, 0xFE}) - if s.Reg.getY() != 0xFE { + s.executeLine([]uint8{0xA0, 0xFE}) + if s.reg.getY() != 0xFE { t.Error("Error in LDY #") } - s.Mem.Poke(0x38, 0x87) - executeLine(&s, []uint8{0xA5, 0x38}) - if s.Reg.getA() != 0x87 { + s.mem.Poke(0x38, 0x87) + s.executeLine([]uint8{0xA5, 0x38}) + if s.reg.getA() != 0x87 { t.Error("Error in LDA zpg") } - s.Mem.Poke(0x57, 0x90) - s.Reg.setX(0x10) - executeLine(&s, []uint8{0xB5, 0x47}) - if s.Reg.getA() != 0x90 { + s.mem.Poke(0x57, 0x90) + s.reg.setX(0x10) + s.executeLine([]uint8{0xB5, 0x47}) + if s.reg.getA() != 0x90 { t.Error("Error in LDA zpg, X") } - s.Mem.Poke(0x38, 0x12) - s.Reg.setX(0x89) - executeLine(&s, []uint8{0xB5, 0xAF}) - if s.Reg.getA() != 0x12 { + s.mem.Poke(0x38, 0x12) + s.reg.setX(0x89) + s.executeLine([]uint8{0xB5, 0xAF}) + if s.reg.getA() != 0x12 { t.Error("Error in LDA zpgX with sero page overflow") } - s.Mem.Poke(0x1234, 0x67) - executeLine(&s, []uint8{0xAD, 0x34, 0x12}) - if s.Reg.getA() != 0x67 { + s.mem.Poke(0x1234, 0x67) + s.executeLine([]uint8{0xAD, 0x34, 0x12}) + if s.reg.getA() != 0x67 { t.Error("Error in LDA abs") } - s.Mem.Poke(0xC057, 0x7E) - s.Reg.setX(0x57) - executeLine(&s, []uint8{0xBD, 0x00, 0xC0}) - if s.Reg.getA() != 0x7E { + s.mem.Poke(0xC057, 0x7E) + s.reg.setX(0x57) + s.executeLine([]uint8{0xBD, 0x00, 0xC0}) + if s.reg.getA() != 0x7E { t.Error("Error in LDA abs, X") } - s.Mem.Poke(0xD059, 0x7A) - s.Reg.setY(0x59) - executeLine(&s, []uint8{0xB9, 0x00, 0xD0}) - if s.Reg.getA() != 0x7A { + s.mem.Poke(0xD059, 0x7A) + s.reg.setY(0x59) + s.executeLine([]uint8{0xB9, 0x00, 0xD0}) + if s.reg.getA() != 0x7A { t.Error("Error in LDA abs, Y") } - s.Mem.Poke(0x24, 0x74) - s.Mem.Poke(0x25, 0x20) - s.Reg.setX(0x04) - s.Mem.Poke(0x2074, 0x66) - executeLine(&s, []uint8{0xA1, 0x20}) - if s.Reg.getA() != 0x66 { + s.mem.Poke(0x24, 0x74) + s.mem.Poke(0x25, 0x20) + s.reg.setX(0x04) + s.mem.Poke(0x2074, 0x66) + s.executeLine([]uint8{0xA1, 0x20}) + if s.reg.getA() != 0x66 { t.Error("Error in LDA (oper,X)") } - s.Mem.Poke(0x86, 0x28) - s.Mem.Poke(0x87, 0x40) - s.Reg.setY(0x10) - s.Mem.Poke(0x4038, 0x99) - executeLine(&s, []uint8{0xB1, 0x86}) - if s.Reg.getA() != 0x99 { + s.mem.Poke(0x86, 0x28) + s.mem.Poke(0x87, 0x40) + s.reg.setY(0x10) + s.mem.Poke(0x4038, 0x99) + s.executeLine([]uint8{0xB1, 0x86}) + if s.reg.getA() != 0x99 { t.Error("Error in LDA (oper),Y") } } func TestStore(t *testing.T) { - var s State - s.Mem = new(FlatMemory) - s.Reg.setA(0x10) - s.Reg.setX(0x40) - s.Reg.setY(0x80) + s := NewNMOS6502(new(FlatMemory)) + s.reg.setA(0x10) + s.reg.setX(0x40) + s.reg.setY(0x80) - executeLine(&s, []uint8{0x85, 0x50}) - if s.Mem.Peek(0x0050) != 0x10 { + s.executeLine([]uint8{0x85, 0x50}) + if s.mem.Peek(0x0050) != 0x10 { t.Error("Error in STA zpg") } - executeLine(&s, []uint8{0x86, 0x51}) - if s.Mem.Peek(0x0051) != 0x40 { + s.executeLine([]uint8{0x86, 0x51}) + if s.mem.Peek(0x0051) != 0x40 { t.Error("Error in STX zpg") } - executeLine(&s, []uint8{0x84, 0x52}) - if s.Mem.Peek(0x0052) != 0x80 { + s.executeLine([]uint8{0x84, 0x52}) + if s.mem.Peek(0x0052) != 0x80 { t.Error("Error in STY zpg") } - executeLine(&s, []uint8{0x8D, 0x20, 0xC0}) - if s.Mem.Peek(0xC020) != 0x10 { + s.executeLine([]uint8{0x8D, 0x20, 0xC0}) + if s.mem.Peek(0xC020) != 0x10 { t.Error("Error in STA abs") } - executeLine(&s, []uint8{0x9D, 0x08, 0x10}) - if s.Mem.Peek(0x1048) != 0x10 { + s.executeLine([]uint8{0x9D, 0x08, 0x10}) + if s.mem.Peek(0x1048) != 0x10 { t.Error("Error in STA abs, X") } } func TestTransfer(t *testing.T) { - var s State + s := NewNMOS6502(new(FlatMemory)) - s.Reg.setA(0xB0) - executeLine(&s, []uint8{0xAA}) - if s.Reg.getX() != 0xB0 { + s.reg.setA(0xB0) + s.executeLine([]uint8{0xAA}) + if s.reg.getX() != 0xB0 { t.Error("Error in TAX") } - if s.Reg.getP() != flagN { + if s.reg.getP() != flagN { t.Error("Error in TAX flags") } - s.Reg.setA(0xB1) - executeLine(&s, []uint8{0xA8}) - if s.Reg.getY() != 0xB1 { + s.reg.setA(0xB1) + s.executeLine([]uint8{0xA8}) + if s.reg.getY() != 0xB1 { t.Error("Error in TAY") } - s.Reg.setSP(0xB2) - executeLine(&s, []uint8{0xBA}) - if s.Reg.getX() != 0xB2 { + s.reg.setSP(0xB2) + s.executeLine([]uint8{0xBA}) + if s.reg.getX() != 0xB2 { t.Error("Error in TSX") } - s.Reg.setX(0xB3) - executeLine(&s, []uint8{0x8A}) - if s.Reg.getA() != 0xB3 { + s.reg.setX(0xB3) + s.executeLine([]uint8{0x8A}) + if s.reg.getA() != 0xB3 { t.Error("Error in TXA") } - s.Reg.setX(0xB4) - executeLine(&s, []uint8{0x9A}) - if s.Reg.getSP() != 0xB4 { + s.reg.setX(0xB4) + s.executeLine([]uint8{0x9A}) + if s.reg.getSP() != 0xB4 { t.Error("Error in TXS") } - s.Reg.setY(0xB5) - executeLine(&s, []uint8{0x98}) - if s.Reg.getA() != 0xB5 { + s.reg.setY(0xB5) + s.executeLine([]uint8{0x98}) + if s.reg.getA() != 0xB5 { t.Error("Error in TYA") } } func TestIncDec(t *testing.T) { - var s State + s := NewNMOS6502(new(FlatMemory)) - s.Reg.setX(0x7E) - executeLine(&s, []uint8{0xE8}) - if s.Reg.getX() != 0x7F { + s.reg.setX(0x7E) + s.executeLine([]uint8{0xE8}) + if s.reg.getX() != 0x7F { t.Errorf("Error in INX") } - s.Reg.setY(0xFC) - executeLine(&s, []uint8{0x88}) - if s.Reg.getY() != 0xFB { + s.reg.setY(0xFC) + s.executeLine([]uint8{0x88}) + if s.reg.getY() != 0xFB { t.Error("Error in DEY") } - if s.Reg.getP() != flagN { + if s.reg.getP() != flagN { t.Error("Error in DEY flags") } } func TestShiftRotate(t *testing.T) { - var s State + s := NewNMOS6502(new(FlatMemory)) - s.Reg.setA(0xF0) - executeLine(&s, []uint8{0x2A}) - if s.Reg.getA() != 0xE0 { + s.reg.setA(0xF0) + s.executeLine([]uint8{0x2A}) + if s.reg.getA() != 0xE0 { t.Errorf("Error in ROL") } - if !s.Reg.getFlag(flagC) { - t.Errorf("Error in ROL carry. %v", s.Reg) + if !s.reg.getFlag(flagC) { + t.Errorf("Error in ROL carry. %v", s.reg) } - s.Reg.setFlag(flagC) - s.Reg.setA(0x0F) - executeLine(&s, []uint8{0x6A}) - if s.Reg.getA() != 0x87 { - t.Errorf("Error in ROR. %v", s.Reg) + s.reg.setFlag(flagC) + s.reg.setA(0x0F) + s.executeLine([]uint8{0x6A}) + if s.reg.getA() != 0x87 { + t.Errorf("Error in ROR. %v", s.reg) } - if !s.Reg.getFlag(flagC) { + if !s.reg.getFlag(flagC) { t.Errorf("Error in ROR carry") } - s.Reg.setFlag(flagC) - s.Reg.setA(0x81) - executeLine(&s, []uint8{0x0A}) - if s.Reg.getA() != 0x02 { - t.Errorf("Error in ASL. %v", s.Reg) + s.reg.setFlag(flagC) + s.reg.setA(0x81) + s.executeLine([]uint8{0x0A}) + if s.reg.getA() != 0x02 { + t.Errorf("Error in ASL. %v", s.reg) } - if !s.Reg.getFlag(flagC) { + if !s.reg.getFlag(flagC) { t.Errorf("Error in ASL carry") } - s.Reg.setFlag(flagC) - s.Reg.setA(0x02) - executeLine(&s, []uint8{0x4A}) - if s.Reg.getA() != 0x01 { - t.Errorf("Error in LSR. %v", s.Reg) + s.reg.setFlag(flagC) + s.reg.setA(0x02) + s.executeLine([]uint8{0x4A}) + if s.reg.getA() != 0x01 { + t.Errorf("Error in LSR. %v", s.reg) } - if s.Reg.getFlag(flagC) { + if s.reg.getFlag(flagC) { t.Errorf("Error in LSR carry") } } func TestClearSetFlag(t *testing.T) { - var s State - s.Reg.setP(0x00) + s := NewNMOS6502(new(FlatMemory)) + s.reg.setP(0x00) - executeLine(&s, []uint8{0xF8}) - if !s.Reg.getFlag(flagD) { - t.Errorf("Error in SED. %v", s.Reg) + s.executeLine([]uint8{0xF8}) + if !s.reg.getFlag(flagD) { + t.Errorf("Error in SED. %v", s.reg) } - executeLine(&s, []uint8{0xD8}) - if s.Reg.getFlag(flagD) { - t.Errorf("Error in CLD. %v", s.Reg) + s.executeLine([]uint8{0xD8}) + if s.reg.getFlag(flagD) { + t.Errorf("Error in CLD. %v", s.reg) } } func TestLogic(t *testing.T) { - var s State + s := NewNMOS6502(new(FlatMemory)) - s.Reg.setA(0xF0) - executeLine(&s, []uint8{0x29, 0x1C}) - if s.Reg.getA() != 0x10 { - t.Errorf("Error in AND <. %v", s.Reg) + s.reg.setA(0xF0) + s.executeLine([]uint8{0x29, 0x1C}) + if s.reg.getA() != 0x10 { + t.Errorf("Error in AND <. %v", s.reg) } - s.Reg.setA(0xF0) - executeLine(&s, []uint8{0x49, 0x1C}) - if s.Reg.getA() != 0xEC { - t.Errorf("Error in EOR <. %v", s.Reg) + s.reg.setA(0xF0) + s.executeLine([]uint8{0x49, 0x1C}) + if s.reg.getA() != 0xEC { + t.Errorf("Error in EOR <. %v", s.reg) } - s.Reg.setA(0xF0) - executeLine(&s, []uint8{0x09, 0x0C}) - if s.Reg.getA() != 0xFC { - t.Errorf("Error in ORA <. %v", s.Reg) + s.reg.setA(0xF0) + s.executeLine([]uint8{0x09, 0x0C}) + if s.reg.getA() != 0xFC { + t.Errorf("Error in ORA <. %v", s.reg) } } func TestAdd(t *testing.T) { - var s State + s := NewNMOS6502(new(FlatMemory)) - s.Reg.setA(0xA0) - s.Reg.clearFlag(flagC) - executeLine(&s, []uint8{0x69, 0x0B}) - if s.Reg.getA() != 0xAB { - t.Errorf("Error in ADC. %v", s.Reg) + s.reg.setA(0xA0) + s.reg.clearFlag(flagC) + s.executeLine([]uint8{0x69, 0x0B}) + if s.reg.getA() != 0xAB { + t.Errorf("Error in ADC. %v", s.reg) } - if s.Reg.getFlag(flagC) { - t.Errorf("Error in carry ADC. %v", s.Reg) + if s.reg.getFlag(flagC) { + t.Errorf("Error in carry ADC. %v", s.reg) } - s.Reg.setA(0xFF) - s.Reg.clearFlag(flagC) - executeLine(&s, []uint8{0x69, 0x02}) - if s.Reg.getA() != 0x01 { - t.Errorf("Error in ADC with carry. %v", s.Reg) + s.reg.setA(0xFF) + s.reg.clearFlag(flagC) + s.executeLine([]uint8{0x69, 0x02}) + if s.reg.getA() != 0x01 { + t.Errorf("Error in ADC with carry. %v", s.reg) } - if !s.Reg.getFlag(flagC) { - t.Errorf("Error in carry ADC with carry. %v", s.Reg) + if !s.reg.getFlag(flagC) { + t.Errorf("Error in carry ADC with carry. %v", s.reg) } - s.Reg.setA(0xA0) - s.Reg.setFlag(flagC) - executeLine(&s, []uint8{0x69, 0x01}) - if s.Reg.getA() != 0xA2 { - t.Errorf("Error in carried ADC with carry. %v", s.Reg) + s.reg.setA(0xA0) + s.reg.setFlag(flagC) + s.executeLine([]uint8{0x69, 0x01}) + if s.reg.getA() != 0xA2 { + t.Errorf("Error in carried ADC with carry. %v", s.reg) } - if s.Reg.getFlag(flagC) { - t.Errorf("Error in carry in carried ADC with carry. %v", s.Reg) + if s.reg.getFlag(flagC) { + t.Errorf("Error in carry in carried ADC with carry. %v", s.reg) } } func TestAddDecimal(t *testing.T) { - var s State - s.Reg.setFlag(flagD) + s := NewNMOS6502(new(FlatMemory)) + s.reg.setFlag(flagD) - s.Reg.setA(0x12) - s.Reg.clearFlag(flagC) - executeLine(&s, []uint8{0x69, 0x013}) - if s.Reg.getA() != 0x25 { - t.Errorf("Error in ADC decimal. %v", s.Reg) + s.reg.setA(0x12) + s.reg.clearFlag(flagC) + s.executeLine([]uint8{0x69, 0x013}) + if s.reg.getA() != 0x25 { + t.Errorf("Error in ADC decimal. %v", s.reg) } - if s.Reg.getFlag(flagC) { - t.Errorf("Error in carry ADC. %v", s.Reg) + if s.reg.getFlag(flagC) { + t.Errorf("Error in carry ADC. %v", s.reg) } - s.Reg.setA(0x44) - s.Reg.clearFlag(flagC) - executeLine(&s, []uint8{0x69, 0x68}) - if s.Reg.getA() != 0x12 { - t.Errorf("Error in ADC decimal with carry. %v", s.Reg) + s.reg.setA(0x44) + s.reg.clearFlag(flagC) + s.executeLine([]uint8{0x69, 0x68}) + if s.reg.getA() != 0x12 { + t.Errorf("Error in ADC decimal with carry. %v", s.reg) } - if !s.Reg.getFlag(flagC) { - t.Errorf("Error in carry ADC decimal with carry. %v", s.Reg) + if !s.reg.getFlag(flagC) { + t.Errorf("Error in carry ADC decimal with carry. %v", s.reg) } - s.Reg.setA(0x44) - s.Reg.setFlag(flagC) - executeLine(&s, []uint8{0x69, 0x23}) - if s.Reg.getA() != 0x68 { - t.Errorf("Error in carried ADC decimal with carry. %v", s.Reg) + s.reg.setA(0x44) + s.reg.setFlag(flagC) + s.executeLine([]uint8{0x69, 0x23}) + if s.reg.getA() != 0x68 { + t.Errorf("Error in carried ADC decimal with carry. %v", s.reg) } - if s.Reg.getFlag(flagC) { - t.Errorf("Error in carry in carried ADC decimal with carry. %v", s.Reg) + if s.reg.getFlag(flagC) { + t.Errorf("Error in carry in carried ADC decimal with carry. %v", s.reg) } } func TestSub(t *testing.T) { - var s State + s := NewNMOS6502(new(FlatMemory)) - s.Reg.setA(0x09) - s.Reg.clearFlag(flagC) - executeLine(&s, []uint8{0xE9, 0x05}) - if s.Reg.getA() != 0x03 { - t.Errorf("Error in SBC. %v", s.Reg) + s.reg.setA(0x09) + s.reg.clearFlag(flagC) + s.executeLine([]uint8{0xE9, 0x05}) + if s.reg.getA() != 0x03 { + t.Errorf("Error in SBC. %v", s.reg) } - if !s.Reg.getFlag(flagC) { - t.Errorf("Error in carry SBC. %v", s.Reg) + if !s.reg.getFlag(flagC) { + t.Errorf("Error in carry SBC. %v", s.reg) } - s.Reg.setA(0x01) - s.Reg.clearFlag(flagC) - executeLine(&s, []uint8{0xE9, 0x02}) - if s.Reg.getA() != 0xFE { - t.Errorf("Error in SBC with carry. %v", s.Reg) + s.reg.setA(0x01) + s.reg.clearFlag(flagC) + s.executeLine([]uint8{0xE9, 0x02}) + if s.reg.getA() != 0xFE { + t.Errorf("Error in SBC with carry. %v", s.reg) } - if s.Reg.getFlag(flagC) { - t.Errorf("Error in carry SBC with carry. %v", s.Reg) + if s.reg.getFlag(flagC) { + t.Errorf("Error in carry SBC with carry. %v", s.reg) } - s.Reg.setA(0x08) - s.Reg.setFlag(flagC) - executeLine(&s, []uint8{0xE9, 0x02}) - if s.Reg.getA() != 0x06 { - t.Errorf("Error in carried SBC with carry. %v", s.Reg) + s.reg.setA(0x08) + s.reg.setFlag(flagC) + s.executeLine([]uint8{0xE9, 0x02}) + if s.reg.getA() != 0x06 { + t.Errorf("Error in carried SBC with carry. %v", s.reg) } - if !s.Reg.getFlag(flagC) { - t.Errorf("Error in carry in carried SBC with carry. %v", s.Reg) + if !s.reg.getFlag(flagC) { + t.Errorf("Error in carry in carried SBC with carry. %v", s.reg) } } func TestCompare(t *testing.T) { - var s State + s := NewNMOS6502(new(FlatMemory)) - s.Reg.setA(0x02) - executeLine(&s, []uint8{0xC9, 0x01}) - if s.Reg.getP() != 0x01 { - t.Errorf("Error in CMP <. %v", s.Reg) + s.reg.setA(0x02) + s.executeLine([]uint8{0xC9, 0x01}) + if s.reg.getP() != 0x01 { + t.Errorf("Error in CMP <. %v", s.reg) } - executeLine(&s, []uint8{0xC9, 0x02}) - if s.Reg.getP() != 0x03 { - t.Errorf("Error in CMP =. %v", s.Reg) + s.executeLine([]uint8{0xC9, 0x02}) + if s.reg.getP() != 0x03 { + t.Errorf("Error in CMP =. %v", s.reg) } - executeLine(&s, []uint8{0xC9, 0x03}) - if s.Reg.getP() != 0x80 { - t.Errorf("Error in CMP >. %v", s.Reg) + s.executeLine([]uint8{0xC9, 0x03}) + if s.reg.getP() != 0x80 { + t.Errorf("Error in CMP >. %v", s.reg) } - s.Reg.setX(0x04) - executeLine(&s, []uint8{0xE0, 0x05}) - if s.Reg.getP() != 0x80 { - t.Errorf("Error in CPX >. %v", s.Reg) + s.reg.setX(0x04) + s.executeLine([]uint8{0xE0, 0x05}) + if s.reg.getP() != 0x80 { + t.Errorf("Error in CPX >. %v", s.reg) } - s.Reg.setY(0x08) - executeLine(&s, []uint8{0xC0, 0x09}) - if s.Reg.getP() != 0x80 { - t.Errorf("Error in CPY >. %v", s.Reg) + s.reg.setY(0x08) + s.executeLine([]uint8{0xC0, 0x09}) + if s.reg.getP() != 0x80 { + t.Errorf("Error in CPY >. %v", s.reg) } } func TestBit(t *testing.T) { - var s State - s.Mem = new(FlatMemory) + s := NewNMOS6502(new(FlatMemory)) - s.Reg.setA(0x0F) - s.Mem.Poke(0x0040, 0xF0) - executeLine(&s, []uint8{0x24, 0x40}) - if s.Reg.getP() != 0xC2 { - t.Errorf("Error in BIT. %v", s.Reg) + s.reg.setA(0x0F) + s.mem.Poke(0x0040, 0xF0) + s.executeLine([]uint8{0x24, 0x40}) + if s.reg.getP() != 0xC2 { + t.Errorf("Error in BIT. %v", s.reg) } - s.Reg.setA(0xF0) - s.Mem.Poke(0x0040, 0xF0) - executeLine(&s, []uint8{0x24, 0x40}) - if s.Reg.getP() != 0xC0 { - t.Errorf("Error in BIT, 2. %v", s.Reg) + s.reg.setA(0xF0) + s.mem.Poke(0x0040, 0xF0) + s.executeLine([]uint8{0x24, 0x40}) + if s.reg.getP() != 0xC0 { + t.Errorf("Error in BIT, 2. %v", s.reg) } - s.Reg.setA(0xF0) - s.Mem.Poke(0x01240, 0x80) - executeLine(&s, []uint8{0x2C, 0x40, 0x12}) - if s.Reg.getP() != 0x80 { - t.Errorf("Error in BIT, 2. %v", s.Reg) + s.reg.setA(0xF0) + s.mem.Poke(0x01240, 0x80) + s.executeLine([]uint8{0x2C, 0x40, 0x12}) + if s.reg.getP() != 0x80 { + t.Errorf("Error in BIT, 2. %v", s.reg) } } func TestBranch(t *testing.T) { - var s State + s := NewNMOS6502(new(FlatMemory)) - s.Reg.setPC(0xC600) - s.Reg.setFlag(flagV) - executeLine(&s, []uint8{0x50, 0x20}) - if s.Reg.getPC() != 0xC600 { - t.Errorf("Error in BVC, %v", s.Reg) + s.reg.setPC(0xC600) + s.reg.setFlag(flagV) + s.executeLine([]uint8{0x50, 0x20}) + if s.reg.getPC() != 0xC600 { + t.Errorf("Error in BVC, %v", s.reg) } - executeLine(&s, []uint8{0x70, 0x20}) - if s.Reg.getPC() != 0xC620 { - t.Errorf("Error in BVS, %v", s.Reg) + s.executeLine([]uint8{0x70, 0x20}) + if s.reg.getPC() != 0xC620 { + t.Errorf("Error in BVS, %v", s.reg) } - s.Reg.setPC(0xD600) - s.Reg.clearFlag(flagC) - executeLine(&s, []uint8{0x90, 0xA0}) - if s.Reg.getPC() != 0xD5A0 { - t.Errorf("Error in BCC, %v", s.Reg) + s.reg.setPC(0xD600) + s.reg.clearFlag(flagC) + s.executeLine([]uint8{0x90, 0xA0}) + if s.reg.getPC() != 0xD5A0 { + t.Errorf("Error in BCC, %v", s.reg) } } func TestStack(t *testing.T) { - var s State - s.Mem = new(FlatMemory) + s := NewNMOS6502(new(FlatMemory)) - s.Reg.setSP(0xF0) - s.Reg.setA(0xA0) - s.Reg.setP(0x0A) - executeLine(&s, []uint8{0x48}) - if s.Reg.getSP() != 0xEF { - t.Errorf("Error in PHA stack pointer, %v", s.Reg) + s.reg.setSP(0xF0) + s.reg.setA(0xA0) + s.reg.setP(0x0A) + s.executeLine([]uint8{0x48}) + if s.reg.getSP() != 0xEF { + t.Errorf("Error in PHA stack pointer, %v", s.reg) } - if s.Mem.Peek(0x01F0) != 0xA0 { - t.Errorf("Error in PHA, %v", s.Reg) + if s.mem.Peek(0x01F0) != 0xA0 { + t.Errorf("Error in PHA, %v", s.reg) } - executeLine(&s, []uint8{0x08}) - if s.Reg.getSP() != 0xEE { - t.Errorf("Error in PHP stack pointer, %v", s.Reg) + s.executeLine([]uint8{0x08}) + if s.reg.getSP() != 0xEE { + t.Errorf("Error in PHP stack pointer, %v", s.reg) } - if s.Mem.Peek(0x01EF) != 0x3A { - t.Errorf("Error in PHP, %v", s.Reg) + if s.mem.Peek(0x01EF) != 0x3A { + t.Errorf("Error in PHP, %v", s.reg) } - executeLine(&s, []uint8{0x68}) - if s.Reg.getSP() != 0xEF { - t.Errorf("Error in PLA stack pointer, %v", s.Reg) + s.executeLine([]uint8{0x68}) + if s.reg.getSP() != 0xEF { + t.Errorf("Error in PLA stack pointer, %v", s.reg) } - if s.Reg.getA() != 0x3A { - t.Errorf("Error in PLA, %v", s.Reg) + if s.reg.getA() != 0x3A { + t.Errorf("Error in PLA, %v", s.reg) } - executeLine(&s, []uint8{0x28}) - if s.Reg.getSP() != 0xF0 { - t.Errorf("Error in PLP stack pointer, %v", s.Reg) + s.executeLine([]uint8{0x28}) + if s.reg.getSP() != 0xF0 { + t.Errorf("Error in PLP stack pointer, %v", s.reg) } - if s.Reg.getP() != 0xA0 { - t.Errorf("Error in PLP, %v", s.Reg) + if s.reg.getP() != 0xA0 { + t.Errorf("Error in PLP, %v", s.reg) } } diff --git a/core6502/opcodesNMOS6502.go b/core6502/opcodesNMOS6502.go new file mode 100644 index 0000000..6551ea6 --- /dev/null +++ b/core6502/opcodesNMOS6502.go @@ -0,0 +1,184 @@ +package core6502 + +// NewNMOS6502 returns an initialized NMOS6502 +func NewNMOS6502(m Memory) *State { + var s State + s.mem = m + s.opcodes = &opcodesNMOS6502 + return &s +} + +var opcodesNMOS6502 = [256]opcode{ + 0x00: opcode{"BRK", 1, 7, modeImplicit, opBRK}, + 0x4C: opcode{"JMP", 3, 3, modeAbsolute, opJMP}, + 0x6C: opcode{"JMP", 3, 3, modeIndirect, opJMP}, + 0x20: opcode{"JSR", 3, 6, modeAbsolute, opJSR}, + 0x40: opcode{"RTI", 1, 6, modeImplicit, opRTI}, + 0x60: opcode{"RTS", 1, 6, modeImplicit, opRTS}, + + 0x48: opcode{"PHA", 1, 3, modeImplicit, opPHA}, + 0x08: opcode{"PHP", 1, 3, modeImplicit, opPHP}, + 0x68: opcode{"PLA", 1, 4, modeImplicit, opPLA}, + 0x28: opcode{"PLP", 1, 4, modeImplicit, opPLP}, + + 0x09: opcode{"ORA", 2, 2, modeImmediate, buildOpLogic(operationOr)}, + 0x05: opcode{"ORA", 2, 3, modeZeroPage, buildOpLogic(operationOr)}, + 0x15: opcode{"ORA", 2, 4, modeZeroPageX, buildOpLogic(operationOr)}, + 0x0D: opcode{"ORA", 3, 4, modeAbsolute, buildOpLogic(operationOr)}, + 0x1D: opcode{"ORA", 3, 4, modeAbsoluteX, buildOpLogic(operationOr)}, // Extra cycles + 0x19: opcode{"ORA", 3, 4, modeAbsoluteY, buildOpLogic(operationOr)}, // Extra cycles + 0x01: opcode{"ORA", 2, 6, modeIndexedIndirectX, buildOpLogic(operationOr)}, + 0x11: opcode{"ORA", 2, 5, modeIndirectIndexedY, buildOpLogic(operationOr)}, // Extra cycles + + 0x29: opcode{"AND", 2, 2, modeImmediate, buildOpLogic(operationAnd)}, + 0x25: opcode{"AND", 2, 3, modeZeroPage, buildOpLogic(operationAnd)}, + 0x35: opcode{"AND", 2, 4, modeZeroPageX, buildOpLogic(operationAnd)}, + 0x2D: opcode{"AND", 3, 4, modeAbsolute, buildOpLogic(operationAnd)}, + 0x3D: opcode{"AND", 3, 4, modeAbsoluteX, buildOpLogic(operationAnd)}, // Extra cycles + 0x39: opcode{"AND", 3, 4, modeAbsoluteY, buildOpLogic(operationAnd)}, // Extra cycles + 0x21: opcode{"AND", 2, 6, modeIndexedIndirectX, buildOpLogic(operationAnd)}, + 0x31: opcode{"AND", 2, 5, modeIndirectIndexedY, buildOpLogic(operationAnd)}, // Extra cycles + + 0x49: opcode{"EOR", 2, 2, modeImmediate, buildOpLogic(operationXor)}, + 0x45: opcode{"EOR", 2, 3, modeZeroPage, buildOpLogic(operationXor)}, + 0x55: opcode{"EOR", 2, 4, modeZeroPageX, buildOpLogic(operationXor)}, + 0x4D: opcode{"EOR", 3, 4, modeAbsolute, buildOpLogic(operationXor)}, + 0x5D: opcode{"EOR", 3, 4, modeAbsoluteX, buildOpLogic(operationXor)}, // Extra cycles + 0x59: opcode{"EOR", 3, 4, modeAbsoluteY, buildOpLogic(operationXor)}, // Extra cycles + 0x41: opcode{"EOR", 2, 6, modeIndexedIndirectX, buildOpLogic(operationXor)}, + 0x51: opcode{"EOR", 2, 5, modeIndirectIndexedY, buildOpLogic(operationXor)}, // Extra cycles + + 0x69: opcode{"ADC", 2, 2, modeImmediate, opADC}, + 0x65: opcode{"ADC", 2, 3, modeZeroPage, opADC}, + 0x75: opcode{"ADC", 2, 4, modeZeroPageX, opADC}, + 0x6D: opcode{"ADC", 3, 4, modeAbsolute, opADC}, + 0x7D: opcode{"ADC", 3, 4, modeAbsoluteX, opADC}, // Extra cycles + 0x79: opcode{"ADC", 3, 4, modeAbsoluteY, opADC}, // Extra cycles + 0x61: opcode{"ADC", 2, 6, modeIndexedIndirectX, opADC}, + 0x71: opcode{"ADC", 2, 5, modeIndirectIndexedY, opADC}, // Extra cycles + + 0xE9: opcode{"SBC", 2, 2, modeImmediate, opSBC}, + 0xE5: opcode{"SBC", 2, 3, modeZeroPage, opSBC}, + 0xF5: opcode{"SBC", 2, 4, modeZeroPageX, opSBC}, + 0xED: opcode{"SBC", 3, 4, modeAbsolute, opSBC}, + 0xFD: opcode{"SBC", 3, 4, modeAbsoluteX, opSBC}, // Extra cycles + 0xF9: opcode{"SBC", 3, 4, modeAbsoluteY, opSBC}, // Extra cycles + 0xE1: opcode{"SBC", 2, 6, modeIndexedIndirectX, opSBC}, + 0xF1: opcode{"SBC", 2, 5, modeIndirectIndexedY, opSBC}, // Extra cycles + + 0x24: opcode{"BIT", 2, 3, modeZeroPage, opBIT}, + 0x2C: opcode{"BIT", 3, 3, modeAbsolute, opBIT}, + + 0xC9: opcode{"CMP", 2, 2, modeImmediate, buildOpCompare(regA)}, + 0xC5: opcode{"CMP", 2, 3, modeZeroPage, buildOpCompare(regA)}, + 0xD5: opcode{"CMP", 2, 4, modeZeroPageX, buildOpCompare(regA)}, + 0xCD: opcode{"CMP", 3, 4, modeAbsolute, buildOpCompare(regA)}, + 0xDD: opcode{"CMP", 3, 4, modeAbsoluteX, buildOpCompare(regA)}, // Extra cycles + 0xD9: opcode{"CMP", 3, 4, modeAbsoluteY, buildOpCompare(regA)}, // Extra cycles + 0xC1: opcode{"CMP", 2, 6, modeIndexedIndirectX, buildOpCompare(regA)}, + 0xD1: opcode{"CMP", 2, 5, modeIndirectIndexedY, buildOpCompare(regA)}, // Extra cycles + + 0xE0: opcode{"CPX", 2, 2, modeImmediate, buildOpCompare(regX)}, + 0xE4: opcode{"CPX", 2, 3, modeZeroPage, buildOpCompare(regX)}, + 0xEC: opcode{"CPX", 3, 4, modeAbsolute, buildOpCompare(regX)}, + + 0xC0: opcode{"CPY", 2, 2, modeImmediate, buildOpCompare(regY)}, + 0xC4: opcode{"CPY", 2, 3, modeZeroPage, buildOpCompare(regY)}, + 0xCC: opcode{"CPY", 3, 4, modeAbsolute, buildOpCompare(regY)}, + + 0x2A: opcode{"ROL", 1, 2, modeAccumulator, buildOpShift(true, true)}, + 0x26: opcode{"ROL", 2, 5, modeZeroPage, buildOpShift(true, true)}, + 0x36: opcode{"ROL", 2, 6, modeZeroPageX, buildOpShift(true, true)}, + 0x2E: opcode{"ROL", 3, 6, modeAbsolute, buildOpShift(true, true)}, + 0x3E: opcode{"ROL", 3, 7, modeAbsoluteX, buildOpShift(true, true)}, + + 0x6A: opcode{"ROR", 1, 2, modeAccumulator, buildOpShift(false, true)}, + 0x66: opcode{"ROR", 2, 5, modeZeroPage, buildOpShift(false, true)}, + 0x76: opcode{"ROR", 2, 6, modeZeroPageX, buildOpShift(false, true)}, + 0x6E: opcode{"ROR", 3, 6, modeAbsolute, buildOpShift(false, true)}, + 0x7E: opcode{"ROR", 3, 7, modeAbsoluteX, buildOpShift(false, true)}, + + 0x0A: opcode{"ASL", 1, 2, modeAccumulator, buildOpShift(true, false)}, + 0x06: opcode{"ASL", 2, 5, modeZeroPage, buildOpShift(true, false)}, + 0x16: opcode{"ASL", 2, 6, modeZeroPageX, buildOpShift(true, false)}, + 0x0E: opcode{"ASL", 3, 6, modeAbsolute, buildOpShift(true, false)}, + 0x1E: opcode{"ASL", 3, 7, modeAbsoluteX, buildOpShift(true, false)}, + + 0x4A: opcode{"LSR", 1, 2, modeAccumulator, buildOpShift(false, false)}, + 0x46: opcode{"LSR", 2, 5, modeZeroPage, buildOpShift(false, false)}, + 0x56: opcode{"LSR", 2, 6, modeZeroPageX, buildOpShift(false, false)}, + 0x4E: opcode{"LSR", 3, 6, modeAbsolute, buildOpShift(false, false)}, + 0x5E: opcode{"LSR", 3, 7, modeAbsoluteX, buildOpShift(false, false)}, + + 0x38: opcode{"SEC", 1, 2, modeImplicit, buildOpUpdateFlag(flagC, true)}, + 0xF8: opcode{"SED", 1, 2, modeImplicit, buildOpUpdateFlag(flagD, true)}, + 0x78: opcode{"SEI", 1, 2, modeImplicit, buildOpUpdateFlag(flagI, true)}, + 0x18: opcode{"CLC", 1, 2, modeImplicit, buildOpUpdateFlag(flagC, false)}, + 0xD8: opcode{"CLD", 1, 2, modeImplicit, buildOpUpdateFlag(flagD, false)}, + 0x58: opcode{"CLI", 1, 2, modeImplicit, buildOpUpdateFlag(flagI, false)}, + 0xB8: opcode{"CLV", 1, 2, modeImplicit, buildOpUpdateFlag(flagV, false)}, + + 0xE6: opcode{"INC", 2, 5, modeZeroPage, buildOpIncDec(true)}, + 0xF6: opcode{"INC", 2, 6, modeZeroPageX, buildOpIncDec(true)}, + 0xEE: opcode{"INC", 3, 6, modeAbsolute, buildOpIncDec(true)}, + 0xFE: opcode{"INC", 3, 7, modeAbsoluteX, buildOpIncDec(true)}, + 0xC6: opcode{"DEC", 2, 5, modeZeroPage, buildOpIncDec(false)}, + 0xD6: opcode{"DEC", 2, 6, modeZeroPageX, buildOpIncDec(false)}, + 0xCE: opcode{"DEC", 3, 6, modeAbsolute, buildOpIncDec(false)}, + 0xDE: opcode{"DEC", 3, 7, modeAbsoluteX, buildOpIncDec(false)}, + 0xE8: opcode{"INX", 1, 2, modeImplicitX, buildOpIncDec(true)}, + 0xC8: opcode{"INY", 1, 2, modeImplicitY, buildOpIncDec(true)}, + 0xCA: opcode{"DEX", 1, 2, modeImplicitX, buildOpIncDec(false)}, + 0x88: opcode{"DEY", 1, 2, modeImplicitY, buildOpIncDec(false)}, + + 0xAA: opcode{"TAX", 1, 2, modeImplicit, buildOpTransfer(regA, regX)}, + 0xA8: opcode{"TAY", 1, 2, modeImplicit, buildOpTransfer(regA, regY)}, + 0x8A: opcode{"TXA", 1, 2, modeImplicit, buildOpTransfer(regX, regA)}, + 0x98: opcode{"TYA", 1, 2, modeImplicit, buildOpTransfer(regY, regA)}, + 0x9A: opcode{"TXS", 1, 2, modeImplicit, buildOpTransfer(regX, regSP)}, + 0xBA: opcode{"TSX", 1, 2, modeImplicit, buildOpTransfer(regSP, regX)}, + + 0xA9: opcode{"LDA", 2, 2, modeImmediate, buildOpLoad(regA)}, + 0xA5: opcode{"LDA", 2, 3, modeZeroPage, buildOpLoad(regA)}, + 0xB5: opcode{"LDA", 2, 4, modeZeroPageX, buildOpLoad(regA)}, + 0xAD: opcode{"LDA", 3, 4, modeAbsolute, buildOpLoad(regA)}, + 0xBD: opcode{"LDA", 3, 4, modeAbsoluteX, buildOpLoad(regA)}, // Extra cycles + 0xB9: opcode{"LDA", 3, 4, modeAbsoluteY, buildOpLoad(regA)}, // Extra cycles + 0xA1: opcode{"LDA", 2, 6, modeIndexedIndirectX, buildOpLoad(regA)}, + 0xB1: opcode{"LDA", 2, 5, modeIndirectIndexedY, buildOpLoad(regA)}, // Extra cycles + 0xA2: opcode{"LDX", 2, 2, modeImmediate, buildOpLoad(regX)}, + 0xA6: opcode{"LDX", 2, 3, modeZeroPage, buildOpLoad(regX)}, + 0xB6: opcode{"LDX", 2, 4, modeZeroPageY, buildOpLoad(regX)}, + 0xAE: opcode{"LDX", 3, 4, modeAbsolute, buildOpLoad(regX)}, + 0xBE: opcode{"LDX", 3, 4, modeAbsoluteY, buildOpLoad(regX)}, // Extra cycles + 0xA0: opcode{"LDY", 2, 2, modeImmediate, buildOpLoad(regY)}, + 0xA4: opcode{"LDY", 2, 3, modeZeroPage, buildOpLoad(regY)}, + 0xB4: opcode{"LDY", 2, 4, modeZeroPageX, buildOpLoad(regY)}, + 0xAC: opcode{"LDY", 3, 4, modeAbsolute, buildOpLoad(regY)}, + 0xBC: opcode{"LDY", 3, 4, modeAbsoluteX, buildOpLoad(regY)}, // Extra cycles + + 0x85: opcode{"STA", 2, 3, modeZeroPage, buildOpStore(regA)}, + 0x95: opcode{"STA", 2, 4, modeZeroPageX, buildOpStore(regA)}, + 0x8D: opcode{"STA", 3, 4, modeAbsolute, buildOpStore(regA)}, + 0x9D: opcode{"STA", 3, 5, modeAbsoluteX, buildOpStore(regA)}, + 0x99: opcode{"STA", 3, 5, modeAbsoluteY, buildOpStore(regA)}, + 0x81: opcode{"STA", 2, 6, modeIndexedIndirectX, buildOpStore(regA)}, + 0x91: opcode{"STA", 2, 6, modeIndirectIndexedY, buildOpStore(regA)}, + 0x86: opcode{"STX", 2, 3, modeZeroPage, buildOpStore(regX)}, + 0x96: opcode{"STX", 2, 4, modeZeroPageY, buildOpStore(regX)}, + 0x8E: opcode{"STX", 3, 4, modeAbsolute, buildOpStore(regX)}, + 0x84: opcode{"STY", 2, 3, modeZeroPage, buildOpStore(regY)}, + 0x94: opcode{"STY", 2, 4, modeZeroPageX, buildOpStore(regY)}, + 0x8C: opcode{"STY", 3, 4, modeAbsolute, buildOpStore(regY)}, + + 0x90: opcode{"BCC", 2, 2, modeRelative, buildOpBranch(flagC, false)}, // Extra cycles + 0xB0: opcode{"BCS", 2, 2, modeRelative, buildOpBranch(flagC, true)}, // Extra cycles + 0xD0: opcode{"BNE", 2, 2, modeRelative, buildOpBranch(flagZ, false)}, // Extra cycles + 0xF0: opcode{"BEQ", 2, 2, modeRelative, buildOpBranch(flagZ, true)}, // Extra cycles + 0x10: opcode{"BPL", 2, 2, modeRelative, buildOpBranch(flagN, false)}, // Extra cycles + 0x30: opcode{"BMI", 2, 2, modeRelative, buildOpBranch(flagN, true)}, // Extra cycles + 0x50: opcode{"BVC", 2, 2, modeRelative, buildOpBranch(flagV, false)}, // Extra cycles + 0x70: opcode{"BVS", 2, 2, modeRelative, buildOpBranch(flagV, true)}, // Extra cycles + + 0xEA: opcode{"NOP", 1, 2, modeImplicit, opNOP}, +} diff --git a/core6502/operations.go b/core6502/operations.go new file mode 100644 index 0000000..db10eee --- /dev/null +++ b/core6502/operations.go @@ -0,0 +1,242 @@ +package core6502 + +func buildOpTransfer(regSrc int, regDst int) opFunc { + return func(s *State, line []uint8, opcode opcode) { + value := s.reg.getRegister(regSrc) + s.reg.setRegister(regDst, value) + if regDst != regSP { + s.reg.updateFlagZN(value) + } + } +} + +func buildOpIncDec(inc bool) opFunc { + return func(s *State, line []uint8, opcode opcode) { + value, setValue := resolveGetSetValue(s, line, opcode) + if inc { + value++ + } else { + value-- + } + s.reg.updateFlagZN(value) + setValue(value) + } +} + +func buildOpShift(isLeft bool, isRotate bool) opFunc { + return func(s *State, line []uint8, opcode opcode) { + value, setValue := resolveGetSetValue(s, line, opcode) + + oldCarry := s.reg.getFlagBit(flagC) + var carry bool + if isLeft { + carry = (value & 0x80) != 0 + value <<= 1 + if isRotate { + value += oldCarry + } + } else { + carry = (value & 0x01) != 0 + value >>= 1 + if isRotate { + value += oldCarry << 7 + } + } + s.reg.updateFlag(flagC, carry) + s.reg.updateFlagZN(value) + setValue(value) + } +} + +func buildOpLoad(regDst int) opFunc { + return func(s *State, line []uint8, opcode opcode) { + value := resolveValue(s, line, opcode) + s.reg.setRegister(regDst, value) + s.reg.updateFlagZN(value) + } +} + +func buildOpStore(regSrc int) opFunc { + return func(s *State, line []uint8, opcode opcode) { + setValue := resolveSetValue(s, line, opcode) + value := s.reg.getRegister(regSrc) + setValue(value) + } +} + +func buildOpUpdateFlag(flag uint8, value bool) opFunc { + return func(s *State, line []uint8, opcode opcode) { + s.reg.updateFlag(flag, value) + } +} + +func buildOpBranch(flag uint8, value bool) opFunc { + return func(s *State, line []uint8, opcode opcode) { + if s.reg.getFlag(flag) == value { + // This assumes that PC is already pointing to the next instruction + pc := s.reg.getPC() + pc += uint16(int8(line[1])) + s.reg.setPC(pc) + } + } +} + +func opBIT(s *State, line []uint8, opcode opcode) { + value := resolveValue(s, line, opcode) + acc := s.reg.getA() + // Future note: The immediate addressing mode (65C02 or 65816 only) does not affect V. + s.reg.updateFlag(flagZ, value&acc == 0) + s.reg.updateFlag(flagN, value&(1<<7) != 0) + s.reg.updateFlag(flagV, value&(1<<6) != 0) +} + +func buildOpCompare(reg int) opFunc { + return func(s *State, line []uint8, opcode opcode) { + value := resolveValue(s, line, opcode) + reference := s.reg.getRegister(reg) + s.reg.updateFlagZN(reference - value) + s.reg.updateFlag(flagC, reference >= value) + } +} + +func operationAnd(a uint8, b uint8) uint8 { return a & b } +func operationOr(a uint8, b uint8) uint8 { return a | b } +func operationXor(a uint8, b uint8) uint8 { return a ^ b } + +func buildOpLogic(operation func(uint8, uint8) uint8) opFunc { + return func(s *State, line []uint8, opcode opcode) { + value := resolveValue(s, line, opcode) + result := operation(value, s.reg.getA()) + s.reg.setA(result) + s.reg.updateFlagZN(result) + } +} + +func opADC(s *State, line []uint8, opcode opcode) { + value := resolveValue(s, line, opcode) + aValue := s.reg.getA() + carry := s.reg.getFlagBit(flagC) + + total := uint16(aValue) + uint16(value) + uint16(carry) + signedTotal := int16(int8(aValue)) + int16(int8(value)) + int16(carry) + truncated := uint8(total) + + if s.reg.getFlag(flagD) { + totalBcdLo := int(aValue&0x0f) + int(value&0x0f) + int(carry) + totalBcdHi := int(aValue>>4) + int(value>>4) + if totalBcdLo >= 10 { + totalBcdHi++ + } + totalBcd := (totalBcdHi%10)<<4 + (totalBcdLo % 10) + s.reg.setA(uint8(totalBcd)) + s.reg.updateFlag(flagC, totalBcdHi > 9) + } else { + s.reg.setA(truncated) + s.reg.updateFlag(flagC, total > 0xFF) + } + + // ZNV flags behave for BCD as if the operation was binary? + s.reg.updateFlagZN(truncated) + s.reg.updateFlag(flagV, signedTotal < -128 || signedTotal > 127) +} + +func opSBC(s *State, line []uint8, opcode opcode) { + value := resolveValue(s, line, opcode) + aValue := s.reg.getA() + carry := s.reg.getFlagBit(flagC) + + total := 0x100 + uint16(aValue) - uint16(value) + uint16(carry) - 1 + signedTotal := int16(int8(aValue)) - int16(int8(value)) + int16(carry) - 1 + truncated := uint8(total) + + if s.reg.getFlag(flagD) { + totalBcdLo := 10 + int(aValue&0x0f) - int(value&0x0f) + int(carry) - 1 + totalBcdHi := 10 + int(aValue>>4) - int(value>>4) + if totalBcdLo < 10 { + totalBcdHi-- + } + totalBcd := (totalBcdHi%10)<<4 + (totalBcdLo % 10) + s.reg.setA(uint8(totalBcd)) + s.reg.updateFlag(flagC, totalBcdHi >= 10) + } else { + s.reg.setA(truncated) + s.reg.updateFlag(flagC, total > 0xFF) + } + + // ZNV flags behave for SBC as if the operation was binary + s.reg.updateFlagZN(truncated) + s.reg.updateFlag(flagV, signedTotal < -128 || signedTotal > 127) +} + +const stackAddress uint16 = 0x0100 + +func pushByte(s *State, value uint8) { + adresss := stackAddress + uint16(s.reg.getSP()) + s.mem.Poke(adresss, value) + s.reg.setSP(s.reg.getSP() - 1) +} + +func pullByte(s *State) uint8 { + s.reg.setSP(s.reg.getSP() + 1) + adresss := stackAddress + uint16(s.reg.getSP()) + return s.mem.Peek(adresss) +} + +func pushWord(s *State, value uint16) { + pushByte(s, uint8(value>>8)) + pushByte(s, uint8(value)) +} + +func pullWord(s *State) uint16 { + return uint16(pullByte(s)) + + (uint16(pullByte(s)) << 8) + +} + +func opPLA(s *State, line []uint8, opcode opcode) { + value := pullByte(s) + s.reg.setA(value) + s.reg.updateFlagZN(value) +} + +func opPLP(s *State, line []uint8, opcode opcode) { + value := pullByte(s) + s.reg.setP(value) +} + +func opPHA(s *State, line []uint8, opcode opcode) { + pushByte(s, s.reg.getA()) +} + +func opPHP(s *State, line []uint8, opcode opcode) { + pushByte(s, s.reg.getP()|(flagB+flag5)) +} + +func opJMP(s *State, line []uint8, opcode opcode) { + address := resolveAddress(s, line, opcode) + s.reg.setPC(address) +} + +func opNOP(s *State, line []uint8, opcode opcode) {} + +func opJSR(s *State, line []uint8, opcode opcode) { + pushWord(s, s.reg.getPC()-1) + address := resolveAddress(s, line, opcode) + s.reg.setPC(address) +} + +func opRTI(s *State, line []uint8, opcode opcode) { + s.reg.setP(pullByte(s)) + s.reg.setPC(pullWord(s)) +} + +func opRTS(s *State, line []uint8, opcode opcode) { + s.reg.setPC(pullWord(s) + 1) +} + +func opBRK(s *State, line []uint8, opcode opcode) { + pushWord(s, s.reg.getPC()+1) + pushByte(s, s.reg.getP()|(flagB+flag5)) + s.reg.setFlag(flagI) + s.reg.setPC(getWord(s.mem, vectorBreak)) +}