mirror of
https://github.com/trudnai/Steve2.git
synced 2024-12-22 06:29:15 +00:00
LoRes Graphics
This commit is contained in:
parent
b9efc3e009
commit
2d58c13376
@ -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 */,
|
||||
|
@ -311,6 +311,23 @@
|
||||
</PersistentString>
|
||||
</PersistentStrings>
|
||||
</ContextState>
|
||||
<ContextState
|
||||
contextName = "LoRes.Update():LoRes.swift">
|
||||
<PersistentStrings>
|
||||
<PersistentString
|
||||
value = "UInt8( (block >> 4) & 0x0F )">
|
||||
</PersistentString>
|
||||
<PersistentString
|
||||
value = "UInt8(block)">
|
||||
</PersistentString>
|
||||
<PersistentString
|
||||
value = "UInt8(block & 4)">
|
||||
</PersistentString>
|
||||
<PersistentString
|
||||
value = "blockChanged[ screenIdx ]">
|
||||
</PersistentString>
|
||||
</PersistentStrings>
|
||||
</ContextState>
|
||||
<ContextState
|
||||
contextName = "CPY:6502_instr_compare_test.h">
|
||||
<PersistentStrings>
|
||||
|
@ -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>
|
||||
|
@ -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
403
A2Mac/LoRes.swift
Normal 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)
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user