From f3bbb0413e663a4a998b2181ee705c6feac9353c Mon Sep 17 00:00:00 2001 From: Luigi Thirty Date: Sat, 22 Jul 2017 15:46:02 -0400 Subject: [PATCH] started on the disassembler --- FruitMachine.xcodeproj/project.pbxproj | 12 +- FruitMachine/Base.lproj/Main.storyboard | 122 +++++++++++++++++-- FruitMachine/DebuggerViewController.swift | 139 ++++++++++++++++++++++ FruitMachine/M6502/CPUInstructions.swift | 9 ++ FruitMachine/M6502/CPUState.swift | 42 +++++++ FruitMachine/M6502/Disassembly.swift | 21 ++++ FruitMachine/M6502/Opcodes.swift | 27 +++++ FruitMachine/ViewController.swift | 59 --------- 8 files changed, 356 insertions(+), 75 deletions(-) create mode 100644 FruitMachine/DebuggerViewController.swift create mode 100644 FruitMachine/M6502/Disassembly.swift delete mode 100644 FruitMachine/ViewController.swift diff --git a/FruitMachine.xcodeproj/project.pbxproj b/FruitMachine.xcodeproj/project.pbxproj index f53124a..199bf20 100644 --- a/FruitMachine.xcodeproj/project.pbxproj +++ b/FruitMachine.xcodeproj/project.pbxproj @@ -9,20 +9,21 @@ /* Begin PBXBuildFile section */ 2A22EBFB1F21A7A700A36A61 /* IntegerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A22EBFA1F21A7A700A36A61 /* IntegerExtensions.swift */; }; 2AD458CE1F205EB700F05121 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD458CD1F205EB700F05121 /* AppDelegate.swift */; }; - 2AD458D01F205EB700F05121 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD458CF1F205EB700F05121 /* ViewController.swift */; }; + 2AD458D01F205EB700F05121 /* DebuggerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD458CF1F205EB700F05121 /* DebuggerViewController.swift */; }; 2AD458D21F205EB700F05121 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2AD458D11F205EB700F05121 /* Assets.xcassets */; }; 2AD458D51F205EB700F05121 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2AD458D31F205EB700F05121 /* Main.storyboard */; }; 2AD458DF1F205F4500F05121 /* CPUState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD458DE1F205F4500F05121 /* CPUState.swift */; }; 2AD458E11F2064CB00F05121 /* MemoryInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD458E01F2064CB00F05121 /* MemoryInterface.swift */; }; 2AD458E31F20661300F05121 /* CPUInstructions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD458E21F20661300F05121 /* CPUInstructions.swift */; }; 2AD458E51F2070DF00F05121 /* Opcodes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD458E41F2070DF00F05121 /* Opcodes.swift */; }; + 2AE5BA041F23DE4400FAA343 /* Disassembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AE5BA031F23DE4400FAA343 /* Disassembly.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 2A22EBFA1F21A7A700A36A61 /* IntegerExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegerExtensions.swift; sourceTree = ""; }; 2AD458CA1F205EB700F05121 /* FruitMachine.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FruitMachine.app; sourceTree = BUILT_PRODUCTS_DIR; }; 2AD458CD1F205EB700F05121 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 2AD458CF1F205EB700F05121 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 2AD458CF1F205EB700F05121 /* DebuggerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebuggerViewController.swift; sourceTree = ""; }; 2AD458D11F205EB700F05121 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 2AD458D41F205EB700F05121 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 2AD458D61F205EB700F05121 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -31,6 +32,7 @@ 2AD458E01F2064CB00F05121 /* MemoryInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryInterface.swift; sourceTree = ""; }; 2AD458E21F20661300F05121 /* CPUInstructions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CPUInstructions.swift; sourceTree = ""; }; 2AD458E41F2070DF00F05121 /* Opcodes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Opcodes.swift; sourceTree = ""; }; + 2AE5BA031F23DE4400FAA343 /* Disassembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Disassembly.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -65,7 +67,7 @@ children = ( 2AD458DD1F205F0D00F05121 /* M6502 */, 2AD458CD1F205EB700F05121 /* AppDelegate.swift */, - 2AD458CF1F205EB700F05121 /* ViewController.swift */, + 2AD458CF1F205EB700F05121 /* DebuggerViewController.swift */, 2AD458D11F205EB700F05121 /* Assets.xcassets */, 2AD458D31F205EB700F05121 /* Main.storyboard */, 2AD458D61F205EB700F05121 /* Info.plist */, @@ -79,6 +81,7 @@ children = ( 2AD458DE1F205F4500F05121 /* CPUState.swift */, 2AD458E21F20661300F05121 /* CPUInstructions.swift */, + 2AE5BA031F23DE4400FAA343 /* Disassembly.swift */, 2AD458E41F2070DF00F05121 /* Opcodes.swift */, 2AD458E01F2064CB00F05121 /* MemoryInterface.swift */, 2A22EBFA1F21A7A700A36A61 /* IntegerExtensions.swift */, @@ -157,9 +160,10 @@ buildActionMask = 2147483647; files = ( 2AD458E31F20661300F05121 /* CPUInstructions.swift in Sources */, - 2AD458D01F205EB700F05121 /* ViewController.swift in Sources */, + 2AD458D01F205EB700F05121 /* DebuggerViewController.swift in Sources */, 2AD458CE1F205EB700F05121 /* AppDelegate.swift in Sources */, 2A22EBFB1F21A7A700A36A61 /* IntegerExtensions.swift in Sources */, + 2AE5BA041F23DE4400FAA343 /* Disassembly.swift in Sources */, 2AD458E51F2070DF00F05121 /* Opcodes.swift in Sources */, 2AD458E11F2064CB00F05121 /* MemoryInterface.swift in Sources */, 2AD458DF1F205F4500F05121 /* CPUState.swift in Sources */, diff --git a/FruitMachine/Base.lproj/Main.storyboard b/FruitMachine/Base.lproj/Main.storyboard index b34d67e..ce5c2cd 100644 --- a/FruitMachine/Base.lproj/Main.storyboard +++ b/FruitMachine/Base.lproj/Main.storyboard @@ -685,7 +685,7 @@ - + @@ -702,10 +702,10 @@ - + - + @@ -767,7 +767,7 @@ - + @@ -782,6 +782,15 @@ + + + + + + + + + @@ -802,18 +811,107 @@ - - + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FruitMachine/DebuggerViewController.swift b/FruitMachine/DebuggerViewController.swift new file mode 100644 index 0000000..997315f --- /dev/null +++ b/FruitMachine/DebuggerViewController.swift @@ -0,0 +1,139 @@ +// +// ViewController.swift +// FruitMachine +// +// Created by Christopher Rohl on 7/19/17. +// Copyright © 2017 Christopher Rohl. All rights reserved. +// + +import Cocoa + +class DebuggerViewController: NSViewController { + @IBOutlet weak var text_CPU_A: NSTextField! + @IBOutlet weak var text_CPU_X: NSTextField! + @IBOutlet weak var text_CPU_Y: NSTextField! + @IBOutlet weak var text_CPU_IP: NSTextField! + @IBOutlet weak var text_CPU_SR: NSTextField! + + @IBOutlet weak var debuggerTableView: NSTableView! + + let CPU = CPUState.sharedInstance + var disassembly: [Disassembly] = [Disassembly]() + + func updateCPUStatusFields() { + text_CPU_A.stringValue = String(format:"%02X", CPU.accumulator) + text_CPU_X.stringValue = String(format:"%02X", CPU.index_x) + text_CPU_Y.stringValue = String(format:"%02X", CPU.index_y) + text_CPU_IP.stringValue = String(format:"%04X", CPU.program_counter) + text_CPU_SR.stringValue = String(format:"%02X", CPU.stack_pointer) + } + + override func viewDidLoad() { + super.viewDidLoad() + + debuggerTableView.delegate = self + debuggerTableView.dataSource = self + + CPU.memoryInterface.loadBinary(path: "/Users/luigi/6502/test.bin") + updateCPUStatusFields() + disassembly = CPU.disassemble(fromAddress: 0x0000, length: 16) + debuggerTableView.reloadData() + + // Do any additional setup after loading the view. + } + + override var representedObject: Any? { + didSet { + // Update the view, if already loaded. + } + } + + func cpuStep() { + do { + try CPU.executeNextInstruction() + updateCPUStatusFields() + } catch CPUExceptions.invalidInstruction { + print("*** 6502 Exception: Invalid instruction 0xXX at 0xXXXX") + } catch { + print(error) + } + } + + @IBAction func btn_CPUStep(_ sender: Any) { + cpuStep() + } + +} + +extension DebuggerViewController: NSTableViewDelegate { + + fileprivate enum CellIdentifiers { + static let AddressCell = "AddressCellID" + static let DataCell = "DataCellID" + } + + func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { + var cellText: String = "" + var cellIdentifier: String = "" + let operation = disassembly[row] + + if(tableColumn == tableView.tableColumns[0]) { + cellText = String(format: "%04X", operation.address) + cellIdentifier = CellIdentifiers.AddressCell + } + + if(tableColumn == tableView.tableColumns[1]) { + if(operation.instruction == nil) { + cellText = "ILLEGAL" + } else { + switch(operation.instruction!.addressingMode) { + case .accumulator: + cellText = String(format: "%@ A", operation.instruction!.mnemonic) + case .immediate: + cellText = String(format: "%@ #%02X", operation.instruction!.mnemonic, operation.data[0]) + case .implied: + cellText = String(format: "%@", operation.instruction!.mnemonic) + case .relative: + cellText = String(format: "%@ #%04X", operation.instruction!.mnemonic, UInt16(operation.data[0]) + operation.address) + case .absolute: + cellText = String(format: "%@ #%02X%02X", operation.instruction!.mnemonic, operation.data[1], operation.data[0]) + case .zeropage: + cellText = String(format: "%@ $%02X", operation.instruction!.mnemonic, operation.data[0]) + case .indirect: + cellText = String(format: "%@ ($%02X%02X)", operation.instruction!.mnemonic, operation.data[1], operation.data[0]) + case .absolute_indexed_x: + cellText = String(format: "%@ #%02X%02X,X", operation.instruction!.mnemonic, operation.data[1], operation.data[0]) + case .absolute_indexed_y: + cellText = String(format: "%@ #%02X%02X,Y", operation.instruction!.mnemonic, operation.data[1], operation.data[0]) + case .zeropage_indexed_x: + cellText = String(format: "%@ $%02X,X", operation.instruction!.mnemonic, operation.data[0]) + case .zeropage_indexed_y: + cellText = String(format: "%@ $%02X,Y", operation.instruction!.mnemonic, operation.data[0]) + case .indexed_indirect: + cellText = String(format: "%@ ($%02X,X)", operation.instruction!.mnemonic, operation.data[0]) + case .indirect_indexed: + cellText = String(format: "%@ ($%02X),Y", operation.instruction!.mnemonic, operation.data[0]) + } + } + cellIdentifier = CellIdentifiers.DataCell + } + + if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: cellIdentifier), owner: nil) as? NSTableCellView { + cell.textField?.stringValue = cellText + return cell + } + + return nil + } + +} + +extension DebuggerViewController: NSTableViewDataSource { + func numberOfRows(in tableView: NSTableView) -> Int { + return disassembly.count + } + + func getItem(atIndex: Int) -> Disassembly { + return disassembly[atIndex] + } +} diff --git a/FruitMachine/M6502/CPUInstructions.swift b/FruitMachine/M6502/CPUInstructions.swift index 5c4ccea..4921e34 100644 --- a/FruitMachine/M6502/CPUInstructions.swift +++ b/FruitMachine/M6502/CPUInstructions.swift @@ -83,6 +83,15 @@ let InstructionTable: [UInt8:CPUInstruction] = [ 0xAC: CPUInstruction(mnemonic: "LDY", cycles: 4, bytes: 3, addressingMode: .absolute, action: Opcodes.LDY), 0xBC: CPUInstruction(mnemonic: "LDY", cycles: 4, bytes: 3, addressingMode: .absolute_indexed_x, action: Opcodes.LDY), + //ST functions + 0x85: CPUInstruction(mnemonic: "STA", cycles: 3, bytes: 2, addressingMode: .zeropage, action: Opcodes.STA), + 0x95: CPUInstruction(mnemonic: "STA", cycles: 4, bytes: 2, addressingMode: .zeropage_indexed_x, action: Opcodes.STA), + 0x8D: CPUInstruction(mnemonic: "STA", cycles: 4, bytes: 3, addressingMode: .absolute, action: Opcodes.STA), + 0x9D: CPUInstruction(mnemonic: "STA", cycles: 5, bytes: 3, addressingMode: .absolute_indexed_x, action: Opcodes.STA), + 0x99: CPUInstruction(mnemonic: "STA", cycles: 5, bytes: 3, addressingMode: .absolute_indexed_y, action: Opcodes.STA), + 0x81: CPUInstruction(mnemonic: "STA", cycles: 6, bytes: 2, addressingMode: .indexed_indirect, action: Opcodes.STA), + 0x91: CPUInstruction(mnemonic: "STA", cycles: 6, bytes: 2, addressingMode: .indirect_indexed, action: Opcodes.STA), + //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 e737730..2d12c77 100644 --- a/FruitMachine/M6502/CPUState.swift +++ b/FruitMachine/M6502/CPUState.swift @@ -95,6 +95,32 @@ class CPUState: NSObject { page_boundary_crossed = false } + func disassemble(fromAddress: UInt16, length: UInt16) -> [Disassembly] { + var disassembly: [Disassembly] = [Disassembly]() + + var currentAddress: UInt16 = fromAddress + let endAddress: UInt16 = fromAddress + length + + while(currentAddress < endAddress) { + let instruction = memoryInterface.readByte(offset: currentAddress) + let operation = InstructionTable[instruction] + var data = [UInt8]() + + if(operation != nil) { + for index in 1...operation!.bytes { + data.append(memoryInterface.readByte(offset:currentAddress + UInt16(index))) + } + + disassembly.append(Disassembly(instruction: operation, address: currentAddress, data: data)) + currentAddress = currentAddress + UInt16(operation!.bytes) + } else { + currentAddress = currentAddress + 1 + } + } + + return disassembly + } + func getOperandByte() -> UInt8 { //Returns the operand byte after the current instruction byte. return memoryInterface.readByte(offset: program_counter + 1) @@ -111,6 +137,22 @@ class CPUState: NSObject { return word } + + func getInstructionBytes(atAddress: UInt16) -> [UInt8] { + var bytes = [UInt8]() + + let instruction = memoryInterface.readByte(offset: atAddress) + let operation = InstructionTable[instruction] + + if(operation != nil){ + for offset in 0...operation!.bytes { + bytes.append(memoryInterface.readByte(offset: atAddress + UInt16(offset))) + } + } + + return bytes + + } func executeNextInstruction() throws { instruction_register = memoryInterface.readByte(offset: program_counter) diff --git a/FruitMachine/M6502/Disassembly.swift b/FruitMachine/M6502/Disassembly.swift new file mode 100644 index 0000000..d6d1ccd --- /dev/null +++ b/FruitMachine/M6502/Disassembly.swift @@ -0,0 +1,21 @@ +// +// Disassembly.swift +// FruitMachine +// +// Created by Christopher Rohl on 7/22/17. +// Copyright © 2017 Christopher Rohl. All rights reserved. +// + +import Cocoa + +class Disassembly: NSObject { + let instruction: CPUInstruction? + let address: UInt16 + let data: [UInt8] + + init(instruction: CPUInstruction?, address: UInt16, data: [UInt8]) { + self.instruction = instruction + self.address = address + self.data = data + } +} diff --git a/FruitMachine/M6502/Opcodes.swift b/FruitMachine/M6502/Opcodes.swift index 0fae35d..2f91d6d 100644 --- a/FruitMachine/M6502/Opcodes.swift +++ b/FruitMachine/M6502/Opcodes.swift @@ -65,6 +65,15 @@ func getOperandWordForAddressingMode(state: CPUState, mode: AddressingMode) -> U return state.getOperandWord() + state.index_y case .indirect: return state.memoryInterface.readWord(offset: state.getOperandWord()) + case .indexed_indirect: + let zp: UInt8 = state.memoryInterface.readByte(offset: UInt16(state.getOperandByte() + state.index_x)) + //read from (ZP) + let pointer: UInt16 = state.memoryInterface.readWord(offset: UInt16(zp)) + return state.memoryInterface.readWord(offset: pointer) + case .indirect_indexed: + let zp: UInt8 = state.memoryInterface.readByte(offset: UInt16(state.getOperandByte())) + let pointer: UInt16 = state.memoryInterface.readWord(offset: UInt16(zp)) + UInt16(state.index_y) + return state.memoryInterface.readWord(offset: pointer) default: print("Called getOperand: UInt16 on an instruction that expects a UInt8") return 0 @@ -97,6 +106,24 @@ class Opcodes: NSObject { state.updateNegativeFlag(value: state.index_y) } + static func STA(state: CPUState, addressingMode: AddressingMode) -> Void { + let address: UInt16 + + if(addressingMode == .zeropage || addressingMode == .zeropage_indexed_x) { + address = zpAsUInt16(address: state.getOperandByte()) + state.memoryInterface.writeByte(offset: address, value: state.accumulator) + + } + else if (addressingMode == .absolute || addressingMode == .absolute_indexed_x || addressingMode == .absolute_indexed_y || addressingMode == .indexed_indirect || addressingMode == .indirect_indexed) { + address = state.getOperandWord() + state.memoryInterface.writeByte(offset: address, value: state.accumulator) + } + else { + print("Illegal addressing mode for STA") + return + } + } + //Register instructions static func TAX(state: CPUState, addressingMode: AddressingMode) -> Void { state.index_x = state.accumulator diff --git a/FruitMachine/ViewController.swift b/FruitMachine/ViewController.swift deleted file mode 100644 index b762103..0000000 --- a/FruitMachine/ViewController.swift +++ /dev/null @@ -1,59 +0,0 @@ -// -// ViewController.swift -// FruitMachine -// -// Created by Christopher Rohl on 7/19/17. -// Copyright © 2017 Christopher Rohl. All rights reserved. -// - -import Cocoa - -class ViewController: NSViewController { - @IBOutlet weak var text_CPU_A: NSTextField! - @IBOutlet weak var text_CPU_X: NSTextField! - @IBOutlet weak var text_CPU_Y: NSTextField! - @IBOutlet weak var text_CPU_IP: NSTextField! - @IBOutlet weak var text_CPU_SR: NSTextField! - - let CPU = CPUState.sharedInstance - - func updateCPUStatusFields() { - text_CPU_A.stringValue = String(format:"%02X", CPU.accumulator) - text_CPU_X.stringValue = String(format:"%02X", CPU.index_x) - text_CPU_Y.stringValue = String(format:"%02X", CPU.index_y) - text_CPU_IP.stringValue = String(format:"%04X", CPU.instruction_register) - text_CPU_SR.stringValue = String(format:"%02X", CPU.instruction_register) - } - - override func viewDidLoad() { - super.viewDidLoad() - - CPU.memoryInterface.loadBinary(path: "/Users/luigi/6502/test.bin") - updateCPUStatusFields() - - // Do any additional setup after loading the view. - } - - override var representedObject: Any? { - didSet { - // Update the view, if already loaded. - } - } - - func cpuStep() { - do { - try CPU.executeNextInstruction() - updateCPUStatusFields() - } catch CPUExceptions.invalidInstruction { - print("*** 6502 Exception: Invalid instruction 0xXX at 0xXXXX") - } catch { - print(error) - } - } - - @IBAction func btn_CPUStep(_ sender: Any) { - cpuStep() - } - -} -