diff --git a/FruitMachine/AppleII/AppleII.swift b/FruitMachine/AppleII/AppleII.swift index b88866e..36e9c4c 100644 --- a/FruitMachine/AppleII/AppleII.swift +++ b/FruitMachine/AppleII/AppleII.swift @@ -11,12 +11,14 @@ import Cocoa class AppleII: NSObject, EmulatedSystem { static let sharedInstance = AppleII(cpuFrequency: (14.31818 / 7 / 2) * 1000000, fps: 60.0) + let cg = A2CharacterGenerator(romPath: "/Users/luigi/apple2/a2.chr"); + 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 emulatorView = AppleII.ScreenView(frame: NSMakeRect(0, 0, 560, 384)) let emuScreenLayer = CALayer() required init(cpuFrequency: Double, fps: Double) { @@ -27,7 +29,6 @@ class AppleII: NSObject, EmulatedSystem { loadROMs() - /* emuScreenLayer.shouldRasterize = true emuScreenLayer.delegate = emulatorViewDelegate emuScreenLayer.frame = emulatorView.bounds @@ -38,11 +39,8 @@ class AppleII: NSObject, EmulatedSystem { 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() { @@ -62,6 +60,23 @@ class AppleII: NSObject, EmulatedSystem { 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) + + //Text mode: Get character codes from $0400-$07FF + for address in 0x0400 ..< 0x07C0 { + let charCode = CPU.sharedInstance.memoryInterface.readByte(offset: UInt16(address), bypassOverrides: true) + + emulatorViewDelegate.putGlyph(buffer: buf, + glyph: cg.glyphs[Int(charCode & ~(0x80))], + pixelPosition: emulatorViewDelegate.getPixelOffset(charCellIndex: address - 0x400)) + } + + CVPixelBufferUnlockBaseAddress(emulatorViewDelegate.pixels!, CVPixelBufferLockFlags(rawValue: 0)) + + emulatorView.setNeedsDisplay(emulatorView.frame) } } diff --git a/FruitMachine/AppleII/Video/ScreenDelegate.swift b/FruitMachine/AppleII/Video/ScreenDelegate.swift index a5bd40e..14052fc 100644 --- a/FruitMachine/AppleII/Video/ScreenDelegate.swift +++ b/FruitMachine/AppleII/Video/ScreenDelegate.swift @@ -13,7 +13,91 @@ extension AppleII { class ScreenDelegate: NSObject, CALayerDelegate { static let PIXEL_WIDTH = 280 static let PIXEL_HEIGHT = 192 + + static let CELLS_WIDTH = 40 + static let CELLS_HEIGHT = 24 + static let CELLS_COUNT = CELLS_WIDTH * CELLS_HEIGHT + + /* Pixel data stuff. */ + let bitmapInfo: CGBitmapInfo = [.byteOrder16Big, CGBitmapInfo(rawValue: CGImageAlphaInfo.noneSkipFirst.rawValue)] + + var pixels: CVPixelBuffer? + + let sourceRowBytes: Int + let bufferWidth: Int + let bufferHeight: Int + + var renderedImage: CGImage? + + var scanlineOffsets: [Int] + override init() { + _ = CVPixelBufferCreate(kCFAllocatorDefault, AppleII.ScreenDelegate.PIXEL_WIDTH, AppleII.ScreenDelegate.PIXEL_HEIGHT, OSType(k16BE555PixelFormat), nil, &pixels) + + sourceRowBytes = CVPixelBufferGetBytesPerRow(pixels!) + bufferWidth = CVPixelBufferGetWidth(pixels!) + bufferHeight = CVPixelBufferGetHeight(pixels!) + + renderedImage = nil + + scanlineOffsets = [Int]() + for i in 0..?, glyph: Glyph, pixelPosition: CGPoint) { + //You better have locked the buffer before getting here... + + //Calculate the offset to reach the desired position. + let baseOffset = scanlineOffsets[Int(pixelPosition.y)] + Int(pixelPosition.x) + + for charY in 0.. CGPoint { + return CGPoint(x: charCellX * 7, y: charCellY * 8) + } + + func getPixelOffset(charCellIndex: Int) -> CGPoint { + return getPixelOffset(charCellX: charCellIndex % AppleII.ScreenDelegate.CELLS_WIDTH, charCellY: charCellIndex / AppleII.ScreenDelegate.CELLS_WIDTH) + } + + /* Draw the screen. */ + func draw(_ layer: CALayer, in ctx: CGContext) { + CVPixelBufferLockBaseAddress(pixels!, CVPixelBufferLockFlags.readOnly) + let pixelBase = CVPixelBufferGetBaseAddress(pixels!) + let pixelRef = CGDataProvider(dataInfo: nil, data: pixelBase!, size: sourceRowBytes * bufferHeight, releaseData: releaseMaskImagePixelData) + + 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 + bitmapInfo: bitmapInfo, //BE555 + provider: pixelRef!, + decode: nil, + shouldInterpolate: false, + intent: CGColorRenderingIntent.defaultIntent) + + ctx.draw(renderedImage!, in: layer.bounds) + + CVPixelBufferUnlockBaseAddress(pixels!, CVPixelBufferLockFlags(rawValue: 0)) + } + + let releaseMaskImagePixelData: CGDataProviderReleaseDataCallback = { (info: UnsafeMutableRawPointer?, data: UnsafeRawPointer, size: Int) -> () in + // https://developer.apple.com/reference/coregraphics/cgdataproviderreleasedatacallback + // N.B. 'CGDataProviderRelease' is unavailable: Core Foundation objects are automatically memory managed + return + } } } diff --git a/FruitMachine/AppleII/Video/ScreenView.swift b/FruitMachine/AppleII/Video/ScreenView.swift index 1df41c5..45836e3 100644 --- a/FruitMachine/AppleII/Video/ScreenView.swift +++ b/FruitMachine/AppleII/Video/ScreenView.swift @@ -28,8 +28,8 @@ extension AppleII { super.draw(dirtyRect) // Drawing code here. - //layer?.sublayers![0].setNeedsDisplay() - //layer?.sublayers![0].display() + layer?.sublayers![0].setNeedsDisplay() + layer?.sublayers![0].display() } } diff --git a/FruitMachine/FruitMachine.storyboard b/FruitMachine/FruitMachine.storyboard index 1a4f89a..cccddf0 100644 --- a/FruitMachine/FruitMachine.storyboard +++ b/FruitMachine/FruitMachine.storyboard @@ -224,10 +224,10 @@ - + - + diff --git a/FruitMachine/M6502/CPU.swift b/FruitMachine/M6502/CPU.swift index 0539f42..076b152 100644 --- a/FruitMachine/M6502/CPU.swift +++ b/FruitMachine/M6502/CPU.swift @@ -231,7 +231,7 @@ final class CPU: NSObject { self.branch_was_taken = false } - self.program_counter = UInt16(Int(self.program_counter) + operation!.bytes) + self.program_counter = UInt16(self.program_counter &+ UInt16(operation!.bytes)) } func outOfCycles() -> Bool { diff --git a/FruitMachine/M6502/Debugger/Disassembly.swift b/FruitMachine/M6502/Debugger/Disassembly.swift index 44a2dfd..7fd5e5d 100644 --- a/FruitMachine/M6502/Debugger/Disassembly.swift +++ b/FruitMachine/M6502/Debugger/Disassembly.swift @@ -34,9 +34,9 @@ class Disassembly: NSObject { case .relative: var destination: UInt16 = address if((data[1] & 0x80) == 0x80) { - destination = destination + 1 - UInt16(~data[1]) + destination = destination &+ 1 &- UInt16(~data[1]) } else { - destination = destination + 2 + UInt16(data[1]) + destination = destination &+ 2 &+ UInt16(data[1]) } return String(format: "%@ #$%04X", instruction!.mnemonic, destination) case .absolute: diff --git a/FruitMachine/M6502/Opcodes/Opcodes.swift b/FruitMachine/M6502/Opcodes/Opcodes.swift index ed57541..f7ba5d6 100644 --- a/FruitMachine/M6502/Opcodes/Opcodes.swift +++ b/FruitMachine/M6502/Opcodes/Opcodes.swift @@ -580,12 +580,12 @@ final class Opcodes: NSObject { //Misc static func JMP(state: CPU, addressingMode: CPU.AddressingMode) -> Void { - state.program_counter = getOperandAddressForAddressingMode(state: state, mode: addressingMode) - 3 + state.program_counter = getOperandAddressForAddressingMode(state: state, mode: addressingMode) &- 3 } static func JSR(state: CPU, addressingMode: CPU.AddressingMode) -> Void { state.pushWord(data: state.program_counter + 2) - state.program_counter = state.getOperandWord() - 3 + state.program_counter = state.getOperandWord() &- 3 } static func RTS(state: CPU, addressingMode: CPU.AddressingMode) -> Void { @@ -594,7 +594,7 @@ final class Opcodes: NSObject { static func RTI(state: CPU, addressingMode: CPU.AddressingMode) -> Void { state.status_register.fromByte(state: state.popByte()) - state.program_counter = state.popWord() - 1 + state.program_counter = state.popWord() &- 1 state.status_register.brk = false //RTI sets B to 0 } @@ -604,7 +604,7 @@ final class Opcodes: NSObject { state.pushWord(data: state.program_counter + 2) state.pushByte(data: sr.asByte()) state.status_register.irq_disable = true //BRK disables interrupts before transferring control - state.program_counter = state.memoryInterface.readWord(offset: 0xFFFE) - 1 + state.program_counter = state.memoryInterface.readWord(offset: 0xFFFE) &- 1 } static func NOP(state: CPU, addressingMode: CPU.AddressingMode) -> Void {}