diff --git a/FruitMachine/DebuggerViewController.swift b/FruitMachine/DebuggerViewController.swift index b3a9b48..e7d0bb6 100644 --- a/FruitMachine/DebuggerViewController.swift +++ b/FruitMachine/DebuggerViewController.swift @@ -21,6 +21,16 @@ class DebuggerViewController: NSViewController { let CPU = CPUState.sharedInstance var disassembly: [Disassembly] = [Disassembly]() + func highlightCurrentInstruction() -> Bool { + for (index, instruction) in disassembly.enumerated() { + if(instruction.address == CPU.program_counter) { + debuggerTableView.selectRowIndexes(NSIndexSet(index: index) as IndexSet, byExtendingSelection: false) + return true //instruction found + } + } + return false //instruction not found + } + func updateCPUStatusFields() { text_CPU_A.stringValue = String(format:"%02X", CPU.accumulator) text_CPU_X.stringValue = String(format:"%02X", CPU.index_x) @@ -28,6 +38,11 @@ class DebuggerViewController: NSViewController { text_CPU_IP.stringValue = String(format:"%04X", CPU.program_counter) text_CPU_SR.stringValue = String(format:"%02X", CPU.stack_pointer) text_CPU_Flags.stringValue = String(CPU.status_register.asString()) + + if(!highlightCurrentInstruction()) { + disassembly = CPU.disassemble(fromAddress: CPU.program_counter, length: 256) + highlightCurrentInstruction() + } } override func viewDidLoad() { @@ -37,8 +52,10 @@ class DebuggerViewController: NSViewController { debuggerTableView.dataSource = self CPU.memoryInterface.loadBinary(path: "/Users/luigi/6502/test.bin") + CPU.performReset() + CPU.program_counter = 0x400 //entry point for the test program updateCPUStatusFields() - disassembly = CPU.disassemble(fromAddress: 0x0000, length: 16) + disassembly = CPU.disassemble(fromAddress: CPU.program_counter, length: 10000) debuggerTableView.reloadData() // Do any additional setup after loading the view. diff --git a/FruitMachine/M6502/CPUInstructions.swift b/FruitMachine/M6502/CPUInstructions.swift index f4c01a0..7ef976a 100644 --- a/FruitMachine/M6502/CPUInstructions.swift +++ b/FruitMachine/M6502/CPUInstructions.swift @@ -46,6 +46,16 @@ class CPUInstruction: NSObject { let InstructionTable: [UInt8:CPUInstruction] = [ + //ADC/SBC + 0x69: CPUInstruction(mnemonic: "ADC", cycles: 2, bytes: 2, addressingMode: .immediate, action: Opcodes.ADC), + 0x65: CPUInstruction(mnemonic: "ADC", cycles: 3, bytes: 2, addressingMode: .zeropage, action: Opcodes.ADC), + 0x75: CPUInstruction(mnemonic: "ADC", cycles: 4, bytes: 2, addressingMode: .zeropage_indexed_x, action: Opcodes.ADC), + 0x6D: CPUInstruction(mnemonic: "ADC", cycles: 4, bytes: 3, addressingMode: .absolute, action: Opcodes.ADC), + 0x7D: CPUInstruction(mnemonic: "ADC", cycles: 4, bytes: 3, addressingMode: .absolute_indexed_x, action: Opcodes.ADC), + 0x79: CPUInstruction(mnemonic: "ADC", cycles: 4, bytes: 3, addressingMode: .absolute_indexed_y, action: Opcodes.ADC), + 0x61: CPUInstruction(mnemonic: "ADC", cycles: 6, bytes: 2, addressingMode: .indexed_indirect, action: Opcodes.ADC), + 0x71: CPUInstruction(mnemonic: "ADC", cycles: 5, bytes: 2, addressingMode: .indirect_indexed, action: Opcodes.ADC), + //Boolean operators 0x09: CPUInstruction(mnemonic: "ORA", cycles: 2, bytes: 2, addressingMode: .immediate, action: Opcodes.ORA), 0x05: CPUInstruction(mnemonic: "ORA", cycles: 3, bytes: 2, addressingMode: .zeropage, action: Opcodes.ORA), @@ -74,6 +84,34 @@ let InstructionTable: [UInt8:CPUInstruction] = [ 0x21: CPUInstruction(mnemonic: "AND", cycles: 6, bytes: 2, addressingMode: .indexed_indirect, action: Opcodes.AND), 0x31: CPUInstruction(mnemonic: "AND", cycles: 5, bytes: 2, addressingMode: .indirect_indexed, action: Opcodes.AND), + //Bitwise operations + 0x24: CPUInstruction(mnemonic: "BIT", cycles: 3, bytes: 2, addressingMode: .zeropage, action: Opcodes.BIT), + 0x2C: CPUInstruction(mnemonic: "BIT", cycles: 4, bytes: 3, addressingMode: .absolute, action: Opcodes.BIT), + + 0x0A: CPUInstruction(mnemonic: "ASL", cycles: 2, bytes: 1, addressingMode: .accumulator, action: Opcodes.ASL), + 0x06: CPUInstruction(mnemonic: "ASL", cycles: 5, bytes: 2, addressingMode: .zeropage, action: Opcodes.ASL), + 0x16: CPUInstruction(mnemonic: "ASL", cycles: 6, bytes: 2, addressingMode: .zeropage_indexed_x, action: Opcodes.ASL), + 0x0E: CPUInstruction(mnemonic: "ASL", cycles: 6, bytes: 3, addressingMode: .absolute, action: Opcodes.ASL), + 0x1E: CPUInstruction(mnemonic: "ASL", cycles: 7, bytes: 3, addressingMode: .absolute_indexed_x, action: Opcodes.ASL), + + 0x4A: CPUInstruction(mnemonic: "LSR", cycles: 2, bytes: 1, addressingMode: .accumulator, action: Opcodes.LSR), + 0x46: CPUInstruction(mnemonic: "LSR", cycles: 5, bytes: 2, addressingMode: .zeropage, action: Opcodes.LSR), + 0x56: CPUInstruction(mnemonic: "LSR", cycles: 6, bytes: 2, addressingMode: .zeropage_indexed_x, action: Opcodes.LSR), + 0x4E: CPUInstruction(mnemonic: "LSR", cycles: 6, bytes: 3, addressingMode: .absolute, action: Opcodes.LSR), + 0x5E: CPUInstruction(mnemonic: "LSR", cycles: 7, bytes: 3, addressingMode: .absolute_indexed_x, action: Opcodes.LSR), + + 0x2A: CPUInstruction(mnemonic: "ROL", cycles: 2, bytes: 1, addressingMode: .accumulator, action: Opcodes.ROL), + 0x26: CPUInstruction(mnemonic: "ROL", cycles: 5, bytes: 2, addressingMode: .zeropage, action: Opcodes.ROL), + 0x36: CPUInstruction(mnemonic: "ROL", cycles: 6, bytes: 2, addressingMode: .zeropage_indexed_x, action: Opcodes.ROL), + 0x2E: CPUInstruction(mnemonic: "ROL", cycles: 6, bytes: 3, addressingMode: .absolute, action: Opcodes.ROL), + 0x3E: CPUInstruction(mnemonic: "ROL", cycles: 7, bytes: 3, addressingMode: .absolute_indexed_x, action: Opcodes.ROL), + + 0x6A: CPUInstruction(mnemonic: "ROL", cycles: 2, bytes: 1, addressingMode: .accumulator, action: Opcodes.ROR), + 0x66: CPUInstruction(mnemonic: "ROL", cycles: 5, bytes: 2, addressingMode: .zeropage, action: Opcodes.ROR), + 0x76: CPUInstruction(mnemonic: "ROL", cycles: 6, bytes: 2, addressingMode: .zeropage_indexed_x, action: Opcodes.ROR), + 0x6E: CPUInstruction(mnemonic: "ROL", cycles: 6, bytes: 3, addressingMode: .absolute, action: Opcodes.ROR), + 0x7E: CPUInstruction(mnemonic: "ROL", cycles: 7, bytes: 3, addressingMode: .absolute_indexed_x, action: Opcodes.ROR), + //INC/DEC 0xC6: CPUInstruction(mnemonic: "DEC", cycles: 5, bytes: 2, addressingMode: .zeropage, action: Opcodes.DEC), 0xD6: CPUInstruction(mnemonic: "DEC", cycles: 6, bytes: 2, addressingMode: .zeropage_indexed_x, action: Opcodes.DEC), @@ -128,6 +166,24 @@ let InstructionTable: [UInt8:CPUInstruction] = [ 0x94: CPUInstruction(mnemonic: "STY", cycles: 4, bytes: 2, addressingMode: .zeropage_indexed_x, action: Opcodes.STY), 0x8C: CPUInstruction(mnemonic: "STY", cycles: 4, bytes: 3, addressingMode: .absolute, action: Opcodes.STY), + //Compare functions + 0xC9: CPUInstruction(mnemonic: "CMP", cycles: 2, bytes: 2, addressingMode: .immediate, action: Opcodes.CMP), + 0xC5: CPUInstruction(mnemonic: "CMP", cycles: 3, bytes: 2, addressingMode: .zeropage, action: Opcodes.CMP), + 0xD5: CPUInstruction(mnemonic: "CMP", cycles: 4, bytes: 2, addressingMode: .zeropage_indexed_x, action: Opcodes.CMP), + 0xCD: CPUInstruction(mnemonic: "CMP", cycles: 4, bytes: 3, addressingMode: .absolute, action: Opcodes.CMP), + 0xDD: CPUInstruction(mnemonic: "CMP", cycles: 4, bytes: 3, addressingMode: .absolute_indexed_x, action: Opcodes.CMP), + 0xD9: CPUInstruction(mnemonic: "CMP", cycles: 4, bytes: 3, addressingMode: .absolute_indexed_y, action: Opcodes.CMP), + 0xC1: CPUInstruction(mnemonic: "CMP", cycles: 6, bytes: 2, addressingMode: .indexed_indirect, action: Opcodes.CMP), + 0xD1: CPUInstruction(mnemonic: "CMP", cycles: 5, bytes: 2, addressingMode: .indirect_indexed, action: Opcodes.CMP), + + 0xE0: CPUInstruction(mnemonic: "CPX", cycles: 2, bytes: 2, addressingMode: .immediate, action: Opcodes.CPX), + 0xE4: CPUInstruction(mnemonic: "CPX", cycles: 3, bytes: 2, addressingMode: .zeropage, action: Opcodes.CPX), + 0xEC: CPUInstruction(mnemonic: "CPX", cycles: 4, bytes: 3, addressingMode: .absolute, action: Opcodes.CPX), + + 0xC0: CPUInstruction(mnemonic: "CPY", cycles: 2, bytes: 2, addressingMode: .immediate, action: Opcodes.CPY), + 0xC4: CPUInstruction(mnemonic: "CPY", cycles: 3, bytes: 2, addressingMode: .zeropage, action: Opcodes.CPY), + 0xCC: CPUInstruction(mnemonic: "CPY", cycles: 4, bytes: 3, addressingMode: .absolute, action: Opcodes.CPY), + //Register functions 0x88: CPUInstruction(mnemonic: "DEY", cycles: 2, bytes: 1, addressingMode: .implied, action: Opcodes.DEY), 0x8A: CPUInstruction(mnemonic: "TXA", cycles: 2, bytes: 1, addressingMode: .implied, action: Opcodes.TXA), diff --git a/FruitMachine/M6502/CPUState.swift b/FruitMachine/M6502/CPUState.swift index 14151e1..e1777a4 100644 --- a/FruitMachine/M6502/CPUState.swift +++ b/FruitMachine/M6502/CPUState.swift @@ -77,7 +77,7 @@ struct StatusRegister { } func asByte() -> UInt8 { - var val: UInt8 = 0x00 + var val: UInt8 = 0x20 //unused bit is hardwired to 1 if(negative) { val |= 0x80 @@ -106,6 +106,10 @@ struct StatusRegister { } class CPUState: NSObject { + let NMI_VECTOR: UInt16 = 0xFFFA + let RESET_VECTOR: UInt16 = 0xFFFC + let IRQ_VECTOR: UInt16 = 0xFFFE + static var sharedInstance = CPUState() var cycles: Int @@ -228,6 +232,12 @@ class CPUState: NSObject { } } + func performReset() { + program_counter = memoryInterface.readWord(offset: RESET_VECTOR) + stack_pointer = 0xFF + status_register.irq_disable = true + } + func updateNegativeFlag(value: UInt8) { status_register.negative = (value & 0x80 == 0x80) } diff --git a/FruitMachine/M6502/IntegerExtensions.swift b/FruitMachine/M6502/IntegerExtensions.swift index 13bfdda..4d6262f 100644 --- a/FruitMachine/M6502/IntegerExtensions.swift +++ b/FruitMachine/M6502/IntegerExtensions.swift @@ -10,6 +10,6 @@ import Cocoa extension UInt16 { static func + (left: UInt16, right: UInt8) -> UInt16 { - return left + right + return left + UInt16(right) } } diff --git a/FruitMachine/M6502/Opcodes.swift b/FruitMachine/M6502/Opcodes.swift index 77d9a4b..30672e6 100644 --- a/FruitMachine/M6502/Opcodes.swift +++ b/FruitMachine/M6502/Opcodes.swift @@ -40,12 +40,19 @@ extension CPUState { } func doBranch() { - let distance = getOperandByte() + let distance = Int8(bitPattern: getOperandByte()) - if(((program_counter & 0x00FF) + distance) > 0x0100) { - page_boundary_crossed = true + if(distance < 0) { + if((program_counter & 0x00FF) - UInt16(abs(Int16(distance))) > 0x8000) { + page_boundary_crossed = true + } + } else { + if((program_counter & 0x00FF) + UInt16(abs(Int16(distance)))ß > 0x0100) { + page_boundary_crossed = true + } } - program_counter = program_counter + distance + + program_counter = UInt16(Int(program_counter) + Int(distance)) branch_was_taken = true } } @@ -115,10 +122,35 @@ func getOperandWordForAddressingMode(state: CPUState, mode: AddressingMode) -> U } +func hex2bcd(hex: UInt8) -> UInt8 { + var y: UInt8 = (hex / 10) << 4 + y = y | (hex % 10) + return y +} + /* */ class Opcodes: NSObject { + static func ADC(state: CPUState, addressingMode: AddressingMode) -> Void { + let operand = UInt8(getOperandByteForAddressingMode(state: state, mode: addressingMode)) + + var t16: UInt16 = UInt16(state.accumulator &+ operand) + UInt16((state.status_register.carry ? UInt8(1) : UInt8(0))) + let t8: UInt8 = UInt8(t16 & 0xFF) + + state.status_register.overflow = (~(state.accumulator ^ operand) & (state.accumulator ^ t8) & 0x80) == 0x80 + state.status_register.zero = (t8 == 0) + state.status_register.negative = (t8 & 0x80) == 0x80 + + if(state.status_register.decimal) { + t16 = UInt16(hex2bcd(hex: state.accumulator) + hex2bcd(hex: operand) + (state.status_register.carry ? UInt8(1) : UInt8(0))) + } else { + state.status_register.carry = (t16 > 255) + } + + state.accumulator = (UInt8(t16 & 0xFF)) + } + static func LDA(state: CPUState, addressingMode: AddressingMode) -> Void { state.accumulator = getOperandByteForAddressingMode(state: state, mode: addressingMode) @@ -323,7 +355,7 @@ class Opcodes: NSObject { state.updateZeroFlag(value: data) state.updateNegativeFlag(value: data) - state.status_register.carry = (data >= 0) + state.status_register.carry = (data >= 0) } //Boolean operators @@ -348,6 +380,106 @@ class Opcodes: NSObject { state.updateNegativeFlag(value: state.accumulator) } + //Bitwise operators + static func BIT(state: CPUState, addressingMode: AddressingMode) -> Void { + let operand = getOperandByteForAddressingMode(state: state, mode: addressingMode) + let data = state.accumulator & operand + + state.updateZeroFlag(value: data) + state.updateNegativeFlag(value: operand) + state.status_register.overflow = (state.accumulator & UInt8(0x40)) == 0x40 + } + + static func ASL(state: CPUState, addressingMode: AddressingMode) -> Void { + let operand: UInt8 + + if(addressingMode == .implied) { + operand = state.accumulator + state.status_register.carry = ((operand & 0x80) == 0x80) + state.accumulator = (state.accumulator &<< 1) & 0xFE + state.updateZeroFlag(value: state.accumulator) + state.updateNegativeFlag(value: state.accumulator) + } else { + let address = getOperandWordForAddressingMode(state: state, mode: addressingMode) + var data = state.memoryInterface.readByte(offset: address) + state.status_register.carry = (data & 0x80) == 0x80 + data = (data &<< 1) & 0xFE + state.memoryInterface.writeByte(offset: address, value: data) + state.updateZeroFlag(value: data) + state.updateNegativeFlag(value: data) + } + } + + static func LSR(state: CPUState, addressingMode: AddressingMode) -> Void { + let operand: UInt8 + + if(addressingMode == .implied) { + operand = state.accumulator + state.status_register.carry = ((operand & 0x01) == 0x01) + state.accumulator = (state.accumulator &>> 1) & 0x7F + state.updateZeroFlag(value: state.accumulator) + state.status_register.negative = false + } else { + let address = getOperandWordForAddressingMode(state: state, mode: addressingMode) + var data = state.memoryInterface.readByte(offset: address) + state.status_register.carry = (data & 0x01) == 0x01 + data = (data &>> 1) & 0x7F + state.memoryInterface.writeByte(offset: address, value: data) + state.updateZeroFlag(value: data) + state.updateNegativeFlag(value: data) + } + } + + static func ROL(state: CPUState, addressingMode: AddressingMode) -> Void { + let operand: UInt8 + + if(addressingMode == .implied) { + operand = state.accumulator + + state.accumulator = (state.accumulator &<< 1) & 0xFE + state.accumulator = state.accumulator | (state.status_register.carry ? 0x01 : 0x00) + + state.status_register.carry = ((operand & 0x80) == 0x80) + state.updateZeroFlag(value: state.accumulator) + state.status_register.negative = (state.accumulator & 0x80) == 0x80 + } else { + let address = getOperandWordForAddressingMode(state: state, mode: addressingMode) + var data = state.memoryInterface.readByte(offset: address) + + data = (data &<< 1) & 0xFE + data = data | (state.status_register.carry ? 0x01 : 0x00) + state.memoryInterface.writeByte(offset: address, value: data) + + state.status_register.carry = (data & 0x80) == 0x80 + state.updateZeroFlag(value: data) + state.status_register.negative = (data & 0x80) == 0x80 + } + } + + static func ROR(state: CPUState, addressingMode: AddressingMode) -> Void { + let operand: UInt8 + + if(addressingMode == .implied) { + operand = state.accumulator + + state.status_register.carry = ((operand & 0x01) == 0x01) + state.accumulator = (state.accumulator &>> 1) & 0x7F + state.accumulator = state.accumulator | (state.status_register.carry ? 0x80 : 0x00) + state.updateZeroFlag(value: state.accumulator) + state.status_register.negative = (state.accumulator & 0x80) == 0x80 + } else { + let address = getOperandWordForAddressingMode(state: state, mode: addressingMode) + var data = state.memoryInterface.readByte(offset: address) + + state.status_register.carry = (data & 0x01) == 0x01 + data = (data &>> 1) & 0x7F + data = data | (state.status_register.carry ? 0x80 : 0x00) + state.memoryInterface.writeByte(offset: address, value: data) + state.updateZeroFlag(value: data) + state.status_register.negative = (data & 0x80) == 0x80 + } + } + //Processor flag instructions static func CLC(state: CPUState, addressingMode: AddressingMode) -> Void { state.status_register.carry = false