Steve2/A2Mac/ViewController.swift

2380 lines
80 KiB
Swift
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// ViewController.swift
// Steve ][
//
// Created by Tamas Rudnai on 7/25/19.
// Copyright © 2019, 2020 Tamas Rudnai. All rights reserved.
//
// 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/>.
//
import Cocoa
//import AVFoundation
//import Metal
//
//var device : MTLDevice!
//var metalLayer: CAMetalLayer!
//var vertexBuffer: MTLBuffer!
//var pipelineState: MTLRenderPipelineState!
//var commandQueue: MTLCommandQueue!
////var timer: CADisplayLink!
let K : Double = 1000.0
let M : Double = (K * K)
let G : Double = (M * K)
let T : Double = (G * K)
let KB : Double = 1024.0
let MB : Double = (KB * KB)
let GB : Double = (MB * KB)
let TB : Double = (GB * KB)
let colorWhite = NSColor.init( red:0.9296875, green:0.9296875, blue:0.9296875, alpha: 1 )
let colorGreen = NSColor.init( red:0.16796875, green:0.84375, blue:0.2890625, alpha: 1 )
let colorOrange = NSColor.init( red:1, green:0.38671875, blue:0.0078125, alpha: 1 )
var monoColor = colorGreen;
//var spk_up: AVAudioPlayer?
//var spk_dn: AVAudioPlayer?
//
//@_cdecl("ViewController_spk_up_play")
//func spk_up_play() {
// spk_up?.stop()
// spk_dn?.stop()
// spk_up?.play()
//}
//
//@_cdecl("ViewController_spk_dn_play")
//func spk_dn_play() {
// spk_up?.stop()
// spk_dn?.stop()
// spk_dn?.play()
//}
#if METAL_YES
import Metal
#endif
class ViewController: NSViewController {
static var shared : ViewController? = nil
var displayLink: CVDisplayLink?
@IBOutlet var monitorView: MonitorView!
@IBOutlet weak var textDisplayScroller: NSScrollView!
@IBOutlet var textDisplay: NSTextView!
@IBOutlet weak var speedometer: NSTextFieldCell!
@IBOutlet weak var lores: LoRes!
@IBOutlet weak var hires: HiRes!
@IBOutlet weak var splashScreen: NSView!
@IBOutlet weak var scanLines: NSImageView!
var CRTMonitor = false
var ColorMonitor = true
var Keyboard2Joystick = true
var Mouse2Joystick = false
var MouseInterface = true
// static let charConvStr : String =
// "@🄰🄱🄲🄳🄴🄵🄶🄷🄸🄹🄺🄻🄼🄽🄾🄿🅀🅁🅂🅃🅄🅅🅆🅇🅈🅉[\\]^_ !\"#$%&'()*+,-./0123456789:;<=>?" +
// "@🅰🅱🅲🅳🅴🅵🅶🅷🅸🅹🅺🅻🅼🅽🅾🅿🆀🆁🆂🆃🆄🆅🆆🆇🆈🆉[\\]^_!\"#$%&'()*+,-./0123456789:;<=>?" + // FL
// "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_ !\"#$%&'()*+,-./0123456789:;<=>?" +
// "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~?"
// static let charConvStr : String =
// "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_ !\"#$%&'()*+,-./0123456789:;<=>?" +
// "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_!\"#$%&'()*+,-./0123456789:;<=>?" + // FL
// "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_\u{E0A0}!\"#$%&'()*+,-./0123456789:;<=>?" +
// "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~?"
// static let charConvStr : String =
// "\u{E140}\u{E141}\u{E142}\u{E143}\u{E144}\u{E145}\u{E146}\u{E147}\u{E148}\u{E149}\u{E14A}\u{E14B}\u{E14C}\u{E14D}\u{E14E}\u{E14F}\u{E150}\u{E151}\u{E152}\u{E153}\u{E154}\u{E155}\u{E156}\u{E157}\u{E158}\u{E159}\u{E15A}\u{E15B}\u{E15C}\u{E15D}\u{E15E}\u{E15F}\u{E120}\u{E121}\u{E122}\u{E123}\u{E124}\u{E125}\u{E126}\u{E127}\u{E128}\u{E129}\u{E12A}\u{E12B}\u{E12C}\u{E12D}\u{E12E}\u{E12F}\u{E130}\u{E131}\u{E132}\u{E133}\u{E134}\u{E135}\u{E136}\u{E137}\u{E138}\u{E139}\u{E13A}\u{E13B}\u{E13C}\u{E13D}\u{E13E}\u{E13F}" +
// "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_!\"#$%&'()*+,-./0123456789:;<=>?" + // FL
// "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_\u{E0A0}!\"#$%&'()*+,-./0123456789:;<=>?" +
// "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~?"
// TODO: On 80n col mode no flash + small caps are inversed on the Flash map
static let charConvStrFlashOff40 : String =
// INVERSE
"\u{E140}\u{E141}\u{E142}\u{E143}\u{E144}\u{E145}\u{E146}\u{E147}\u{E148}\u{E149}\u{E14A}\u{E14B}\u{E14C}\u{E14D}\u{E14E}\u{E14F}\u{E150}\u{E151}\u{E152}\u{E153}\u{E154}\u{E155}\u{E156}\u{E157}\u{E158}\u{E159}\u{E15A}\u{E15B}\u{E15C}\u{E15D}\u{E15E}\u{E15F}\u{E120}\u{E121}\u{E122}\u{E123}\u{E124}\u{E125}\u{E126}\u{E127}\u{E128}\u{E129}\u{E12A}\u{E12B}\u{E12C}\u{E12D}\u{E12E}\u{E12F}\u{E130}\u{E131}\u{E132}\u{E133}\u{E134}\u{E135}\u{E136}\u{E137}\u{E138}\u{E139}\u{E13A}\u{E13B}\u{E13C}\u{E13D}\u{E13E}\u{E13F}" +
// FLASH
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_ !\"#$%&'()*+,-./0123456789:;<=>?" + // FL
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_ !\"#$%&'()*+,-./0123456789:;<=>?" +
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u{E27F}"
static let charConvStrFlashOn40 : String =
// INVERSE
"\u{E140}\u{E141}\u{E142}\u{E143}\u{E144}\u{E145}\u{E146}\u{E147}\u{E148}\u{E149}\u{E14A}\u{E14B}\u{E14C}\u{E14D}\u{E14E}\u{E14F}\u{E150}\u{E151}\u{E152}\u{E153}\u{E154}\u{E155}\u{E156}\u{E157}\u{E158}\u{E159}\u{E15A}\u{E15B}\u{E15C}\u{E15D}\u{E15E}\u{E15F}\u{E120}\u{E121}\u{E122}\u{E123}\u{E124}\u{E125}\u{E126}\u{E127}\u{E128}\u{E129}\u{E12A}\u{E12B}\u{E12C}\u{E12D}\u{E12E}\u{E12F}\u{E130}\u{E131}\u{E132}\u{E133}\u{E134}\u{E135}\u{E136}\u{E137}\u{E138}\u{E139}\u{E13A}\u{E13B}\u{E13C}\u{E13D}\u{E13E}\u{E13F}" +
// FLASH
"\u{E140}\u{E141}\u{E142}\u{E143}\u{E144}\u{E145}\u{E146}\u{E147}\u{E148}\u{E149}\u{E14A}\u{E14B}\u{E14C}\u{E14D}\u{E14E}\u{E14F}\u{E150}\u{E151}\u{E152}\u{E153}\u{E154}\u{E155}\u{E156}\u{E157}\u{E158}\u{E159}\u{E15A}\u{E15B}\u{E15C}\u{E15D}\u{E15E}\u{E15F}\u{E120}\u{E121}\u{E122}\u{E123}\u{E124}\u{E125}\u{E126}\u{E127}\u{E128}\u{E129}\u{E12A}\u{E12B}\u{E12C}\u{E12D}\u{E12E}\u{E12F}\u{E130}\u{E131}\u{E132}\u{E133}\u{E134}\u{E135}\u{E136}\u{E137}\u{E138}\u{E139}\u{E13A}\u{E13B}\u{E13C}\u{E13D}\u{E13E}\u{E13F}" +
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_ !\"#$%&'()*+,-./0123456789:;<=>?" +
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u{E27F}"
static let charConvStrCol80 : String =
// INVERSE
"\u{E140}\u{E141}\u{E142}\u{E143}\u{E144}\u{E145}\u{E146}\u{E147}\u{E148}\u{E149}\u{E14A}\u{E14B}\u{E14C}\u{E14D}\u{E14E}\u{E14F}\u{E150}\u{E151}\u{E152}\u{E153}\u{E154}\u{E155}\u{E156}\u{E157}\u{E158}\u{E159}\u{E15A}\u{E15B}\u{E15C}\u{E15D}\u{E15E}\u{E15F}\u{E120}\u{E121}\u{E122}\u{E123}\u{E124}\u{E125}\u{E126}\u{E127}\u{E128}\u{E129}\u{E12A}\u{E12B}\u{E12C}\u{E12D}\u{E12E}\u{E12F}\u{E130}\u{E131}\u{E132}\u{E133}\u{E134}\u{E135}\u{E136}\u{E137}\u{E138}\u{E139}\u{E13A}\u{E13B}\u{E13C}\u{E13D}\u{E13E}\u{E13F}" +
// INVERSE 2 with small caps
"\u{E140}\u{E141}\u{E142}\u{E143}\u{E144}\u{E145}\u{E146}\u{E147}\u{E148}\u{E149}\u{E14A}\u{E14B}\u{E14C}\u{E14D}\u{E14E}\u{E14F}\u{E150}\u{E151}\u{E152}\u{E153}\u{E154}\u{E155}\u{E156}\u{E157}\u{E158}\u{E159}\u{E15A}\u{E15B}\u{E15C}\u{E15D}\u{E15E}\u{E15F}\u{E160}\u{E161}\u{E162}\u{E163}\u{E164}\u{E165}\u{E166}\u{E167}\u{E168}\u{E169}\u{E16A}\u{E16B}\u{E16C}\u{E16D}\u{E16E}\u{E16F}\u{E170}\u{E171}\u{E172}\u{E173}\u{E174}\u{E175}\u{E176}\u{E177}\u{E178}\u{E179}\u{E17A}\u{E13B}\u{E13C}\u{E13D}\u{E13E}\u{E13F}" +
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_ !\"#$%&'()*+,-./0123456789:;<=>?" +
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u{E27F}"
static let charConvTblFlashOn40 = Array( charConvStrFlashOn40 )
static let charConvTblFlashOff40 = Array( charConvStrFlashOff40 )
static let charConvTblCol80 = Array( charConvStrCol80 )
static var charConvTblFlashOn = charConvTblFlashOn40
static var charConvTblFlashOff = charConvTblFlashOff40
static var charConvTbl = charConvTblFlashOn
// static var romFileName = "Apple2e_Enhanced.rom"
static var romFileName = "Apple2e_32k.rom"
// static var romFileName = "077-0019 Apple IIe Diagnostic Card - English.rom"
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
]
func dialogOK(title: String, text: String) {
let alert = NSAlert()
alert.messageText = title
alert.informativeText = text
alert.alertStyle = .warning
alert.addButton(withTitle: "OK")
alert.runModal()
// return alert.runModal() == .alertFirstButtonReturn
}
@objc func chk_woz_load(err : Int32) {
switch (err) {
case WOZ_ERR_OK:
break
case WOZ_ERR_FILE_NOT_FOUND:
dialogOK(title: "Error Loading Disk Image", text: "File Not Found!")
break
case WOZ_ERR_NOT_WOZ_FILE:
dialogOK(title: "Error Loading Disk Image", text: "File is not a WOZ image!")
break
case WOZ_ERR_BAD_CHUNK_HDR, WOZ_ERR_BAD_DATA:
dialogOK(title: "Error Loading Disk Image", text: "Malformed WOZ image!")
break
default:
dialogOK(title: "Error Loading Disk Image", text: "Unknown Error! (\(err))" )
break
}
}
var workItem : DispatchWorkItem? = nil;
@IBAction func PowerOn(_ sender: Any) {
#if SCHEDULER_CVDISPLAYLINK
CVDisplayLinkStop(displayLink!)
#else
upd.suspend()
#endif
cpuState = cpuState_inited;
spkr_stopAll()
//------------------------------------------------------------
// Animated Splash Screen fade out and (Text) Monitor fade in
hires.isHidden = true
lores.isHidden = true
// displayField.alphaValue = 0
// displayField.isHidden = false
textDisplayScroller.alphaValue = 0
textDisplayScroller.isHidden = false
splashScreen.alphaValue = 1
splashScreen.isHidden = false
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
NSAnimationContext.runAnimationGroup({ (context) in
context.duration = 1.0
// Use the value you want to animate to (NOT the starting value)
self.textDisplayScroller.animator().alphaValue = 1
self.hires.animator().alphaValue = 1
self.lores.animator().alphaValue = 1
self.splashScreen.animator().alphaValue = 0
},
completionHandler:{ () -> Void in
self.textDisplayScroller.alphaValue = 1
self.hires.alphaValue = 1
self.lores.alphaValue = 1
self.splashScreen.isHidden = true
})
m6502_ColdReset( Bundle.main.resourcePath! + "/rom/", ViewController.romFileName )
cpuState = cpuState_running;
#if SCHEDULER_CVDISPLAYLINK
CVDisplayLinkStart(self.displayLink!)
#else
self.upd.resume()
#endif
if let debugger = DebuggerWindowController.shared {
debugger.PauseButtonUpdate(needUpdateMainToolbar: false)
}
}
//------------------------------------------------------------
#if SPEEDTEST
if ( workItem != nil ) {
workItem!.cancel();
workItem = nil;
}
else {
workItem = DispatchWorkItem {
DispatchQueue.global(qos: .userInteractive).async {
// DispatchQueue.global(qos: .userInitiated).async {
// DispatchQueue.global(qos: .background).async {
tst6502()
}
}
DispatchQueue.global().async(execute: workItem!);
}
#else
#endif
}
@IBAction func PowerOff(_ sender: Any) {
#if SCHEDULER_CVDISPLAYLINK
CVDisplayLinkStop(displayLink!)
#else
upd.suspend()
#endif
cpuState = cpuState_inited;
spkr_stopAll()
//------------------------------------------------------------
// Animated Splash Screen fade out and (Text) Monitor fade in
DispatchQueue.main.asyncAfter(deadline: .now() + 0.0) {
self.splashScreen.alphaValue = 0
self.splashScreen.isHidden = false
NSAnimationContext.runAnimationGroup({ (context) in
context.duration = 0.5
// Use the value you want to animate to (NOT the starting value)
self.textDisplayScroller.animator().alphaValue = 0
self.hires.animator().alphaValue = 0
self.lores.animator().alphaValue = 0
self.splashScreen.animator().alphaValue = 1
},
completionHandler:{ () -> Void in
self.textDisplayScroller.alphaValue = 0
self.textDisplayScroller.isHidden = true
self.splashScreen.isHidden = false
self.hires.alphaValue = 0
self.lores.alphaValue = 0
self.hires.isHidden = true
self.lores.isHidden = true
self.splashScreen.isHidden = false
})
}
if let debugger = DebuggerWindowController.shared {
debugger.PauseButtonUpdate(needUpdateMainToolbar: false)
}
// hires.isHidden = true
// lores.isHidden = true
// textDisplayScroller.alphaValue = 0
//// textDisplayScroller.isHidden = false
// splashScreen.alphaValue = 1
// splashScreen.isHidden = false
//------------------------------------------------------------
}
func debuggerShowWindow() {
if let debuggerWindowController = DebuggerWindowController.shared {
DispatchQueue.main.async {
debuggerWindowController.showWindow(self)
}
}
}
func debuggerRemoveHighlight() {
if let debuggerViewController = DebuggerViewController.shared {
debuggerViewController.remove_highlight(view: debuggerViewController.Disass_Display, line: debuggerViewController.highlighted_line_number)
}
}
func debuggerPauseUpdate() {
if let debuggerViewController = DebuggerViewController.shared {
debuggerViewController.TrunDisassAddressPC(.on)
debuggerViewController.remove_highlight(view: debuggerViewController.Disass_Display, line: debuggerViewController.highlighted_line_number)
debuggerViewController.Update()
}
}
func Resume() {
debuggerRemoveHighlight()
#if SCHEDULER_CVDISPLAYLINK
CVDisplayLinkStart(displayLink!)
#else
upd.resume()
#endif
cpuState = cpuState_running
DispatchQueue.main.async {
self.view.window?.windowController?.showWindow(self)
}
if let debugger = DebuggerWindowController.shared {
debugger.PauseButtonUpdate()
}
}
func Pause() {
#if SCHEDULER_CVDISPLAYLINK
CVDisplayLinkStop(displayLink!)
#else
upd.suspend()
#endif
cpuState = cpuState_halted
if let debugger = DebuggerWindowController.shared {
debugger.PauseButtonUpdate()
}
debuggerPauseUpdate()
}
@IBAction func Pause(_ sender: Any) {
switch ( cpuState ) {
case cpuState_halted:
Resume()
break
case cpuState_running:
Pause()
break
default:
// upd.suspend()
// cpuState = cpuState_halted
break
}
}
@IBAction func Reset(_ sender: Any) {
// m6502.interrupt = SOFTRESET;
// let saved_frm_set = clk_6502_per_frm_set;
// clk_6502_per_frm_set = 0
// clk_6502_per_frm_max = 0
// // wait for 1 ms to allow the simulation to halt
// usleep(10000);
softReset()
// clk_6502_per_frm_set = saved_frm_set
}
static let textPage1Addr = 0x400
static let textPage2Addr = 0x800
static let textBufferSize = 0x400
// static only needed to be able to initialize things
static let textLines = 24
static let textCols = 40
static let lineEndChars = 1
// these are needed to be able to easy access to these constants from instances
let textLines = ViewController.textLines
let textCols = ViewController.textCols
let lineEndChars = ViewController.lineEndChars
var frameCnt = 0
var flashInverted = false
// let spaceChar : Character = "\u{E17F}"
// let blockChar : Character = "\u{E07F}"
// static let spaceChar : Character = " "
// static let blockChar : Character = ""
// static var flashingSpace : Character = " "
let ramBufferPointer = UnsafeRawBufferPointer(start: MEM, count: 64 * 1024)
static let textPage1Pointer = UnsafeRawBufferPointer(start: MEM + textPage1Addr, count: textBufferSize)
static let textPage2Pointer = UnsafeRawBufferPointer(start: MEM + textPage2Addr, count: textBufferSize)
static let textIntPage1BufferPointer = UnsafeRawBufferPointer(start: RAM + textPage1Addr, count: textBufferSize)
static let textIntPage2BufferPointer = UnsafeRawBufferPointer(start: RAM + textPage2Addr, count: textBufferSize)
static let textAuxBufferPointer = UnsafeRawBufferPointer(start: AUX + textPage1Addr, count: textBufferSize)
static let textPageShadowBuffer = UnsafeMutableRawBufferPointer.allocate(byteCount: textBufferSize, alignment: 1)
// TODO: Render text screen in native C
// static let textScreen = UnsafeMutableRawPointer(mutating: testText)
var textBufferPointer = textPage1Pointer
static let textArraySize = textLines * (textCols + lineEndChars) + textCols * 2
let txtClear = [Character](repeating: " ", count: textArraySize * 2)
var unicodeTextArray = [Character](repeating: " ", count: textArraySize * 2)
var s = String()
func HexDump() {
var txt : String = ""
for y in 0...textLines - 1 {
txt += String(format: "%04X: ", y * 16)
for x in 0...15 {
let byte = ramBufferPointer[ y * 16 + x ]
let chr = String(format: "%02X ", byte)
txt += chr
}
txt += "\n"
}
DispatchQueue.main.async {
self.textDisplay.string = txt
self.speedometer.stringValue = String(format: "%0.3lf MHz", mhz);
}
}
// AppleScript Keycodes
let leftArrowKey = 123
let rightArrowKey = 124
let upArrowKey = 126
let downArrowKey = 125
let F4FunctionKey = 118
let F5FunctionKey = 96
let F6FunctionKey = 97
let F7FunctionKey = 98
let F8FunctionKey = 100
override var acceptsFirstResponder: Bool {
get {
return true
}
}
func SelectAll() {
// textDisplayScroller.currentEditor()?.selectAll(nil)
// displayField.selectText(nil)
textDisplay.setSelectedRange(NSRange())
}
func Copy() {
let pasteBoard = NSPasteboard.general
pasteBoard.clearContents()
// TODO: Find a better way to avoid index out of range error when the entire text area is selected
let string = textDisplay.string + " "
let selectedRange = textDisplay.selectedRange()
if selectedRange != NSRange() {
let startIndex = string.index(string.startIndex, offsetBy: selectedRange.lowerBound)
let endIndex = string.index(string.startIndex, offsetBy: selectedRange.upperBound)
let selectedString = string[startIndex..<endIndex]
pasteBoard.setString(String(selectedString), forType: .string)
}
}
func Paste() {
let pasteBoard = NSPasteboard.general
if let str = pasteBoard.string( forType: .string ) {
print("PASTED:", str)
DispatchQueue.global(qos: .background).async {
for char in str.uppercased() {
if let ascii = char.asciiValue {
// TODO: Write separate Paste Accelerator
// disk_accelerator_speedup()
kbdPaste(ascii)
}
}
}
}
}
let mouseCursorHidden = true;
let mouseCursorJoystickEmulation = NSCursor.crosshair
let mouseCursorHiddenJoystickEmulation = NSCursor.init(image: NSImage.init(size: NSSize(width: 1, height: 1)), hotSpot: NSPoint(x: 0, y: 0))
func mouseCursor(hide : Bool) {
if hide {
if mouseCursorHidden {
// NSCursor.hide() is working weird, better to set a 1px transparent cursor
// mouseCursorHiddenJoystickEmulation.set()
mouseCursorJoystickEmulation.set()
}
else {
mouseCursorJoystickEmulation.set()
}
}
else {
// if mouseCursorHidden {
// NSCursor.unhide()
// }
NSCursor.arrow.set()
}
}
func getScreenWithMouse() -> NSScreen? {
let mouseLocation = NSEvent.mouseLocation
let screens = NSScreen.screens
let screenWithMouse = (screens.first { NSMouseInRect(mouseLocation, $0.frame, false) })
return screenWithMouse
}
func convertPoint(toCG point : NSPoint) -> CGPoint {
/// Cocoa and Core Graphics (a.k.a. Quartz) use different coordinate systems. In Cocoa, the origin is at the lower left of the primary screen and y increases as you go up. In Core Graphics, the origin is at the top left of the primary screen and y increases as you go down.
/// Need to convert coordinates from Cocoa to Core Graphics
var cgpoint = view.window!.convertPoint(toScreen: point)
if let screen = getScreenWithMouse() {
cgpoint.y = NSHeight(screen.frame) - cgpoint.y;
}
return cgpoint
}
override func mouseMoved(with event: NSEvent) {
// print(#function)
var location = event.locationInWindow
// displayOrigin = textDisplayScroller.frame.origin.
// print("mx:", location.x, " my:", location.y)
var mouseCursorNeedsReplace = false
if location.x < 8 {
mouseCursorNeedsReplace = true
location.x = 8
}
if location.x > textDisplay.frame.width - 8 {
mouseCursorNeedsReplace = true
location.x = textDisplay.frame.width - 8
}
if location.y < 8 {
mouseCursorNeedsReplace = true
location.y = 8
}
if location.y > textDisplay.frame.height - 8 {
mouseCursorNeedsReplace = true
location.y = textDisplay.frame.height - 8
}
mouseCursor(hide: Mouse2Joystick)
if ( Mouse2Joystick ) {
if mouseCursorNeedsReplace {
CGWarpMouseCursorPosition(convertPoint(toCG: location))
}
pdl_prevarr[0] = pdl_valarr[0]
pdl_valarr[0] = Double(location.x / (textDisplay.frame.width) )
pdl_diffarr[0] = pdl_valarr[0] - pdl_prevarr[0]
pdl_prevarr[1] = pdl_valarr[1]
pdl_valarr[1] = 1 - Double(location.y / (textDisplay.frame.height) )
pdl_diffarr[1] = pdl_valarr[1] - pdl_prevarr[1]
}
if ( MouseInterface ) {
pdl_prevarr[2] = pdl_valarr[2]
pdl_valarr[2] = Double(location.x / (textDisplay.frame.width) )
pdl_diffarr[2] = pdl_valarr[2] - pdl_prevarr[2]
pdl_prevarr[3] = pdl_valarr[3]
pdl_valarr[3] = 1 - Double(location.y / (textDisplay.frame.height) )
pdl_diffarr[3] = pdl_valarr[3] - pdl_prevarr[3]
}
}
var savedVideoMode = videoMode_t.init()
override func keyDown(with event: NSEvent) {
m6502.ecoSpindown = ecoSpindown;
if ( cpuMode == cpuMode_eco ) {
cpuState = cpuState_running;
#if SCHEDULER_CVDISPLAYLINK
CVDisplayLinkStart(displayLink!)
#else
upd.resume()
#endif
}
// print("keyDown")
// switch event.modifierFlags.intersection(.deviceIndependentFlagsMask) {
// case [.command] where event.characters == "l",
// [.command, .shift] where event.characters == "l":
// print("command-l or command-shift-l")
// default:
// break
// }
// print( "key = " + (event.charactersIgnoringModifiers ?? ""))
// print( "\ncharacter = " + (event.characters ?? ""))
if event.modifierFlags.contains(.command) { // .shift, .option, .control ...
if let chars = event.charactersIgnoringModifiers {
switch chars {
case "a":
// print("CMD + A")
SelectAll()
return // to avoid deselect text
case "c":
// print("CMD + C")
Copy()
case "v":
// print("CMD + V")
Paste()
default:
super.keyDown(with: event)
}
}
}
else {
#if FUNCTIONTEST
#else
let keyCode = Int(event.keyCode)
switch keyCode {
case leftArrowKey:
// print("LEFT");
if ( Keyboard2Joystick ) {
// Keyboard 2 JoyStick (Game Controller / Paddle)
pdl_valarr[0] = 0
}
kbdInput(0x88)
case rightArrowKey:
// print("RIGHT")
// Keyboard 2 JoyStick (Game Controller / Paddle)
if ( Keyboard2Joystick ) {
pdl_valarr[0] = 1
}
kbdInput(0x95)
case downArrowKey:
// print("DOWN")
// Keyboard 2 JoyStick (Game Controller / Paddle)
if ( Keyboard2Joystick ) {
pdl_valarr[1] = 1
}
else {
kbdInput(0x8A)
}
case upArrowKey:
// print("UP")
// Keyboard 2 JoyStick (Game Controller / Paddle)
if ( Keyboard2Joystick ) {
pdl_valarr[1] = 0
}
else {
kbdInput(0x8B)
}
case F4FunctionKey:
// if let debugger = DebuggerWindowController.current {
// debugger.Continue()
// }
Resume()
case F5FunctionKey:
// if let debugger = DebuggerWindowController.current {
// debugger.Pause()
// }
Pause()
case F6FunctionKey:
if let debugger = DebuggerWindowController.shared {
debugger.Step_Over(event)
}
case F7FunctionKey:
if let debugger = DebuggerWindowController.shared {
debugger.Step_In(event)
}
case F8FunctionKey:
if let debugger = DebuggerWindowController.shared {
debugger.Step_Out(event)
}
default:
// print("keycode: %d", keyCode)
if let chars = event.characters {
let char = chars.uppercased()[chars.startIndex]
if let ascii = char.asciiValue {
kbdInput(ascii)
}
}
}
#endif
}
// displayField.currentEditor()?.selectedRange = NSMakeRange(0, 0)
textDisplay.setSelectedRange(NSRange())
}
override func keyUp(with event: NSEvent) {
// print("keyUp")
// switch event.modifierFlags.intersection(.deviceIndependentFlagsMask) {
// case [.command] where event.characters == "l",
// [.command, .shift] where event.characters == "l":
// print("command-l or command-shift-l")
// default:
// break
// }
// print( "key = " + (event.charactersIgnoringModifiers ?? ""))
// print( "\ncharacter = " + (event.characters ?? ""))
#if FUNCTIONTEST
#else
let keyCode = Int(event.keyCode)
switch keyCode {
case leftArrowKey:
// kbdInput(0x08)
// setIO(0xC064, 127);
// print("left")
// Keyboard 2 JoyStick (Game Controller / Paddle)
pdl_valarr[0] = 0.5
case rightArrowKey:
// kbdInput(0x15)
// setIO(0xC064, 128);
// print("right")
// Keyboard 2 JoyStick (Game Controller / Paddle)
pdl_valarr[0] = 0.5
case downArrowKey:
// kbdInput(0x0B)
// setIO(0xC065, 127);
// print("down")
// Keyboard 2 JoyStick (Game Controller / Paddle)
pdl_valarr[1] = 0.5
case upArrowKey:
// kbdInput(0x0A)
// setIO(0xC065, 128);
// print("up")
// Keyboard 2 JoyStick (Game Controller / Paddle)
pdl_valarr[1] = 0.5
default:
// print("keycode: %d", keyCode)
// if let chars = event.characters {
// let char = chars.uppercased()[chars.startIndex]
// if let ascii = char.asciiValue {
// kbdInput(ascii)
// }
// }
break
}
#endif
kbdUp()
}
override func flagsChanged(with event: NSEvent) {
let flags = event.modifierFlags.intersection(.deviceIndependentFlagsMask)
switch flags {
case [.shift]:
setIO(0xC061, 0)
setIO(0xC062, 0)
setIO(0xC063, 0) // inverted (bit 7: not pressed)
// print("shift key is pressed")
// case [.control]:
// print("control key is pressed")
case [.option] :
setIO(0xC061, 0)
setIO(0xC062, 1 << 7)
setIO(0xC063, 1 << 7) // inverted (bit 7: not pressed)
// print("option key is pressed")
case [.command]:
// print("Command key is pressed")
setIO(0xC061, 1 << 7)
setIO(0xC062, 0)
setIO(0xC063, 1 << 7) // inverted (bit 7: not pressed)
// case [.control, .shift]:
// print("control-shift keys are pressed")
case [.option, .shift]:
setIO(0xC061, 1 << 7)
setIO(0xC062, 0)
setIO(0xC063, 0) // inverted (bit 7: not pressed)
// print("option-shift keys are pressed")
case [.command, .shift]:
setIO(0xC061, 1 << 7)
setIO(0xC062, 0)
setIO(0xC063, 0) // inverted (bit 7: not pressed)
// print("command-shift keys are pressed")
// case [.control, .option]:
// print("control-option keys are pressed")
// case [.control, .command]:
// print("control-command keys are pressed")
case [.option, .command]:
setIO(0xC061, 1 << 7)
setIO(0xC062, 1 << 7)
setIO(0xC063, 1 << 7) // inverted (bit 7: not pressed)
// print("option-command keys are pressed")
// case [.shift, .control, .option]:
// print("shift-control-option keys are pressed")
// case [.shift, .control, .command]:
// print("shift-control-command keys are pressed")
// case [.control, .option, .command]:
// print("control-option-command keys are pressed")
case [.shift, .command, .option]:
setIO(0xC061, 1 << 7)
setIO(0xC062, 1 << 7)
setIO(0xC063, 0); // inverted (bit 7: not pressed)
// print("shift-command-option keys are pressed")
// case [.shift, .control, .option, .command]:
// print("shift-control-option-command keys are pressed")
// case [.function]:
// print("function key is pressed")
// case [.capsLock]:
// print("capsLock key is pressed")
case [.control, .command, .option]:
Mouse2Joystick = !Mouse2Joystick
mouseCursor(hide: Mouse2Joystick)
ToolBarController.current?.MouseToJoystickMenuItem.state = Mouse2Joystick ? .on : .off
default:
setIO(0xC061, 0)
setIO(0xC062, 0)
setIO(0xC063, 1 << 7) // inverted (bit 7: not pressed)
// print("no modifier keys are pressed")
}
}
var was = 0;
var currentVideoMode = videoMode
var lastFrameTime = CACurrentMediaTime() as Double
var frameCounter : UInt32 = 0
var clkCounter : Double = 0
var shadowTxt : String = ""
var unicodeTextString : String = ""
func Flash() {
self.frameCnt += 1
if ( self.frameCnt == fps / video_fps_divider / 2 ) {
if !flashInverted {
ViewController.charConvTbl = ViewController.charConvTblFlashOn
flashInverted = true
}
}
else if ( self.frameCnt >= fps / video_fps_divider ) {
self.frameCnt = 0
if flashInverted {
ViewController.charConvTbl = ViewController.charConvTblFlashOff
flashInverted = false
}
}
}
var textNeedRender = false
func RenderText() {
Flash()
textNeedRender = false
var fromLines = 0
var toLines = textLines
if videoMode.text == 0 {
if videoMode.mixed == 1 {
fromLines = toLines - 4
}
else {
toLines = 0
}
}
unicodeTextArray = NSArray(array: txtClear, copyItems: true) as! [Character]
// render an empty space to eiminate displaying text portion of the screen covered by graphics
let charDisposition = videoMode.col80 == 0 ? 1 : 2
for y in 0 ..< fromLines {
unicodeTextArray[ y * (textCols * charDisposition + lineEndChars) + textCols * charDisposition] = "\n"
}
// 40 col
if videoMode.col80 == 0 {
if MEMcfg.txt_page_2 == 0 {
if (MEMcfg.RD_AUX_MEM == 0) {
textBufferPointer = ViewController.textPage1Pointer
}
else {
textBufferPointer = ViewController.textIntPage1BufferPointer
}
}
else {
if (MEMcfg.RD_AUX_MEM == 0) {
textBufferPointer = ViewController.textPage2Pointer
}
else {
textBufferPointer = ViewController.textIntPage2BufferPointer
}
}
if textBufferPointer.elementsEqual(ViewController.textPageShadowBuffer) {
}
else {
ViewController.textPage1Pointer.copyBytes(to: ViewController.textPageShadowBuffer)
textNeedRender = true
// render the rest of the text screen
for y in fromLines ..< toLines {
for x in 0 ..< textCols {
let byte = textBufferPointer[ ViewController.textLineOfs[y] + x ]
let idx = Int(byte);
let chr = ViewController.charConvTbl[idx]
unicodeTextArray[ y * (textCols + lineEndChars) + x ] = chr
}
unicodeTextArray[ y * (textCols + lineEndChars) + textCols ] = "\n"
}
unicodeTextString = String(unicodeTextArray)
}
}
// 80 col
else {
let auxPage = ( MEMcfg.is_80STORE == 1 ) && ( MEMcfg.txt_page_2 == 1 )
let textIntBuffer = auxPage ? ViewController.textIntPage1BufferPointer : ViewController.textPage1Pointer
let textAuxBuffer = auxPage ? ViewController.textPage1Pointer : ViewController.textAuxBufferPointer
// render the rest of the text screen
for y in fromLines ..< toLines {
for x in 0 ..< textCols {
let byte = textIntBuffer[ ViewController.textLineOfs[y] + x ]
let idx = Int(byte);
let chr = ViewController.charConvTbl[idx]
unicodeTextArray[ y * (textCols * 2 + lineEndChars) + x * 2 + 1] = chr
let byte2 = textAuxBuffer[ ViewController.textLineOfs[y] + x ]
let idx2 = Int(byte2);
let chr2 = ViewController.charConvTbl[idx2]
unicodeTextArray[ y * (textCols * 2 + lineEndChars) + x * 2] = chr2
}
unicodeTextArray[ y * (textCols * 2 + lineEndChars) + textCols * 2] = "\n"
}
unicodeTextString = String(unicodeTextArray)
}
}
func SetSplashScreenFont() {
for view in splashScreen.subviews {
if view is NSTextField {
let textField = view as! NSTextField
if let fontSize = textField.font?.pointSize {
textField.font = NSFont(name: "PrintChar21", size: fontSize)
}
}
}
// Set Apple ][ font
if let fontSize = textDisplay.font?.pointSize {
textDisplay.font = NSFont(name: "PrintChar21", size: fontSize)
ViewController.charConvTblFlashOn = ViewController.charConvTblFlashOn40
ViewController.charConvTblFlashOff = ViewController.charConvTblFlashOff40
}
}
func SetCol40() {
// Set Apple ][ font
if let fontSize = textDisplay.font?.pointSize {
textDisplay.font = NSFont(name: "PrintChar21", size: fontSize)
ViewController.charConvTblFlashOn = ViewController.charConvTblFlashOn40
ViewController.charConvTblFlashOff = ViewController.charConvTblFlashOff40
}
}
func SetCol80() {
// Set Apple ][ font
if let fontSize = textDisplay.font?.pointSize {
textDisplay.font = NSFont(name: "PRNumber3", size: fontSize)
ViewController.charConvTblFlashOn = ViewController.charConvTblCol80
ViewController.charConvTblFlashOff = ViewController.charConvTblCol80
}
}
func UpdateText() {
// TODO: Render text Screen in native C
// txt = String(bytesNoCopy: ViewController.textScreen!, length: 10, encoding: .ascii, freeWhenDone: false) ?? "HMM"
if videoMode.col80 != currentVideoMode.col80 {
currentVideoMode.col80 = videoMode.col80
if videoMode.col80 == 1 {
SetCol80()
}
else {
SetCol40()
}
}
if textNeedRender || shadowTxt != unicodeTextString {
shadowTxt = unicodeTextString
let selectedRange = textDisplay.selectedRange()
// DispatchQueue.main.async { [self] in
textDisplay.string = unicodeTextString
textDisplay.setSelectedRange(selectedRange)
// }
// let bold14 = NSFont.boldSystemFont(ofSize: 14.0)
// let textColor = NSColor.red
// let attribs = [NSAttributedString.Key.font:bold14,NSAttributedString.Key.foregroundColor:textColor,NSAttributedString.Key.paragraphStyle:textParagraph]
// let textParagraph = NSMutableParagraphStyle()
// textParagraph.lineSpacing = 0
// textParagraph.minimumLineHeight = 32.0
// textParagraph.maximumLineHeight = 32.0
//
// let attribs = [NSAttributedString.Key.paragraphStyle: textParagraph]
// let attrString:NSAttributedString = NSAttributedString.init(string: unicodeTextString, attributes: attribs)
// display.attributedStringValue = attrString
}
// display.stringValue = "testing\nit\nout"
}
func UpdateCPUspeed() {
// DispatchQueue.main.async { [self] in
// under ~1.5 MHz -- 3 decimals to be able to display 1.023 MHz
if ( (mhz < 1.4) && (mhz != floor(mhz)) ) {
speedometer.stringValue = String(format: "%0.3lf MHz", mhz);
}
// under ~100 MHz -- 1 decimal
else if (mhz < 95) {
speedometer.stringValue = String(format: "%0.1lf MHz", mhz);
}
// over ~1000 MHz -- 1 decimal GHz
else if (mhz > 950) {
speedometer.stringValue = String(format: "%0.1lf GHz", mhz / 1000);
}
// hundreds -- no decimals
else {
speedometer.stringValue = String(format: "%0.0lf MHz", mhz);
}
// }
}
func RenderGraphics() {
// only refresh graphics view when needed (aka not in text mode)
if ( videoMode.text == 0 ) {
if ( videoMode.hires == 0 ) {
// when we change video mode, buffer needs to be cleared to avoid artifacts
if ( savedVideoMode.text == 1 )
|| ( savedVideoMode.mixed != videoMode.mixed )
|| ( savedVideoMode.hires != videoMode.hires )
{
lores.clearScreen()
lores.isHidden = false
hires.isHidden = true
unicodeTextString = String(unicodeTextArray)
}
lores.Render()
}
else {
// when we change video mode, buffer needs to be cleared to avoid artifacts
if ( savedVideoMode.text == 1 )
|| ( savedVideoMode.mixed != videoMode.mixed )
|| ( savedVideoMode.hires != videoMode.hires )
{
hires.clearScreen()
hires.isHidden = false
lores.isHidden = true
unicodeTextString = String(unicodeTextArray)
}
hires.Render()
}
}
else if ( savedVideoMode.text == 0 ) {
// we just switched from grahics to text
lores.isHidden = true
hires.isHidden = true
unicodeTextString = String(unicodeTextArray)
}
savedVideoMode = videoMode
}
func Render() {
self.RenderText()
// Rendering is happening in the main thread, which has two implications:
// 1. We can update UI elements
// 2. it is independent of the simulation, de that is running in the background thread while we are busy with rendering...
// DispatchQueue.global(qos: .userInitiated).async {
DispatchQueue.main.async {
self.UpdateText()
self.UpdateCPUspeed()
#if HIRES
self.RenderGraphics()
#endif // HIRES
}
}
override func mouseDown(with event: NSEvent) {
// print(#function)
switch event.modifierFlags.intersection(.deviceIndependentFlagsMask) {
case [.control, .command, .option]:
Mouse2Joystick = !Mouse2Joystick
mouseCursor(hide: Mouse2Joystick)
ToolBarController.current?.MouseToJoystickMenuItem.state = Mouse2Joystick ? .on : .off
default:
break
}
if ( Mouse2Joystick ) {
setIO(0xC061, 1 << 7)
}
}
override func mouseUp(with event: NSEvent) {
// print(#function)
if ( Mouse2Joystick ) {
setIO(0xC061, 0)
}
}
override func rightMouseDown(with event: NSEvent) {
// print(#function)
if ( Mouse2Joystick ) {
setIO(0xC062, 1 << 7)
}
}
override func rightMouseUp(with event: NSEvent) {
// print(#function)
if ( Mouse2Joystick ) {
setIO(0xC062, 0)
}
}
override func otherMouseDown(with event: NSEvent) {
// print(#function)
if ( Mouse2Joystick ) {
setIO(0xC063, 0) // inverted (bit 7: 0 = pressed)
}
}
override func otherMouseUp(with event: NSEvent) {
// print(#function)
if ( Mouse2Joystick ) {
setIO(0xC063, 1 << 7) // inverted (bit 7: 1 = not pressed)
}
}
func diskButtonUpdate() {
DispatchQueue.main.async {
if ( self.frameCounter % DEF_DRV_LED_DIV == 0 ) {
// Disk Motor LED
if spkr_is_disk_motor_playing() {
if self.disk1_led.isHidden {
self.disk1_led.isHidden = false
}
}
else {
if !self.disk1_led.isHidden {
self.disk1_led.isHidden = true
}
}
// Disk Loaded
if woz_is_loaded() > 0 {
if self.disk1_closed.isHidden {
self.disk1_closed.isHidden = false
}
}
else {
if !self.disk1_closed.isHidden {
self.disk1_closed.isHidden = true
}
}
}
}
}
func debugBreak() {
Pause()
spkr_play_disk_motor_time = 0
spkr_stopAll()
// TODO: This should be in Debugger!
debuggerPauseUpdate()
debuggerShowWindow()
}
let UpdateSemaphore = DispatchSemaphore(value: 1)
func Update() {
// clk_6502_per_frm_max = 0
if UpdateSemaphore.wait(timeout: .now() + 0.001) == .timedOut {
// get back here next time...
return
}
diskButtonUpdate()
switch cpuState {
case cpuState_running:
clkCounter += Double(m6502.clkfrm)
// we start a new frame from here, so CPU is running even while rendering
// m6502.clkfrm = 0
frameCounter += 1
if ( frameCounter % fps == 0 ) {
let currentTime = CACurrentMediaTime() as Double
let elpasedTime = currentTime - lastFrameTime
lastFrameTime = currentTime
mhz = Double( clkCounter ) / (elpasedTime * M);
clkCounter = 0
}
#if SPEEDTEST
#else
// poll input devices like mouse and joystick
// Input()
// run some code
// cpuState = cpuState_executing
// DispatchQueue.global(qos: .userInitiated).async {
if m6502.debugger.on {
m6502_Debug()
switch m6502.interrupt {
case HALT:
debugBreak()
case BREAK: // BRK instruction
debugBreak()
case BREAKPOINT: // CPU halted because of a breakpoint
debugBreak()
case BREAKRDMEM: // CPU halted because of a breakpoint
debugBreak()
case BREAKWRMEM: // CPU halted because of a breakpoint
debugBreak()
case RET:
if m6502.debugger.mask.ret == 1 {
// Step_Out / Step_Over
if m6502.PC >= m6502.debugger.SP {
debugBreak()
}
}
case INV: // invalid instruction
debugBreak()
default:
break
}
// clear iterrupt
m6502.interrupt = NO_INT
}
else {
// DispatchQueue.global(qos: .userInitiated).async {
m6502_Run()
// }
// cpuState = cpuState_running
}
// video rendering
if ( frameCounter % video_fps_divider == 0 ) {
Render()
}
// TODO: This should be in Debugger!
if let debugger = DebuggerViewController.shared {
debugger.Update()
}
#endif
break
case cpuState_executing:
// prevent running more instances per session
// setCPUClockSpeed(freq: MHz_6502 - 1)
break
case cpuState_halting:
cpuState = cpuState_halted
// last video rendering before halt
Render()
break
case cpuState_halted:
#if SCHEDULER_CVDISPLAYLINK
CVDisplayLinkStop(displayLink!)
#else
upd.suspend()
#endif
break
default:
break
}
// ok, from now you can update again
UpdateSemaphore.signal()
}
// func FromBuf(ptr: UnsafeMutablePointer<UInt8>, length len: Int) -> String? {
// // convert the bytes using the UTF8 encoding
// if let theString = NSString(bytes: ptr, length: len, encoding: NSUTF8StringEncoding) {
// return theString as String
// } else {
// return nil // the bytes aren't valid UTF8
// }
// }
#if SCHEDULER_CVDISPLAYLINK
#else
var upd = RepeatingTimer(timeInterval: 1)
func newUpdateTimer( timeInterval : Double ) {
upd.kill()
upd = RepeatingTimer(timeInterval: timeInterval)
upd.eventHandler = {
self.Update()
}
upd.resume()
}
#endif
// Kelvin Sherlock's fix to avoid uninstalled font problems
override func awakeFromNib() {
// self.display.font = NSFont(name: "PrintChar21", size: 32)
}
required init?(coder: NSCoder) {
// print(#function)
super.init(coder: coder)
ViewController.shared = self
}
// func render() {
//
// var x = vertexData[2 * 3 + 0]
// x -= 0.01
// if x < -1 {
// x = 1
// }
// vertexData[2 * 3 + 0] = x
//
// guard let drawable = metalLayer?.nextDrawable() else { return }
// let renderPassDescriptor = MTLRenderPassDescriptor()
// renderPassDescriptor.colorAttachments[0].texture = drawable.texture
// renderPassDescriptor.colorAttachments[0].loadAction = .clear
// renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(
// red: 0.0,
// green: 104.0/255.0,
// blue: 55.0/255.0,
// alpha: 0.3)
//
// if let commandBuffer = commandQueue.makeCommandBuffer() {
// if let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) {
// renderEncoder.setRenderPipelineState(pipelineState)
// let dataSize = vertexData.count * MemoryLayout.size(ofValue: vertexData[0]) // 1
// vertexBuffer = device.makeBuffer(bytes: vertexData, length: dataSize, options: []) // 2
// renderEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
// renderEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 3, instanceCount: 1)
// renderEncoder.endEncoding()
// commandBuffer.present(drawable)
// commandBuffer.commit()
// }
// }
// }
//
//
// var vertexData: [Float] = [
// 0.0, 1.0, 0.0,
// -1.0, -1.0, 0.0,
// 1.0, -1.0, 0.0
// ]
#if SCHEDULER_CVDISPLAYLINK
// The callback function is called everytime CVDisplayLink says its time to get a new frame.
let displayLinkOutputCallback : CVDisplayLinkOutputCallback = { (displayLink: CVDisplayLink, _ inNow: UnsafePointer<CVTimeStamp>, _ inOutputTime: UnsafePointer<CVTimeStamp>, _ flagsIn: CVOptionFlags, _ flagsOut: UnsafeMutablePointer<CVOptionFlags>, _ vController: UnsafeMutableRawPointer?) -> CVReturn in
/* The displayLinkContext is CVDisplayLink's parameter definition of the view in which we are working.
In order to access the methods of a given view we need to specify what kind of view it is as right
now the UnsafeMutablePointer<Void> just means we have a pointer to "something". To cast the pointer
such that the compiler at runtime can access the methods associated with our SwiftOpenGLView, we use
an unsafeBitCast. The definition of which states, "Returns the the bits of x, interpreted as having
type U." We may then call any of that view's methods. Here we call drawView() which we draw a
frame for rendering. */
// unsafeBitCast(displayLinkContext, SwiftOpenGLView.self).renderFrame()
/// This can be a precise FPS updater so MHz would be dead perfect pitch -- however, it makes things worse
// let now : CVTimeStamp = inNow.pointee
//// print( "RateScaler:", now.rateScalar )
// if (mySelf.last_frame_time != 0) {
// let videoTimeDiff = now.videoTime - mySelf.last_frame_time
// let currentFPS = Double(now.videoTimeScale) / Double(videoTimeDiff)
//
// fps = currentFPS
// // mySelf.setCPUClockSpeed(freq: MHz_6502)
// clk_6502_per_frm = UInt64( MHz_6502 * M / currentFPS )
// clk_6502_per_frm_set = clk_6502_per_frm
//
// }
// mySelf.last_frame_time = now.videoTime;
let mySelf = Unmanaged<ViewController>.fromOpaque(vController!).takeUnretainedValue()
mySelf.Update();
// We are going to assume that everything went well for this mock up, and pass success as the CVReturn
return kCVReturnSuccess
}
// sets a callback at every screen refresh (normally 60Hz)
func setupDisplayLink() {
// Grab the a link to the active displays, set the callback defined above, and start the link.
/* An alternative to a nested function is a global function or a closure passed as the argument--a local function
(i.e. a function defined within the class) is NOT allowed. */
// The UnsafeMuta