Debugger: added live screen view; pause/resume; added 6502 code interpreter for viewing code in memory
This commit is contained in:
parent
cc2fee38a4
commit
97e5d8a551
|
@ -245,6 +245,17 @@
|
|||
9250DCB31CAEEF990093CE9A /* MfiGameControllerHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 9250DCB21CAEEF990093CE9A /* MfiGameControllerHandler.m */; };
|
||||
9250DCB51CAEFD3B0093CE9A /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9250DCB41CAEFD3B0093CE9A /* GameController.framework */; };
|
||||
927E431A25C48592008E5517 /* CheatFinderManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927E431925C48592008E5517 /* CheatFinderManager.swift */; };
|
||||
927E436425C94C12008E5517 /* Stream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927E435925C94C12008E5517 /* Stream.swift */; };
|
||||
927E436525C94C12008E5517 /* Bus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927E435A25C94C12008E5517 /* Bus.swift */; };
|
||||
927E436625C94C12008E5517 /* CPU+Instructions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927E435C25C94C12008E5517 /* CPU+Instructions.swift */; };
|
||||
927E436725C94C12008E5517 /* CPU+Flags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927E435D25C94C12008E5517 /* CPU+Flags.swift */; };
|
||||
927E436825C94C12008E5517 /* CPU+Stack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927E435E25C94C12008E5517 /* CPU+Stack.swift */; };
|
||||
927E436925C94C12008E5517 /* CPU.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927E435F25C94C12008E5517 /* CPU.swift */; };
|
||||
927E436A25C94C12008E5517 /* Instruction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927E436125C94C12008E5517 /* Instruction.swift */; };
|
||||
927E436B25C94C12008E5517 /* Mnemonic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927E436225C94C12008E5517 /* Mnemonic.swift */; };
|
||||
927E436C25C94C12008E5517 /* AddressingMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927E436325C94C12008E5517 /* AddressingMode.swift */; };
|
||||
927E437025C99A11008E5517 /* Debug6502Interpreter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927E436F25C99A11008E5517 /* Debug6502Interpreter.swift */; };
|
||||
927E437325C99C06008E5517 /* DebugMemoryCodeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927E437225C99C06008E5517 /* DebugMemoryCodeViewController.swift */; };
|
||||
928410581CA8443A00DC5D93 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 928410571CA8443A00DC5D93 /* Images.xcassets */; };
|
||||
92B9EADF24D3369700E6CFB2 /* EmulatorKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92B9EADE24D3369700E6CFB2 /* EmulatorKeyboard.swift */; };
|
||||
92E2063225AADFB000AE3F28 /* PreviewUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92E2063125AADF6E00AE3F28 /* PreviewUI.swift */; };
|
||||
|
@ -601,6 +612,17 @@
|
|||
9250DCB21CAEEF990093CE9A /* MfiGameControllerHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MfiGameControllerHandler.m; sourceTree = "<group>"; };
|
||||
9250DCB41CAEFD3B0093CE9A /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = System/Library/Frameworks/GameController.framework; sourceTree = SDKROOT; };
|
||||
927E431925C48592008E5517 /* CheatFinderManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheatFinderManager.swift; sourceTree = "<group>"; };
|
||||
927E435925C94C12008E5517 /* Stream.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Stream.swift; sourceTree = "<group>"; };
|
||||
927E435A25C94C12008E5517 /* Bus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Bus.swift; sourceTree = "<group>"; };
|
||||
927E435C25C94C12008E5517 /* CPU+Instructions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CPU+Instructions.swift"; sourceTree = "<group>"; };
|
||||
927E435D25C94C12008E5517 /* CPU+Flags.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CPU+Flags.swift"; sourceTree = "<group>"; };
|
||||
927E435E25C94C12008E5517 /* CPU+Stack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CPU+Stack.swift"; sourceTree = "<group>"; };
|
||||
927E435F25C94C12008E5517 /* CPU.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CPU.swift; sourceTree = "<group>"; };
|
||||
927E436125C94C12008E5517 /* Instruction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Instruction.swift; sourceTree = "<group>"; };
|
||||
927E436225C94C12008E5517 /* Mnemonic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Mnemonic.swift; sourceTree = "<group>"; };
|
||||
927E436325C94C12008E5517 /* AddressingMode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddressingMode.swift; sourceTree = "<group>"; };
|
||||
927E436F25C99A11008E5517 /* Debug6502Interpreter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Debug6502Interpreter.swift; sourceTree = "<group>"; };
|
||||
927E437225C99C06008E5517 /* DebugMemoryCodeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugMemoryCodeViewController.swift; sourceTree = "<group>"; };
|
||||
928410571CA8443A00DC5D93 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = ActiveGS/Images.xcassets; sourceTree = "<group>"; };
|
||||
92B9EADD24D3369600E6CFB2 /* ActiveGS-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ActiveGS-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
92B9EADE24D3369700E6CFB2 /* EmulatorKeyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmulatorKeyboard.swift; sourceTree = "<group>"; };
|
||||
|
@ -1025,10 +1047,7 @@
|
|||
92B9EADE24D3369700E6CFB2 /* EmulatorKeyboard.swift */,
|
||||
92E2063125AADF6E00AE3F28 /* PreviewUI.swift */,
|
||||
92B9EADD24D3369600E6CFB2 /* ActiveGS-Bridging-Header.h */,
|
||||
92FA0F2325B52EA200663577 /* EmuWrapper.mm */,
|
||||
92FA0F2625B52EC000663577 /* EmuWrapper.h */,
|
||||
92FA0F2925B5353D00663577 /* DebugMemoryViewController.swift */,
|
||||
927E431925C48592008E5517 /* CheatFinderManager.swift */,
|
||||
927E435725C94BE7008E5517 /* Debugger */,
|
||||
);
|
||||
name = Common.iphone;
|
||||
path = ../Common.iphone;
|
||||
|
@ -1142,6 +1161,52 @@
|
|||
path = ../Common.res;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
927E435725C94BE7008E5517 /* Debugger */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
927E436F25C99A11008E5517 /* Debug6502Interpreter.swift */,
|
||||
927E435825C94C12008E5517 /* 65C02Interpreter */,
|
||||
92FA0F2325B52EA200663577 /* EmuWrapper.mm */,
|
||||
92FA0F2625B52EC000663577 /* EmuWrapper.h */,
|
||||
92FA0F2925B5353D00663577 /* DebugMemoryViewController.swift */,
|
||||
927E431925C48592008E5517 /* CheatFinderManager.swift */,
|
||||
927E437225C99C06008E5517 /* DebugMemoryCodeViewController.swift */,
|
||||
);
|
||||
path = Debugger;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
927E435825C94C12008E5517 /* 65C02Interpreter */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
927E435925C94C12008E5517 /* Stream.swift */,
|
||||
927E435A25C94C12008E5517 /* Bus.swift */,
|
||||
927E435B25C94C12008E5517 /* CPU */,
|
||||
927E436025C94C12008E5517 /* Instruction */,
|
||||
);
|
||||
path = 65C02Interpreter;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
927E435B25C94C12008E5517 /* CPU */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
927E435C25C94C12008E5517 /* CPU+Instructions.swift */,
|
||||
927E435D25C94C12008E5517 /* CPU+Flags.swift */,
|
||||
927E435E25C94C12008E5517 /* CPU+Stack.swift */,
|
||||
927E435F25C94C12008E5517 /* CPU.swift */,
|
||||
);
|
||||
path = CPU;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
927E436025C94C12008E5517 /* Instruction */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
927E436125C94C12008E5517 /* Instruction.swift */,
|
||||
927E436225C94C12008E5517 /* Mnemonic.swift */,
|
||||
927E436325C94C12008E5517 /* AddressingMode.swift */,
|
||||
);
|
||||
path = Instruction;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
|
@ -1360,10 +1425,14 @@
|
|||
09BB43B011D92F70005ADA46 /* activeconfig.cpp in Sources */,
|
||||
09BB43B111D92F70005ADA46 /* ActiveDownload.cpp in Sources */,
|
||||
92B9EADF24D3369700E6CFB2 /* EmulatorKeyboard.swift in Sources */,
|
||||
927E436B25C94C12008E5517 /* Mnemonic.swift in Sources */,
|
||||
09052B9419053C9F00853FAE /* uncompr.cpp in Sources */,
|
||||
09BB43B211D92F70005ADA46 /* ActiveZip.cpp in Sources */,
|
||||
09052B8619053C9F00853FAE /* pngwtran.cpp in Sources */,
|
||||
09052B8B19053C9F00853FAE /* zip.cpp in Sources */,
|
||||
927E436825C94C12008E5517 /* CPU+Stack.swift in Sources */,
|
||||
927E436725C94C12008E5517 /* CPU+Flags.swift in Sources */,
|
||||
927E436925C94C12008E5517 /* CPU.swift in Sources */,
|
||||
09BB43B311D92F70005ADA46 /* CA2Text.cpp in Sources */,
|
||||
09BB43B411D92F70005ADA46 /* CEmulatorCtrl.cpp in Sources */,
|
||||
09BB43B511D92F70005ADA46 /* interface.cpp in Sources */,
|
||||
|
@ -1372,8 +1441,10 @@
|
|||
09BB43B711D92F70005ADA46 /* rom.cpp in Sources */,
|
||||
09FA6095125A7B3E00B07F77 /* activegsAppDelegate.mm in Sources */,
|
||||
09052B8119053C9F00853FAE /* pngset.cpp in Sources */,
|
||||
927E436425C94C12008E5517 /* Stream.swift in Sources */,
|
||||
09FA6096125A7B3E00B07F77 /* activegsList.mm in Sources */,
|
||||
09FA6097125A7B3E00B07F77 /* activegsViewController.mm in Sources */,
|
||||
927E437325C99C06008E5517 /* DebugMemoryCodeViewController.swift in Sources */,
|
||||
09052B8D19053C9F00853FAE /* compress.cpp in Sources */,
|
||||
09052B8E19053C9F00853FAE /* crc32.cpp in Sources */,
|
||||
09FA609A125A7B3E00B07F77 /* asyncimageview.mm in Sources */,
|
||||
|
@ -1389,6 +1460,7 @@
|
|||
09052B7D19053C9F00853FAE /* pngread.cpp in Sources */,
|
||||
09C81A781657ACAE008539D5 /* adb.cpp in Sources */,
|
||||
09C81A791657ACAE008539D5 /* async_event.cpp in Sources */,
|
||||
927E437025C99A11008E5517 /* Debug6502Interpreter.swift in Sources */,
|
||||
09C81A7A1657ACAE008539D5 /* clock.cpp in Sources */,
|
||||
09052B9119053C9F00853FAE /* inflate.cpp in Sources */,
|
||||
09C81A7B1657ACAE008539D5 /* compile_time.cpp in Sources */,
|
||||
|
@ -1409,11 +1481,14 @@
|
|||
924A1BAC1CB81B5800D69162 /* GameControllerKeyRemapController.m in Sources */,
|
||||
09C81A881657ACAE008539D5 /* SaveState.cpp in Sources */,
|
||||
09C81A891657ACAE008539D5 /* scc.cpp in Sources */,
|
||||
927E436625C94C12008E5517 /* CPU+Instructions.swift in Sources */,
|
||||
09C81A8A1657ACAE008539D5 /* scc_socket_driver.cpp in Sources */,
|
||||
09052B8319053C9F00853FAE /* pngvcrd.cpp in Sources */,
|
||||
927E436C25C94C12008E5517 /* AddressingMode.swift in Sources */,
|
||||
09C81A8B1657ACAE008539D5 /* sim65816.cpp in Sources */,
|
||||
09C81A8C1657ACAE008539D5 /* smartport.cpp in Sources */,
|
||||
09052B9019053C9F00853FAE /* inffast.cpp in Sources */,
|
||||
927E436A25C94C12008E5517 /* Instruction.swift in Sources */,
|
||||
09C81A8D1657ACAE008539D5 /* sound.cpp in Sources */,
|
||||
09052B7819053C9F00853FAE /* pngerror.cpp in Sources */,
|
||||
09052B7B19053C9F00853FAE /* pngmem.cpp in Sources */,
|
||||
|
@ -1425,6 +1500,7 @@
|
|||
09C81A9C1657ACDD008539D5 /* macdriver_generic.cpp in Sources */,
|
||||
09052B8919053C9F00853FAE /* iowin32.cpp in Sources */,
|
||||
09C81A9F1657ACDD008539D5 /* scc_macdriver.cpp in Sources */,
|
||||
927E436525C94C12008E5517 /* Bus.swift in Sources */,
|
||||
09C81AA71657AD18008539D5 /* emulatorView.mm in Sources */,
|
||||
09C81AA81657AD18008539D5 /* joystick_iphone.cpp in Sources */,
|
||||
09C81AA91657AD18008539D5 /* zoomEmulatorView.mm in Sources */,
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "KeyMapper.h"
|
||||
|
||||
#import "EmuWrapper.h"
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
//
|
||||
// BUS.swift
|
||||
// Monitor
|
||||
//
|
||||
// Created by Michał Kałużny on 08.01.18.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol Bus {
|
||||
func read(from address: UInt16) throws -> UInt8
|
||||
func read(from address: UInt16) throws -> UInt16
|
||||
func write(to address: UInt16, value: UInt8) throws
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
//
|
||||
// Flags.swift
|
||||
// 6502
|
||||
//
|
||||
// Created by Michał Kałużny on 17/11/2016.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension CPU {
|
||||
struct Flags: OptionSet {
|
||||
let rawValue: UInt8
|
||||
|
||||
static let negative = Flags(rawValue: 1 << 7)
|
||||
static let overflow = Flags(rawValue: 1 << 6)
|
||||
static let always = Flags(rawValue: 1 << 5)
|
||||
static let `break` = Flags(rawValue: 1 << 4)
|
||||
static let decimal = Flags(rawValue: 1 << 3)
|
||||
static let interrupt = Flags(rawValue: 1 << 2)
|
||||
static let zero = Flags(rawValue: 1 << 1)
|
||||
static let carry = Flags(rawValue: 1 << 0)
|
||||
|
||||
init(rawValue: UInt8) {
|
||||
self.rawValue = rawValue | 0b0010_0000
|
||||
}
|
||||
}
|
||||
|
||||
internal func recalculateStatus(flags: Flags, for value: UInt8) {
|
||||
if flags.contains(.carry) {
|
||||
calculateCarry(value: value)
|
||||
}
|
||||
|
||||
if flags.contains(.overflow) {
|
||||
calculateCarry(value: value)
|
||||
}
|
||||
|
||||
if flags.contains(.negative) {
|
||||
calculateSign(value: value)
|
||||
}
|
||||
|
||||
if flags.contains(.zero) {
|
||||
calculateZero(value: value)
|
||||
}
|
||||
}
|
||||
|
||||
private func calculateCarry(value: UInt8) {
|
||||
|
||||
}
|
||||
|
||||
private func calculateOverflow(value: UInt8) {
|
||||
|
||||
}
|
||||
|
||||
private func calculateSign(value: UInt8) {
|
||||
let bit = (value & (1 << 7)) != 0
|
||||
|
||||
if bit {
|
||||
Status.insert(.negative)
|
||||
} else {
|
||||
Status.remove(.negative)
|
||||
}
|
||||
}
|
||||
|
||||
private func calculateZero(value: UInt8) {
|
||||
if value != 0 {
|
||||
Status.remove(.zero)
|
||||
} else {
|
||||
Status.insert(.zero)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,246 @@
|
|||
//
|
||||
// CPU+Instructions.swift
|
||||
// MOS6502PackageDescription
|
||||
//
|
||||
// Created by Michał Kałużny on 09.01.18.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension CPU {
|
||||
internal func execute(instruction: Instruction) throws {
|
||||
switch instruction.mnemonic {
|
||||
//MARK: Branch Instructions
|
||||
case .BNE:
|
||||
PC += instruction.size
|
||||
|
||||
if Status.contains(.zero) != true {
|
||||
PC = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
}
|
||||
case .BEQ:
|
||||
PC += instruction.size
|
||||
|
||||
if Status.contains(.zero) == true {
|
||||
PC = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
}
|
||||
case .BPL:
|
||||
PC += instruction.size
|
||||
|
||||
if Status.contains(.negative) != true {
|
||||
PC = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
}
|
||||
case .BCC:
|
||||
PC += instruction.size
|
||||
|
||||
if Status.contains(.carry) != true {
|
||||
PC = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
}
|
||||
case .BCS:
|
||||
PC += instruction.size
|
||||
|
||||
if Status.contains(.carry) == true {
|
||||
PC = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
}
|
||||
case .BMI:
|
||||
PC += instruction.size
|
||||
|
||||
if Status.contains(.negative) == true {
|
||||
PC = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
}
|
||||
case .BVC:
|
||||
PC += instruction.size
|
||||
|
||||
if Status.contains(.overflow) != true {
|
||||
PC = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
}
|
||||
case .BVS:
|
||||
PC += instruction.size
|
||||
|
||||
if Status.contains(.overflow) == true {
|
||||
PC = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
}
|
||||
//MARK: Stack Operations
|
||||
case .TXS:
|
||||
SP = X
|
||||
PC += instruction.size
|
||||
case .TSX:
|
||||
X = SP
|
||||
recalculateStatus(flags: [.zero, .negative], for: X)
|
||||
PC += instruction.size
|
||||
case .PLP:
|
||||
let value = try pop() as UInt8
|
||||
Status = Flags(rawValue: value)
|
||||
PC += instruction.size
|
||||
case .PHP:
|
||||
try push(Status.rawValue | 0b0001_0000)
|
||||
PC += instruction.size
|
||||
case .PHA:
|
||||
try push(A)
|
||||
PC += instruction.size
|
||||
case .PLA:
|
||||
A = try pop()
|
||||
recalculateStatus(flags: [.zero, .negative], for: A)
|
||||
PC += instruction.size
|
||||
case .JSR:
|
||||
// JSR writes the *address of the last byte* of the instruction.
|
||||
try push(PC + instruction.size - 1)
|
||||
PC = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
case .RTS:
|
||||
PC += instruction.size
|
||||
PC = try pop()
|
||||
// We would land on the *last byte* of the JSR, just before the jump, we need to advance the PC
|
||||
PC += 1
|
||||
//MARK: Register Operations
|
||||
case .INX:
|
||||
X = X &+ 1
|
||||
recalculateStatus(flags: [.zero, .negative], for: X)
|
||||
PC += instruction.size
|
||||
case .DEX:
|
||||
X = X &- 1
|
||||
recalculateStatus(flags: [.zero, .negative], for: X)
|
||||
PC += instruction.size
|
||||
case .INY:
|
||||
Y = Y &+ 1
|
||||
recalculateStatus(flags: [.zero, .negative], for: Y)
|
||||
PC += instruction.size
|
||||
case .DEY:
|
||||
Y = Y &- 1
|
||||
recalculateStatus(flags: [.zero, .negative], for: Y)
|
||||
PC += instruction.size
|
||||
case .TYA:
|
||||
A = Y
|
||||
recalculateStatus(flags: [.negative, .zero], for: A)
|
||||
PC += instruction.size
|
||||
case .TXA:
|
||||
A = X
|
||||
recalculateStatus(flags: [.negative, .zero], for: A)
|
||||
PC += instruction.size
|
||||
case .TAX:
|
||||
X = A
|
||||
recalculateStatus(flags: [.negative, .zero], for: X)
|
||||
PC += instruction.size
|
||||
case .TAY:
|
||||
Y = A
|
||||
recalculateStatus(flags: [.negative, .zero], for: Y)
|
||||
PC += instruction.size
|
||||
//MARK: Flags Operations:
|
||||
case .SEI:
|
||||
Status.insert(.interrupt)
|
||||
PC += instruction.size
|
||||
case .CLD:
|
||||
Status.remove(.decimal)
|
||||
PC += instruction.size
|
||||
case .CLC:
|
||||
Status.remove(.carry)
|
||||
PC += instruction.size
|
||||
//MARK: Interrupt Operations:
|
||||
case .BRK:
|
||||
try push(PC)
|
||||
try push(Status.rawValue)
|
||||
|
||||
PC = try bus.read(from: CPU.interruptVector)
|
||||
|
||||
Status.insert(.break)
|
||||
//MARK: Other Instructions, clean me up please.
|
||||
case .LDA:
|
||||
A = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
recalculateStatus(flags: [.zero, .negative], for: A)
|
||||
PC += instruction.size
|
||||
case .STA:
|
||||
let address: UInt16 = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
try bus.write(to: address, value: A)
|
||||
PC += instruction.size
|
||||
case .LDX:
|
||||
X = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
recalculateStatus(flags: [.zero, .negative], for: X)
|
||||
PC += instruction.size
|
||||
case .STX:
|
||||
let address = try instruction.addressingMode.value(with: self, bus: bus) as UInt16
|
||||
try bus.write(to: address, value: X)
|
||||
PC += instruction.size
|
||||
case .LDY:
|
||||
Y = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
recalculateStatus(flags: [.zero, .negative], for: Y)
|
||||
PC += instruction.size
|
||||
case .STY:
|
||||
let address = try instruction.addressingMode.value(with: self, bus: bus) as UInt16
|
||||
try bus.write(to: address, value: Y)
|
||||
PC += instruction.size
|
||||
case .JMP:
|
||||
PC = try instruction.addressingMode.value(with: self, bus: bus)
|
||||
case .CMP:
|
||||
let value = try instruction.addressingMode.value(with: self, bus: bus) as UInt8
|
||||
|
||||
if A >= value {
|
||||
Status.insert(.carry)
|
||||
} else {
|
||||
Status.remove(.carry)
|
||||
}
|
||||
|
||||
let result = A &- value
|
||||
recalculateStatus(flags: [.negative, .zero], for: result)
|
||||
|
||||
PC += instruction.size
|
||||
case .CPY:
|
||||
let value = try instruction.addressingMode.value(with: self, bus: bus) as UInt8
|
||||
|
||||
if Y >= value {
|
||||
Status.insert(.carry)
|
||||
} else {
|
||||
Status.remove(.carry)
|
||||
}
|
||||
|
||||
let result = Y &- value
|
||||
recalculateStatus(flags: [.negative, .zero], for: result)
|
||||
|
||||
PC += instruction.size
|
||||
case .CPX:
|
||||
let value = try instruction.addressingMode.value(with: self, bus: bus) as UInt8
|
||||
|
||||
if X >= value {
|
||||
Status.insert(.carry)
|
||||
} else {
|
||||
Status.remove(.carry)
|
||||
}
|
||||
|
||||
let result = X &- value
|
||||
recalculateStatus(flags: [.negative, .zero], for: result)
|
||||
|
||||
PC += instruction.size
|
||||
case .ADC:
|
||||
let value = try instruction.addressingMode.value(with: self, bus: bus) as UInt8
|
||||
let carry: UInt8 = Status.contains(.carry) ? 1 : 0
|
||||
|
||||
let result = [A, value, carry].map(UInt16.init).reduce(0, +)
|
||||
if result > UInt8.max {
|
||||
Status.insert(.carry)
|
||||
} else {
|
||||
Status.remove(.carry)
|
||||
}
|
||||
|
||||
let final: UInt8 = UInt8(result & 0xFF)
|
||||
|
||||
let overflow = (((A ^ final) & 0x80) != 0) && (((UInt16(A) ^ result) & 0x80) == 0)
|
||||
if overflow {
|
||||
Status.insert(.overflow)
|
||||
} else {
|
||||
Status.remove(.overflow)
|
||||
}
|
||||
|
||||
A = final
|
||||
|
||||
recalculateStatus(flags: [.negative, .zero], for: A)
|
||||
PC += instruction.size
|
||||
case .EOR:
|
||||
let value = try instruction.addressingMode.value(with: self, bus: bus) as UInt8
|
||||
A = A ^ value
|
||||
|
||||
recalculateStatus(flags: [.negative, .zero], for: A)
|
||||
PC += instruction.size
|
||||
case .NOP:
|
||||
PC += instruction.size
|
||||
case _:
|
||||
throw Error.unimplementedInstruction(instruction: instruction)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// CPU+Stack.swift
|
||||
// MOS6502PackageDescription
|
||||
//
|
||||
// Created by Michał Kałużny on 09.01.18.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension CPU {
|
||||
static let stackPointerBase: UInt16 = 0x100
|
||||
|
||||
internal func pop() throws -> UInt8 {
|
||||
SP += 1
|
||||
|
||||
let value: UInt8 = try bus.read(from: UInt16(SP) + CPU.stackPointerBase)
|
||||
|
||||
print("Popping value: \(value.hex)")
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
internal func pop() throws -> UInt16 {
|
||||
let low: UInt8 = try pop()
|
||||
let high: UInt8 = try pop()
|
||||
|
||||
return UInt16(low) | UInt16(high) << 8
|
||||
}
|
||||
|
||||
internal func push(_ value: UInt8) throws {
|
||||
try bus.write(to: UInt16(SP) + CPU.stackPointerBase, value: value)
|
||||
|
||||
print("Pushing value: \(value.hex)")
|
||||
|
||||
SP -= 1
|
||||
}
|
||||
|
||||
internal func push(_ value: UInt16) throws {
|
||||
let low: UInt8 = UInt8(value & 0xFF)
|
||||
let high: UInt8 = UInt8(value >> 8) & 0xFF
|
||||
|
||||
try push(high)
|
||||
try push(low)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
//
|
||||
// CPU.swift
|
||||
// 6502
|
||||
//
|
||||
// Created by Michał Kałużny on 15/11/2016.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class CPU {
|
||||
static let nmiVector: UInt16 = 0xFFFA
|
||||
static let resetVector: UInt16 = 0xFFFC
|
||||
static let interruptVector: UInt16 = 0xFFFE
|
||||
|
||||
enum Error: Swift.Error {
|
||||
case unimplementedInstruction(instruction: Instruction)
|
||||
}
|
||||
|
||||
public enum Register {
|
||||
case X
|
||||
case Y
|
||||
}
|
||||
|
||||
//MARK: Registers
|
||||
public var PC: UInt16 = 0
|
||||
var A: UInt8 = 0
|
||||
var X: UInt8 = 0
|
||||
var Y: UInt8 = 0
|
||||
var SP: UInt8 = 0
|
||||
var Status: Flags = []
|
||||
|
||||
let bus: Bus
|
||||
|
||||
let breakpoints: [UInt16] = [0x378A]
|
||||
|
||||
public init(bus: Bus) {
|
||||
self.bus = bus
|
||||
}
|
||||
|
||||
public func reset() throws {
|
||||
PC = try bus.read(from: CPU.resetVector)
|
||||
A = 0
|
||||
X = 0
|
||||
Y = 0
|
||||
SP = 0xFF
|
||||
Status = [.break, .interrupt, .always]
|
||||
}
|
||||
|
||||
public func step() throws {
|
||||
if breakpoints.contains(PC) {
|
||||
print("Breakpoint!")
|
||||
}
|
||||
print(self)
|
||||
let instruction = try fetch()
|
||||
print(instruction)
|
||||
try execute(instruction: instruction)
|
||||
print(self)
|
||||
print("=============================")
|
||||
}
|
||||
|
||||
public func run() throws {
|
||||
while true {
|
||||
try step()
|
||||
}
|
||||
}
|
||||
|
||||
public func fetch() throws -> Instruction {
|
||||
return try Instruction(from: bus, PC: PC)
|
||||
}
|
||||
|
||||
subscript(register: Register) -> UInt8 {
|
||||
switch register {
|
||||
case .X:
|
||||
return X
|
||||
case .Y:
|
||||
return Y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CPU: CustomStringConvertible {
|
||||
public var description: String {
|
||||
return "PC: \(PC.hex) SP: \(SP.hex) A: \(A.hex) X: \(X.hex) Y: \(Y.hex) \nFlags: \(Status.rawValue.bin)"
|
||||
}
|
||||
}
|
||||
|
||||
extension FixedWidthInteger {
|
||||
public var hex: String {
|
||||
return String(self, radix: 16, uppercase: true)
|
||||
}
|
||||
|
||||
var bin: String {
|
||||
return String(self, radix: 2, uppercase: true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
//
|
||||
// AddressingMode.swift
|
||||
// 6502
|
||||
//
|
||||
// Created by Michał Kałużny on 16/11/2016.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public extension Instruction {
|
||||
enum AddressingMode {
|
||||
enum Error: Swift.Error {
|
||||
case addressingModeNotImplemented
|
||||
}
|
||||
|
||||
case accumulator
|
||||
case immediate(data: UInt8)
|
||||
case implied
|
||||
case relative(data: Int8)
|
||||
case absolute(data: UInt16)
|
||||
case zeroPage(data: UInt8)
|
||||
case indirect(data: UInt16)
|
||||
case indexedIndirect(data: UInt8, register: CPU.Register)
|
||||
case indirectIndexed(data: UInt8, register: CPU.Register)
|
||||
case absoluteIndexed(data: UInt16, register: CPU.Register)
|
||||
case zeroPageIndexed(data: UInt8, register: CPU.Register)
|
||||
|
||||
func value(with cpu: CPU, bus: Bus) throws -> UInt8 {
|
||||
switch self {
|
||||
case .immediate(let data):
|
||||
return data
|
||||
case .absolute(let data):
|
||||
return try bus.read(from: data)
|
||||
case .absoluteIndexed(let data, let register):
|
||||
// Check if we should be overflowing here?
|
||||
return try bus.read(from: data &+ UInt16(cpu[register]))
|
||||
case _: throw Error.addressingModeNotImplemented
|
||||
}
|
||||
}
|
||||
|
||||
func value(with cpu: CPU, bus: Bus) throws -> UInt16 {
|
||||
switch self {
|
||||
case .absolute(let data):
|
||||
return data
|
||||
case .zeroPageIndexed(let base, let register):
|
||||
switch register {
|
||||
case .X:
|
||||
return UInt16(base &+ cpu.X)
|
||||
case .Y:
|
||||
return UInt16(base &+ cpu.Y)
|
||||
}
|
||||
case .indirect(let data):
|
||||
return try bus.read(from: data)
|
||||
case .relative(let data):
|
||||
return UInt16(Int32(cpu.PC) + Int32(data))
|
||||
case .zeroPage(let data):
|
||||
return UInt16(data)
|
||||
case _: throw Error.addressingModeNotImplemented
|
||||
}
|
||||
}
|
||||
|
||||
public var dataSize: UInt16 {
|
||||
switch self {
|
||||
case .absolute: return 2
|
||||
case .absoluteIndexed: return 1
|
||||
case .implied: return 0
|
||||
case .immediate: return 1
|
||||
case .indirect: return 2
|
||||
case .accumulator: return 0
|
||||
case .relative: return 1
|
||||
case .zeroPage: return 1
|
||||
case .indirectIndexed: return 1
|
||||
case .indexedIndirect: return 1
|
||||
case .zeroPageIndexed: return 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Instruction.AddressingMode: CustomStringConvertible {
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .accumulator: return "A"
|
||||
case .implied: return ""
|
||||
case .immediate(let data): return "#$\(String(format: "%02x", data))"
|
||||
case .relative(let data): return "$\(String(format: "%0X", data))"
|
||||
case .zeroPage(let data): return "$\(String(format: "%02x", data))"
|
||||
case .indirect(let data): return "$\(String(format: "%02x", data))"
|
||||
case .absolute(let data): return "$\(String(format: "%04x", data))"
|
||||
case .indirectIndexed(let data, let register): return "($\(String(format: "%02x", data)), \(register))"
|
||||
case .indexedIndirect(let data, let register): return "($\(String(format: "%02x", data))), \(register)"
|
||||
case .absoluteIndexed(let data, let register): return "$\(String(format: "%04x", data)), \(register)"
|
||||
case .zeroPageIndexed(let data, let register): return "$\(String(format: "%02x", data)), \(register)"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,215 @@
|
|||
//
|
||||
// Instruction.swift
|
||||
// 6502
|
||||
//
|
||||
// Created by Michał Kałużny on 15/11/2016.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct Instruction {
|
||||
public let opcode: UInt8
|
||||
public let mnemonic: Mnemonic
|
||||
public let addressingMode: AddressingMode
|
||||
|
||||
public enum Error: Swift.Error {
|
||||
case unknownOpcode(opcode: UInt8)
|
||||
}
|
||||
|
||||
public init(from bus: Bus, PC: UInt16) throws {
|
||||
opcode = try bus.read(from: PC) as UInt8
|
||||
mnemonic = try Mnemonic(opcode)
|
||||
|
||||
//MARK: Addressing Mode
|
||||
switch opcode {
|
||||
case 0x20: fallthrough
|
||||
case 0x0d: fallthrough
|
||||
case 0x0e: fallthrough
|
||||
case 0x2c: fallthrough
|
||||
case 0x2d: fallthrough
|
||||
case 0x2e: fallthrough
|
||||
case 0x4c: fallthrough
|
||||
case 0x4d: fallthrough
|
||||
case 0x4e: fallthrough
|
||||
case 0x6d: fallthrough
|
||||
case 0x7e: fallthrough
|
||||
case 0x8c: fallthrough
|
||||
case 0x8d: fallthrough
|
||||
case 0x8e: fallthrough
|
||||
case 0xac: fallthrough
|
||||
case 0xad: fallthrough
|
||||
case 0xae: fallthrough
|
||||
case 0xcc: fallthrough
|
||||
case 0xcd: fallthrough
|
||||
case 0xce: fallthrough
|
||||
case 0xec: fallthrough
|
||||
case 0xed: fallthrough
|
||||
case 0xee:
|
||||
let data = try bus.read(from: PC + 1) as UInt16
|
||||
addressingMode = .absolute(data: data)
|
||||
case 0x1d: fallthrough
|
||||
case 0x1e: fallthrough
|
||||
case 0x3d: fallthrough
|
||||
case 0x3e: fallthrough
|
||||
case 0x5d: fallthrough
|
||||
case 0x5e: fallthrough
|
||||
case 0x6e: fallthrough
|
||||
case 0x7d: fallthrough
|
||||
case 0x9d: fallthrough
|
||||
case 0xbc: fallthrough
|
||||
case 0xbd: fallthrough
|
||||
case 0xdd: fallthrough
|
||||
case 0xde: fallthrough
|
||||
case 0xfd: fallthrough
|
||||
case 0xfe:
|
||||
let data = try bus.read(from: PC + 1) as UInt16
|
||||
addressingMode = .absoluteIndexed(data: data, register: .X)
|
||||
case 0x19: fallthrough
|
||||
case 0x39: fallthrough
|
||||
case 0x59: fallthrough
|
||||
case 0x79: fallthrough
|
||||
case 0x99: fallthrough
|
||||
case 0xb9: fallthrough
|
||||
case 0xbe: fallthrough
|
||||
case 0xd9: fallthrough
|
||||
case 0xf9:
|
||||
let data = try bus.read(from: PC + 1) as UInt16
|
||||
addressingMode = .absoluteIndexed(data: data, register: .Y)
|
||||
case 0x0a: fallthrough
|
||||
case 0x2a: fallthrough
|
||||
case 0x4a: fallthrough
|
||||
case 0x6a:
|
||||
addressingMode = .accumulator
|
||||
case 0x09: fallthrough
|
||||
case 0x29: fallthrough
|
||||
case 0x49: fallthrough
|
||||
case 0x69: fallthrough
|
||||
case 0xa0: fallthrough
|
||||
case 0xa2: fallthrough
|
||||
case 0xa9: fallthrough
|
||||
case 0xc0: fallthrough
|
||||
case 0xc9: fallthrough
|
||||
case 0xe0: fallthrough
|
||||
case 0xe9:
|
||||
let data = try bus.read(from: PC + 1) as UInt8
|
||||
addressingMode = .immediate(data: data)
|
||||
case 0x0: fallthrough
|
||||
case 0x8: fallthrough
|
||||
case 0x18: fallthrough
|
||||
case 0x28: fallthrough
|
||||
case 0x38: fallthrough
|
||||
case 0x40: fallthrough
|
||||
case 0x48: fallthrough
|
||||
case 0x58: fallthrough
|
||||
case 0x60: fallthrough
|
||||
case 0x68: fallthrough
|
||||
case 0x78: fallthrough
|
||||
case 0x88: fallthrough
|
||||
case 0x98: fallthrough
|
||||
case 0x8a: fallthrough
|
||||
case 0x9a: fallthrough
|
||||
case 0xa8: fallthrough
|
||||
case 0xaa: fallthrough
|
||||
case 0xb8: fallthrough
|
||||
case 0xba: fallthrough
|
||||
case 0xc8: fallthrough
|
||||
case 0xca: fallthrough
|
||||
case 0xd8: fallthrough
|
||||
case 0xe8: fallthrough
|
||||
case 0xea: fallthrough
|
||||
case 0xf8:
|
||||
addressingMode = .implied
|
||||
case 0x1: fallthrough
|
||||
case 0x21: fallthrough
|
||||
case 0x41: fallthrough
|
||||
case 0x61: fallthrough
|
||||
case 0x81: fallthrough
|
||||
case 0xa1: fallthrough
|
||||
case 0xc1: fallthrough
|
||||
case 0xe1:
|
||||
let data = try bus.read(from: PC + 1) as UInt8
|
||||
addressingMode = .indexedIndirect(data: data, register: .X)
|
||||
case 0x6c:
|
||||
let data = try bus.read(from: PC + 1) as UInt16
|
||||
addressingMode = .indirect(data: data)
|
||||
case 0x11: fallthrough
|
||||
case 0x31: fallthrough
|
||||
case 0x51: fallthrough
|
||||
case 0x71: fallthrough
|
||||
case 0x91: fallthrough
|
||||
case 0xb1: fallthrough
|
||||
case 0xd1: fallthrough
|
||||
case 0xf1:
|
||||
let data = try bus.read(from: PC + 1) as UInt8
|
||||
addressingMode = .indirectIndexed(data: data, register: .Y)
|
||||
case 0x10: fallthrough
|
||||
case 0x30: fallthrough
|
||||
case 0x50: fallthrough
|
||||
case 0x70: fallthrough
|
||||
case 0x90: fallthrough
|
||||
case 0xb0: fallthrough
|
||||
case 0xd0: fallthrough
|
||||
case 0xf0:
|
||||
let data = try bus.read(from: PC + 1) as UInt8
|
||||
addressingMode = .relative(data: Int8(bitPattern: data))
|
||||
case 0x5: fallthrough
|
||||
case 0x6: fallthrough
|
||||
case 0x24: fallthrough
|
||||
case 0x25: fallthrough
|
||||
case 0x26: fallthrough
|
||||
case 0x45: fallthrough
|
||||
case 0x46: fallthrough
|
||||
case 0x65: fallthrough
|
||||
case 0x66: fallthrough
|
||||
case 0x84: fallthrough
|
||||
case 0x85: fallthrough
|
||||
case 0x86: fallthrough
|
||||
case 0xa4: fallthrough
|
||||
case 0xa5: fallthrough
|
||||
case 0xa6: fallthrough
|
||||
case 0xc4: fallthrough
|
||||
case 0xc5: fallthrough
|
||||
case 0xc6: fallthrough
|
||||
case 0xe4: fallthrough
|
||||
case 0xe5: fallthrough
|
||||
case 0xe6:
|
||||
let data = try bus.read(from: PC + 1) as UInt8
|
||||
addressingMode = .zeroPage(data: data)
|
||||
case 0x15: fallthrough
|
||||
case 0x16: fallthrough
|
||||
case 0x35: fallthrough
|
||||
case 0x36: fallthrough
|
||||
case 0x55: fallthrough
|
||||
case 0x56: fallthrough
|
||||
case 0x75: fallthrough
|
||||
case 0x76: fallthrough
|
||||
case 0x94: fallthrough
|
||||
case 0x95: fallthrough
|
||||
case 0xb4: fallthrough
|
||||
case 0xb5: fallthrough
|
||||
case 0xd5: fallthrough
|
||||
case 0xd6: fallthrough
|
||||
case 0xf5: fallthrough
|
||||
case 0xf6:
|
||||
let data = try bus.read(from: PC + 1) as UInt8
|
||||
addressingMode = .zeroPageIndexed(data: data, register: .X)
|
||||
case 0x96: fallthrough
|
||||
case 0xb6:
|
||||
let data = try bus.read(from: PC + 1) as UInt8
|
||||
addressingMode = .zeroPageIndexed(data: data, register: .Y)
|
||||
default:
|
||||
throw Error.unknownOpcode(opcode: opcode)
|
||||
}
|
||||
}
|
||||
|
||||
public var size: UInt16 {
|
||||
return addressingMode.dataSize + 1
|
||||
}
|
||||
}
|
||||
|
||||
extension Instruction: CustomStringConvertible {
|
||||
public var description: String {
|
||||
return "\(mnemonic) \(addressingMode)"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
//
|
||||
// Opcode.swift
|
||||
// 6502
|
||||
//
|
||||
// Created by Michał Kałużny on 16/11/2016.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
private let mnemonicTable: [Instruction.Mnemonic?] = [
|
||||
/* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */
|
||||
/* 0 */ .BRK, .ORA, nil, .SLO, nil, .ORA, .ASL, .SLO, .PHP, .ORA, .ASL, nil, nil, .ORA, .ASL, .SLO, /* 0 */
|
||||
/* 1 */ .BPL, .ORA, nil, .SLO, nil, .ORA, .ASL, .SLO, .CLC, .ORA, nil, .SLO, nil, .ORA, .ASL, .SLO, /* 1 */
|
||||
/* 2 */ .JSR, .AND, nil, .RLA, .BIT, .AND, .ROL, .RLA, .PLP, .AND, .ROL, nil, .BIT, .AND, .ROL, .RLA, /* 2 */
|
||||
/* 3 */ .BMI, .AND, nil, .RLA, nil, .AND, .ROL, .RLA, .SEC, .AND, nil, .RLA, nil, .AND, .ROL, .RLA, /* 3 */
|
||||
/* 4 */ .RTI, .EOR, nil, .SRE, nil, .EOR, .LSR, .SRE, .PHA, .EOR, .LSR, nil, .JMP, .EOR, .LSR, .SRE, /* 4 */
|
||||
/* 5 */ .BVC, .EOR, nil, .SRE, nil, .EOR, .LSR, .SRE, .CLI, .EOR, nil, .SRE, nil, .EOR, .LSR, .SRE, /* 5 */
|
||||
/* 6 */ .RTS, .ADC, nil, .RRA, nil, .ADC, .ROR, .RRA, .PLA, .ADC, .ROR, nil, .JMP, .ADC, .ROR, .RRA, /* 6 */
|
||||
/* 7 */ .BVS, .ADC, nil, .RRA, nil, .ADC, .ROR, .RRA, .SEI, .ADC, nil, .RRA, nil, .ADC, .ROR, .RRA, /* 7 */
|
||||
/* 8 */ nil, .STA, nil, .SAX, .STY, .STA, .STX, .SAX, .DEY, nil, .TXA, nil, .STY, .STA, .STX, .SAX, /* 8 */
|
||||
/* 9 */ .BCC, .STA, nil, nil, .STY, .STA, .STX, .SAX, .TYA, .STA, .TXS, nil, nil, .STA, nil, nil, /* 9 */
|
||||
/* A */ .LDY, .LDA, .LDX, .LAX, .LDY, .LDA, .LDX, .LAX, .TAY, .LDA, .TAX, nil, .LDY, .LDA, .LDX, .LAX, /* A */
|
||||
/* B */ .BCS, .LDA, nil, .LAX, .LDY, .LDA, .LDX, .LAX, .CLV, .LDA, .TSX, .LAX, .LDY, .LDA, .LDX, .LAX, /* B */
|
||||
/* C */ .CPY, .CMP, nil, .DCP, .CPY, .CMP, .DEC, .DCP, .INY, .CMP, .DEX, nil, .CPY, .CMP, .DEC, .DCP, /* C */
|
||||
/* D */ .BNE, .CMP, nil, .DCP, nil, .CMP, .DEC, .DCP, .CLD, .CMP, nil, .DCP, nil, .CMP, .DEC, .DCP, /* D */
|
||||
/* E */ .CPX, .SBC, nil, .ISB, .CPX, .SBC, .INC, .ISB, .INX, .SBC, .NOP, .SBC, .CPX, .SBC, .INC, .ISB, /* E */
|
||||
/* F */ .BEQ, .SBC, nil, .ISB, nil, .SBC, .INC, .ISB, .SED, .SBC, nil, .ISB, nil, .SBC, .INC, .ISB /* F */
|
||||
]
|
||||
|
||||
public extension Instruction {
|
||||
enum Mnemonic: String {
|
||||
|
||||
enum Error: Swift.Error {
|
||||
case unknownMnemonic(opcode: String)
|
||||
}
|
||||
|
||||
init(_ opcode: UInt8) throws {
|
||||
guard let mnemonic = mnemonicTable[Int(opcode)] else {
|
||||
throw Instruction.Error.unknownOpcode(opcode: opcode)
|
||||
}
|
||||
self = mnemonic
|
||||
}
|
||||
|
||||
init(_ string: String) throws {
|
||||
guard let value = Mnemonic(rawValue: string) else {
|
||||
throw Error.unknownMnemonic(opcode: string)
|
||||
}
|
||||
self = value
|
||||
}
|
||||
|
||||
case ADC
|
||||
case AND
|
||||
case ASL
|
||||
case BCC
|
||||
case BCS
|
||||
case BEQ
|
||||
case BIT
|
||||
case BMI
|
||||
case BNE
|
||||
case BPL
|
||||
case BRK
|
||||
case BVC
|
||||
case BVS
|
||||
case CLC
|
||||
case CLD
|
||||
case CLI
|
||||
case CLV
|
||||
case CMP
|
||||
case CPX
|
||||
case CPY
|
||||
case DCP
|
||||
case DEC
|
||||
case DEX
|
||||
case DEY
|
||||
case EOR
|
||||
case INC
|
||||
case INX
|
||||
case INY
|
||||
case ISB
|
||||
case JMP
|
||||
case JSR
|
||||
case LAX
|
||||
case LDA
|
||||
case LDX
|
||||
case LDY
|
||||
case LSR
|
||||
case NOP
|
||||
case ORA
|
||||
case PHA
|
||||
case PHP
|
||||
case PLA
|
||||
case PLP
|
||||
case RLA
|
||||
case ROL
|
||||
case ROR
|
||||
case RRA
|
||||
case RTI
|
||||
case RTS
|
||||
case SBC
|
||||
case SEC
|
||||
case SED
|
||||
case SEI
|
||||
case SLO
|
||||
case SRE
|
||||
case SAX
|
||||
case STA
|
||||
case STX
|
||||
case STY
|
||||
case TAX
|
||||
case TAY
|
||||
case TSX
|
||||
case TXA
|
||||
case TXS
|
||||
case TYA
|
||||
}
|
||||
}
|
||||
|
||||
extension Instruction.Mnemonic: CustomStringConvertible {
|
||||
public var description: String {
|
||||
return "\(rawValue)"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
//
|
||||
// Stream.swift
|
||||
// 6502
|
||||
//
|
||||
// Created by Michał Kałużny on 15/11/2016.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol RawStreamReadable {}
|
||||
|
||||
extension UInt8: RawStreamReadable {}
|
||||
extension UInt16: RawStreamReadable {}
|
||||
|
||||
extension InputStream: Stream {
|
||||
func read(_ length: Int) -> [UInt8] {
|
||||
var buffer = Array<UInt8>(repeating: 0, count: length)
|
||||
_ = read(&buffer, maxLength: length)
|
||||
return buffer
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MemoryStream: Stream {
|
||||
let storage: [UInt8]
|
||||
var position: Int = 0
|
||||
|
||||
init(storage: [UInt8]) {
|
||||
self.storage = storage
|
||||
}
|
||||
|
||||
open func read(_ length: Int) -> [UInt8] {
|
||||
if position+length > storage.count {
|
||||
return [UInt8](repeating: 0, count: length)
|
||||
}
|
||||
|
||||
let slice = storage[position..<position+length]
|
||||
position += length
|
||||
return Array(slice)
|
||||
}
|
||||
}
|
||||
|
||||
protocol Stream {
|
||||
func read(_ length: Int) -> [UInt8]
|
||||
}
|
||||
|
||||
extension Stream {
|
||||
func read<T: RawStreamReadable>() -> T {
|
||||
let size = MemoryLayout<T>.size
|
||||
let bytes = read(size)
|
||||
let data = Data(bytes: bytes)
|
||||
let value: T = data.withUnsafeBytes { $0.pointee }
|
||||
return value
|
||||
}
|
||||
|
||||
func skip(_ count: Int) {
|
||||
_ = read(count)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
//
|
||||
// Debug6502Interpreter.swift
|
||||
// ActiveGS
|
||||
//
|
||||
// Created by Yoshi Sugawara on 2/2/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct AddressedInstruction: CustomStringConvertible {
|
||||
let address: UInt16
|
||||
let instruction: Instruction
|
||||
|
||||
public var description: String {
|
||||
return String(format: "$%04X : \(instruction.description)", address)
|
||||
}
|
||||
}
|
||||
|
||||
enum InterpreterError: Error {
|
||||
case instructionError(String)
|
||||
}
|
||||
|
||||
public class Debug6502Interpreter: Bus {
|
||||
public func read(from address: UInt16) throws -> UInt8 {
|
||||
return memory[Int(address)]
|
||||
}
|
||||
|
||||
public func read(from address: UInt16) throws -> UInt16 {
|
||||
if address + 1 >= memory.count {
|
||||
throw InterpreterError.instructionError("Tried to read beyond memory bus")
|
||||
}
|
||||
let low: UInt8 = try read(from: address)
|
||||
let high: UInt8 = try read(from: address + 1)
|
||||
return (UInt16(high) << 8 | UInt16(low))
|
||||
}
|
||||
|
||||
public func write(to address: UInt16, value: UInt8) throws {
|
||||
// no-op, interpreter only
|
||||
}
|
||||
|
||||
var memory: [UInt8]
|
||||
var cpu: CPU! = nil
|
||||
|
||||
init(memory: [UInt8]) {
|
||||
self.memory = memory
|
||||
self.cpu = CPU(bus: self)
|
||||
}
|
||||
|
||||
public func interpret(startAt address:UInt16 = 0) -> [AddressedInstruction] {
|
||||
cpu.PC = address
|
||||
var instructions = [AddressedInstruction]()
|
||||
while cpu.PC < memory.count {
|
||||
do {
|
||||
let instruction = try cpu.fetch()
|
||||
instructions.append(AddressedInstruction(address: cpu.PC, instruction: instruction))
|
||||
cpu.PC += instruction.size
|
||||
} catch Instruction.Error.unknownOpcode(let opcode) {
|
||||
print("Unknown opcode: \(opcode), skipping...")
|
||||
cpu.PC += 1
|
||||
} catch {
|
||||
print("Error when interpreting, stopping...")
|
||||
break
|
||||
}
|
||||
}
|
||||
return instructions
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
//
|
||||
// DebugMemoryCodeViewController.swift
|
||||
// ActiveGS
|
||||
//
|
||||
// Created by Yoshi Sugawara on 2/2/21.
|
||||
//
|
||||
|
||||
class DebugMemoryCodeViewController: UIViewController {
|
||||
let memoryModel: EmuMemoryModel
|
||||
|
||||
init(memoryModel: EmuMemoryModel) {
|
||||
self.memoryModel = memoryModel
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
lazy var tableView: UITableView = {
|
||||
let view = UITableView(frame: .zero)
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.backgroundColor = .clear
|
||||
view.separatorStyle = .none
|
||||
view.dataSource = self
|
||||
view.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
|
||||
return view
|
||||
}()
|
||||
|
||||
func setupView() {
|
||||
view.addSubview(tableView)
|
||||
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
|
||||
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
|
||||
tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
|
||||
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
|
||||
tableView.backgroundColor = .clear
|
||||
view.backgroundColor = .black
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
setupView()
|
||||
}
|
||||
}
|
||||
|
||||
extension DebugMemoryCodeViewController: UITableViewDataSource {
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return memoryModel.interpretedInstructions.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let instruction = memoryModel.interpretedInstructions[indexPath.row].description
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
|
||||
cell.textLabel?.font = UIFont(name: "Print Char 21", size: 14)
|
||||
cell.textLabel?.text = instruction
|
||||
return cell
|
||||
}
|
||||
}
|
|
@ -27,6 +27,16 @@ struct Orientation {
|
|||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
func getSnapshot() -> UIImage {
|
||||
UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.main.scale)
|
||||
drawHierarchy(in: self.bounds, afterScreenUpdates: true)
|
||||
let image = UIGraphicsGetImageFromCurrentImageContext()!
|
||||
UIGraphicsEndImageContext()
|
||||
return image
|
||||
}
|
||||
}
|
||||
|
||||
enum EmuMemoryMapSection: Int {
|
||||
case zeroPage = 0
|
||||
case processorStack
|
||||
|
@ -104,7 +114,6 @@ enum EmuMemoryMapSection: Int {
|
|||
}
|
||||
|
||||
class EmuMemoryModel {
|
||||
// make this static for now
|
||||
let numToDisplayPerCell = 8
|
||||
let maxMemorySize = 128 * 1024
|
||||
|
||||
|
@ -180,6 +189,22 @@ class EmuMemoryModel {
|
|||
}
|
||||
return memory[address]
|
||||
}
|
||||
|
||||
var memoryAsArray: [UInt8] {
|
||||
var buffer = [UInt8]()
|
||||
guard let memory = memory else {
|
||||
return buffer
|
||||
}
|
||||
for address in 0..<0x95ff {
|
||||
buffer.append(memory[address])
|
||||
}
|
||||
return buffer
|
||||
}
|
||||
|
||||
lazy var interpretedInstructions: [AddressedInstruction] = {
|
||||
let interpreter = Debug6502Interpreter(memory: memoryAsArray)
|
||||
return interpreter.interpret()
|
||||
}()
|
||||
|
||||
func refresh() {
|
||||
memory = EmuWrapper.memory()
|
||||
|
@ -206,6 +231,24 @@ class DebugMemoryButton: UIButton {
|
|||
}
|
||||
}
|
||||
|
||||
class DebugPauseResumeButton: DebugMemoryButton {
|
||||
override open var isSelected: Bool {
|
||||
didSet {
|
||||
backgroundColor = isSelected ? .red : .clear
|
||||
}
|
||||
}
|
||||
|
||||
convenience init() {
|
||||
self.init(type: .custom)
|
||||
addTarget(self, action: #selector(tapped(_:)), for: .touchUpInside)
|
||||
setTitle("RESUME", for: .normal)
|
||||
setTitleColor(.green, for: .normal)
|
||||
setTitle("PAUSE", for: .selected)
|
||||
setTitleColor(.white, for: .selected)
|
||||
titleLabel?.font = UIFont(name: "Print Char 21", size: 11)
|
||||
}
|
||||
}
|
||||
|
||||
protocol DebugMemoryCellDelegate: class {
|
||||
func updateSelection(to address: Int)
|
||||
func isAddressSelected(_ address: Int) -> Bool
|
||||
|
@ -310,6 +353,10 @@ class DebugMemoryCell: UITableViewCell {
|
|||
var actionControllerAnimatorProgress = 0.0
|
||||
var animator: UIViewPropertyAnimator?
|
||||
|
||||
private var displayLink: CADisplayLink?
|
||||
private var framesSince = 0
|
||||
private var framesSinceUpdateScreen = 0
|
||||
|
||||
let titleLabel: UILabel = {
|
||||
let label = UILabel(frame: .zero)
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
@ -329,6 +376,20 @@ class DebugMemoryCell: UITableViewCell {
|
|||
return button
|
||||
}()
|
||||
|
||||
let testCodeButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
button.setTitle("[CODE]", for: .normal)
|
||||
button.addTarget(self, action: #selector(testCodeButtonTapped(_:)), for: .touchUpInside)
|
||||
button.titleLabel?.font = UIFont(name: "Print Char 21", size: 12)
|
||||
button.setTitleColor(.red, for: .normal)
|
||||
return button
|
||||
}()
|
||||
@objc func testCodeButtonTapped(_ sender: UIButton) {
|
||||
let controller = DebugMemoryCodeViewController(memoryModel: self.memoryModel)
|
||||
present(controller, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
let tableView: UITableView = {
|
||||
let view = UITableView(frame: .zero)
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
@ -337,6 +398,12 @@ class DebugMemoryCell: UITableViewCell {
|
|||
return view
|
||||
}()
|
||||
|
||||
let resumePauseEmulationButton: DebugPauseResumeButton = {
|
||||
let button = DebugPauseResumeButton()
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
return button
|
||||
}()
|
||||
|
||||
init() {
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
@ -349,6 +416,7 @@ class DebugMemoryCell: UITableViewCell {
|
|||
view.addSubview(titleLabel)
|
||||
view.addSubview(tableView)
|
||||
view.addSubview(dismissButton)
|
||||
view.addSubview(resumePauseEmulationButton)
|
||||
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
|
||||
|
@ -357,11 +425,18 @@ class DebugMemoryCell: UITableViewCell {
|
|||
// 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
|
||||
resumePauseEmulationButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 8).isActive = true
|
||||
resumePauseEmulationButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 8).isActive = true
|
||||
view.backgroundColor = .black
|
||||
|
||||
view.addSubview(testCodeButton)
|
||||
testCodeButton.trailingAnchor.constraint(equalTo: dismissButton.trailingAnchor).isActive = true
|
||||
testCodeButton.topAnchor.constraint(equalTo: dismissButton.bottomAnchor, constant: 4).isActive = true
|
||||
}
|
||||
|
||||
func setupTableView() {
|
||||
tableView.dataSource = self
|
||||
tableView.delegate = self
|
||||
tableView.register(DebugMemoryCell.self, forCellReuseIdentifier: DebugMemoryCell.identifier)
|
||||
}
|
||||
|
||||
|
@ -416,17 +491,50 @@ class DebugMemoryCell: UITableViewCell {
|
|||
}
|
||||
}
|
||||
|
||||
func setupPauseResumeButton() {
|
||||
resumePauseEmulationButton.onTapped = { selected in
|
||||
if selected {
|
||||
EmuWrapper.resume()
|
||||
self.displayLink = CADisplayLink(target: self, selector: #selector(self.updateMemoryIfNeeded))
|
||||
self.displayLink?.isPaused = false
|
||||
self.displayLink?.add(to: RunLoop.main, forMode: .common)
|
||||
} else {
|
||||
EmuWrapper.pause()
|
||||
self.displayLink?.isPaused = true
|
||||
self.displayLink = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func updateMemoryIfNeeded() {
|
||||
// maybe apply cheats here?
|
||||
if framesSinceUpdateScreen > 3 {
|
||||
delegate?.updateEmulatorScreen()
|
||||
framesSinceUpdateScreen = 1
|
||||
}
|
||||
if framesSince > 30 {
|
||||
memoryModel.refresh()
|
||||
tableView.reloadData()
|
||||
delegate?.refreshActionController()
|
||||
framesSince = 1
|
||||
}
|
||||
framesSince += 1
|
||||
framesSinceUpdateScreen += 1
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
setupView()
|
||||
setupActionController()
|
||||
setupTableView()
|
||||
setupPauseResumeButton()
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
memoryModel.refresh()
|
||||
setupActionControllerOrientationConstraints()
|
||||
resumePauseEmulationButton.isSelected = false
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
|
@ -459,6 +567,8 @@ class DebugMemoryCell: UITableViewCell {
|
|||
}
|
||||
|
||||
@objc func closeTapped(_ sender: UIButton) {
|
||||
displayLink?.isPaused = true
|
||||
displayLink = nil
|
||||
dismiss(animated: true) {
|
||||
EmuWrapper.resume()
|
||||
}
|
||||
|
@ -505,6 +615,26 @@ extension DebugMemoryViewController: UITableViewDataSource {
|
|||
}
|
||||
}
|
||||
|
||||
extension DebugMemoryViewController: UITableViewDelegate {
|
||||
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
||||
let title = self.tableView(tableView, titleForHeaderInSection: section)
|
||||
let label = UILabel(frame: .zero)
|
||||
label.text = title
|
||||
label.font = UIFont(name: "Print Char 21", size: 12)
|
||||
label.textColor = .cyan
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
let view = UIView(frame: .zero)
|
||||
view.addSubview(label)
|
||||
label.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
|
||||
label.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 8).isActive = true
|
||||
view.backgroundColor = .black
|
||||
view.layer.borderColor = UIColor.cyan.cgColor
|
||||
view.layer.borderWidth = 1.0
|
||||
view.heightAnchor.constraint(equalToConstant: 30).isActive = true
|
||||
return view
|
||||
}
|
||||
}
|
||||
|
||||
extension DebugMemoryViewController: DebugMemoryCellDelegate {
|
||||
func updateSelection(to address: Int) {
|
||||
memoryModel.selectedAddress = address
|
||||
|
@ -524,6 +654,7 @@ extension DebugMemoryViewController: DebugMemoryCellDelegate {
|
|||
|
||||
protocol DebugMemoryViewControllerDelegate: class {
|
||||
func refreshActionController()
|
||||
func updateEmulatorScreen()
|
||||
}
|
||||
|
||||
protocol DebugMemoryActionViewControllerDelegate: class {
|
||||
|
@ -560,7 +691,7 @@ extension DebugMemoryViewController: DebugMemoryActionViewControllerDelegate {
|
|||
class DebugMemoryActionViewController: UIViewController {
|
||||
|
||||
enum Mode {
|
||||
case jumpToAddress, changeMemory, cheat
|
||||
case jumpToAddress, changeMemory, cheat, screen
|
||||
}
|
||||
|
||||
var mode: Mode = .jumpToAddress
|
||||
|
@ -569,7 +700,7 @@ class DebugMemoryActionViewController: UIViewController {
|
|||
weak var delegate:DebugMemoryActionViewControllerDelegate?
|
||||
|
||||
let segmentedControl: UISegmentedControl = {
|
||||
let control = UISegmentedControl(items: ["Jump", "Edit", "Cheat"])
|
||||
let control = UISegmentedControl(items: ["Jump", "Edit", "Cheat", "Screen"])
|
||||
control.translatesAutoresizingMaskIntoConstraints = false
|
||||
control.tintColor = .orange
|
||||
control.selectedSegmentIndex = 0
|
||||
|
@ -665,6 +796,7 @@ class DebugMemoryActionViewController: UIViewController {
|
|||
label.text = "Memory Tools"
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
label.textColor = .orange
|
||||
label.heightAnchor.constraint(equalToConstant: 20).isActive = true
|
||||
return label
|
||||
}()
|
||||
|
||||
|
@ -673,7 +805,7 @@ class DebugMemoryActionViewController: UIViewController {
|
|||
button.titleLabel?.font = UIFont(name: "Print Char 21", size: 12)
|
||||
button.setTitle("New Search", for: .normal)
|
||||
button.layer.borderWidth = 1
|
||||
button.layer.borderColor = UIColor.red.cgColor
|
||||
button.layer.borderColor = UIColor.purple.cgColor
|
||||
button.addTarget(self, action: #selector(cheatFinderNewSearchButtonPressed(_:)), for: .touchUpInside)
|
||||
return button
|
||||
}()
|
||||
|
@ -765,6 +897,12 @@ class DebugMemoryActionViewController: UIViewController {
|
|||
return view
|
||||
}()
|
||||
|
||||
lazy var emulatorScreenView: UIImageView = {
|
||||
let screenView = UIImageView(frame: .zero)
|
||||
screenView.translatesAutoresizingMaskIntoConstraints = false
|
||||
return screenView
|
||||
}()
|
||||
|
||||
@objc func cheatFinderNewSearchButtonPressed(_ sender: UIButton) {
|
||||
if let memory = delegate?.memory {
|
||||
cheatFinder.update(with: memory)
|
||||
|
@ -858,6 +996,8 @@ class DebugMemoryActionViewController: UIViewController {
|
|||
view.addSubview(segmentedControl)
|
||||
|
||||
view.addSubview(editFieldsStackView)
|
||||
|
||||
view.addSubview(emulatorScreenView)
|
||||
|
||||
// view.addSubview(memoryField)
|
||||
|
||||
|
@ -875,6 +1015,13 @@ class DebugMemoryActionViewController: UIViewController {
|
|||
// keyboardView.widthAnchor.constraint(equalToConstant: 200).isActive = true
|
||||
keyboardView.heightAnchor.constraint(equalToConstant: 200).isActive = true
|
||||
keyboardView.viewModel.delegate = self
|
||||
|
||||
emulatorScreenView.topAnchor.constraint(equalTo: segmentedControl.bottomAnchor, constant: 8).isActive = true
|
||||
emulatorScreenView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 4.0).isActive = true
|
||||
emulatorScreenView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -4.0).isActive = true
|
||||
emulatorScreenView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -16).isActive = true
|
||||
emulatorScreenView.isHidden = true
|
||||
|
||||
view.backgroundColor = .black
|
||||
}
|
||||
|
||||
|
@ -922,6 +1069,8 @@ class DebugMemoryActionViewController: UIViewController {
|
|||
mode = .changeMemory
|
||||
case 2:
|
||||
mode = .cheat
|
||||
case 3:
|
||||
mode = .screen
|
||||
default:
|
||||
mode = .jumpToAddress
|
||||
}
|
||||
|
@ -930,10 +1079,12 @@ class DebugMemoryActionViewController: UIViewController {
|
|||
|
||||
func update() {
|
||||
cheatFinderHide()
|
||||
emulatorScreenView.isHidden = true
|
||||
switch mode {
|
||||
case .jumpToAddress:
|
||||
memoryField.isHidden = false
|
||||
memoryField.layer.borderColor = UIColor.cyan.cgColor
|
||||
memoryField.textColor = .cyan
|
||||
editFieldsStackView.isHidden = false
|
||||
updateMemoryButton.isHidden = true
|
||||
resetMemoryButton.isHidden = true
|
||||
|
@ -957,6 +1108,12 @@ class DebugMemoryActionViewController: UIViewController {
|
|||
editFieldsStackView.isHidden = true
|
||||
keyboardView.isHidden = true
|
||||
cheatFinderUpdateUI()
|
||||
case .screen:
|
||||
memoryField.isHidden = true
|
||||
editFieldsStackView.isHidden = true
|
||||
keyboardView.isHidden = true
|
||||
emulatorScreenView.isHidden = false
|
||||
updateEmulatorScreen()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -990,6 +1147,13 @@ extension DebugMemoryActionViewController: DebugMemoryViewControllerDelegate {
|
|||
func refreshActionController() {
|
||||
update()
|
||||
}
|
||||
func updateEmulatorScreen() {
|
||||
let emulatorView = EmuWrapper.getEmulatorView()
|
||||
if let snapshot = emulatorView?.getSnapshot() {
|
||||
let flipped = UIImage(cgImage: snapshot.cgImage!, scale: 1.0, orientation: .downMirrored)
|
||||
emulatorScreenView.image = flipped
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension DebugMemoryActionViewController: UITableViewDataSource {
|
|
@ -18,5 +18,6 @@
|
|||
+(unsigned char*) memory;
|
||||
+(void)pause;
|
||||
+(void)resume;
|
||||
+(UIView*)getEmulatorView;
|
||||
|
||||
@end
|
|
@ -6,11 +6,13 @@
|
|||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "activegsAppDelegate.h"
|
||||
#import "EmuWrapper.h"
|
||||
|
||||
#include "../Common.osx/cemulatorctrlmac.h"
|
||||
#include "../kegs/Src/defc.h"
|
||||
#include "../kegs/Src/sim65816.h"
|
||||
#include "../../Common.osx/cemulatorctrlmac.h"
|
||||
#include "../../kegs/Src/defc.h"
|
||||
#include "../../kegs/Src/sim65816.h"
|
||||
|
||||
|
||||
@implementation EmuWrapper
|
||||
|
||||
|
@ -26,4 +28,8 @@
|
|||
r_sim65816.resume();
|
||||
}
|
||||
|
||||
+(UIView*)getEmulatorView {
|
||||
return [pManager getEmulatorView].zv.ew;
|
||||
}
|
||||
|
||||
@end
|
|
@ -491,7 +491,7 @@ struct KeyPosition {
|
|||
AppleIIKey(label: "SHIFT", code: AppleKeyboardKey.KEY_SHIFT.rawValue,
|
||||
keySize: .standard, isModifier: true, imageName: "shift", imageNameHighlighted: "shift.fill"),
|
||||
AppleIIKey(label: "ABC", code: AppleKeyboardKey.KEY_SPECIAL_TOGGLE.rawValue, keySize: .standard, imageName: "textformat.abc"),
|
||||
AppleIIKey(label: "\(UnicodeScalar(0xe080)!)", code: AppleKeyboardKey.KEY_OPTION.rawValue,
|
||||
AppleIIKey(label: "\(UnicodeScalar(0xe081)!)", code: AppleKeyboardKey.KEY_APPLE.rawValue,
|
||||
keySize: .standard, isModifier: true),
|
||||
AppleIIKey(label: "SPACE", code: AppleKeyboardKey.KEY_SPACE.rawValue, keySize: .wide)
|
||||
]
|
||||
|
@ -528,6 +528,8 @@ struct KeyPosition {
|
|||
AppleIIKey(label: "DELETE", code: AppleKeyboardKey.KEY_DELETE.rawValue, imageName: "delete.left", imageNameHighlighted: "delete.left.fill")
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "\(UnicodeScalar(0xe080)!)", code: AppleKeyboardKey.KEY_OPTION.rawValue,
|
||||
keySize: .standard, isModifier: true),
|
||||
AppleIIKey(label: "RETURN", code: AppleKeyboardKey.KEY_RETURN.rawValue, keySize: .wide)
|
||||
]
|
||||
],
|
||||
|
@ -543,15 +545,15 @@ struct KeyPosition {
|
|||
[
|
||||
SpacerKey(),
|
||||
SpacerKey(),
|
||||
AppleIIKey(label: "⬆", code: AppleKeyboardKey.KEY_UP_CURSOR.rawValue),
|
||||
AppleIIKey(label: "\(UnicodeScalar(0xe08b)!)", code: AppleKeyboardKey.KEY_UP_CURSOR.rawValue),
|
||||
SpacerKey(),
|
||||
SpacerKey()
|
||||
],
|
||||
[
|
||||
SpacerKey(),
|
||||
AppleIIKey(label: "⬅", code: AppleKeyboardKey.KEY_LEFT_CURSOR.rawValue),
|
||||
AppleIIKey(label: "⬇", code: AppleKeyboardKey.KEY_DOWN_CURSOR.rawValue),
|
||||
AppleIIKey(label: "➡", code: AppleKeyboardKey.KEY_RIGHT_CURSOR.rawValue),
|
||||
AppleIIKey(label: "\(UnicodeScalar(0xe088)!)", code: AppleKeyboardKey.KEY_LEFT_CURSOR.rawValue),
|
||||
AppleIIKey(label: "\(UnicodeScalar(0xe08a)!)", code: AppleKeyboardKey.KEY_DOWN_CURSOR.rawValue),
|
||||
AppleIIKey(label: "\(UnicodeScalar(0xe095)!)", code: AppleKeyboardKey.KEY_RIGHT_CURSOR.rawValue),
|
||||
SpacerKey()
|
||||
],
|
||||
[
|
||||
|
@ -561,7 +563,9 @@ struct KeyPosition {
|
|||
AppleIIKey(label: "DELETE", code: AppleKeyboardKey.KEY_DELETE.rawValue, imageName: "delete.left", imageNameHighlighted: "delete.left.fill")
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "RETURN", code: AppleKeyboardKey.KEY_RETURN.rawValue, keySize: .standard)
|
||||
AppleIIKey(label: "\(UnicodeScalar(0xe080)!)", code: AppleKeyboardKey.KEY_OPTION.rawValue,
|
||||
keySize: .standard, isModifier: true),
|
||||
AppleIIKey(label: "RETURN", code: AppleKeyboardKey.KEY_RETURN.rawValue, keySize: .wide)
|
||||
]
|
||||
]
|
||||
)
|
||||
|
|
|
@ -87,7 +87,12 @@ void x_invalidrect()
|
|||
|
||||
CGContextRef g = UIGraphicsGetCurrentContext();
|
||||
x_async_refresh(g,rect);
|
||||
|
||||
|
||||
// // yoshi test...
|
||||
// CGImageRef imgRef = CGBitmapContextCreateImage(g);
|
||||
// UIImage *image = [UIImage imageWithCGImage:imgRef];
|
||||
// CGImageRelease(imgRef);
|
||||
// NSLog(@"yoshi buffer image = %@",image);
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue