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 {
|
class AppleII: NSObject, EmulatedSystem {
|
||||||
static let sharedInstance = AppleII(cpuFrequency: (14.31818 / 7 / 2) * 1000000, fps: 60.0)
|
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 CPU_FREQUENCY: Double
|
||||||
var FRAMES_PER_SECOND: Double
|
var FRAMES_PER_SECOND: Double
|
||||||
var CYCLES_PER_BATCH: Int
|
var CYCLES_PER_BATCH: Int
|
||||||
|
|
||||||
let emulatorViewDelegate = AppleII.ScreenDelegate()
|
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()
|
let emuScreenLayer = CALayer()
|
||||||
|
|
||||||
required init(cpuFrequency: Double, fps: Double) {
|
required init(cpuFrequency: Double, fps: Double) {
|
||||||
|
@ -27,7 +29,6 @@ class AppleII: NSObject, EmulatedSystem {
|
||||||
|
|
||||||
loadROMs()
|
loadROMs()
|
||||||
|
|
||||||
/*
|
|
||||||
emuScreenLayer.shouldRasterize = true
|
emuScreenLayer.shouldRasterize = true
|
||||||
emuScreenLayer.delegate = emulatorViewDelegate
|
emuScreenLayer.delegate = emulatorViewDelegate
|
||||||
emuScreenLayer.frame = emulatorView.bounds
|
emuScreenLayer.frame = emulatorView.bounds
|
||||||
|
@ -38,11 +39,8 @@ class AppleII: NSObject, EmulatedSystem {
|
||||||
emulatorView.layer?.addSublayer(emuScreenLayer)
|
emulatorView.layer?.addSublayer(emuScreenLayer)
|
||||||
|
|
||||||
installOverrides()
|
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()
|
CPU.sharedInstance.performReset()
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadROMs() {
|
func loadROMs() {
|
||||||
|
@ -62,6 +60,23 @@ class AppleII: NSObject, EmulatedSystem {
|
||||||
CPU.sharedInstance.runCyclesBatch()
|
CPU.sharedInstance.runCyclesBatch()
|
||||||
|
|
||||||
//TODO
|
//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 {
|
class ScreenDelegate: NSObject, CALayerDelegate {
|
||||||
static let PIXEL_WIDTH = 280
|
static let PIXEL_WIDTH = 280
|
||||||
static let PIXEL_HEIGHT = 192
|
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)
|
super.draw(dirtyRect)
|
||||||
// Drawing code here.
|
// Drawing code here.
|
||||||
|
|
||||||
//layer?.sublayers![0].setNeedsDisplay()
|
layer?.sublayers![0].setNeedsDisplay()
|
||||||
//layer?.sublayers![0].display()
|
layer?.sublayers![0].display()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,10 +224,10 @@
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="97" y="-661"/>
|
<point key="canvasLocation" x="97" y="-661"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--AppleI View Controller-->
|
<!--AppleII View Controller-->
|
||||||
<scene sceneID="AMw-8X-nlH">
|
<scene sceneID="AMw-8X-nlH">
|
||||||
<objects>
|
<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">
|
<view key="view" id="DiY-xb-Usb">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="640" height="384"/>
|
<rect key="frame" x="0.0" y="0.0" width="640" height="384"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
|
|
@ -231,7 +231,7 @@ final class CPU: NSObject {
|
||||||
self.branch_was_taken = false
|
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 {
|
func outOfCycles() -> Bool {
|
||||||
|
|
|
@ -34,9 +34,9 @@ class Disassembly: NSObject {
|
||||||
case .relative:
|
case .relative:
|
||||||
var destination: UInt16 = address
|
var destination: UInt16 = address
|
||||||
if((data[1] & 0x80) == 0x80) {
|
if((data[1] & 0x80) == 0x80) {
|
||||||
destination = destination + 1 - UInt16(~data[1])
|
destination = destination &+ 1 &- UInt16(~data[1])
|
||||||
} else {
|
} else {
|
||||||
destination = destination + 2 + UInt16(data[1])
|
destination = destination &+ 2 &+ UInt16(data[1])
|
||||||
}
|
}
|
||||||
return String(format: "%@ #$%04X", instruction!.mnemonic, destination)
|
return String(format: "%@ #$%04X", instruction!.mnemonic, destination)
|
||||||
case .absolute:
|
case .absolute:
|
||||||
|
|
|
@ -580,12 +580,12 @@ final class Opcodes: NSObject {
|
||||||
|
|
||||||
//Misc
|
//Misc
|
||||||
static func JMP(state: CPU, addressingMode: CPU.AddressingMode) -> Void {
|
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 {
|
static func JSR(state: CPU, addressingMode: CPU.AddressingMode) -> Void {
|
||||||
state.pushWord(data: state.program_counter + 2)
|
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 {
|
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 {
|
static func RTI(state: CPU, addressingMode: CPU.AddressingMode) -> Void {
|
||||||
state.status_register.fromByte(state: state.popByte())
|
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
|
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.pushWord(data: state.program_counter + 2)
|
||||||
state.pushByte(data: sr.asByte())
|
state.pushByte(data: sr.asByte())
|
||||||
state.status_register.irq_disable = true //BRK disables interrupts before transferring control
|
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 {}
|
static func NOP(state: CPU, addressingMode: CPU.AddressingMode) -> Void {}
|
||||||
|
|
Loading…
Reference in New Issue