some bugfixes and adaptations for Apple II
This commit is contained in:
parent
f6e0467fe1
commit
caad9c6ff6
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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..<AppleII.ScreenDelegate.PIXEL_HEIGHT {
|
||||
scanlineOffsets.append(i * AppleII.ScreenDelegate.PIXEL_WIDTH)
|
||||
}
|
||||
}
|
||||
|
||||
func putGlyph(buffer: UnsafeMutablePointer<BitmapPixelsBE555.PixelData>?, 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..<AppleII.A2CharacterGenerator.CHAR_HEIGHT {
|
||||
let offset = baseOffset + (AppleII.ScreenDelegate.PIXEL_WIDTH * charY)
|
||||
let glyphOffsetY = (charY * 8)
|
||||
|
||||
for charX in 0..<7 {
|
||||
buffer![offset + 6 - charX] = glyph.pixels[glyphOffsetY + charX]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getPixelOffset(charCellX: Int, charCellY: Int) -> 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<BitmapPixelsBE555.PixelData>.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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -224,10 +224,10 @@
|
|||
</objects>
|
||||
<point key="canvasLocation" x="97" y="-661"/>
|
||||
</scene>
|
||||
<!--AppleI View Controller-->
|
||||
<!--AppleII View Controller-->
|
||||
<scene sceneID="AMw-8X-nlH">
|
||||
<objects>
|
||||
<viewController id="gw1-M7-9Je" customClass="AppleIViewController" customModule="FruitMachine" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<viewController id="gw1-M7-9Je" customClass="AppleIIViewController" 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"/>
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 {}
|
||||
|
|
Loading…
Reference in New Issue