some bugfixes and adaptations for Apple II

This commit is contained in:
Luigi Thirty 2017-08-01 03:28:26 -04:00
parent f6e0467fe1
commit caad9c6ff6
7 changed files with 116 additions and 17 deletions

View File

@ -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)
}
}

View File

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

View File

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

View File

@ -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"/>

View File

@ -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 {

View File

@ -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:

View File

@ -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 {}