FruitMachine-Swift/FruitMachine/M6502/Debugger/DebuggerViewController.swift

198 lines
6.5 KiB
Swift

//
// ViewController.swift
// FruitMachine
//
// Created by Christopher Rohl on 7/19/17.
// Copyright © 2017 Christopher Rohl. All rights reserved.
//
import Cocoa
class DebuggerNotifications {
static let Break = NSNotification.Name(rawValue: "DebuggerBreak")
}
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 text_debugger_output: NSTextView!
@IBOutlet weak var text_debugger_input: NSTextField!
@IBOutlet weak var debuggerTableView: NSTableView!
@IBOutlet weak var btn_Break: NSButton!
@IBOutlet weak var btn_Step: NSButton!
@IBOutlet weak var btn_Run: NSButton!
var cpuInstance = CPU.sharedInstance
var isRunning = false
var disassembly: [Disassembly] = [Disassembly]()
@discardableResult 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())
disassembly = cpuInstance.disassemble(fromAddress: CPU.sharedInstance.program_counter &- 16, length: 256)
debuggerTableView.reloadData()
highlightCurrentInstruction()
}
override func viewDidLoad() {
super.viewDidLoad()
debuggerTableView.delegate = self
debuggerTableView.dataSource = self
updateCPUStatusFields()
disassembly = cpuInstance.disassemble(fromAddress: CPU.sharedInstance.program_counter &- 16, length: 256)
debuggerTableView.reloadData()
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
func queueCPUStep(queue: DispatchQueue) {
queue.async {
self.cpuInstance.cpuStep()
}
}
@IBAction func btn_CPUStep(_ sender: Any) {
cpuInstance.cpuStep()
updateCPUStatusFields()
EmulatedSystemInstance!.updateScreen()
}
@IBAction func btn_Break(_ sender: Any) {
NotificationCenter.default.post(name: EmulationNotifications.StopEmulation, object: nil)
btn_Step.isEnabled = true
}
@IBAction func btn_CPURun(_ sender: Any) {
NotificationCenter.default.post(name: EmulationNotifications.StartEmulation, object: nil)
btn_Step.isEnabled = false
}
@IBAction func btn_CPU_Restart(_ sender: Any) {
cpuInstance.performReset()
cpuInstance.program_counter = CPU.sharedInstance.memoryInterface.readWord(offset: 0xFFFC)
debugConsolePrint(str: "CPU restarted from \(cpuInstance.program_counter)", newline: true)
}
@IBAction func debuggerInput_submit(_ sender: NSTextField) {
interpretCommand(command: sender.stringValue)
sender.stringValue = ""
}
func debugConsolePrint(str: String, newline: Bool) {
text_debugger_output.appendText(line: str)
if(newline) {
text_debugger_output.appendText(line:"\r\n")
}
}
}
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 = ""
if(row >= disassembly.count) {
return nil //no cell
}
let operation = disassembly[row]
if(tableColumn == tableView.tableColumns[0]) {
cellText = operation.getAddressString()
cellIdentifier = CellIdentifiers.AddressCell
}
if(tableColumn == tableView.tableColumns[1]) {
if(operation.instruction == nil) {
cellText = "ILLEGAL"
} else {
cellText = operation.getInstructionString()
}
cellIdentifier = CellIdentifiers.DataCell
}
if(tableColumn == tableView.tableColumns[2]) {
cellText = operation.getDataString()
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]
}
}
extension NSTextView {
func appendText(line: String) {
let attrDict = [NSAttributedStringKey.font: NSFont.userFixedPitchFont(ofSize: 11)]
let astring = NSAttributedString(string: "\(line)", attributes: attrDict)
self.textStorage?.append(astring)
let loc = self.string.lengthOfBytes(using: String.Encoding.utf8)
let range = NSRange(location: loc, length: 0)
self.scrollRangeToVisible(range)
}
}
func xlog(logView:NSTextView?, line:String) {
if let view = logView {
view.appendText(line: line)
}
}