LoRes Graphics

This commit is contained in:
tudnai 2020-05-29 08:50:47 -07:00
parent b9efc3e009
commit 2d58c13376
6 changed files with 486 additions and 28 deletions

View File

@ -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 = "<group>"; };
32440B9C247F9DC7000F9DA1 /* blank.woz */ = {isa = PBXFileReference; lastKnownFileType = file; path = blank.woz; sourceTree = "<group>"; };
32440BA0247F9F99000F9DA1 /* LOCKSMITH_V7_REV_B.woz */ = {isa = PBXFileReference; lastKnownFileType = file; path = LOCKSMITH_V7_REV_B.woz; sourceTree = "<group>"; };
32440BA22480D5C0000F9DA1 /* LoRes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoRes.swift; sourceTree = "<group>"; };
325EB62D23F8856F00C6B4A4 /* woz.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = woz.h; sourceTree = "<group>"; };
325EB62E23F8856F00C6B4A4 /* woz.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = woz.c; sourceTree = "<group>"; };
325EB63423F8F78300C6B4A4 /* disk.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = disk.h; sourceTree = "<group>"; };
@ -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 */,

View File

@ -311,6 +311,23 @@
</PersistentString>
</PersistentStrings>
</ContextState>
<ContextState
contextName = "LoRes.Update():LoRes.swift">
<PersistentStrings>
<PersistentString
value = "UInt8( (block &gt;&gt; 4) &amp; 0x0F )">
</PersistentString>
<PersistentString
value = "UInt8(block)">
</PersistentString>
<PersistentString
value = "UInt8(block &amp; 4)">
</PersistentString>
<PersistentString
value = "blockChanged[ screenIdx ]">
</PersistentString>
</PersistentStrings>
</ContextState>
<ContextState
contextName = "CPY:6502_instr_compare_test.h">
<PersistentStrings>

View File

@ -995,7 +995,10 @@
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<customView hidden="YES" canDrawConcurrently="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LlM-EV-ruZ" customClass="HiRes" customModule="A2Mac" customModuleProvider="target">
<customView hidden="YES" canDrawConcurrently="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Gqh-qw-fjy" userLabel="LoRes" customClass="LoRes" customModule="A2Mac" customModuleProvider="target">
<rect key="frame" x="16" y="16" width="1120" height="768"/>
</customView>
<customView hidden="YES" canDrawConcurrently="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LlM-EV-ruZ" userLabel="HiRes" customClass="HiRes" customModule="A2Mac" customModuleProvider="target">
<rect key="frame" x="16" y="16" width="1120" height="768"/>
</customView>
<imageView canDrawConcurrently="YES" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="M1E-lz-4q8" userLabel="Splash Screen">
@ -1221,12 +1224,16 @@
</customView>
</subviews>
<constraints>
<constraint firstItem="Gqh-qw-fjy" firstAttribute="top" secondItem="SEL-hl-0c0" secondAttribute="top" id="FF5-Ke-cOk"/>
<constraint firstAttribute="bottom" secondItem="Fdf-pv-iEw" secondAttribute="bottom" constant="8" id="H1Q-Vj-rPM"/>
<constraint firstItem="iIF-Q5-Onc" firstAttribute="leading" secondItem="m2S-Jp-Qdl" secondAttribute="leading" id="LGA-tZ-gFh"/>
<constraint firstItem="Gqh-qw-fjy" firstAttribute="trailing" secondItem="SEL-hl-0c0" secondAttribute="trailing" id="Mbh-RX-74d"/>
<constraint firstItem="Gqh-qw-fjy" firstAttribute="bottom" secondItem="SEL-hl-0c0" secondAttribute="bottom" id="NJ1-qp-iku"/>
<constraint firstItem="SEL-hl-0c0" firstAttribute="bottom" secondItem="iIF-Q5-Onc" secondAttribute="bottom" constant="-16" id="Q2B-a3-gBp"/>
<constraint firstItem="SEL-hl-0c0" firstAttribute="leading" secondItem="iIF-Q5-Onc" secondAttribute="leading" constant="16" id="QGx-a9-RR9"/>
<constraint firstItem="M1E-lz-4q8" firstAttribute="leading" secondItem="SEL-hl-0c0" secondAttribute="leading" id="SK0-9r-JCV"/>
<constraint firstItem="Fdf-pv-iEw" firstAttribute="top" secondItem="m2S-Jp-Qdl" secondAttribute="top" constant="8" id="Umg-3A-zmv"/>
<constraint firstItem="Gqh-qw-fjy" firstAttribute="leading" secondItem="SEL-hl-0c0" secondAttribute="leading" id="VcW-Wh-wiF"/>
<constraint firstItem="Fdf-pv-iEw" firstAttribute="leading" secondItem="iIF-Q5-Onc" secondAttribute="trailing" constant="8" id="Xbh-Ub-1Ng"/>
<constraint firstItem="M1E-lz-4q8" firstAttribute="bottom" secondItem="SEL-hl-0c0" secondAttribute="bottom" id="aA5-fx-nSB"/>
<constraint firstItem="SEL-hl-0c0" firstAttribute="top" secondItem="iIF-Q5-Onc" secondAttribute="top" constant="16" id="aWz-Ad-PIM"/>
@ -1247,6 +1254,7 @@
<outlet property="display" destination="pIk-RC-s5g" id="Hvd-DI-h6z"/>
<outlet property="displayField" destination="SEL-hl-0c0" id="4Pc-hG-qQf"/>
<outlet property="hires" destination="LlM-EV-ruZ" id="E60-pA-HM1"/>
<outlet property="lores" destination="Gqh-qw-fjy" id="vOE-5m-Jfi"/>
<outlet property="speedometer" destination="FBZ-dh-6Fs" id="L0v-RY-xwB"/>
<outlet property="splashScreen" destination="M1E-lz-4q8" id="xc3-CZ-dRf"/>
</connections>

View File

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

403
A2Mac/LoRes.swift Normal file
View File

@ -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<CVTimeStamp>,
const inOutputTime : UnsafePointer<CVTimeStamp>,
flagsIn : CVOptionFlags,
flagsOut : UnsafeMutablePointer<CVOptionFlags>,
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<CUnsignedChar>.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)
}
}

View File

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