parsing vdt commands

This commit is contained in:
minjaesong
2020-08-05 02:30:09 +09:00
parent 9a2d215637
commit 95a9555031
3 changed files with 273 additions and 211 deletions

View File

@@ -1,6 +1,7 @@
package net.torvald.tsvm package net.torvald.tsvm
import net.torvald.tsvm.peripheral.GraphicsAdapter import net.torvald.tsvm.peripheral.GraphicsAdapter
import net.torvald.tsvm.vdc.Videotron2K
import java.io.FileReader import java.io.FileReader
import javax.script.ScriptContext import javax.script.ScriptContext
import javax.script.ScriptEngineManager import javax.script.ScriptEngineManager
@@ -40,7 +41,8 @@ object VMRunnerFactory {
"vt2" -> { "vt2" -> {
object : VMRunner(extension) { object : VMRunner(extension) {
val engine = Videotron2K(vm.findPeribyType(VM.PERITYPE_GPU_AND_TERM)!!.peripheral!! as GraphicsAdapter) val engine =
Videotron2K(vm.findPeribyType(VM.PERITYPE_GPU_AND_TERM)!!.peripheral!! as GraphicsAdapter)
override suspend fun executeCommand(command: String) { override suspend fun executeCommand(command: String) {
engine.eval(command) engine.eval(command)

View File

@@ -0,0 +1,10 @@
package net.torvald.tsvm.vdc
import net.torvald.tsvm.VM
import net.torvald.tsvm.peripheral.GraphicsAdapter
fun main() {
val vdc = Videotron2K(null)
vdc.eval(Videotron2K.screenfiller)
}

View File

@@ -1,4 +1,4 @@
package net.torvald.tsvm package net.torvald.tsvm.vdc
import net.torvald.UnsafeHelper import net.torvald.UnsafeHelper
import net.torvald.random.HQRNG import net.torvald.random.HQRNG
@@ -7,10 +7,61 @@ import java.lang.NumberFormatException
/** /**
* See ./Videotron2K.md for documentation * See ./Videotron2K.md for documentation
*
* ## Variable Namespace
* - Scenes and Variables use same namespace
*
*/ */
class Videotron2K(val gpu: GraphicsAdapter) { class Videotron2K(var gpu: GraphicsAdapter?) {
private val screenfiller = """ companion object {
private const val REGISTER_PREFIX = 0x7FFFFFFF_00000000L
private const val VARIABLE_PREFIX = 0x3FFFFFFF_00000000L
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
private const val REG_R1 = REGISTER_PREFIX
private const val REG_C1 = REGISTER_PREFIX + 6
private const val INDEXED_SCENE_MAX = 1048575
private val specialRegs = hashMapOf(
"px" to REG_PX,
"py" to REG_PY,
"frm" to REG_FRM,
"tmr" to REG_TMR
)
private val reverseSpecialRegs = HashMap(specialRegs.entries.associate { (k, v) -> v to k })
/*
Registers internal variable ID:
r1 = REGISTER_PREFIX + 0
r2 = REGISTER_PREFIX + 1
r3 = REGISTER_PREFIX + 2
r4 = REGISTER_PREFIX + 3
r5 = REGISTER_PREFIX + 4
r6 = REGISTER_PREFIX + 5
c1 = REGISTER_PREFIX + 6
c2 = REGISTER_PREFIX + 7
c3 = REGISTER_PREFIX + 8
c4 = REGISTER_PREFIX + 9
c5 = REGISTER_PREFIX + 10
c6 = REGISTER_PREFIX + 11
px = REGISTER_PREFIX + 12
py = REGISTER_PREFIX + 13
frm = REGISTER_PREFIX + 14
tmr = REGISTER_PREFIX + 15
*/
val screenfiller = """
DEFINE RATEF 60 DEFINE RATEF 60
DEFINE height 448 DEFINE height 448
DEFINE width 560 DEFINE width 560
@@ -36,16 +87,20 @@ class Videotron2K(val gpu: GraphicsAdapter) {
perform loop_frame perform loop_frame
""".trimIndent() """.trimIndent()
}
private var regs = UnsafeHelper.allocate(16 * 8) private var regs = UnsafeHelper.allocate(16 * 8)
private var internalMem = UnsafeHelper.allocate(16384) private var internalMem = UnsafeHelper.allocate(16384)
private var scenes = HashMap<Long, Array<VT2Statement>>() 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 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. private var currentScene: Long? = null // if it's named_scene, VARIABLE_PREFIX is added; indexed_scene does not.
private val reComment = Regex(""";[^\n]*""") private val reComment = Regex(""";[^\n]*""")
private val reTokenizer = Regex(""" +""") private val reTokenizer = Regex(""" +""")
private val reGeneralReg = Regex("""[rR][0-9]""")
private val reCountReg = Regex("""[cC][0-9]""")
private val debugPrint = true private val debugPrint = true
private val rng = HQRNG() private val rng = HQRNG()
@@ -57,6 +112,7 @@ class Videotron2K(val gpu: GraphicsAdapter) {
command.replace(reComment, "").split('\n') command.replace(reComment, "").split('\n')
.mapIndexed { index, s -> index to s }.filter { it.second.isNotBlank() } .mapIndexed { index, s -> index to s }.filter { it.second.isNotBlank() }
.forEach { (lnum, stmt) -> .forEach { (lnum, stmt) ->
val stmt = stmt.trim()
val stmtUpper = stmt.toUpperCase() val stmtUpper = stmt.toUpperCase()
val wordsUpper = stmtUpper.split(reTokenizer) val wordsUpper = stmtUpper.split(reTokenizer)
@@ -80,7 +136,7 @@ class Videotron2K(val gpu: GraphicsAdapter) {
throw IllegalArgumentException("Line $lnum: Scene name or variable '$sceneName' already exists") throw IllegalArgumentException("Line $lnum: Scene name or variable '$sceneName' already exists")
} }
currentScene = registerNewVariable(sceneName) 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 else if (wordsUpper[0] == "END" && wordsUpper[1] == "SCENE") { // END SCENE
if (currentScene == null) { if (currentScene == null) {
@@ -95,14 +151,14 @@ class Videotron2K(val gpu: GraphicsAdapter) {
else { else {
val cmdBuffer = if (currentScene != null) sceneStatements else rootStatements val cmdBuffer = if (currentScene != null) sceneStatements else rootStatements
cmdBuffer.add(translateLine(lnum, stmt)) cmdBuffer.add(translateLine(lnum + 1, stmt))
} }
} }
if (debugPrint) { if (debugPrint) {
scenes.forEach { id, statements -> scenes.forEach { id, statements ->
println("SCENE #$id") println("SCENE #${id and 0xFFFFFFFFL}")
statements.forEach { println(" $it") } statements.forEach { println(" $it") }
println("END SCENE\n") println("END SCENE\n")
} }
@@ -115,26 +171,37 @@ class Videotron2K(val gpu: GraphicsAdapter) {
val tokens = line.split(reTokenizer) val tokens = line.split(reTokenizer)
if (tokens.isEmpty()) throw InternalError("Line $lnum: empty line not filtered!") if (tokens.isEmpty()) throw InternalError("Line $lnum: empty line not filtered!")
//println(tokens)
val isInit = tokens[0] == "@" val isInit = tokens[0] == "@"
val cmdstr = tokens[isInit.toInt()].toUpperCase() val cmdstr = tokens[isInit.toInt()].toUpperCase()
val cmd: Int = Command.dict[cmdstr] ?: throw RuntimeException("Syntax Error at line $lnum") // conditional code is pre-added on dict val cmd: Int = Command.dict[cmdstr] ?: throw RuntimeException("Undefined instruction on line $lnum: $cmdstr ($line)") // conditional code is pre-added on dict
val args = tokens.subList(1 + isInit.toInt(), tokens.size).map { parseArgString(it) } val args = tokens.subList(1 + isInit.toInt(), tokens.size).map { parseArgString(cmdstr, lnum, it) }
return VT2Statement(if (isInit) StatementPrefix.INIT else StatementPrefix.NONE, cmd, args.toLongArray()) return VT2Statement(
if (isInit) StatementPrefix.INIT else StatementPrefix.NONE,
cmd,
args.toLongArray()
)
} }
private fun parseArgString(token: String): Long { private fun parseArgString(parentCmd: String, lnum: Int, token: String): Long {
if (token.toIntOrNull() != null) // number literal if (token.toIntOrNull() != null) // number literal
return token.toLong().and(0xFFFFFFFF) return token.toLong().and(0xFFFFFFFF)
else if (token.endsWith('h') && token.substring(0, token.lastIndex).toIntOrNull() != null) // hex literal else if (token.endsWith('h') && token.substring(0, token.lastIndex).toIntOrNull() != null) // hex literal
return token.substring(0, token.lastIndex).toInt(16).toLong().and(0xFFFFFFFF) return token.substring(0, token.lastIndex).toInt(16).toLong().and(0xFFFFFFFF)
else if (token.startsWith('r') && token.substring(1, token.length).toIntOrNull() != null) // r-registers else if (specialRegs.contains(token.toLowerCase())) // special registers
return specialRegs[token.toLowerCase()]!!
else if (token.matches(reGeneralReg)) // r-registers
return REGISTER_PREFIX or token.substring(1, token.length).toLong().minus(1).and(0xFFFFFFFF) return REGISTER_PREFIX or token.substring(1, token.length).toLong().minus(1).and(0xFFFFFFFF)
else if (token.startsWith('c') && token.substring(1, token.length).toIntOrNull() != null) // c-registers else if (token.matches(reCountReg)) // c-registers
return REGISTER_PREFIX or token.substring(1, token.length).toLong().plus(5).and(0xFFFFFFFF) return REGISTER_PREFIX or token.substring(1, token.length).toLong().plus(5).and(0xFFFFFFFF)
else { else {
val varId = varIdTable[token.toUpperCase()] ?: throw IllegalArgumentException("Undefined variable: $token") val varId = varIdTable[token.toUpperCase()] ?: (
if (parentCmd in Command.varDefiningCommands) registerNewVariable(token)
else throw NullPointerException("Undefined variable '$token' in line $lnum")
)
return varId return varId
} }
@@ -144,7 +211,7 @@ class Videotron2K(val gpu: GraphicsAdapter) {
var id: Long var id: Long
do { do {
id = VARIABLE_PREFIX or rng.nextLong().and(0xFFFFFFFFL) id = VARIABLE_PREFIX or rng.nextLong().and(0xFFFFFFFFL)
} while (varIdTable.containsValue(id)) } while (varIdTable.containsValue(id) || (id and 0xFFFFFFFFL) <= INDEXED_SCENE_MAX)
varIdTable[varName.toUpperCase()] = id varIdTable[varName.toUpperCase()] = id
return id return id
@@ -155,14 +222,19 @@ class Videotron2K(val gpu: GraphicsAdapter) {
private class VT2Statement(val prefix: Int = StatementPrefix.NONE, val command: Int, val args: LongArray) { private class VT2Statement(val prefix: Int = StatementPrefix.NONE, val command: Int, val args: LongArray) {
override fun toString(): String { override fun toString(): String {
return StatementPrefix.toString(prefix) + " " + Command.reverseDict[command] + " " + (args.map { argsToString(it) + " " }) return StatementPrefix.toString(prefix) + " " + Command.reverseDict[command] + " " + (args.map { argsToString(it) })
} }
private fun argsToString(i: Long): String { private fun argsToString(i: Long): String {
if (i and REGISTER_PREFIX != 0L) { if (reverseSpecialRegs.contains(i))
return reverseSpecialRegs[i]!!
else if (i and REGISTER_PREFIX == REGISTER_PREFIX) {
val regnum = i and 0xFFFFFFFFL val regnum = i and 0xFFFFFFFFL
return if (regnum < 6) "r${regnum + 1}" else "c${regnum - 5}" return if (regnum < 6) "r${regnum + 1}" else "c${regnum - 5}"
} }
else if (i and VARIABLE_PREFIX == VARIABLE_PREFIX) {
return "var:${i and 0xFFFFFFFF}"
}
else return i.toInt().toString() else return i.toInt().toString()
} }
} }
@@ -180,43 +252,6 @@ class Videotron2K(val gpu: GraphicsAdapter) {
return null return null
} }
companion object {
private const val REGISTER_PREFIX = 0x7FFFFFFF_00000000L
private const val VARIABLE_PREFIX = 0x3FFFFFFF_00000000L
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
private const val REG_R1 = REGISTER_PREFIX
private const val REG_C1 = REGISTER_PREFIX + 6
/*
Registers internal variable ID:
r1 = REGISTER_PREFIX + 0
r2 = REGISTER_PREFIX + 1
r3 = REGISTER_PREFIX + 2
r4 = REGISTER_PREFIX + 3
r5 = REGISTER_PREFIX + 4
r6 = REGISTER_PREFIX + 5
c1 = REGISTER_PREFIX + 6
c2 = REGISTER_PREFIX + 7
c3 = REGISTER_PREFIX + 8
c4 = REGISTER_PREFIX + 9
c5 = REGISTER_PREFIX + 10
c6 = REGISTER_PREFIX + 11
px = REGISTER_PREFIX + 12
py = REGISTER_PREFIX + 13
frm = REGISTER_PREFIX + 14
tmr = REGISTER_PREFIX + 15
*/
}
} }
object StatementPrefix { object StatementPrefix {
@@ -269,7 +304,14 @@ object Command {
const val DEFINE = 0xFFF8 const val DEFINE = 0xFFF8
val dict = hashMapOf( val dict = HashMap<String, Int>()
val varDefiningCommands = HashSet<String>()
// fill in conditionals to dict
init {
/* dict = */hashMapOf(
"NOP" to NOP, "NOP" to NOP,
"ADD" to ADD, "ADD" to ADD,
"SUB" to SUB, "SUB" to SUB,
@@ -306,15 +348,23 @@ object Command {
"WAIT" to WAIT, "WAIT" to WAIT,
"DEFINE" to DEFINE "DEFINE" to DEFINE
) ).entries.forEach { (command, opcode) ->
dict[command] = opcode
// fill in conditionals to dict
init {
dict.entries.forEach { (command, opcode) ->
conditional.forEachIndexed { i, cond -> conditional.forEachIndexed { i, cond ->
dict[command + cond] = opcode + i + 1 dict[command + cond] = opcode + i + 1
} }
} }
/* varDefiningCommands = */hashSetOf(
"DEFINE"
).forEach { command ->
varDefiningCommands.add(command)
conditional.forEach { cond ->
varDefiningCommands.add(command + cond)
}
}
} }
val reverseDict = HashMap<Int, String>(dict.entries.associate { (k,v)-> v to k }) val reverseDict = HashMap<Int, String>(dict.entries.associate { (k,v)-> v to k })