From d0c22b09eba1c78a6e275c6d2961e7c5d3ec9065 Mon Sep 17 00:00:00 2001 From: Luigi Thirty Date: Wed, 2 Aug 2017 18:54:50 -0400 Subject: [PATCH] low res graphics! --- FruitMachine.xcodeproj/project.pbxproj | 4 + FruitMachine/AppleI/AppleI.swift | 2 +- FruitMachine/AppleI/AppleIBitmapDisplay.swift | 10 +- .../AppleI/Video/A1CharacterGenerator.swift | 8 +- FruitMachine/AppleII/AppleII.swift | 90 ++++++++++++++--- .../AppleII/SoftswitchOverrides.swift | 58 +++++++++++ .../AppleII/Video/A2CharacterGenerator.swift | 8 +- .../AppleII/Video/ScreenDelegate.swift | 49 +++++++-- FruitMachine/AppleII/Video/VideoModes.swift | 99 +++++++++++++++++++ FruitMachine/AppleIIViewController.swift | 4 - FruitMachine/Common/Video/BitmapPixels.swift | 17 +++- FruitMachine/Common/Video/Glyph.swift | 4 +- FruitMachine/FruitMachine.storyboard | 8 +- .../Debugger/DebuggerViewController.swift | 2 +- FruitMachine/M6502/Opcodes/Opcodes.swift | 34 +++---- 15 files changed, 327 insertions(+), 70 deletions(-) create mode 100644 FruitMachine/AppleII/Video/VideoModes.swift diff --git a/FruitMachine.xcodeproj/project.pbxproj b/FruitMachine.xcodeproj/project.pbxproj index 9b9d077..57237f2 100644 --- a/FruitMachine.xcodeproj/project.pbxproj +++ b/FruitMachine.xcodeproj/project.pbxproj @@ -23,6 +23,7 @@ 2A7665781F2F05F600135518 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A7665761F2F05F600135518 /* PreferencesWindowController.swift */; }; 2A7665791F2F05F600135518 /* PreferencesWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2A7665771F2F05F600135518 /* PreferencesWindow.xib */; }; 2A86FB971F316CB500AD0C68 /* KeyboardController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A86FB961F316CB500AD0C68 /* KeyboardController.swift */; }; + 2A86FB991F32694A00AD0C68 /* VideoModes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A86FB981F32694A00AD0C68 /* VideoModes.swift */; }; 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 */; }; @@ -64,6 +65,7 @@ 2A7665761F2F05F600135518 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = ""; }; 2A7665771F2F05F600135518 /* PreferencesWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = PreferencesWindow.xib; sourceTree = ""; }; 2A86FB961F316CB500AD0C68 /* KeyboardController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardController.swift; sourceTree = ""; }; + 2A86FB981F32694A00AD0C68 /* VideoModes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoModes.swift; sourceTree = ""; }; 2A9185291F2EA84D00A9E5BE /* BitmapPixels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BitmapPixels.swift; sourceTree = ""; }; 2AA8B5F71F2A8889002B350F /* AppleI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleI.swift; sourceTree = ""; }; 2AA8B5FB1F2A8EAD002B350F /* Terminal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Terminal.swift; sourceTree = ""; }; @@ -144,6 +146,7 @@ 2A5C5BBB1F304C3A00ED351D /* A2CharacterGenerator.swift */, 2A6DC7EC1F30492C0066FE0D /* ScreenDelegate.swift */, 2A6DC7EF1F30495D0066FE0D /* ScreenView.swift */, + 2A86FB981F32694A00AD0C68 /* VideoModes.swift */, ); path = Video; sourceTree = ""; @@ -374,6 +377,7 @@ 2AD458E11F2064CB00F05121 /* MemoryInterface.swift in Sources */, 2AA8B5F81F2A8889002B350F /* AppleI.swift in Sources */, 2AD458DF1F205F4500F05121 /* CPU.swift in Sources */, + 2A86FB991F32694A00AD0C68 /* VideoModes.swift in Sources */, 2A6DC7E91F3045280066FE0D /* AppleIIViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/FruitMachine/AppleI/AppleI.swift b/FruitMachine/AppleI/AppleI.swift index dcae11b..8a29f90 100644 --- a/FruitMachine/AppleI/AppleI.swift +++ b/FruitMachine/AppleI/AppleI.swift @@ -67,7 +67,7 @@ class AppleI: NSObject, EmulatedSystem { //update the video display CVPixelBufferLockBaseAddress(emulatorViewDelegate.pixels!, CVPixelBufferLockFlags(rawValue: 0)) let pixelBase = CVPixelBufferGetBaseAddress(emulatorViewDelegate.pixels!) - let buf = pixelBase?.assumingMemoryBound(to: BitmapPixelsBE555.PixelData.self) + let buf = pixelBase?.assumingMemoryBound(to: BitmapPixelsLE555.PixelData.self) for (cellNum, character) in terminal.characters.enumerated() { emulatorViewDelegate.putGlyph(buffer: buf, diff --git a/FruitMachine/AppleI/AppleIBitmapDisplay.swift b/FruitMachine/AppleI/AppleIBitmapDisplay.swift index 9c0cf29..36155bb 100644 --- a/FruitMachine/AppleI/AppleIBitmapDisplay.swift +++ b/FruitMachine/AppleI/AppleIBitmapDisplay.swift @@ -40,7 +40,7 @@ class AppleIBitmapDisplay: NSObject, CALayerDelegate { } } - func putGlyph(buffer: UnsafeMutablePointer?, glyph: Glyph, pixelPosition: CGPoint) { + func putGlyph(buffer: UnsafeMutablePointer?, glyph: Glyph, pixelPosition: CGPoint) { //You better have locked the buffer before getting here... //Calculate the offset to reach the desired position. @@ -72,10 +72,10 @@ class AppleIBitmapDisplay: NSObject, CALayerDelegate { renderedImage = CGImage(width: AppleIBitmapDisplay.PIXEL_WIDTH, height: AppleIBitmapDisplay.PIXEL_HEIGHT, - bitsPerComponent: Int(BitmapPixelsBE555.bitsPerComponent), //5 - bitsPerPixel: Int(BitmapPixelsBE555.bitsPerPixel), //16 - bytesPerRow: AppleIBitmapDisplay.PIXEL_WIDTH * Int(MemoryLayout.size), - space: BitmapPixelsBE555.colorSpace, //RGB + bitsPerComponent: Int(BitmapPixelsLE555.bitsPerComponent), //5 + bitsPerPixel: Int(BitmapPixelsLE555.bitsPerPixel), //16 + bytesPerRow: AppleIBitmapDisplay.PIXEL_WIDTH * Int(MemoryLayout.size), + space: BitmapPixelsLE555.colorSpace, //RGB bitmapInfo: bitmapInfo, //BE555 provider: pixelRef!, decode: nil, diff --git a/FruitMachine/AppleI/Video/A1CharacterGenerator.swift b/FruitMachine/AppleI/Video/A1CharacterGenerator.swift index 293da6a..7daecd6 100644 --- a/FruitMachine/AppleI/Video/A1CharacterGenerator.swift +++ b/FruitMachine/AppleI/Video/A1CharacterGenerator.swift @@ -21,7 +21,7 @@ extension AppleI { init(romPath: String) { ROM = [UInt8](repeating: 0xCC, count: 512) - glyphs = [Glyph](repeating: Glyph(inPixels: [BitmapPixelsBE555.PixelData]()), count: 64) + glyphs = [Glyph](repeating: Glyph(inPixels: [BitmapPixelsLE555.PixelData]()), count: 64) super.init() loadROM(path: romPath) @@ -40,7 +40,7 @@ extension AppleI { } } - private func getCharacterPixels(charIndex: UInt8) -> [BitmapPixelsBE555.PixelData] { + private func getCharacterPixels(charIndex: UInt8) -> [BitmapPixelsLE555.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. @@ -51,11 +51,11 @@ extension AppleI { pixelArray[scanlineIndex] = ROM[scanlineIndex + (Int(charIndex) * A1CharacterGenerator.CHAR_HEIGHT)] } - var glyphPixels = [BitmapPixelsBE555.PixelData]() + var glyphPixels = [BitmapPixelsLE555.PixelData]() for charY in 0.. 0 ? BitmapPixelsBE555.White : BitmapPixelsBE555.Black) + glyphPixels.append(pixelArray[Int(charY)] & (1 << charX) > 0 ? BitmapPixelsLE555.White : BitmapPixelsLE555.Black) } } diff --git a/FruitMachine/AppleII/AppleII.swift b/FruitMachine/AppleII/AppleII.swift index 76f6a79..d7a0b99 100644 --- a/FruitMachine/AppleII/AppleII.swift +++ b/FruitMachine/AppleII/AppleII.swift @@ -15,6 +15,8 @@ class AppleII: NSObject, EmulatedSystem { let cg = A2CharacterGenerator(romPath: "/Users/luigi/apple2/a2.chr"); let keyboardController = KeyboardController() + var videoSoftswitches = VideoSoftswitches() + var videoMode: VideoMode var CPU_FREQUENCY: Double var FRAMES_PER_SECOND: Double @@ -28,6 +30,9 @@ class AppleII: NSObject, EmulatedSystem { CPU_FREQUENCY = cpuFrequency FRAMES_PER_SECOND = fps CYCLES_PER_BATCH = Int(cpuFrequency / fps) + + videoMode = .Text + super.init() loadROMs() @@ -48,23 +53,40 @@ class AppleII: NSObject, EmulatedSystem { } func doReset() { + videoSoftswitches.reset() + videoMode = .Text 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) - */ - CPU.sharedInstance.memoryInterface.loadBinary(path: "/Users/luigi/6502/test.bin", offset: 0x0000, length: 0x10000) } func installOverrides() { CPU.sharedInstance.memoryInterface.read_overrides.append(SoftswitchOverrides.readKeyboard) CPU.sharedInstance.memoryInterface.read_overrides.append(SoftswitchOverrides.clearKeypressStrobeR) CPU.sharedInstance.memoryInterface.write_overrides.append(SoftswitchOverrides.clearKeypressStrobeW) + + CPU.sharedInstance.memoryInterface.read_overrides.append(SoftswitchOverrides.switchC050R) + CPU.sharedInstance.memoryInterface.read_overrides.append(SoftswitchOverrides.switchC051R) + CPU.sharedInstance.memoryInterface.read_overrides.append(SoftswitchOverrides.switchC052R) + CPU.sharedInstance.memoryInterface.read_overrides.append(SoftswitchOverrides.switchC053R) + CPU.sharedInstance.memoryInterface.read_overrides.append(SoftswitchOverrides.switchC054R) + CPU.sharedInstance.memoryInterface.read_overrides.append(SoftswitchOverrides.switchC055R) + CPU.sharedInstance.memoryInterface.read_overrides.append(SoftswitchOverrides.switchC056R) + CPU.sharedInstance.memoryInterface.read_overrides.append(SoftswitchOverrides.switchC057R) + + CPU.sharedInstance.memoryInterface.write_overrides.append(SoftswitchOverrides.switchC050W) + CPU.sharedInstance.memoryInterface.write_overrides.append(SoftswitchOverrides.switchC051W) + CPU.sharedInstance.memoryInterface.write_overrides.append(SoftswitchOverrides.switchC052W) + CPU.sharedInstance.memoryInterface.write_overrides.append(SoftswitchOverrides.switchC053W) + CPU.sharedInstance.memoryInterface.write_overrides.append(SoftswitchOverrides.switchC054W) + CPU.sharedInstance.memoryInterface.write_overrides.append(SoftswitchOverrides.switchC055W) + CPU.sharedInstance.memoryInterface.write_overrides.append(SoftswitchOverrides.switchC056W) + CPU.sharedInstance.memoryInterface.write_overrides.append(SoftswitchOverrides.switchC057W) } func runFrame() { @@ -77,25 +99,65 @@ class AppleII: NSObject, EmulatedSystem { CPU.sharedInstance.cyclesInBatch = CYCLES_PER_BATCH CPU.sharedInstance.runCyclesBatch() - //TODO //update the video display CVPixelBufferLockBaseAddress(emulatorViewDelegate.pixels!, CVPixelBufferLockFlags(rawValue: 0)) let pixelBase = CVPixelBufferGetBaseAddress(emulatorViewDelegate.pixels!) - let buf = pixelBase?.assumingMemoryBound(to: BitmapPixelsBE555.PixelData.self) + let buf = pixelBase?.assumingMemoryBound(to: BitmapPixelsLE555.PixelData.self) - //Text mode: Get character codes from $0400-$07FF - for address in 0x0400 ..< 0x07F8 { - let charCode = CPU.sharedInstance.memoryInterface.readByte(offset: UInt16(address), bypassOverrides: true) + videoMode = getCurrentVideoMode(switches: videoSoftswitches) + + if(videoMode == .Text) + { + //Text mode: Get character codes from $0400-$07FF + putGlyphs(buffer: buf!, start: 0x400, end: 0x7F8) + } + else if(videoMode == .Lores) + { + putLoresPixels(buffer: buf!, start: 0x400, end: 0x7F8) + } + else if(videoMode == .MixedLores) { + //Draw the lores pixel rows. + putLoresPixels(buffer: buf!, start: 0x400, end: 0x650) + putLoresPixels(buffer: buf!, start: 0x680, end: 0x6A8) + putLoresPixels(buffer: buf!, start: 0x700, end: 0x728) + putLoresPixels(buffer: buf!, start: 0x780, end: 0x7A8) + putLoresPixels(buffer: buf!, start: 0x6A8, end: 0x6D0) + putLoresPixels(buffer: buf!, start: 0x728, end: 0x750) + putLoresPixels(buffer: buf!, start: 0x7A8, end: 0x7D0) - emulatorViewDelegate.putGlyph(buffer: buf, - glyph: cg.glyphs[Int(charCode & 0x3F)], - attributes: charCode & 0xC0, //d6 and d7 - pixelPosition: emulatorViewDelegate.getPixelOffset(memoryOffset: address - 0x400)) + //Draw the bottom 4 text rows. + putGlyphs(buffer: buf!, start: 0x650, end: 0x678) + putGlyphs(buffer: buf!, start: 0x6D0, end: 0x6F8) + putGlyphs(buffer: buf!, start: 0x750, end: 0x778) + putGlyphs(buffer: buf!, start: 0x7D0, end: 0x7F8) + } else { + print("Unimplemented video mode!") } - CVPixelBufferUnlockBaseAddress(emulatorViewDelegate.pixels!, CVPixelBufferLockFlags(rawValue: 0)) - emulatorView.setNeedsDisplay(emulatorView.frame) + CVPixelBufferUnlockBaseAddress(emulatorViewDelegate.pixels!, CVPixelBufferLockFlags(rawValue: 0)) + emulatorView.display() + } + + func putLoresPixels(buffer: UnsafeMutablePointer, start: UInt16, end: UInt16) { + for address in start ..< end { + let pixelData = CPU.sharedInstance.memoryInterface.readByte(offset: UInt16(address), bypassOverrides: true) + + emulatorViewDelegate.putLoresPixel(buffer: buffer, + pixel: pixelData, + address: UInt16(address)) + } + } + + func putGlyphs(buffer: UnsafeMutablePointer, start: UInt16, end: UInt16) { + for address in start ... end { + let charCode = CPU.sharedInstance.memoryInterface.readByte(offset: UInt16(address), bypassOverrides: true) + + emulatorViewDelegate.putGlyph(buffer: buffer, + glyph: cg.glyphs[Int(charCode & 0x3F)], + attributes: charCode & 0xC0, //d6 and d7 + pixelPosition: emulatorViewDelegate.getPixelOffset(memoryOffset: Int(address - 0x400))) + } } enum MemoryConfiguration { diff --git a/FruitMachine/AppleII/SoftswitchOverrides.swift b/FruitMachine/AppleII/SoftswitchOverrides.swift index 156b487..53e5b87 100644 --- a/FruitMachine/AppleII/SoftswitchOverrides.swift +++ b/FruitMachine/AppleII/SoftswitchOverrides.swift @@ -11,6 +11,7 @@ import Cocoa extension AppleII { class SoftswitchOverrides: NSObject { + /* Keyboard port */ static let readKeyboard = ReadOverride(start: 0xC000, end: 0xC000, readAnyway: false, action: SoftswitchOverrides.actionReadKeyboard) static func actionReadKeyboard(dummy: AnyObject, byte: UInt8?) -> UInt8? { //let b = CPU.sharedInstance.memoryInterface.readByte(offset: 0xC000, bypassOverrides: true) @@ -34,6 +35,63 @@ extension AppleII { AppleII.sharedInstance.keyboardController.STROBE = b & 0x7F return b } + + /* Video settings */ + static let switchC050R = ReadOverride(start: 0xC050, end: 0xC050, readAnyway: false, action: SoftswitchOverrides.actionSwitchC050) + static let switchC050W = WriteOverride(start: 0xC050, end: 0xC050, writeAnyway: false, action: SoftswitchOverrides.actionSwitchC050) + static func actionSwitchC050(dummy: AnyObject, byte: UInt8?) -> UInt8? { + AppleII.sharedInstance.videoSoftswitches.TEXT_MODE = false + return 0x00 + } + + static let switchC051R = ReadOverride(start: 0xC051, end: 0xC051, readAnyway: false, action: SoftswitchOverrides.actionSwitchC051) + static let switchC051W = WriteOverride(start: 0xC051, end: 0xC051, writeAnyway: false, action: SoftswitchOverrides.actionSwitchC051) + static func actionSwitchC051(dummy: AnyObject, byte: UInt8?) -> UInt8? { + AppleII.sharedInstance.videoSoftswitches.TEXT_MODE = true + return 0x00 + } + + static let switchC052R = ReadOverride(start: 0xC052, end: 0xC052, readAnyway: false, action: SoftswitchOverrides.actionSwitchC052) + static let switchC052W = WriteOverride(start: 0xC052, end: 0xC052, writeAnyway: false, action: SoftswitchOverrides.actionSwitchC052) + static func actionSwitchC052(dummy: AnyObject, byte: UInt8?) -> UInt8? { + AppleII.sharedInstance.videoSoftswitches.MIX_MODE = false + return 0x00 + } + + static let switchC053R = ReadOverride(start: 0xC053, end: 0xC053, readAnyway: false, action: SoftswitchOverrides.actionSwitchC053) + static let switchC053W = WriteOverride(start: 0xC053, end: 0xC053, writeAnyway: false, action: SoftswitchOverrides.actionSwitchC053) + static func actionSwitchC053(dummy: AnyObject, byte: UInt8?) -> UInt8? { + AppleII.sharedInstance.videoSoftswitches.MIX_MODE = true + return 0x00 + } + + static let switchC054R = ReadOverride(start: 0xC054, end: 0xC054, readAnyway: false, action: SoftswitchOverrides.actionSwitchC054) + static let switchC054W = WriteOverride(start: 0xC054, end: 0xC054, writeAnyway: false, action: SoftswitchOverrides.actionSwitchC054) + static func actionSwitchC054(dummy: AnyObject, byte: UInt8?) -> UInt8? { + AppleII.sharedInstance.videoSoftswitches.PAGE_2 = false + return 0x00 + } + + static let switchC055R = ReadOverride(start: 0xC055, end: 0xC055, readAnyway: false, action: SoftswitchOverrides.actionSwitchC055) + static let switchC055W = WriteOverride(start: 0xC055, end: 0xC055, writeAnyway: false, action: SoftswitchOverrides.actionSwitchC055) + static func actionSwitchC055(dummy: AnyObject, byte: UInt8?) -> UInt8? { + AppleII.sharedInstance.videoSoftswitches.MIX_MODE = true + return 0x00 + } + + static let switchC056R = ReadOverride(start: 0xC056, end: 0xC056, readAnyway: false, action: SoftswitchOverrides.actionSwitchC056) + static let switchC056W = WriteOverride(start: 0xC056, end: 0xC056, writeAnyway: false, action: SoftswitchOverrides.actionSwitchC056) + static func actionSwitchC056(dummy: AnyObject, byte: UInt8?) -> UInt8? { + AppleII.sharedInstance.videoSoftswitches.HIRES_MODE = false + return 0x00 + } + + static let switchC057R = ReadOverride(start: 0xC057, end: 0xC057, readAnyway: false, action: SoftswitchOverrides.actionSwitchC057) + static let switchC057W = WriteOverride(start: 0xC057, end: 0xC057, writeAnyway: false, action: SoftswitchOverrides.actionSwitchC057) + static func actionSwitchC057(dummy: AnyObject, byte: UInt8?) -> UInt8? { + AppleII.sharedInstance.videoSoftswitches.HIRES_MODE = true + return 0x00 + } } } diff --git a/FruitMachine/AppleII/Video/A2CharacterGenerator.swift b/FruitMachine/AppleII/Video/A2CharacterGenerator.swift index d0314d9..d17422c 100644 --- a/FruitMachine/AppleII/Video/A2CharacterGenerator.swift +++ b/FruitMachine/AppleII/Video/A2CharacterGenerator.swift @@ -21,7 +21,7 @@ extension AppleII { init(romPath: String) { ROM = [UInt8](repeating: 0xCC, count: 0x800) - glyphs = [Glyph](repeating: Glyph(inPixels: [BitmapPixelsBE555.PixelData]()), count: 64) + glyphs = [Glyph](repeating: Glyph(inPixels: [BitmapPixelsLE555.PixelData]()), count: 64) super.init() loadROM(path: romPath) @@ -31,7 +31,7 @@ extension AppleII { } } - private func getCharacterPixels(charIndex: UInt8) -> [BitmapPixelsBE555.PixelData] { + private func getCharacterPixels(charIndex: UInt8) -> [BitmapPixelsLE555.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. @@ -42,11 +42,11 @@ extension AppleII { pixelArray[scanlineIndex] = ROM[scanlineIndex + (Int(charIndex) * A2CharacterGenerator.CHAR_HEIGHT)] } - var glyphPixels = [BitmapPixelsBE555.PixelData]() + var glyphPixels = [BitmapPixelsLE555.PixelData]() for charY in 0.. 0 ? BitmapPixelsBE555.White : BitmapPixelsBE555.Black) + glyphPixels.append(pixelArray[Int(charY)] & (1 << charX) > 0 ? BitmapPixelsLE555.White : BitmapPixelsLE555.Black) } } diff --git a/FruitMachine/AppleII/Video/ScreenDelegate.swift b/FruitMachine/AppleII/Video/ScreenDelegate.swift index f59a9dc..e53b3df 100644 --- a/FruitMachine/AppleII/Video/ScreenDelegate.swift +++ b/FruitMachine/AppleII/Video/ScreenDelegate.swift @@ -27,7 +27,7 @@ extension AppleII { var flashIsInverse = false /* Pixel data stuff. */ - let bitmapInfo: CGBitmapInfo = [.byteOrder16Big, CGBitmapInfo(rawValue: CGImageAlphaInfo.noneSkipFirst.rawValue)] + let bitmapInfo: CGBitmapInfo = [.byteOrder16Little, CGBitmapInfo(rawValue: CGImageAlphaInfo.noneSkipFirst.rawValue)] var pixels: CVPixelBuffer? @@ -54,7 +54,7 @@ extension AppleII { } } - func putGlyph(buffer: UnsafeMutablePointer?, glyph: Glyph, attributes: UInt8, pixelPosition: CGPoint) { + func putGlyph(buffer: UnsafeMutablePointer?, glyph: Glyph, attributes: UInt8, pixelPosition: CGPoint) { //You better have locked the buffer before getting here... if(pixelPosition.x == -1 && pixelPosition.y == -1) { return } @@ -79,12 +79,12 @@ extension AppleII { case .normal: buffer![offset + 6 - charX] = glyph.pixels[glyphOffsetY + charX] case .inverse: - buffer![offset + 6 - charX] = BitmapPixelsBE555.PixelData(data: ~glyph.pixels[glyphOffsetY + charX].data) + buffer![offset + 6 - charX] = BitmapPixelsLE555.PixelData(data: ~glyph.pixels[glyphOffsetY + charX].data) case .flashing: if(!flashIsInverse) { buffer![offset + 6 - charX] = glyph.pixels[glyphOffsetY + charX] } else { - buffer![offset + 6 - charX] = BitmapPixelsBE555.PixelData(data: ~glyph.pixels[glyphOffsetY + charX].data) + buffer![offset + 6 - charX] = BitmapPixelsLE555.PixelData(data: ~glyph.pixels[glyphOffsetY + charX].data) } } @@ -92,6 +92,39 @@ extension AppleII { } } + func putLoresPixel(buffer: UnsafeMutablePointer?, pixel: UInt8, address: UInt16) { + let pageOffset = address - 0x400 + let pixelPosition = getPixelOffset(memoryOffset: Int(pageOffset)) + if(pixelPosition.x == -1 && pixelPosition.y == -1) { + return + } + + let pixelNybbleHi = pixel & 0x0F + let pixelNybbleLo = (pixel & 0xF0) >> 4 + + let colorHi = LoresColors.getColor(index: pixelNybbleHi) + let colorLo = LoresColors.getColor(index: pixelNybbleLo) + + //One lores pixel is 7px wide and 4px tall for a resolution of 40x48. + let baseOffset = scanlineOffsets[Int(pixelPosition.y)] + Int(pixelPosition.x) + + for charY in 0..<5 { + let offsetHi = baseOffset + (AppleII.ScreenDelegate.PIXEL_WIDTH * charY) + + for charX in 0..<7 { + buffer![offsetHi + 6 - charX] = colorHi + } + } + for charY in 4..<8 { + let offsetLo = baseOffset + (AppleII.ScreenDelegate.PIXEL_WIDTH * charY) + + for charX in 0..<7 { + buffer![offsetLo + 6 - charX] = colorLo + } + } + + } + func getPixelOffset(charCellX: Int, charCellY: Int) -> CGPoint { return CGPoint(x: charCellX * 7, y: charCellY * 8) } @@ -137,10 +170,10 @@ extension AppleII { renderedImage = CGImage(width: AppleII.ScreenDelegate.PIXEL_WIDTH, height: AppleII.ScreenDelegate.PIXEL_HEIGHT, - bitsPerComponent: Int(BitmapPixelsBE555.bitsPerComponent), //5 - bitsPerPixel: Int(BitmapPixelsBE555.bitsPerPixel), //16 - bytesPerRow: AppleII.ScreenDelegate.PIXEL_WIDTH * Int(MemoryLayout.size), - space: BitmapPixelsBE555.colorSpace, //RGB + bitsPerComponent: Int(BitmapPixelsLE555.bitsPerComponent), //5 + bitsPerPixel: Int(BitmapPixelsLE555.bitsPerPixel), //16 + bytesPerRow: AppleII.ScreenDelegate.PIXEL_WIDTH * Int(MemoryLayout.size), + space: BitmapPixelsLE555.colorSpace, //RGB bitmapInfo: bitmapInfo, //BE555 provider: pixelRef!, decode: nil, diff --git a/FruitMachine/AppleII/Video/VideoModes.swift b/FruitMachine/AppleII/Video/VideoModes.swift new file mode 100644 index 0000000..72c5893 --- /dev/null +++ b/FruitMachine/AppleII/Video/VideoModes.swift @@ -0,0 +1,99 @@ +// +// VideoModes.swift +// FruitMachine +// +// Created by Christopher Rohl on 8/2/17. +// Copyright © 2017 Christopher Rohl. All rights reserved. +// + +import Cocoa + +extension AppleII { + + typealias Softswitch = Bool + + struct VideoSoftswitches { + var TEXT_MODE: Softswitch = false // $C050/$C051 + var MIX_MODE: Softswitch = false // $C052/$C053 + var PAGE_2: Softswitch = false // $C054/$C055 + var HIRES_MODE: Softswitch = false // $C056/$C057 + + mutating func reset() { + TEXT_MODE = true + MIX_MODE = false + PAGE_2 = false + HIRES_MODE = false + } + } + + enum VideoMode { + case Text + case Lores + case Hires + case MixedLores + case MixedHires + } + + struct LoresColors { + static let Black = BitmapPixelsLE555.RGB32toLE555(r: 0, g: 0, b: 0) + static let Magenta = BitmapPixelsLE555.RGB32toLE555(r: 227, g: 30, b: 96) + static let DarkBlue = BitmapPixelsLE555.RGB32toLE555(r: 96, g: 78, b: 189) + static let Purple = BitmapPixelsLE555.RGB32toLE555(r: 255, g: 68, b: 253) + static let DarkGreen = BitmapPixelsLE555.RGB32toLE555(r: 0, g: 163, b: 96) + static let Gray1 = BitmapPixelsLE555.RGB32toLE555(r: 156, g: 156, b: 156) + static let MediumBlue = BitmapPixelsLE555.RGB32toLE555(r: 20, g: 207, b: 253) + static let LightBlue = BitmapPixelsLE555.RGB32toLE555(r: 208, g: 195, b: 255) + static let Brown = BitmapPixelsLE555.RGB32toLE555(r: 96, g: 114, b: 3) + static let Orange = BitmapPixelsLE555.RGB32toLE555(r: 255, g: 106, b: 60) + static let Gray2 = BitmapPixelsLE555.RGB32toLE555(r: 156, g: 156, b: 156) + static let Pink = BitmapPixelsLE555.RGB32toLE555(r: 255, g: 160, b: 208) + static let LightGreen = BitmapPixelsLE555.RGB32toLE555(r: 20, g: 245, b: 60) + static let Yellow = BitmapPixelsLE555.RGB32toLE555(r: 208, g: 221, b: 141) + static let Aquamarine = BitmapPixelsLE555.RGB32toLE555(r: 114, g: 255, b: 208) + static let White = BitmapPixelsLE555.RGB32toLE555(r: 255, g: 255, b: 255) + + static func getColor(index: UInt8) -> BitmapPixelsLE555.PixelData { + switch index { + case 0: return AppleII.LoresColors.Black + case 1: return AppleII.LoresColors.Magenta + case 2: return AppleII.LoresColors.DarkBlue + case 3: return AppleII.LoresColors.Purple + case 4: return AppleII.LoresColors.DarkGreen + case 5: return AppleII.LoresColors.Gray1 + case 6: return AppleII.LoresColors.MediumBlue + case 7: return AppleII.LoresColors.LightBlue + case 8: return AppleII.LoresColors.Brown + case 9: return AppleII.LoresColors.Orange + case 10: return AppleII.LoresColors.Gray2 + case 11: return AppleII.LoresColors.Pink + case 12: return AppleII.LoresColors.LightGreen + case 13: return AppleII.LoresColors.Yellow + case 14: return AppleII.LoresColors.Aquamarine + case 15: return AppleII.LoresColors.White + default: + print("tried to get color > 15") + return AppleII.LoresColors.Black + } + + } + } + + func getCurrentVideoMode(switches: VideoSoftswitches) -> VideoMode { + if(switches.TEXT_MODE == true) + { + return .Text + } + else if(switches.MIX_MODE) { + if(switches.HIRES_MODE == false) { + return .MixedLores + } else { + return .MixedHires + } + } + else if(switches.HIRES_MODE) { + return .Hires + } else { + return .Lores + } + } +} diff --git a/FruitMachine/AppleIIViewController.swift b/FruitMachine/AppleIIViewController.swift index 1b1dd4b..f661845 100644 --- a/FruitMachine/AppleIIViewController.swift +++ b/FruitMachine/AppleIIViewController.swift @@ -25,15 +25,11 @@ class AppleIIViewController: NSViewController { self.view.addSubview(computer.emulatorView) - /* self.frameTimer = Timer.scheduledTimer(timeInterval: 1.0/60.0, target: self, selector: #selector(runEmulation), userInfo: nil, repeats: true) - */ - - CPU.sharedInstance.program_counter = 0x400 } @objc func runEmulation() { diff --git a/FruitMachine/Common/Video/BitmapPixels.swift b/FruitMachine/Common/Video/BitmapPixels.swift index 0c267ca..d49a278 100644 --- a/FruitMachine/Common/Video/BitmapPixels.swift +++ b/FruitMachine/Common/Video/BitmapPixels.swift @@ -38,7 +38,7 @@ class BitmapPixelsARGB32 : NSObject { static let Black = PixelData(a: 255, r: 0, g: 0, b: 0) } -class BitmapPixelsBE555 : NSObject { +class BitmapPixelsLE555 : NSObject { struct PixelData { var data: UInt16 = 0 } @@ -47,6 +47,19 @@ class BitmapPixelsBE555 : NSObject { static let bitsPerPixel: UInt = 16 static let colorSpace = CGColorSpaceCreateDeviceRGB() - static let White = PixelData(data: 0b1111111101111111) + static let White = PixelData(data: 0b0111111111111111) static let Black = PixelData(data: 0b0000000000000000) + + static func RGB32toLE555(r: UInt8, g: UInt8, b: UInt8) -> PixelData { + let r5: UInt16 = UInt16(r >> 3) + let g5: UInt16 = UInt16(g >> 3) + let b5: UInt16 = UInt16(b >> 3) + + var pixel = PixelData() + pixel.data |= b5 + pixel.data |= g5 << 5 + pixel.data |= r5 << 10 + + return pixel + } } diff --git a/FruitMachine/Common/Video/Glyph.swift b/FruitMachine/Common/Video/Glyph.swift index cb43072..899e331 100644 --- a/FruitMachine/Common/Video/Glyph.swift +++ b/FruitMachine/Common/Video/Glyph.swift @@ -9,9 +9,9 @@ import Cocoa struct Glyph { - var pixels: [BitmapPixelsBE555.PixelData] = [BitmapPixelsBE555.PixelData]() + var pixels: [BitmapPixelsLE555.PixelData] = [BitmapPixelsLE555.PixelData]() - init(inPixels: [BitmapPixelsBE555.PixelData]) { + init(inPixels: [BitmapPixelsLE555.PixelData]) { pixels = inPixels } } diff --git a/FruitMachine/FruitMachine.storyboard b/FruitMachine/FruitMachine.storyboard index 7354b21..9939e8d 100644 --- a/FruitMachine/FruitMachine.storyboard +++ b/FruitMachine/FruitMachine.storyboard @@ -157,10 +157,10 @@ - + - - + + @@ -178,7 +178,7 @@ - + diff --git a/FruitMachine/M6502/Debugger/DebuggerViewController.swift b/FruitMachine/M6502/Debugger/DebuggerViewController.swift index 5a66c96..4782a12 100644 --- a/FruitMachine/M6502/Debugger/DebuggerViewController.swift +++ b/FruitMachine/M6502/Debugger/DebuggerViewController.swift @@ -74,7 +74,7 @@ class DebuggerViewController: NSViewController { isRunning = true cpuInstance.cycles = 0 - cpuInstance.cyclesInBatch = 10000 + cpuInstance.cyclesInBatch = 1000000 while(!cpuInstance.outOfCycles() && isRunning) { cpuInstance.cpuStep() diff --git a/FruitMachine/M6502/Opcodes/Opcodes.swift b/FruitMachine/M6502/Opcodes/Opcodes.swift index faae70b..a8bd065 100644 --- a/FruitMachine/M6502/Opcodes/Opcodes.swift +++ b/FruitMachine/M6502/Opcodes/Opcodes.swift @@ -142,20 +142,21 @@ fileprivate func hex2bcd(hex: UInt8) -> UInt8 { final class Opcodes: NSObject { static func _Add(state: CPU, operand: UInt8, isSubtract: Bool) { - if(state.accumulator == 0xFF) { + if((state.accumulator == 0xFF) && operand == 0x01 && state.status_register.carry == false) { _ = 1 } - var t16: UInt16 = UInt16(state.accumulator &+ operand) + UInt16((state.status_register.carry ? UInt8(1) : UInt8(0))) + var t16: UInt16 = UInt16(state.accumulator) &+ UInt16(operand) + UInt16((state.status_register.carry ? UInt8(1) : UInt8(0))) let t8: UInt8 = UInt8(t16 & 0xFF) - state.status_register.overflow = (state.accumulator & 0x80) != (t8 & 0x80) + //state.status_register.overflow = (state.accumulator & 0x80) != (t8 & 0x80) + state.status_register.overflow = ((t16 ^ UInt16(state.accumulator)) & (t16 ^ UInt16(operand)) & 0x0080) > UInt16(0) state.status_register.zero = (t8 == 0) state.status_register.negative = (t8 & 0x80) == 0x80 if(state.status_register.decimal) { t16 = UInt16(hex2bcd(hex: state.accumulator) + hex2bcd(hex: operand) + (state.status_register.carry ? UInt8(1) : UInt8(0))) } else { - state.status_register.carry = (t16 > 255) + state.status_register.carry = (t16 & 0xFF00) > 0 } state.accumulator = t8 @@ -166,25 +167,16 @@ final class Opcodes: NSObject { } static func SBC(state: CPU, addressingMode: CPU.AddressingMode) -> Void { - let operand = UInt8(getOperandByteForAddressingMode(state: state, mode: addressingMode)) - //I don't think this 16-bit calculation is working right - var t16: UInt16 = UInt16(state.accumulator &- operand) &- UInt16((state.status_register.carry ? UInt8(0) : UInt8(1))) - let t8: UInt8 = UInt8(t16 & 0xFF) + let operand = UInt16(getOperandByteForAddressingMode(state: state, mode: addressingMode)) - state.status_register.overflow = (t8 >= 0x80 && t8 <= 0xFF) + let carryValue = UInt16(state.status_register.carry ? 0 : 1) + let t16 = UInt16(state.accumulator) &- operand &- carryValue - if(state.status_register.decimal == true) { - t16 = UInt16(hex2bcd(hex: state.accumulator) + hex2bcd(hex: operand) + (state.status_register.carry ? UInt8(1) : UInt8(0))) - } else { - state.status_register.carry = (t16 >> 8) == 0 - //carry flag isn't being set properly - //let signed = Int8(bitPattern: t8) - //state.status_register.carry = ((-128 > signed) || (127 < signed)) ? true : false - } - - state.accumulator = t8 - state.updateZeroFlag(value: t8) - state.updateNegativeFlag(value: t8) + state.status_register.overflow = ((UInt16(state.accumulator) ^ operand) & (UInt16(state.accumulator) ^ t16) & 0x80) > 0 + state.status_register.carry = (t16 >> 8) == 0 + state.accumulator = UInt8(t16 & 0xFF) + state.updateZeroFlag(value: UInt8(t16 & 0xFF)) + state.updateNegativeFlag(value: UInt8(t16 & 0xFF)) } static func LDA(state: CPU, addressingMode: CPU.AddressingMode) -> Void {