FruitMachine-Swift/FruitMachine/DebuggerViewController.swift

207 lines
7.7 KiB
Swift

//
// 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 text_CPU_Flags: NSTextField!
@IBOutlet weak var debuggerTableView: NSTableView!
var cpuInstance = CPU.sharedInstance
var isRunning = false
var disassembly: [Disassembly] = [Disassembly]()
func highlightCurrentInstruction() -> Bool {
for (index, instruction) in disassembly.enumerated() {
if(instruction.address == cpuInstance.program_counter) {
debuggerTableView.selectRowIndexes(NSIndexSet(index: index) as IndexSet, byExtendingSelection: false)
debuggerTableView.scrollRowToVisible(index+10)
debuggerTableView.scrollRowToVisible(index-5)
return true //instruction found
}
}
return false //instruction not found
}
func updateCPUStatusFields() {
text_CPU_A.stringValue = String(format:"%02X", cpuInstance.accumulator)
text_CPU_X.stringValue = String(format:"%02X", cpuInstance.index_x)
text_CPU_Y.stringValue = String(format:"%02X", cpuInstance.index_y)
text_CPU_IP.stringValue = String(format:"%04X", cpuInstance.program_counter)
text_CPU_SR.stringValue = String(format:"%02X", cpuInstance.stack_pointer)
text_CPU_Flags.stringValue = String(cpuInstance.status_register.asString())
if(!highlightCurrentInstruction()) {
disassembly = cpuInstance.disassemble(fromAddress: cpuInstance.program_counter, length: 256)
highlightCurrentInstruction()
}
}
override func viewDidLoad() {
super.viewDidLoad()
debuggerTableView.delegate = self
debuggerTableView.dataSource = self
cpuInstance.memoryInterface.loadBinary(path: "/Users/luigi/6502/test.bin")
cpuInstance.performReset()
cpuInstance.program_counter = 0x400 //entry point for the test program
updateCPUStatusFields()
disassembly = cpuInstance.disassemble(fromAddress: cpuInstance.program_counter, length: 10000)
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 cpuInstance.executeNextInstruction()
} catch CPUExceptions.invalidInstruction {
print("*** 6502 Exception: Invalid instruction 0xXX at 0xXXXX")
} catch {
print(error)
}
}
func cpuRun() {
isRunning = true
let queue = DispatchQueue(label: "com.luigithirty.m6502.instructions")
let main = DispatchQueue.main
queue.async {
while(self.isRunning == true)
{
queue.asyncAfter(deadline: .now() + .seconds(1), execute: {
self.cpuStep()
main.sync {
self.updateCPUStatusFields()
}
})
}
}
}
@IBAction func btn_CPUStep(_ sender: Any) {
cpuStep()
updateCPUStatusFields()
}
@IBAction func btn_Break(_ sender: Any) {
isRunning = false
_ = 0
}
@IBAction func btn_CPURun(_ sender: Any) {
cpuRun()
}
}
extension DebuggerViewController: NSTableViewDelegate {
fileprivate enum CellIdentifiers {
static let AddressCell = "AddressCellID"
static let DataCell = "DataCellID"
static let BytesCell = "BytesCellID"
}
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[1])
case .implied:
cellText = String(format: "%@", operation.instruction!.mnemonic)
case .relative:
var destination: UInt16 = operation.address
if((operation.data[1] & 0x80) == 0x80) {
destination = destination + 1 - UInt16(~operation.data[1])
} else {
destination = destination + UInt16(operation.data[1])
}
cellText = String(format: "%@ #$%04X", operation.instruction!.mnemonic, destination)
case .absolute:
cellText = String(format: "%@ $%02X%02X", operation.instruction!.mnemonic, operation.data[2], operation.data[1])
case .zeropage:
cellText = String(format: "%@ $%02X", operation.instruction!.mnemonic, operation.data[1])
case .indirect:
cellText = String(format: "%@ ($%02X%02X)", operation.instruction!.mnemonic, operation.data[2], operation.data[1])
case .absolute_indexed_x:
cellText = String(format: "%@ $%02X%02X,X", operation.instruction!.mnemonic, operation.data[2], operation.data[1])
case .absolute_indexed_y:
cellText = String(format: "%@ $%02X%02X,Y", operation.instruction!.mnemonic, operation.data[2], operation.data[1])
case .zeropage_indexed_x:
cellText = String(format: "%@ $%02X,X", operation.instruction!.mnemonic, operation.data[1])
case .zeropage_indexed_y:
cellText = String(format: "%@ $%02X,Y", operation.instruction!.mnemonic, operation.data[1])
case .indexed_indirect:
cellText = String(format: "%@ ($%02X,X)", operation.instruction!.mnemonic, operation.data[1])
case .indirect_indexed:
cellText = String(format: "%@ ($%02X),Y", operation.instruction!.mnemonic, operation.data[1])
}
}
cellIdentifier = CellIdentifiers.DataCell
}
if(tableColumn == tableView.tableColumns[2]) {
cellText = ""
for byte in operation.data {
cellText += String(format: "%02X ", byte)
}
cellIdentifier = CellIdentifiers.BytesCell
}
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]
}
}