mirror of
https://github.com/irmen/prog8.git
synced 2025-01-11 13:29:45 +00:00
vm: added cmp() and most of the status-branch instructions
This commit is contained in:
parent
6f2fdbe447
commit
ee36d47c27
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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()) {
|
||||
|
@ -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 {
|
||||
|
@ -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!
|
||||
|
||||
|
@ -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
|
||||
; }
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
|
||||
|
@ -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}")
|
||||
}
|
||||
}
|
||||
|
@ -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!!)
|
||||
|
Loading…
x
Reference in New Issue
Block a user