mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-08 04:01:51 +09:00
vdc loop is working but it seems 'plot (var)' is screwing up
This commit is contained in:
@@ -24,8 +24,8 @@ Consider the following code snippet:
|
||||
|
||||
```
|
||||
DEFINE RATEF 60 ; 60 fps
|
||||
DEFINE height 448
|
||||
DEFINE width 560
|
||||
DEFINE HEIGHT 448
|
||||
DEFINE WIDTH 560
|
||||
|
||||
SCENE initialise
|
||||
@ mov r1 height ; this line runs only once when the SCENE is called
|
||||
@@ -144,6 +144,7 @@ NOTE: Any drawing command will clobber internal memory starting from address zer
|
||||
* fillin byte y x-start y-end-exclusive : fills entire scanline of `y` with `byte` from the horizontal position `x-start` through
|
||||
`y-end-exclusive` MINUS ONE. final (px,py) will be (scanline,x-end-exclusive)
|
||||
* plot byte... : writes bytes into the framebuffer. The `px` register will auto-increment but `py` won't!
|
||||
* plotp byte... : same as `plot` but treats bytes as PackBits-compressed payload
|
||||
* fillscr byte : fills entire screen with a given byte
|
||||
* goto x y : writes `x` to px and `y` to py (use `mov px <something>` to write to px/py only)
|
||||
* border r g b : sets border colour
|
||||
@@ -162,7 +163,7 @@ NOTE: Any drawing command will clobber internal memory starting from address zer
|
||||
#### Predefined Constants
|
||||
|
||||
* RATET : framerate defined by miliseconds between each frame. Mutually exclusive with RATEF.
|
||||
* RATEF : framerate defined by how many frames must be shown in one second. Mutually exclusive with RATET.
|
||||
* RATEF : framerate defined by how many frames must be shown in one second. Mutually exclusive with RATET
|
||||
|
||||
### Auto-incrementation
|
||||
|
||||
|
||||
@@ -105,6 +105,19 @@ class VMGUI(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter()
|
||||
|
||||
fun poke(addr: Long, value: Byte) = vm.poke(addr, value)
|
||||
|
||||
private fun renderGame(delta: Float) {
|
||||
gpu.render(delta, batch, 0f, 0f)
|
||||
|
||||
}
|
||||
|
||||
|
||||
override fun dispose() {
|
||||
super.dispose()
|
||||
batch.dispose()
|
||||
coroutineJob.cancel()
|
||||
vm.dispose()
|
||||
}
|
||||
|
||||
private val gpuTestPaletteKt = """
|
||||
val w = 560
|
||||
val h = 448
|
||||
@@ -385,20 +398,6 @@ while True:
|
||||
print("Apparent FPS: " + str(1000000000.0 / (tend - tstart)))
|
||||
|
||||
""".trimIndent()
|
||||
|
||||
|
||||
private fun renderGame(delta: Float) {
|
||||
gpu.render(delta, batch, 0f, 0f)
|
||||
|
||||
}
|
||||
|
||||
|
||||
override fun dispose() {
|
||||
super.dispose()
|
||||
batch.dispose()
|
||||
coroutineJob.cancel()
|
||||
vm.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
const val EMDASH = 0x2014.toChar()
|
||||
117
src/net/torvald/tsvm/vdc/V2kRunTest.kt
Normal file
117
src/net/torvald/tsvm/vdc/V2kRunTest.kt
Normal file
@@ -0,0 +1,117 @@
|
||||
package net.torvald.tsvm.vdc
|
||||
|
||||
import com.badlogic.gdx.ApplicationAdapter
|
||||
import com.badlogic.gdx.Gdx
|
||||
import com.badlogic.gdx.backends.lwjgl.LwjglApplication
|
||||
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration
|
||||
import com.badlogic.gdx.graphics.OrthographicCamera
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import com.badlogic.gdx.graphics.glutils.ShaderProgram
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import net.torvald.tsvm.*
|
||||
import net.torvald.tsvm.peripheral.GraphicsAdapter
|
||||
|
||||
class V2kRunTest(val appConfig: LwjglApplicationConfiguration) : ApplicationAdapter() {
|
||||
|
||||
val vm = VM(64.kB())
|
||||
lateinit var gpu: GraphicsAdapter
|
||||
|
||||
lateinit var batch: SpriteBatch
|
||||
lateinit var camera: OrthographicCamera
|
||||
|
||||
lateinit var vmRunner: VMRunner
|
||||
|
||||
lateinit var coroutineJob: Job
|
||||
lateinit var vdc: Videotron2K
|
||||
|
||||
override fun create() {
|
||||
super.create()
|
||||
|
||||
gpu = GraphicsAdapter(vm, lcdMode = false)
|
||||
|
||||
vm.peripheralTable[1] = PeripheralEntry(
|
||||
VM.PERITYPE_GPU_AND_TERM,
|
||||
gpu,
|
||||
GraphicsAdapter.VRAM_SIZE,
|
||||
16,
|
||||
0
|
||||
)
|
||||
|
||||
batch = SpriteBatch()
|
||||
camera = OrthographicCamera(appConfig.width.toFloat(), appConfig.height.toFloat())
|
||||
camera.setToOrtho(false)
|
||||
camera.update()
|
||||
batch.projectionMatrix = camera.combined
|
||||
Gdx.gl20.glViewport(0, 0, appConfig.width, appConfig.height)
|
||||
|
||||
vm.getPrintStream = { gpu.getPrintStream() }
|
||||
vm.getErrorStream = { gpu.getErrorStream() }
|
||||
vm.getInputStream = { gpu.getInputStream() }
|
||||
|
||||
vdc = Videotron2K(gpu)
|
||||
|
||||
vmRunner = VMRunnerFactory(vm, "js")
|
||||
coroutineJob = GlobalScope.launch {
|
||||
vdc.eval(Videotron2K.screenfiller)
|
||||
}
|
||||
|
||||
|
||||
Gdx.input.inputProcessor = vm.getIO()
|
||||
}
|
||||
|
||||
private var updateAkku = 0.0
|
||||
private var updateRate = 1f / 60f
|
||||
|
||||
override fun render() {
|
||||
Gdx.graphics.setTitle("${AppLoader.appTitle} $EMDASH F: ${Gdx.graphics.framesPerSecond}")
|
||||
|
||||
super.render()
|
||||
|
||||
val dt = Gdx.graphics.rawDeltaTime
|
||||
updateAkku += dt
|
||||
|
||||
var i = 0L
|
||||
while (updateAkku >= updateRate) {
|
||||
updateGame(updateRate)
|
||||
updateAkku -= updateRate
|
||||
i += 1
|
||||
}
|
||||
|
||||
renderGame(dt)
|
||||
}
|
||||
|
||||
private var latch = true
|
||||
|
||||
private fun updateGame(delta: Float) {
|
||||
vm.update(delta)
|
||||
}
|
||||
|
||||
private fun renderGame(delta: Float) {
|
||||
gpu.render(delta, batch, 0f, 0f)
|
||||
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
super.dispose()
|
||||
batch.dispose()
|
||||
coroutineJob.cancel()
|
||||
vm.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
ShaderProgram.pedantic = false
|
||||
val appConfig = LwjglApplicationConfiguration()
|
||||
appConfig.foregroundFPS = 60
|
||||
appConfig.backgroundFPS = 60
|
||||
appConfig.vSyncEnabled = false
|
||||
appConfig.useGL30 = true
|
||||
appConfig.resizable = false
|
||||
appConfig.title = "Videotron2K Test"
|
||||
appConfig.forceExit = true
|
||||
appConfig.width = 560
|
||||
appConfig.height = 448
|
||||
LwjglApplication(V2kRunTest(appConfig), appConfig)
|
||||
}
|
||||
@@ -4,6 +4,10 @@ import net.torvald.UnsafeHelper
|
||||
import net.torvald.random.HQRNG
|
||||
import net.torvald.tsvm.peripheral.GraphicsAdapter
|
||||
import java.lang.NumberFormatException
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.HashMap
|
||||
import kotlin.collections.HashSet
|
||||
|
||||
/**
|
||||
* See ./Videotron2K.md for documentation
|
||||
@@ -15,18 +19,24 @@ import java.lang.NumberFormatException
|
||||
class Videotron2K(var gpu: GraphicsAdapter?) {
|
||||
|
||||
companion object {
|
||||
private const val REGISTER_PREFIX = 0x7FFFFFFF_00000000L
|
||||
private const val VARIABLE_PREFIX = 0x3FFFFFFF_00000000L
|
||||
const val REGISTER_PREFIX = 0x7FFFFFFF_00000000L
|
||||
const val VARIABLE_PREFIX = 0x3FFFFFFF_00000000L
|
||||
const val PREFIX_MASK = (0xFFFFFFFFL).shl(32)
|
||||
|
||||
private const val REG_PX = REGISTER_PREFIX or 12
|
||||
private const val REG_PY = REGISTER_PREFIX or 13
|
||||
private const val REG_FRM = REGISTER_PREFIX or 14
|
||||
private const val REG_TMR = REGISTER_PREFIX or 15
|
||||
const val REG_PX = REGISTER_PREFIX or 12
|
||||
const val REG_PY = REGISTER_PREFIX or 13
|
||||
const val REG_FRM = REGISTER_PREFIX or 14
|
||||
const val REG_TMR = REGISTER_PREFIX or 15
|
||||
|
||||
private const val REG_R1 = REGISTER_PREFIX
|
||||
private const val REG_C1 = REGISTER_PREFIX + 6
|
||||
const val REG_R1 = REGISTER_PREFIX
|
||||
const val REG_C1 = REGISTER_PREFIX + 6
|
||||
|
||||
private const val INDEXED_SCENE_MAX = 1048575
|
||||
const val INDEXED_SCENE_MAX = 1048575
|
||||
|
||||
const val VARIABLE_RATET = VARIABLE_PREFIX or 0
|
||||
const val VARIABLE_RATEF = VARIABLE_PREFIX or 1
|
||||
const val VARIABLE_WIDTH = VARIABLE_PREFIX or 2
|
||||
const val VARIABLE_HEIGHT = VARIABLE_PREFIX or 3
|
||||
|
||||
private val specialRegs = hashMapOf(
|
||||
"px" to REG_PX,
|
||||
@@ -67,20 +77,22 @@ class Videotron2K(var gpu: GraphicsAdapter?) {
|
||||
DEFINE width 560
|
||||
|
||||
SCENE fill_line
|
||||
@ mov 0 px
|
||||
@ mov px 0
|
||||
plot c1 ; will auto-increment px by one
|
||||
inc c1
|
||||
cmp c1 251 r3
|
||||
movzr r3 c3 0 ; mov (-zr r3) c3 0 -- first, the comparison is made with r3 then runs 'mov c3 0' if r3 == 0
|
||||
cmp px 560 r1
|
||||
exitzr r1
|
||||
cmp c1 251 r1
|
||||
movzr r1 c1 0 ; mov (-zr r1) c1 0 -- first, the comparison is made with r1 then runs 'mov c1 0' if r1 == 0
|
||||
exitzr px
|
||||
END SCENE
|
||||
|
||||
SCENE loop_frame
|
||||
@ mov 0 py
|
||||
@ mov py 0
|
||||
perform fill_line
|
||||
inc py
|
||||
cmp py 448 r1
|
||||
movzr r1 py 0
|
||||
next
|
||||
; exeunt
|
||||
; there's no EXIT command so this scene will make the program to go loop indefinitely
|
||||
END SCENE
|
||||
|
||||
@@ -89,47 +101,94 @@ class Videotron2K(var gpu: GraphicsAdapter?) {
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
private var regs = UnsafeHelper.allocate(16 * 8)
|
||||
private var internalMem = UnsafeHelper.allocate(16384)
|
||||
internal var regs = UnsafeHelper.allocate(16 * 4)
|
||||
internal var internalMem = UnsafeHelper.allocate(16384)
|
||||
internal var callStack = Stack<Pair<Long, Int>>() // Pair of Scene-ID (has SCENE_PREFIX; 0 with no prefix for root scene) and ProgramCounter
|
||||
|
||||
/* Compile-time variables */
|
||||
private var scenes = HashMap<Long, Array<VT2Statement>>() // Long can have either SCENE_PREFIX- or INDEXED_SCENE_PREFIX-prefixed value
|
||||
private var varIdTable = HashMap<String, Long>() // String is always uppercase, Long always has VARIABLE_PREFIX added
|
||||
//private var sceneIdTable = HashMap<String, Long>() // String is always uppercase, Long always has SCENE_PREFIX added
|
||||
private var currentScene: Long? = null // if it's named_scene, VARIABLE_PREFIX is added; indexed_scene does not.
|
||||
internal var currentScene = 0L // VARIABLE_PREFIX is normally added but ROOT scene is just zero. Used by both parser and executor
|
||||
internal var currentLineIndex = 0
|
||||
|
||||
/* Run-time variables */
|
||||
private var variableMap = HashMap<Long, Int>() // VarId, Integer-value
|
||||
internal var pcLoopedBack = false
|
||||
internal var exeunt = false
|
||||
|
||||
/* Run-time variables and status */
|
||||
internal var variableMap = HashMap<Long, Int>() // VarId with VARIABLE_PREFIX, Integer-value
|
||||
internal var sleepLatch = false
|
||||
|
||||
internal var performanceCounterTmr = 0L
|
||||
|
||||
fun resetVarIdTable() {
|
||||
varIdTable.clear()
|
||||
varIdTable["RATET"] = VARIABLE_RATET
|
||||
varIdTable["RATEF"] = VARIABLE_RATEF
|
||||
varIdTable["WIDTH"] = VARIABLE_WIDTH
|
||||
varIdTable["HEIGHT"] = VARIABLE_HEIGHT
|
||||
}
|
||||
|
||||
private val reComment = Regex(""";[^\n]*""")
|
||||
private val reTokenizer = Regex(""" +""")
|
||||
private val reGeneralReg = Regex("""[rR][0-9]""")
|
||||
private val reCountReg = Regex("""[cC][0-9]""")
|
||||
|
||||
private val debugPrint = true
|
||||
private val infoPrint = true
|
||||
private val debugPrint = false
|
||||
private val rng = HQRNG()
|
||||
|
||||
fun eval(command: String) {
|
||||
val rootStatements = parseCommands(command)
|
||||
|
||||
|
||||
if (debugPrint) {
|
||||
if (infoPrint) {
|
||||
scenes.forEach { id, statements ->
|
||||
println("SCENE #${id and 0xFFFFFFFFL}")
|
||||
statements.forEach { println("$it") }
|
||||
statements.forEachIndexed { i, it -> println("I ${i.toString().padEnd(5, ' ')}$it") }
|
||||
println("END SCENE\n")
|
||||
}
|
||||
|
||||
rootStatements.forEach { println(it) }
|
||||
rootStatements.forEachIndexed { i, it -> println("I ${i.toString().padEnd(5, ' ')}$it") }
|
||||
}
|
||||
|
||||
execute(rootStatements)
|
||||
}
|
||||
|
||||
private fun execute(rootStatements: ArrayList<VT2Statement>) {
|
||||
private fun execute(rootStatements: Array<VT2Statement>) {
|
||||
variableMap.clear()
|
||||
currentScene = 0
|
||||
regs.fillWith(0)
|
||||
|
||||
rootStatements.forEach {
|
||||
while (!exeunt) {
|
||||
val scene = if (currentScene == 0L) rootStatements else scenes[currentScene]!!
|
||||
val it = scene[currentLineIndex]
|
||||
val oldSceneNo = currentScene
|
||||
|
||||
if (it.prefix != StatementPrefix.INIT || it.prefix == StatementPrefix.INIT && !pcLoopedBack) {
|
||||
if (debugPrint) println("Run-Scene: ${currentScene and 0xFFFFFFFFL}, Lindex: $currentLineIndex, Inst: $it")
|
||||
Command.checkConditionAndRun(it.command, this, it.args)
|
||||
if (debugPrint) println("Reg-r1: ${regs.getInt((REG_R1 and 0xF) * 4)}, c1: ${regs.getInt((REG_C1 and 0xF) * 4)}, px: ${regs.getInt((REG_PX and 0xF) * 4)}")
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// increment PC
|
||||
currentLineIndex += 1
|
||||
//// check if PC should loop back into the beginning of the scene
|
||||
if (currentScene == oldSceneNo && currentLineIndex == scene.size) {
|
||||
currentLineIndex = 0
|
||||
pcLoopedBack = true
|
||||
}
|
||||
if (currentScene != oldSceneNo) {
|
||||
pcLoopedBack = false
|
||||
}
|
||||
}
|
||||
exeunt = false
|
||||
|
||||
|
||||
if (infoPrint) println("Ende")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,9 +196,9 @@ class Videotron2K(var gpu: GraphicsAdapter?) {
|
||||
*
|
||||
* @return root statements; scene statements are stored in 'scenes'
|
||||
*/
|
||||
private fun parseCommands(command: String): ArrayList<VT2Statement> {
|
||||
private fun parseCommands(command: String): Array<VT2Statement> {
|
||||
scenes.clear()
|
||||
varIdTable.clear()
|
||||
resetVarIdTable()
|
||||
//sceneIdTable.clear()
|
||||
val rootStatements = ArrayList<VT2Statement>()
|
||||
val sceneStatements = ArrayList<VT2Statement>()
|
||||
@@ -156,7 +215,7 @@ class Videotron2K(var gpu: GraphicsAdapter?) {
|
||||
try {
|
||||
val scenenum = scenenumStr.toLong()
|
||||
|
||||
currentScene = scenenum
|
||||
currentScene = VARIABLE_PREFIX or scenenum
|
||||
}
|
||||
catch (e: NumberFormatException) {
|
||||
throw IllegalArgumentException("Line $lnum: Illegal scene numeral on $scenenumStr")
|
||||
@@ -174,23 +233,23 @@ class Videotron2K(var gpu: GraphicsAdapter?) {
|
||||
currentScene = registerNewVariable(sceneName) // scenes use same addr space as vars, to make things easier on the backend
|
||||
}
|
||||
else if (wordsUpper[0] == "END" && wordsUpper[1] == "SCENE") { // END SCENE
|
||||
if (currentScene == null) {
|
||||
if (currentScene == 0L) {
|
||||
throw IllegalArgumentException("Line $lnum: END SCENE is called without matching SCENE definition")
|
||||
}
|
||||
|
||||
scenes[currentScene!!] = sceneStatements.toTypedArray()
|
||||
scenes[currentScene] = sceneStatements.toTypedArray()
|
||||
|
||||
sceneStatements.clear()
|
||||
currentScene = null
|
||||
currentScene = 0L
|
||||
}
|
||||
else {
|
||||
val cmdBuffer = if (currentScene != null) sceneStatements else rootStatements
|
||||
val cmdBuffer = if (currentScene != 0L) sceneStatements else rootStatements
|
||||
|
||||
cmdBuffer.add(translateLine(lnum + 1, stmt))
|
||||
}
|
||||
}
|
||||
|
||||
return rootStatements
|
||||
return rootStatements.toTypedArray()
|
||||
}
|
||||
|
||||
private fun translateLine(lnum: Int, line: String): VT2Statement {
|
||||
@@ -310,30 +369,32 @@ object Command {
|
||||
const val NOT = 0x68
|
||||
const val NEG = 0x70
|
||||
|
||||
const val CMP = 0x100
|
||||
const val CMP = 0x80
|
||||
const val MOV = 0x88
|
||||
const val DATA = 0x90
|
||||
const val MCP = 0x98
|
||||
|
||||
const val MOV = 0x200
|
||||
const val DATA = 0x208
|
||||
const val MCP = 0x210
|
||||
const val PERFORM = 0x100
|
||||
const val NEXT = 0x108
|
||||
const val EXIT = 0x110
|
||||
const val EXEUNT = 0x118
|
||||
|
||||
const val PERFORM = 0x300
|
||||
const val NEXT = 0x308
|
||||
const val EXIT = 0x310
|
||||
const val EXEUNT = 0x318
|
||||
const val FILLIN = 0x200
|
||||
const val PLOT = 0x208
|
||||
const val FILLSCR = 0x210
|
||||
const val GOTO = 0x218
|
||||
const val BORDER = 0x220
|
||||
const val PLOTP = 0x228
|
||||
|
||||
const val FILLIN = 0x400
|
||||
const val PLOT = 0x408
|
||||
const val FILLSCR = 0x410
|
||||
const val GOTO = 0x418
|
||||
const val BORDER = 0x420
|
||||
const val WAIT = 0x700
|
||||
const val DEFINE = 0x708
|
||||
|
||||
const val WAIT = 0x600
|
||||
|
||||
const val DEFINE = 0xFFF8
|
||||
const val INST_ID_MAX = 0x800 // 256 if divided by 8
|
||||
|
||||
val dict = HashMap<String, Int>()
|
||||
|
||||
val varDefiningCommands = HashSet<String>()
|
||||
val transferInst = HashSet<Int>()
|
||||
|
||||
|
||||
// fill in conditionals to dict
|
||||
@@ -368,6 +429,7 @@ object Command {
|
||||
|
||||
"FILLIN" to FILLIN,
|
||||
"PLOT" to PLOT,
|
||||
"PLOTP" to PLOTP,
|
||||
"FILLSCR" to FILLSCR,
|
||||
"GOTO" to GOTO,
|
||||
"BORDER" to BORDER,
|
||||
@@ -392,7 +454,149 @@ object Command {
|
||||
varDefiningCommands.add(command + cond)
|
||||
}
|
||||
}
|
||||
|
||||
/* transferInst = */hashSetOf(
|
||||
PERFORM, EXIT, EXEUNT
|
||||
).forEach { command ->
|
||||
transferInst.add(command)
|
||||
|
||||
conditional.forEachIndexed { cond, _ ->
|
||||
transferInst.add(command + cond)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val reverseDict = HashMap<Int, String>(dict.entries.associate { (k,v)-> v to k })
|
||||
}
|
||||
|
||||
/**
|
||||
* function (instance: Videotron2K, args: LongArray)
|
||||
*/
|
||||
val instSet = Array<(Videotron2K, LongArray) -> Unit>(256) { { _, _ -> TODO("Instruction ID $it (${reverseDict[it * 8]})") } }
|
||||
|
||||
init {
|
||||
instSet[NOP] = { _, _ -> }
|
||||
instSet[DEFINE shr 3] = { instance, args -> // DEFINE variable value
|
||||
if (args.size != 2) throw ArgsCountMismatch(2, args)
|
||||
|
||||
instance.variableMap[args[0]] = if (args[1] and Videotron2K.VARIABLE_PREFIX == Videotron2K.VARIABLE_PREFIX) {
|
||||
instance.variableMap[args[1]] ?: throw NullVar()
|
||||
}
|
||||
else if (args[1] and Videotron2K.PREFIX_MASK == 0L) {
|
||||
args[1].toInt()
|
||||
}
|
||||
else {
|
||||
throw UnknownVariableType(args[1])
|
||||
}
|
||||
}
|
||||
instSet[MOV shr 3] = { instance, args -> // MOV register value
|
||||
if (args.size != 2) throw ArgsCountMismatch(2, args)
|
||||
checkRegisterLH(args[0])
|
||||
instance.regs.setInt((args[0] and 0xF) * 4, resolveVar(instance, args[1]))
|
||||
}
|
||||
instSet[INC shr 3] = { instance, args -> // INC register
|
||||
if (args.size != 1) throw ArgsCountMismatch(1, args)
|
||||
checkRegisterLH(args[0])
|
||||
instance.regs.setInt((args[0] and 0xF) * 4, 1 + instance.regs.getInt((args[0] and 0xF) * 4))
|
||||
}
|
||||
instSet[DEC shr 3] = { instance, args -> // DEC register
|
||||
if (args.size != 1) throw ArgsCountMismatch(1, args)
|
||||
checkRegisterLH(args[0])
|
||||
instance.regs.setInt((args[0] and 0xF) * 4, 1 - instance.regs.getInt((args[0] and 0xF) * 4))
|
||||
}
|
||||
instSet[NEXT shr 3] = { instance, _ ->
|
||||
instance.regs.setInt((Videotron2K.REG_FRM and 0xF) * 4, 1 + instance.regs.getInt((Videotron2K.REG_FRM and 0xF) * 4))
|
||||
instance.sleepLatch = true
|
||||
|
||||
val timeTook = (System.nanoTime() - instance.performanceCounterTmr).toDouble()
|
||||
println("Frame time: ${timeTook / 1000000.0} ms (fps: ${1000000000.0 / timeTook})")
|
||||
instance.performanceCounterTmr = System.nanoTime()
|
||||
}
|
||||
instSet[CMP shr 3] = { instance, args -> // CMP rA rB rC
|
||||
if (args.size != 3) throw ArgsCountMismatch(3, args)
|
||||
val v1 = resolveVar(instance, args[0])
|
||||
val v2 = resolveVar(instance, args[1])
|
||||
val cmpvar = if (v1 > v2) 1 else if (v1 < v2) -1 else 0
|
||||
instance.regs.setInt((args[2] and 0xF) * 4, cmpvar)
|
||||
}
|
||||
instSet[PLOT shr 3] = { instance, args -> // PLOT vararg-bytes
|
||||
if (args.isNotEmpty()) {
|
||||
val px = instance.regs.getInt((Videotron2K.REG_PX and 0xF) * 4)
|
||||
val py = instance.regs.getInt((Videotron2K.REG_PY and 0xF) * 4)
|
||||
val width = instance.variableMap[Videotron2K.VARIABLE_WIDTH]!!
|
||||
val memAddr = py * width + px
|
||||
|
||||
args.forEachIndexed { index, value ->
|
||||
instance.gpu?.poke(memAddr.toLong() + index, value.toByte())
|
||||
}
|
||||
|
||||
// write back auto-incremented value
|
||||
instance.regs.setInt((Videotron2K.REG_PX and 0xF) * 4, (px + args.size) fmod width)
|
||||
}
|
||||
}
|
||||
instSet[PERFORM shr 3] = { instance, args -> // PERFORM scene
|
||||
instance.callStack.push(instance.currentScene to instance.currentLineIndex)
|
||||
instance.currentScene = args[0]
|
||||
instance.currentLineIndex = -1
|
||||
}
|
||||
instSet[EXIT shr 3] = { instance, _ ->
|
||||
val (scene, line) = instance.callStack.pop()
|
||||
instance.currentScene = scene
|
||||
instance.currentLineIndex = line
|
||||
//println("EXIT!")
|
||||
//Thread.sleep(1000L)
|
||||
}
|
||||
instSet[EXEUNT shr 3] = { instance, _ ->
|
||||
instance.exeunt = true
|
||||
}
|
||||
}
|
||||
|
||||
fun checkConditionAndRun(inst: Int, instance: Videotron2K, args: LongArray) {
|
||||
val opcode = inst shr 3
|
||||
val condCode = inst and 7
|
||||
|
||||
if (condCode == 0) {
|
||||
//if (inst !in transferInst) instance.currentLineIndex += 1
|
||||
instSet[opcode].invoke(instance, args)
|
||||
return
|
||||
}
|
||||
|
||||
val condition = when (condCode) {
|
||||
1 -> resolveVar(instance, args[0]) == 0
|
||||
2 -> resolveVar(instance, args[0]) != 0
|
||||
3 -> resolveVar(instance, args[0]) > 0
|
||||
4 -> resolveVar(instance, args[0]) < 0
|
||||
5 -> resolveVar(instance, args[0]) >= 0
|
||||
6 -> resolveVar(instance, args[0]) <= 0
|
||||
else -> throw InternalError()
|
||||
}
|
||||
|
||||
if (condition) {
|
||||
//if (inst !in transferInst) instance.currentLineIndex += 1
|
||||
instSet[opcode].invoke(instance, args.sliceArray(1 until args.size))
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveVar(instance: Videotron2K, arg: Long): Int {
|
||||
return if (arg and Videotron2K.REGISTER_PREFIX == Videotron2K.REGISTER_PREFIX) {
|
||||
instance.regs.getInt((arg and 0xF) * 4)
|
||||
}
|
||||
else if (arg and Videotron2K.VARIABLE_PREFIX == Videotron2K.VARIABLE_PREFIX) {
|
||||
instance.variableMap[arg] ?: throw NullVar()
|
||||
}
|
||||
else arg.toInt()
|
||||
}
|
||||
|
||||
private fun checkRegisterLH(arg: Long) {
|
||||
if (arg and Videotron2K.REGISTER_PREFIX != Videotron2K.REGISTER_PREFIX) {
|
||||
throw BadLeftHand("register")
|
||||
}
|
||||
}
|
||||
|
||||
private infix fun Int.fmod(other: Int) = Math.floorMod(this, other)
|
||||
}
|
||||
|
||||
internal class ArgsCountMismatch(expected: Int, args: LongArray) : RuntimeException("Argument count mismatch: expected $expected, got ${args.size}")
|
||||
internal class UnknownVariableType(arg: Long) : RuntimeException("Unknown variable type: ${arg.ushr(32).toString(16)}")
|
||||
internal class NullVar : RuntimeException("Variable is undeclared")
|
||||
internal class BadLeftHand(type: String) : RuntimeException("Left-hand argument is not $type")
|
||||
internal class BadRightHand(type: String) : RuntimeException("Right-hand argument is not $type")
|
||||
|
||||
Reference in New Issue
Block a user