how hard can bolting Apple II onto this be?

This commit is contained in:
Luigi Thirty 2017-08-01 02:02:49 -04:00
parent a2b4003bb8
commit f6e0467fe1
20 changed files with 566 additions and 212 deletions

View File

@ -9,16 +9,23 @@
/* 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 */; };
2A5BC5191F29A28D008C03BE /* AppleIScreenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5BC5181F29A28D008C03BE /* AppleIScreenView.swift */; };
2A5BC51C1F29A2EB008C03BE /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A5BC51B1F29A2EB008C03BE /* QuartzCore.framework */; };
2A5BC51E1F29A4C3008C03BE /* AppleIBitmapDisplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5BC51D1F29A4C3008C03BE /* AppleIBitmapDisplay.swift */; };
2A5C5BBC1F304C3A00ED351D /* A2CharacterGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5C5BBB1F304C3A00ED351D /* A2CharacterGenerator.swift */; };
2A5C5BBE1F304D4B00ED351D /* Glyph.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5C5BBD1F304D4B00ED351D /* Glyph.swift */; };
2A60851E1F2AFAE900E05B64 /* PIA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A60851D1F2AFAE900E05B64 /* PIA.swift */; };
2A6DC7E91F3045280066FE0D /* AppleIIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A6DC7E81F3045280066FE0D /* AppleIIViewController.swift */; };
2A6DC7EB1F3045C90066FE0D /* EmulatedSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A6DC7EA1F3045C90066FE0D /* EmulatedSystem.swift */; };
2A6DC7ED1F30492C0066FE0D /* ScreenDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A6DC7EC1F30492C0066FE0D /* ScreenDelegate.swift */; };
2A6DC7F01F30495D0066FE0D /* ScreenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A6DC7EF1F30495D0066FE0D /* ScreenView.swift */; };
2A7665781F2F05F600135518 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A7665761F2F05F600135518 /* PreferencesWindowController.swift */; };
2A7665791F2F05F600135518 /* PreferencesWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2A7665771F2F05F600135518 /* PreferencesWindow.xib */; };
2A91852A1F2EA84D00A9E5BE /* BitmapPixels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A9185291F2EA84D00A9E5BE /* BitmapPixels.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 */; };
2AB6CACD1F3041A200DECAC0 /* AppleII.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AB6CACC1F3041A200DECAC0 /* AppleII.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,9 +38,9 @@
2AE42E081F2850F400C4900E /* ReadOverride.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AE42E071F2850F400C4900E /* ReadOverride.swift */; };
2AE42E0A1F28521E00C4900E /* WriteOverride.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AE42E091F28521E00C4900E /* WriteOverride.swift */; };
2AE42E0C1F28522D00C4900E /* MemoryOverride.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AE42E0B1F28522D00C4900E /* MemoryOverride.swift */; };
2AE42E3A1F28628300C4900E /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AE42E381F28628300C4900E /* MainViewController.swift */; };
2AE42E3A1F28628300C4900E /* AppleIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AE42E381F28628300C4900E /* AppleIViewController.swift */; };
2AE42E401F28638100C4900E /* FruitMachine.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2AE42E3F1F28638100C4900E /* FruitMachine.storyboard */; };
2AE42E431F28665300C4900E /* CharacterGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AE42E421F28665300C4900E /* CharacterGenerator.swift */; };
2AE42E431F28665300C4900E /* A1CharacterGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AE42E421F28665300C4900E /* A1CharacterGenerator.swift */; };
2AE5BA041F23DE4400FAA343 /* Disassembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AE5BA031F23DE4400FAA343 /* Disassembly.swift */; };
2AE5BA061F2469EB00FAA343 /* AddressConversions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AE5BA051F2469EB00FAA343 /* AddressConversions.swift */; };
/* End PBXBuildFile section */
@ -41,16 +48,23 @@
/* 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>"; };
2A5BC5181F29A28D008C03BE /* AppleIScreenView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleIScreenView.swift; sourceTree = "<group>"; };
2A5BC51B1F29A2EB008C03BE /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
2A5BC51D1F29A4C3008C03BE /* AppleIBitmapDisplay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleIBitmapDisplay.swift; sourceTree = "<group>"; };
2A5C5BBB1F304C3A00ED351D /* A2CharacterGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = A2CharacterGenerator.swift; sourceTree = "<group>"; };
2A5C5BBD1F304D4B00ED351D /* Glyph.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Glyph.swift; sourceTree = "<group>"; };
2A60851D1F2AFAE900E05B64 /* PIA.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIA.swift; sourceTree = "<group>"; };
2A6DC7E81F3045280066FE0D /* AppleIIViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleIIViewController.swift; sourceTree = "<group>"; };
2A6DC7EA1F3045C90066FE0D /* EmulatedSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmulatedSystem.swift; sourceTree = "<group>"; };
2A6DC7EC1F30492C0066FE0D /* ScreenDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenDelegate.swift; sourceTree = "<group>"; };
2A6DC7EF1F30495D0066FE0D /* ScreenView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenView.swift; sourceTree = "<group>"; };
2A7665761F2F05F600135518 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = "<group>"; };
2A7665771F2F05F600135518 /* PreferencesWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PreferencesWindow.xib; sourceTree = "<group>"; };
2A9185291F2EA84D00A9E5BE /* BitmapPixels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BitmapPixels.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>"; };
2AB6CACC1F3041A200DECAC0 /* AppleII.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleII.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>"; };
@ -66,9 +80,9 @@
2AE42E071F2850F400C4900E /* ReadOverride.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadOverride.swift; sourceTree = "<group>"; };
2AE42E091F28521E00C4900E /* WriteOverride.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WriteOverride.swift; sourceTree = "<group>"; };
2AE42E0B1F28522D00C4900E /* MemoryOverride.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryOverride.swift; sourceTree = "<group>"; };
2AE42E381F28628300C4900E /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = "<group>"; };
2AE42E381F28628300C4900E /* AppleIViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleIViewController.swift; sourceTree = "<group>"; };
2AE42E3F1F28638100C4900E /* FruitMachine.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = FruitMachine.storyboard; sourceTree = "<group>"; };
2AE42E421F28665300C4900E /* CharacterGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CharacterGenerator.swift; sourceTree = "<group>"; };
2AE42E421F28665300C4900E /* A1CharacterGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = A1CharacterGenerator.swift; sourceTree = "<group>"; };
2AE5BA031F23DE4400FAA343 /* Disassembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Disassembly.swift; sourceTree = "<group>"; };
2AE5BA051F2469EB00FAA343 /* AddressConversions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressConversions.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -93,18 +107,64 @@
name = Frameworks;
sourceTree = "<group>";
};
2A6DC7E51F3043DB0066FE0D /* Common */ = {
isa = PBXGroup;
children = (
2A6DC7E71F3044080066FE0D /* Peripherals */,
2A6DC7E61F3043E10066FE0D /* Video */,
2A6DC7EA1F3045C90066FE0D /* EmulatedSystem.swift */,
);
path = Common;
sourceTree = "<group>";
};
2A6DC7E61F3043E10066FE0D /* Video */ = {
isa = PBXGroup;
children = (
2A9185291F2EA84D00A9E5BE /* BitmapPixels.swift */,
2A5C5BBD1F304D4B00ED351D /* Glyph.swift */,
);
path = Video;
sourceTree = "<group>";
};
2A6DC7E71F3044080066FE0D /* Peripherals */ = {
isa = PBXGroup;
children = (
2A60851D1F2AFAE900E05B64 /* PIA.swift */,
);
path = Peripherals;
sourceTree = "<group>";
};
2A6DC7EE1F30492E0066FE0D /* Video */ = {
isa = PBXGroup;
children = (
2A5C5BBB1F304C3A00ED351D /* A2CharacterGenerator.swift */,
2A6DC7EC1F30492C0066FE0D /* ScreenDelegate.swift */,
2A6DC7EF1F30495D0066FE0D /* ScreenView.swift */,
);
path = Video;
sourceTree = "<group>";
};
2AA8B5F61F2A8844002B350F /* AppleI */ = {
isa = PBXGroup;
children = (
2AE42E411F28663600C4900E /* Video */,
2A5BC5181F29A28D008C03BE /* AppleScreenView.swift */,
2A5BC5181F29A28D008C03BE /* AppleIScreenView.swift */,
2A5BC51D1F29A4C3008C03BE /* AppleIBitmapDisplay.swift */,
2AA8B5F71F2A8889002B350F /* AppleI.swift */,
2A60851D1F2AFAE900E05B64 /* PIA.swift */,
2AA8B5FD1F2A942C002B350F /* PIAOverrides.swift */,
);
path = AppleI;
sourceTree = "<group>";
};
2AB6CAC71F30407200DECAC0 /* AppleII */ = {
isa = PBXGroup;
children = (
2A6DC7EE1F30492E0066FE0D /* Video */,
2AB6CACC1F3041A200DECAC0 /* AppleII.swift */,
);
path = AppleII;
sourceTree = "<group>";
};
2AD458C11F205EB700F05121 = {
isa = PBXGroup;
children = (
@ -125,10 +185,13 @@
2AD458CC1F205EB700F05121 /* FruitMachine */ = {
isa = PBXGroup;
children = (
2A6DC7E51F3043DB0066FE0D /* Common */,
2AA8B5F61F2A8844002B350F /* AppleI */,
2AB6CAC71F30407200DECAC0 /* AppleII */,
2AD458DD1F205F0D00F05121 /* M6502 */,
2AD458CD1F205EB700F05121 /* AppDelegate.swift */,
2AE42E381F28628300C4900E /* MainViewController.swift */,
2AE42E381F28628300C4900E /* AppleIViewController.swift */,
2A6DC7E81F3045280066FE0D /* AppleIIViewController.swift */,
2AE42E3F1F28638100C4900E /* FruitMachine.storyboard */,
2A7665761F2F05F600135518 /* PreferencesWindowController.swift */,
2A7665771F2F05F600135518 /* PreferencesWindow.xib */,
@ -195,9 +258,7 @@
2AE42E411F28663600C4900E /* Video */ = {
isa = PBXGroup;
children = (
2A9185291F2EA84D00A9E5BE /* BitmapPixels.swift */,
2AE42E421F28665300C4900E /* CharacterGenerator.swift */,
2AA8B5FD1F2A942C002B350F /* PIAOverrides.swift */,
2AE42E421F28665300C4900E /* A1CharacterGenerator.swift */,
2AA8B5FB1F2A8EAD002B350F /* Terminal.swift */,
);
path = Video;
@ -276,29 +337,36 @@
buildActionMask = 2147483647;
files = (
2A60851E1F2AFAE900E05B64 /* PIA.swift in Sources */,
2A5C5BBE1F304D4B00ED351D /* Glyph.swift in Sources */,
2A2126841F2A9FA300E43DC1 /* DebuggerWindowController.swift in Sources */,
2AD458E31F20661300F05121 /* CPUInstructions.swift in Sources */,
2A5C5BBC1F304C3A00ED351D /* A2CharacterGenerator.swift in Sources */,
2AD458D01F205EB700F05121 /* DebuggerViewController.swift in Sources */,
2A7665781F2F05F600135518 /* PreferencesWindowController.swift in Sources */,
2AA8B5FC1F2A8EAD002B350F /* Terminal.swift in Sources */,
2A5BC51E1F29A4C3008C03BE /* AppleIBitmapDisplay.swift in Sources */,
2AD458CE1F205EB700F05121 /* AppDelegate.swift in Sources */,
2A91852A1F2EA84D00A9E5BE /* BitmapPixels.swift in Sources */,
2AE42E431F28665300C4900E /* CharacterGenerator.swift in Sources */,
2AE42E431F28665300C4900E /* A1CharacterGenerator.swift in Sources */,
2A6DC7F01F30495D0066FE0D /* ScreenView.swift in Sources */,
2AE42E0C1F28522D00C4900E /* MemoryOverride.swift in Sources */,
2A22EBFB1F21A7A700A36A61 /* IntegerExtensions.swift in Sources */,
2AD6D5841F26E6BF008F3CF5 /* DebuggerCommands.swift in Sources */,
2AE5BA041F23DE4400FAA343 /* Disassembly.swift in Sources */,
2AE42E3A1F28628300C4900E /* MainViewController.swift in Sources */,
2A6DC7EB1F3045C90066FE0D /* EmulatedSystem.swift in Sources */,
2AE42E3A1F28628300C4900E /* AppleIViewController.swift in Sources */,
2AD458E51F2070DF00F05121 /* Opcodes.swift in Sources */,
2AE42E081F2850F400C4900E /* ReadOverride.swift in Sources */,
2AA8B5FE1F2A942C002B350F /* PIAOverrides.swift in Sources */,
2AB6CACD1F3041A200DECAC0 /* AppleII.swift in Sources */,
2AE5BA061F2469EB00FAA343 /* AddressConversions.swift in Sources */,
2AE42E0A1F28521E00C4900E /* WriteOverride.swift in Sources */,
2A5BC5191F29A28D008C03BE /* AppleScreenView.swift in Sources */,
2A6DC7ED1F30492C0066FE0D /* ScreenDelegate.swift in Sources */,
2A5BC5191F29A28D008C03BE /* AppleIScreenView.swift in Sources */,
2AD458E11F2064CB00F05121 /* MemoryInterface.swift in Sources */,
2AA8B5F81F2A8889002B350F /* AppleI.swift in Sources */,
2AD458DF1F205F4500F05121 /* CPU.swift in Sources */,
2A6DC7E91F3045280066FE0D /* AppleIIViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -8,10 +8,14 @@
import Cocoa
class AppleI: NSObject {
static let sharedInstance = AppleI()
class AppleI: NSObject, EmulatedSystem {
var CPU_FREQUENCY: Double
var FRAMES_PER_SECOND: Double
var CYCLES_PER_BATCH: Int
let cg = CharacterGenerator(romPath: "/Users/luigi/apple1/apple1.vid");
static let sharedInstance = AppleI(cpuFrequency: 1000000.0, fps: 60.0)
let cg = A1CharacterGenerator(romPath: "/Users/luigi/apple1/apple1.vid");
let terminal = Terminal()
let pia: [String:PIA] = [
@ -20,14 +24,13 @@ class AppleI: NSObject {
]
let emulatorViewDelegate = AppleIBitmapDisplay()
let emulatorView = AppleScreenView(frame: NSMakeRect(0, 0, 640, 384))
let emulatorView = AppleIScreenView(frame: NSMakeRect(0, 0, 640, 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() {
required init(cpuFrequency: Double, fps: Double) {
CPU_FREQUENCY = cpuFrequency
FRAMES_PER_SECOND = fps
CYCLES_PER_BATCH = Int(cpuFrequency / fps)
super.init()
emuScreenLayer.shouldRasterize = true
@ -35,7 +38,7 @@ class AppleI: NSObject {
emuScreenLayer.frame = emulatorView.bounds
emulatorView.wantsLayer = true
emuScreenLayer.setNeedsDisplay()
emulatorView.layer?.addSublayer(emuScreenLayer)
@ -44,7 +47,6 @@ class AppleI: NSObject {
CPU.sharedInstance.memoryInterface.loadBinary(path: "/Users/luigi/apple1/apple1.rom", offset: 0xFF00, length: 0x100)
CPU.sharedInstance.memoryInterface.loadBinary(path: "/Users/luigi/apple1/basic.bin", offset: 0xE000, length: 0x1000)
CPU.sharedInstance.performReset()
}
func installOverrides() {
@ -59,7 +61,7 @@ class AppleI: NSObject {
func runFrame() {
CPU.sharedInstance.cycles = 0
CPU.sharedInstance.cyclesInBatch = AppleI.CYCLES_PER_BATCH
CPU.sharedInstance.cyclesInBatch = AppleI.sharedInstance.CYCLES_PER_BATCH
CPU.sharedInstance.runCyclesBatch()
//update the video display

View File

@ -46,7 +46,7 @@ class AppleIBitmapDisplay: NSObject, CALayerDelegate {
//Calculate the offset to reach the desired position.
let baseOffset = scanlineOffsets[Int(pixelPosition.y)] + Int(pixelPosition.x)
for charY in 0..<CharacterGenerator.CHAR_HEIGHT {
for charY in 0..<AppleI.A1CharacterGenerator.CHAR_HEIGHT {
let offset = baseOffset + AppleIBitmapDisplay.PIXEL_WIDTH * charY
let glyphOffsetY = (charY * 8)
@ -61,7 +61,7 @@ class AppleIBitmapDisplay: NSObject, CALayerDelegate {
}
func getPixelOffset(charCellIndex: Int) -> CGPoint {
return getPixelOffset(charCellX: charCellIndex % Terminal.CELLS_WIDTH, charCellY: charCellIndex / Terminal.CELLS_WIDTH)
return getPixelOffset(charCellX: charCellIndex % AppleI.Terminal.CELLS_WIDTH, charCellY: charCellIndex / AppleI.Terminal.CELLS_WIDTH)
}
/* Draw the screen. */

View File

@ -8,7 +8,7 @@
import Cocoa
class AppleScreenView: NSView {
class AppleIScreenView: NSView {
override var acceptsFirstResponder: Bool {
return true

View File

@ -0,0 +1,62 @@
//
// Overrides.swift
// FruitMachine
//
// Created by Christopher Rohl on 7/27/17.
// Copyright © 2017 Christopher Rohl. All rights reserved.
//
import Cocoa
extension AppleI {
class PIAOverrides: NSObject {
static let writeDSP = WriteOverride(start: 0xD012, end: 0xD012, writeAnyway: false, action: PIAOverrides.actionWriteDSP)
static func actionWriteDSP(terminal: AnyObject, byte: UInt8?) -> UInt8? {
let pia = AppleI.sharedInstance.pia["display"]!
if(pia.getMode() == .Output) {
//Writing to DSP sets DSP.7
AppleI.sharedInstance.pia["display"]!.data = byte! | 0x80
//Output our character to the terminal
AppleI.sharedInstance.terminal.putCharacter(charIndex: byte!)
AppleI.sharedInstance.pia["display"]!.data = byte! & ~(0x80)
}
return nil
}
static let writeDSPCR = WriteOverride(start: 0xD013, end: 0xD013, writeAnyway: false, action: PIAOverrides.actionWriteDSPCR)
static func actionWriteDSPCR(terminal: AnyObject, byte: UInt8?) -> UInt8? {
AppleI.sharedInstance.pia["display"]?.control = byte!
return nil
}
static let readDSP = ReadOverride(start: 0xD012, end: 0xD012, readAnyway: false, action: PIAOverrides.actionReadDSP)
static func actionReadDSP(terminal: AnyObject, byte: UInt8?) -> UInt8? {
//DSP.7 is unset when the character is accepted by the terminal
return AppleI.sharedInstance.pia["display"]!.data
}
/* */
static let readKBDCR = ReadOverride(start: 0xD011, end: 0xD011, readAnyway: false, action: PIAOverrides.actionReadKBDCR)
static func actionReadKBDCR(terminal: AnyObject, byte: UInt8?) -> UInt8? {
return AppleI.sharedInstance.pia["keyboard"]!.control
}
/* */
static let readKBD = ReadOverride(start: 0xD010, end: 0xD010, readAnyway: false, action: PIAOverrides.actionReadKBD)
static func actionReadKBD(terminal: AnyObject, byte: UInt8?) -> UInt8? {
//Reading KBD clears KBDCR.7
AppleI.sharedInstance.pia["keyboard"]!.control = AppleI.sharedInstance.pia["keyboard"]!.control & ~(0x80)
//KBD.7 is tied to +5V
return AppleI.sharedInstance.pia["keyboard"]!.data | 0x80
}
}
}

View File

@ -0,0 +1,71 @@
//
// CharacterGenerator.swift
// FruitMachine
//
// Created by Christopher Rohl on 7/26/17.
// Copyright © 2017 Christopher Rohl. All rights reserved.
//
import Cocoa
//The character generator ROM contains 64 8x5 glyphs.
extension AppleI {
class A1CharacterGenerator: NSObject {
static let CHAR_WIDTH = 5
static let CHAR_HEIGHT = 8
var ROM: [UInt8]
var glyphs: [Glyph]
init(romPath: String) {
ROM = [UInt8](repeating: 0xCC, count: 512)
glyphs = [Glyph](repeating: Glyph(inPixels: [BitmapPixelsBE555.PixelData]()), count: 64)
super.init()
loadROM(path: romPath)
for index in 0..<64 {
glyphs[index] = Glyph(inPixels: getCharacterPixels(charIndex: UInt8(index)))
}
}
private func loadROM(path: String) {
do {
let fileContent: NSData = try NSData(contentsOfFile: path)
fileContent.getBytes(&ROM, range: NSRange(location: 0, length: 512))
} catch {
print(error)
}
}
private func getCharacterPixels(charIndex: UInt8) -> [BitmapPixelsBE555.PixelData] {
var pixelArray = [UInt8](repeating: 0x00, count: A1CharacterGenerator.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.
*/
//Don't convert the character indexes if we're populating the glyphs array.
for scanlineIndex in 0..<A1CharacterGenerator.CHAR_HEIGHT {
pixelArray[scanlineIndex] = ROM[scanlineIndex + (Int(charIndex) * A1CharacterGenerator.CHAR_HEIGHT)]
}
var glyphPixels = [BitmapPixelsBE555.PixelData]()
for charY in 0..<A1CharacterGenerator.CHAR_HEIGHT {
for charX in 0..<8 {
glyphPixels.append(pixelArray[Int(charY)] & (1 << charX) > 0 ? BitmapPixelsBE555.White : BitmapPixelsBE555.Black)
}
}
return glyphPixels
}
func asciiToAppleCharIndex(ascii: UInt8) -> UInt8 {
return (ascii & 0x1f) | (((ascii ^ 0x40) & 0x40) >> 1)
}
}
}

View File

@ -1,75 +0,0 @@
//
// CharacterGenerator.swift
// FruitMachine
//
// Created by Christopher Rohl on 7/26/17.
// Copyright © 2017 Christopher Rohl. All rights reserved.
//
import Cocoa
//The character generator ROM contains 64 8x5 glyphs.
struct Glyph {
var pixels: [BitmapPixelsBE555.PixelData] = [BitmapPixelsBE555.PixelData]()
init(inPixels: [BitmapPixelsBE555.PixelData]) {
pixels = inPixels
}
}
class CharacterGenerator: NSObject {
static let CHAR_WIDTH = 5
static let CHAR_HEIGHT = 8
var ROM: [UInt8]
var glyphs: [Glyph]
init(romPath: String) {
ROM = [UInt8](repeating: 0xCC, count: 512)
glyphs = [Glyph](repeating: Glyph(inPixels: [BitmapPixelsBE555.PixelData]()), count: 64)
super.init()
loadROM(path: romPath)
for index in 0..<64 {
glyphs[index] = Glyph(inPixels: getCharacterPixels(charIndex: UInt8(index)))
}
}
private func loadROM(path: String) {
do {
let fileContent: NSData = try NSData(contentsOfFile: path)
fileContent.getBytes(&ROM, range: NSRange(location: 0, length: 512))
} catch {
print(error)
}
}
private func getCharacterPixels(charIndex: UInt8) -> [BitmapPixelsBE555.PixelData] {
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.
*/
//Don't convert the character indexes if we're populating the glyphs array.
for scanlineIndex in 0..<CharacterGenerator.CHAR_HEIGHT {
pixelArray[scanlineIndex] = ROM[scanlineIndex + (Int(charIndex) * CharacterGenerator.CHAR_HEIGHT)]
}
var glyphPixels = [BitmapPixelsBE555.PixelData]()
for charY in 0..<CharacterGenerator.CHAR_HEIGHT {
for charX in 0..<8 {
glyphPixels.append(pixelArray[Int(charY)] & (1 << charX) > 0 ? BitmapPixelsBE555.White : BitmapPixelsBE555.Black)
}
}
return glyphPixels
}
func asciiToAppleCharIndex(ascii: UInt8) -> UInt8 {
return (ascii & 0x1f) | (((ascii ^ 0x40) & 0x40) >> 1)
}
}

View File

@ -1,58 +0,0 @@
//
// 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, writeAnyway: false, action: PIAOverrides.actionWriteDSP)
static func actionWriteDSP(terminal: AnyObject, byte: UInt8?) -> UInt8? {
let pia = AppleI.sharedInstance.pia["display"]!
if(pia.getMode() == .Output) {
//Writing to DSP sets DSP.7
AppleI.sharedInstance.pia["display"]!.data = byte! | 0x80
//Output our character to the terminal
AppleI.sharedInstance.terminal.putCharacter(charIndex: byte!)
AppleI.sharedInstance.pia["display"]!.data = byte! & ~(0x80)
}
return nil
}
static let writeDSPCR = WriteOverride(start: 0xD013, end: 0xD013, writeAnyway: false, action: PIAOverrides.actionWriteDSPCR)
static func actionWriteDSPCR(terminal: AnyObject, byte: UInt8?) -> UInt8? {
AppleI.sharedInstance.pia["display"]?.control = byte!
return nil
}
static let readDSP = ReadOverride(start: 0xD012, end: 0xD012, readAnyway: false, action: PIAOverrides.actionReadDSP)
static func actionReadDSP(terminal: AnyObject, byte: UInt8?) -> UInt8? {
//DSP.7 is unset when the character is accepted by the terminal
return AppleI.sharedInstance.pia["display"]!.data
}
/* */
static let readKBDCR = ReadOverride(start: 0xD011, end: 0xD011, readAnyway: false, action: PIAOverrides.actionReadKBDCR)
static func actionReadKBDCR(terminal: AnyObject, byte: UInt8?) -> UInt8? {
return AppleI.sharedInstance.pia["keyboard"]!.control
}
/* */
static let readKBD = ReadOverride(start: 0xD010, end: 0xD010, readAnyway: false, action: PIAOverrides.actionReadKBD)
static func actionReadKBD(terminal: AnyObject, byte: UInt8?) -> UInt8? {
//Reading KBD clears KBDCR.7
AppleI.sharedInstance.pia["keyboard"]!.control = AppleI.sharedInstance.pia["keyboard"]!.control & ~(0x80)
//KBD.7 is tied to +5V
return AppleI.sharedInstance.pia["keyboard"]!.data | 0x80
}
}

View File

@ -8,42 +8,54 @@
import Cocoa
struct Cell {
var x: Int
var y: Int
}
extension AppleI {
class Terminal: NSObject {
static let CELLS_WIDTH = 40
static let CELLS_HEIGHT = 24
static let CELLS_COUNT = CELLS_WIDTH * CELLS_HEIGHT
var cursorPosition: Cell
var characters: [UInt8]
override init() {
cursorPosition = Cell(x: 0, y: 0)
characters = [UInt8](repeating: 0x00, count: Terminal.CELLS_WIDTH * Terminal.CELLS_HEIGHT)
struct Cell {
var x: Int
var y: Int
}
func cellToIndex(cell: Cell) -> Int {
return (cell.y * Terminal.CELLS_WIDTH) + (cell.x % Terminal.CELLS_WIDTH)
}
func putCharacter(charIndex: UInt8) {
if(charIndex == 0x8D)
{
carriageReturn()
class Terminal: NSObject {
static let CELLS_WIDTH = 40
static let CELLS_HEIGHT = 24
static let CELLS_COUNT = CELLS_WIDTH * CELLS_HEIGHT
var cursorPosition: Cell
var characters: [UInt8]
override init() {
cursorPosition = Cell(x: 0, y: 0)
characters = [UInt8](repeating: 0x00, count: Terminal.CELLS_WIDTH * Terminal.CELLS_HEIGHT)
}
else {
characters[cellToIndex(cell: cursorPosition)] = charIndex
advanceCursor()
func cellToIndex(cell: Cell) -> Int {
return (cell.y * Terminal.CELLS_WIDTH) + (cell.x % Terminal.CELLS_WIDTH)
}
}
func advanceCursor() {
cursorPosition.x += 1
if(cursorPosition.x == Terminal.CELLS_WIDTH) {
func putCharacter(charIndex: UInt8) {
if(charIndex == 0x8D)
{
carriageReturn()
}
else {
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 = Terminal.CELLS_HEIGHT - 1
scrollUp(lines: 1)
}
}
}
func carriageReturn() {
cursorPosition.x = 0
cursorPosition.y += 1
if(cursorPosition.y >= Terminal.CELLS_HEIGHT) {
@ -51,20 +63,12 @@ class Terminal: NSObject {
scrollUp(lines: 1)
}
}
}
func carriageReturn() {
cursorPosition.x = 0
cursorPosition.y += 1
if(cursorPosition.y >= Terminal.CELLS_HEIGHT) {
cursorPosition.y = Terminal.CELLS_HEIGHT - 1
scrollUp(lines: 1)
func scrollUp(lines: Int) {
let scrolled = characters[Terminal.CELLS_WIDTH ..< Terminal.CELLS_COUNT]
characters = [UInt8](scrolled)
characters.append(contentsOf: [UInt8](repeating: 0x00, count: Terminal.CELLS_WIDTH))
}
}
func scrollUp(lines: Int) {
let scrolled = characters[Terminal.CELLS_WIDTH ..< Terminal.CELLS_COUNT]
characters = [UInt8](scrolled)
characters.append(contentsOf: [UInt8](repeating: 0x00, count: Terminal.CELLS_WIDTH))
}
}

View File

@ -0,0 +1,67 @@
//
// AppleII.swift
// FruitMachine
//
// Created by Christopher Rohl on 8/1/17.
// Copyright © 2017 Christopher Rohl. All rights reserved.
//
import Cocoa
class AppleII: NSObject, EmulatedSystem {
static let sharedInstance = AppleII(cpuFrequency: (14.31818 / 7 / 2) * 1000000, fps: 60.0)
var CPU_FREQUENCY: Double
var FRAMES_PER_SECOND: Double
var CYCLES_PER_BATCH: Int
let emulatorViewDelegate = AppleII.ScreenDelegate()
let emulatorView = AppleII.ScreenView(frame: NSMakeRect(0, 0, 640, 384))
let emuScreenLayer = CALayer()
required init(cpuFrequency: Double, fps: Double) {
CPU_FREQUENCY = cpuFrequency
FRAMES_PER_SECOND = fps
CYCLES_PER_BATCH = Int(cpuFrequency / fps)
super.init()
loadROMs()
/*
emuScreenLayer.shouldRasterize = true
emuScreenLayer.delegate = emulatorViewDelegate
emuScreenLayer.frame = emulatorView.bounds
emulatorView.wantsLayer = true
emuScreenLayer.setNeedsDisplay()
emulatorView.layer?.addSublayer(emuScreenLayer)
installOverrides()
CPU.sharedInstance.memoryInterface.loadBinary(path: "/Users/luigi/apple1/apple1.rom", offset: 0xFF00, length: 0x100)
CPU.sharedInstance.memoryInterface.loadBinary(path: "/Users/luigi/apple1/basic.bin", offset: 0xE000, length: 0x1000)
CPU.sharedInstance.performReset()
*/
}
func loadROMs() {
CPU.sharedInstance.memoryInterface.loadBinary(path: "/Users/luigi/apple2/341-0001-00.e0", offset: 0xE000, length: 0x800)
CPU.sharedInstance.memoryInterface.loadBinary(path: "/Users/luigi/apple2/341-0002-00.e8", offset: 0xE800, length: 0x800)
CPU.sharedInstance.memoryInterface.loadBinary(path: "/Users/luigi/apple2/341-0003-00.f0", offset: 0xF000, length: 0x800)
CPU.sharedInstance.memoryInterface.loadBinary(path: "/Users/luigi/apple2/341-0004-00.f8", offset: 0xF800, length: 0x800)
}
func installOverrides() {
//TODO
}
func runFrame() {
CPU.sharedInstance.cycles = 0
CPU.sharedInstance.cyclesInBatch = CYCLES_PER_BATCH
CPU.sharedInstance.runCyclesBatch()
//TODO
}
}

View File

@ -0,0 +1,70 @@
//
// CharacterGenerator.swift
// FruitMachine
//
// Created by Christopher Rohl on 8/1/17.
// Copyright © 2017 Christopher Rohl. All rights reserved.
//
import Cocoa
extension AppleII {
//The Apple II character generator is a clone of the Signetics 2513 from the Apple I.
class A2CharacterGenerator: NSObject {
static let CHAR_WIDTH = 5
static let CHAR_HEIGHT = 8
var ROM: [UInt8]
var glyphs: [Glyph]
init(romPath: String) {
ROM = [UInt8](repeating: 0xCC, count: 0x800)
glyphs = [Glyph](repeating: Glyph(inPixels: [BitmapPixelsBE555.PixelData]()), count: 64)
super.init()
loadROM(path: romPath)
for index in 0..<64 {
glyphs[index] = Glyph(inPixels: getCharacterPixels(charIndex: UInt8(index)))
}
}
private func getCharacterPixels(charIndex: UInt8) -> [BitmapPixelsBE555.PixelData] {
var pixelArray = [UInt8](repeating: 0x00, count: A2CharacterGenerator.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.
*/
//Don't convert the character indexes if we're populating the glyphs array.
for scanlineIndex in 0..<A2CharacterGenerator.CHAR_HEIGHT {
pixelArray[scanlineIndex] = ROM[scanlineIndex + (Int(charIndex) * A2CharacterGenerator.CHAR_HEIGHT)]
}
var glyphPixels = [BitmapPixelsBE555.PixelData]()
for charY in 0..<A2CharacterGenerator.CHAR_HEIGHT {
for charX in 0..<8 {
glyphPixels.append(pixelArray[Int(charY)] & (1 << charX) > 0 ? BitmapPixelsBE555.White : BitmapPixelsBE555.Black)
}
}
return glyphPixels
}
func asciiToAppleCharIndex(ascii: UInt8) -> UInt8 {
return (ascii & 0x1f) | (((ascii ^ 0x40) & 0x40) >> 1)
}
private func loadROM(path: String) {
do {
let fileContent: NSData = try NSData(contentsOfFile: path)
fileContent.getBytes(&ROM, range: NSRange(location: 0, length: 0x800))
} catch {
print(error)
}
}
}
}

View File

@ -0,0 +1,19 @@
//
// ScreenDelegate.swift
// FruitMachine
//
// Created by Christopher Rohl on 8/1/17.
// Copyright © 2017 Christopher Rohl. All rights reserved.
//
import Cocoa
extension AppleII {
class ScreenDelegate: NSObject, CALayerDelegate {
static let PIXEL_WIDTH = 280
static let PIXEL_HEIGHT = 192
}
}

View File

@ -0,0 +1,37 @@
//
// ScreenView.swift
// FruitMachine
//
// Created by Christopher Rohl on 8/1/17.
// Copyright © 2017 Christopher Rohl. All rights reserved.
//
import Cocoa
extension AppleII {
class ScreenView: NSView {
override var acceptsFirstResponder: Bool {
return true
}
override func becomeFirstResponder() -> Bool {
return true
}
override func resignFirstResponder() -> Bool {
return true
}
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
// Drawing code here.
//layer?.sublayers![0].setNeedsDisplay()
//layer?.sublayers![0].display()
}
}
}

View File

@ -0,0 +1,52 @@
//
// AppleIIViewController.swift
// FruitMachine
//
// Created by Christopher Rohl on 8/1/17.
// Copyright © 2017 Christopher Rohl. All rights reserved.
//
import Cocoa
class AppleIIViewController: NSViewController {
let computer = AppleII.sharedInstance
var debuggerWindowController: DebuggerWindowController!
var preferencesWindowController: PreferencesWindowController!
var isPaused = false
var frameTimer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
preferencesWindowController = PreferencesWindowController()
self.view.addSubview(computer.emulatorView)
self.frameTimer = Timer.scheduledTimer(timeInterval: 1/60,
target: self,
selector: #selector(runEmulation),
userInfo: nil,
repeats: true)
}
@objc func runEmulation() {
computer.runFrame()
if(!CPU.sharedInstance.isRunning) {
self.frameTimer?.invalidate()
}
}
@IBAction func showDebugger(_ sender: Any) {
let debuggerStoryboard = NSStoryboard(name: NSStoryboard.Name(rawValue: "Debugger"), bundle: nil)
debuggerWindowController = debuggerStoryboard.instantiateInitialController() as! DebuggerWindowController
debuggerWindowController.showWindow(self)
}
@IBAction func showPreferences(_ sender: Any) {
preferencesWindowController.loadWindow()
}
}

View File

@ -9,7 +9,7 @@
import Cocoa
import CoreGraphics
class MainViewController: NSViewController {
class AppleIViewController: NSViewController {
let computer = AppleI.sharedInstance
var debuggerWindowController: DebuggerWindowController!

View File

@ -0,0 +1,18 @@
//
// EmulatedSystem.swift
// FruitMachine
//
// Created by Christopher Rohl on 8/1/17.
// Copyright © 2017 Christopher Rohl. All rights reserved.
//
import Cocoa
protocol EmulatedSystem {
var CPU_FREQUENCY: Double { get }
var FRAMES_PER_SECOND: Double { get }
var CYCLES_PER_BATCH: Int { get }
init(cpuFrequency: Double, fps: Double)
func installOverrides()
}

View File

@ -0,0 +1,17 @@
//
// Glyph.swift
// FruitMachine
//
// Created by Christopher Rohl on 8/1/17.
// Copyright © 2017 Christopher Rohl. All rights reserved.
//
import Cocoa
struct Glyph {
var pixels: [BitmapPixelsBE555.PixelData] = [BitmapPixelsBE555.PixelData]()
init(inPixels: [BitmapPixelsBE555.PixelData]) {
pixels = inPixels
}
}

View File

@ -224,10 +224,10 @@
</objects>
<point key="canvasLocation" x="97" y="-661"/>
</scene>
<!--Main View Controller-->
<!--AppleI View Controller-->
<scene sceneID="AMw-8X-nlH">
<objects>
<viewController id="gw1-M7-9Je" customClass="MainViewController" customModule="FruitMachine" customModuleProvider="target" sceneMemberID="viewController">
<viewController id="gw1-M7-9Je" customClass="AppleIViewController" customModule="FruitMachine" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="DiY-xb-Usb">
<rect key="frame" x="0.0" y="0.0" width="640" height="384"/>
<autoresizingMask key="autoresizingMask"/>