fix vm signed comparisons

This commit is contained in:
Irmen de Jong 2022-03-28 23:49:44 +02:00
parent f07065bf84
commit 3e1a7c6102
11 changed files with 234 additions and 155 deletions

View File

@ -92,6 +92,13 @@ internal class BuiltinFuncGen(val codeGen: CodeGen, val exprGen: ExpressionGen)
code += exprGen.translateExpression(call.args.single(), addressReg, regUsage)
code += VmCodeInstruction(Instruction(Opcode.LOADI, VmDataType.WORD, reg1 = resultRegister, reg2=addressReg))
}
"mkword" -> {
val msbReg = regUsage.nextFree()
val lsbReg = regUsage.nextFree()
code += exprGen.translateExpression(call.args[0], msbReg, regUsage)
code += exprGen.translateExpression(call.args[1], lsbReg, regUsage)
code += VmCodeInstruction(Instruction(Opcode.CONCAT, VmDataType.BYTE, reg1=resultRegister, reg2=msbReg, reg3=lsbReg))
}
else -> {
TODO("builtinfunc ${call.name}")
// code += VmCodeInstruction(Instruction(Opcode.NOP))

View File

@ -233,7 +233,8 @@ internal class ExpressionGen(val codeGen: CodeGen) {
val rightCode = translateExpression(binExpr.right, rightResultReg, regUsage)
code += leftCode
code += rightCode
val vmDt = codeGen.vmType(binExpr.type)
val vmDt = codeGen.vmType(binExpr.left.type)
val signed = binExpr.left.type in SignedDatatypes
when(binExpr.operator) {
"+" -> {
code += VmCodeInstruction(Instruction(Opcode.ADD, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
@ -263,7 +264,8 @@ internal class ExpressionGen(val codeGen: CodeGen) {
code += VmCodeInstruction(Instruction(Opcode.LSL, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
}
">>" -> {
code += VmCodeInstruction(Instruction(Opcode.LSR, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
val opc = if(signed) Opcode.ASR else Opcode.LSR
code += VmCodeInstruction(Instruction(opc, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
}
"==" -> {
code += VmCodeInstruction(Instruction(Opcode.SEQ, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
@ -272,19 +274,19 @@ internal class ExpressionGen(val codeGen: CodeGen) {
code += VmCodeInstruction(Instruction(Opcode.SNE, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
}
"<" -> {
val ins = if(binExpr.type in SignedDatatypes) Opcode.SLTS else Opcode.SLT
val ins = if(signed) Opcode.SLTS else Opcode.SLT
code += VmCodeInstruction(Instruction(ins, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
}
">" -> {
val ins = if(binExpr.type in SignedDatatypes) Opcode.SGTS else Opcode.SGT
val ins = if(signed) Opcode.SGTS else Opcode.SGT
code += VmCodeInstruction(Instruction(ins, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
}
"<=" -> {
val ins = if(binExpr.type in SignedDatatypes) Opcode.SLES else Opcode.SLE
val ins = if(signed) Opcode.SLES else Opcode.SLE
code += VmCodeInstruction(Instruction(ins, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
}
">=" -> {
val ins = if(binExpr.type in SignedDatatypes) Opcode.SGES else Opcode.SGE
val ins = if(signed) Opcode.SGES else Opcode.SGE
code += VmCodeInstruction(Instruction(ins, vmDt, reg1=resultRegister, reg2=leftResultReg, reg3=rightResultReg))
}
else -> throw AssemblyError("weird operator ${binExpr.operator}")

View File

@ -32,6 +32,9 @@ sys {
const ubyte SC_GFX_ENABLE = 8
const ubyte SC_GFX_CLEAR = 9
const ubyte SC_GFX_PLOT = 10
const ubyte SC_RND = 11
const ubyte SC_WAIT = 12
const ubyte SC_WAITVSYNC = 13
sub reset_system() {
@ -41,24 +44,34 @@ sys {
sub wait(uword jiffies) {
; --- wait approximately the given number of jiffies (1/60th seconds)
; TODO
syscall1(SC_WAIT, jiffies)
}
sub waitvsync() {
; --- busy wait till the next vsync has occurred (approximately), without depending on custom irq handling.
; TODO
syscall(SC_WAITVSYNC)
}
sub memcopy(uword source, uword target, uword count) {
; TODO
repeat count {
@(target) = @(source)
source++
target++
}
}
sub memset(uword mem, uword numbytes, ubyte value) {
; TODO
repeat numbytes {
@(mem) = value
mem++
}
}
sub memsetw(uword mem, uword numwords, uword value) {
; TODO
repeat numwords {
pokew(mem, value)
mem+=2
}
}
sub exit(ubyte returnvalue) {

View File

@ -3,7 +3,6 @@ TODO
For next release
^^^^^^^^^^^^^^^^
- vm codegen: fix ifelse comparisons such as if x< 0
- vm codegen: fix primes endless loop stuck on '2'
- x16: check additional FP lib changes https://github.com/commanderx16/x16-rom/commit/ae608673f0210953172d6837acfbb231d62ddbd1
and https://github.com/commanderx16/x16-docs/commit/21238aedc641da91df88e04c4ce9bf3324a3c12d
@ -13,6 +12,7 @@ For next release
broken examples: amiga, colorbars, cube3d, highresbitmap, rasterbars, tehtriz, testgfx2, testvtui
can we make the code read the new layout from vera registers instead of hardcoding it?
- x16: check new ZP free addresses https://github.com/commanderx16/x16-docs/commit/541f2ce9e61d1d0d0e157d7f52fe16bc0895e6f0
and https://www.commanderx16.com/forum/index.php?/topic/363-keep-zero-page-empty-as-much-as-possible/#comment-18561
- x16: optimize diskio load_raw because headerless files are now supported https://github.com/commanderx16/x16-rom/pull/216
note: must still work on c64/c128 that don't have this!
- major compiler version bump once X16 r39 rom is officially finalized
@ -23,6 +23,7 @@ For next release
- vm codegen: postincrdecr arrayvalue
- vm: support no globals re-init option
- vm: how to remove all unused subroutines? (for asm, 64tass used to do this)
- vm: rather than being able to jump to any 'address' (IPTR), use 'blocks'
- vm codegen/assembler: variable memory locations should also be referenced by the variable name instead of just the address
- when the vm is stable and *if* its language can get promoted to prog8 IL, the variable allocation should be changed.
It's now done before the vm code generation, but the IL should probably not depend on the allocations already performed.

View File

@ -4,39 +4,13 @@
main {
sub start() {
byte bb = -9
word ww = -1234
if bb<0 {
txt.print("bb <0!\n")
} else {
txt.print("comparison error!\n")
}
if ww<0 {
txt.print("ww <0!\n")
} else {
txt.print("comparison error!\n")
}
if bb>0 {
txt.print("comparison error!\n")
} else {
txt.print("bb not >0\n")
}
if ww>0 {
txt.print("comparison error!\n")
} else {
txt.print("ww not >0\n")
}
txt.print_w(ww)
txt.spc()
ww <<= 3
txt.print_w(ww)
ubyte x1 = $ea
ubyte x2 = $31
uword uw = mkword(x1, $99)
txt.print_uwhex(uw, true)
txt.nl()
txt.print_b(bb)
txt.spc()
bb <<= 3
txt.print_b(bb)
uw = mkword(x1, x2)
txt.print_uwhex(uw, true)
txt.nl()
sys.exit(99)
@ -80,6 +54,7 @@ main {
; sys.exit(99)
; the "pixelshader":
; syscall1(8, 0) ; enable lo res creen
; ubyte shifter
;

View File

@ -32,14 +32,14 @@ class Assembler {
"ubyte", "byte" -> {
val array = values.split(',').map { parseValue(it.trim(), 0) }
for (value in array) {
memory.setB(address, value.toUByte())
memory.setUB(address, value.toUByte())
address++
}
}
"uword", "word" -> {
val array = values.split(',').map { parseValue(it.trim(), 0) }
for (value in array) {
memory.setW(address, value.toUShort())
memory.setUW(address, value.toUShort())
address++
}
}

View File

@ -115,11 +115,11 @@ and reg1, reg2, reg3 - reg1 = reg2 bitwise and reg3
or reg1, reg2, reg3 - reg1 = reg2 bitwise or reg3
xor reg1, reg2, reg3 - reg1 = reg2 bitwise xor reg3
lsr reg1, reg2, reg3 - reg1 = shift reg2 right by reg3 bits
asr reg1, reg2, reg3 - reg1 = shift reg2 right by reg3 bits (signed)
lsl reg1, reg2, reg3 - reg1 = shift reg2 left by reg3 bits
ror reg1, reg2, reg3 - reg1 = rotate reg2 right by reg3 bits, not using carry
rol reg1, reg2, reg3 - reg1 = rotate reg2 left by reg3 bits, not using carry
TODO add asr? (arithmetical shift right, so with sign bit)
TODO also add ror/rol variants using the carry bit? These do map directly on 6502 and 68k instructions.
@ -131,6 +131,7 @@ breakpoint - trigger a breakpoint
copy reg1, reg2, length - copy memory from ptrs in reg1 to reg3, length bytes
copyz reg1, reg2 - copy memory from ptrs in reg1 to reg3, stop after first 0-byte
swap [b, w] reg1, reg2 - reg1 = swapped lsb and msb from register reg2 (16 bits) or lsw and msw (32 bits)
concat [b, w] reg1, reg2, reg3 - reg1 = concatenated lsb/lsw of reg2 and lsb/lsw of reg3 into new word or int (int not yet implemented, requires 32bits regs)
push [b, w] reg1 - push value in reg1 on the stack
pop [b, w] reg1 - pop value from stack into reg1
@ -143,7 +144,6 @@ enum class Opcode {
LOADI,
LOADX,
LOADR,
SWAPREG,
STOREM,
STOREI,
STOREX,
@ -194,6 +194,7 @@ enum class Opcode {
AND,
OR,
XOR,
ASR,
LSR,
LSL,
ROR,
@ -202,6 +203,8 @@ enum class Opcode {
PUSH,
POP,
SWAP,
SWAPREG,
CONCAT,
COPY,
COPYZ,
BREAKPOINT
@ -321,6 +324,7 @@ val instructionFormats = mutableMapOf(
Opcode.AND to InstructionFormat(BW, true, true, true, false),
Opcode.OR to InstructionFormat(BW, true, true, true, false),
Opcode.XOR to InstructionFormat(BW, true, true, true, false),
Opcode.ASR to InstructionFormat(BW, true, true, true, false),
Opcode.LSR to InstructionFormat(BW, true, true, true, false),
Opcode.LSL to InstructionFormat(BW, true, true, true, false),
Opcode.ROR to InstructionFormat(BW, true, true, true, false),
@ -331,5 +335,6 @@ val instructionFormats = mutableMapOf(
Opcode.SWAP to InstructionFormat(BW, true, true, false, false),
Opcode.PUSH to InstructionFormat(BW, true, false, false, false),
Opcode.POP to InstructionFormat(BW, true, false, false, false),
Opcode.CONCAT to InstructionFormat(BW, true, true, true, false),
Opcode.BREAKPOINT to InstructionFormat(NN, false, false, false, false)
)

View File

@ -10,23 +10,41 @@ class Memory {
mem.fill(0u)
}
fun getB(address: Int): UByte {
fun getUB(address: Int): UByte {
return mem[address]
}
fun setB(address: Int, value: UByte) {
fun getSB(address: Int): Byte {
return mem[address].toByte()
}
fun setUB(address: Int, value: UByte) {
mem[address] = value
}
fun getW(address: Int): UShort {
fun setSB(address: Int, value: Byte) {
mem[address] = value.toUByte()
}
fun getUW(address: Int): UShort {
return (256u*mem[address] + mem[address+1]).toUShort()
}
fun setW(address: Int, value: UShort) {
fun getSW(address: Int): Short {
return (mem[address].toInt()*256 + mem[address+1].toInt()).toShort()
}
fun setUW(address: Int, value: UShort) {
mem[address] = (value.toInt() ushr 8).toUByte()
mem[address+1] = value.toUByte()
}
fun setSW(address: Int, value: Short) {
val uv = value.toUShort()
mem[address] = (uv.toInt() ushr 8).toUByte()
mem[address+1] = uv.toUByte()
}
// for now, no LONG 32-bits and no FLOAT support.
// fun getL(address: Int): UInt {
// return mem[address+3] + 256u*mem[address+2] + 65536u*mem[address+1] + 16777216u*mem[address]

View File

@ -10,15 +10,27 @@ class Registers {
registers.fill(0u)
}
fun setB(reg: Int, value: UByte) {
fun setUB(reg: Int, value: UByte) {
registers[reg] = registers[reg] and 0xff00u or value.toUShort()
}
fun setW(reg: Int, value: UShort) {
fun setSB(reg: Int, value: Byte) {
registers[reg] = registers[reg] and 0xff00u or (value.toUShort() and 0x00ffu)
}
fun setUW(reg: Int, value: UShort) {
registers[reg] = value
}
fun getB(reg: Int) = registers[reg].toUByte()
fun setSW(reg: Int, value: Short) {
registers[reg] = value.toUShort()
}
fun getW(reg: Int) = registers[reg]
}
fun getUB(reg: Int) = registers[reg].toUByte()
fun getSB(reg: Int) = registers[reg].toByte()
fun getUW(reg: Int) = registers[reg]
fun getSW(reg: Int) = registers[reg].toShort()
}

View File

@ -17,7 +17,9 @@ SYSCALLS:
8 = gfx_enable ; enable graphics window r0.b = 0 -> lores 320x240, r0.b = 1 -> hires 640x480
9 = gfx_clear ; clear graphics window with shade in r0.b
10 = gfx_plot ; plot pixel in graphics window, r0.w/r1.w contain X and Y coordinates, r2.b contains brightness
11 = rnd ; random BYTE
11 = rnd ; random BYTE
12 = wait ; wait certain amount of jiffies (1/60 sec)
13 = waitvsync ; wait on vsync
*/
enum class Syscall {
@ -33,6 +35,8 @@ enum class Syscall {
GFX_CLEAR,
GFX_PLOT,
RND,
WAIT,
WAITVSYNC
}
object SysCalls {
@ -45,13 +49,13 @@ object SysCalls {
vm.exit()
}
Syscall.PRINT_C -> {
val char = vm.registers.getB(0).toInt()
val char = vm.registers.getUB(0).toInt()
print(Char(char))
}
Syscall.PRINT_S -> {
var addr = vm.registers.getW(0).toInt()
var addr = vm.registers.getUW(0).toInt()
while(true) {
val char = vm.memory.getB(addr).toInt()
val char = vm.memory.getUB(addr).toInt()
if(char==0)
break
print(Char(char))
@ -59,29 +63,34 @@ object SysCalls {
}
}
Syscall.PRINT_U8 -> {
print(vm.registers.getB(0))
print(vm.registers.getUB(0))
}
Syscall.PRINT_U16 -> {
print(vm.registers.getW(0))
print(vm.registers.getUW(0))
}
Syscall.INPUT -> {
var input = readln()
val maxlen = vm.registers.getB(1).toInt()
val maxlen = vm.registers.getUB(1).toInt()
if(maxlen>0)
input = input.substring(0, min(input.length, maxlen))
vm.memory.setString(vm.registers.getW(0).toInt(), input, true)
vm.registers.setW(65535, input.length.toUShort())
vm.memory.setString(vm.registers.getUW(0).toInt(), input, true)
vm.registers.setUW(65535, input.length.toUShort())
}
Syscall.SLEEP -> {
val duration = vm.registers.getW(0).toLong()
val duration = vm.registers.getUW(0).toLong()
Thread.sleep(duration)
}
Syscall.GFX_ENABLE -> vm.gfx_enable()
Syscall.GFX_CLEAR -> vm.gfx_clear()
Syscall.GFX_PLOT -> vm.gfx_plot()
Syscall.RND -> {
vm.registers.setB(0, (Random.nextInt() ushr 3).toUByte())
vm.registers.setUB(0, (Random.nextInt() ushr 3).toUByte())
}
Syscall.WAIT -> {
val millis = vm.registers.getUW(0).toLong() * 1000/60
Thread.sleep(millis)
}
Syscall.WAITVSYNC -> vm.waitvsync()
else -> TODO("syscall ${call.name}")
}
}

View File

@ -1,5 +1,6 @@
package prog8.vm
import java.awt.Toolkit
import java.util.*
import kotlin.math.roundToInt
@ -60,7 +61,7 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
}
fun exit() {
throw ProgramExitException(registers.getW(0).toInt())
throw ProgramExitException(registers.getUW(0).toInt())
}
fun step(count: Int=1) {
@ -132,11 +133,13 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
Opcode.AND -> InsAND(ins)
Opcode.OR -> InsOR(ins)
Opcode.XOR -> InsXOR(ins)
Opcode.ASR -> InsASR(ins)
Opcode.LSR -> InsLSR(ins)
Opcode.LSL -> InsLSL(ins)
Opcode.ROR -> InsROR(ins)
Opcode.ROL -> InsROL(ins)
Opcode.SWAP -> InsSWAP(ins)
Opcode.CONCAT -> InsCONCAT(ins)
Opcode.PUSH -> InsPUSH(ins)
Opcode.POP -> InsPOP(ins)
Opcode.COPY -> InsCOPY(ins)
@ -148,8 +151,8 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
private inline fun setResultReg(reg: Int, value: Int, type: VmDataType) {
when(type) {
VmDataType.BYTE -> registers.setB(reg, value.toUByte())
VmDataType.WORD -> registers.setW(reg, value.toUShort())
VmDataType.BYTE -> registers.setUB(reg, value.toUByte())
VmDataType.WORD -> registers.setUW(reg, value.toUShort())
}
}
@ -158,8 +161,8 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
throw StackOverflowError("valuestack limit 128 exceeded")
val value = when(i.type!!) {
VmDataType.BYTE -> registers.getB(i.reg1!!).toInt()
VmDataType.WORD -> registers.getW(i.reg1!!).toInt()
VmDataType.BYTE -> registers.getUB(i.reg1!!).toInt()
VmDataType.WORD -> registers.getUW(i.reg1!!).toInt()
}
valueStack.push(value)
pc++
@ -189,32 +192,32 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
private fun InsLOADM(i: Instruction) {
when(i.type!!) {
VmDataType.BYTE -> registers.setB(i.reg1!!, memory.getB(i.value!!))
VmDataType.WORD -> registers.setW(i.reg1!!, memory.getW(i.value!!))
VmDataType.BYTE -> registers.setUB(i.reg1!!, memory.getUB(i.value!!))
VmDataType.WORD -> registers.setUW(i.reg1!!, memory.getUW(i.value!!))
}
pc++
}
private fun InsLOADI(i: Instruction) {
when(i.type!!) {
VmDataType.BYTE -> registers.setB(i.reg1!!, memory.getB(registers.getW(i.reg2!!).toInt()))
VmDataType.WORD -> registers.setW(i.reg1!!, memory.getW(registers.getW(i.reg2!!).toInt()))
VmDataType.BYTE -> registers.setUB(i.reg1!!, memory.getUB(registers.getUW(i.reg2!!).toInt()))
VmDataType.WORD -> registers.setUW(i.reg1!!, memory.getUW(registers.getUW(i.reg2!!).toInt()))
}
pc++
}
private fun InsLOADX(i: Instruction) {
when (i.type!!) {
VmDataType.BYTE -> registers.setB(i.reg1!!, memory.getB(i.value!! + registers.getW(i.reg2!!).toInt()))
VmDataType.WORD -> registers.setW(i.reg1!!, memory.getW(i.value!! + registers.getW(i.reg2!!).toInt()))
VmDataType.BYTE -> registers.setUB(i.reg1!!, memory.getUB(i.value!! + registers.getUW(i.reg2!!).toInt()))
VmDataType.WORD -> registers.setUW(i.reg1!!, memory.getUW(i.value!! + registers.getUW(i.reg2!!).toInt()))
}
pc++
}
private fun InsLOADR(i: Instruction) {
when(i.type!!) {
VmDataType.BYTE -> registers.setB(i.reg1!!, registers.getB(i.reg2!!))
VmDataType.WORD -> registers.setW(i.reg1!!, registers.getW(i.reg2!!))
VmDataType.BYTE -> registers.setUB(i.reg1!!, registers.getUB(i.reg2!!))
VmDataType.WORD -> registers.setUW(i.reg1!!, registers.getUW(i.reg2!!))
}
pc++
}
@ -222,14 +225,14 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
private fun InsSWAPREG(i: Instruction) {
when(i.type!!) {
VmDataType.BYTE -> {
val oldR2 = registers.getB(i.reg2!!)
registers.setB(i.reg2, registers.getB(i.reg1!!))
registers.setB(i.reg1, oldR2)
val oldR2 = registers.getUB(i.reg2!!)
registers.setUB(i.reg2, registers.getUB(i.reg1!!))
registers.setUB(i.reg1, oldR2)
}
VmDataType.WORD -> {
val oldR2 = registers.getW(i.reg2!!)
registers.setW(i.reg2, registers.getW(i.reg1!!))
registers.setW(i.reg1, oldR2)
val oldR2 = registers.getUW(i.reg2!!)
registers.setUW(i.reg2, registers.getUW(i.reg1!!))
registers.setUW(i.reg1, oldR2)
}
}
pc++
@ -237,47 +240,47 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
private fun InsSTOREM(i: Instruction) {
when(i.type!!) {
VmDataType.BYTE -> memory.setB(i.value!!, registers.getB(i.reg1!!))
VmDataType.WORD -> memory.setW(i.value!!, registers.getW(i.reg1!!))
VmDataType.BYTE -> memory.setUB(i.value!!, registers.getUB(i.reg1!!))
VmDataType.WORD -> memory.setUW(i.value!!, registers.getUW(i.reg1!!))
}
pc++
}
private fun InsSTOREI(i: Instruction) {
when (i.type!!) {
VmDataType.BYTE -> memory.setB(registers.getW(i.reg2!!).toInt(), registers.getB(i.reg1!!))
VmDataType.WORD -> memory.setW(registers.getW(i.reg2!!).toInt(), registers.getW(i.reg1!!))
VmDataType.BYTE -> memory.setUB(registers.getUW(i.reg2!!).toInt(), registers.getUB(i.reg1!!))
VmDataType.WORD -> memory.setUW(registers.getUW(i.reg2!!).toInt(), registers.getUW(i.reg1!!))
}
pc++
}
private fun InsSTOREX(i: Instruction) {
when (i.type!!) {
VmDataType.BYTE -> memory.setB(registers.getW(i.reg2!!).toInt() + i.value!!, registers.getB(i.reg1!!))
VmDataType.WORD -> memory.setW(registers.getW(i.reg2!!).toInt() + i.value!!, registers.getW(i.reg1!!))
VmDataType.BYTE -> memory.setUB(registers.getUW(i.reg2!!).toInt() + i.value!!, registers.getUB(i.reg1!!))
VmDataType.WORD -> memory.setUW(registers.getUW(i.reg2!!).toInt() + i.value!!, registers.getUW(i.reg1!!))
}
pc++
}
private fun InsSTOREZ(i: Instruction) {
when(i.type!!) {
VmDataType.BYTE -> memory.setB(i.value!!, 0u)
VmDataType.WORD -> memory.setW(i.value!!, 0u)
VmDataType.BYTE -> memory.setUB(i.value!!, 0u)
VmDataType.WORD -> memory.setUW(i.value!!, 0u)
}
}
private fun InsSTOREZI(i: Instruction) {
when (i.type!!) {
VmDataType.BYTE -> memory.setB(registers.getW(i.reg2!!).toInt(), 0u)
VmDataType.WORD -> memory.setW(registers.getW(i.reg2!!).toInt(), 0u)
VmDataType.BYTE -> memory.setUB(registers.getUW(i.reg2!!).toInt(), 0u)
VmDataType.WORD -> memory.setUW(registers.getUW(i.reg2!!).toInt(), 0u)
}
pc++
}
private fun InsSTOREZX(i: Instruction) {
when (i.type!!) {
VmDataType.BYTE -> memory.setB(registers.getW(i.reg2!!).toInt() + i.value!!, 0u)
VmDataType.WORD -> memory.setW(registers.getW(i.reg2!!).toInt() + i.value!!, 0u)
VmDataType.BYTE -> memory.setUB(registers.getUW(i.reg2!!).toInt() + i.value!!, 0u)
VmDataType.WORD -> memory.setUW(registers.getUW(i.reg2!!).toInt() + i.value!!, 0u)
}
pc++
}
@ -287,7 +290,7 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
}
private fun InsJUMPI(i: Instruction) {
pc = registers.getW(i.reg1!!).toInt()
pc = registers.getUW(i.reg1!!).toInt()
}
private fun InsCALL(i: Instruction) {
@ -297,7 +300,7 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
private fun InsCALLI(i: Instruction) {
callStack.push(pc+1)
pc = registers.getW(i.reg1!!).toInt()
pc = registers.getUW(i.reg1!!).toInt()
}
private fun InsRETURN() {
@ -310,13 +313,13 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
private fun InsBZ(i: Instruction) {
when(i.type!!) {
VmDataType.BYTE -> {
if(registers.getB(i.reg1!!)==0.toUByte())
if(registers.getUB(i.reg1!!)==0.toUByte())
pc = i.value!!
else
pc++
}
VmDataType.WORD -> {
if(registers.getW(i.reg1!!)==0.toUShort())
if(registers.getUW(i.reg1!!)==0.toUShort())
pc = i.value!!
else
pc++
@ -327,13 +330,13 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
private fun InsBNZ(i: Instruction) {
when(i.type!!) {
VmDataType.BYTE -> {
if(registers.getB(i.reg1!!)!=0.toUByte())
if(registers.getUB(i.reg1!!)!=0.toUByte())
pc = i.value!!
else
pc++
}
VmDataType.WORD -> {
if(registers.getW(i.reg1!!)!=0.toUShort())
if(registers.getUW(i.reg1!!)!=0.toUShort())
pc = i.value!!
else
pc++
@ -498,24 +501,24 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
private fun InsINC(i: Instruction) {
when(i.type!!) {
VmDataType.BYTE -> registers.setB(i.reg1!!, (registers.getB(i.reg1)+1u).toUByte())
VmDataType.WORD -> registers.setW(i.reg1!!, (registers.getW(i.reg1)+1u).toUShort())
VmDataType.BYTE -> registers.setUB(i.reg1!!, (registers.getUB(i.reg1)+1u).toUByte())
VmDataType.WORD -> registers.setUW(i.reg1!!, (registers.getUW(i.reg1)+1u).toUShort())
}
pc++
}
private fun InsDEC(i: Instruction) {
when(i.type!!) {
VmDataType.BYTE -> registers.setB(i.reg1!!, (registers.getB(i.reg1)-1u).toUByte())
VmDataType.WORD -> registers.setW(i.reg1!!, (registers.getW(i.reg1)-1u).toUShort())
VmDataType.BYTE -> registers.setUB(i.reg1!!, (registers.getUB(i.reg1)-1u).toUByte())
VmDataType.WORD -> registers.setUW(i.reg1!!, (registers.getUW(i.reg1)-1u).toUShort())
}
pc++
}
private fun InsNEG(i: Instruction) {
when(i.type!!) {
VmDataType.BYTE -> registers.setB(i.reg1!!, (-registers.getB(i.reg1).toInt()).toUByte())
VmDataType.WORD -> registers.setW(i.reg1!!, (-registers.getW(i.reg1).toInt()).toUShort())
VmDataType.BYTE -> registers.setUB(i.reg1!!, (-registers.getUB(i.reg1).toInt()).toUByte())
VmDataType.WORD -> registers.setUW(i.reg1!!, (-registers.getUW(i.reg1).toInt()).toUShort())
}
pc++
}
@ -553,8 +556,8 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
}
private fun arithByte(operator: String, reg1: Int, reg2: Int, reg3: Int?, value: UByte?) {
val left = registers.getB(reg2)
val right = value ?: registers.getB(reg3!!)
val left = registers.getUB(reg2)
val right = value ?: registers.getUB(reg3!!)
val result = when(operator) {
"+" -> left + right
"-" -> left - right
@ -569,12 +572,12 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
}
else -> TODO("operator $operator")
}
registers.setB(reg1, result.toUByte())
registers.setUB(reg1, result.toUByte())
}
private fun arithWord(operator: String, reg1: Int, reg2: Int, reg3: Int?, value: UShort?) {
val left = registers.getW(reg2)
val right = value ?: registers.getW(reg3!!)
val left = registers.getUW(reg2)
val right = value ?: registers.getUW(reg3!!)
val result = when(operator) {
"+" -> left + right
"-" -> left - right
@ -589,7 +592,7 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
}
else -> TODO("operator $operator")
}
registers.setW(reg1, result.toUShort())
registers.setUW(reg1, result.toUShort())
}
private fun InsSUB(i: Instruction) {
@ -602,7 +605,7 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
private fun InsEXT(i: Instruction) {
when(i.type!!){
VmDataType.BYTE -> registers.setW(i.reg1!!, registers.getB(i.reg1).toUShort())
VmDataType.BYTE -> registers.setUW(i.reg1!!, registers.getUB(i.reg1).toUShort())
VmDataType.WORD -> TODO("ext.w requires 32 bits registers")
}
pc++
@ -610,7 +613,7 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
private fun InsEXTS(i: Instruction) {
when(i.type!!){
VmDataType.BYTE -> registers.setW(i.reg1!!, (registers.getB(i.reg1).toByte()).toUShort())
VmDataType.BYTE -> registers.setSW(i.reg1!!, registers.getSB(i.reg1).toShort())
VmDataType.WORD -> TODO("ext.w requires 32 bits registers")
}
pc++
@ -619,8 +622,8 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
private fun InsAND(i: Instruction) {
val (left: UInt, right: UInt) = getLogicalOperandsU(i)
when(i.type!!) {
VmDataType.BYTE -> registers.setB(i.reg1!!, (left and right).toUByte())
VmDataType.WORD -> registers.setW(i.reg1!!, (left and right).toUShort())
VmDataType.BYTE -> registers.setUB(i.reg1!!, (left and right).toUByte())
VmDataType.WORD -> registers.setUW(i.reg1!!, (left and right).toUShort())
}
pc++
}
@ -628,8 +631,8 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
private fun InsOR(i: Instruction) {
val (left: UInt, right: UInt) = getLogicalOperandsU(i)
when(i.type!!) {
VmDataType.BYTE -> registers.setB(i.reg1!!, (left or right).toUByte())
VmDataType.WORD -> registers.setW(i.reg1!!, (left or right).toUShort())
VmDataType.BYTE -> registers.setUB(i.reg1!!, (left or right).toUByte())
VmDataType.WORD -> registers.setUW(i.reg1!!, (left or right).toUShort())
}
pc++
}
@ -637,8 +640,17 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
private fun InsXOR(i: Instruction) {
val (left: UInt, right: UInt) = getLogicalOperandsU(i)
when(i.type!!) {
VmDataType.BYTE -> registers.setB(i.reg1!!, (left xor right).toUByte())
VmDataType.WORD -> registers.setW(i.reg1!!, (left xor right).toUShort())
VmDataType.BYTE -> registers.setUB(i.reg1!!, (left xor right).toUByte())
VmDataType.WORD -> registers.setUW(i.reg1!!, (left xor right).toUShort())
}
pc++
}
private fun InsASR(i: Instruction) {
val (left: Int, right: Int) = getLogicalOperandsS(i)
when(i.type!!) {
VmDataType.BYTE -> registers.setSB(i.reg1!!, (left shr right).toByte())
VmDataType.WORD -> registers.setSW(i.reg1!!, (left shr right).toShort())
}
pc++
}
@ -646,8 +658,8 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
private fun InsLSR(i: Instruction) {
val (left: UInt, right: UInt) = getLogicalOperandsU(i)
when(i.type!!) {
VmDataType.BYTE -> registers.setB(i.reg1!!, (left shr right.toInt()).toUByte())
VmDataType.WORD -> registers.setW(i.reg1!!, (left shr right.toInt()).toUShort())
VmDataType.BYTE -> registers.setUB(i.reg1!!, (left shr right.toInt()).toUByte())
VmDataType.WORD -> registers.setUW(i.reg1!!, (left shr right.toInt()).toUShort())
}
pc++
}
@ -655,8 +667,8 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
private fun InsLSL(i: Instruction) {
val (left: UInt, right: UInt) = getLogicalOperandsU(i)
when(i.type!!) {
VmDataType.BYTE -> registers.setB(i.reg1!!, (left shl right.toInt()).toUByte())
VmDataType.WORD -> registers.setW(i.reg1!!, (left shl right.toInt()).toUShort())
VmDataType.BYTE -> registers.setUB(i.reg1!!, (left shl right.toInt()).toUByte())
VmDataType.WORD -> registers.setUW(i.reg1!!, (left shl right.toInt()).toUShort())
}
pc++
}
@ -664,8 +676,8 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
private fun InsROR(i: Instruction) {
val (left: UInt, right: UInt) = getLogicalOperandsU(i)
when(i.type!!) {
VmDataType.BYTE -> registers.setB(i.reg1!!, (left.rotateRight(right.toInt()).toUByte()))
VmDataType.WORD -> registers.setW(i.reg1!!, (left.rotateRight(right.toInt()).toUShort()))
VmDataType.BYTE -> registers.setUB(i.reg1!!, (left.rotateRight(right.toInt()).toUByte()))
VmDataType.WORD -> registers.setUW(i.reg1!!, (left.rotateRight(right.toInt()).toUShort()))
}
pc++
}
@ -673,8 +685,8 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
private fun InsROL(i: Instruction) {
val (left: UInt, right: UInt) = getLogicalOperandsU(i)
when(i.type!!) {
VmDataType.BYTE -> registers.setB(i.reg1!!, (left.rotateLeft(right.toInt()).toUByte()))
VmDataType.WORD -> registers.setW(i.reg1!!, (left.rotateLeft(right.toInt()).toUShort()))
VmDataType.BYTE -> registers.setUB(i.reg1!!, (left.rotateLeft(right.toInt()).toUByte()))
VmDataType.WORD -> registers.setUW(i.reg1!!, (left.rotateLeft(right.toInt()).toUShort()))
}
pc++
}
@ -682,9 +694,21 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
private fun InsSWAP(i: Instruction) {
when(i.type!!) {
VmDataType.BYTE -> {
val value = registers.getW(i.reg2!!)
val value = registers.getUW(i.reg2!!)
val newValue = value.toUByte()*256u + (value.toInt() ushr 8).toUInt()
registers.setW(i.reg1!!, newValue.toUShort())
registers.setUW(i.reg1!!, newValue.toUShort())
}
VmDataType.WORD -> TODO("swap.w requires 32-bits registers")
}
pc++
}
private fun InsCONCAT(i: Instruction) {
when(i.type!!) {
VmDataType.BYTE -> {
val msb = registers.getUB(i.reg2!!)
val lsb = registers.getUB(i.reg3!!)
registers.setUW(i.reg1!!, ((msb.toInt() shl 8) or lsb.toInt()).toUShort())
}
VmDataType.WORD -> TODO("swap.w requires 32-bits registers")
}
@ -696,13 +720,13 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
private fun InsCOPYZ(i: Instruction) = doCopy(i.reg1!!, i.reg2!!, null, true)
private fun doCopy(reg1: Int, reg2: Int, length: Int?, untilzero: Boolean) {
var from = registers.getW(reg1).toInt()
var to = registers.getW(reg2).toInt()
var from = registers.getUW(reg1).toInt()
var to = registers.getUW(reg2).toInt()
if(untilzero) {
while(true) {
val char = memory.getB(from).toInt()
memory.setB(to, char.toUByte())
if(char==0)
val char = memory.getUB(from)
memory.setUB(to, char)
if(char.toInt()==0)
break
from++
to++
@ -710,8 +734,8 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
} else {
var len = length!!
while(len>0) {
val char = memory.getB(from).toInt()
memory.setB(to, char.toUByte())
val char = memory.getUB(from)
memory.setUB(to, char)
from++
to++
len--
@ -722,40 +746,49 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
private fun getBranchOperands(i: Instruction): Pair<Int, Int> {
return when(i.type) {
VmDataType.BYTE -> Pair(registers.getB(i.reg1!!).toInt(), registers.getB(i.reg2!!).toInt())
VmDataType.WORD -> Pair(registers.getW(i.reg1!!).toInt(), registers.getW(i.reg2!!).toInt())
VmDataType.BYTE -> Pair(registers.getSB(i.reg1!!).toInt(), registers.getSB(i.reg2!!).toInt())
VmDataType.WORD -> Pair(registers.getSW(i.reg1!!).toInt(), registers.getSW(i.reg2!!).toInt())
null -> throw IllegalArgumentException("need type for branch instruction")
}
}
private fun getBranchOperandsU(i: Instruction): Pair<UInt, UInt> {
return when(i.type) {
VmDataType.BYTE -> Pair(registers.getB(i.reg1!!).toUInt(), registers.getB(i.reg2!!).toUInt())
VmDataType.WORD -> Pair(registers.getW(i.reg1!!).toUInt(), registers.getW(i.reg2!!).toUInt())
VmDataType.BYTE -> Pair(registers.getUB(i.reg1!!).toUInt(), registers.getUB(i.reg2!!).toUInt())
VmDataType.WORD -> Pair(registers.getUW(i.reg1!!).toUInt(), registers.getUW(i.reg2!!).toUInt())
null -> throw IllegalArgumentException("need type for branch instruction")
}
}
private fun getLogicalOperandsU(i: Instruction): Pair<UInt, UInt> {
return when(i.type) {
VmDataType.BYTE -> Pair(registers.getB(i.reg2!!).toUInt(), registers.getB(i.reg3!!).toUInt())
VmDataType.WORD -> Pair(registers.getW(i.reg2!!).toUInt(), registers.getW(i.reg3!!).toUInt())
VmDataType.BYTE -> Pair(registers.getUB(i.reg2!!).toUInt(), registers.getUB(i.reg3!!).toUInt())
VmDataType.WORD -> Pair(registers.getUW(i.reg2!!).toUInt(), registers.getUW(i.reg3!!).toUInt())
null -> throw IllegalArgumentException("need type for logical instruction")
}
}
private fun getLogicalOperandsS(i: Instruction): Pair<Int, Int> {
return when(i.type) {
VmDataType.BYTE -> Pair(registers.getSB(i.reg2!!).toInt(), registers.getSB(i.reg3!!).toInt())
VmDataType.WORD -> Pair(registers.getSW(i.reg2!!).toInt(), registers.getSW(i.reg3!!).toInt())
null -> throw IllegalArgumentException("need type for logical instruction")
}
}
private fun getSetOnConditionOperands(ins: Instruction): Triple<Int, Int, Int> {
return when(ins.type) {
VmDataType.BYTE -> Triple(ins.reg1!!, registers.getB(ins.reg2!!).toInt(), registers.getB(ins.reg3!!).toInt())
VmDataType.WORD -> Triple(ins.reg1!!, registers.getW(ins.reg2!!).toInt(), registers.getW(ins.reg3!!).toInt())
VmDataType.BYTE -> Triple(ins.reg1!!, registers.getSB(ins.reg2!!).toInt(), registers.getSB(ins.reg3!!).toInt())
VmDataType.WORD -> Triple(ins.reg1!!, registers.getSW(ins.reg2!!).toInt(), registers.getSW(ins.reg3!!).toInt())
null -> throw IllegalArgumentException("need type for branch instruction")
}
}
private fun getSetOnConditionOperandsU(ins: Instruction): Triple<Int, UInt, UInt> {
return when(ins.type) {
VmDataType.BYTE -> Triple(ins.reg1!!, registers.getB(ins.reg2!!).toUInt(), registers.getB(ins.reg3!!).toUInt())
VmDataType.WORD -> Triple(ins.reg1!!, registers.getW(ins.reg2!!).toUInt(), registers.getW(ins.reg3!!).toUInt())
VmDataType.BYTE -> Triple(ins.reg1!!, registers.getUB(ins.reg2!!).toUInt(), registers.getUB(ins.reg3!!).toUInt())
VmDataType.WORD -> Triple(ins.reg1!!, registers.getUW(ins.reg2!!).toUInt(), registers.getUW(ins.reg3!!).toUInt())
null -> throw IllegalArgumentException("need type for branch instruction")
}
}
@ -763,7 +796,7 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
private var window: GraphicsWindow? = null
fun gfx_enable() {
window = when(registers.getB(0).toInt()) {
window = when(registers.getUB(0).toInt()) {
0 -> GraphicsWindow(320, 240, 3)
1 -> GraphicsWindow(640, 480, 2)
else -> throw IllegalArgumentException("invalid screen mode")
@ -772,14 +805,18 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
}
fun gfx_clear() {
window?.clear(registers.getB(0).toInt())
window?.clear(registers.getUB(0).toInt())
}
fun gfx_plot() {
window?.plot(registers.getW(0).toInt(), registers.getW(1).toInt(), registers.getB(2).toInt())
window?.plot(registers.getUW(0).toInt(), registers.getUW(1).toInt(), registers.getUB(2).toInt())
}
fun gfx_close() {
window?.close()
}
fun waitvsync() {
Toolkit.getDefaultToolkit().sync() // not really the same as wait on vsync, but there's noting else
}
}