fixed register reuse and types on syscall interface

This commit is contained in:
Irmen de Jong 2024-12-28 00:39:28 +01:00
parent ee521793f8
commit 76b29aa629
15 changed files with 239 additions and 204 deletions

View File

@ -285,9 +285,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
addToResult(result, elementTr, elementTr.resultReg, -1)
val iterableTr = translateExpression(haystackVar)
addToResult(result, iterableTr, iterableTr.resultReg, -1)
result += codeGen.makeSyscall(IMSyscall.STRING_CONTAINS, listOf(IRDataType.BYTE to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg), IRDataType.BYTE to elementTr.resultReg)
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=elementTr.resultReg, immediate = 0), null)
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
val resultReg = codeGen.registers.nextFree()
result += codeGen.makeSyscall(IMSyscall.STRING_CONTAINS, listOf(IRDataType.BYTE to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg), IRDataType.BYTE to resultReg)
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=resultReg, immediate = 0), null)
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
}
haystackVar.type.isByteArray -> {
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
@ -298,9 +299,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val lengthReg = codeGen.registers.nextFree()
val iterableLength = codeGen.symbolTable.getLength(haystackVar.name)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterableLength!!), null)
result += codeGen.makeSyscall(IMSyscall.BYTEARRAY_CONTAINS, listOf(IRDataType.BYTE to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to elementTr.resultReg)
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=elementTr.resultReg, immediate = 0), null)
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
val resultReg = codeGen.registers.nextFree()
result += codeGen.makeSyscall(IMSyscall.BYTEARRAY_CONTAINS, listOf(IRDataType.BYTE to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to resultReg)
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=resultReg, immediate = 0), null)
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
}
haystackVar.type.isWordArray -> {
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
@ -311,9 +313,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val lengthReg = codeGen.registers.nextFree()
val iterableLength = codeGen.symbolTable.getLength(haystackVar.name)
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterableLength!!), null)
result += codeGen.makeSyscall(IMSyscall.WORDARRAY_CONTAINS, listOf(IRDataType.WORD to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to elementTr.resultReg)
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=elementTr.resultReg, immediate = 0), null)
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
val resultReg = codeGen.registers.nextFree()
result += codeGen.makeSyscall(IMSyscall.WORDARRAY_CONTAINS, listOf(IRDataType.WORD to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to resultReg)
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=resultReg, immediate = 0), null)
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
}
haystackVar.type.isFloatArray -> {
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
@ -1071,6 +1074,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val leftTr = translateExpression(binExpr.left)
addToResult(result, leftTr, leftTr.resultReg, -1)
val rightTr = translateExpression(binExpr.right)
require(rightTr.dt== IRDataType.BYTE) { "can only shift by 0-255" }
addToResult(result, rightTr, rightTr.resultReg, -1)
val opc = if (signed) Opcode.ASRN else Opcode.LSRN
addInstr(result, IRInstruction(opc, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg), null)
@ -1089,6 +1093,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val leftTr = translateExpression(binExpr.left)
addToResult(result, leftTr, leftTr.resultReg, -1)
val rightTr = translateExpression(binExpr.right)
require(rightTr.dt== IRDataType.BYTE) { "can only shift by 0-255" }
addToResult(result, rightTr, rightTr.resultReg, -1)
addInstr(result, IRInstruction(Opcode.LSLN, vmDt, reg1=leftTr.resultReg, rightTr.resultReg), null)
ExpressionCodeResult(result, vmDt, leftTr.resultReg, -1)

View File

@ -808,7 +808,7 @@ class IRCodeGen(
else if(pow2>=1) {
// just shift multiple bits
val pow2reg = registers.nextFree()
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, immediate = pow2)
code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=pow2reg, immediate = pow2)
code += IRInstruction(Opcode.LSLN, dt, reg1=reg, reg2=pow2reg)
} else {
code += if (factor == 0) {
@ -906,7 +906,7 @@ class IRCodeGen(
} else {
// just shift multiple bits (signed)
val pow2reg = registers.nextFree()
code += IRInstruction(Opcode.LOAD, dt, reg1=pow2reg, immediate = pow2)
code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=pow2reg, immediate = pow2)
code += IRInstruction(Opcode.ASRN, dt, reg1=reg, reg2=pow2reg)
}
} else {
@ -916,7 +916,7 @@ class IRCodeGen(
} else {
// just shift multiple bits (unsigned)
val pow2reg = registers.nextFree()
code += IRInstruction(Opcode.LOAD, dt, reg1 = pow2reg, immediate = pow2)
code += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = pow2reg, immediate = pow2)
code += IRInstruction(Opcode.LSRN, dt, reg1 = reg, reg2 = pow2reg)
}
}

View File

@ -1,20 +1,29 @@
package prog8.codegen.intermediate
import prog8.code.core.AssemblyError
internal class RegisterPool {
// reserve 0,1,2 for return values of subroutine calls and syscalls in IR assembly code
// reserve first 3 registers a subroutine return registers TODO is this still needed? how does returning values go in IR? Double types?
private var firstFree: Int=3
private var firstFreeFloat: Int=3
fun peekNext() = firstFree
fun peekNextFloat() = firstFreeFloat
// everything from 99000 onwards is reserved for special purposes:
// 99000 - 99099 : WORD registers for syscall arguments and response value(s)
// 99100 - 99199 : BYTE registers for syscall arguments and response value(s)
fun nextFree(): Int {
if(firstFree>=99000)
throw AssemblyError("register pool depleted")
val result = firstFree
firstFree++
return result
}
fun nextFreeFloat(): Int {
if(firstFreeFloat>=99000)
throw AssemblyError("float register pool depleted")
val result = firstFreeFloat
firstFreeFloat++
return result

View File

@ -212,9 +212,9 @@ sub str2uword(str string) -> uword {
; the number may NOT be preceded by a + sign and may NOT contain spaces
; (any non-digit character will terminate the number string that is parsed)
%ir {{
loadm.w r65535,conv.str2uword.string
syscall 11 (r65535.w) : r0.w
returnr.w r0
loadm.w r99000,conv.str2uword.string
syscall 11 (r99000.w) : r99000.w
returnr.w r99000
}}
}
@ -223,9 +223,9 @@ sub str2word(str string) -> word {
; the number may be preceded by a + or - sign but may NOT contain spaces
; (any non-digit character will terminate the number string that is parsed)
%ir {{
loadm.w r65535,conv.str2word.string
syscall 12 (r65535.w) : r0.w
returnr.w r0
loadm.w r99000,conv.str2word.string
syscall 12 (r99000.w) : r99000.w
returnr.w r99000
}}
}

View File

@ -13,10 +13,8 @@ diskio {
sub directory() -> bool {
; -- Prints the directory contents to the screen. Returns success.
%ir {{
loadm.w r65534,diskio.load.filenameptr
loadm.w r65535,diskio.load.address_override
syscall 45 (): r0.b
returnr.b r0
syscall 45 (): r99100.b
returnr.b r99100
}}
}
@ -64,9 +62,9 @@ diskio {
; if you're going to read from it yourself instead of using f_read()!
%ir {{
loadm.w r65535,diskio.f_open.filenameptr
syscall 52 (r65535.w): r0.b
returnr.b r0
loadm.w r99000,diskio.f_open.filenameptr
syscall 52 (r99000.w): r99100.b
returnr.b r99100
}}
}
@ -76,8 +74,8 @@ diskio {
uword actual
repeat num_bytes {
%ir {{
syscall 54 (): r0.w
storem.w r0,$ff02
syscall 54 (): r99000.w
storem.w r99000,$ff02
}}
if cx16.r0H==0
return actual
@ -94,8 +92,8 @@ diskio {
uword actual
repeat {
%ir {{
syscall 54 (): r0.w
storem.w r0,$ff02
syscall 54 (): r99000.w
storem.w r99000,$ff02
}}
if cx16.r0H==0
return actual
@ -114,8 +112,8 @@ diskio {
ubyte size
repeat {
%ir {{
syscall 54 (): r0.w
storem.w r0,$ff02
syscall 54 (): r99000.w
storem.w r99000,$ff02
}}
if cx16.r0H==0 {
@ -157,9 +155,9 @@ diskio {
; To be 100% sure if this call was successful, you have to use status()
; and check the drive's status message!
%ir {{
loadm.w r65535,diskio.f_open_w.filenameptr
syscall 53 (r65535.w): r0.b
returnr.b r0
loadm.w r99000,diskio.f_open_w.filenameptr
syscall 53 (r99000.w): r99100.b
returnr.b r99100
}}
}
@ -169,9 +167,9 @@ diskio {
repeat num_bytes {
%ir {{
loadm.w r0,diskio.f_write.bufferpointer
loadi.b r1,r0
syscall 55 (r1.b): r0.b
storem.b r0,$ff02
loadi.b r99100,r0
syscall 55 (r99100.b): r99100.b
storem.b r99100,$ff02
}}
if cx16.r0L==0
return false
@ -194,32 +192,32 @@ diskio {
sub chdir(str path) {
; -- change current directory.
%ir {{
loadm.w r65535,diskio.chdir.path
syscall 50 (r65535.w)
loadm.w r99000,diskio.chdir.path
syscall 50 (r99000.w)
}}
}
sub mkdir(str name) {
; -- make a new subdirectory.
%ir {{
loadm.w r65535,diskio.mkdir.name
syscall 49 (r65535.w)
loadm.w r99000,diskio.mkdir.name
syscall 49 (r99000.w)
}}
}
sub rmdir(str name) {
; -- remove a subdirectory.
%ir {{
loadm.w r65535,diskio.rmdir.name
syscall 51 (r65535.w)
loadm.w r99000,diskio.rmdir.name
syscall 51 (r99000.w)
}}
}
sub curdir() -> uword {
; return current directory name or 0 if error
%ir {{
syscall 48 (): r0.w
returnr.w r0
syscall 48 (): r99000.w
returnr.w r99000
}}
}
@ -236,24 +234,24 @@ diskio {
sub save(uword filenameptr, uword start_address, uword savesize) -> bool {
%ir {{
load.b r65532,0
loadm.w r65533,diskio.save.filenameptr
loadm.w r65534,diskio.save.start_address
loadm.w r65535,diskio.save.savesize
syscall 42 (r65532.b, r65533.w, r65534.w, r65535.w): r0.b
returnr.b r0
load.b r99100,0
loadm.w r99000,diskio.save.filenameptr
loadm.w r99001,diskio.save.start_address
loadm.w r99002,diskio.save.savesize
syscall 42 (r99100.b, r99000.w, r99001.w, r99002.w): r99100.b
returnr.b r99100
}}
}
; like save() but omits the 2 byte prg header.
sub save_raw(uword filenameptr, uword startaddress, uword savesize) -> bool {
%ir {{
load.b r65532,1
loadm.w r65533,diskio.save.filenameptr
loadm.w r65534,diskio.save.start_address
loadm.w r65535,diskio.save.savesize
syscall 42 (r65532.b, r65533.w, r65534.w, r65535.w): r0.b
returnr.b r0
load.b r99100,1
loadm.w r99000,diskio.save.filenameptr
loadm.w r99001,diskio.save.start_address
loadm.w r99002,diskio.save.savesize
syscall 42 (r99100.b, r99000.w, r99001.w, r99002.w): r99100.b
returnr.b r99100
}}
}
@ -265,10 +263,10 @@ diskio {
; Returns the end load address+1 if successful or 0 if a load error occurred.
sub load(uword filenameptr, uword address_override) -> uword {
%ir {{
loadm.w r65534,diskio.load.filenameptr
loadm.w r65535,diskio.load.address_override
syscall 40 (r65534.w, r65535.w): r0.w
returnr.w r0
loadm.w r99000,diskio.load.filenameptr
loadm.w r99001,diskio.load.address_override
syscall 40 (r99000.w, r99001.w): r99002.w
returnr.w r99002
}}
}
@ -277,27 +275,27 @@ diskio {
; See comments on load() for more details.
sub load_raw(uword filenameptr, uword start_address) -> uword {
%ir {{
loadm.w r65534,diskio.load_raw.filenameptr
loadm.w r65535,diskio.load_raw.start_address
syscall 41 (r65534.w, r65535.w): r0.w
returnr.w r0
loadm.w r99000,diskio.load_raw.filenameptr
loadm.w r99001,diskio.load_raw.start_address
syscall 41 (r99000.w, r99001.w): r99002.w
returnr.w r99002
}}
}
sub delete(uword filenameptr) {
; -- delete a file on the drive
%ir {{
loadm.w r65535,diskio.delete.filenameptr
syscall 43 (r65535.w)
loadm.w r99000,diskio.delete.filenameptr
syscall 43 (r99000.w)
}}
}
sub rename(uword oldfileptr, uword newfileptr) {
; -- rename a file on the drive
%ir {{
loadm.w r65534,diskio.rename.oldfileptr
loadm.w r65535,diskio.rename.newfileptr
syscall 44 (r65534.w, r65535.w)
loadm.w r99000,diskio.rename.oldfileptr
loadm.w r99001,diskio.rename.newfileptr
syscall 44 (r99000.w, r99001.w)
}}
}

View File

@ -35,8 +35,8 @@ floats {
sub print(float value) {
; ---- prints the floating point value (without a newline and no leading spaces).
%ir {{
loadm.f fr65535,floats.print.value
syscall 15 (fr65535.f)
loadm.f fr99000,floats.print.value
syscall 15 (fr99000.f)
return
}}
}
@ -45,9 +45,9 @@ sub tostr(float value) -> str {
; ---- converts the floating point value to a string (no leading spaces)
str @shared buffer=" "*20
%ir {{
load.w r65535,floats.tostr.buffer
loadm.f fr65535,floats.tostr.value
syscall 34 (r65535.w, fr65535.f)
load.w r99000,floats.tostr.buffer
loadm.f fr99000,floats.tostr.value
syscall 34 (r99000.w, fr99000.f)
load.w r0,floats.tostr.buffer
returnr.w r0
}}
@ -56,9 +56,9 @@ sub tostr(float value) -> str {
sub parse(str value) -> float {
; -- parse a string value of a number to float
%ir {{
loadm.w r65535,floats.parse.value
syscall 32 (r65535.w): fr0.f
returnr.f fr0
loadm.w r99000,floats.parse.value
syscall 32 (r99000.w): fr99000.f
returnr.f fr99000
}}
}
@ -170,15 +170,15 @@ sub ceil(float value) -> float {
sub rnd() -> float {
%ir {{
syscall 22 () : fr0.f
returnr.f fr0
syscall 22 () : fr99000.f
returnr.f fr99000
}}
}
sub rndseed(float seed) {
%ir {{
loadm.f fr65535,floats.rndseed.seed
syscall 19 (fr65535.f)
loadm.f fr99000,floats.rndseed.seed
syscall 19 (fr99000.f)
return
}}
}
@ -213,16 +213,16 @@ sub normalize(float value) -> float {
sub push(float value) {
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
%ir {{
loadm.f fr65535,floats.push.value
push.f fr65535
loadm.f fr99000,floats.push.value
push.f fr99000
}}
}
sub pop() -> float {
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
%ir {{
pop.f fr65535
returnr.f fr65535
pop.f fr99000
returnr.f fr99000
}}
}

View File

@ -164,15 +164,15 @@ math {
sub rnd() -> ubyte {
%ir {{
syscall 20 (): r0.b
returnr.b r0
syscall 20 (): r99100.b
returnr.b r99100
}}
}
sub rndw() -> uword {
%ir {{
syscall 21 (): r0.w
returnr.w r0
syscall 21 (): r99000.w
returnr.w r99000
}}
}
@ -197,9 +197,9 @@ math {
sub rndseed(uword seed1, uword seed2) {
; -- reset the pseudo RNG's seed values. Defaults are: $a55a, $7653.
%ir {{
loadm.w r65534,math.rndseed.seed1
loadm.w r65535,math.rndseed.seed2
syscall 18 (r65534.w, r65535.w)
loadm.w r99000,math.rndseed.seed1
loadm.w r99001,math.rndseed.seed2
syscall 18 (r99000.w, r99001.w)
return
}}
}
@ -276,12 +276,12 @@ math {
;; Calculate the angle, in a 256-degree circle, between two points into A.
;; The points (x1, y1) and (x2, y2) have to use *unsigned coordinates only* from the positive quadrant in the carthesian plane!
%ir {{
loadm.b r65532,math.atan2.x1
loadm.b r65533,math.atan2.y1
loadm.b r65534,math.atan2.x2
loadm.b r65535,math.atan2.y2
syscall 31 (r65532.b, r65533.b, r65534.b, r65535.b): r0.b
returnr.b r0
loadm.b r99100,math.atan2.x1
loadm.b r99101,math.atan2.y1
loadm.b r99102,math.atan2.x2
loadm.b r99103,math.atan2.y2
syscall 31 (r99100.b, r99101.b, r99102.b, r99103.b): r99100.b
returnr.b r99100
}}
}
@ -296,8 +296,8 @@ math {
; - THE RESULT IS ONLY VALID IF THE MULTIPLICATION WAS DONE WITH UWORD ARGUMENTS (or two positive WORD arguments)
; as soon as a negative word value (or 2) was used in the multiplication, these upper 16 bits are not valid!!
%ir {{
syscall 33 (): r0.w
returnr.w r0
syscall 33 (): r99000.w
returnr.w r99000
}}
}

View File

@ -96,10 +96,10 @@ strings {
; Often you dont have to call this explicitly and can just write string1 = string2
; but this function is useful if youre dealing with addresses for instance.
%ir {{
loadm.w r65534,strings.copy.source
loadm.w r65535,strings.copy.target
syscall 39 (r65534.w, r65535.w): r0.b
returnr.b r0
loadm.w r99000,strings.copy.source
loadm.w r99001,strings.copy.target
syscall 39 (r99000.w, r99001.w): r99100.b
returnr.b r99100
}}
}
@ -116,10 +116,10 @@ strings {
; Note that you can also directly compare strings and string values with eachother using
; comparison operators ==, < etcetera (this will use strcmp automatically).
%ir {{
loadm.w r65534,strings.compare.st1
loadm.w r65535,strings.compare.st2
syscall 16 (r65534.w, r65535.w) : r0.b
returnr.b r0
loadm.w r99000,strings.compare.st1
loadm.w r99001,strings.compare.st2
syscall 16 (r99000.w, r99001.w) : r99100.b
returnr.b r99100
}}
}

View File

@ -34,8 +34,8 @@ sys {
sub wait(uword jiffies) {
; --- wait approximately the given number of jiffies (1/60th seconds)
%ir {{
loadm.w r65535,sys.wait.jiffies
syscall 13 (r65535.w)
loadm.w r99000,sys.wait.jiffies
syscall 13 (r99000.w)
}}
}
@ -49,36 +49,36 @@ sys {
sub internal_stringcopy(uword source, uword tgt) {
; Called when the compiler wants to assign a string value to another string.
%ir {{
loadm.w r65534,sys.internal_stringcopy.source
loadm.w r65535,sys.internal_stringcopy.tgt
syscall 39 (r65534.w, r65535.w): r0.b
loadm.w r99000,sys.internal_stringcopy.source
loadm.w r99001,sys.internal_stringcopy.tgt
syscall 39 (r99000.w, r99001.w): r99100.b
}}
}
sub memcopy(uword source, uword tgt, uword count) {
%ir {{
loadm.w r65533,sys.memcopy.source
loadm.w r65534,sys.memcopy.tgt
loadm.w r65535,sys.memcopy.count
syscall 36 (r65533.w, r65534.w, r65535.w)
loadm.w r99000,sys.memcopy.source
loadm.w r99001,sys.memcopy.tgt
loadm.w r99002,sys.memcopy.count
syscall 36 (r99000.w, r99001.w, r99002.w)
}}
}
sub memset(uword mem, uword numbytes, ubyte value) {
%ir {{
loadm.w r65533,sys.memset.mem
loadm.w r65534,sys.memset.numbytes
loadm.b r65535,sys.memset.value
syscall 37 (r65533.w, r65534.w, r65535.b)
loadm.w r99000,sys.memset.mem
loadm.w r99001,sys.memset.numbytes
loadm.b r99100,sys.memset.value
syscall 37 (r99000.w, r99001.w, r99100.b)
}}
}
sub memsetw(uword mem, uword numwords, uword value) {
%ir {{
loadm.w r65533,sys.memsetw.mem
loadm.w r65534,sys.memsetw.numwords
loadm.w r65535,sys.memsetw.value
syscall 38 (r65533.w, r65534.w, r65535.w)
loadm.w r99000,sys.memsetw.mem
loadm.w r99001,sys.memsetw.numwords
loadm.w r99002,sys.memsetw.value
syscall 38 (r99000.w, r99001.w, r99002.w)
}}
}
@ -86,19 +86,19 @@ sys {
; Compares two blocks of memory of up to 65535 bytes in size
; Returns -1 (255), 0 or 1, meaning: block 1 sorts before, equal or after block 2.
%ir {{
loadm.w r65533,sys.memcmp.address1
loadm.w r65534,sys.memcmp.address2
loadm.w r65535,sys.memcmp.size
syscall 47 (r65533.w, r65534.w, r65535.w) : r0.b
returnr.b r0
loadm.w r99000,sys.memcmp.address1
loadm.w r99001,sys.memcmp.address2
loadm.w r99002,sys.memcmp.size
syscall 47 (r99000.w, r99001.w, r99002.w) : r99100.b
returnr.b r99100
}}
}
sub exit(ubyte returnvalue) {
; -- immediately exit the program with a return code in the A register
%ir {{
loadm.b r65535,sys.exit.returnvalue
syscall 1 (r65535.b)
loadm.b r99100,sys.exit.returnvalue
syscall 1 (r99100.b)
}}
}
@ -144,73 +144,73 @@ sys {
sub gfx_enable(ubyte mode) {
%ir {{
loadm.b r65535,sys.gfx_enable.mode
syscall 8 (r65535.b)
loadm.b r99100,sys.gfx_enable.mode
syscall 8 (r99100.b)
}}
}
sub gfx_clear(ubyte color) {
%ir {{
loadm.b r65535,sys.gfx_clear.color
syscall 9 (r65535.b)
loadm.b r99100,sys.gfx_clear.color
syscall 9 (r99100.b)
}}
}
sub gfx_plot(uword xx, uword yy, ubyte color) {
%ir {{
loadm.w r65533,sys.gfx_plot.xx
loadm.w r65534,sys.gfx_plot.yy
loadm.b r65535,sys.gfx_plot.color
syscall 10 (r65533.w, r65534.w, r65535.b)
loadm.w r99000,sys.gfx_plot.xx
loadm.w r99001,sys.gfx_plot.yy
loadm.b r99100,sys.gfx_plot.color
syscall 10 (r99000.w, r99001.w, r99100.b)
}}
}
sub gfx_getpixel(uword xx, uword yy) -> ubyte {
%ir {{
loadm.w r65534,sys.gfx_getpixel.xx
loadm.w r65535,sys.gfx_getpixel.yy
syscall 17 (r65534.w, r65535.w): r0.b
returnr.b r0
loadm.w r99000,sys.gfx_getpixel.xx
loadm.w r99001,sys.gfx_getpixel.yy
syscall 17 (r99000.w, r99001.w): r99100.b
returnr.b r99100
}}
}
sub push(ubyte b) {
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
%ir {{
loadm.b r65535,sys.push.b
push.b r65535
loadm.b r99100,sys.push.b
push.b r99100
}}
}
sub pushw(uword w) {
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
%ir {{
loadm.w r65535,sys.pushw.w
push.w r65535
loadm.w r99000,sys.pushw.w
push.w r99000
}}
}
sub push_returnaddress(uword w) {
; note: this actually doesn't do anything useful on the VM because the code execution doesn't use the simulated cpu stack
%ir {{
loadm.w r65535,sys.pushw.w
push.w r65535
loadm.w r99000,sys.pushw.w
push.w r99000
}}
}
sub pop() -> ubyte {
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
%ir {{
pop.b r65535
returnr.b r65535
pop.b r99100
returnr.b r99100
}}
}
sub popw() -> uword {
; note: this *should* be inlined, however since the VM has separate program counter and value stacks, this also works
%ir {{
pop.w r65535
returnr.w r65535
pop.w r99000
returnr.w r99000
}}
}

View File

@ -7,24 +7,25 @@ txt {
sub width() -> ubyte {
%ir {{
syscall 46 (): r0.w
returnr.b r0
syscall 46 (): r99000.w
lsig.b r99100,r99000
returnr.b r99100
}}
}
sub height() -> ubyte {
%ir {{
syscall 46 (): r0.w
msig.b r1,r0
returnr.b r1
syscall 46 (): r99000.w
msig.b r99100,r99000
returnr.b r99100
}}
}
sub clear_screen() {
str @shared sequence = "\x1b[2J\x1B[H"
%ir {{
load.w r65535,txt.clear_screen.sequence
syscall 3 (r65535.w)
load.w r99000,txt.clear_screen.sequence
syscall 3 (r99000.w)
}}
}
@ -54,8 +55,8 @@ sub uppercase() {
sub chrout(ubyte char) {
%ir {{
loadm.b r65535,txt.chrout.char
syscall 2 (r65535.b)
loadm.b r99100,txt.chrout.char
syscall 2 (r99100.b)
}}
}
@ -65,8 +66,8 @@ sub bell() {
sub print (str text) {
%ir {{
loadm.w r65535,txt.print.text
syscall 3 (r65535.w)
loadm.w r99000,txt.print.text
syscall 3 (r99000.w)
}}
}
@ -139,10 +140,10 @@ sub input_chars (uword buffer) -> ubyte {
; ---- Input a string (max. 80 chars) from the keyboard. Returns length of input. (string is terminated with a 0 byte as well)
; It assumes the keyboard is selected as I/O channel!
%ir {{
loadm.w r65534,txt.input_chars.buffer
load.b r65535,80
syscall 6 (r65534.w, r65535.b): r0.b
returnr.b r0
loadm.w r99000,txt.input_chars.buffer
load.b r99100,80
syscall 6 (r99000.w, r99100.b): r99100.b
returnr.b r99100
}}
}

View File

@ -1,8 +1,6 @@
TODO
====
- addUsedRegistersCounts() doesn't always determine the datatype correctly. --> GET RID OF THE Sxxx OPCODES FOR NOW?
- add paypal donation button as well?
- announce prog8 on the 6502.org site?
@ -56,8 +54,8 @@ Future Things and Ideas
IR/VM
-----
- fix the syscall interface. It should not use r0 as return reg, but something in the 65000 range. Also, separete input regs for byte or word types.
- getting it in shape for code generation...: the IR file should be able to encode every detail about a prog8 program (the VM doesn't have to actually be able to run all of it though!)
- registerPool should have separate pools, one for byte and word registers each (and 1 for floats)?
- add BZ and BNZ instructions? To replace CMPI #0 + Branch?
- fix TODO("IR rol/ror on split words array")
- fix "<< in array" / ">> in array"

View File

@ -1,14 +1,8 @@
%zeropage basicsafe
%option no_sysinit
main {
ubyte @shared width
sub start() {
if width==22 or width==33 {
cx16.r1++
}
str localstr = "hello"
uword[] words = [1111,2222,"three"]
bool r1 = 'z' in localstr
bool result = 2222 in words
}
}

View File

@ -1,5 +1,6 @@
package prog8.intermediate
import prog8.code.core.AssemblyError
import prog8.code.core.RegisterOrStatusflag
import prog8.code.core.toHex
@ -795,11 +796,11 @@ data class IRInstruction(
}
}
if(labelSymbolOffset!=null) require(labelSymbolOffset>0 && labelSymbol!=null) {"labelsymbol offset inconsistency"}
require(reg1==null || reg1 in 0..65536) {"reg1 out of bounds"}
require(reg2==null || reg2 in 0..65536) {"reg2 out of bounds"}
require(reg3==null || reg3 in 0..65536) {"reg3 out of bounds"}
require(fpReg1==null || fpReg1 in 0..65536) {"fpReg1 out of bounds"}
require(fpReg2==null || fpReg2 in 0..65536) {"fpReg2 out of bounds"}
require(reg1==null || reg1 in 0..99999) {"reg1 out of bounds"}
require(reg2==null || reg2 in 0..99999) {"reg2 out of bounds"}
require(reg3==null || reg3 in 0..99999) {"reg3 out of bounds"}
require(fpReg1==null || fpReg1 in 0..99999) {"fpReg1 out of bounds"}
require(fpReg2==null || fpReg2 in 0..99999) {"fpReg2 out of bounds"}
if(reg1!=null && reg2!=null) require(reg1!=reg2) {"reg1 must not be same as reg2"} // note: this is ok for fpRegs as these are always the same type
if(reg1!=null && reg3!=null) require(reg1!=reg3) {"reg1 must not be same as reg3"} // note: this is ok for fpRegs as these are always the same type
if(reg2!=null && reg3!=null) require(reg2!=reg3) {"reg2 must not be same as reg3"} // note: this is ok for fpRegs as these are always the same type
@ -855,6 +856,20 @@ data class IRInstruction(
if(opcode==Opcode.SYSCALL) {
requireNotNull(immediate) { "syscall needs immediate integer for the syscall number" }
val callRegisters = fcallArgs?.arguments?.map { it.reg.registerNum } ?: emptyList()
val returnRegisters = fcallArgs?.returns?.map { it.registerNum } ?: emptyList()
val reused = callRegisters.intersect(returnRegisters)
if(reused.isNotEmpty()) {
for(r in reused) {
val argType = fcallArgs!!.arguments.single { it.reg.registerNum==r }.reg.dt
val returnType = fcallArgs.returns.single { it.registerNum==r }.dt
if (argType!=IRDataType.FLOAT && returnType!=IRDataType.FLOAT) {
if(argType!=returnType)
throw AssemblyError("syscall cannot reuse argument register as return register with different type $this")
}
}
}
}
}
@ -863,7 +878,8 @@ data class IRInstruction(
writeRegsCounts: MutableMap<Int, Int>,
readFpRegsCounts: MutableMap<Int, Int>,
writeFpRegsCounts: MutableMap<Int, Int>,
regsTypes: MutableMap<Int, IRDataType>
regsTypes: MutableMap<Int, IRDataType>,
chunk: IRCodeChunk?
) {
when (this.reg1direction) {
OperandDirection.UNUSED -> {}
@ -874,7 +890,7 @@ data class IRInstruction(
val existingType = regsTypes[reg1]
if (existingType!=null) {
if (existingType != actualtype)
throw IllegalArgumentException("register $reg1 assigned multiple types! $existingType and $actualtype")
throw IllegalArgumentException("register $reg1 assigned multiple types! $existingType and $actualtype in label ${chunk?.label} chunk $chunk")
} else
regsTypes[reg1] = actualtype
}
@ -886,7 +902,7 @@ data class IRInstruction(
val existingType = regsTypes[reg1]
if (existingType!=null) {
if (existingType != actualtype)
throw IllegalArgumentException("register $reg1 assigned multiple types! $existingType and $actualtype")
throw IllegalArgumentException("register $reg1 assigned multiple types! $existingType and $actualtype in label ${chunk?.label} chunk $chunk")
} else
regsTypes[reg1] = actualtype
@ -900,7 +916,7 @@ data class IRInstruction(
val existingType = regsTypes[reg1]
if (existingType!=null) {
if (existingType != actualtype)
throw IllegalArgumentException("register $reg1 assigned multiple types! $existingType and $actualtype")
throw IllegalArgumentException("register $reg1 assigned multiple types! $existingType and $actualtype in label ${chunk?.label} chunk $chunk")
} else
regsTypes[reg1] = actualtype
@ -916,7 +932,7 @@ data class IRInstruction(
val existingType = regsTypes[reg2]
if (existingType!=null) {
if (existingType != actualtype)
throw IllegalArgumentException("register $reg2 assigned multiple types! $existingType and $actualtype")
throw IllegalArgumentException("register $reg2 assigned multiple types! $existingType and $actualtype in label ${chunk?.label} chunk $chunk")
} else
regsTypes[reg2] = actualtype
}
@ -932,7 +948,7 @@ data class IRInstruction(
val existingType = regsTypes[reg3]
if (existingType!=null) {
if (existingType != actualtype)
throw IllegalArgumentException("register $reg3 assigned multiple types! $existingType and $actualtype")
throw IllegalArgumentException("register $reg3 assigned multiple types! $existingType and $actualtype in label ${chunk?.label} chunk $chunk")
} else
regsTypes[reg3] = actualtype
}
@ -965,7 +981,7 @@ data class IRInstruction(
val existingType = regsTypes[it.registerNum]
if (existingType!=null) {
if (existingType != it.dt)
throw IllegalArgumentException("register ${it.registerNum} assigned multiple types! $existingType and ${it.dt}")
throw IllegalArgumentException("register ${it.registerNum} assigned multiple types! $existingType and ${it.dt} in label ${chunk?.label} chunk $chunk")
} else
regsTypes[it.registerNum] = it.dt
}
@ -978,7 +994,7 @@ data class IRInstruction(
val existingType = regsTypes[it.reg.registerNum]
if (existingType!=null) {
if (existingType != it.reg.dt)
throw IllegalArgumentException("register ${it.reg.registerNum} assigned multiple types! $existingType and ${it.reg.dt}")
throw IllegalArgumentException("register ${it.reg.registerNum} assigned multiple types! $existingType and ${it.reg.dt} in label ${chunk?.label} chunk $chunk")
} else
regsTypes[it.reg.registerNum] = it.reg.dt
}

View File

@ -286,7 +286,14 @@ class IRProgram(val name: String,
}
globalInits.instructions.forEach {
it.addUsedRegistersCounts(readRegsCounts, writeRegsCounts, readFpRegsCounts, writeFpRegsCounts, regsTypes)
it.addUsedRegistersCounts(
readRegsCounts,
writeRegsCounts,
readFpRegsCounts,
writeFpRegsCounts,
regsTypes,
globalInits
)
}
blocks.forEach {block ->
@ -474,7 +481,7 @@ class IRCodeChunk(label: String?, next: IRCodeChunkBase?): IRCodeChunkBase(label
val readFpRegsCounts = mutableMapOf<Int, Int>().withDefault { 0 }
val writeRegsCounts = mutableMapOf<Int, Int>().withDefault { 0 }
val writeFpRegsCounts = mutableMapOf<Int, Int>().withDefault { 0 }
instructions.forEach { it.addUsedRegistersCounts(readRegsCounts, writeRegsCounts, readFpRegsCounts, writeFpRegsCounts, regsTypes) }
instructions.forEach { it.addUsedRegistersCounts(readRegsCounts, writeRegsCounts, readFpRegsCounts, writeFpRegsCounts, regsTypes, this) }
return RegistersUsed(readRegsCounts, writeRegsCounts, readFpRegsCounts, writeFpRegsCounts, regsTypes)
}
@ -559,7 +566,14 @@ private fun registersUsedInAssembly(isIR: Boolean, assembly: String): RegistersU
if(t.isNotEmpty()) {
val result = parseIRCodeLine(t)
result.fold(
ifLeft = { it.addUsedRegistersCounts(readRegsCounts, writeRegsCounts,readFpRegsCounts, writeFpRegsCounts, regsTypes) },
ifLeft = { it.addUsedRegistersCounts(
readRegsCounts,
writeRegsCounts,
readFpRegsCounts,
writeFpRegsCounts,
regsTypes,
null
) },
ifRight = { /* labels can be skipped */ }
)
}

View File

@ -6,8 +6,8 @@ package prog8.vm
* A,X and Y "physical" 6502 registers.
*/
class Registers {
private val registers = Array<UShort>(65536) { 0u }
private val floatRegisters = Array(65536) { 0.0 }
private val registers = Array<UShort>(99999) { 0u }
private val floatRegisters = Array(99999) { 0.0 }
var cpuA: UByte = 0u
var cpuX: UByte = 0u
var cpuY: UByte = 0u