vm: added cmp() and most of the status-branch instructions

This commit is contained in:
Irmen de Jong 2022-04-18 19:59:48 +02:00
parent 6f2fdbe447
commit ee36d47c27
9 changed files with 183 additions and 41 deletions

View File

@ -102,16 +102,16 @@ enum class Statusflag {
enum class BranchCondition {
CS,
CC,
EQ,
EQ, // EQ == Z
Z,
NE,
NE, // NE == NZ
NZ,
MI, // MI == NEG
NEG,
PL, // PL == POS
POS,
VS,
VC,
MI,
NEG,
PL,
POS
}

View File

@ -16,7 +16,7 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
"any" -> funcAny(call, resultRegister)
"all" -> funcAll(call, resultRegister)
"abs" -> TODO("abs once we can compare plus minus")
"cmp" -> TODO("cmp() can't be used on vm because no processor status bits implemented")
"cmp" -> funcCmp(call)
"sgn" -> funcSgn(call, resultRegister)
"sin" -> TODO("floats not yet implemented")
"cos" -> TODO("floats not yet implemented")
@ -68,6 +68,16 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
}
}
private fun funcCmp(call: PtBuiltinFunctionCall): VmCodeChunk {
val code = VmCodeChunk()
val leftRegister = codeGen.vmRegisters.nextFree()
val rightRegister = codeGen.vmRegisters.nextFree()
code += exprGen.translateExpression(call.args[0], leftRegister)
code += exprGen.translateExpression(call.args[1], rightRegister)
code += VmCodeInstruction(Opcode.CMP, codeGen.vmType(call.args[0].type), reg1=leftRegister, reg2=rightRegister)
return code
}
private fun funcAny(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
val arrayName = call.args[0] as PtIdentifier
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable

View File

@ -110,16 +110,16 @@ class CodeGen(internal val program: PtProgram,
private fun translate(branch: PtConditionalBranch): VmCodeChunk {
val code = VmCodeChunk()
val elseLabel = createLabelName()
when(branch.condition) {
BranchCondition.CS -> {
code += VmCodeInstruction(Opcode.BSTCC, symbol = elseLabel)
}
BranchCondition.CC -> {
code += VmCodeInstruction(Opcode.BSTCS, symbol = elseLabel)
}
else -> {
throw AssemblyError("conditional branch ${branch.condition} not supported in vm target due to lack of cpu flags ${branch.position}")
}
// note that the branch opcode used is the opposite as the branch condition, because the generated code jumps to the 'else' part
code += when(branch.condition) {
BranchCondition.CS -> VmCodeInstruction(Opcode.BSTCC, symbol = elseLabel)
BranchCondition.CC -> VmCodeInstruction(Opcode.BSTCS, symbol = elseLabel)
BranchCondition.EQ, BranchCondition.Z -> VmCodeInstruction(Opcode.BSTNE, symbol = elseLabel)
BranchCondition.NE, BranchCondition.NZ -> VmCodeInstruction(Opcode.BSTEQ, symbol = elseLabel)
BranchCondition.MI, BranchCondition.NEG -> VmCodeInstruction(Opcode.BSTPOS, symbol = elseLabel)
BranchCondition.PL, BranchCondition.POS -> VmCodeInstruction(Opcode.BSTNEG, symbol = elseLabel)
BranchCondition.VC,
BranchCondition.VS -> throw AssemblyError("conditional branch ${branch.condition} not supported in vm target due to lack of cpu V flag ${branch.position}")
}
code += translateNode(branch.trueScope)
if(branch.falseScope.children.isNotEmpty()) {

View File

@ -15,11 +15,37 @@ sys {
; 3 = print_s ; print 0-terminated string from memory
; 4 = print_u8 ; print unsigned int byte
; 5 = print_u16 ; print unsigned int word
; 6 = input ; reads a line of text entered by the user, r0.w = memory buffer, r1.b = maxlength (0-255, 0=unlimited). Zero-terminates the string. Returns length in r65535.w
; 6 = input ; reads a line of text entered by the user, r0.w = memory buffer, r1.b = maxlength (0-255, 0=unlimited). Zero-terminates the string. Returns length in r0.w
; 7 = sleep ; sleep amount of milliseconds
; 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
; 12 = rndw ; random WORD
; 13 = wait ; wait certain amount of jiffies (1/60 sec)
; 14 = waitvsync ; wait on vsync
; 15 = sort_ubyte array
; 16 = sort_byte array
; 17 = sort_uword array
; 18 = sort_word array
; 19 = max_ubyte array
; 20 = max_byte array
; 21 = max_uword array
; 22 = max_word array
; 23 = min_ubyte array
; 24 = min_byte array
; 25 = min_uword array
; 26 = min_word array
; 27 = sum_byte array
; 28 = sum_word array
; 29 = any_byte array
; 30 = any_word array
; 31 = all_byte array
; 32 = all_word array
; 33 = reverse_bytes array
; 34 = reverse_words array
; 35 = set_carry status flag
; 36 = clear_carry status flag
const ubyte SC_RESET = 0
const ubyte SC_EXIT = 1
@ -33,8 +59,11 @@ sys {
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
const ubyte SC_RNDW = 12
const ubyte SC_WAIT = 13
const ubyte SC_WAITVSYNC = 14
const ubyte SC_SET_CARRY = 35
const ubyte SC_CLEAR_CARRY = 36
sub reset_system() {
@ -78,6 +107,14 @@ sys {
; -- immediately exit the program with a return code in the A register
void syscall1(SC_EXIT, returnvalue)
}
sub clear_carry() {
void syscall(SC_CLEAR_CARRY)
}
sub set_carry() {
void syscall(SC_SET_CARRY)
}
}
cx16 {

View File

@ -3,6 +3,7 @@ TODO
For next release
^^^^^^^^^^^^^^^^
- vm: add abs().
- pipe operator: allow non-unary function calls in the pipe that specify the other argument(s) in the calls.
- createAssemblyAndAssemble(): make it possible to actually get rid of the VarDecl nodes by fixing the rest of the code mentioned there.
- allow "xxx" * constexpr (where constexpr is not a number literal), now gives expression error not same type
@ -11,7 +12,6 @@ For next release
If we can do that why not perhaps also able to inline multi-line subroutines? Why would it be limited to just 1 line? Maybe to protect against code size bloat.
Inlined subroutines cannot contain further nested subroutines!
Once this works, look for library subroutines that should be inlined.
- vm: add support for status bits, status-branch instructions, and cmp() and abs() functions.
- floats: remove all floating point builtin functions and move them to the floats module instead,
note: first try to move only sin and cos and see if the various examples still work!

View File

@ -7,6 +7,16 @@
main {
sub start() {
byte v1 = -10
byte v2 = 20
cmp(v1,v2)
;sys.clear_carry()
if_cc
txt.print("cc\n")
if_cs
txt.print("cs\n")
; uword ww = 100
; uword vv
; vv = ww+1
@ -16,24 +26,24 @@ main {
; txt.print_uw(vv)
; txt.nl()
; a "pixelshader":
void syscall1(8, 0) ; enable lo res creen
ubyte shifter
; pokemon(1,0)
repeat {
uword xx
uword yy = 0
repeat 240 {
xx = 0
repeat 320 {
syscall3(10, xx, yy, xx*yy + shifter) ; plot pixel
xx++
}
yy++
}
shifter+=4
}
; ; a "pixelshader":
; void syscall1(8, 0) ; enable lo res creen
; ubyte shifter
;
; ; pokemon(1,0)
;
; repeat {
; uword xx
; uword yy = 0
; repeat 240 {
; xx = 0
; repeat 320 {
; syscall3(10, xx, yy, xx*yy + shifter) ; plot pixel
; xx++
; }
; yy++
; }
; shifter+=4
; }
}
}

View File

@ -64,6 +64,10 @@ All have type b or w except the branches that only check status bits.
bstcc location - branch to location if Status bit Carry is Clear
bstcs location - branch to location if Status bit Carry is Set
bsteq location - branch to location if Status bit Zero is set
bstne location - branch to location if Status bit Zero is not set
bstneg location - branch to location if Status bit Negative is not set
bstpos location - branch to location if Status bit Negative is not set
bz reg1, location - branch to location if reg1 is zero
bnz reg1, location - branch to location if reg1 is not zero
beq reg1, reg2, location - jump to location in program given by location, if reg1 == reg2
@ -109,6 +113,7 @@ div reg1, reg2, reg3 - unsigned division reg1=reg2/reg3
mod reg1, reg2, reg3 - remainder (modulo) of unsigned division reg1=reg2%reg3 note: division by zero yields max signed int $ff/$ffff
sqrt reg1, reg2 - reg1 is the square root of reg2 (for .w and .b both , the result is a byte)
sgn reg1, reg2 - reg1 is the sign of reg2 (0, 1 or -1)
cmp reg1, reg2 - set processor status bits C, N, Z according to comparison of reg1 with reg2. (semantics taken from 6502/68000 CMP instruction)
NOTE: because mul/div are constrained (truncated) to remain in 8 or 16 bits, there is NO NEED for separate signed/unsigned mul and div instructions. The result is identical.
@ -172,6 +177,10 @@ enum class Opcode {
BSTCC,
BSTCS,
BSTEQ,
BSTNE,
BSTNEG,
BSTPOS,
BZ,
BNZ,
BEQ,
@ -207,6 +216,7 @@ enum class Opcode {
MOD,
SQRT,
SGN,
CMP,
EXT,
EXTS,
@ -341,6 +351,10 @@ val instructionFormats = mutableMapOf(
Opcode.BSTCC to InstructionFormat(NN, false, false,false, true ),
Opcode.BSTCS to InstructionFormat(NN, false, false,false, true ),
Opcode.BSTEQ to InstructionFormat(NN, false, false,false, true ),
Opcode.BSTNE to InstructionFormat(NN, false, false,false, true ),
Opcode.BSTNEG to InstructionFormat(NN, false, false,false, true ),
Opcode.BSTPOS to InstructionFormat(NN, false, false,false, true ),
Opcode.BZ to InstructionFormat(BW, true, false, false, true ),
Opcode.BNZ to InstructionFormat(BW, true, false, false, true ),
Opcode.BEQ to InstructionFormat(BW, true, true, false, true ),
@ -376,6 +390,7 @@ val instructionFormats = mutableMapOf(
Opcode.MOD to InstructionFormat(BW, true, true, true, false),
Opcode.SQRT to InstructionFormat(BW, true, true, false, false),
Opcode.SGN to InstructionFormat(BW, true, true, false, false),
Opcode.CMP to InstructionFormat(BW, true, true, false, false),
Opcode.EXT to InstructionFormat(BW, true, false, false, false),
Opcode.EXTS to InstructionFormat(BW, true, false, false, false),

View File

@ -41,6 +41,8 @@ SYSCALLS:
32 = all_word array
33 = reverse_bytes array
34 = reverse_words array
35 = set_carry status flag
36 = clear_carry status flag
*/
enum class Syscall {
@ -78,7 +80,9 @@ enum class Syscall {
ALL_BYTE,
ALL_WORD,
REVERSE_BYTES,
REVERSE_WORDS
REVERSE_WORDS,
SET_CARRY,
CLEAR_CARRY
}
object SysCalls {
@ -302,6 +306,8 @@ object SysCalls {
else
vm.registers.setUB(0, 0u)
}
Syscall.SET_CARRY -> vm.statusCarry = true
Syscall.CLEAR_CARRY -> vm.statusCarry = false
else -> TODO("syscall ${call.name}")
}
}

View File

@ -23,6 +23,8 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
var pc = 0
var stepCount = 0
var statusCarry = false
var statusZero = false
var statusNegative = false
init {
if(program.size>65536)
@ -104,6 +106,10 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
Opcode.RETURN -> InsRETURN()
Opcode.BSTCC -> InsBSTCC(ins)
Opcode.BSTCS -> InsBSTCS(ins)
Opcode.BSTEQ -> InsBSTEQ(ins)
Opcode.BSTNE -> InsBSTNE(ins)
Opcode.BSTNEG -> InsBSTNEG(ins)
Opcode.BSTPOS -> InsBSTPOS(ins)
Opcode.BZ -> InsBZ(ins)
Opcode.BNZ -> InsBNZ(ins)
Opcode.BEQ -> InsBEQ(ins)
@ -139,6 +145,7 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
Opcode.MOD -> InsMOD(ins)
Opcode.SQRT -> InsSQRT(ins)
Opcode.SGN -> InsSGN(ins)
Opcode.CMP -> InsCMP(ins)
Opcode.EXT -> InsEXT(ins)
Opcode.EXTS -> InsEXTS(ins)
Opcode.AND -> InsAND(ins)
@ -353,6 +360,34 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
pc++
}
private fun InsBSTEQ(i: Instruction) {
if(statusZero)
pc = i.value!!
else
pc++
}
private fun InsBSTNE(i: Instruction) {
if(!statusZero)
pc = i.value!!
else
pc++
}
private fun InsBSTNEG(i: Instruction) {
if(statusNegative)
pc = i.value!!
else
pc++
}
private fun InsBSTPOS(i: Instruction) {
if(!statusNegative)
pc = i.value!!
else
pc++
}
private fun InsBZ(i: Instruction) {
when(i.type!!) {
VmDataType.BYTE -> {
@ -630,6 +665,35 @@ class VirtualMachine(val memory: Memory, program: List<Instruction>) {
pc++
}
private fun InsCMP(i: Instruction) {
val comparison: Int
when(i.type!!) {
VmDataType.BYTE -> {
val reg1 = registers.getUB(i.reg1!!)
val reg2 = registers.getUB(i.reg2!!)
comparison = reg1.toInt() - reg2.toInt()
statusNegative = (comparison and 0x80)==0x80
}
VmDataType.WORD -> {
val reg1 = registers.getUW(i.reg1!!)
val reg2 = registers.getUW(i.reg2!!)
comparison = reg1.toInt() - reg2.toInt()
statusNegative = (comparison and 0x8000)==0x8000
}
}
if(comparison==0){
statusZero = true
statusCarry = true
} else if(comparison>0) {
statusZero = false
statusCarry = true
} else {
statusZero = false
statusCarry = false
}
pc++
}
private fun arithByte(operator: String, reg1: Int, reg2: Int, reg3: Int?, value: UByte?) {
val left = registers.getUB(reg2)
val right = value ?: registers.getUB(reg3!!)