From 89314a0e1a4d5dea11b91e8bcf39a520d67be726 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Tue, 2 Jul 2019 20:48:14 +0200 Subject: [PATCH] fix reading and writing rtc jiffy clock, memory can now intercept reads and writes --- compiler/res/version.txt | 2 +- compiler/src/prog8/StackVmMain.kt | 3 +- compiler/src/prog8/astvm/AstVm.kt | 52 ++++- compiler/src/prog8/astvm/BuiltinFunctions.kt | 2 +- compiler/src/prog8/astvm/Memory.kt | 80 +++++--- compiler/src/prog8/astvm/ScreenDialog.kt | 3 +- compiler/src/prog8/stackvm/Memory.kt | 102 ---------- compiler/src/prog8/stackvm/ScreenDialog.kt | 188 ------------------- compiler/src/prog8/stackvm/StackVm.kt | 47 ++++- examples/test.p8 | 54 +++++- 10 files changed, 192 insertions(+), 341 deletions(-) delete mode 100644 compiler/src/prog8/stackvm/Memory.kt delete mode 100644 compiler/src/prog8/stackvm/ScreenDialog.kt diff --git a/compiler/res/version.txt b/compiler/res/version.txt index 625934097..ab9dda59f 100644 --- a/compiler/res/version.txt +++ b/compiler/res/version.txt @@ -1 +1 @@ -1.8 +1.9-dev diff --git a/compiler/src/prog8/StackVmMain.kt b/compiler/src/prog8/StackVmMain.kt index 757972e19..99e46111b 100644 --- a/compiler/src/prog8/StackVmMain.kt +++ b/compiler/src/prog8/StackVmMain.kt @@ -1,5 +1,6 @@ package prog8 +import prog8.astvm.ScreenDialog import prog8.stackvm.* import java.awt.EventQueue import javax.swing.Timer @@ -19,7 +20,7 @@ fun stackVmMain(args: Array) { val program = Program.load(args.first()) val vm = StackVm(traceOutputFile = null) - val dialog = ScreenDialog() + val dialog = ScreenDialog("StackVM") vm.load(program, dialog.canvas) EventQueue.invokeLater { dialog.pack() diff --git a/compiler/src/prog8/astvm/AstVm.kt b/compiler/src/prog8/astvm/AstVm.kt index 7a7f3ab70..0962627b6 100644 --- a/compiler/src/prog8/astvm/AstVm.kt +++ b/compiler/src/prog8/astvm/AstVm.kt @@ -5,6 +5,9 @@ import prog8.compiler.RuntimeValue import prog8.compiler.RuntimeValueRange import prog8.compiler.target.c64.Petscii import java.awt.EventQueue +import kotlin.NoSuchElementException +import kotlin.concurrent.fixedRateTimer +import kotlin.math.min class VmExecutionException(msg: String?) : Exception(msg) @@ -111,13 +114,19 @@ class RuntimeVariables { class AstVm(val program: Program) { - val mem = Memory() + + val mem = Memory(::memread, ::memwrite) val statusflags = StatusFlags() - private var dialog = ScreenDialog() + private var dialog = ScreenDialog("AstVM") var instructionCounter = 0 + val bootTime = System.currentTimeMillis() + var rtcOffset = bootTime init { + // observe the jiffyclock + mem.observe(0xa0, 0xa1, 0xa2) + dialog.requestFocusInWindow() EventQueue.invokeLater { @@ -125,6 +134,27 @@ class AstVm(val program: Program) { dialog.isVisible = true dialog.start() } + + fixedRateTimer("60hz-irq", true, period=1000/60) { + irq(this.scheduledExecutionTime()) + } + } + + fun memread(address: Int, value: Short): Short { + // println("MEM READ $address -> $value") + return value + } + + fun memwrite(address: Int, value: Short): Short { + if(address==0xa0 || address==0xa1 || address==0xa2) { + // a write to the jiffy clock, update the clock offset for the irq + val time_hi = if(address==0xa0) value else mem.getUByte_DMA(0xa0) + val time_mid = if(address==0xa1) value else mem.getUByte_DMA(0xa1) + val time_lo = if(address==0xa2) value else mem.getUByte_DMA(0xa2) + val jiffies = (time_hi.toInt() shl 16) + (time_mid.toInt() shl 8) + time_lo + rtcOffset = bootTime - (jiffies*1000/60) + } + return value } fun run() { @@ -188,6 +218,18 @@ class AstVm(val program: Program) { } } + private fun irq(timeStamp: Long) { + // 60hz IRQ handling + if(statusflags.irqd) + return // interrupt is disabled + + val jiffies = min((timeStamp-rtcOffset)*60/1000, 24*3600*60-1) + // update the C-64 60hz jiffy clock in the ZP addresses: + mem.setUByte_DMA(0x00a0, (jiffies ushr 16).toShort()) + mem.setUByte_DMA(0x00a1, (jiffies ushr 8 and 255).toShort()) + mem.setUByte_DMA(0x00a2, (jiffies and 255).toShort()) + } + private val runtimeVariables = RuntimeVariables() private val functions = BuiltinFunctions() private val evalCtx = EvalContext(program, mem, statusflags, runtimeVariables, functions, ::executeSubroutine) @@ -509,12 +551,18 @@ class AstVm(val program: Program) { "c64scr.print_ub" -> { dialog.canvas.printText(args[0].byteval!!.toString(), 1, true) } + "c64scr.print_ub0" -> { + dialog.canvas.printText("%03d".format(args[0].byteval!!), 1, true) + } "c64scr.print_b" -> { dialog.canvas.printText(args[0].byteval!!.toString(), 1, true) } "c64scr.print_uw" -> { dialog.canvas.printText(args[0].wordval!!.toString(), 1, true) } + "c64scr.print_uw0" -> { + dialog.canvas.printText("%05d".format(args[0].wordval!!), 1, true) + } "c64scr.print_w" -> { dialog.canvas.printText(args[0].wordval!!.toString(), 1, true) } diff --git a/compiler/src/prog8/astvm/BuiltinFunctions.kt b/compiler/src/prog8/astvm/BuiltinFunctions.kt index e98a1178d..28c218242 100644 --- a/compiler/src/prog8/astvm/BuiltinFunctions.kt +++ b/compiler/src/prog8/astvm/BuiltinFunctions.kt @@ -160,7 +160,7 @@ class BuiltinFunctions { null } "mkword" -> { - val result = (args[0].integerValue() shl 8) or args[1].integerValue() + val result = (args[1].integerValue() shl 8) or args[0].integerValue() RuntimeValue(DataType.UWORD, result) } "set_carry" -> { diff --git a/compiler/src/prog8/astvm/Memory.kt b/compiler/src/prog8/astvm/Memory.kt index 9aeaa0cce..1b2c64f26 100644 --- a/compiler/src/prog8/astvm/Memory.kt +++ b/compiler/src/prog8/astvm/Memory.kt @@ -4,36 +4,57 @@ import prog8.compiler.target.c64.Mflpt5 import prog8.compiler.target.c64.Petscii import kotlin.math.abs -class Memory { +class Memory(private val readObserver: (address: Int, value: Short) -> Short, + private val writeObserver: (address: Int, value: Short) -> Short) +{ + private val mem = ShortArray(65536) // shorts because byte is signed and we store values 0..255 + private val observed = BooleanArray(65536) // what addresses are observed + + + fun observe(vararg address: Int) { + address.forEach { observed[it]=true } + } fun getUByte(address: Int): Short { + return if(observed[address]) readObserver(address, mem[address]) + else mem[address] + } + + fun getUByte_DMA(address: Int): Short { return mem[address] } fun getSByte(address: Int): Short { val ubyte = getUByte(address) - if(ubyte <= 127) - return ubyte - return (-((ubyte.toInt() xor 255)+1)).toShort() // 2's complement + return if(ubyte <= 127) ubyte + else (-((ubyte.toInt() xor 255)+1)).toShort() // 2's complement } fun setUByte(address: Int, value: Short) { if(value !in 0..255) - throw VmExecutionException("ubyte value out of range") + throw VmExecutionException("ubyte value out of range $value") + mem[address] = + if(observed[address]) writeObserver(address, value) + else value + } + + fun setUByte_DMA(address: Int, value: Short) { + if(value !in 0..255) + throw VmExecutionException("ubyte value out of range $value") mem[address] = value } fun setSByte(address: Int, value: Short) { - if(value !in -128..127) throw VmExecutionException("byte value out of range") - if(value>=0) - mem[address] = value - else - mem[address] = ((abs(value.toInt()) xor 255)+1).toShort() // 2's complement + if(value !in -128..127) throw VmExecutionException("byte value out of range $value") + val ubyte = + if(value>=0) value + else ((abs(value.toInt()) xor 255)+1).toShort() // 2's complement + setUByte(address, ubyte) } fun getUWord(address: Int): Int { - return mem[address] + 256*mem[address+1] + return getUByte(address) + 256*getUByte(address+1) } fun getSWord(address: Int): Int { @@ -45,13 +66,13 @@ class Memory { fun setUWord(address: Int, value: Int) { if(value !in 0..65535) - throw VmExecutionException("uword value out of range") - mem[address] = value.and(255).toShort() - mem[address+1] = (value / 256).toShort() + throw VmExecutionException("uword value out of range $value") + setUByte(address, value.and(255).toShort()) + setUByte(address+1, (value / 256).toShort()) } fun setSWord(address: Int, value: Int) { - if(value !in -32768..32767) throw VmExecutionException("word value out of range") + if(value !in -32768..32767) throw VmExecutionException("word value out of range $value") if(value>=0) setUWord(address, value) else @@ -60,23 +81,24 @@ class Memory { fun setFloat(address: Int, value: Double) { val mflpt5 = Mflpt5.fromNumber(value) - mem[address] = mflpt5.b0 - mem[address+1] = mflpt5.b1 - mem[address+2] = mflpt5.b2 - mem[address+3] = mflpt5.b3 - mem[address+4] = mflpt5.b4 + setUByte(address, mflpt5.b0) + setUByte(address+1, mflpt5.b1) + setUByte(address+2, mflpt5.b2) + setUByte(address+3, mflpt5.b3) + setUByte(address+4, mflpt5.b4) } fun getFloat(address: Int): Double { - return Mflpt5(mem[address], mem[address + 1], mem[address + 2], mem[address + 3], mem[address + 4]).toDouble() + return Mflpt5(getUByte(address), getUByte(address + 1), getUByte(address + 2), + getUByte(address + 3), getUByte(address + 4)).toDouble() } fun setString(address: Int, str: String) { // lowercase PETSCII val petscii = Petscii.encodePetscii(str, true) var addr = address - for (c in petscii) mem[addr++] = c - mem[addr] = 0 + for (c in petscii) setUByte(addr++, c) + setUByte(addr, 0) } fun getString(strAddress: Int): String { @@ -84,7 +106,7 @@ class Memory { val petscii = mutableListOf() var addr = strAddress while(true) { - val byte = mem[addr++] + val byte = getUByte(addr++) if(byte==0.toShort()) break petscii.add(byte) } @@ -92,12 +114,12 @@ class Memory { } fun clear() { - for(i in 0..65535) mem[i]=0 + for(i in 0..65535) setUByte(i, 0) } fun copy(from: Int, to: Int, numbytes: Int) { for(i in 0 until numbytes) - mem[to+i] = mem[from+i] + setUByte(to+i, getUByte(from+i)) } fun getScreencodeString(strAddress: Int): String? { @@ -105,7 +127,7 @@ class Memory { val screencodes = mutableListOf() var addr = strAddress while(true) { - val byte = mem[addr++] + val byte = getUByte(addr++) if(byte==0.toShort()) break screencodes.add(byte) } @@ -116,7 +138,7 @@ class Memory { // lowercase screencodes val screencodes = Petscii.encodeScreencode(str, true) var addr = address - for (c in screencodes) mem[addr++] = c - mem[addr] = 0 + for (c in screencodes) setUByte(addr++, c) + setUByte(addr, 0) } } diff --git a/compiler/src/prog8/astvm/ScreenDialog.kt b/compiler/src/prog8/astvm/ScreenDialog.kt index e63265e5b..9c5ad1ae7 100644 --- a/compiler/src/prog8/astvm/ScreenDialog.kt +++ b/compiler/src/prog8/astvm/ScreenDialog.kt @@ -138,12 +138,11 @@ class BitmapScreenPanel : KeyListener, JPanel() { } -class ScreenDialog : JFrame() { +class ScreenDialog(title: String) : JFrame(title) { val canvas = BitmapScreenPanel() init { val borderWidth = 16 - title = "AstVm graphics. Text I/O goes to console." layout = GridBagLayout() defaultCloseOperation = JFrame.EXIT_ON_CLOSE isResizable = false diff --git a/compiler/src/prog8/stackvm/Memory.kt b/compiler/src/prog8/stackvm/Memory.kt deleted file mode 100644 index ef80064ba..000000000 --- a/compiler/src/prog8/stackvm/Memory.kt +++ /dev/null @@ -1,102 +0,0 @@ -package prog8.stackvm - -import prog8.compiler.target.c64.Mflpt5 -import prog8.compiler.target.c64.Petscii -import kotlin.math.abs - -class Memory { - private val mem = ShortArray(65536) // shorts because byte is signed and we store values 0..255 - - fun getUByte(address: Int): Short { - return mem[address] - } - - fun getSByte(address: Int): Short { - val ubyte = getUByte(address) - if(ubyte <= 127) - return ubyte - return (-((ubyte.toInt() xor 255)+1)).toShort() // 2's complement - } - - fun setUByte(address: Int, value: Short) { - if(value !in 0..255) - throw VmExecutionException("ubyte value out of range") - mem[address] = value - } - - fun setSByte(address: Int, value: Short) { - if(value !in -128..127) throw VmExecutionException("byte value out of range") - if(value>=0) - mem[address] = value - else - mem[address] = ((abs(value.toInt()) xor 255)+1).toShort() // 2's complement - } - - fun getUWord(address: Int): Int { - return mem[address] + 256*mem[address+1] - } - - fun getSWord(address: Int): Int { - val uword = getUWord(address) - if(uword <= 32767) - return uword - return -((uword xor 65535)+1) // 2's complement - } - - fun setUWord(address: Int, value: Int) { - if(value !in 0..65535) - throw VmExecutionException("uword value out of range") - mem[address] = value.and(255).toShort() - mem[address+1] = (value / 256).toShort() - } - - fun setSWord(address: Int, value: Int) { - if(value !in -32768..32767) throw VmExecutionException("word value out of range") - if(value>=0) - setUWord(address, value) - else - setUWord(address, (abs(value) xor 65535)+1) // 2's complement - } - - fun setFloat(address: Int, value: Double) { - val mflpt5 = Mflpt5.fromNumber(value) - mem[address] = mflpt5.b0 - mem[address+1] = mflpt5.b1 - mem[address+2] = mflpt5.b2 - mem[address+3] = mflpt5.b3 - mem[address+4] = mflpt5.b4 - } - - fun getFloat(address: Int): Double { - return Mflpt5(mem[address], mem[address + 1], mem[address + 2], mem[address + 3], mem[address + 4]).toDouble() - } - - fun setString(address: Int, str: String) { - // lowercase PETSCII - val petscii = Petscii.encodePetscii(str, true) - var addr = address - for (c in petscii) mem[addr++] = c - mem[addr] = 0 - } - - fun getString(strAddress: Int): String { - // lowercase PETSCII - val petscii = mutableListOf() - var addr = strAddress - while(true) { - val byte = mem[addr++] - if(byte==0.toShort()) break - petscii.add(byte) - } - return Petscii.decodePetscii(petscii, true) - } - - fun clear() { - for(i in 0..65535) mem[i]=0 - } - - fun copy(from: Int, to: Int, numbytes: Int) { - for(i in 0 until numbytes) - mem[to+i] = mem[from+i] - } -} diff --git a/compiler/src/prog8/stackvm/ScreenDialog.kt b/compiler/src/prog8/stackvm/ScreenDialog.kt deleted file mode 100644 index bdf34d2b6..000000000 --- a/compiler/src/prog8/stackvm/ScreenDialog.kt +++ /dev/null @@ -1,188 +0,0 @@ -package prog8.stackvm - -import prog8.compiler.target.c64.Charset -import prog8.compiler.target.c64.Colors -import prog8.compiler.target.c64.Petscii -import java.awt.* -import java.awt.event.KeyEvent -import java.awt.event.KeyListener -import java.awt.image.BufferedImage -import javax.swing.JFrame -import javax.swing.JPanel -import javax.swing.Timer - - -class BitmapScreenPanel : KeyListener, JPanel() { - - private val image = BufferedImage(SCREENWIDTH, SCREENHEIGHT, BufferedImage.TYPE_INT_ARGB) - private val g2d = image.graphics as Graphics2D - private var cursorX: Int=0 - private var cursorY: Int=0 - - init { - val size = Dimension(image.width * SCALING, image.height * SCALING) - minimumSize = size - maximumSize = size - preferredSize = size - clearScreen(6) - isFocusable = true - requestFocusInWindow() - addKeyListener(this) - } - - override fun keyTyped(p0: KeyEvent?) {} - - override fun keyPressed(p0: KeyEvent?) { - println("pressed: $p0.k") - } - - override fun keyReleased(p0: KeyEvent?) { - println("released: $p0") - } - - override fun paint(graphics: Graphics?) { - val g2d = graphics as Graphics2D? - g2d!!.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF) - g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE) - g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR) - g2d.drawImage(image, 0, 0, image.width * 3, image.height * 3, null) - } - - fun clearScreen(color: Short) { - g2d.background = Colors.palette[color % Colors.palette.size] - g2d.clearRect(0, 0, SCREENWIDTH, SCREENHEIGHT) - cursorX = 0 - cursorY = 0 - } - fun setPixel(x: Int, y: Int, color: Short) { - image.setRGB(x, y, Colors.palette[color % Colors.palette.size].rgb) - } - fun drawLine(x1: Int, y1: Int, x2: Int, y2: Int, color: Short) { - g2d.color = Colors.palette[color % Colors.palette.size] - g2d.drawLine(x1, y1, x2, y2) - } - fun printText(text: String, color: Short, lowercase: Boolean) { - val lines = text.split('\n') - for(line in lines.withIndex()) { - printTextSingleLine(line.value, color, lowercase) - if(line.index=(SCREENWIDTH/8)) { - cursorY++ - cursorX=0 - } - } - } - - fun printChar(char: Short) { - if(char==13.toShort() || char==141.toShort()) { - cursorX=0 - cursorY++ - } else { - setChar(cursorX, cursorY, char, 1) - cursorX++ - if (cursorX >= (SCREENWIDTH / 8)) { - cursorY++ - cursorX = 0 - } - } - } - - fun setChar(x: Int, y: Int, screenCode: Short, color: Short) { - g2d.clearRect(8*x, 8*y, 8, 8) - val colorIdx = (color % Colors.palette.size).toShort() - val coloredImage = Charset.getColoredChar(screenCode, colorIdx) - g2d.drawImage(coloredImage, 8*x, 8*y , null) - } - - fun setCursorPos(x: Int, y: Int) { - cursorX = x - cursorY = y - } - - fun getCursorPos(): Pair { - return Pair(cursorX, cursorY) - } - - fun writeText(x: Int, y: Int, text: String, color: Short, lowercase: Boolean) { - var xx=x - for(clearx in xx until xx+text.length) { - g2d.clearRect(8*clearx, 8*y, 8, 8) - } - for(sc in Petscii.encodeScreencode(text, lowercase)) { - setChar(xx++, y, sc, color) - } - } - - - companion object { - const val SCREENWIDTH = 320 - const val SCREENHEIGHT = 200 - const val SCALING = 3 - } -} - - -class ScreenDialog : JFrame() { - val canvas = BitmapScreenPanel() - - init { - val borderWidth = 16 - title = "StackVm graphics. Text I/O goes to console." - layout = GridBagLayout() - defaultCloseOperation = JFrame.EXIT_ON_CLOSE - isResizable = false - - // the borders (top, left, right, bottom) - val borderTop = JPanel().apply { - preferredSize = Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH+2*borderWidth), BitmapScreenPanel.SCALING * borderWidth) - background = Colors.palette[14] - } - val borderBottom = JPanel().apply { - preferredSize =Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH+2*borderWidth), BitmapScreenPanel.SCALING * borderWidth) - background = Colors.palette[14] - } - val borderLeft = JPanel().apply { - preferredSize =Dimension(BitmapScreenPanel.SCALING * borderWidth, BitmapScreenPanel.SCALING * BitmapScreenPanel.SCREENHEIGHT) - background = Colors.palette[14] - } - val borderRight = JPanel().apply { - preferredSize =Dimension(BitmapScreenPanel.SCALING * borderWidth, BitmapScreenPanel.SCALING * BitmapScreenPanel.SCREENHEIGHT) - background = Colors.palette[14] - } - var c = GridBagConstraints() - c.gridx=0; c.gridy=1; c.gridwidth=3 - add(borderTop, c) - c = GridBagConstraints() - c.gridx=0; c.gridy=2 - add(borderLeft, c) - c = GridBagConstraints() - c.gridx=2; c.gridy=2 - add(borderRight, c) - c = GridBagConstraints() - c.gridx=0; c.gridy=3; c.gridwidth=3 - add(borderBottom, c) - // the screen canvas(bitmap) - c = GridBagConstraints() - c.gridx = 1; c.gridy = 2 - add(canvas, c) - - canvas.requestFocusInWindow() - } - - fun start() { - val repaintTimer = Timer(1000 / 60) { repaint() } - repaintTimer.start() - } -} diff --git a/compiler/src/prog8/stackvm/StackVm.kt b/compiler/src/prog8/stackvm/StackVm.kt index 5d04d7a61..b35fe0052 100644 --- a/compiler/src/prog8/stackvm/StackVm.kt +++ b/compiler/src/prog8/stackvm/StackVm.kt @@ -1,6 +1,8 @@ package prog8.stackvm import prog8.ast.* +import prog8.astvm.BitmapScreenPanel +import prog8.astvm.Memory import prog8.compiler.RuntimeValue import prog8.compiler.HeapValues import prog8.compiler.IntegerOrAddressOf @@ -85,7 +87,7 @@ enum class Syscall(val callNr: Short) { // vm intercepts of system routines: SYSCALLSTUB(200), - SYSASM_c64scr_PLOT(201), + SYSASM_c64scr_plot(201), SYSASM_c64scr_print(202), SYSASM_c64scr_print_ub0(203), SYSASM_c64scr_print_ub(204), @@ -137,7 +139,7 @@ class MyStack : Stack() { class StackVm(private var traceOutputFile: String?) { - val mem = Memory() + val mem = Memory(::memread, ::memwrite) var P_carry: Boolean = false private set var P_zero: Boolean = true @@ -161,12 +163,29 @@ class StackVm(private var traceOutputFile: String?) { private var canvas: BitmapScreenPanel? = null private val rnd = Random() private val bootTime = System.currentTimeMillis() + private var rtcOffset = bootTime private var currentInstructionPtr: Int = -1 private var irqStartInstructionPtr: Int = -1 private var registerSaveX: RuntimeValue = RuntimeValue(DataType.UBYTE, 0) var sourceLine: String = "" private set + fun memread(address: Int, value: Short): Short { + //println("MEM READ $address -> $value") + return value + } + + fun memwrite(address: Int, value: Short): Short { + if(address==0xa0 || address==0xa1 || address==0xa2) { + // a write to the jiffy clock, update the clock offset for the irq + val time_hi = if(address==0xa0) value else mem.getUByte_DMA(0xa0) + val time_mid = if(address==0xa1) value else mem.getUByte_DMA(0xa1) + val time_lo = if(address==0xa2) value else mem.getUByte_DMA(0xa2) + val jiffies = (time_hi.toInt() shl 16) + (time_mid.toInt() shl 8) + time_lo + rtcOffset = bootTime - (jiffies*1000/60) + } + return value + } fun load(program: Program, canvas: BitmapScreenPanel?) { this.program = program.program + Instruction(Opcode.RETURN) // append a RETURN for use in the IRQ handler @@ -241,6 +260,7 @@ class StackVm(private var traceOutputFile: String?) { private fun initMemory(memory: Map>) { mem.clear() + for (meminit in memory) { var address = meminit.key for (value in meminit.value) { @@ -274,6 +294,9 @@ class StackVm(private var traceOutputFile: String?) { } } } + + // observe the jiffyclock + mem.observe(0xa0, 0xa1, 0xa2) } private fun checkDt(value: RuntimeValue?, vararg expected: DataType) { @@ -2266,7 +2289,7 @@ class StackVm(private var traceOutputFile: String?) { } } Syscall.SYSCALLSTUB -> throw VmExecutionException("unimplemented sysasm called: ${ins.callLabel} Create a Syscall enum for this and implement the vm intercept for it.") - Syscall.SYSASM_c64scr_PLOT -> { + Syscall.SYSASM_c64scr_plot -> { val x = variables.getValue("Y").integerValue() val y = variables.getValue("A").integerValue() canvas?.setCursorPos(x, y) @@ -2280,6 +2303,10 @@ class StackVm(private var traceOutputFile: String?) { val num = variables.getValue("A").integerValue() canvas?.printText(num.toString(), 1, true) } + Syscall.SYSASM_c64scr_print_ub0 -> { + val num = variables.getValue("A").integerValue() + canvas?.printText("%03d".format(num), 1, true) + } Syscall.SYSASM_c64scr_print_b -> { val num = variables.getValue("A").integerValue() if(num<=127) @@ -2293,6 +2320,12 @@ class StackVm(private var traceOutputFile: String?) { val number = lo+256*hi canvas?.printText(number.toString(), 1, true) } + Syscall.SYSASM_c64scr_print_uw0 -> { + val lo = variables.getValue("A").integerValue() + val hi = variables.getValue("Y").integerValue() + val number = lo+256*hi + canvas?.printText("%05d".format(number), 1, true) + } Syscall.SYSASM_c64scr_print_uwhex -> { val prefix = if(this.P_carry) "$" else "" val lo = variables.getValue("A").integerValue() @@ -2332,11 +2365,11 @@ class StackVm(private var traceOutputFile: String?) { P_irqd=true swapIrqExecutionContexts(true) - val jiffies = min((timestamp-bootTime)*60/1000, 24*3600*60-1) + val jiffies = min((timestamp-rtcOffset)*60/1000, 24*3600*60-1) // update the C-64 60hz jiffy clock in the ZP addresses: - mem.setUByte(0x00a0, (jiffies ushr 16).toShort()) - mem.setUByte(0x00a1, (jiffies ushr 8 and 255).toShort()) - mem.setUByte(0x00a2, (jiffies and 255).toShort()) + mem.setUByte_DMA(0x00a0, (jiffies ushr 16).toShort()) + mem.setUByte_DMA(0x00a1, (jiffies ushr 8 and 255).toShort()) + mem.setUByte_DMA(0x00a2, (jiffies and 255).toShort()) if(irqStartInstructionPtr>=0) { try { diff --git a/examples/test.p8 b/examples/test.p8 index 63e8897c4..182b3087f 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,21 +1,59 @@ %import c64utils %zeropage basicsafe - +%import c64flt ~ main { sub start() { - ubyte x = 99 + c64.TIME_HI = 22 + c64.TIME_MID = 33 + c64.TIME_LO = 44 - return + loop: + ubyte hi = c64.TIME_HI + ubyte mid = c64.TIME_MID + ubyte lo = c64.TIME_LO - startqqq: + c64scr.plot(0,0) + c64scr.print_ub0(hi) + c64scr.print(" \n") + c64scr.print_ub0(mid) + c64scr.print(" \n") + c64scr.print_ub0(lo) + c64scr.print(" \n") + + uword x = mkword(c64.TIME_LO, c64.TIME_MID) + c64scr.print_uw(x) + c64scr.print(" \n") + + float clock_seconds_f = ((mkword(c64.TIME_LO, c64.TIME_MID) as float) + (c64.TIME_HI as float)*65536.0) / 60.0 + c64flt.print_f(clock_seconds_f) + c64scr.print(" \n") + float hours_f = floor(clock_seconds_f / 3600.0) + clock_seconds_f -= hours_f*3600.0 + float minutes_f = floor(clock_seconds_f / 60.0) + clock_seconds_f = floor(clock_seconds_f - minutes_f * 60.0) + + c64flt.print_f(hours_f) + c64.CHROUT(':') + c64flt.print_f(minutes_f) + c64.CHROUT(':') + c64flt.print_f(clock_seconds_f) + c64scr.print(" \n") + + ubyte hours = hours_f as ubyte + ubyte minutes = minutes_f as ubyte + ubyte seconds = clock_seconds_f as ubyte + c64scr.print_ub(hours) + c64.CHROUT(':') + c64scr.print_ub(minutes) + c64.CHROUT(':') + c64scr.print_ub(seconds) + c64scr.print(" \n") + + goto loop - sub startzzz() { - if_cc goto startqqq - c64.EXTCOL++ - } }