mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 19:51:51 +09:00
v2k parser wip
This commit is contained in:
@@ -80,10 +80,23 @@ Commands can have "conditional postfix" used to execute the command conditionall
|
||||
- ge r : if r >= 0
|
||||
- le r : if r <= 0
|
||||
|
||||
## Programming Guidelines
|
||||
|
||||
### Scene
|
||||
|
||||
* A scene will always need one or more `exit` command; scenes are infini-loop object and without `exit`, there will be no way to terminate the scene.
|
||||
|
||||
### Program
|
||||
|
||||
* A program will not infini-loop on its own; you need to explicitly enter the `loop` command.
|
||||
|
||||
|
||||
## Available Commands
|
||||
|
||||
### Arithmetic
|
||||
|
||||
NOTE : immediates and variables can substitute registers
|
||||
|
||||
* add rA rB rC : rC = rA + rB
|
||||
* sub rA rB rC : rC = rA - rB
|
||||
* mul rA rB rC : rC = rA * rB
|
||||
@@ -95,21 +108,22 @@ Commands can have "conditional postfix" used to execute the command conditionall
|
||||
* shr rA rB rC : rC = rA >> rB
|
||||
* ushr rA rB rC : rC = rA >>> rB
|
||||
|
||||
* inc rA rB : rC = rA + 1
|
||||
* dec rA rB : rC = rA - 1
|
||||
|
||||
* inc R : R = R + 1
|
||||
* dec R : R = R - 1
|
||||
* not R : R = !R (ones' complement of R)
|
||||
* neg R : R = -R (twos' complement of R)
|
||||
|
||||
### Conditional
|
||||
|
||||
NOTE : immediates and variables can substitute registers
|
||||
|
||||
* cmp rA rB rC : compares rA and rB and stores result to rC. 1 if rA > rB, -1 if rA < rB, 0 if rA == rB.
|
||||
|
||||
### Data Control
|
||||
|
||||
NOTE: Any drawing command will clobber internal memory starting from address zero.
|
||||
|
||||
* mov rA rB/I : assignes R with contents of rB/constant I
|
||||
* mov rA rB/I : assignes rA with contents of rB/constant I
|
||||
* data rA/I bytes : writes bytes to the internal memory of address starting from rA/I
|
||||
* mcp from y x len : copies part of the internal memory to the framebuffer, from the internal memory address `from`,
|
||||
to scanline `y`, horizontal position `x`, with copying length of `len`.
|
||||
@@ -117,17 +131,19 @@ NOTE: Any drawing command will clobber internal memory starting from address zer
|
||||
### Flow Control
|
||||
|
||||
* perform scenename : gosub into the scenename
|
||||
* jumpto label_name : goto the label name. JUMPTO only works when the label is within the same scope.
|
||||
* next : advance a frame counter (frm) and sleeps until it is time to draw next frame
|
||||
* exit : terminates current scene. System will error out if this command is used outside of a scene
|
||||
* loop : will jump to the beginning of the current scope (scene). @-padded line will NOT be executed. The opposite of EXIT
|
||||
* exit : terminates current scene. System will error out if this command is used outside of a scene. The opposite of LOOP
|
||||
* exeunt : completely terminates the program
|
||||
|
||||
### Drawing
|
||||
|
||||
NOTE: Any drawing command will clobber internal memory starting from address zero.
|
||||
|
||||
* fillin byte y x-start y-end-exclusive : fills entire scanline of `y` from the horizontal position `x-start` through
|
||||
* 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
|
||||
* plot byte... : writes bytes into the framebuffer. The `px` register will auto-increment but `py` won't!
|
||||
* 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
|
||||
@@ -146,4 +162,12 @@ 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
|
||||
|
||||
Some instructions will cause a register to auto-increment. Auto-increment rule is as follows:
|
||||
|
||||
* `px` : px = (px + 1 fmod width)
|
||||
* `py` : py = (py + 1 fmod height)
|
||||
* If an arithmetic command is used against `px` or `py` register, above rules will apply to the registers.
|
||||
194
src/net/torvald/random/HQRNG.java
Normal file
194
src/net/torvald/random/HQRNG.java
Normal file
@@ -0,0 +1,194 @@
|
||||
package net.torvald.random;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Xoroshiro128
|
||||
*
|
||||
* Note: low 4 bits are considered "dirty"; avoid these bits for making random set of booleans
|
||||
*
|
||||
* see https://github.com/SquidPony/SquidLib/blob/master/squidlib-util/src/main/java/squidpony/squidmath/XoRoRNG.java
|
||||
*/
|
||||
public class HQRNG extends Random {
|
||||
|
||||
private static final long DOUBLE_MASK = (1L << 53) - 1;
|
||||
private static final double NORM_53 = 1. / (1L << 53);
|
||||
private static final long FLOAT_MASK = (1L << 24) - 1;
|
||||
private static final double NORM_24 = 1. / (1L << 24);
|
||||
|
||||
private static final long serialVersionUID = 1018744536171610262L;
|
||||
|
||||
private long state0, state1;
|
||||
|
||||
public long getState0() {
|
||||
return state0;
|
||||
}
|
||||
|
||||
public long getState1() {
|
||||
return state1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new generator seeded using four calls to Math.random().
|
||||
*/
|
||||
public HQRNG() {
|
||||
this((long) ((Math.random() - 0.5) * 0x10000000000000L)
|
||||
^ (long) (((Math.random() - 0.5) * 2.0) * 0x8000000000000000L),
|
||||
(long) ((Math.random() - 0.5) * 0x10000000000000L)
|
||||
^ (long) (((Math.random() - 0.5) * 2.0) * 0x8000000000000000L));
|
||||
}
|
||||
/**
|
||||
* Constructs this XoRoRNG by dispersing the bits of seed using {@link #setSeed(long)} across the two parts of state
|
||||
* this has.
|
||||
* @param seed a long that won't be used exactly, but will affect both components of state
|
||||
*/
|
||||
public HQRNG(final long seed) {
|
||||
setSeed(seed);
|
||||
}
|
||||
/**
|
||||
* Constructs this XoRoRNG by calling {@link #setSeed(long, long)} on the arguments as given; see that method for
|
||||
* the specific details (stateA and stateB are kept as-is unless they are both 0).
|
||||
* @param stateA the number to use as the first part of the state; this will be 1 instead if both seeds are 0
|
||||
* @param stateB the number to use as the second part of the state
|
||||
*/
|
||||
public HQRNG(final long stateA, final long stateB) {
|
||||
setSeed(stateA, stateB);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int next(int bits) {
|
||||
final long s0 = state0;
|
||||
long s1 = state1;
|
||||
final int result = (int)(s0 + s1) >>> (32 - bits);
|
||||
s1 ^= s0;
|
||||
state0 = (s0 << 55 | s0 >>> 9) ^ s1 ^ (s1 << 14); // a, b
|
||||
state1 = (s1 << 36 | s1 >>> 28); // c
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final long nextLong() {
|
||||
final long s0 = state0;
|
||||
long s1 = state1;
|
||||
final long result = s0 + s1;
|
||||
|
||||
s1 ^= s0;
|
||||
state0 = (s0 << 55 | s0 >>> 9) ^ s1 ^ (s1 << 14); // a, b
|
||||
state1 = (s1 << 36 | s1 >>> 28); // c
|
||||
/*
|
||||
state0 = Long.rotateLeft(s0, 55) ^ s1 ^ (s1 << 14); // a, b
|
||||
state1 = Long.rotateLeft(s1, 36); // c
|
||||
*/
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can return any int, positive or negative, of any size permissible in a 32-bit signed integer.
|
||||
* @return any int, all 32 bits are random
|
||||
*/
|
||||
public int nextInt() {
|
||||
return (int)nextLong();
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclusive on the outer bound; the inner bound is 0. The bound may be negative, which will produce a non-positive
|
||||
* result.
|
||||
* @param bound the outer exclusive bound; may be positive or negative
|
||||
* @return a random int between 0 (inclusive) and bound (exclusive)
|
||||
*/
|
||||
public int nextInt(final int bound) {
|
||||
return (int) ((bound * (nextLong() >>> 33)) >> 31);
|
||||
}
|
||||
/**
|
||||
* Inclusive lower, exclusive upper.
|
||||
* @param inner the inner bound, inclusive, can be positive or negative
|
||||
* @param outer the outer bound, exclusive, should be positive, should usually be greater than inner
|
||||
* @return a random int that may be equal to inner and will otherwise be between inner and outer
|
||||
*/
|
||||
public int nextInt(final int inner, final int outer) {
|
||||
return inner + nextInt(outer - inner);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclusive on the outer bound; the inner bound is 0. The bound may be negative, which will produce a non-positive
|
||||
* result.
|
||||
* @param bound the outer exclusive bound; may be positive or negative
|
||||
* @return a random long between 0 (inclusive) and bound (exclusive)
|
||||
*/
|
||||
public long nextLong(long bound) {
|
||||
long rand = nextLong();
|
||||
final long randLow = rand & 0xFFFFFFFFL;
|
||||
final long boundLow = bound & 0xFFFFFFFFL;
|
||||
rand >>>= 32;
|
||||
bound >>= 32;
|
||||
final long z = (randLow * boundLow >> 32);
|
||||
long t = rand * boundLow + z;
|
||||
final long tLow = t & 0xFFFFFFFFL;
|
||||
t >>>= 32;
|
||||
return rand * bound + t + (tLow + randLow * bound >> 32) - (z >> 63) - (bound >> 63);
|
||||
}
|
||||
/**
|
||||
* Inclusive inner, exclusive outer; both inner and outer can be positive or negative.
|
||||
* @param inner the inner bound, inclusive, can be positive or negative
|
||||
* @param outer the outer bound, exclusive, can be positive or negative and may be greater than or less than inner
|
||||
* @return a random long that may be equal to inner and will otherwise be between inner and outer
|
||||
*/
|
||||
public long nextLong(final long inner, final long outer) {
|
||||
return inner + nextLong(outer - inner);
|
||||
}
|
||||
|
||||
public double nextDouble() {
|
||||
return (nextLong() & DOUBLE_MASK) * NORM_53;
|
||||
}
|
||||
|
||||
public float nextFloat() {
|
||||
return (float) ((nextLong() & FLOAT_MASK) * NORM_24);
|
||||
}
|
||||
|
||||
public boolean nextBoolean() {
|
||||
return nextLong() < 0L;
|
||||
}
|
||||
|
||||
public void nextBytes(final byte[] bytes) {
|
||||
int i = bytes.length, n = 0;
|
||||
while (i != 0) {
|
||||
n = Math.min(i, 8);
|
||||
for (long bits = nextLong(); n-- != 0; bits >>>= 8) {
|
||||
bytes[--i] = (byte) bits;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the seed of this generator using one long, running that through LightRNG's algorithm twice to get the state.
|
||||
* @param seed the number to use as the seed
|
||||
*/
|
||||
public void setSeed(final long seed) {
|
||||
|
||||
long state = seed + 0x9E3779B97F4A7C15L,
|
||||
z = state;
|
||||
z = (z ^ (z >>> 30)) * 0xBF58476D1CE4E5B9L;
|
||||
z = (z ^ (z >>> 27)) * 0x94D049BB133111EBL;
|
||||
state0 = z ^ (z >>> 31);
|
||||
state += 0x9E3779B97F4A7C15L;
|
||||
z = state;
|
||||
z = (z ^ (z >>> 30)) * 0xBF58476D1CE4E5B9L;
|
||||
z = (z ^ (z >>> 27)) * 0x94D049BB133111EBL;
|
||||
state1 = z ^ (z >>> 31);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the seed of this generator using two longs, using them without changes unless both are 0 (then it makes the
|
||||
* state variable corresponding to stateA 1 instead).
|
||||
* @param stateA the number to use as the first part of the state; this will be 1 instead if both seeds are 0
|
||||
* @param stateB the number to use as the second part of the state
|
||||
*/
|
||||
public void setSeed(final long stateA, final long stateB) {
|
||||
|
||||
state0 = stateA;
|
||||
state1 = stateB;
|
||||
if((stateA | stateB) == 0L)
|
||||
state0 = 1L;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.torvald.tsvm
|
||||
|
||||
import net.torvald.UnsafeHelper
|
||||
import net.torvald.random.HQRNG
|
||||
import net.torvald.tsvm.peripheral.GraphicsAdapter
|
||||
import java.lang.NumberFormatException
|
||||
|
||||
@@ -9,49 +10,162 @@ import java.lang.NumberFormatException
|
||||
*/
|
||||
class Videotron2K(val gpu: GraphicsAdapter) {
|
||||
|
||||
private val screenfiller = """
|
||||
DEFINE RATEF 60
|
||||
DEFINE height 448
|
||||
DEFINE width 560
|
||||
|
||||
SCENE fill_line
|
||||
@ mov 0 px
|
||||
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
|
||||
END SCENE
|
||||
|
||||
SCENE loop_frame
|
||||
@ mov 0 py
|
||||
perform fill_line
|
||||
inc py
|
||||
next
|
||||
; there's no EXIT command so this scene will make the program to go loop indefinitely
|
||||
END SCENE
|
||||
|
||||
perform loop_frame
|
||||
|
||||
""".trimIndent()
|
||||
|
||||
private var regs = UnsafeHelper.allocate(16 * 8)
|
||||
private var internalMem = UnsafeHelper.allocate(16384)
|
||||
|
||||
private var scenes = HashMap<String, Array<VT2Statement>>()
|
||||
private var varIdTable = HashMap<String, Long>()
|
||||
private var scenes = HashMap<Long, Array<VT2Statement>>()
|
||||
private var varIdTable = HashMap<String, Long>() // String is always uppercase, Long always has VARIABLE_PREFIX added
|
||||
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 reTokenizer = Regex(""" +""")
|
||||
|
||||
private val conditional = arrayOf("ZR", "NZ", "GT", "LS", "GE", "LE")
|
||||
private val debugPrint = true
|
||||
private val rng = HQRNG()
|
||||
|
||||
fun eval(command: String) {
|
||||
var command = command.replace(reComment, "")
|
||||
}
|
||||
val rootStatements = ArrayList<VT2Statement>()
|
||||
val sceneStatements = ArrayList<VT2Statement>()
|
||||
|
||||
private fun translateLine(lnum: Int, line: String): VT2Statement? {
|
||||
val tokens = line.split(reTokenizer)
|
||||
if (tokens.isEmpty()) return null
|
||||
command.replace(reComment, "").split('\n')
|
||||
.mapIndexed { index, s -> index to s }.filter { it.second.isNotBlank() }
|
||||
.forEach { (lnum, stmt) ->
|
||||
val stmtUpper = stmt.toUpperCase()
|
||||
val wordsUpper = stmtUpper.split(reTokenizer)
|
||||
|
||||
val isInit = tokens[0] == "@"
|
||||
val cmdstr = tokens[isInit.toInt()].toUpperCase()
|
||||
val cmdcond = (conditional.linearSearch { it == cmdstr.substring(cmdstr.length - 2, cmdstr.length) } ?: -1) + 1
|
||||
val realcmd = if (cmdcond > 0) cmdstr.substring(0, cmdstr.length - 2) else cmdstr
|
||||
if (stmtUpper.startsWith("SCENE_")) { // indexed scene
|
||||
val scenenumStr = stmt.substring(6)
|
||||
try {
|
||||
val scenenum = scenenumStr.toLong()
|
||||
|
||||
val cmd: Int = Command.dict[realcmd] ?: throw RuntimeException("Syntax Error at line $lnum")
|
||||
val args = tokens.subList(1 + isInit.toInt(), tokens.size).map { parseArgString(it) }
|
||||
currentScene = scenenum
|
||||
}
|
||||
catch (e: NumberFormatException) {
|
||||
throw IllegalArgumentException("Line $lnum: Illegal scene numeral on $scenenumStr")
|
||||
}
|
||||
}
|
||||
else if (stmtUpper.startsWith("SCENE ")) { // named scene
|
||||
val sceneName = wordsUpper[1]
|
||||
if (sceneName.isNullOrBlank()) {
|
||||
throw IllegalArgumentException("Line $lnum: Illegal scene name on $stmt")
|
||||
}
|
||||
else if (hasVar(sceneName)) {
|
||||
throw IllegalArgumentException("Line $lnum: Scene name or variable '$sceneName' already exists")
|
||||
}
|
||||
|
||||
return VT2Statement(if (isInit) StatementPrefix.INIT else StatementPrefix.NONE, cmd or cmdcond, args.toLongArray())
|
||||
}
|
||||
currentScene = registerNewVariable(sceneName)
|
||||
}
|
||||
else if (wordsUpper[0] == "END" && wordsUpper[1] == "SCENE") { // END SCENE
|
||||
if (currentScene == null) {
|
||||
throw IllegalArgumentException("Line $lnum: END SCENE is called without matching SCENE definition")
|
||||
}
|
||||
|
||||
private fun parseArgString(token: String): Long {
|
||||
if (token.toIntOrNull() != null)
|
||||
return token.toLong().and(0xFFFFFFFF)
|
||||
else if (token.endsWith('h') && token.substring(0, token.lastIndex).toIntOrNull() != null)
|
||||
return token.substring(0, token.lastIndex).toInt(16).toLong().and(0xFFFFFFFF)
|
||||
else if (token.startsWith('r') && token.substring(1, token.length).toIntOrNull() != null)
|
||||
return REGISTER_PREFIX or token.substring(1, token.length).toLong().and(0xFFFFFFFF)
|
||||
else {
|
||||
TODO("variable assignation and utilisation")
|
||||
scenes[currentScene!!] = sceneStatements.toTypedArray()
|
||||
|
||||
sceneStatements.clear()
|
||||
currentScene = null
|
||||
}
|
||||
else {
|
||||
val cmdBuffer = if (currentScene != null) sceneStatements else rootStatements
|
||||
|
||||
cmdBuffer.add(translateLine(lnum, stmt))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (debugPrint) {
|
||||
scenes.forEach { id, statements ->
|
||||
println("SCENE #$id")
|
||||
statements.forEach { println(" $it") }
|
||||
println("END SCENE\n")
|
||||
}
|
||||
|
||||
rootStatements.forEach { println(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private class VT2Statement(val prefix: Int = StatementPrefix.NONE, val command: Int, val args: LongArray)
|
||||
private fun translateLine(lnum: Int, line: String): VT2Statement {
|
||||
val tokens = line.split(reTokenizer)
|
||||
if (tokens.isEmpty()) throw InternalError("Line $lnum: empty line not filtered!")
|
||||
|
||||
val isInit = tokens[0] == "@"
|
||||
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 args = tokens.subList(1 + isInit.toInt(), tokens.size).map { parseArgString(it) }
|
||||
|
||||
return VT2Statement(if (isInit) StatementPrefix.INIT else StatementPrefix.NONE, cmd, args.toLongArray())
|
||||
}
|
||||
|
||||
private fun parseArgString(token: String): Long {
|
||||
if (token.toIntOrNull() != null) // number literal
|
||||
return token.toLong().and(0xFFFFFFFF)
|
||||
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)
|
||||
else if (token.startsWith('r') && token.substring(1, token.length).toIntOrNull() != null) // r-registers
|
||||
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
|
||||
return REGISTER_PREFIX or token.substring(1, token.length).toLong().plus(5).and(0xFFFFFFFF)
|
||||
else {
|
||||
val varId = varIdTable[token.toUpperCase()] ?: throw IllegalArgumentException("Undefined variable: $token")
|
||||
|
||||
return varId
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerNewVariable(varName: String): Long {
|
||||
var id: Long
|
||||
do {
|
||||
id = VARIABLE_PREFIX or rng.nextLong().and(0xFFFFFFFFL)
|
||||
} while (varIdTable.containsValue(id))
|
||||
|
||||
varIdTable[varName.toUpperCase()] = id
|
||||
return id
|
||||
}
|
||||
|
||||
private fun hasVar(name: String) = (varIdTable.containsKey(name.toUpperCase()))
|
||||
|
||||
|
||||
private class VT2Statement(val prefix: Int = StatementPrefix.NONE, val command: Int, val args: LongArray) {
|
||||
override fun toString(): String {
|
||||
return StatementPrefix.toString(prefix) + " " + Command.reverseDict[command] + " " + (args.map { argsToString(it) + " " })
|
||||
}
|
||||
|
||||
private fun argsToString(i: Long): String {
|
||||
if (i and REGISTER_PREFIX != 0L) {
|
||||
val regnum = i and 0xFFFFFFFFL
|
||||
return if (regnum < 6) "r${regnum + 1}" else "c${regnum - 5}"
|
||||
}
|
||||
else return i.toInt().toString()
|
||||
}
|
||||
}
|
||||
|
||||
fun dispose() {
|
||||
regs.destroy()
|
||||
@@ -70,15 +184,54 @@ class Videotron2K(val gpu: GraphicsAdapter) {
|
||||
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 {
|
||||
const val NONE = 0
|
||||
const val INIT = 1
|
||||
|
||||
fun toString(key: Int) = when(key) {
|
||||
INIT -> "@"
|
||||
else -> " "
|
||||
}
|
||||
}
|
||||
|
||||
object Command {
|
||||
val conditional = arrayOf("ZR", "NZ", "GT", "LS", "GE", "LE")
|
||||
|
||||
const val NOP = 0
|
||||
const val ADD = 0x8
|
||||
const val SUB = 0x10
|
||||
@@ -154,4 +307,15 @@ object Command {
|
||||
|
||||
"DEFINE" to DEFINE
|
||||
)
|
||||
|
||||
// fill in conditionals to dict
|
||||
init {
|
||||
dict.entries.forEach { (command, opcode) ->
|
||||
conditional.forEachIndexed { i, cond ->
|
||||
dict[command + cond] = opcode + i + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val reverseDict = HashMap<Int, String>(dict.entries.associate { (k,v)-> v to k })
|
||||
}
|
||||
Reference in New Issue
Block a user