graphics... sort of

This commit is contained in:
Luigi Thirty 2017-07-27 19:22:13 -04:00
parent dd8301ccab
commit 0c75e6f75e
18 changed files with 316 additions and 76 deletions

View File

@ -7,10 +7,14 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* 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 */; }; 2A22EBFB1F21A7A700A36A61 /* IntegerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A22EBFA1F21A7A700A36A61 /* IntegerExtensions.swift */; };
2A5BC5191F29A28D008C03BE /* AppleScreenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5BC5181F29A28D008C03BE /* AppleScreenView.swift */; }; 2A5BC5191F29A28D008C03BE /* AppleScreenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5BC5181F29A28D008C03BE /* AppleScreenView.swift */; };
2A5BC51C1F29A2EB008C03BE /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A5BC51B1F29A2EB008C03BE /* QuartzCore.framework */; }; 2A5BC51C1F29A2EB008C03BE /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A5BC51B1F29A2EB008C03BE /* QuartzCore.framework */; };
2A5BC51E1F29A4C3008C03BE /* AppleScreenViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5BC51D1F29A4C3008C03BE /* AppleScreenViewDelegate.swift */; }; 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 */; }; 2AD458CE1F205EB700F05121 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD458CD1F205EB700F05121 /* AppDelegate.swift */; };
2AD458D01F205EB700F05121 /* DebuggerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD458CF1F205EB700F05121 /* DebuggerViewController.swift */; }; 2AD458D01F205EB700F05121 /* DebuggerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD458CF1F205EB700F05121 /* DebuggerViewController.swift */; };
2AD458D21F205EB700F05121 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2AD458D11F205EB700F05121 /* Assets.xcassets */; }; 2AD458D21F205EB700F05121 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2AD458D11F205EB700F05121 /* Assets.xcassets */; };
@ -31,10 +35,14 @@
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference 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>"; }; 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 /* 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; }; 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>"; }; 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; }; 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>"; }; 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>"; }; 2AD458CF1F205EB700F05121 /* DebuggerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebuggerViewController.swift; sourceTree = "<group>"; };
@ -77,6 +85,17 @@
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
2AA8B5F61F2A8844002B350F /* AppleI */ = {
isa = PBXGroup;
children = (
2AE42E411F28663600C4900E /* Video */,
2A5BC5181F29A28D008C03BE /* AppleScreenView.swift */,
2A5BC51D1F29A4C3008C03BE /* AppleScreenViewDelegate.swift */,
2AA8B5F71F2A8889002B350F /* AppleI.swift */,
);
path = AppleI;
sourceTree = "<group>";
};
2AD458C11F205EB700F05121 = { 2AD458C11F205EB700F05121 = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -97,11 +116,9 @@
2AD458CC1F205EB700F05121 /* FruitMachine */ = { 2AD458CC1F205EB700F05121 /* FruitMachine */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
2AA8B5F61F2A8844002B350F /* AppleI */,
2AD458DD1F205F0D00F05121 /* M6502 */, 2AD458DD1F205F0D00F05121 /* M6502 */,
2AE42E411F28663600C4900E /* Video */,
2AD458CD1F205EB700F05121 /* AppDelegate.swift */, 2AD458CD1F205EB700F05121 /* AppDelegate.swift */,
2A5BC5181F29A28D008C03BE /* AppleScreenView.swift */,
2A5BC51D1F29A4C3008C03BE /* AppleScreenViewDelegate.swift */,
2AE42E381F28628300C4900E /* MainViewController.swift */, 2AE42E381F28628300C4900E /* MainViewController.swift */,
2AE42E3F1F28638100C4900E /* FruitMachine.storyboard */, 2AE42E3F1F28638100C4900E /* FruitMachine.storyboard */,
2AD458D11F205EB700F05121 /* Assets.xcassets */, 2AD458D11F205EB700F05121 /* Assets.xcassets */,
@ -139,6 +156,7 @@
children = ( children = (
2AD458D31F205EB700F05121 /* Debugger.storyboard */, 2AD458D31F205EB700F05121 /* Debugger.storyboard */,
2AD458CF1F205EB700F05121 /* DebuggerViewController.swift */, 2AD458CF1F205EB700F05121 /* DebuggerViewController.swift */,
2A2126831F2A9FA300E43DC1 /* DebuggerWindowController.swift */,
2AD6D5831F26E6BF008F3CF5 /* DebuggerCommands.swift */, 2AD6D5831F26E6BF008F3CF5 /* DebuggerCommands.swift */,
2AE5BA031F23DE4400FAA343 /* Disassembly.swift */, 2AE5BA031F23DE4400FAA343 /* Disassembly.swift */,
); );
@ -167,6 +185,8 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
2AE42E421F28665300C4900E /* CharacterGenerator.swift */, 2AE42E421F28665300C4900E /* CharacterGenerator.swift */,
2AA8B5FB1F2A8EAD002B350F /* Terminal.swift */,
2AA8B5FD1F2A942C002B350F /* PIAOverrides.swift */,
); );
path = Video; path = Video;
sourceTree = "<group>"; sourceTree = "<group>";
@ -242,8 +262,10 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
2A2126841F2A9FA300E43DC1 /* DebuggerWindowController.swift in Sources */,
2AD458E31F20661300F05121 /* CPUInstructions.swift in Sources */, 2AD458E31F20661300F05121 /* CPUInstructions.swift in Sources */,
2AD458D01F205EB700F05121 /* DebuggerViewController.swift in Sources */, 2AD458D01F205EB700F05121 /* DebuggerViewController.swift in Sources */,
2AA8B5FC1F2A8EAD002B350F /* Terminal.swift in Sources */,
2A5BC51E1F29A4C3008C03BE /* AppleScreenViewDelegate.swift in Sources */, 2A5BC51E1F29A4C3008C03BE /* AppleScreenViewDelegate.swift in Sources */,
2AD458CE1F205EB700F05121 /* AppDelegate.swift in Sources */, 2AD458CE1F205EB700F05121 /* AppDelegate.swift in Sources */,
2AE42E431F28665300C4900E /* CharacterGenerator.swift in Sources */, 2AE42E431F28665300C4900E /* CharacterGenerator.swift in Sources */,
@ -254,10 +276,12 @@
2AE42E3A1F28628300C4900E /* MainViewController.swift in Sources */, 2AE42E3A1F28628300C4900E /* MainViewController.swift in Sources */,
2AD458E51F2070DF00F05121 /* Opcodes.swift in Sources */, 2AD458E51F2070DF00F05121 /* Opcodes.swift in Sources */,
2AE42E081F2850F400C4900E /* ReadOverride.swift in Sources */, 2AE42E081F2850F400C4900E /* ReadOverride.swift in Sources */,
2AA8B5FE1F2A942C002B350F /* PIAOverrides.swift in Sources */,
2AE5BA061F2469EB00FAA343 /* AddressConversions.swift in Sources */, 2AE5BA061F2469EB00FAA343 /* AddressConversions.swift in Sources */,
2AE42E0A1F28521E00C4900E /* WriteOverride.swift in Sources */, 2AE42E0A1F28521E00C4900E /* WriteOverride.swift in Sources */,
2A5BC5191F29A28D008C03BE /* AppleScreenView.swift in Sources */, 2A5BC5191F29A28D008C03BE /* AppleScreenView.swift in Sources */,
2AD458E11F2064CB00F05121 /* MemoryInterface.swift in Sources */, 2AD458E11F2064CB00F05121 /* MemoryInterface.swift in Sources */,
2AA8B5F81F2A8889002B350F /* AppleI.swift in Sources */,
2AD458DF1F205F4500F05121 /* CPU.swift in Sources */, 2AD458DF1F205F4500F05121 /* CPU.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;

View File

@ -10,6 +10,7 @@ import Cocoa
@NSApplicationMain @NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate { class AppDelegate: NSObject, NSApplicationDelegate {
let AppleScreenNotifications = Notification.Name("com.luigithirty.appleScreenNotifications")
func applicationDidFinishLaunching(_ aNotification: Notification) { func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application // Insert code here to initialize your application
@ -19,5 +20,12 @@ class AppDelegate: NSObject, NSApplicationDelegate {
// Insert code here to tear down your application // 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]))
}
} }

View 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()
}
}

View File

@ -10,10 +10,6 @@ import Cocoa
class AppleScreenView: NSView { class AppleScreenView: NSView {
//In characters
static let TERMINAL_WIDTH = 40
static let TERMINAL_HEIGHT = 24
override func draw(_ dirtyRect: NSRect) { override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect) super.draw(dirtyRect)
// Drawing code here. // Drawing code here.

View File

@ -52,7 +52,7 @@ class AppleScreenViewDelegate: NSObject, CALayerDelegate {
for charY in 0..<CharacterGenerator.CHAR_HEIGHT { for charY in 0..<CharacterGenerator.CHAR_HEIGHT {
for charX in 0..<CharacterGenerator.CHAR_WIDTH { 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) 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. */ /* Draw the screen. */
func draw(_ layer: CALayer, in ctx: CGContext) { func draw(_ layer: CALayer, in ctx: CGContext) {
let bounds = layer.bounds 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 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) 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) ctx.draw(renderedImage!, in: bounds)
//draw stuff here
} }
} }

View File

@ -35,8 +35,26 @@ class CharacterGenerator: NSObject {
func getCharacterPixels(charIndex: UInt8) -> [UInt8] { func getCharacterPixels(charIndex: UInt8) -> [UInt8] {
var pixelArray = [UInt8](repeating: 0x00, count: CharacterGenerator.CHAR_HEIGHT) 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 { 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 return pixelArray

View 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
}
}

View 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
}
}
}
}

View File

@ -629,6 +629,17 @@
<action selector="toggleFullScreen:" target="Ady-hI-5gd" id="dU3-MA-1Rq"/> <action selector="toggleFullScreen:" target="Ady-hI-5gd" id="dU3-MA-1Rq"/>
</connections> </connections>
</menuItem> </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> </items>
</menu> </menu>
</menuItem> </menuItem>

View File

@ -116,7 +116,9 @@ class CPU: NSObject {
let RESET_VECTOR: UInt16 = 0xFFFC let RESET_VECTOR: UInt16 = 0xFFFC
let IRQ_VECTOR: UInt16 = 0xFFFE let IRQ_VECTOR: UInt16 = 0xFFFE
static var sharedInstance = CPU() static let sharedInstance = CPU()
var isRunning: Bool
var cycles: Int var cycles: Int
var cyclesInBatch: Int var cyclesInBatch: Int
@ -138,6 +140,8 @@ class CPU: NSObject {
var breakpoints: [UInt16] var breakpoints: [UInt16]
override init() { override init() {
isRunning = false
cycles = 0 cycles = 0
cyclesInBatch = 0 cyclesInBatch = 0
@ -216,7 +220,7 @@ class CPU: NSObject {
} }
} }
func checkOutOfCycles() -> Bool { func outOfCycles() -> Bool {
if(cycles > cyclesInBatch) { if(cycles > cyclesInBatch) {
return true return true
} else { } else {
@ -238,4 +242,28 @@ class CPU: NSObject {
status_register.zero = (value == 0) 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
}
}
}
} }

View File

@ -9,7 +9,7 @@
<!--Window Controller--> <!--Window Controller-->
<scene sceneID="R2V-B0-nI4"> <scene sceneID="R2V-B0-nI4">
<objects> <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"> <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"/> <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/> <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>

View File

@ -46,7 +46,7 @@ class DebuggerViewController: NSViewController {
text_CPU_SR.stringValue = String(format:"%02X", cpuInstance.stack_pointer) text_CPU_SR.stringValue = String(format:"%02X", cpuInstance.stack_pointer)
text_CPU_Flags.stringValue = String(cpuInstance.status_register.asString()) 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() highlightCurrentInstruction()
} }
@ -56,15 +56,10 @@ class DebuggerViewController: NSViewController {
debuggerTableView.delegate = self debuggerTableView.delegate = self
debuggerTableView.dataSource = 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() updateCPUStatusFields()
disassembly = cpuInstance.disassemble(fromAddress: 0, length: 65535) disassembly = cpuInstance.disassemble(fromAddress: CPU.sharedInstance.program_counter, length: 256)
debuggerTableView.reloadData() debuggerTableView.reloadData()
cpuInstance.breakpoints.append(0x34E8) //failing at $34FD SBC test
// Do any additional setup after loading the view. // Do any additional setup after loading the view.
} }
@ -74,25 +69,14 @@ class DebuggerViewController: NSViewController {
} }
} }
func cpuStep() { func debugRun() {
do {
try cpuInstance.executeNextInstruction()
} catch CPUExceptions.invalidInstruction {
isRunning = false
} catch {
print(error)
}
}
func cpuRun() {
isRunning = true isRunning = true
cpuInstance.cycles = 0 cpuInstance.cycles = 0
cpuInstance.cyclesInBatch = 1000000 cpuInstance.cyclesInBatch = 1000000
while(!cpuInstance.checkOutOfCycles() && isRunning) { while(!cpuInstance.outOfCycles() && isRunning) {
cpuStep() cpuInstance.cpuStep()
if (cpuInstance.breakpoints.contains(cpuInstance.program_counter)) { if (cpuInstance.breakpoints.contains(cpuInstance.program_counter)) {
isRunning = false isRunning = false
@ -105,12 +89,12 @@ class DebuggerViewController: NSViewController {
func queueCPUStep(queue: DispatchQueue) { func queueCPUStep(queue: DispatchQueue) {
queue.async { queue.async {
self.cpuStep() self.cpuInstance.cpuStep()
} }
} }
@IBAction func btn_CPUStep(_ sender: Any) { @IBAction func btn_CPUStep(_ sender: Any) {
cpuStep() cpuInstance.cpuStep()
updateCPUStatusFields() updateCPUStatusFields()
} }
@ -120,7 +104,7 @@ class DebuggerViewController: NSViewController {
} }
@IBAction func btn_CPURun(_ sender: Any) { @IBAction func btn_CPURun(_ sender: Any) {
cpuRun() debugRun()
} }
@IBAction func btn_CPU_Restart(_ sender: Any) { @IBAction func btn_CPU_Restart(_ sender: Any) {
@ -155,6 +139,11 @@ extension DebuggerViewController: NSTableViewDelegate {
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
var cellText: String = "" var cellText: String = ""
var cellIdentifier: String = "" var cellIdentifier: String = ""
if(row >= disassembly.count) {
return nil //no cell
}
let operation = disassembly[row] let operation = disassembly[row]
if(tableColumn == tableView.tableColumns[0]) { if(tableColumn == tableView.tableColumns[0]) {

View 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.
}
}

View File

@ -74,7 +74,7 @@ extension CPU {
var disassembly: [Disassembly] = [Disassembly]() var disassembly: [Disassembly] = [Disassembly]()
var currentAddress: UInt16 = fromAddress var currentAddress: UInt16 = fromAddress
let endAddress: UInt16 = fromAddress + length let endAddress: UInt16 = max(fromAddress &+ length, 0xFFFF)
while(currentAddress < endAddress) { while(currentAddress < endAddress) {
let instruction = memoryInterface.readByte(offset: currentAddress) let instruction = memoryInterface.readByte(offset: currentAddress)

View File

@ -21,10 +21,13 @@ class MemoryInterface: NSObject {
write_overrides = [WriteOverride]() write_overrides = [WriteOverride]()
} }
func readByte(offset: UInt16) -> UInt8 { func readByte(offset: UInt16, bypassOverrides: Bool = false) -> UInt8 {
for override in read_overrides {
if case override.rangeStart ... override.rangeEnd = offset { if(!bypassOverrides) {
override.action(CPU.sharedInstance, nil) 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)] return memory[Int(offset)]
} }
func writeByte(offset: UInt16, value: UInt8) { func writeByte(offset: UInt16, value: UInt8, bypassOverrides: Bool = false) {
for override in read_overrides {
if case override.rangeStart ... override.rangeEnd = offset { if(!bypassOverrides) {
override.action(CPU.sharedInstance, value) 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) return (UInt16(high) << 8) | UInt16(low)
} }
func loadBinary(path: String) { func loadBinary(path: String, offset: UInt16) {
do { do {
let fileContent: NSData = try NSData(contentsOfFile: path) 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 { } catch {
print(error) print(error)
} }

View File

@ -11,12 +11,14 @@ import Cocoa
class MemoryOverride: NSObject { class MemoryOverride: NSObject {
let rangeStart: UInt16 let rangeStart: UInt16
let rangeEnd: UInt16 let rangeEnd: UInt16
let writeValue: Bool
let action: (CPU, UInt8?) -> Void 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 rangeStart = start
rangeEnd = end rangeEnd = end
self.writeValue = writeValue
self.action = action self.action = action
} }

View File

@ -10,36 +10,23 @@ import Cocoa
import CoreGraphics import CoreGraphics
class MainViewController: NSViewController { class MainViewController: NSViewController {
var windowController: NSWindowController?
var debuggerViewController = DebuggerViewController()
let emuScreenLayer = CALayer()
let cg = CharacterGenerator(romPath: "/Users/luigi/apple1/apple1.vid"); let computer = AppleI.sharedInstance
var appleScreenView: AppleScreenView = AppleScreenView(frame: NSMakeRect(0, 0, 400, 384))
let appleScreenViewDelegate = AppleScreenViewDelegate() var debuggerWindowController: DebuggerWindowController!
override func viewDidLoad() { override func viewDidLoad() {
super.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. // Do view setup here.
self.view.addSubview(appleScreenView) self.view.addSubview(computer.emulatorView)
computer.emulatorView.display()
appleScreenView.wantsLayer = true computer.runFrame()
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()
} }
} }

View 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>