fix reading and writing rtc jiffy clock, memory can now intercept reads and writes

This commit is contained in:
Irmen de Jong 2019-07-02 20:48:14 +02:00
parent fd0abf61df
commit 89314a0e1a
10 changed files with 192 additions and 341 deletions

View File

@ -1 +1 @@
1.8
1.9-dev

View File

@ -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<String>) {
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()

View File

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

View File

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

View File

@ -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<Short>()
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<Short>()
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)
}
}

View File

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

View File

@ -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<Short>()
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]
}
}

View File

@ -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<lines.size-1) {
cursorX=0
cursorY++
}
}
}
private fun printTextSingleLine(text: String, color: Short, lowercase: Boolean) {
for(clearx in cursorX until cursorX+text.length) {
g2d.clearRect(8*clearx, 8*y, 8, 8)
}
for(sc in Petscii.encodeScreencode(text, lowercase)) {
setChar(cursorX, cursorY, sc, color)
cursorX++
if(cursorX>=(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<Int, Int> {
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()
}
}

View File

@ -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<T> : Stack<T>() {
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<Int, List<RuntimeValue>>) {
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 {

View File

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