diff --git a/A2Mac.xcodeproj/project.pbxproj b/A2Mac.xcodeproj/project.pbxproj index 9855792..627ce0d 100644 --- a/A2Mac.xcodeproj/project.pbxproj +++ b/A2Mac.xcodeproj/project.pbxproj @@ -40,6 +40,7 @@ 32440B97247F86D6000F9DA1 /* Apple ][ Dealer Service Programs.woz in Resources */ = {isa = PBXBuildFile; fileRef = 32440B8E247F86D6000F9DA1 /* Apple ][ Dealer Service Programs.woz */; }; 32440B9D247F9DC7000F9DA1 /* blank.woz in Resources */ = {isa = PBXBuildFile; fileRef = 32440B9C247F9DC7000F9DA1 /* blank.woz */; }; 32440BA1247F9F99000F9DA1 /* LOCKSMITH_V7_REV_B.woz in Resources */ = {isa = PBXBuildFile; fileRef = 32440BA0247F9F99000F9DA1 /* LOCKSMITH_V7_REV_B.woz */; }; + 32440BA32480D5C0000F9DA1 /* LoRes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32440BA22480D5C0000F9DA1 /* LoRes.swift */; }; 325EB62F23F8856F00C6B4A4 /* woz.c in Sources */ = {isa = PBXBuildFile; fileRef = 325EB62E23F8856F00C6B4A4 /* woz.c */; }; 325EB63623F8F78300C6B4A4 /* disk.c in Sources */ = {isa = PBXBuildFile; fileRef = 325EB63523F8F78300C6B4A4 /* disk.c */; }; 325EB63923F9E48100C6B4A4 /* common.c in Sources */ = {isa = PBXBuildFile; fileRef = 325EB63823F9E48100C6B4A4 /* common.c */; }; @@ -194,6 +195,7 @@ 32440B8E247F86D6000F9DA1 /* Apple ][ Dealer Service Programs.woz */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Apple ][ Dealer Service Programs.woz"; sourceTree = ""; }; 32440B9C247F9DC7000F9DA1 /* blank.woz */ = {isa = PBXFileReference; lastKnownFileType = file; path = blank.woz; sourceTree = ""; }; 32440BA0247F9F99000F9DA1 /* LOCKSMITH_V7_REV_B.woz */ = {isa = PBXFileReference; lastKnownFileType = file; path = LOCKSMITH_V7_REV_B.woz; sourceTree = ""; }; + 32440BA22480D5C0000F9DA1 /* LoRes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoRes.swift; sourceTree = ""; }; 325EB62D23F8856F00C6B4A4 /* woz.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = woz.h; sourceTree = ""; }; 325EB62E23F8856F00C6B4A4 /* woz.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = woz.c; sourceTree = ""; }; 325EB63423F8F78300C6B4A4 /* disk.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = disk.h; sourceTree = ""; }; @@ -551,6 +553,7 @@ 32B18435233F10BC00DBB4AB /* Shaders.metal */, 32C4532D233345420000EBA1 /* MonitorView.swift */, 32C453072331C0910000EBA1 /* NSLayoutManager-Extension.swift */, + 32440BA22480D5C0000F9DA1 /* LoRes.swift */, 32DBF7632334657900DD50E7 /* HiRes.swift */, 32EDB7A123272CA80073AF2D /* fail1.txt */, 32BFFB5E22EACC660003B53F /* Assets.xcassets */, @@ -961,6 +964,7 @@ 32BFFB5D22EACC630003B53F /* ViewController.swift in Sources */, 325EB69323FE6C6200C6B4A4 /* HiRes.swift in Sources */, 32C4532E233345430000EBA1 /* MonitorView.swift in Sources */, + 32440BA32480D5C0000F9DA1 /* LoRes.swift in Sources */, 32440B84247E27D3000F9DA1 /* 6502.c in Sources */, 325EB62F23F8856F00C6B4A4 /* woz.c in Sources */, 32BFFB5B22EACC630003B53F /* AppDelegate.swift in Sources */, diff --git a/A2Mac.xcodeproj/project.xcworkspace/xcuserdata/trudnai.xcuserdatad/xcdebugger/Expressions.xcexplist b/A2Mac.xcodeproj/project.xcworkspace/xcuserdata/trudnai.xcuserdatad/xcdebugger/Expressions.xcexplist index 990f74d..07e8c6a 100644 --- a/A2Mac.xcodeproj/project.xcworkspace/xcuserdata/trudnai.xcuserdatad/xcdebugger/Expressions.xcexplist +++ b/A2Mac.xcodeproj/project.xcworkspace/xcuserdata/trudnai.xcuserdatad/xcdebugger/Expressions.xcexplist @@ -311,6 +311,23 @@ + + + + + + + + + + + + diff --git a/A2Mac/Base.lproj/Main.storyboard b/A2Mac/Base.lproj/Main.storyboard index 1575cbb..e812a12 100644 --- a/A2Mac/Base.lproj/Main.storyboard +++ b/A2Mac/Base.lproj/Main.storyboard @@ -995,7 +995,10 @@ - + + + + @@ -1247,6 +1254,7 @@ + diff --git a/A2Mac/HiRes.swift b/A2Mac/HiRes.swift index 95d3f57..f50aef9 100644 --- a/A2Mac/HiRes.swift +++ b/A2Mac/HiRes.swift @@ -494,7 +494,7 @@ class HiRes: NSView { let color_yellow : UInt32 = 0xFFBBBB11; - func hiresColorPixel ( pixelAddr : Int, pixel : Int, prev : Int ) { + func colorPixel ( pixelAddr : Int, pixel : Int, prev : Int ) { let colorAddr = pixelAddr / 4 switch ( pixel ) { @@ -647,20 +647,20 @@ class HiRes: NSView { for px in 0 ... 2 { // let bitMask = 3 << ( px * 2 ) let pixel = blockH7 | ( (block >> (px * 2)) & 3 ) - hiresColorPixel(pixelAddr: pixelAddr, pixel: pixel, prev: prev ) + colorPixel(pixelAddr: pixelAddr, pixel: pixel, prev: prev ) pixelAddr += 8 prev = pixel } let pixel = (blockL7 | blockH7) | ( (block >> (3 * 2)) & 3 ) - hiresColorPixel(pixelAddr: pixelAddr, pixel: pixel, prev: prev ) + colorPixel(pixelAddr: pixelAddr, pixel: pixel, prev: prev ) pixelAddr += 8 prev = pixel for px in 4 ... 6 { // let bitMask = 3 << ( px * 2 ) let pixel = blockL7 | ( (block >> (px * 2)) & 3 ) - hiresColorPixel(pixelAddr: pixelAddr, pixel: pixel, prev: prev ) + colorPixel(pixelAddr: pixelAddr, pixel: pixel, prev: prev ) pixelAddr += 8 prev = pixel } diff --git a/A2Mac/LoRes.swift b/A2Mac/LoRes.swift new file mode 100644 index 0000000..2cfd66c --- /dev/null +++ b/A2Mac/LoRes.swift @@ -0,0 +1,403 @@ +// +// LoRes.swift +// A2Mac +// +// Created by Tamas Rudnai on 9/19/19. +// Copyright © 2019 GameAlloy. All rights reserved. +// + +//import Foundation +import AppKit + +class LoRes: NSView { + + static let PageSize = 0x400 + static let Page1Addr = 0x400 + static let Page2Addr = 0x800 + + static let PixelWidth = 40 + static let PixelMixedHeight = 40 + static let PixelHeight = 48 + static let MixedTextHeight = 4 + static let blockRows = 24 + static let blockCols = 40 + static let blockWidth = PixelWidth / blockCols + static let blockHeight = PixelHeight / blockRows + + let LoResBuffer1 = UnsafeRawBufferPointer(start: MEM + Page1Addr, count: PageSize * 2) + let LoResBuffer2 = UnsafeRawBufferPointer(start: MEM + Page2Addr, count: PageSize * 2) + var LoResBufferPointer = UnsafeRawBufferPointer(start: MEM + Page1Addr, count: PageSize * 2) + + let LoResRawPointer = UnsafeRawPointer(RAM + Page1Addr) + + // holds the starting addresses for each lines minus the screen page starting address + var LoResLineAddrTbl = [Int](repeating: 0, count: PixelHeight * 4) + + func initLoResLineAddresses() { + var i = 0 + for x in stride(from: 0, through: 0x50, by: 0x28) { + for y in stride(from: 0, through: 0x380, by: 0x80) { + for z in stride(from: 0, through: 0x1C00, by: 0x400) { + LoResLineAddrTbl[i] = x + y + z + i += 1 + } + } + } + } + + + var LoResSubView = [[NSView]]() + + func createLoRes() { + for y in 0 ..< LoRes.blockRows { + LoResSubView.append([NSView]()) + for x in 0 ..< LoRes.blockCols { + let blockView = NSView(frame: NSRect(x: x * LoRes.blockWidth, y: y * 8, width: LoRes.blockWidth, height: 8)) + LoResSubView[y].append(blockView) + self.addSubview(blockView) + } + } + } + + + func clearScreen() { + LoRes.context?.clear( CGRect(x: 0, y: 0, width: frame.width, height: frame.height) ) + needsDisplay = true + } + + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + initLoResLineAddresses() + clearScreen() + +// currentContext?.setShouldAntialias(false) +// currentContext?.interpolationQuality = CGInterpolationQuality.none + +// let scaleSizeW = Double((frame.size).width) / Double(LoRes.PixelWidth) +// let scaleSizeH = Double((frame.size).height) / Double(LoRes.PixelHeight) + +// let scaleSizeW = 4 +// let scaleSizeH = 4 +// scaleUnitSquare(to: NSSize(width: scaleSizeW, height: scaleSizeH)) + + // create smaller box views for draw optimization + createLoRes() + + } + + override init(frame: CGRect) { + super.init(frame: frame) + } + + + + func renderCallback(displayLink : CVDisplayLink, + const inNow : UnsafePointer, + const inOutputTime : UnsafePointer, + flagsIn : CVOptionFlags, + flagsOut : UnsafeMutablePointer, + displayLinkContext : UnsafeMutableRawPointer) -> CVReturn + { + /* It's prudent to also have a brief discussion about the CVTimeStamp. + CVTimeStamp has five properties. Three of the five are very useful + for keeping track of the current time, calculating delta time, the + frame number, and the number of frames per second. The utility of + each property is not terribly obvious from just reading the names + or the descriptions in the Developer dcumentation and has been a + mystery to many a developer. Thankfully, CaptainRedmuff on + StackOverflow asked a question that provided the equation that + calculates frames per second. From that equation, we can + extrapolate the value of each field. + + @hostTime = current time in Units of the "root". Yeah, I don't know. + The key to this field is to understand that it is in nanoseconds + (e.g. 1/1_000_000_000 of a second) not units. To convert it to + seconds divide by 1_000_000_000. Dividing by videoRefreshPeriod + and videoTimeScale in a calculation for frames per second yields + the appropriate number of frames. This works as a result of + proportionality--dividing seconds by seconds. Note that dividing + by videoTimeScale to get the time in seconds does not work like it + does for videoTime. + + framesPerSecond: + (videoTime / videoRefreshPeriod) / (videoTime / videoTimeScale) = 59 + and + (hostTime / videoRefreshPeriod) / (hostTime / videoTimeScale) = 59 + but + hostTime * videoTimeScale ≠ seconds, but Units = seconds * (Units / seconds) = Units + + @rateScalar = ratio of "rate of device in CVTimeStamp/unitOfTime" to + the "Nominal Rate". I think the "Nominal Rate" is + videoRefreshPeriod, but unfortunately, the documentation doesn't + just say videoRefreshPeriod is the Nominal rate and then define + what that means. Regardless, because this is a ratio, and the fact + that we know the value of one of the parts (e.g. Units/frame), we + then know that the "rate of the device" is frame/Units (the units of + measure need to cancel out for the ratio to be a ratio). This + makes sense in that rateScalar's definition tells us the rate is + "measured by timeStamps". Since there is a frame for every + timeStamp, the rate of the device equals CVTimeStamp/Unit or + frame/Unit. Thus, + + rateScalar = frame/Units : Units/frame + + @videoTime = the time the frame was created since computer started up. + If you turn your computer off and then turn it back on, this timer + returns to zero. The timer is paused when you put your computer to + sleep. This value is in Units not seconds. To get the number of + seconds this value represents, you have to apply videoTimeScale. + + @videoRefreshPeriod = the number of Units per frame (i.e. Units/frame) + This is useful in calculating the frame number or frames per second. + The documentation calls this the "nominal update period" and I am + pretty sure that is quivalent to the aforementioned "nominal rate". + Unfortunately, the documetation mixes naming conventions and this + inconsistency creates confusion. + + frame = videoTime / videoRefreshPeriod + + @videoTimeScale = Units/second, used to convert videoTime into seconds + and may also be used with videoRefreshPeriod to calculate the expected + framesPerSecond. I say expected, because videoTimeScale and + videoRefreshPeriod don't change while videoTime does change. Thus, + to calculate fps in the case of system slow down, one would need to + use videoTime with videoTimeScale to calculate the actual fps value. + + seconds = videoTime / videoTimeScale + + framesPerSecondConstant = videoTimeScale / videoRefreshPeriod (this value does not change if their is system slowdown) + + USE CASE 1: Time in DD:HH:mm:ss using hostTime + let rootTotalSeconds = inNow.pointee.hostTime + let rootDays = inNow.pointee.hostTime / (1_000_000_000 * 60 * 60 * 24) % 365 + let rootHours = inNow.pointee.hostTime / (1_000_000_000 * 60 * 60) % 24 + let rootMinutes = inNow.pointee.hostTime / (1_000_000_000 * 60) % 60 + let rootSeconds = inNow.pointee.hostTime / 1_000_000_000 % 60 + Swift.print("rootTotalSeconds: \(rootTotalSeconds) rootDays: \(rootDays) rootHours: \(rootHours) rootMinutes: \(rootMinutes) rootSeconds: \(rootSeconds)") + + USE CASE 2: Time in DD:HH:mm:ss using videoTime + let totalSeconds = inNow.pointee.videoTime / Int64(inNow.pointee.videoTimeScale) + let days = (totalSeconds / (60 * 60 * 24)) % 365 + let hours = (totalSeconds / (60 * 60)) % 24 + let minutes = (totalSeconds / 60) % 60 + let seconds = totalSeconds % 60 + Swift.print("totalSeconds: \(totalSeconds) Days: \(days) Hours: \(hours) Minutes: \(minutes) Seconds: \(seconds)") + + Swift.print("fps: \(Double(inNow.pointee.videoTimeScale) / Double(inNow.pointee.videoRefreshPeriod)) seconds: \(Double(inNow.pointee.videoTime) / Double(inNow.pointee.videoTimeScale))") + */ + + /* The displayLinkContext in CVDisplayLinkOutputCallback's parameter list is the + view being driven by the CVDisplayLink. In order to use the context as an + instance of SwiftOpenGLView (which has our drawView() method) we need to use + unsafeBitCast() to cast this context to a SwiftOpenGLView. + */ + +// let view = unsafeBitCast(displayLinkContext, to: SwiftOpenGLView.self) +// // Capture the current time in the currentTime property. +// view.currentTime = inNow.pointee.videoTime / Int64(inNow.pointee.videoTimeScale) +// view.drawView() + +// self.render() + + return kCVReturnSuccess + } + + static func createBitmapContext(pixelsWide: Int, _ pixelsHigh: Int) -> CGContext? { + let bytesPerPixel = 4 + let bytesPerRow = bytesPerPixel * pixelsWide + + let byteCount = (bytesPerRow * pixelsHigh) + +// guard let colorSpace = CGColorSpace(name: CGColorSpace.linearSRGB) else { return nil } + guard let colorSpace = CGColorSpace(name: CGColorSpace.genericRGBLinear) else { return nil } + + let pixels = UnsafeMutablePointer.allocate(capacity: byteCount) + + let bitmapInfo = CGImageAlphaInfo.premultipliedFirst.rawValue | CGBitmapInfo.byteOrder32Little.rawValue + + let context = CGContext( + data: pixels, + width: pixelsWide, + height: pixelsHigh, + bitsPerComponent: 8, + bytesPerRow: bytesPerRow, + space: colorSpace, + bitmapInfo: bitmapInfo) + + return context + } + + + private var currentContext : CGContext? { + get { + if #available(OSX 10.10, *) { + return NSGraphicsContext.current?.cgContext + } else if let contextPointer = NSGraphicsContext.current?.graphicsPort { + let context: CGContext = Unmanaged.fromOpaque(contextPointer).takeUnretainedValue() + return context + } + + return nil + } + } + + static let ScreenBitmapSize = (PixelWidth * PixelHeight * 4) + static let context = createBitmapContext(pixelsWide: PixelWidth, PixelHeight) + static let pixels = UnsafeMutableRawBufferPointer(start: context?.data, count: ScreenBitmapSize) + static var pixelsRGBA = pixels.bindMemory(to: UInt32.self) + + let R = 2 + let G = 1 + let B = 0 + let A = 3 + + var blockChanged = [Bool](repeating: false, count: LoRes.blockRows * LoRes.blockCols) + var shadowScreen = [Int](repeating: 0, count: PageSize) + + var was = 0; + + + static let color_black : UInt32 = 0x00000000; // 0 + static let color_magenta : UInt32 = 0xFF660022; // 1 + static let color_dark_blue : UInt32 = 0xFF000077; // 2 + static let color_purple : UInt32 = 0xFF9908DD; // 3 + static let color_dark_green : UInt32 = 0xFF005500; // 4 + static let color_dark_gray : UInt32 = 0xFF333333; // 5 + static let color_medium_blue : UInt32 = 0xFF0011BB; // 6 + static let color_light_blue : UInt32 = 0xFF4488FF; // 7 + static let color_brown : UInt32 = 0xFF552200; // 8 + static let color_orange : UInt32 = 0xFFFF6611; // 9 + static let color_gray : UInt32 = 0xFF888888; // 10 + static let color_pink : UInt32 = 0xFFFF8888; // 11 + static let color_green : UInt32 = 0xFF0BBB11; // 12 + static let color_yellow : UInt32 = 0xFFFFFF00; // 13 + static let color_aqua : UInt32 = 0xFF66CC99; // 14 + static let color_white : UInt32 = 0xFFEEEEEE; // 15 + + let colorTable = [ + color_black, + color_magenta, + color_dark_blue, + color_purple, + color_dark_green, + color_dark_gray, + color_medium_blue, + color_light_blue, + color_brown, + color_orange, + color_gray, + color_pink, + color_green, + color_yellow, + color_aqua, + color_white + ] + + // for debugging only: + let color_turquis : UInt32 = 0xFF11BBBB; + let color_blue : UInt32 = 0xFF1155FF; + + + func colorPixel ( pixelAddr : Int, color : Int ) { + LoRes.pixelsRGBA[pixelAddr] = colorTable[color] + } + + + func Update() { + var height = LoRes.PixelHeight / 2 + + // do not even render it... + if videoMode.text == 1 { + return + } + else { + if videoMode.mixed == 1 { + height = LoRes.PixelMixedHeight / 2 + } + if MEMcfg.txt_page_2 == 1 { + LoResBufferPointer = LoResBuffer2 + } + else { + LoResBufferPointer = LoResBuffer1 + } + } + + var y = 0 + + blockChanged = [Bool](repeating: false, count: LoRes.blockRows * LoRes.blockCols) + + LoRes.context?.clear( CGRect(x: 0, y: 0, width: frame.width, height: frame.height) ) + + for lineAddr in ViewController.textLineOfs { + + if ( height <= 0 ) { + break + } + height -= 1 + + let blockVertIdx = y * LoRes.blockCols + + for blockHorIdx in 0 ..< LoRes.blockCols { +// print("blockVertIdx:", blockVertIdx, " blockHorIdx:", blockHorIdx) + + let block = Int(LoResBufferPointer[ Int(lineAddr + blockHorIdx) ]) + + let screenIdx = blockVertIdx + blockHorIdx + let pixelHAddr = blockVertIdx * 2 + blockHorIdx + let pixelLAddr = pixelHAddr + LoRes.blockCols + + // get all changed blocks + blockChanged[ screenIdx ] = blockChanged[ screenIdx ] || shadowScreen[ screenIdx ] != block + shadowScreen[ screenIdx ] = block + + colorPixel(pixelAddr: pixelHAddr, color: block & 0x0F ) + colorPixel(pixelAddr: pixelLAddr, color: (block >> 4) & 0x0F ) + + } + y += 1 + + if ( y >= LoRes.PixelHeight ) { + break + } + } + + + // refresh changed block only + + let screenBlockMargin = 6 + + let blockScreenWidth = Int(frame.width) / LoRes.blockCols + let blockScreenHeigth = Int(frame.height) / LoRes.blockRows + + for blockVertIdx in 0 ..< LoRes.blockRows { + for blockHorIdx in 0 ..< LoRes.blockCols { + if blockChanged[ blockVertIdx * LoRes.blockCols + blockHorIdx ] { + // refresh the entire screen + let boundingBox = CGRect( + x: blockHorIdx * blockScreenWidth - screenBlockMargin, + y: Int(frame.height) - blockVertIdx * blockScreenHeigth - blockScreenHeigth - screenBlockMargin, + width: blockScreenWidth + screenBlockMargin * 2, + height: blockScreenHeigth + screenBlockMargin * 2) + + self.setNeedsDisplay( boundingBox ) + } + } + } + +// needsDisplay = true // refresh the entire screen + + } + + override func draw(_ rect: CGRect) { + guard let image = LoRes.context?.makeImage() else { return } + + // refresh the entire screen + let boundingBox = CGRect(x: 0, y: 0, width: frame.width, height: frame.height) + currentContext?.interpolationQuality = .none + currentContext?.draw(image, in: boundingBox) + } + + + +} diff --git a/A2Mac/ViewController.swift b/A2Mac/ViewController.swift index 8cffd07..05091e7 100644 --- a/A2Mac/ViewController.swift +++ b/A2Mac/ViewController.swift @@ -49,6 +49,7 @@ class ViewController: NSViewController { @IBOutlet weak var displayField: NSTextField! @IBOutlet weak var display: NSTextFieldCell! @IBOutlet weak var speedometer: NSTextFieldCell! + @IBOutlet weak var lores: LoRes! @IBOutlet weak var hires: HiRes! @IBOutlet weak var splashScreen: NSImageView! @@ -95,7 +96,7 @@ class ViewController: NSViewController { static var romFileName = "Apple2e_Enhanced.rom"; - let textLineOfs : [Int] = [ + static let textLineOfs : [Int] = [ 0x000, 0x080, 0x100, 0x180, 0x200, 0x280, 0x300, 0x380, 0x028, 0x0A8, 0x128, 0x1A8, 0x228, 0x2A8, 0x328, 0x3A8, 0x050, 0x0D0, 0x150, 0x1D0, 0x250, 0x2D0, 0x350, 0x3D0 ] @@ -197,6 +198,10 @@ class ViewController: NSViewController { static let textIntBufferPointer = UnsafeRawBufferPointer(start: RAM + textPage1Addr, count: textBufferSize) static let textAuxBufferPointer = UnsafeRawBufferPointer(start: AUX + textPage1Addr, count: textBufferSize) + // TODO: Render text screen in native C +// static let textScreen = UnsafeMutableRawPointer(mutating: testText) + + var textBufferPointer = textPage1Pointer static let textArraySize = textLines * (textCols + lineEndChars) + textCols * 2 @@ -552,7 +557,7 @@ class ViewController: NSViewController { // render the rest of the text screen for y in fromLines ..< toLines { for x in 0 ..< self.textCols { - let byte = self.textBufferPointer[ self.textLineOfs[y] + x ] + let byte = self.textBufferPointer[ ViewController.textLineOfs[y] + x ] let idx = Int(byte); let chr = ViewController.charConvTbl[idx] @@ -572,13 +577,13 @@ class ViewController: NSViewController { // render the rest of the text screen for y in fromLines ..< toLines { for x in 0 ..< self.textCols { - let byte = textIntBuffer[ self.textLineOfs[y] + x ] + let byte = textIntBuffer[ ViewController.textLineOfs[y] + x ] let idx = Int(byte); let chr = ViewController.charConvTbl[idx] self.txtArr[ y * (self.textCols * 2 + self.lineEndChars) + x * 2 + 1] = chr - let byte2 = textAuxBuffer[ self.textLineOfs[y] + x ] + let byte2 = textAuxBuffer[ ViewController.textLineOfs[y] + x ] let idx2 = Int(byte2); let chr2 = ViewController.charConvTbl[idx2] @@ -592,6 +597,9 @@ class ViewController: NSViewController { txt = String(self.txtArr) + // TODO: Render text Screen in native C +// txt = String(bytesNoCopy: ViewController.textScreen!, length: 10, encoding: .ascii, freeWhenDone: false) ?? "HMM" + if videoMode.col80 != self.currentVideoMode.col80 { self.currentVideoMode.col80 = videoMode.col80 @@ -625,18 +633,36 @@ class ViewController: NSViewController { // only refresh graphics view when needed (aka not in text mode) if ( videoMode.text == 0 ) { - // when we change video mode, buffer needs to be cleared to avoid artifacts - if ( self.savedVideoMode.text == 1 ) - || ( self.savedVideoMode.mixed != videoMode.mixed ) - { - self.hires.clearScreen() - self.hires.isHidden = false - } + if ( videoMode.hires == 0 ) { + // when we change video mode, buffer needs to be cleared to avoid artifacts + if ( self.savedVideoMode.text == 1 ) + || ( self.savedVideoMode.mixed != videoMode.mixed ) + || ( self.savedVideoMode.hires != videoMode.hires ) + { + self.lores.clearScreen() + self.lores.isHidden = false + self.hires.isHidden = true + } - self.hires.Update() + self.lores.Update() + } + else { + // when we change video mode, buffer needs to be cleared to avoid artifacts + if ( self.savedVideoMode.text == 1 ) + || ( self.savedVideoMode.mixed != videoMode.mixed ) + || ( self.savedVideoMode.hires != videoMode.hires ) + { + self.hires.clearScreen() + self.hires.isHidden = false + self.lores.isHidden = true + } + + self.hires.Update() + } } else if ( self.savedVideoMode.text == 0 ) { // we just switched from grahics to text + self.lores.isHidden = true self.hires.isHidden = true } @@ -648,23 +674,23 @@ class ViewController: NSViewController { // TODO: Do we need to do this from here? // spkr_update() + // Mouse 2 JoyStick (Game Controller / Paddle) + mouseLocation = view.window!.mouseLocationOutsideOfEventStream + + pdl_prevarr[2] = pdl_valarr[2] + pdl_valarr[2] = Double(mouseLocation.x / (displayField.frame.width) ) + pdl_diffarr[2] = pdl_valarr[2] - pdl_prevarr[2] + + pdl_prevarr[3] = pdl_valarr[3] + pdl_valarr[3] = 1 - Double(mouseLocation.y / (displayField.frame.height) ) + pdl_diffarr[3] = pdl_valarr[3] - pdl_prevarr[3] + } #if SPEEDTEST #else if ( !halted ) { - // Mouse 2 JoyStick (Game Controller / Paddle) - mouseLocation = view.window!.mouseLocationOutsideOfEventStream - - pdl_prevarr[2] = pdl_valarr[2] - pdl_valarr[2] = Double(mouseLocation.x / (displayField.frame.width) ) - pdl_diffarr[2] = pdl_valarr[2] - pdl_prevarr[2] - - pdl_prevarr[3] = pdl_valarr[3] - pdl_valarr[3] = 1 - Double(mouseLocation.y / (displayField.frame.height) ) - pdl_diffarr[3] = pdl_valarr[3] - pdl_prevarr[3] - m6502_Run() } #endif