Added action controller as child vc to memory debugger controller; change memory listing to be sectioned according to the Apple II memory map
This commit is contained in:
parent
bb56179099
commit
d2e512ad40
|
@ -8,6 +8,101 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
|
||||
struct Orientation {
|
||||
// indicate current device is in the LandScape orientation
|
||||
static var isLandscape: Bool {
|
||||
get {
|
||||
return UIDevice.current.orientation.isValidInterfaceOrientation
|
||||
? UIDevice.current.orientation.isLandscape
|
||||
: (UIApplication.shared.windows.first?.windowScene?.interfaceOrientation.isLandscape)!
|
||||
}
|
||||
}
|
||||
// indicate current device is in the Portrait orientation
|
||||
static var isPortrait: Bool {
|
||||
get {
|
||||
return UIDevice.current.orientation.isValidInterfaceOrientation
|
||||
? UIDevice.current.orientation.isPortrait
|
||||
: (UIApplication.shared.windows.first?.windowScene?.interfaceOrientation.isPortrait)!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum EmuMemoryMapSection: Int {
|
||||
case zeroPage = 0
|
||||
case processorStack
|
||||
case getlnBuffer
|
||||
case freeSpace
|
||||
case dosProdosInterruptVectors
|
||||
case textVideoPageAndPeripheralScreenholes
|
||||
case textVideoPageTwoOrApplesoftProgramVariables
|
||||
case freespace2
|
||||
case highResGraphicsPage1
|
||||
case highResGraphicsPage2
|
||||
case applesoftStringData
|
||||
|
||||
var range:Range<Int> {
|
||||
switch self {
|
||||
case .zeroPage: return 0..<0x100
|
||||
case .processorStack: return 0x100..<0x200
|
||||
case .getlnBuffer: return 0x200..<0x300
|
||||
case .freeSpace: return 0x300..<0x3d0
|
||||
case .dosProdosInterruptVectors: return 0x3d0..<0x400
|
||||
case .textVideoPageAndPeripheralScreenholes: return 0x400..<0x800
|
||||
case .textVideoPageTwoOrApplesoftProgramVariables: return 0x800..<0xc00
|
||||
case .freespace2: return 0xc00..<0x2000
|
||||
case .highResGraphicsPage1: return 0x2000..<0x4000
|
||||
case .highResGraphicsPage2: return 0x4000..<0x6000
|
||||
case .applesoftStringData: return 0x6000..<0x95ff
|
||||
}
|
||||
}
|
||||
|
||||
var numberOfItems: Int {
|
||||
self.range.count
|
||||
}
|
||||
|
||||
var title: String {
|
||||
switch self {
|
||||
case .zeroPage: return "Zero Page"
|
||||
case .processorStack: return "6502 Processor Stack"
|
||||
case .getlnBuffer: return "GETLN Line Input Buffer"
|
||||
case .freeSpace: return "Free Space for Machine Language, Shape Table, etc."
|
||||
case .dosProdosInterruptVectors: return "DOS, ProDOS, and Interrupt Vectors"
|
||||
case .textVideoPageAndPeripheralScreenholes: return "Text Video Page and Peripheral Screenholes"
|
||||
case .textVideoPageTwoOrApplesoftProgramVariables: return "Text Video Page 2 or Applesoft Program and Variables"
|
||||
case .freespace2: return "Free Space for Machine Language, Shapes, etc."
|
||||
case .highResGraphicsPage1: return "High Resolution Graphics Page 1"
|
||||
case .highResGraphicsPage2: return "High Resolution Graphics Page 2"
|
||||
case .applesoftStringData: return "Applesoft String Data"
|
||||
}
|
||||
}
|
||||
|
||||
static func section(for address:Int) -> EmuMemoryMapSection {
|
||||
if EmuMemoryMapSection.zeroPage.range.contains(address) {
|
||||
return .zeroPage
|
||||
} else if EmuMemoryMapSection.processorStack.range.contains(address) {
|
||||
return .processorStack
|
||||
} else if EmuMemoryMapSection.getlnBuffer.range.contains(address) {
|
||||
return .getlnBuffer
|
||||
} else if EmuMemoryMapSection.freeSpace.range.contains(address) {
|
||||
return .freeSpace
|
||||
} else if EmuMemoryMapSection.dosProdosInterruptVectors.range.contains(address) {
|
||||
return .dosProdosInterruptVectors
|
||||
} else if EmuMemoryMapSection.textVideoPageAndPeripheralScreenholes.range.contains(address) {
|
||||
return .textVideoPageAndPeripheralScreenholes
|
||||
} else if EmuMemoryMapSection.textVideoPageTwoOrApplesoftProgramVariables.range.contains(address) {
|
||||
return .textVideoPageTwoOrApplesoftProgramVariables
|
||||
} else if EmuMemoryMapSection.freespace2.range.contains(address) {
|
||||
return .freespace2
|
||||
} else if EmuMemoryMapSection.highResGraphicsPage1.range.contains(address) {
|
||||
return .highResGraphicsPage1
|
||||
} else if EmuMemoryMapSection.highResGraphicsPage2.range.contains(address) {
|
||||
return .highResGraphicsPage2
|
||||
} else {
|
||||
return .applesoftStringData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class EmuMemoryModel {
|
||||
// make this static for now
|
||||
let numToDisplayPerCell = 8
|
||||
|
@ -15,13 +110,22 @@ class EmuMemoryModel {
|
|||
|
||||
let slowMemory = EmuWrapper.slowMemory()
|
||||
|
||||
var selectedAddress: Int?
|
||||
|
||||
func offset(for indexPath: IndexPath) -> Int {
|
||||
return indexPath.row * numToDisplayPerCell
|
||||
let memMapSection = EmuMemoryMapSection(rawValue: indexPath.section)!
|
||||
return memMapSection.range.lowerBound + (indexPath.row * numToDisplayPerCell)
|
||||
}
|
||||
|
||||
func hexStrings(for indexPath: IndexPath) -> [String] {
|
||||
let memMapSection = EmuMemoryMapSection(rawValue: indexPath.section)!
|
||||
let startIndex = offset(for: indexPath)
|
||||
let endIndex = min(maxMemorySize, startIndex + numToDisplayPerCell)
|
||||
let endIndex = min(startIndex + numToDisplayPerCell, memMapSection.range.upperBound)
|
||||
guard startIndex < memMapSection.range.upperBound,
|
||||
endIndex <= memMapSection.range.upperBound else {
|
||||
print("indexes are out of range of this section: startIndex=\(startIndex) endIndex=\(endIndex) section range: \(memMapSection.range.lowerBound) - \(memMapSection.range.upperBound)")
|
||||
return []
|
||||
}
|
||||
var row = [String]()
|
||||
guard let slowMemory = slowMemory else {
|
||||
return row
|
||||
|
@ -32,6 +136,12 @@ class EmuMemoryModel {
|
|||
return row
|
||||
}
|
||||
|
||||
func indexPath(for address: Int) -> IndexPath {
|
||||
let section = EmuMemoryMapSection.section(for: address)
|
||||
let offset = (address - section.range.lowerBound) / numToDisplayPerCell
|
||||
return IndexPath(item: offset, section: section.rawValue)
|
||||
}
|
||||
|
||||
func getMemoryHexString(at address: Int) -> String {
|
||||
guard address > 0 && address < maxMemorySize else {
|
||||
print("Cannot get memory: address out of range \(address) > \(maxMemorySize)")
|
||||
|
@ -61,8 +171,36 @@ class EmuMemoryModel {
|
|||
|
||||
}
|
||||
|
||||
class DebugMemoryButton: UIButton {
|
||||
var onTapped: ((Bool) -> Void)?
|
||||
|
||||
override open var isSelected: Bool {
|
||||
didSet {
|
||||
backgroundColor = isSelected ? .white : .clear
|
||||
}
|
||||
}
|
||||
|
||||
convenience init() {
|
||||
self.init(type: .custom)
|
||||
addTarget(self, action: #selector(tapped(_:)), for: .touchUpInside)
|
||||
}
|
||||
|
||||
@objc func tapped(_ sender: UIButton) {
|
||||
isSelected.toggle()
|
||||
onTapped?(isSelected)
|
||||
}
|
||||
}
|
||||
|
||||
protocol DebugMemoryCellDelegate: class {
|
||||
func updateSelection(to address: Int)
|
||||
func isAddressSelected(_ address: Int) -> Bool
|
||||
}
|
||||
|
||||
class DebugMemoryCell: UITableViewCell {
|
||||
static let identifier = "DebugMemoryCell"
|
||||
weak var delegate: DebugMemoryCellDelegate?
|
||||
|
||||
var updateSelection: ((Int) -> Void)?
|
||||
|
||||
var offset: Int?
|
||||
|
||||
|
@ -99,19 +237,28 @@ class DebugMemoryCell: UITableViewCell {
|
|||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func updateWith(offset: Int, hexMemoryValues: [String]) {
|
||||
func updateWith(delegate: DebugMemoryCellDelegate, offset: Int, hexMemoryValues: [String]) {
|
||||
self.delegate = delegate
|
||||
self.offset = offset
|
||||
stackView.arrangedSubviews.forEach{ $0.removeFromSuperview() }
|
||||
addressLabel.text = String(format: "%04X:", offset)
|
||||
stackView.addArrangedSubview(addressLabel)
|
||||
stackView.setCustomSpacing(3, after: addressLabel)
|
||||
for (index, hexValue) in hexMemoryValues.enumerated() {
|
||||
let button = UIButton(type: .custom)
|
||||
let button = DebugMemoryButton()
|
||||
let address = offset + index
|
||||
button.setTitle(hexValue, for: .normal)
|
||||
button.titleLabel?.font = UIFont(name: "Print Char 21", size: 16)
|
||||
button.setTitleColor(.green, for: .normal)
|
||||
button.tag = index
|
||||
button.addTarget(self, action: #selector(didTapOnButton(_:)), for: .touchUpInside)
|
||||
button.setTitleColor(.black, for: .selected)
|
||||
button.onTapped = { didSelect in
|
||||
if didSelect {
|
||||
self.delegate?.updateSelection(to: address)
|
||||
}
|
||||
}
|
||||
button.isSelected = self.delegate?.isAddressSelected(address) ?? false
|
||||
// button.addTarget(self, action: #selector(didTapOnButton(_:)), for: .touchUpInside)
|
||||
stackView.addArrangedSubview(button)
|
||||
}
|
||||
let spacer = UIView()
|
||||
|
@ -128,12 +275,24 @@ class DebugMemoryCell: UITableViewCell {
|
|||
@objc class DebugMemoryViewController: UIViewController {
|
||||
let memoryModel = EmuMemoryModel()
|
||||
|
||||
// Landscape constraints
|
||||
var actionControllerTopToViewTopConstraint: NSLayoutConstraint?
|
||||
var actionControllerWidthConstraint: NSLayoutConstraint?
|
||||
var actionControllerLeadingToTableViewTrailingConstraint: NSLayoutConstraint?
|
||||
var tableViewToViewBottomConstraint: NSLayoutConstraint?
|
||||
|
||||
// Portrait constraints
|
||||
var actionControllerLeadingToViewLeadingConstraint: NSLayoutConstraint?
|
||||
var actionControllerTopToTableViewBottomConstraint: NSLayoutConstraint?
|
||||
var actionControllerHeightConstraint: NSLayoutConstraint?
|
||||
var tableViewTrailingToViewTrailingConstraint: NSLayoutConstraint?
|
||||
|
||||
let titleLabel: UILabel = {
|
||||
let label = UILabel(frame: .zero)
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
label.font = UIFont(name: "Print Char 21", size: 14)
|
||||
label.textColor = .white
|
||||
label.text = "Memory Debugger"
|
||||
label.text = "Apple II Memory Debugger"
|
||||
return label
|
||||
}()
|
||||
|
||||
|
@ -166,13 +325,13 @@ class DebugMemoryCell: UITableViewCell {
|
|||
view.addSubview(titleLabel)
|
||||
view.addSubview(tableView)
|
||||
view.addSubview(dismissButton)
|
||||
titleLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
|
||||
titleLabel.centerXAnchor.constraint(equalTo: tableView.centerXAnchor).isActive = true
|
||||
titleLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 16).isActive = true
|
||||
tableView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 8).isActive = true
|
||||
tableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -8).isActive = true
|
||||
// tableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -8).isActive = true
|
||||
tableView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 16).isActive = true
|
||||
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
|
||||
dismissButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -8).isActive = true
|
||||
// tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
|
||||
dismissButton.trailingAnchor.constraint(equalTo: tableView.trailingAnchor, constant: -8).isActive = true
|
||||
dismissButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 8.0).isActive = true
|
||||
view.backgroundColor = .black
|
||||
}
|
||||
|
@ -182,23 +341,90 @@ class DebugMemoryCell: UITableViewCell {
|
|||
tableView.register(DebugMemoryCell.self, forCellReuseIdentifier: DebugMemoryCell.identifier)
|
||||
}
|
||||
|
||||
func setupActionController() {
|
||||
let actionController = DebugMemoryActionViewController()
|
||||
actionController.delegate = self
|
||||
addChild(actionController)
|
||||
actionController.didMove(toParent: self)
|
||||
actionController.view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(actionController.view)
|
||||
actionController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
|
||||
actionController.view.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
|
||||
|
||||
actionControllerHeightConstraint = actionController.view.heightAnchor.constraint(equalToConstant: 320)
|
||||
actionControllerLeadingToViewLeadingConstraint = actionController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor)
|
||||
actionControllerTopToTableViewBottomConstraint = actionController.view.topAnchor.constraint(equalTo: tableView.bottomAnchor)
|
||||
tableViewTrailingToViewTrailingConstraint = tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -8)
|
||||
|
||||
actionControllerWidthConstraint = actionController.view.widthAnchor.constraint(equalToConstant: 400)
|
||||
actionControllerTopToViewTopConstraint = actionController.view.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor)
|
||||
actionControllerLeadingToTableViewTrailingConstraint = actionController.view.leadingAnchor.constraint(equalTo: tableView.trailingAnchor)
|
||||
tableViewToViewBottomConstraint = tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
|
||||
|
||||
}
|
||||
|
||||
func setupActionControllerOrientationConstraints(orientation: UIInterfaceOrientation? = nil) {
|
||||
actionControllerLeadingToViewLeadingConstraint?.isActive = false
|
||||
actionControllerTopToTableViewBottomConstraint?.isActive = false
|
||||
actionControllerHeightConstraint?.isActive = false
|
||||
actionControllerTopToViewTopConstraint?.isActive = false
|
||||
actionControllerWidthConstraint?.isActive = false
|
||||
actionControllerLeadingToTableViewTrailingConstraint?.isActive = false
|
||||
tableViewToViewBottomConstraint?.isActive = false
|
||||
tableViewTrailingToViewTrailingConstraint?.isActive = false
|
||||
|
||||
let isPortrait = orientation != nil ? orientation!.isPortrait : Orientation.isPortrait
|
||||
|
||||
if isPortrait {
|
||||
actionControllerLeadingToViewLeadingConstraint?.isActive = true
|
||||
actionControllerTopToTableViewBottomConstraint?.isActive = true
|
||||
actionControllerHeightConstraint?.isActive = true
|
||||
tableViewTrailingToViewTrailingConstraint?.isActive = true
|
||||
} else {
|
||||
actionControllerTopToViewTopConstraint?.isActive = true
|
||||
actionControllerWidthConstraint?.isActive = true
|
||||
actionControllerLeadingToTableViewTrailingConstraint?.isActive = true
|
||||
tableViewToViewBottomConstraint?.isActive = true
|
||||
}
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
setupView()
|
||||
setupActionController()
|
||||
setupTableView()
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
setupActionControllerOrientationConstraints()
|
||||
}
|
||||
|
||||
@objc func closeTapped(_ sender: UIButton) {
|
||||
dismiss(animated: true) {
|
||||
EmuWrapper.resume()
|
||||
}
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
setupActionControllerOrientationConstraints()
|
||||
}
|
||||
|
||||
// override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||
// if size.width > size.height {
|
||||
// setupActionControllerOrientationConstraints(orientation: .landscapeLeft)
|
||||
// } else {
|
||||
// setupActionControllerOrientationConstraints(orientation: .portrait)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
extension DebugMemoryViewController: UITableViewDataSource {
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
var numRows = memoryModel.maxMemorySize / memoryModel.numToDisplayPerCell
|
||||
if memoryModel.maxMemorySize % memoryModel.numToDisplayPerCell > 0 {
|
||||
let numItems = EmuMemoryMapSection(rawValue: section)!.numberOfItems
|
||||
var numRows = numItems / memoryModel.numToDisplayPerCell
|
||||
if numItems % memoryModel.numToDisplayPerCell > 0 {
|
||||
numRows += 1
|
||||
}
|
||||
return numRows
|
||||
|
@ -207,9 +433,209 @@ extension DebugMemoryViewController: UITableViewDataSource {
|
|||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: DebugMemoryCell.identifier, for: indexPath) as! DebugMemoryCell
|
||||
let memoryVals = memoryModel.hexStrings(for: indexPath)
|
||||
cell.updateWith(offset: memoryModel.offset(for: indexPath), hexMemoryValues: memoryVals)
|
||||
let offset = memoryModel.offset(for: indexPath)
|
||||
cell.updateWith(delegate: self, offset: offset, hexMemoryValues: memoryVals)
|
||||
return cell
|
||||
}
|
||||
|
||||
func numberOfSections(in tableView: UITableView) -> Int {
|
||||
return EmuMemoryMapSection.applesoftStringData.rawValue + 1
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
return EmuMemoryMapSection.init(rawValue: section)!.title
|
||||
}
|
||||
}
|
||||
|
||||
extension DebugMemoryViewController: DebugMemoryCellDelegate {
|
||||
func updateSelection(to address: Int) {
|
||||
memoryModel.selectedAddress = address
|
||||
self.tableView.reloadData()
|
||||
// communicate selected address to action controller
|
||||
}
|
||||
|
||||
func isAddressSelected(_ address: Int) -> Bool {
|
||||
if let selectedAddress = memoryModel.selectedAddress,
|
||||
selectedAddress == address {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
protocol DebugMemoryActionViewControllerDelegate: class {
|
||||
func jump(to address: Int)
|
||||
}
|
||||
|
||||
extension DebugMemoryViewController: DebugMemoryActionViewControllerDelegate {
|
||||
func jump(to address: Int) {
|
||||
print("jumping to address: \(String(format: "%04X",address)) decimal: \(address)")
|
||||
let indexPath = memoryModel.indexPath(for: address)
|
||||
tableView.scrollToRow(at: indexPath, at: .middle, animated: true)
|
||||
updateSelection(to: address)
|
||||
}
|
||||
}
|
||||
|
||||
class DebugMemoryActionViewController: UIViewController {
|
||||
|
||||
enum Mode {
|
||||
case jumpToAddress, changeMemory
|
||||
}
|
||||
|
||||
var mode: Mode = .jumpToAddress
|
||||
|
||||
weak var delegate:DebugMemoryActionViewControllerDelegate?
|
||||
|
||||
let segmentedControl: UISegmentedControl = {
|
||||
let control = UISegmentedControl(items: ["Jump to Address", "Edit Memory"])
|
||||
control.translatesAutoresizingMaskIntoConstraints = false
|
||||
control.tintColor = .orange
|
||||
control.selectedSegmentIndex = 0
|
||||
return control
|
||||
}()
|
||||
|
||||
let jumpToAddressField: UITextField = {
|
||||
let field = UITextField(frame: .zero)
|
||||
field.translatesAutoresizingMaskIntoConstraints = false
|
||||
field.font = UIFont(name: "Print Char 21", size: 14)
|
||||
field.isUserInteractionEnabled = false
|
||||
field.text = ""
|
||||
field.textColor = .cyan
|
||||
field.layer.borderWidth = 1.0
|
||||
field.layer.borderColor = UIColor.cyan.cgColor
|
||||
field.textAlignment = .center
|
||||
return field
|
||||
}()
|
||||
|
||||
let changeMemoryField: UITextField = {
|
||||
let field = UITextField(frame: .zero)
|
||||
field.translatesAutoresizingMaskIntoConstraints = false
|
||||
field.font = UIFont(name: "Print Char 21", size: 14)
|
||||
field.isUserInteractionEnabled = false
|
||||
field.text = ""
|
||||
return field
|
||||
}()
|
||||
|
||||
let keyboardModel: EmulatorKeyboardViewModel = {
|
||||
let model = EmulatorKeyboardViewModel(keys:
|
||||
[
|
||||
[
|
||||
AppleIIKey(label: "0", code: 0),
|
||||
AppleIIKey(label: "1", code: 1),
|
||||
AppleIIKey(label: "2", code: 2),
|
||||
AppleIIKey(label: "3", code: 3)
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "4", code: 4),
|
||||
AppleIIKey(label: "5", code: 5),
|
||||
AppleIIKey(label: "6", code: 6),
|
||||
AppleIIKey(label: "7", code: 7)
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "8", code: 8),
|
||||
AppleIIKey(label: "9", code: 9),
|
||||
AppleIIKey(label: "A", code: 10),
|
||||
AppleIIKey(label: "B", code: 11)
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "C", code: 12),
|
||||
AppleIIKey(label: "D", code: 13),
|
||||
AppleIIKey(label: "E", code: 14),
|
||||
AppleIIKey(label: "F", code: 15)
|
||||
]
|
||||
]
|
||||
)
|
||||
model.isDraggable = false
|
||||
return model
|
||||
}()
|
||||
|
||||
lazy var keyboardView: EmulatorKeyboardView = {
|
||||
let view = keyboardModel.createView()
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
return view
|
||||
}()
|
||||
|
||||
let titleLabel: UILabel = {
|
||||
let label = UILabel(frame: .zero)
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
label.font = UIFont(name: "Print Char 21", size: 14)
|
||||
label.text = "Memory Tools"
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
label.textColor = .orange
|
||||
return label
|
||||
}()
|
||||
|
||||
func setupView() {
|
||||
view.addSubview(titleLabel)
|
||||
view.addSubview(segmentedControl)
|
||||
view.addSubview(jumpToAddressField)
|
||||
view.addSubview(keyboardView)
|
||||
titleLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 16).isActive = true
|
||||
titleLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
|
||||
segmentedControl.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 8).isActive = true
|
||||
segmentedControl.centerXAnchor.constraint(equalTo: titleLabel.centerXAnchor).isActive = true
|
||||
jumpToAddressField.topAnchor.constraint(equalTo: segmentedControl.bottomAnchor, constant: 8).isActive = true
|
||||
jumpToAddressField.widthAnchor.constraint(equalToConstant: 80).isActive = true
|
||||
jumpToAddressField.heightAnchor.constraint(equalToConstant: 40).isActive = true
|
||||
jumpToAddressField.centerXAnchor.constraint(equalTo: titleLabel.centerXAnchor).isActive = true
|
||||
keyboardView.centerXAnchor.constraint(equalTo: titleLabel.centerXAnchor).isActive = true
|
||||
keyboardView.topAnchor.constraint(equalTo: jumpToAddressField.bottomAnchor, constant: 8).isActive = true
|
||||
// keyboardView.widthAnchor.constraint(equalToConstant: 200).isActive = true
|
||||
keyboardView.heightAnchor.constraint(equalToConstant: 200).isActive = true
|
||||
keyboardView.viewModel.delegate = self
|
||||
view.backgroundColor = .black
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
setupView()
|
||||
}
|
||||
|
||||
func updateTextField(with keyCode: Int) {
|
||||
guard var text = jumpToAddressField.text else {
|
||||
print("no text in address field!")
|
||||
return
|
||||
}
|
||||
text.append(String(format: "%1X",keyCode))
|
||||
let charLimit: Int = {
|
||||
switch mode {
|
||||
case .jumpToAddress:
|
||||
return 4
|
||||
case .changeMemory:
|
||||
return 2
|
||||
}
|
||||
}()
|
||||
if text.count > charLimit {
|
||||
text.removeFirst()
|
||||
}
|
||||
if mode == .jumpToAddress {
|
||||
let scanner = Scanner(string: text)
|
||||
var address: UInt64 = 0
|
||||
if scanner.scanHexInt64(&address) && address < 0x95ff {
|
||||
delegate?.jump(to: Int(address))
|
||||
}
|
||||
}
|
||||
jumpToAddressField.text = text
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
extension DebugMemoryActionViewController: EmulatorKeyboardKeyPressedDelegate {
|
||||
func keyDown(_ key: KeyCoded) {
|
||||
print("DebugMemoryActionViewController keydown: \(key.keyLabel) ( \(key.keyCode) )")
|
||||
}
|
||||
|
||||
func keyUp(_ key: KeyCoded) {
|
||||
print("DebugMemoryActionViewController keyUp: \(key.keyLabel) ( \(key.keyCode) )")
|
||||
updateTextField(with: key.keyCode)
|
||||
}
|
||||
|
||||
func updateTransparency(toAlpha alpha: CGFloat) {
|
||||
// no op
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// delegate impl: EmulatorKeyboardKeyPressedDelegate
|
||||
// make delegate:
|
||||
|
|
|
@ -161,9 +161,11 @@ class EmulatorKeyboardView: UIView {
|
|||
print("sender frame: \(sender.frame), bounds: \(sender.bounds), convertedBounds = \(converted)")
|
||||
var labelFrame = converted.offsetBy(dx: 0, dy: -60)
|
||||
labelFrame = CGRect(x: labelFrame.origin.x, y: labelFrame.origin.y, width: labelFrame.width * 2, height: labelFrame.height * 2)
|
||||
label.backgroundColor = .purple
|
||||
label.textColor = .green
|
||||
label.backgroundColor = .white
|
||||
label.textColor = .black
|
||||
label.frame = labelFrame
|
||||
label.font = UIFont(name: "Print Char 21", size: 12)
|
||||
label.textAlignment = .center
|
||||
addSubview(label)
|
||||
pressedKeyLabels[label.text ?? "😭"] = label
|
||||
}
|
||||
|
@ -198,6 +200,9 @@ class EmulatorKeyboardView: UIView {
|
|||
alternateKeyRowsStackView.addArrangedSubview(keysInRow)
|
||||
}
|
||||
}
|
||||
if !model.isDraggable {
|
||||
dragMeView.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
func toggleKeysStackView() {
|
||||
|
@ -225,7 +230,7 @@ class EmulatorKeyboardView: UIView {
|
|||
}
|
||||
} else {
|
||||
key.setTitle(keyCoded.keyLabel, for: .normal)
|
||||
key.titleLabel?.font = UIFont.systemFont(ofSize: 12.0)
|
||||
key.titleLabel?.font = UIFont(name: "Print Char 21", size: 12)
|
||||
key.setTitleColor(.white, for: .normal)
|
||||
key.setTitleColor(.black, for: .highlighted)
|
||||
}
|
||||
|
@ -350,6 +355,8 @@ struct KeyPosition {
|
|||
var alternateKeys: [[KeyCoded]]?
|
||||
var modifiers: [Int16: KeyCoded]?
|
||||
|
||||
var isDraggable = true
|
||||
|
||||
@objc weak var delegate: EmulatorKeyboardKeyPressedDelegate?
|
||||
@objc weak var modifierDelegate: EmulatorKeyboardModifierPressedDelegate?
|
||||
|
||||
|
|
|
@ -1119,6 +1119,7 @@ extern int x_frame_rate ;
|
|||
-(void) memoryDebuggerButtonPressed:(id)sender {
|
||||
r_sim65816.pause();
|
||||
DebugMemoryViewController *controller = [[DebugMemoryViewController alloc] init];
|
||||
controller.modalPresentationStyle = UIModalPresentationFullScreen;
|
||||
[self presentViewController:controller animated:YES completion:nil];
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue