diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index e2a65f196..2fac45fa9 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -291,7 +291,7 @@ class IRCodeGen( BranchCondition.VS -> IRInstruction(Opcode.BSTVS, address = address) } addInstr(result, branchIns, null) - } else if(label!=null) { + } else if(label!=null && !isIndirectJump(goto)) { val branchIns = when(branch.condition) { BranchCondition.CS -> IRInstruction(Opcode.BSTCS, labelSymbol = label) BranchCondition.CC -> IRInstruction(Opcode.BSTCC, labelSymbol = label) @@ -304,7 +304,7 @@ class IRCodeGen( } addInstr(result, branchIns, null) } else { - TODO("JUMP to expression address ${goto.target}") + TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this! } if(branch.falseScope.children.isNotEmpty()) result += translateNode(branch.falseScope) @@ -1027,12 +1027,12 @@ class IRCodeGen( it += IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = afterIfLabel) } } - val identifier = goto.target as? PtIdentifier - if(identifier!=null) { - it += IRInstruction(Opcode.JUMPI, labelSymbol = identifier.name) - } else { - TODO("JUMP to expression address ${goto.target}") + // evaluate jump address expression into a register and jump indirectly to it + val tr = expressionEval.translateExpression(goto.target) + for(i in tr.chunks.flatMap { it.instructions }) { + it += i } + it += IRInstruction(Opcode.JUMPI, reg1 = tr.resultReg) } else { // normal jump, directly to target with branch opcode when(condition.operator) { @@ -1054,10 +1054,10 @@ class IRCodeGen( } it += if (goto.target.asConstInteger() != null) IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, address = goto.target.asConstInteger()) - else if(goto.target is PtIdentifier) + else if(goto.target is PtIdentifier && !isIndirectJump(goto)) IRInstruction(gotoOpcode, IRDataType.BYTE, reg1 = compResultReg, immediate = 0, labelSymbol = (goto.target as PtIdentifier).name) else - TODO("JUMP to expression address ${goto.target}") + TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this! } } } @@ -1073,10 +1073,10 @@ class IRCodeGen( else { require(!isIndirectJump(goto)) { "indirect jumps cannot be expressed using a branch opcode"} val identifier = goto.target as? PtIdentifier - if(identifier!=null) + if(identifier!=null && !isIndirectJump(goto)) IRInstruction(branchOpcode, labelSymbol = identifier.name) else - TODO("JUMP to expression address ${goto.target}") + TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this! } } @@ -1084,11 +1084,6 @@ class IRCodeGen( // indirect jump to target so the if has to jump past it instead val result = mutableListOf() val afterIfLabel = createLabelName() - val identifier = goto.target as? PtIdentifier - if(identifier==null) { - TODO("JUMP to expression address ${goto.target}") - } - val gotoSymbol = identifier.name fun ifNonZeroIntThenJump_BinExpr(condition: PtBinaryExpression) { if(condition.operator in LogicalOperators) { @@ -1210,7 +1205,10 @@ class IRCodeGen( else -> throw AssemblyError("weird if condition ${ifElse.condition}") } - addInstr(result, IRInstruction(Opcode.JUMPI, labelSymbol = gotoSymbol), null) + // indirect jump to some computed address + val tr = expressionEval.translateExpression(goto.target) + result += tr.chunks + addInstr(result, IRInstruction(Opcode.JUMPI, reg1 = tr.resultReg), null) result += IRCodeChunk(afterIfLabel, null) return result } @@ -1254,10 +1252,10 @@ class IRCodeGen( } if (goto.target.asConstInteger() != null) addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, immediate = number, address = goto.target.asConstInteger()), null) - else if(goto.target is PtIdentifier) + else if(goto.target is PtIdentifier && !isIndirectJump(goto)) addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, immediate = number, labelSymbol = (goto.target as PtIdentifier).name), null) else - TODO("JUMP to expression address ${goto.target}") + TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this! } } } else { @@ -1313,10 +1311,10 @@ class IRCodeGen( } else { if (goto.target.asConstInteger() != null) addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, reg2 = secondReg, address = goto.target.asConstInteger()), null) - else if(goto.target is PtIdentifier) + else if(goto.target is PtIdentifier && !isIndirectJump(goto)) addInstr(result, IRInstruction(opcode, irDt, reg1 = firstReg, reg2 = secondReg, labelSymbol = (goto.target as PtIdentifier).name), null) else - TODO("JUMP to expression address ${goto.target}") + TODO("JUMP to expression address ${goto.target}") // keep in mind the branch insruction that may need to precede this! } } } @@ -1649,23 +1647,26 @@ class IRCodeGen( private fun translate(jump: PtJump): IRCodeChunks { val result = mutableListOf() val chunk = IRCodeChunk(null, null) - chunk += if(jump.target.asConstInteger()!=null) { - IRInstruction(Opcode.JUMP, address = jump.target.asConstInteger()) + if(jump.target.asConstInteger()!=null) { + chunk += IRInstruction(Opcode.JUMP, address = jump.target.asConstInteger()) + result += chunk + return result } else { val identifier = jump.target as? PtIdentifier - if (identifier != null) { - if(isIndirectJump(jump)) { - IRInstruction(Opcode.JUMPI, labelSymbol = identifier.name) - } else { - IRInstruction(Opcode.JUMP, labelSymbol = identifier.name) - } - } else { - // val tr = expressionEval.translateExpression(jump.target) - TODO("JUMP to expression address ${jump.target}") + if (identifier != null && !isIndirectJump(jump)) { + // jump to label + chunk += IRInstruction(Opcode.JUMP, labelSymbol = identifier.name) + result += chunk + return result } + // evaluate jump address expression into a register and jump indirectly to it + val tr = expressionEval.translateExpression(jump.target) + result += tr.chunks + result += IRCodeChunk(null, null).also { + it += IRInstruction(Opcode.JUMPI, reg1=tr.resultReg) + } + return result } - result += chunk - return result } private fun isIndirectJump(jump: PtJump): Boolean { diff --git a/compiler/res/prog8lib/cx16/syslib.p8 b/compiler/res/prog8lib/cx16/syslib.p8 index 9bccf71ee..ec8651bb1 100644 --- a/compiler/res/prog8lib/cx16/syslib.p8 +++ b/compiler/res/prog8lib/cx16/syslib.p8 @@ -397,7 +397,7 @@ cx16 { extsub $ff4a = CLOSE_ALL(ubyte device @A) clobbers(A,X,Y) extsub $ff59 = LKUPLA(ubyte la @A) clobbers(A,X,Y) extsub $ff5c = LKUPSA(ubyte sa @Y) clobbers(A,X,Y) -extsub $ff5f = screen_mode(ubyte mode @A, bool getCurrent @Pc) -> ubyte @A, ubyte @X, ubyte @Y, bool @Pc ; also see SCREEN or get_screen_mode() +extsub $ff5f = screen_mode(ubyte mode @A, bool getCurrent @Pc) -> ubyte @A, ubyte @X, ubyte @Y, bool @Pc ; also see SCREEN or get/set_screen_mode() extsub $ff62 = screen_set_charset(ubyte charset @A, uword charsetptr @XY) clobbers(A,X,Y) extsub $ff6e = JSRFAR() ; following word = address to call, byte after that=rom/ram bank it is in extsub $ff74 = fetch(ubyte zp_startaddr @A, ubyte bank @X, ubyte index @Y) clobbers(X) -> ubyte @A diff --git a/docs/source/todo.rst b/docs/source/todo.rst index a22624754..fa4c67d71 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -24,6 +24,9 @@ Future Things and Ideas Need to add some way to generate a stable jump table at a given address. Need library to not call init_system AND init_system_phase2 not either. Library must not include prog8_program_start stuff either. +- Add a LZSA decompressor to the compression library to be able to decompress lzsa when you don't have it in ROM or when the ROM is banked out or unavailable + Problem is: on the X16, it should replicate the Kernal's behavior with decompressing to Vram / not incrementing the output address +- Add TSCrunch or ZX0 decruncher to compression lib. Same requirement on X16 again to be able to decompress into vram. - Fix missing cases where regular & has to return the start of the split array in memory whatever byte comes first. Search TODO("address of split word array") - Add a syntax to access specific bits in a variable, to avoid manually shifts&ands, something like variable[4:8] ? (or something else this may be too similar to regular array indexing) - something to reduce the need to use fully qualified names all the time. 'with' ? Or 'using '? @@ -56,7 +59,7 @@ IR/VM - fix call() return value handling - fix float register parameters (FAC1,FAC2) for extsubs, search for TODO("floating point register parameters not supported") - proper code gen for the CALLI instruction and that it (optionally) returns a word value that needs to be assigned to a reg -- make it possible to jump and branch to a computed address (expression), see TODO("JUMP to expression address" . This needs a change in the JUMPI instruction: it has to take a register instead (like CALLI) +- make it possible to jump and branch to a computed address (expression) in all cases, see TODO("JUMP to expression address" - 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) global initialization values are simply a list of LOAD instructions. Variables replaced include all subroutine parameters! So the only variables that remain as variables are arrays and strings. diff --git a/examples/test.p8 b/examples/test.p8 index 4dd639858..136932984 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,7 +1,8 @@ main { sub start() { - ;str test = '?' * 10 - str name = "foo" - cx16.r0 = name+2 + cx16.r0 = $aabb + cx16.r1 = $1122 + + goto cx16.r1+256 } } diff --git a/intermediate/src/prog8/intermediate/IRInstructions.kt b/intermediate/src/prog8/intermediate/IRInstructions.kt index 3315b4382..d6245347c 100644 --- a/intermediate/src/prog8/intermediate/IRInstructions.kt +++ b/intermediate/src/prog8/intermediate/IRInstructions.kt @@ -65,7 +65,7 @@ storehxy reg1 - store reg1.w into cpu hardware register CONTROL FLOW ------------ jump location - continue running at instruction at 'location' (label/memory address) -jumpi pointervar - continue running at memory address contained in the pointer variable (indirect jump) +jumpi rqg1 - continue running at memory address in reg1 (indirect jump) preparecall numparams - indicator that the next instructions are the param setup and function call/syscall with parameters calli reg1 - calls a subroutine (without arguments and without return valus) at memory addres in reg1 (indirect jsr) call label(argument register list) [: resultreg.type] @@ -645,7 +645,7 @@ val instructionFormats = mutableMapOf( Opcode.STOREHAY to InstructionFormat.from("W,