2019-09-22 08:31:09 +00:00
|
|
|
//
|
|
|
|
// HiRes.swift
|
2020-07-13 17:23:33 +00:00
|
|
|
// Steve ][
|
2019-09-22 08:31:09 +00:00
|
|
|
//
|
|
|
|
// Created by Tamas Rudnai on 9/19/19.
|
2020-07-13 17:16:37 +00:00
|
|
|
// Copyright © 2019, 2020 Tamas Rudnai. All rights reserved.
|
2020-07-13 17:10:33 +00:00
|
|
|
//
|
|
|
|
// This file is part of Steve ][ -- The Apple ][ Emulator.
|
|
|
|
//
|
|
|
|
// Steve ][ is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// Steve ][ is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with Steve ][. If not, see <https://www.gnu.org/licenses/>.
|
2019-09-22 08:31:09 +00:00
|
|
|
//
|
|
|
|
|
|
|
|
//import Foundation
|
|
|
|
import AppKit
|
|
|
|
|
|
|
|
class HiRes: NSView {
|
2024-01-06 15:11:52 +00:00
|
|
|
|
2019-09-22 08:31:09 +00:00
|
|
|
static let PageSize = 0x2000
|
|
|
|
static let Page1Addr = 0x2000
|
|
|
|
static let Page2Addr = 0x4000
|
|
|
|
|
|
|
|
static let PixelWidth = 280
|
|
|
|
static let PixelMixedHeight = 160
|
|
|
|
static let PixelHeight = 192
|
2020-02-23 00:37:54 +00:00
|
|
|
static let MixedTextHeight = 4
|
|
|
|
static let MixedHeight = 160
|
2019-11-28 04:27:32 +00:00
|
|
|
static let blockRows = 24
|
|
|
|
static let blockCols = 40
|
|
|
|
static let blockWidth = PixelWidth / blockCols
|
|
|
|
static let blockHeight = PixelHeight / blockRows
|
2019-09-22 08:31:09 +00:00
|
|
|
|
2020-05-02 01:56:30 +00:00
|
|
|
let HiResBuffer1 = UnsafeRawBufferPointer(start: MEM + Page1Addr, count: PageSize * 2)
|
|
|
|
let HiResBuffer2 = UnsafeRawBufferPointer(start: MEM + Page2Addr, count: PageSize * 2)
|
|
|
|
var HiResBufferPointer = UnsafeRawBufferPointer(start: MEM + Page1Addr, count: PageSize * 2)
|
2020-02-23 00:37:54 +00:00
|
|
|
|
2019-11-28 04:27:32 +00:00
|
|
|
let HiResRawPointer = UnsafeRawPointer(RAM + Page1Addr)
|
|
|
|
|
2019-09-22 08:31:09 +00:00
|
|
|
// holds the starting addresses for each lines minus the screen page starting address
|
2019-11-28 04:27:32 +00:00
|
|
|
var HiResLineAddrTbl = [Int](repeating: 0, count: PixelHeight)
|
2019-09-22 08:31:09 +00:00
|
|
|
|
|
|
|
func initHiResLineAddresses() {
|
|
|
|
var i = 0
|
2019-11-28 04:27:32 +00:00
|
|
|
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) {
|
2019-09-22 08:31:09 +00:00
|
|
|
HiResLineAddrTbl[i] = x + y + z
|
|
|
|
i += 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-28 04:27:32 +00:00
|
|
|
|
|
|
|
var HiResSubView = [[NSView]]()
|
2024-01-06 15:11:52 +00:00
|
|
|
|
2019-11-28 04:27:32 +00:00
|
|
|
func createHiRes() {
|
|
|
|
for y in 0 ..< HiRes.blockRows {
|
|
|
|
HiResSubView.append([NSView]())
|
|
|
|
for x in 0 ..< HiRes.blockCols {
|
|
|
|
let blockView = NSView(frame: NSRect(x: x * HiRes.blockWidth, y: y * 8, width: HiRes.blockWidth, height: 8))
|
|
|
|
HiResSubView[y].append(blockView)
|
|
|
|
self.addSubview(blockView)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-05-06 06:03:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
func clearScreen() {
|
|
|
|
HiRes.context?.clear( CGRect(x: 0, y: 0, width: frame.width, height: frame.height) )
|
|
|
|
needsDisplay = true
|
|
|
|
}
|
2019-11-28 04:27:32 +00:00
|
|
|
|
|
|
|
|
2019-09-22 08:31:09 +00:00
|
|
|
required init?(coder aDecoder: NSCoder) {
|
2024-01-06 15:11:52 +00:00
|
|
|
pixelsSRGB = HiRes.context?.data?.bindMemory(to: UInt32.self, capacity: HiRes.ScreenBitmapSize)
|
|
|
|
|
2019-09-22 08:31:09 +00:00
|
|
|
super.init(coder: aDecoder)
|
|
|
|
initHiResLineAddresses()
|
2020-05-06 06:03:36 +00:00
|
|
|
clearScreen()
|
2020-05-05 15:35:57 +00:00
|
|
|
|
2020-02-28 08:28:22 +00:00
|
|
|
// currentContext?.setShouldAntialias(false)
|
|
|
|
// currentContext?.interpolationQuality = CGInterpolationQuality.none
|
2020-02-26 07:54:08 +00:00
|
|
|
|
2019-11-28 04:27:32 +00:00
|
|
|
// let scaleSizeW = Double((frame.size).width) / Double(HiRes.PixelWidth)
|
|
|
|
// let scaleSizeH = Double((frame.size).height) / Double(HiRes.PixelHeight)
|
2020-04-23 02:18:28 +00:00
|
|
|
|
|
|
|
// let scaleSizeW = 4
|
|
|
|
// let scaleSizeH = 4
|
|
|
|
// scaleUnitSquare(to: NSSize(width: scaleSizeW, height: scaleSizeH))
|
2019-09-22 08:31:09 +00:00
|
|
|
|
2019-11-28 04:27:32 +00:00
|
|
|
// create smaller box views for draw optimization
|
2020-02-25 08:14:05 +00:00
|
|
|
createHiRes()
|
2019-11-28 04:27:32 +00:00
|
|
|
|
2019-09-22 08:31:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
override init(frame: CGRect) {
|
|
|
|
super.init(frame: frame)
|
|
|
|
}
|
|
|
|
|
2020-02-23 00:37:54 +00:00
|
|
|
|
2019-09-22 08:31:09 +00:00
|
|
|
static func createBitmapContext(pixelsWide: Int, _ pixelsHigh: Int) -> CGContext? {
|
|
|
|
let bytesPerPixel = 4
|
|
|
|
let bytesPerRow = bytesPerPixel * pixelsWide
|
|
|
|
|
|
|
|
let byteCount = (bytesPerRow * pixelsHigh)
|
|
|
|
|
2019-11-28 04:27:32 +00:00
|
|
|
// guard let colorSpace = CGColorSpace(name: CGColorSpace.linearSRGB) else { return nil }
|
2020-06-11 00:48:37 +00:00
|
|
|
// guard let colorSpace = CGColorSpace(name: CGColorSpace.genericRGBLinear) else { return nil }
|
|
|
|
guard let colorSpace = CGColorSpace(name: CGColorSpace.sRGB) else { return nil }
|
2019-11-28 04:27:32 +00:00
|
|
|
|
2019-09-22 08:31:09 +00:00
|
|
|
let pixels = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: byteCount)
|
|
|
|
|
|
|
|
let bitmapInfo = CGImageAlphaInfo.premultipliedFirst.rawValue | CGBitmapInfo.byteOrder32Little.rawValue
|
|
|
|
|
2019-11-28 04:27:32 +00:00
|
|
|
let context = CGContext(
|
|
|
|
data: pixels,
|
|
|
|
width: pixelsWide,
|
|
|
|
height: pixelsHigh,
|
|
|
|
bitsPerComponent: 8,
|
|
|
|
bytesPerRow: bytesPerRow,
|
|
|
|
space: colorSpace,
|
|
|
|
bitmapInfo: bitmapInfo)
|
2019-09-22 08:31:09 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-06 15:11:52 +00:00
|
|
|
static let ScreenBitmapSize = (PixelWidth * 2 * PixelHeight * 4)
|
|
|
|
static let context = createBitmapContext(pixelsWide: PixelWidth * 2, PixelHeight)
|
2020-05-04 20:54:04 +00:00
|
|
|
static let pixels = UnsafeMutableRawBufferPointer(start: context?.data, count: ScreenBitmapSize)
|
2024-01-06 15:11:52 +00:00
|
|
|
// static var pixelsSRGB = pixels.bindMemory(to: UInt32.self)
|
2019-09-22 08:31:09 +00:00
|
|
|
|
|
|
|
let R = 2
|
|
|
|
let G = 1
|
|
|
|
let B = 0
|
|
|
|
let A = 3
|
|
|
|
|
2024-01-06 15:11:52 +00:00
|
|
|
// var blockChanged = [Bool](repeating: false, count: HiRes.blockRows * HiRes.blockCols)
|
2020-04-23 02:18:28 +00:00
|
|
|
var shadowScreen = [Int](repeating: 0, count: PageSize)
|
2019-09-22 08:31:09 +00:00
|
|
|
|
2019-11-28 04:27:32 +00:00
|
|
|
var was = 0;
|
|
|
|
|
2020-02-25 08:14:05 +00:00
|
|
|
|
2020-06-21 01:46:26 +00:00
|
|
|
// let color_black : UInt32 = 0x00000000;
|
|
|
|
// let color_white : UInt32 = 0xEEEEEEEE;
|
|
|
|
// let color_purple : UInt32 = 0xFFBB11EE;
|
|
|
|
// let color_green : UInt32 = 0xFF0BBB11;
|
|
|
|
// let color_blue : UInt32 = 0xFF1155FF;
|
|
|
|
// let color_orange : UInt32 = 0xFFEE2211;
|
2020-05-04 20:54:04 +00:00
|
|
|
|
2020-06-11 00:48:37 +00:00
|
|
|
// HiRes Colors for the SRGB color space
|
2020-08-02 22:00:42 +00:00
|
|
|
let color_black : UInt32 = 0x00000000
|
|
|
|
let color_white : UInt32 = 0xFFEEEEEE
|
|
|
|
let color_purple : UInt32 = 0xFFDD55FF
|
|
|
|
let color_green : UInt32 = 0xFF2BD84A
|
|
|
|
let color_blue : UInt32 = 0xFF5599FF
|
|
|
|
let color_orange : UInt32 = 0xFFFF6302
|
2020-06-21 01:46:26 +00:00
|
|
|
|
2020-05-25 03:26:54 +00:00
|
|
|
// for debugging only:
|
2020-08-02 22:00:42 +00:00
|
|
|
let color_turquis : UInt32 = 0xFF11BBBB
|
|
|
|
let color_yellow : UInt32 = 0xFFBBBB11
|
2020-06-21 01:46:26 +00:00
|
|
|
|
2020-06-21 05:14:39 +00:00
|
|
|
// default is green
|
2020-08-02 22:00:42 +00:00
|
|
|
var monoColor : UInt32 = 0xFF2BD84A
|
2020-06-21 05:14:39 +00:00
|
|
|
|
2024-01-06 15:11:52 +00:00
|
|
|
static let refreshUnderscan: CGFloat = 1;
|
|
|
|
static let refreshOverscan : CGFloat = refreshUnderscan * 2;
|
2020-06-21 01:46:26 +00:00
|
|
|
|
|
|
|
func refreshChanged( blockSize : Int ) {
|
|
|
|
// refresh changed block only
|
|
|
|
|
2024-01-06 15:11:52 +00:00
|
|
|
let screenBlockMargin = 16 / blockSize
|
2020-06-21 01:46:26 +00:00
|
|
|
|
2024-01-06 15:11:52 +00:00
|
|
|
let blockScreenWidth = CGFloat(frame.width) / CGFloat(HiRes.blockCols) * CGFloat(blockSize)
|
|
|
|
let blockScreenHeigth = CGFloat(frame.height) / CGFloat(HiRes.blockRows)
|
2020-06-21 01:46:26 +00:00
|
|
|
|
|
|
|
for blockVertIdx in 0 ..< HiRes.blockRows {
|
|
|
|
for blockHorIdx in 0 ..< HiRes.blockCols / blockSize {
|
2024-01-06 15:11:52 +00:00
|
|
|
if blockChanged[ blockVertIdx * HiRes.blockCols / blockSize + blockHorIdx ] != 0 {
|
2020-06-21 01:46:26 +00:00
|
|
|
// refresh the entire screen
|
2024-01-06 15:11:52 +00:00
|
|
|
let x = CGFloat(blockHorIdx) * blockScreenWidth - CGFloat(screenBlockMargin)
|
|
|
|
let y = frame.height - CGFloat(blockVertIdx) * blockScreenHeigth - blockScreenHeigth - CGFloat(screenBlockMargin)
|
|
|
|
let w = blockScreenWidth + CGFloat(screenBlockMargin) * CGFloat(blockSize)
|
|
|
|
let h = blockScreenHeigth + CGFloat(screenBlockMargin) * CGFloat(blockSize)
|
2020-06-21 01:46:26 +00:00
|
|
|
|
2024-01-06 15:11:52 +00:00
|
|
|
let boundingBox = CGRect(
|
|
|
|
x: x - HiRes.refreshUnderscan,
|
|
|
|
y: y - HiRes.refreshUnderscan,
|
|
|
|
width: w + HiRes.refreshOverscan,
|
|
|
|
height: h + HiRes.refreshOverscan
|
|
|
|
)
|
2020-06-21 01:46:26 +00:00
|
|
|
self.setNeedsDisplay( boundingBox )
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-01-06 15:11:52 +00:00
|
|
|
|
|
|
|
|
|
|
|
let usePixelTrail = true
|
2020-06-21 01:46:26 +00:00
|
|
|
|
2024-01-06 15:11:52 +00:00
|
|
|
// let pixelTrail = 2 // maybe too fast?
|
|
|
|
// let pixelTrail = 1.5
|
|
|
|
// let pixelTrail = 1.4 // maybe a bit slow?
|
|
|
|
// let pixelTrail = 1.35 // maybe too slow?
|
|
|
|
// let pixelTrail = 1.25 // maybe too slow?
|
2020-06-21 01:46:26 +00:00
|
|
|
|
2020-06-21 05:14:39 +00:00
|
|
|
func RenderMono() {
|
|
|
|
var height = HiRes.PixelHeight
|
|
|
|
|
|
|
|
// do not even render it...
|
|
|
|
if videoMode.text == 1 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if videoMode.mixed == 1 {
|
|
|
|
height = HiRes.MixedHeight
|
|
|
|
}
|
|
|
|
if MEMcfg.txt_page_2 == 1 {
|
|
|
|
HiResBufferPointer = HiResBuffer2
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
HiResBufferPointer = HiResBuffer1
|
|
|
|
}
|
|
|
|
}
|
2020-06-21 01:46:26 +00:00
|
|
|
|
|
|
|
var pixelAddr = 0
|
2024-01-06 15:11:52 +00:00
|
|
|
|
2020-06-21 01:46:26 +00:00
|
|
|
var y = 0
|
2024-01-06 15:11:52 +00:00
|
|
|
|
|
|
|
// blockChanged = [Bool](repeating: false, count: HiRes.blockRows * HiRes.blockCols)
|
|
|
|
|
|
|
|
if ( ViewController.shared?.CRTMonitor ?? false ) {
|
|
|
|
// do not clear the changes table
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
hires_clearChanges()
|
|
|
|
}
|
|
|
|
|
2020-06-21 01:46:26 +00:00
|
|
|
for lineAddr in HiResLineAddrTbl {
|
2020-06-21 05:14:39 +00:00
|
|
|
if ( height <= 0 ) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
height -= 1
|
2024-01-06 15:11:52 +00:00
|
|
|
|
2020-06-21 01:46:26 +00:00
|
|
|
let blockVertIdx = y / HiRes.blockHeight * HiRes.blockCols
|
2024-01-06 15:11:52 +00:00
|
|
|
var prevColor = color_black
|
2020-06-21 01:46:26 +00:00
|
|
|
|
|
|
|
for blockHorIdx in 0..<HiRes.blockCols {
|
|
|
|
let block = Int(HiResBufferPointer[ Int(lineAddr + blockHorIdx) ])
|
|
|
|
let screenIdx = y * HiRes.blockCols + blockHorIdx
|
2020-05-04 20:54:04 +00:00
|
|
|
|
2020-06-21 01:46:26 +00:00
|
|
|
// get all changed blocks
|
2024-01-06 15:11:52 +00:00
|
|
|
if shadowScreen[ screenIdx ] != block {
|
|
|
|
blockChanged[ blockVertIdx + blockHorIdx ] = 0xFF
|
|
|
|
}
|
|
|
|
else if usePixelTrail && ( ViewController.shared?.CRTMonitor ?? false ) {
|
|
|
|
// slow CRT fade out effect
|
|
|
|
if (y % HiRes.blockHeight == 0) && (blockChanged[ blockVertIdx + blockHorIdx ] > 0) {
|
|
|
|
blockChanged[ blockVertIdx + blockHorIdx ] = UInt8( Double(blockChanged[ blockVertIdx + blockHorIdx ]) / pixelTrail )
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
blockChanged[ blockVertIdx + blockHorIdx ] = 0
|
|
|
|
}
|
|
|
|
|
2020-06-21 01:46:26 +00:00
|
|
|
shadowScreen[ screenIdx ] = block
|
|
|
|
|
2024-01-06 15:11:52 +00:00
|
|
|
let highBit = (block >> 7) & 1
|
|
|
|
|
2020-06-21 01:46:26 +00:00
|
|
|
for bit in stride(from: 0, through: 6, by: 1) {
|
|
|
|
let bitMask = 1 << bit
|
|
|
|
if (block & bitMask) != 0 {
|
2024-01-06 15:11:52 +00:00
|
|
|
pixelsSRGB[pixelAddr + highBit] = monoColor
|
|
|
|
pixelsSRGB[pixelAddr + highBit + 1] = monoColor
|
|
|
|
if highBit == 1 && prevColor == monoColor {
|
|
|
|
pixelsSRGB[pixelAddr] = monoColor
|
|
|
|
}
|
|
|
|
prevColor = monoColor
|
|
|
|
}
|
|
|
|
else if usePixelTrail && ( ViewController.shared?.CRTMonitor ?? false ) {
|
|
|
|
var srgb = pixelsSRGB[pixelAddr + highBit]
|
|
|
|
|
|
|
|
let s = srgb >> 24 & 0xFF
|
|
|
|
let r = srgb >> 16 & 0xFF
|
|
|
|
let g = srgb >> 8 & 0xFF
|
|
|
|
let b = srgb >> 0 & 0xFF
|
|
|
|
|
|
|
|
srgb = UInt32(Double(s) / pixelTrail) << 24
|
|
|
|
| UInt32(Double(r) / pixelTrail) << 16
|
|
|
|
| UInt32(Double(g) / pixelTrail) << 8
|
|
|
|
| UInt32(Double(b) / pixelTrail)
|
|
|
|
|
|
|
|
pixelsSRGB[pixelAddr + highBit] = srgb;
|
|
|
|
pixelsSRGB[pixelAddr + highBit + 1] = srgb;
|
2020-06-21 01:46:26 +00:00
|
|
|
}
|
|
|
|
else {
|
2024-01-06 15:11:52 +00:00
|
|
|
pixelsSRGB[pixelAddr + highBit] = color_black
|
|
|
|
pixelsSRGB[pixelAddr + highBit + 1] = color_black
|
|
|
|
prevColor = color_black
|
2020-06-21 01:46:26 +00:00
|
|
|
}
|
|
|
|
|
2024-01-06 15:11:52 +00:00
|
|
|
pixelAddr += 2 // two physical pixels per logical pixel
|
2020-06-21 01:46:26 +00:00
|
|
|
}
|
|
|
|
}
|
2024-01-06 15:11:52 +00:00
|
|
|
|
2020-06-21 01:46:26 +00:00
|
|
|
y += 1
|
|
|
|
}
|
2024-01-06 15:11:52 +00:00
|
|
|
|
|
|
|
// hires_renderMono()
|
2020-06-21 01:46:26 +00:00
|
|
|
|
|
|
|
refreshChanged(blockSize: 1)
|
|
|
|
}
|
|
|
|
|
2020-05-25 03:26:54 +00:00
|
|
|
|
2020-05-29 15:50:47 +00:00
|
|
|
func colorPixel ( pixelAddr : Int, pixel : Int, prev : Int ) {
|
2020-05-04 20:54:04 +00:00
|
|
|
let colorAddr = pixelAddr / 4
|
|
|
|
|
2020-02-25 08:14:05 +00:00
|
|
|
switch ( pixel ) {
|
2020-06-21 01:46:26 +00:00
|
|
|
case 1: // purple (bits are in reverse!)
|
2024-01-06 15:11:52 +00:00
|
|
|
pixelsSRGB[colorAddr] = color_purple
|
|
|
|
pixelsSRGB[colorAddr + 1] = color_purple
|
|
|
|
pixelsSRGB[colorAddr + 2] = color_purple
|
|
|
|
if (colorAddr >= 2) && (prev != 0x03) && (prev != 0x07) && (prev != 0x00) && (prev != 0x04) {
|
|
|
|
pixelsSRGB[colorAddr - 1] = color_purple
|
|
|
|
pixelsSRGB[colorAddr - 2] = color_purple
|
2020-05-09 10:39:55 +00:00
|
|
|
}
|
2020-02-26 07:54:08 +00:00
|
|
|
|
2020-06-21 01:46:26 +00:00
|
|
|
case 2: // green
|
2020-02-28 08:28:22 +00:00
|
|
|
// reducing color bleeding
|
2024-01-06 15:11:52 +00:00
|
|
|
if (colorAddr > 1) && (pixelsSRGB[colorAddr - 2] != color_black) {
|
|
|
|
pixelsSRGB[colorAddr + 0] = color_green
|
2020-02-28 08:28:22 +00:00
|
|
|
}
|
2024-01-06 15:11:52 +00:00
|
|
|
pixelsSRGB[colorAddr] = color_green
|
|
|
|
pixelsSRGB[colorAddr + 1] = color_green
|
|
|
|
pixelsSRGB[colorAddr + 2] = color_green
|
|
|
|
pixelsSRGB[colorAddr + 3] = color_green
|
2020-02-25 08:14:05 +00:00
|
|
|
|
2020-06-21 01:46:26 +00:00
|
|
|
case 3: // white 1
|
2024-01-06 15:11:52 +00:00
|
|
|
// if ( colorAddr >= 2 ) && ( pixelsSRGB[colorAddr - 2] != color_black ) {
|
|
|
|
// pixelsSRGB[colorAddr - 2] = color_white // HiRes.pixelsSRGB[colorAddr - 2]
|
2020-05-25 03:26:54 +00:00
|
|
|
// }
|
|
|
|
// if (colorAddr >= 1) {
|
2020-06-21 01:46:26 +00:00
|
|
|
// HiRes.pixelsSRGB[colorAddr - 1] = color_yellow
|
2020-05-25 03:26:54 +00:00
|
|
|
// }
|
2024-01-06 15:11:52 +00:00
|
|
|
pixelsSRGB[colorAddr - 1] = color_white
|
|
|
|
pixelsSRGB[colorAddr + 0] = color_white
|
|
|
|
pixelsSRGB[colorAddr + 1] = color_white
|
|
|
|
pixelsSRGB[colorAddr + 2] = color_white
|
|
|
|
pixelsSRGB[colorAddr + 3] = color_white
|
|
|
|
pixelsSRGB[colorAddr + 4] = color_white
|
2020-05-04 20:54:04 +00:00
|
|
|
|
2020-06-21 01:46:26 +00:00
|
|
|
case 5: // blue
|
2024-01-06 15:11:52 +00:00
|
|
|
pixelsSRGB[colorAddr + 1] = color_blue
|
|
|
|
pixelsSRGB[colorAddr + 2] = color_blue
|
|
|
|
pixelsSRGB[colorAddr] = color_blue
|
|
|
|
pixelsSRGB[colorAddr - 1] = color_blue
|
|
|
|
if (colorAddr >= 2) && (prev != 0x00) && (prev != 0x04) {
|
|
|
|
pixelsSRGB[colorAddr - 2] = color_blue
|
2020-05-09 10:39:55 +00:00
|
|
|
}
|
2020-05-04 20:54:04 +00:00
|
|
|
|
2020-06-21 01:46:26 +00:00
|
|
|
case 6: // orange
|
2020-05-08 06:54:11 +00:00
|
|
|
// reducing color bleeding
|
2024-01-06 15:11:52 +00:00
|
|
|
if (colorAddr > 0) && (pixelsSRGB[colorAddr - 2] != color_black) {
|
|
|
|
pixelsSRGB[colorAddr + 0] = color_orange // important for color bleeding and color contiunity
|
|
|
|
pixelsSRGB[colorAddr + 1] = color_orange
|
2020-02-26 07:54:08 +00:00
|
|
|
}
|
2024-01-06 15:11:52 +00:00
|
|
|
pixelsSRGB[colorAddr + 2] = color_orange
|
|
|
|
pixelsSRGB[colorAddr + 3] = color_orange
|
|
|
|
pixelsSRGB[colorAddr + 4] = color_orange
|
2020-05-09 10:39:55 +00:00
|
|
|
|
2020-06-21 01:46:26 +00:00
|
|
|
case 7: // white 2
|
2024-01-06 15:11:52 +00:00
|
|
|
if ( colorAddr >= 2 ) && ( pixelsSRGB[colorAddr - 2] != color_black ) {
|
|
|
|
// pixelsSRGB[colorAddr - 2] = color_white // HiRes.pixelsSRGB[colorAddr - 2]
|
|
|
|
pixelsSRGB[colorAddr - 1] = color_white
|
|
|
|
}
|
|
|
|
pixelsSRGB[colorAddr + 0] = color_white // Donkey Kong would be perfect but problem in Sneakers
|
|
|
|
pixelsSRGB[colorAddr + 1] = color_white
|
|
|
|
pixelsSRGB[colorAddr + 2] = color_white
|
|
|
|
pixelsSRGB[colorAddr + 3] = color_white
|
|
|
|
pixelsSRGB[colorAddr + 4] = color_white
|
|
|
|
|
|
|
|
case 0: // 0x00 (black 1), 0x04 (black 2)
|
|
|
|
// pixelsSRGB[colorAddr + 0] = color_black
|
|
|
|
pixelsSRGB[colorAddr + 1] = color_black
|
|
|
|
pixelsSRGB[colorAddr + 2] = color_black
|
|
|
|
pixelsSRGB[colorAddr + 3] = color_black
|
|
|
|
|
|
|
|
// white adjustment
|
|
|
|
// if (colorAddr >= 2) && ((prev == 3) || (prev == 7)) {
|
|
|
|
if (colorAddr >= 2) && (prev == 7) {
|
|
|
|
pixelsSRGB[colorAddr - 1] = color_black
|
|
|
|
pixelsSRGB[colorAddr - 0] = color_black
|
|
|
|
}
|
|
|
|
// blue adjustment
|
|
|
|
if (colorAddr >= 2) && (prev == 5) {
|
|
|
|
pixelsSRGB[colorAddr - 1] = color_black
|
|
|
|
}
|
|
|
|
|
|
|
|
case 4: // 0x00 (black 1), 0x04 (black 2)
|
|
|
|
pixelsSRGB[colorAddr + 1] = color_black
|
|
|
|
pixelsSRGB[colorAddr + 2] = color_black
|
|
|
|
pixelsSRGB[colorAddr + 3] = color_black
|
|
|
|
pixelsSRGB[colorAddr + 4] = color_black
|
|
|
|
|
|
|
|
// white adjustment
|
|
|
|
// if (colorAddr >= 2) && ((prev == 3) || (prev == 7)) {
|
|
|
|
if (colorAddr >= 2) && (prev == 7) {
|
|
|
|
pixelsSRGB[colorAddr - 0] = color_black
|
|
|
|
}
|
|
|
|
// blue adjustment
|
|
|
|
if (colorAddr >= 2) && (prev == 5) {
|
|
|
|
pixelsSRGB[colorAddr - 0] = color_black
|
|
|
|
pixelsSRGB[colorAddr - 1] = color_black
|
|
|
|
// pixelsSRGB[colorAddr - 2] = color_black // if i put that in there is ladder on Donkey Kong is too thin
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// let pp = pixelAddr - 1 // HiRes.pixelAddrBlockIncrement
|
|
|
|
// let cp = pixelsSRGB[pp / 4]
|
|
|
|
//
|
|
|
|
// let pa = pixelAddr - HiRes.pixelAddrBlockIncrement * 7 * 20
|
|
|
|
// let ca = pixelsSRGB[pa / 4]
|
|
|
|
//
|
|
|
|
// if cp == ca {
|
|
|
|
// switch cp {
|
|
|
|
// case color_blue, color_white, color_green, color_purple, color_orange, color_yellow:
|
|
|
|
//// pixelsSRGB[pp/4] = color_turquis
|
|
|
|
//// pixelsSRGB[pa/4] = color_yellow
|
|
|
|
//
|
|
|
|
// let c1 = ca & 0x00FFFFFF
|
|
|
|
// let a = ca >> 24
|
|
|
|
// let a1 = (a / 6) << 24
|
|
|
|
// let a2 = a1 * 2
|
|
|
|
// let a3 = a1 * 3
|
|
|
|
// let a4 = a1 * 4
|
|
|
|
//
|
|
|
|
//// pixelsSRGB[colorAddr + 0] = a4 | c1
|
|
|
|
//// pixelsSRGB[colorAddr + 1] = a3 | c1
|
|
|
|
//// pixelsSRGB[colorAddr + 2] = a2 | c1
|
|
|
|
//// pixelsSRGB[colorAddr + 3] = a1 | c1
|
|
|
|
//
|
|
|
|
// pixelsSRGB[colorAddr + 0] = a4 | c1
|
|
|
|
// pixelsSRGB[colorAddr + 1] = a2 | c1
|
|
|
|
//
|
|
|
|
// // let pb = pixelAddr + HiRes.pixelAddrBlockIncrement * 7 * 20
|
|
|
|
// // let cb = pb / 4
|
|
|
|
// // pixelsSRGB[cb] = pixelsSRGB[colorAddr]
|
|
|
|
// default:
|
|
|
|
// break
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// }
|
2020-05-25 03:26:54 +00:00
|
|
|
|
2024-01-06 15:11:52 +00:00
|
|
|
default:
|
2020-05-04 20:54:04 +00:00
|
|
|
break
|
2020-02-26 07:54:08 +00:00
|
|
|
}
|
2020-05-09 20:45:41 +00:00
|
|
|
|
2020-02-26 07:54:08 +00:00
|
|
|
// white adjustment
|
|
|
|
if ( (prev & 2) == 2 ) && ( (pixel & 1) == 1 ) {
|
2024-01-06 15:11:52 +00:00
|
|
|
pixelsSRGB[colorAddr] = color_white
|
|
|
|
pixelsSRGB[colorAddr + 1] = color_white
|
|
|
|
if colorAddr >= 2 {
|
|
|
|
pixelsSRGB[colorAddr - 1] = color_white
|
|
|
|
pixelsSRGB[colorAddr - 2] = color_white
|
|
|
|
pixelsSRGB[colorAddr - 3] = color_white
|
|
|
|
}
|
|
|
|
// blue expansion
|
|
|
|
if pixel == 5 {
|
|
|
|
pixelsSRGB[colorAddr + 2] = color_black
|
|
|
|
pixelsSRGB[colorAddr + 3] = color_black
|
|
|
|
pixelsSRGB[colorAddr + 4] = color_black
|
2020-05-09 20:45:41 +00:00
|
|
|
}
|
|
|
|
|
2020-02-28 08:28:22 +00:00
|
|
|
// TODO: Need better check if extra green was created
|
2024-01-06 15:11:52 +00:00
|
|
|
if (colorAddr >= 4) && (pixelsSRGB[colorAddr - 4] == color_green ) {
|
|
|
|
if (colorAddr < 6) || (pixelsSRGB[colorAddr - 6] != color_green) {
|
|
|
|
pixelsSRGB[colorAddr - 3] = color_black
|
|
|
|
pixelsSRGB[colorAddr - 4] = color_black
|
2020-05-25 03:26:54 +00:00
|
|
|
}
|
2020-02-28 08:28:22 +00:00
|
|
|
}
|
2020-02-25 08:14:05 +00:00
|
|
|
}
|
2020-02-26 07:54:08 +00:00
|
|
|
|
2020-05-10 03:57:56 +00:00
|
|
|
// // green adjustment -- followed by white
|
2020-06-11 00:48:37 +00:00
|
|
|
// if (colorAddr >= 1) && (prev == 0x03) && (HiRes.pixelsSRGB[colorAddr - 1] = color_green) {
|
|
|
|
// HiRes.pixelsSRGB[colorAddr - 1] = color_green
|
2020-05-10 03:57:56 +00:00
|
|
|
// }
|
2020-05-25 03:26:54 +00:00
|
|
|
|
2020-02-28 08:28:22 +00:00
|
|
|
// purple adjustment -- followed by white
|
2020-05-10 03:57:56 +00:00
|
|
|
if (prev == 0x01) && (
|
2020-05-08 06:54:11 +00:00
|
|
|
(pixel == 0x01) ||
|
2020-05-09 10:39:55 +00:00
|
|
|
(pixel == 0x03) || (pixel == 0x07) // white
|
2020-02-29 05:57:23 +00:00
|
|
|
) {
|
2020-02-28 08:28:22 +00:00
|
|
|
// was the previous purple pixel promoted to white or is it still purple?
|
2024-01-06 15:11:52 +00:00
|
|
|
if (colorAddr >= 4) && ( pixelsSRGB[colorAddr - 4] == color_purple ) {
|
|
|
|
pixelsSRGB[colorAddr - 1] = color_purple
|
|
|
|
pixelsSRGB[colorAddr - 2] = color_purple
|
2020-02-28 08:28:22 +00:00
|
|
|
}
|
2020-02-26 07:54:08 +00:00
|
|
|
}
|
2020-02-28 08:28:22 +00:00
|
|
|
|
2020-05-08 06:54:11 +00:00
|
|
|
// blue adjustment -- followed by white
|
2020-02-29 05:57:23 +00:00
|
|
|
else if (prev == 0x05) && (
|
2024-01-06 15:11:52 +00:00
|
|
|
(pixel == 0x05)
|
|
|
|
|| (pixel == 0x03)
|
|
|
|
|| (pixel == 0x07) // white
|
2020-02-29 05:57:23 +00:00
|
|
|
) {
|
2024-01-06 15:11:52 +00:00
|
|
|
// pixelsSRGB[colorAddr - 0] = color_blue
|
|
|
|
// pixelsSRGB[colorAddr - 1] = color_blue
|
|
|
|
pixelsSRGB[colorAddr - 2] = color_blue // blue color bleed
|
2020-02-26 07:54:08 +00:00
|
|
|
}
|
2024-01-06 15:11:52 +00:00
|
|
|
|
2020-02-25 08:14:05 +00:00
|
|
|
}
|
|
|
|
|
2024-01-06 15:11:52 +00:00
|
|
|
|
|
|
|
static let logicalPixels = 8
|
|
|
|
static let physicalPixels = 2
|
|
|
|
static let pixelAddrBlockIncrement = logicalPixels * physicalPixels // 2 display pixels per logical pixel
|
|
|
|
|
2020-06-21 05:14:39 +00:00
|
|
|
func RenderColor() {
|
2020-04-23 02:18:28 +00:00
|
|
|
var height = HiRes.PixelHeight
|
2020-05-05 15:35:57 +00:00
|
|
|
|
2020-04-23 02:18:28 +00:00
|
|
|
// do not even render it...
|
|
|
|
if videoMode.text == 1 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if videoMode.mixed == 1 {
|
|
|
|
height = HiRes.MixedHeight
|
|
|
|
}
|
2020-04-28 03:32:58 +00:00
|
|
|
if MEMcfg.txt_page_2 == 1 {
|
2020-04-23 02:18:28 +00:00
|
|
|
HiResBufferPointer = HiResBuffer2
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
HiResBufferPointer = HiResBuffer1
|
|
|
|
}
|
|
|
|
}
|
2020-05-05 15:35:57 +00:00
|
|
|
|
2020-02-25 08:14:05 +00:00
|
|
|
var pixelAddr = 0
|
2020-05-05 15:35:57 +00:00
|
|
|
|
2020-02-25 08:14:05 +00:00
|
|
|
var y = 0
|
2020-05-05 15:35:57 +00:00
|
|
|
|
2024-01-06 15:11:52 +00:00
|
|
|
// blockChanged = [Bool](repeating: false, count: HiRes.blockRows * HiRes.blockCols / 2)
|
|
|
|
|
|
|
|
hires_clearChanges()
|
|
|
|
|
2020-04-23 02:18:28 +00:00
|
|
|
HiRes.context?.clear( CGRect(x: 0, y: 0, width: frame.width, height: frame.height) )
|
|
|
|
|
2020-02-25 08:14:05 +00:00
|
|
|
for lineAddr in HiResLineAddrTbl {
|
2020-04-23 02:18:28 +00:00
|
|
|
|
|
|
|
if ( height <= 0 ) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
height -= 1
|
|
|
|
|
2020-06-21 01:46:26 +00:00
|
|
|
let blockVertIdx = y / HiRes.blockHeight * HiRes.blockCols / 2
|
2020-02-26 07:54:08 +00:00
|
|
|
var prev = 0
|
2020-05-09 20:45:41 +00:00
|
|
|
|
2020-04-23 02:18:28 +00:00
|
|
|
for blockHorIdx in 0 ..< HiRes.blockCols / 2 {
|
2020-05-05 15:35:57 +00:00
|
|
|
// print("blockVertIdx:", blockVertIdx, " blockHorIdx:", blockHorIdx)
|
|
|
|
|
2020-04-23 02:18:28 +00:00
|
|
|
let blockH = Int(HiResBufferPointer[ Int(lineAddr + blockHorIdx * 2) ])
|
2020-02-25 08:14:05 +00:00
|
|
|
let blockH7 = ( blockH >> 5 ) & 0x04
|
2020-04-23 02:18:28 +00:00
|
|
|
let blockL = Int(HiResBufferPointer[ Int(lineAddr + blockHorIdx * 2) + 1 ])
|
2020-02-25 08:14:05 +00:00
|
|
|
let blockL7 = ( blockL >> 5 ) & 0x04
|
|
|
|
|
2020-06-21 05:14:39 +00:00
|
|
|
let block = ( ( blockL & 0x7F ) << 7 ) | ( blockH & 0x7F )
|
|
|
|
let block14 = ( blockL << 8 ) | blockH
|
2020-02-25 08:14:05 +00:00
|
|
|
|
2020-04-23 02:18:28 +00:00
|
|
|
let screenIdx = y * HiRes.blockCols + blockHorIdx
|
2020-05-05 15:35:57 +00:00
|
|
|
|
|
|
|
// get all changed blocks
|
2024-01-06 15:11:52 +00:00
|
|
|
blockChanged[ blockVertIdx + blockHorIdx ] = ((blockChanged[ blockVertIdx + blockHorIdx ] != 0) || (shadowScreen[ screenIdx ] != block14)) ? 1 : 0
|
2020-06-21 05:14:39 +00:00
|
|
|
shadowScreen[ screenIdx ] = block14
|
2020-05-05 15:35:57 +00:00
|
|
|
|
|
|
|
for px in 0 ... 2 {
|
|
|
|
// let bitMask = 3 << ( px * 2 )
|
|
|
|
let pixel = blockH7 | ( (block >> (px * 2)) & 3 )
|
2020-05-29 15:50:47 +00:00
|
|
|
colorPixel(pixelAddr: pixelAddr, pixel: pixel, prev: prev )
|
2024-01-06 15:11:52 +00:00
|
|
|
pixelAddr += HiRes.pixelAddrBlockIncrement
|
2020-05-05 15:35:57 +00:00
|
|
|
prev = pixel
|
2020-04-23 02:18:28 +00:00
|
|
|
}
|
2020-05-05 15:35:57 +00:00
|
|
|
|
2020-05-08 06:54:11 +00:00
|
|
|
let pixel = (blockL7 | blockH7) | ( (block >> (3 * 2)) & 3 )
|
2020-05-29 15:50:47 +00:00
|
|
|
colorPixel(pixelAddr: pixelAddr, pixel: pixel, prev: prev )
|
2024-01-06 15:11:52 +00:00
|
|
|
pixelAddr += HiRes.pixelAddrBlockIncrement
|
2020-05-05 15:35:57 +00:00
|
|
|
prev = pixel
|
|
|
|
|
|
|
|
for px in 4 ... 6 {
|
|
|
|
// let bitMask = 3 << ( px * 2 )
|
|
|
|
let pixel = blockL7 | ( (block >> (px * 2)) & 3 )
|
2020-05-29 15:50:47 +00:00
|
|
|
colorPixel(pixelAddr: pixelAddr, pixel: pixel, prev: prev )
|
2024-01-06 15:11:52 +00:00
|
|
|
pixelAddr += HiRes.pixelAddrBlockIncrement
|
2020-02-26 07:54:08 +00:00
|
|
|
prev = pixel
|
2020-04-23 02:18:28 +00:00
|
|
|
}
|
2020-05-05 15:35:57 +00:00
|
|
|
}
|
2020-02-25 08:14:05 +00:00
|
|
|
y += 1
|
|
|
|
}
|
|
|
|
|
2020-05-05 15:35:57 +00:00
|
|
|
|
|
|
|
// refresh changed block only
|
2020-06-21 01:46:26 +00:00
|
|
|
refreshChanged(blockSize: 2)
|
|
|
|
// needsDisplay = true // refresh the entire screen
|
2020-05-05 15:35:57 +00:00
|
|
|
|
2020-06-21 01:46:26 +00:00
|
|
|
}
|
|
|
|
|
2020-06-21 05:14:39 +00:00
|
|
|
|
|
|
|
func Render() {
|
2024-01-06 15:11:52 +00:00
|
|
|
if ( ViewController.shared?.ColorMonitor ?? true ) {
|
2020-06-21 05:14:39 +00:00
|
|
|
RenderColor()
|
2020-06-21 01:46:26 +00:00
|
|
|
}
|
|
|
|
else {
|
2020-06-21 05:14:39 +00:00
|
|
|
RenderMono()
|
2020-05-05 15:35:57 +00:00
|
|
|
}
|
2020-06-21 01:46:26 +00:00
|
|
|
}
|
2020-06-21 05:14:39 +00:00
|
|
|
|
|
|
|
func RenderFullScreen() {
|
2020-06-21 01:46:26 +00:00
|
|
|
needsDisplay = true
|
2020-06-21 05:14:39 +00:00
|
|
|
Render()
|
2020-05-05 15:35:57 +00:00
|
|
|
}
|
|
|
|
|
2020-06-21 01:46:26 +00:00
|
|
|
|
2020-05-05 15:35:57 +00:00
|
|
|
override func draw(_ rect: CGRect) {
|
2020-02-25 08:14:05 +00:00
|
|
|
guard let image = HiRes.context?.makeImage() else { return }
|
2020-04-23 02:18:28 +00:00
|
|
|
|
|
|
|
// refresh the entire screen
|
|
|
|
let boundingBox = CGRect(x: 0, y: 0, width: frame.width, height: frame.height)
|
2020-06-17 16:46:17 +00:00
|
|
|
|
2024-01-06 15:11:52 +00:00
|
|
|
if ( ViewController.shared?.CRTMonitor ?? false ) {
|
2020-06-17 16:46:17 +00:00
|
|
|
currentContext?.interpolationQuality = .high // TODO: Make a switch that lets you turn on and off "old monitor effects"
|
|
|
|
}
|
|
|
|
else {
|
2024-01-06 15:11:52 +00:00
|
|
|
currentContext?.interpolationQuality = .high // .none
|
2020-06-17 16:46:17 +00:00
|
|
|
}
|
2020-05-09 20:45:41 +00:00
|
|
|
currentContext?.draw(image, in: boundingBox)
|
2020-02-25 08:14:05 +00:00
|
|
|
}
|
|
|
|
|
2019-11-28 04:27:32 +00:00
|
|
|
|
2019-09-22 08:31:09 +00:00
|
|
|
|
|
|
|
}
|