mirror of
https://github.com/Luigi30/FruitMachine-Swift.git
synced 2024-11-23 02:33:00 +00:00
graphics... sort of
This commit is contained in:
parent
dd8301ccab
commit
0c75e6f75e
@ -7,10 +7,14 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
2A2126841F2A9FA300E43DC1 /* DebuggerWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A2126831F2A9FA300E43DC1 /* DebuggerWindowController.swift */; };
|
||||
2A22EBFB1F21A7A700A36A61 /* IntegerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A22EBFA1F21A7A700A36A61 /* IntegerExtensions.swift */; };
|
||||
2A5BC5191F29A28D008C03BE /* AppleScreenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5BC5181F29A28D008C03BE /* AppleScreenView.swift */; };
|
||||
2A5BC51C1F29A2EB008C03BE /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A5BC51B1F29A2EB008C03BE /* QuartzCore.framework */; };
|
||||
2A5BC51E1F29A4C3008C03BE /* AppleScreenViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5BC51D1F29A4C3008C03BE /* AppleScreenViewDelegate.swift */; };
|
||||
2AA8B5F81F2A8889002B350F /* AppleI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AA8B5F71F2A8889002B350F /* AppleI.swift */; };
|
||||
2AA8B5FC1F2A8EAD002B350F /* Terminal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AA8B5FB1F2A8EAD002B350F /* Terminal.swift */; };
|
||||
2AA8B5FE1F2A942C002B350F /* PIAOverrides.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AA8B5FD1F2A942C002B350F /* PIAOverrides.swift */; };
|
||||
2AD458CE1F205EB700F05121 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD458CD1F205EB700F05121 /* AppDelegate.swift */; };
|
||||
2AD458D01F205EB700F05121 /* DebuggerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD458CF1F205EB700F05121 /* DebuggerViewController.swift */; };
|
||||
2AD458D21F205EB700F05121 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2AD458D11F205EB700F05121 /* Assets.xcassets */; };
|
||||
@ -31,10 +35,14 @@
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
2A2126831F2A9FA300E43DC1 /* DebuggerWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebuggerWindowController.swift; sourceTree = "<group>"; };
|
||||
2A22EBFA1F21A7A700A36A61 /* IntegerExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegerExtensions.swift; sourceTree = "<group>"; };
|
||||
2A5BC5181F29A28D008C03BE /* AppleScreenView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleScreenView.swift; sourceTree = "<group>"; };
|
||||
2A5BC51B1F29A2EB008C03BE /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
|
||||
2A5BC51D1F29A4C3008C03BE /* AppleScreenViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleScreenViewDelegate.swift; sourceTree = "<group>"; };
|
||||
2AA8B5F71F2A8889002B350F /* AppleI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleI.swift; sourceTree = "<group>"; };
|
||||
2AA8B5FB1F2A8EAD002B350F /* Terminal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Terminal.swift; sourceTree = "<group>"; };
|
||||
2AA8B5FD1F2A942C002B350F /* PIAOverrides.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIAOverrides.swift; sourceTree = "<group>"; };
|
||||
2AD458CA1F205EB700F05121 /* FruitMachine.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FruitMachine.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
2AD458CD1F205EB700F05121 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
2AD458CF1F205EB700F05121 /* DebuggerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebuggerViewController.swift; sourceTree = "<group>"; };
|
||||
@ -77,6 +85,17 @@
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2AA8B5F61F2A8844002B350F /* AppleI */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2AE42E411F28663600C4900E /* Video */,
|
||||
2A5BC5181F29A28D008C03BE /* AppleScreenView.swift */,
|
||||
2A5BC51D1F29A4C3008C03BE /* AppleScreenViewDelegate.swift */,
|
||||
2AA8B5F71F2A8889002B350F /* AppleI.swift */,
|
||||
);
|
||||
path = AppleI;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2AD458C11F205EB700F05121 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -97,11 +116,9 @@
|
||||
2AD458CC1F205EB700F05121 /* FruitMachine */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2AA8B5F61F2A8844002B350F /* AppleI */,
|
||||
2AD458DD1F205F0D00F05121 /* M6502 */,
|
||||
2AE42E411F28663600C4900E /* Video */,
|
||||
2AD458CD1F205EB700F05121 /* AppDelegate.swift */,
|
||||
2A5BC5181F29A28D008C03BE /* AppleScreenView.swift */,
|
||||
2A5BC51D1F29A4C3008C03BE /* AppleScreenViewDelegate.swift */,
|
||||
2AE42E381F28628300C4900E /* MainViewController.swift */,
|
||||
2AE42E3F1F28638100C4900E /* FruitMachine.storyboard */,
|
||||
2AD458D11F205EB700F05121 /* Assets.xcassets */,
|
||||
@ -139,6 +156,7 @@
|
||||
children = (
|
||||
2AD458D31F205EB700F05121 /* Debugger.storyboard */,
|
||||
2AD458CF1F205EB700F05121 /* DebuggerViewController.swift */,
|
||||
2A2126831F2A9FA300E43DC1 /* DebuggerWindowController.swift */,
|
||||
2AD6D5831F26E6BF008F3CF5 /* DebuggerCommands.swift */,
|
||||
2AE5BA031F23DE4400FAA343 /* Disassembly.swift */,
|
||||
);
|
||||
@ -167,6 +185,8 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2AE42E421F28665300C4900E /* CharacterGenerator.swift */,
|
||||
2AA8B5FB1F2A8EAD002B350F /* Terminal.swift */,
|
||||
2AA8B5FD1F2A942C002B350F /* PIAOverrides.swift */,
|
||||
);
|
||||
path = Video;
|
||||
sourceTree = "<group>";
|
||||
@ -242,8 +262,10 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2A2126841F2A9FA300E43DC1 /* DebuggerWindowController.swift in Sources */,
|
||||
2AD458E31F20661300F05121 /* CPUInstructions.swift in Sources */,
|
||||
2AD458D01F205EB700F05121 /* DebuggerViewController.swift in Sources */,
|
||||
2AA8B5FC1F2A8EAD002B350F /* Terminal.swift in Sources */,
|
||||
2A5BC51E1F29A4C3008C03BE /* AppleScreenViewDelegate.swift in Sources */,
|
||||
2AD458CE1F205EB700F05121 /* AppDelegate.swift in Sources */,
|
||||
2AE42E431F28665300C4900E /* CharacterGenerator.swift in Sources */,
|
||||
@ -254,10 +276,12 @@
|
||||
2AE42E3A1F28628300C4900E /* MainViewController.swift in Sources */,
|
||||
2AD458E51F2070DF00F05121 /* Opcodes.swift in Sources */,
|
||||
2AE42E081F2850F400C4900E /* ReadOverride.swift in Sources */,
|
||||
2AA8B5FE1F2A942C002B350F /* PIAOverrides.swift in Sources */,
|
||||
2AE5BA061F2469EB00FAA343 /* AddressConversions.swift in Sources */,
|
||||
2AE42E0A1F28521E00C4900E /* WriteOverride.swift in Sources */,
|
||||
2A5BC5191F29A28D008C03BE /* AppleScreenView.swift in Sources */,
|
||||
2AD458E11F2064CB00F05121 /* MemoryInterface.swift in Sources */,
|
||||
2AA8B5F81F2A8889002B350F /* AppleI.swift in Sources */,
|
||||
2AD458DF1F205F4500F05121 /* CPU.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -10,6 +10,7 @@ import Cocoa
|
||||
|
||||
@NSApplicationMain
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
let AppleScreenNotifications = Notification.Name("com.luigithirty.appleScreenNotifications")
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
// Insert code here to initialize your application
|
||||
@ -19,5 +20,12 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
// Insert code here to tear down your application
|
||||
}
|
||||
|
||||
@IBAction func view_scale_1x(_ sender: Any) {
|
||||
NotificationCenter.default.post(Notification(name: AppleScreenNotifications, object: "scaleFactor", userInfo: ["scaleFactor": 1]))
|
||||
}
|
||||
|
||||
@IBAction func view_scale_2x(_ sender: Any) {
|
||||
NotificationCenter.default.post(Notification(name: AppleScreenNotifications, object: "scaleFactor", userInfo: ["scaleFactor": 2]))
|
||||
}
|
||||
}
|
||||
|
||||
|
67
FruitMachine/AppleI/AppleI.swift
Normal file
67
FruitMachine/AppleI/AppleI.swift
Normal file
@ -0,0 +1,67 @@
|
||||
//
|
||||
// AppleI.swift
|
||||
// FruitMachine
|
||||
//
|
||||
// Created by Christopher Rohl on 7/27/17.
|
||||
// Copyright © 2017 Christopher Rohl. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
class AppleI: NSObject {
|
||||
static let sharedInstance = AppleI()
|
||||
|
||||
let cg = CharacterGenerator(romPath: "/Users/luigi/apple1/apple1.vid");
|
||||
let terminal = Terminal()
|
||||
|
||||
let emulatorViewDelegate = AppleScreenViewDelegate()
|
||||
let emulatorView = AppleScreenView(frame: NSMakeRect(0, 0, 400, 384))
|
||||
let emuScreenLayer = CALayer()
|
||||
|
||||
static let CPU_FREQUENCY = 1000000
|
||||
static let FRAMES_PER_SECOND = 60
|
||||
static let CYCLES_PER_BATCH = CPU_FREQUENCY / FRAMES_PER_SECOND
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
|
||||
emulatorView.wantsLayer = true
|
||||
emuScreenLayer.delegate = emulatorViewDelegate
|
||||
emuScreenLayer.frame = emulatorView.bounds
|
||||
emuScreenLayer.setNeedsDisplay()
|
||||
emulatorView.layer?.addSublayer(emuScreenLayer)
|
||||
|
||||
installOverrides()
|
||||
|
||||
for (cellNum, character) in terminal.characters.enumerated() {
|
||||
emulatorViewDelegate.putCharacterPixels(charPixels: cg.getCharacterPixels(charIndex: character), pixelPosition: emulatorViewDelegate.getPixelOffset(charCellIndex: cellNum))
|
||||
}
|
||||
|
||||
CPU.sharedInstance.memoryInterface.loadBinary(path: "/Users/luigi/apple1/apple1.rom", offset: 0xFF00)
|
||||
CPU.sharedInstance.performReset()
|
||||
|
||||
}
|
||||
|
||||
func installOverrides() {
|
||||
CPU.sharedInstance.memoryInterface.write_overrides.append(PIAOverrides.writeDSP)
|
||||
CPU.sharedInstance.memoryInterface.read_overrides.append(PIAOverrides.readDSP)
|
||||
}
|
||||
|
||||
func runFrame() {
|
||||
CPU.sharedInstance.cycles = 0
|
||||
CPU.sharedInstance.cyclesInBatch = AppleI.CYCLES_PER_BATCH
|
||||
CPU.sharedInstance.runCyclesBatch()
|
||||
|
||||
//update the video display
|
||||
for (cellNum, character) in terminal.characters.enumerated() {
|
||||
if(character == 0x8D) //CR
|
||||
{
|
||||
continue //ignore for now
|
||||
}
|
||||
|
||||
emulatorViewDelegate.putCharacterPixels(charPixels: cg.getCharacterPixels(charIndex: character), pixelPosition: emulatorViewDelegate.getPixelOffset(charCellIndex: cellNum))
|
||||
}
|
||||
|
||||
emulatorView.display()
|
||||
}
|
||||
}
|
@ -10,10 +10,6 @@ import Cocoa
|
||||
|
||||
class AppleScreenView: NSView {
|
||||
|
||||
//In characters
|
||||
static let TERMINAL_WIDTH = 40
|
||||
static let TERMINAL_HEIGHT = 24
|
||||
|
||||
override func draw(_ dirtyRect: NSRect) {
|
||||
super.draw(dirtyRect)
|
||||
// Drawing code here.
|
@ -52,7 +52,7 @@ class AppleScreenViewDelegate: NSObject, CALayerDelegate {
|
||||
|
||||
for charY in 0..<CharacterGenerator.CHAR_HEIGHT {
|
||||
for charX in 0..<CharacterGenerator.CHAR_WIDTH {
|
||||
indexedPixels[baseOffset + (PIXEL_WIDTH * charY) + CharacterGenerator.CHAR_WIDTH - charX] = (charPixels[charY] & UInt8(1 << charX)) > 0 ? 1 : 0
|
||||
indexedPixels[baseOffset + (PIXEL_WIDTH * charY) + CharacterGenerator.CHAR_WIDTH - charX - 1] = (charPixels[charY] & UInt8(1 << charX)) > 0 ? 1 : 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -61,6 +61,10 @@ class AppleScreenViewDelegate: NSObject, CALayerDelegate {
|
||||
return CGPoint(x: charCellX * 5, y: charCellY * 8)
|
||||
}
|
||||
|
||||
func getPixelOffset(charCellIndex: Int) -> CGPoint {
|
||||
return getPixelOffset(charCellX: charCellIndex % Terminal.CELLS_WIDTH, charCellY: charCellIndex / Terminal.CELLS_WIDTH)
|
||||
}
|
||||
|
||||
/* Draw the screen. */
|
||||
func draw(_ layer: CALayer, in ctx: CGContext) {
|
||||
let bounds = layer.bounds
|
||||
@ -70,8 +74,8 @@ class AppleScreenViewDelegate: NSObject, CALayerDelegate {
|
||||
let pixelProvider = CGDataProvider(data: NSData(bytes: &pixels, length: pixels.count * MemoryLayout<PixelData>.size))
|
||||
|
||||
let renderedImage = CGImage(width: PIXEL_WIDTH, height: PIXEL_HEIGHT, bitsPerComponent: Int(bitsPerComponent), bitsPerPixel: Int(bitsPerPixel), bytesPerRow: PIXEL_WIDTH * Int(MemoryLayout<PixelData>.size), space: colorSpace, bitmapInfo: bitmapInfo, provider: pixelProvider!, decode: nil, shouldInterpolate: true, intent: CGColorRenderingIntent.defaultIntent)
|
||||
|
||||
ctx.draw(renderedImage!, in: bounds)
|
||||
//draw stuff here
|
||||
}
|
||||
|
||||
}
|
@ -35,8 +35,26 @@ class CharacterGenerator: NSObject {
|
||||
func getCharacterPixels(charIndex: UInt8) -> [UInt8] {
|
||||
var pixelArray = [UInt8](repeating: 0x00, count: CharacterGenerator.CHAR_HEIGHT)
|
||||
|
||||
/* Instead of ignoring ASCII bit b6, we ignore bit b5. At the same time ASCII bit b6 must be inverted before it is fed to the character ROM. This way the entire character range from $40 to $7F will end up in the range $00 to $1F (twice of course). Now lower case characters are automatically translated into their corresponding upper case bit maps.
|
||||
*/
|
||||
|
||||
var convertedCharIndex = charIndex & 0x7F
|
||||
convertedCharIndex = convertedCharIndex & ~(0x20)
|
||||
convertedCharIndex = convertedCharIndex & ~(0x40)
|
||||
|
||||
/*
|
||||
if((convertedCharIndex & 0x40) == 0x40)
|
||||
{
|
||||
convertedCharIndex = convertedCharIndex & ~(0x40)
|
||||
}
|
||||
else
|
||||
{
|
||||
convertedCharIndex = convertedCharIndex | 0x40
|
||||
}
|
||||
*/
|
||||
|
||||
for scanlineIndex in 0..<CharacterGenerator.CHAR_HEIGHT {
|
||||
pixelArray[scanlineIndex] = ROM[scanlineIndex + (Int(charIndex) * CharacterGenerator.CHAR_HEIGHT)]
|
||||
pixelArray[scanlineIndex] = ROM[scanlineIndex + (Int(convertedCharIndex) * CharacterGenerator.CHAR_HEIGHT)]
|
||||
}
|
||||
|
||||
return pixelArray
|
23
FruitMachine/AppleI/Video/PIAOverrides.swift
Normal file
23
FruitMachine/AppleI/Video/PIAOverrides.swift
Normal file
@ -0,0 +1,23 @@
|
||||
//
|
||||
// Overrides.swift
|
||||
// FruitMachine
|
||||
//
|
||||
// Created by Christopher Rohl on 7/27/17.
|
||||
// Copyright © 2017 Christopher Rohl. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
class PIAOverrides: NSObject {
|
||||
static let writeDSP = WriteOverride(start: 0xD012, end: 0xD012, writeValue: false, action: PIAOverrides.actionWriteDSP)
|
||||
static func actionWriteDSP(terminal: AnyObject, byte: UInt8?) -> Void {
|
||||
//(terminal as! Terminal).putCharacter(charIndex: byte!)
|
||||
AppleI.sharedInstance.terminal.putCharacter(charIndex: byte!)
|
||||
}
|
||||
|
||||
static let readDSP = ReadOverride(start: 0xD012, end: 0xD012, writeValue: false, action: PIAOverrides.actionReadDSP)
|
||||
static func actionReadDSP(terminal: AnyObject, byte: UInt8?) -> Void {
|
||||
CPU.sharedInstance.memoryInterface.writeByte(offset: 0xD012, value: CPU.sharedInstance.memoryInterface.readByte(offset: 0xD012, bypassOverrides: true) & 0x7F, bypassOverrides: true) //the display is always ready
|
||||
}
|
||||
|
||||
}
|
47
FruitMachine/AppleI/Video/Terminal.swift
Normal file
47
FruitMachine/AppleI/Video/Terminal.swift
Normal file
@ -0,0 +1,47 @@
|
||||
//
|
||||
// Terminal.swift
|
||||
// FruitMachine
|
||||
//
|
||||
// Created by Christopher Rohl on 7/27/17.
|
||||
// Copyright © 2017 Christopher Rohl. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
struct Cell {
|
||||
var x: Int
|
||||
var y: Int
|
||||
}
|
||||
|
||||
class Terminal: NSObject {
|
||||
static let CELLS_WIDTH = 40
|
||||
static let CELLS_HEIGHT = 24
|
||||
|
||||
var cursorPosition: Cell
|
||||
var characters: [UInt8]
|
||||
|
||||
override init() {
|
||||
cursorPosition = Cell(x: 0, y: 0)
|
||||
characters = [UInt8](repeating: 0x40, count: Terminal.CELLS_WIDTH * Terminal.CELLS_HEIGHT)
|
||||
}
|
||||
|
||||
func cellToIndex(cell: Cell) -> Int {
|
||||
return (cell.y * Terminal.CELLS_WIDTH) + (cell.x % Terminal.CELLS_WIDTH)
|
||||
}
|
||||
|
||||
func putCharacter(charIndex: UInt8) {
|
||||
characters[cellToIndex(cell: cursorPosition)] = charIndex
|
||||
advanceCursor()
|
||||
}
|
||||
|
||||
func advanceCursor() {
|
||||
cursorPosition.x += 1
|
||||
if(cursorPosition.x == Terminal.CELLS_WIDTH) {
|
||||
cursorPosition.x = 0
|
||||
cursorPosition.y += 1
|
||||
if(cursorPosition.y == Terminal.CELLS_HEIGHT) {
|
||||
cursorPosition.y = 0 //TODO: scrolling
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -629,6 +629,17 @@
|
||||
<action selector="toggleFullScreen:" target="Ady-hI-5gd" id="dU3-MA-1Rq"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="Lvb-d5-deQ"/>
|
||||
<menuItem title="Scale 1x" keyEquivalent="1" id="nfL-VA-aet">
|
||||
<connections>
|
||||
<action selector="view_scale_1x:" target="Voe-Tx-rLC" id="QA5-yc-5zm"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Scale 2x" keyEquivalent="2" id="FYi-S6-fCx">
|
||||
<connections>
|
||||
<action selector="view_scale_2x:" target="Voe-Tx-rLC" id="rxd-VR-X3F"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
|
@ -116,7 +116,9 @@ class CPU: NSObject {
|
||||
let RESET_VECTOR: UInt16 = 0xFFFC
|
||||
let IRQ_VECTOR: UInt16 = 0xFFFE
|
||||
|
||||
static var sharedInstance = CPU()
|
||||
static let sharedInstance = CPU()
|
||||
|
||||
var isRunning: Bool
|
||||
|
||||
var cycles: Int
|
||||
var cyclesInBatch: Int
|
||||
@ -138,6 +140,8 @@ class CPU: NSObject {
|
||||
var breakpoints: [UInt16]
|
||||
|
||||
override init() {
|
||||
isRunning = false
|
||||
|
||||
cycles = 0
|
||||
cyclesInBatch = 0
|
||||
|
||||
@ -216,7 +220,7 @@ class CPU: NSObject {
|
||||
}
|
||||
}
|
||||
|
||||
func checkOutOfCycles() -> Bool {
|
||||
func outOfCycles() -> Bool {
|
||||
if(cycles > cyclesInBatch) {
|
||||
return true
|
||||
} else {
|
||||
@ -238,4 +242,28 @@ class CPU: NSObject {
|
||||
status_register.zero = (value == 0)
|
||||
}
|
||||
|
||||
/* Running */
|
||||
func cpuStep() {
|
||||
do {
|
||||
try executeNextInstruction()
|
||||
} catch CPUExceptions.invalidInstruction {
|
||||
isRunning = false
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
|
||||
func runCyclesBatch() {
|
||||
isRunning = true
|
||||
|
||||
while(!outOfCycles() && isRunning) {
|
||||
cpuStep()
|
||||
|
||||
if (breakpoints.contains(program_counter)) {
|
||||
isRunning = false
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
<!--Window Controller-->
|
||||
<scene sceneID="R2V-B0-nI4">
|
||||
<objects>
|
||||
<windowController id="B8D-0N-5wS" sceneMemberID="viewController">
|
||||
<windowController id="B8D-0N-5wS" customClass="DebuggerWindowController" customModule="FruitMachine" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<window key="window" title="Debugger" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="IQv-IB-iLA">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
|
@ -46,7 +46,7 @@ class DebuggerViewController: NSViewController {
|
||||
text_CPU_SR.stringValue = String(format:"%02X", cpuInstance.stack_pointer)
|
||||
text_CPU_Flags.stringValue = String(cpuInstance.status_register.asString())
|
||||
|
||||
disassembly = cpuInstance.disassemble(fromAddress: 0, length: 65535)
|
||||
disassembly = cpuInstance.disassemble(fromAddress: CPU.sharedInstance.program_counter, length: 256)
|
||||
highlightCurrentInstruction()
|
||||
}
|
||||
|
||||
@ -56,15 +56,10 @@ class DebuggerViewController: NSViewController {
|
||||
debuggerTableView.delegate = self
|
||||
debuggerTableView.dataSource = self
|
||||
|
||||
cpuInstance.memoryInterface.loadBinary(path: "/Users/luigi/6502/test.bin")
|
||||
cpuInstance.performReset()
|
||||
cpuInstance.program_counter = 0x400 //entry point for the test program
|
||||
updateCPUStatusFields()
|
||||
disassembly = cpuInstance.disassemble(fromAddress: 0, length: 65535)
|
||||
disassembly = cpuInstance.disassemble(fromAddress: CPU.sharedInstance.program_counter, length: 256)
|
||||
debuggerTableView.reloadData()
|
||||
|
||||
cpuInstance.breakpoints.append(0x34E8) //failing at $34FD SBC test
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
}
|
||||
|
||||
@ -74,25 +69,14 @@ class DebuggerViewController: NSViewController {
|
||||
}
|
||||
}
|
||||
|
||||
func cpuStep() {
|
||||
do {
|
||||
try cpuInstance.executeNextInstruction()
|
||||
} catch CPUExceptions.invalidInstruction {
|
||||
isRunning = false
|
||||
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
|
||||
func cpuRun() {
|
||||
func debugRun() {
|
||||
isRunning = true
|
||||
|
||||
cpuInstance.cycles = 0
|
||||
cpuInstance.cyclesInBatch = 1000000
|
||||
|
||||
while(!cpuInstance.checkOutOfCycles() && isRunning) {
|
||||
cpuStep()
|
||||
while(!cpuInstance.outOfCycles() && isRunning) {
|
||||
cpuInstance.cpuStep()
|
||||
|
||||
if (cpuInstance.breakpoints.contains(cpuInstance.program_counter)) {
|
||||
isRunning = false
|
||||
@ -105,12 +89,12 @@ class DebuggerViewController: NSViewController {
|
||||
|
||||
func queueCPUStep(queue: DispatchQueue) {
|
||||
queue.async {
|
||||
self.cpuStep()
|
||||
self.cpuInstance.cpuStep()
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func btn_CPUStep(_ sender: Any) {
|
||||
cpuStep()
|
||||
cpuInstance.cpuStep()
|
||||
updateCPUStatusFields()
|
||||
}
|
||||
|
||||
@ -120,7 +104,7 @@ class DebuggerViewController: NSViewController {
|
||||
}
|
||||
|
||||
@IBAction func btn_CPURun(_ sender: Any) {
|
||||
cpuRun()
|
||||
debugRun()
|
||||
}
|
||||
|
||||
@IBAction func btn_CPU_Restart(_ sender: Any) {
|
||||
@ -155,6 +139,11 @@ extension DebuggerViewController: NSTableViewDelegate {
|
||||
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]) {
|
||||
|
19
FruitMachine/M6502/Debugger/DebuggerWindowController.swift
Normal file
19
FruitMachine/M6502/Debugger/DebuggerWindowController.swift
Normal file
@ -0,0 +1,19 @@
|
||||
//
|
||||
// DebuggerWindowController.swift
|
||||
// FruitMachine
|
||||
//
|
||||
// Created by Christopher Rohl on 7/27/17.
|
||||
// Copyright © 2017 Christopher Rohl. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
class DebuggerWindowController: NSWindowController {
|
||||
|
||||
override func windowDidLoad() {
|
||||
super.windowDidLoad()
|
||||
|
||||
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
|
||||
}
|
||||
|
||||
}
|
@ -74,7 +74,7 @@ extension CPU {
|
||||
var disassembly: [Disassembly] = [Disassembly]()
|
||||
|
||||
var currentAddress: UInt16 = fromAddress
|
||||
let endAddress: UInt16 = fromAddress + length
|
||||
let endAddress: UInt16 = max(fromAddress &+ length, 0xFFFF)
|
||||
|
||||
while(currentAddress < endAddress) {
|
||||
let instruction = memoryInterface.readByte(offset: currentAddress)
|
||||
|
@ -21,10 +21,13 @@ class MemoryInterface: NSObject {
|
||||
write_overrides = [WriteOverride]()
|
||||
}
|
||||
|
||||
func readByte(offset: UInt16) -> UInt8 {
|
||||
for override in read_overrides {
|
||||
if case override.rangeStart ... override.rangeEnd = offset {
|
||||
override.action(CPU.sharedInstance, nil)
|
||||
func readByte(offset: UInt16, bypassOverrides: Bool = false) -> UInt8 {
|
||||
|
||||
if(!bypassOverrides) {
|
||||
for override in read_overrides {
|
||||
if case override.rangeStart ... override.rangeEnd = offset {
|
||||
override.action(CPU.sharedInstance, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,10 +35,16 @@ class MemoryInterface: NSObject {
|
||||
return memory[Int(offset)]
|
||||
}
|
||||
|
||||
func writeByte(offset: UInt16, value: UInt8) {
|
||||
for override in read_overrides {
|
||||
if case override.rangeStart ... override.rangeEnd = offset {
|
||||
override.action(CPU.sharedInstance, value)
|
||||
func writeByte(offset: UInt16, value: UInt8, bypassOverrides: Bool = false) {
|
||||
|
||||
if(!bypassOverrides) {
|
||||
for override in write_overrides {
|
||||
if case override.rangeStart ... override.rangeEnd = offset {
|
||||
override.action(CPU.sharedInstance, value)
|
||||
if(!override.writeValue) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,10 +57,10 @@ class MemoryInterface: NSObject {
|
||||
return (UInt16(high) << 8) | UInt16(low)
|
||||
}
|
||||
|
||||
func loadBinary(path: String) {
|
||||
func loadBinary(path: String, offset: UInt16) {
|
||||
do {
|
||||
let fileContent: NSData = try NSData(contentsOfFile: path)
|
||||
fileContent.getBytes(&memory, range: NSRange(location: 0, length: 65536))
|
||||
fileContent.getBytes(&memory[Int(offset)], range: NSRange(location: 0, length: 256))
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
|
@ -11,12 +11,14 @@ import Cocoa
|
||||
class MemoryOverride: NSObject {
|
||||
let rangeStart: UInt16
|
||||
let rangeEnd: UInt16
|
||||
let writeValue: Bool
|
||||
|
||||
let action: (CPU, UInt8?) -> Void
|
||||
|
||||
init(start: UInt16, end: UInt16, action: @escaping (CPU, UInt8?) -> Void) {
|
||||
init(start: UInt16, end: UInt16, writeValue: Bool, action: @escaping (AnyObject, UInt8?) -> Void) {
|
||||
rangeStart = start
|
||||
rangeEnd = end
|
||||
self.writeValue = writeValue
|
||||
|
||||
self.action = action
|
||||
}
|
||||
|
@ -10,36 +10,23 @@ import Cocoa
|
||||
import CoreGraphics
|
||||
|
||||
class MainViewController: NSViewController {
|
||||
var windowController: NSWindowController?
|
||||
var debuggerViewController = DebuggerViewController()
|
||||
let emuScreenLayer = CALayer()
|
||||
|
||||
let cg = CharacterGenerator(romPath: "/Users/luigi/apple1/apple1.vid");
|
||||
var appleScreenView: AppleScreenView = AppleScreenView(frame: NSMakeRect(0, 0, 400, 384))
|
||||
let appleScreenViewDelegate = AppleScreenViewDelegate()
|
||||
let computer = AppleI.sharedInstance
|
||||
|
||||
var debuggerWindowController: DebuggerWindowController!
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
let debuggerStoryboard = NSStoryboard(name: NSStoryboard.Name(rawValue: "Debugger"), bundle: nil)
|
||||
debuggerWindowController = debuggerStoryboard.instantiateInitialController() as! DebuggerWindowController
|
||||
debuggerWindowController.showWindow(self)
|
||||
|
||||
// Do view setup here.
|
||||
self.view.addSubview(appleScreenView)
|
||||
self.view.addSubview(computer.emulatorView)
|
||||
computer.emulatorView.display()
|
||||
|
||||
appleScreenView.wantsLayer = true
|
||||
|
||||
emuScreenLayer.delegate = appleScreenViewDelegate
|
||||
emuScreenLayer.frame = appleScreenView.bounds
|
||||
emuScreenLayer.setNeedsDisplay()
|
||||
appleScreenView.layer?.addSublayer(emuScreenLayer)
|
||||
|
||||
appleScreenViewDelegate.putCharacterPixels(charPixels: cg.getCharacterPixels(charIndex: 0x00), pixelPosition: appleScreenViewDelegate.getPixelOffset(charCellX: 0, charCellY: 0))
|
||||
appleScreenViewDelegate.putCharacterPixels(charPixels: cg.getCharacterPixels(charIndex: 0x01), pixelPosition: appleScreenViewDelegate.getPixelOffset(charCellX: 1, charCellY: 1))
|
||||
appleScreenViewDelegate.putCharacterPixels(charPixels: cg.getCharacterPixels(charIndex: 0x02), pixelPosition: appleScreenViewDelegate.getPixelOffset(charCellX: 2, charCellY: 2))
|
||||
appleScreenViewDelegate.putCharacterPixels(charPixels: cg.getCharacterPixels(charIndex: 0x03), pixelPosition: appleScreenViewDelegate.getPixelOffset(charCellX: 3, charCellY: 3))
|
||||
appleScreenViewDelegate.putCharacterPixels(charPixels: cg.getCharacterPixels(charIndex: 0x04), pixelPosition: appleScreenViewDelegate.getPixelOffset(charCellX: 4, charCellY: 4))
|
||||
appleScreenViewDelegate.putCharacterPixels(charPixels: cg.getCharacterPixels(charIndex: 0x05), pixelPosition: appleScreenViewDelegate.getPixelOffset(charCellX: 5, charCellY: 5))
|
||||
appleScreenViewDelegate.putCharacterPixels(charPixels: cg.getCharacterPixels(charIndex: 0x06), pixelPosition: appleScreenViewDelegate.getPixelOffset(charCellX: 6, charCellY: 6))
|
||||
appleScreenViewDelegate.putCharacterPixels(charPixels: cg.getCharacterPixels(charIndex: 0x07), pixelPosition: appleScreenViewDelegate.getPixelOffset(charCellX: 7, charCellY: 7))
|
||||
appleScreenViewDelegate.putCharacterPixels(charPixels: cg.getCharacterPixels(charIndex: 0x08), pixelPosition: appleScreenViewDelegate.getPixelOffset(charCellX: 8, charCellY: 8))
|
||||
|
||||
appleScreenView.display()
|
||||
computer.runFrame()
|
||||
}
|
||||
|
||||
}
|
||||
|
8
FruitMachine/MainViewStoryboard.storyboard
Normal file
8
FruitMachine/MainViewStoryboard.storyboard
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="13168.3" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13168.3"/>
|
||||
</dependencies>
|
||||
<scenes/>
|
||||
</document>
|
Loading…
Reference in New Issue
Block a user