This commit is contained in:
Irmen de Jong 2023-04-11 22:28:19 +02:00
parent 7c1d5cadd7
commit b55be093be
14 changed files with 63 additions and 62 deletions

View File

@ -383,10 +383,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
var resultFpReg = -1 var resultFpReg = -1
if(fcall.type==DataType.FLOAT) { if(fcall.type==DataType.FLOAT) {
resultFpReg = codeGen.registers.nextFreeFloat() resultFpReg = codeGen.registers.nextFreeFloat()
addInstr(result, IRInstruction(Opcode.CALLRVAL, IRDataType.FLOAT, fpReg1=resultFpReg, labelSymbol=fcall.name), null) addInstr(result, IRInstruction(Opcode.CALLR, IRDataType.FLOAT, fpReg1=resultFpReg, labelSymbol=fcall.name), null)
} else { } else {
resultReg = codeGen.registers.nextFree() resultReg = codeGen.registers.nextFree()
addInstr(result, IRInstruction(Opcode.CALLRVAL, codeGen.irType(fcall.type), reg1=resultReg, labelSymbol=fcall.name), null) addInstr(result, IRInstruction(Opcode.CALLR, codeGen.irType(fcall.type), reg1=resultReg, labelSymbol=fcall.name), null)
} }
ExpressionCodeResult(result, codeGen.irType(fcall.type), resultReg, resultFpReg) ExpressionCodeResult(result, codeGen.irType(fcall.type), resultReg, resultFpReg)
} }

View File

@ -1458,12 +1458,12 @@ class IRCodeGen(
if(value.type==DataType.FLOAT) { if(value.type==DataType.FLOAT) {
val tr = expressionEval.translateExpression(value) val tr = expressionEval.translateExpression(value)
addToResult(result, tr, -1, tr.resultFpReg) addToResult(result, tr, -1, tr.resultFpReg)
addInstr(result, IRInstruction(Opcode.RETURNREG, IRDataType.FLOAT, fpReg1 = tr.resultFpReg), null) addInstr(result, IRInstruction(Opcode.RETURNR, IRDataType.FLOAT, fpReg1 = tr.resultFpReg), null)
} }
else { else {
val tr = expressionEval.translateExpression(value) val tr = expressionEval.translateExpression(value)
addToResult(result, tr, tr.resultReg, -1) addToResult(result, tr, tr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.RETURNREG, irType(value.type) , reg1=tr.resultReg), null) addInstr(result, IRInstruction(Opcode.RETURNR, irType(value.type) , reg1=tr.resultReg), null)
} }
} }
return result return result

View File

@ -184,7 +184,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
} }
// remove useless RETURN // remove useless RETURN
if(idx>0 && (ins.opcode == Opcode.RETURN || ins.opcode==Opcode.RETURNREG)) { if(idx>0 && (ins.opcode == Opcode.RETURN || ins.opcode==Opcode.RETURNR)) {
val previous = chunk.instructions[idx-1] val previous = chunk.instructions[idx-1]
if(previous.opcode in OpcodesThatJump) { if(previous.opcode in OpcodesThatJump) {
chunk.instructions.removeAt(idx) chunk.instructions.removeAt(idx)
@ -204,7 +204,7 @@ internal class IRPeepholeOptimizer(private val irprog: IRProgram) {
// replace call + return --> jump // replace call + return --> jump
if(idx>0 && ins.opcode==Opcode.RETURN) { if(idx>0 && ins.opcode==Opcode.RETURN) {
val previous = chunk.instructions[idx-1] val previous = chunk.instructions[idx-1]
if(previous.opcode==Opcode.CALL || previous.opcode==Opcode.CALLRVAL) { if(previous.opcode==Opcode.CALL || previous.opcode==Opcode.CALLR) {
chunk.instructions[idx-1] = IRInstruction(Opcode.JUMP, address = previous.address, labelSymbol = previous.labelSymbol, branchTarget = previous.branchTarget) chunk.instructions[idx-1] = IRInstruction(Opcode.JUMP, address = previous.address, labelSymbol = previous.labelSymbol, branchTarget = previous.branchTarget)
chunk.instructions.removeAt(idx) chunk.instructions.removeAt(idx)
changed = true changed = true

View File

@ -200,7 +200,7 @@ sub str2uword(str string) -> uword {
push.w r65535 push.w r65535
syscall 11 syscall 11
pop.w r0 pop.w r0
returnreg.w r0 returnr.w r0
}} }}
} }
@ -213,7 +213,7 @@ sub str2word(str string) -> word {
push.w r65535 push.w r65535
syscall 12 syscall 12
pop.w r0 pop.w r0
returnreg.w r0 returnr.w r0
}} }}
} }

View File

@ -22,7 +22,7 @@ sub pow(float value, float power) -> float {
loadm.f fr0,floats.pow.value loadm.f fr0,floats.pow.value
loadm.f fr1,floats.pow.power loadm.f fr1,floats.pow.power
fpow.f fr0,fr1 fpow.f fr0,fr1
returnreg.f fr0 returnr.f fr0
}} }}
} }
@ -30,7 +30,7 @@ sub fabs(float value) -> float {
%ir {{ %ir {{
loadm.f fr0,floats.fabs.value loadm.f fr0,floats.fabs.value
fabs.f fr0,fr0 fabs.f fr0,fr0
returnreg.f fr0 returnr.f fr0
}} }}
} }
@ -38,7 +38,7 @@ sub sin(float angle) -> float {
%ir {{ %ir {{
loadm.f fr0,floats.sin.angle loadm.f fr0,floats.sin.angle
fsin.f fr0,fr0 fsin.f fr0,fr0
returnreg.f fr0 returnr.f fr0
}} }}
} }
@ -46,7 +46,7 @@ sub cos(float angle) -> float {
%ir {{ %ir {{
loadm.f fr0,floats.cos.angle loadm.f fr0,floats.cos.angle
fcos.f fr0,fr0 fcos.f fr0,fr0
returnreg.f fr0 returnr.f fr0
}} }}
} }
@ -54,7 +54,7 @@ sub tan(float value) -> float {
%ir {{ %ir {{
loadm.f fr0,floats.tan.value loadm.f fr0,floats.tan.value
ftan.f fr0,fr0 ftan.f fr0,fr0
returnreg.f fr0 returnr.f fr0
}} }}
} }
@ -62,7 +62,7 @@ sub atan(float value) -> float {
%ir {{ %ir {{
loadm.f fr0,floats.atan.value loadm.f fr0,floats.atan.value
fatan.f fr0,fr0 fatan.f fr0,fr0
returnreg.f fr0 returnr.f fr0
}} }}
} }
@ -70,7 +70,7 @@ sub ln(float value) -> float {
%ir {{ %ir {{
loadm.f fr0,floats.ln.value loadm.f fr0,floats.ln.value
fln.f fr0,fr0 fln.f fr0,fr0
returnreg.f fr0 returnr.f fr0
}} }}
} }
@ -78,7 +78,7 @@ sub log2(float value) -> float {
%ir {{ %ir {{
loadm.f fr0,floats.log2.value loadm.f fr0,floats.log2.value
flog.f fr0,fr0 flog.f fr0,fr0
returnreg.f fr0 returnr.f fr0
}} }}
} }
@ -86,7 +86,7 @@ sub sqrt(float value) -> float {
%ir {{ %ir {{
loadm.f fr0,floats.sqrt.value loadm.f fr0,floats.sqrt.value
sqrt.f fr0,fr0 sqrt.f fr0,fr0
returnreg.f fr0 returnr.f fr0
}} }}
} }
@ -104,7 +104,7 @@ sub round(float value) -> float {
%ir {{ %ir {{
loadm.f fr0,floats.round.value loadm.f fr0,floats.round.value
fround.f fr0,fr0 fround.f fr0,fr0
returnreg.f fr0 returnr.f fr0
}} }}
} }
@ -112,7 +112,7 @@ sub floor(float value) -> float {
%ir {{ %ir {{
loadm.f fr0,floats.floor.value loadm.f fr0,floats.floor.value
ffloor.f fr0,fr0 ffloor.f fr0,fr0
returnreg.f fr0 returnr.f fr0
}} }}
} }
@ -121,7 +121,7 @@ sub ceil(float value) -> float {
%ir {{ %ir {{
loadm.f fr0,floats.ceil.value loadm.f fr0,floats.ceil.value
fceil.f fr0,fr0 fceil.f fr0,fr0
returnreg.f fr0 returnr.f fr0
}} }}
} }
@ -129,7 +129,7 @@ sub rndf() -> float {
%ir {{ %ir {{
syscall 35 syscall 35
pop.f fr0 pop.f fr0
returnreg.f fr0 returnr.f fr0
}} }}
} }

View File

@ -163,7 +163,7 @@ math {
%ir {{ %ir {{
syscall 33 syscall 33
pop.b r0 pop.b r0
returnreg.b r0 returnr.b r0
}} }}
} }
@ -171,7 +171,7 @@ math {
%ir {{ %ir {{
syscall 34 syscall 34
pop.w r0 pop.w r0
returnreg.w r0 returnr.w r0
}} }}
} }

View File

@ -90,7 +90,7 @@ string {
push.w r65535 push.w r65535
syscall 29 syscall 29
pop.b r0 pop.b r0
returnreg.b r0 returnr.b r0
}} }}
} }

View File

@ -118,7 +118,7 @@ sys {
push.w r65535 push.w r65535
syscall 30 syscall 30
pop.b r0 pop.b r0
returnreg.b r0 returnr.b r0
}} }}
} }
} }

View File

@ -131,7 +131,7 @@ sub input_chars (uword buffer) -> ubyte {
push.b r65535 push.b r65535
syscall 6 syscall 6
pop.b r0 pop.b r0
returnreg.b r0 returnr.b r0
}} }}
} }

View File

@ -3,11 +3,15 @@ TODO
For next minor release For next minor release
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
- try to optimize newexpr a bit more
... ...
For 9.0 major changes For 9.0 major changes
^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
- try to reintroduce builtin functions max/maxw/min/minw that take 2 args and return the largest/smallest of them.
This is a major change because it will likely break existing code that is now using min and max as variable names.
- get rid of the disknumber parameter everywhere in diskio, make it a configurable variable that defaults to 8. - get rid of the disknumber parameter everywhere in diskio, make it a configurable variable that defaults to 8.
the large majority of users will only deal with a single disk drive so why not make it easier for them. the large majority of users will only deal with a single disk drive so why not make it easier for them.
- duplicate diskio for cx16 (get rid of cx16diskio, just copy diskio and tweak everything) + documentation - duplicate diskio for cx16 (get rid of cx16diskio, just copy diskio and tweak everything) + documentation
@ -33,9 +37,9 @@ Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
Compiler: Compiler:
- ir: investigate passing parameters to function calls using fixed set of regs r60000,r60001, etc. - ir: idea? (but LLVM IR simply keeps the variables, so not a good idea then?...): replace all scalar variables by an allocated register. Keep a table of the variable to register mapping (including the datatype)
these have to be offset somehow by the subroutine number (?) somehow to have a unique list of them per subroutine. global initialization values are simply a list of LOAD instructions.
also only if we can optimize the subroutine to replace the parameter variables to those registers. Variables replaced include all subroutine parameters! So the only variables that remain as variables are arrays and strings.
- ir: can we determine for the loop variable in forloops if it could be kept in a (virtual) register instead of a real variable? Need to be able to check if the variable is used by another statement beside just the for loop. - ir: can we determine for the loop variable in forloops if it could be kept in a (virtual) register instead of a real variable? Need to be able to check if the variable is used by another statement beside just the for loop.
- ir: mechanism to determine for chunks which registers are getting input values from "outside" - ir: mechanism to determine for chunks which registers are getting input values from "outside"
- ir: mechanism to determine for chunks which registers are passing values out? (i.e. are used again in another chunk) - ir: mechanism to determine for chunks which registers are passing values out? (i.e. are used again in another chunk)

View File

@ -17,8 +17,11 @@ Status flags: Carry, Zero, Negative. NOTE: status flags are only affected by t
logical or arithmetic operations DO NOT AFFECT THE STATUS FLAGS UNLESS EXPLICITLY NOTED! logical or arithmetic operations DO NOT AFFECT THE STATUS FLAGS UNLESS EXPLICITLY NOTED!
Instruction set is mostly a load/store architecture, there are few instructions operating on memory directly. Instruction set is mostly a load/store architecture, there are few instructions operating on memory directly.
Most instructions have an associated data type 'b','w','f'. (omitting it defaults to 'b' - byte).
Currently NO support for 24 or 32 bits integers. Value types: integers (.b=byte=8 bits, .w=word=16 bits) and float (.f=32 bits). Omitting it defaults to b.
Currently ther is NO support for 24 or 32 bits integers.
There is no distinction between signed and unsigned integers.
Instead, a different instruction is used if a distinction should be made (for example div and divs).
Floating point operations are just 'f' typed regular instructions, however there are a few unique fp conversion instructions. Floating point operations are just 'f' typed regular instructions, however there are a few unique fp conversion instructions.
Instructions taking more than 1 register cannot take the same register multiple times! (to avoid confusing different datatypes) Instructions taking more than 1 register cannot take the same register multiple times! (to avoid confusing different datatypes)
@ -50,11 +53,11 @@ CONTROL FLOW
------------ ------------
jump location - continue running at instruction number given by location jump location - continue running at instruction number given by location
jumpa address - continue running at memory address (note: only used to encode a physical cpu jump to fixed address instruction) jumpa address - continue running at memory address (note: only used to encode a physical cpu jump to fixed address instruction)
call location - save current instruction location+1, continue execution at instruction nr given by location. Expect no return value. call location - save current instruction location+1, continue execution at instruction nr given by location. No return value is expected.
callrval reg1, location - like call but expects a return value from a returnreg instruction, and puts that in reg1 callr reg1, location - like call but expects the routine to return a value with a returnr instruction, it then puts that in reg1
syscall value - do a systemcall identified by call number, result value(s) are pushed on value stack so need to be POPped off (depends on syscall) syscall value - do a systemcall identified by call number, result value(s) are pushed on value stack so need to be POPped off (depends on syscall)
return - restore last saved instruction location and continue at that instruction. No return value. return - restore last saved instruction location and continue at that instruction. No return value.
returnreg reg1 - like return, but also returns a value to the caller via reg1 returnr reg1 - like return, but also returns the value in reg1 to the caller
BRANCHING and CONDITIONALS BRANCHING and CONDITIONALS
@ -112,12 +115,12 @@ dec reg1 - reg1 = reg1-1
decm address - memory at address -= 1 decm address - memory at address -= 1
neg reg1 - reg1 = sign negation of reg1 neg reg1 - reg1 = sign negation of reg1
negm address - sign negate memory at address negm address - sign negate memory at address
addr reg1, reg2 - reg1 += reg2 (unsigned + signed) addr reg1, reg2 - reg1 += reg2
add reg1, value - reg1 += value (unsigned + signed) add reg1, value - reg1 += value
addm reg1, address - memory at address += reg1 (unsigned + signed) addm reg1, address - memory at address += reg1
subr reg1, reg2 - reg1 -= reg2 (unsigned + signed) subr reg1, reg2 - reg1 -= reg2
sub reg1, value - reg1 -= value (unsigned + signed) sub reg1, value - reg1 -= value
subm reg1, address - memory at address -= reg1 (unsigned + signed) subm reg1, address - memory at address -= reg1
mulr reg1, reg2 - unsigned multiply reg1 *= reg2 note: byte*byte->byte, no type extension to word! mulr reg1, reg2 - unsigned multiply reg1 *= reg2 note: byte*byte->byte, no type extension to word!
mul reg1, value - unsigned multiply reg1 *= value note: byte*byte->byte, no type extension to word! mul reg1, value - unsigned multiply reg1 *= value note: byte*byte->byte, no type extension to word!
mulm reg1, address - memory at address *= reg2 note: byte*byte->byte, no type extension to word! mulm reg1, address - memory at address *= reg2 note: byte*byte->byte, no type extension to word!
@ -235,10 +238,10 @@ enum class Opcode {
JUMP, JUMP,
JUMPA, JUMPA,
CALL, CALL,
CALLRVAL, CALLR,
SYSCALL, SYSCALL,
RETURN, RETURN,
RETURNREG, RETURNR,
BSTCC, BSTCC,
BSTCS, BSTCS,
@ -375,16 +378,16 @@ val OpcodesThatJump = setOf(
Opcode.JUMP, Opcode.JUMP,
Opcode.JUMPA, Opcode.JUMPA,
Opcode.RETURN, Opcode.RETURN,
Opcode.RETURNREG Opcode.RETURNR
) )
val OpcodesThatBranch = setOf( val OpcodesThatBranch = setOf(
Opcode.JUMP, Opcode.JUMP,
Opcode.JUMPA, Opcode.JUMPA,
Opcode.RETURN, Opcode.RETURN,
Opcode.RETURNREG, Opcode.RETURNR,
Opcode.CALL, Opcode.CALL,
Opcode.CALLRVAL, Opcode.CALLR,
Opcode.SYSCALL, Opcode.SYSCALL,
Opcode.BSTCC, Opcode.BSTCC,
Opcode.BSTCS, Opcode.BSTCS,
@ -515,10 +518,10 @@ val instructionFormats = mutableMapOf(
Opcode.JUMP to InstructionFormat.from("N,<a"), Opcode.JUMP to InstructionFormat.from("N,<a"),
Opcode.JUMPA to InstructionFormat.from("N,<a"), Opcode.JUMPA to InstructionFormat.from("N,<a"),
Opcode.CALL to InstructionFormat.from("N,<a"), Opcode.CALL to InstructionFormat.from("N,<a"),
Opcode.CALLRVAL to InstructionFormat.from("BW,>r1,<a | F,>fr1,<a"), Opcode.CALLR to InstructionFormat.from("BW,>r1,<a | F,>fr1,<a"),
Opcode.SYSCALL to InstructionFormat.from("N,<i"), Opcode.SYSCALL to InstructionFormat.from("N,<i"),
Opcode.RETURN to InstructionFormat.from("N"), Opcode.RETURN to InstructionFormat.from("N"),
Opcode.RETURNREG to InstructionFormat.from("BW,>r1 | F,>fr1"), Opcode.RETURNR to InstructionFormat.from("BW,>r1 | F,>fr1"),
Opcode.BSTCC to InstructionFormat.from("N,<a"), Opcode.BSTCC to InstructionFormat.from("N,<a"),
Opcode.BSTCS to InstructionFormat.from("N,<a"), Opcode.BSTCS to InstructionFormat.from("N,<a"),
Opcode.BSTEQ to InstructionFormat.from("N,<a"), Opcode.BSTEQ to InstructionFormat.from("N,<a"),

View File

@ -134,7 +134,7 @@ class IRProgram(val name: String,
// link all jump and branching instructions to their target // link all jump and branching instructions to their target
chunk.instructions.forEach { chunk.instructions.forEach {
if(it.opcode in OpcodesThatBranch && it.opcode!=Opcode.RETURN && it.opcode!=Opcode.RETURNREG && it.labelSymbol!=null) if(it.opcode in OpcodesThatBranch && it.opcode!=Opcode.RETURN && it.opcode!=Opcode.RETURNR && it.labelSymbol!=null)
it.branchTarget = labeledChunks.getValue(it.labelSymbol) it.branchTarget = labeledChunks.getValue(it.labelSymbol)
// note: branches with an address value cannot be linked to something... // note: branches with an address value cannot be linked to something...
} }

View File

@ -89,7 +89,7 @@ internal class BitmapScreenPanel(private val drawImage: BufferedImage, val pixel
override fun paint(graphics: Graphics) { override fun paint(graphics: Graphics) {
val g2d = graphics as Graphics2D val g2d = graphics as Graphics2D
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR) g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR)
g2d.drawImage(drawImage, 0, 0, size.width, size.height, null) g2d.drawImage(drawImage, 0, 0, size.width, size.height, null)
Toolkit.getDefaultToolkit().sync() Toolkit.getDefaultToolkit().sync()
} }

View File

@ -17,7 +17,7 @@ Virtual machine specs:
Program to execute is not stored in the system memory, it's just a separate list of instructions. Program to execute is not stored in the system memory, it's just a separate list of instructions.
65536 virtual registers, 16 bits wide, can also be used as 8 bits. r0-r65535 65536 virtual registers, 16 bits wide, can also be used as 8 bits. r0-r65535
65536 virtual floating point registers (32 bits single precision floats) fr0-fr65535 65536 virtual floating point registers (32 bits single precision floats) fr0-fr65535
65536 bytes of memory. Thus memory pointers (addresses) are limited to 16 bits. 65536 bytes of memory, thus memory pointers (addresses) are limited to 16 bits.
Value stack, max 128 entries of 1 byte each. Value stack, max 128 entries of 1 byte each.
Status flags: Carry, Zero, Negative. NOTE: status flags are only affected by the CMP instruction or explicit CLC/SEC!!! Status flags: Carry, Zero, Negative. NOTE: status flags are only affected by the CMP instruction or explicit CLC/SEC!!!
logical or arithmetic operations DO NOT AFFECT THE STATUS FLAGS UNLESS EXPLICITLY NOTED! logical or arithmetic operations DO NOT AFFECT THE STATUS FLAGS UNLESS EXPLICITLY NOTED!
@ -176,11 +176,10 @@ class VirtualMachine(irProgram: IRProgram) {
Opcode.STOREZI -> InsSTOREZI(ins) Opcode.STOREZI -> InsSTOREZI(ins)
Opcode.JUMP -> InsJUMP(ins) Opcode.JUMP -> InsJUMP(ins)
Opcode.JUMPA -> throw IllegalArgumentException("vm program can't jump to system memory address (JUMPA)") Opcode.JUMPA -> throw IllegalArgumentException("vm program can't jump to system memory address (JUMPA)")
Opcode.CALL -> InsCALL(ins) Opcode.CALL, Opcode.CALLR -> InsCALL(ins)
Opcode.CALLRVAL -> InsCALLRVAL(ins)
Opcode.SYSCALL -> InsSYSCALL(ins) Opcode.SYSCALL -> InsSYSCALL(ins)
Opcode.RETURN -> InsRETURN() Opcode.RETURN -> InsRETURN()
Opcode.RETURNREG -> InsRETURNREG(ins) Opcode.RETURNR -> InsRETURNR(ins)
Opcode.BSTCC -> InsBSTCC(ins) Opcode.BSTCC -> InsBSTCC(ins)
Opcode.BSTCS -> InsBSTCS(ins) Opcode.BSTCS -> InsBSTCS(ins)
Opcode.BSTEQ -> InsBSTEQ(ins) Opcode.BSTEQ -> InsBSTEQ(ins)
@ -591,11 +590,6 @@ class VirtualMachine(irProgram: IRProgram) {
} }
private fun InsCALL(i: IRInstruction) { private fun InsCALL(i: IRInstruction) {
callStack.push(CallSiteContext(pcChunk, pcIndex+1, null, null))
branchTo(i)
}
private fun InsCALLRVAL(i: IRInstruction) {
callStack.push(CallSiteContext(pcChunk, pcIndex+1, i.reg1, i.fpReg1)) callStack.push(CallSiteContext(pcChunk, pcIndex+1, i.reg1, i.fpReg1))
branchTo(i) branchTo(i)
} }
@ -611,7 +605,7 @@ class VirtualMachine(irProgram: IRProgram) {
} }
} }
private fun InsRETURNREG(i: IRInstruction) { private fun InsRETURNR(i: IRInstruction) {
if(callStack.isEmpty()) if(callStack.isEmpty())
exit(0) exit(0)
else { else {
@ -2293,11 +2287,11 @@ class VirtualMachine(irProgram: IRProgram) {
} }
fun gfx_getpixel() { fun gfx_getpixel() {
if(window==null)
registers.setUB(0, 0u)
else {
val y = valueStack.popw() val y = valueStack.popw()
val x = valueStack.popw() val x = valueStack.popw()
if(window==null)
valueStack.push(0u)
else {
val color = Color(window!!.getpixel(x.toInt(), y.toInt())) val color = Color(window!!.getpixel(x.toInt(), y.toInt()))
valueStack.push(color.green.toUByte()) // gets called from a syscall, return value via stack. valueStack.push(color.green.toUByte()) // gets called from a syscall, return value via stack.
} }