From 7136b33f2e55a13429f0c627a5aadce5a2b4bf62 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Thu, 20 Jul 2023 01:59:20 +0200 Subject: [PATCH 1/6] cx16: change reset_system() to use Reset SMC sequence instead of hard reboot --- compiler/res/prog8lib/cx16/syslib.p8 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/res/prog8lib/cx16/syslib.p8 b/compiler/res/prog8lib/cx16/syslib.p8 index fd6873c49..6fbfb8b63 100644 --- a/compiler/res/prog8lib/cx16/syslib.p8 +++ b/compiler/res/prog8lib/cx16/syslib.p8 @@ -961,8 +961,8 @@ asmsub set_rasterline(uword line @AY) { %asm {{ sei ldx #$42 - ldy #1 - tya + ldy #2 + lda #0 jsr cx16.i2c_write_byte bra * }} From c7d54570cc0ae9a18a411057418e26fe5d951f54 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Thu, 20 Jul 2023 23:58:52 +0200 Subject: [PATCH 2/6] IR: sXX, CONCAT instructions now use 3 register format --- .../codegen/intermediate/BuiltinFuncGen.kt | 5 +- .../codegen/intermediate/ExpressionGen.kt | 65 ++++++++------- .../prog8/codegen/intermediate/IRCodeGen.kt | 6 +- docs/source/todo.rst | 1 - .../src/prog8/intermediate/IRInstructions.kt | 80 +++++++++++++------ intermediate/src/prog8/intermediate/Utils.kt | 8 +- virtualmachine/src/prog8/vm/VirtualMachine.kt | 27 ++++--- 7 files changed, 122 insertions(+), 70 deletions(-) diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt index cd4f60d84..0f1760fe3 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/BuiltinFuncGen.kt @@ -330,10 +330,11 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe addToResult(result, msbTr, msbTr.resultReg, -1) val lsbTr = exprGen.translateExpression(call.args[1]) addToResult(result, lsbTr, lsbTr.resultReg, -1) + val resultReg = codeGen.registers.nextFree() result += IRCodeChunk(null, null).also { - it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1 = lsbTr.resultReg, reg2 = msbTr.resultReg) + it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=resultReg, reg2 = lsbTr.resultReg, reg3 = msbTr.resultReg) } - return ExpressionCodeResult(result, IRDataType.WORD, lsbTr.resultReg, -1) + return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1) } private fun funcClamp(call: PtBuiltinFunctionCall): ExpressionCodeResult { diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt index 1dbe1173f..7684c8a88 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/ExpressionGen.kt @@ -166,13 +166,14 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { require(vmDt==IRDataType.WORD) val arrayLength = codeGen.symbolTable.getLength(arrayIx.variable.name) resultRegister = codeGen.registers.nextFree() + val finalResultReg = codeGen.registers.nextFree() if(arrayIx.index is PtNumber) { val memOffset = (arrayIx.index as PtNumber).number.toInt() result += IRCodeChunk(null, null).also { val tmpRegMsb = codeGen.registers.nextFree() it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=resultRegister, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_lsb+$memOffset") it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=tmpRegMsb, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_msb+$memOffset") - it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=resultRegister, reg2=tmpRegMsb) + it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=finalResultReg, reg2=resultRegister, reg3=tmpRegMsb) } } else { val tr = translateExpression(arrayIx.index) @@ -181,10 +182,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { val tmpRegMsb = codeGen.registers.nextFree() it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=resultRegister, reg2 = tr.resultReg, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_lsb") it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2 = tr.resultReg, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_msb") - it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=resultRegister, reg2=tmpRegMsb) + it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=finalResultReg, reg2=resultRegister, reg3=tmpRegMsb) } } - return ExpressionCodeResult(result, vmDt, resultRegister, -1) + return ExpressionCodeResult(result, vmDt, finalResultReg, -1) } var resultFpRegister = -1 @@ -454,6 +455,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { greaterEquals: Boolean ): ExpressionCodeResult { val result = mutableListOf() + val cmpResultReg = codeGen.registers.nextFree() if(vmDt==IRDataType.FLOAT) { val leftTr = translateExpression(binExpr.left) addToResult(result, leftTr, -1, leftTr.resultFpReg) @@ -468,8 +470,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { } else { if (greaterEquals) Opcode.SGE else Opcode.SGT } - addInstr(result, IRInstruction(ins, IRDataType.BYTE, reg1 = resultRegister, reg2 = zeroRegister), null) - return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1) + addInstr(result, IRInstruction(ins, IRDataType.BYTE, reg1=cmpResultReg, reg2 = resultRegister, reg3 = zeroRegister), null) + return ExpressionCodeResult(result, IRDataType.BYTE, cmpResultReg, -1) } else { if(binExpr.left.type==DataType.STR || binExpr.right.type==DataType.STR) { throw AssemblyError("str compares should have been replaced with builtin function call to do the compare") @@ -483,8 +485,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { } else { if (greaterEquals) Opcode.SGE else Opcode.SGT } - addInstr(result, IRInstruction(ins, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg), null) - return ExpressionCodeResult(result, IRDataType.BYTE, leftTr.resultReg, -1) + addInstr(result, IRInstruction(ins, vmDt, reg1=cmpResultReg, reg2 = leftTr.resultReg, reg3 = rightTr.resultReg), null) + return ExpressionCodeResult(result, IRDataType.BYTE, cmpResultReg, -1) } } } @@ -496,6 +498,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { lessEquals: Boolean ): ExpressionCodeResult { val result = mutableListOf() + val cmpResultRegister = codeGen.registers.nextFree() if(vmDt==IRDataType.FLOAT) { val leftTr = translateExpression(binExpr.left) addToResult(result, leftTr, -1, leftTr.resultFpReg) @@ -510,8 +513,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { } else { if (lessEquals) Opcode.SLE else Opcode.SLT } - addInstr(result, IRInstruction(ins, IRDataType.BYTE, reg1 = resultRegister, reg2 = zeroRegister), null) - return ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1) + addInstr(result, IRInstruction(ins, IRDataType.BYTE, reg1=cmpResultRegister, reg2 = resultRegister, reg3 = zeroRegister), null) + return ExpressionCodeResult(result, IRDataType.BYTE, cmpResultRegister, -1) } else { if(binExpr.left.type==DataType.STR || binExpr.right.type==DataType.STR) { throw AssemblyError("str compares should have been replaced with builtin function call to do the compare") @@ -525,8 +528,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { } else { if (lessEquals) Opcode.SLE else Opcode.SLT } - addInstr(result, IRInstruction(ins, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg), null) - return ExpressionCodeResult(result, IRDataType.BYTE, leftTr.resultReg, -1) + addInstr(result, IRInstruction(ins, vmDt, reg1=cmpResultRegister, reg2 = leftTr.resultReg, reg3 = rightTr.resultReg), null) + return ExpressionCodeResult(result, IRDataType.BYTE, cmpResultRegister, -1) } } } @@ -568,8 +571,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { val rightTr = translateExpression(binExpr.right) addToResult(result, rightTr, rightTr.resultReg, -1) val opcode = if (notEquals) Opcode.SNE else Opcode.SEQ - addInstr(result, IRInstruction(opcode, vmDt, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg), null) - ExpressionCodeResult(result, IRDataType.BYTE, leftTr.resultReg, -1) + val resultReg = codeGen.registers.nextFree() + addInstr(result, IRInstruction(opcode, vmDt, reg1 = resultReg, reg2 = leftTr.resultReg, reg3 = rightTr.resultReg), null) + ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1) } } } @@ -1239,6 +1243,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { ): MutableList { val result = mutableListOf() val valueReg = codeGen.registers.nextFree() + val cmpResultReg = codeGen.registers.nextFree() if(operand is PtNumber) { val numberReg = codeGen.registers.nextFree() if (knownAddress != null) { @@ -1246,16 +1251,16 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, address = knownAddress) it += IRInstruction(Opcode.LOAD, vmDt, reg1=numberReg, immediate = operand.number.toInt()) - it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = valueReg, reg2 = numberReg) - it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, address = knownAddress) + it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = cmpResultReg, reg2 = valueReg, reg3 = numberReg) + it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, address = knownAddress) } } else { // in-place modify a symbol (variable) result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, labelSymbol = symbol) it += IRInstruction(Opcode.LOAD, vmDt, reg1=numberReg, immediate = operand.number.toInt()) - it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = valueReg, reg2 = numberReg) - it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, labelSymbol = symbol) + it += IRInstruction(compareAndSetOpcode, vmDt, reg1=cmpResultReg, reg2 = valueReg, reg3 = numberReg) + it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, labelSymbol = symbol) } } } else { @@ -1265,15 +1270,15 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { // in-place modify a memory location result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, address = knownAddress) - it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = valueReg, reg2 = tr.resultReg) - it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, address = knownAddress) + it += IRInstruction(compareAndSetOpcode, vmDt, reg1=cmpResultReg, reg2 = valueReg, reg3 = tr.resultReg) + it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, address = knownAddress) } } else { // in-place modify a symbol (variable) result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.LOADM, vmDt, reg1 = valueReg, labelSymbol = symbol) - it += IRInstruction(compareAndSetOpcode, vmDt, reg1 = valueReg, reg2 = tr.resultReg) - it += IRInstruction(Opcode.STOREM, vmDt, reg1 = valueReg, labelSymbol = symbol) + it += IRInstruction(compareAndSetOpcode, vmDt, reg1=cmpResultReg, reg2 = valueReg, reg3 = tr.resultReg) + it += IRInstruction(Opcode.STOREM, vmDt, reg1 = cmpResultReg, labelSymbol = symbol) } } } @@ -1292,6 +1297,7 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { val zeroReg = codeGen.registers.nextFree() if(operand is PtNumber) { val numberReg = codeGen.registers.nextFreeFloat() + val cmpResultReg = codeGen.registers.nextFree() if (knownAddress != null) { // in-place modify a memory location result += IRCodeChunk(null, null).also { @@ -1299,8 +1305,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, immediateFp = operand.number.toFloat()) it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = numberReg) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0) - it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg) - it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg) + it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg) + it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg) it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, address = knownAddress) } } else { @@ -1310,13 +1316,14 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { it += IRInstruction(Opcode.LOAD, IRDataType.FLOAT, fpReg1 = numberReg, immediateFp = operand.number.toFloat()) it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = numberReg) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0) - it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg) - it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg) + it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg) + it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg) it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol) } } } else { val tr = translateExpression(operand) + val cmpResultReg = codeGen.registers.nextFree() addToResult(result, tr, -1, tr.resultFpReg) if (knownAddress != null) { // in-place modify a memory location @@ -1324,8 +1331,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, address = knownAddress) it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = tr.resultFpReg) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0) - it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg) - it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg) + it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg) + it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg) it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, address = knownAddress) } } else { @@ -1334,8 +1341,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) { it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol) it += IRInstruction(Opcode.FCOMP, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg, fpReg2 = tr.resultFpReg) it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=zeroReg, immediate = 0) - it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpReg, reg2=zeroReg) - it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpReg, fpReg1 = valueReg) + it += IRInstruction(compareAndSetOpcode, IRDataType.BYTE, reg1=cmpResultReg, reg2=cmpReg, reg3=zeroReg) + it += IRInstruction(Opcode.FFROMUB, IRDataType.FLOAT, reg1=cmpResultReg, fpReg1 = valueReg) it += IRInstruction(Opcode.STOREM, IRDataType.FLOAT, fpReg1 = valueReg, labelSymbol = symbol) } } diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index 4d1b987f7..49ab1b0f9 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -177,6 +177,7 @@ class IRCodeGen( old.type, old.reg1, old.reg2, + old.reg3, old.fpReg1, old.fpReg2, immediate = immediateValue, @@ -476,10 +477,11 @@ class IRCodeGen( result += IRCodeChunk(loopLabel, null).also { val tmpRegLsb = registers.nextFree() val tmpRegMsb = registers.nextFree() + val concatReg = registers.nextFree() it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegLsb, reg2=indexReg, immediate = iterableLength, labelSymbol=iterable.name+"_lsb") it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2=indexReg, immediate = iterableLength, labelSymbol=iterable.name+"_msb") - it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=tmpRegLsb, reg2=tmpRegMsb) - it += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=tmpRegLsb, labelSymbol = loopvarSymbol) + it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=concatReg, reg2=tmpRegLsb, reg3=tmpRegMsb) + it += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=concatReg, labelSymbol = loopvarSymbol) } result += translateNode(forLoop.statements) result += IRCodeChunk(null, null).also { diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 6c9a6759a..6a844603e 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,7 +1,6 @@ TODO ==== -- IR: instructions that do type conversion (SZ etc, CONCAT, SGN) should put the result in a DIFFERENT register. - IR: reduce the number of branch instructions (gradually), replace with CMP(I) + status branch instruction ... diff --git a/intermediate/src/prog8/intermediate/IRInstructions.kt b/intermediate/src/prog8/intermediate/IRInstructions.kt index 97bfafb0f..6a4313dc4 100644 --- a/intermediate/src/prog8/intermediate/IRInstructions.kt +++ b/intermediate/src/prog8/intermediate/IRInstructions.kt @@ -99,16 +99,16 @@ bles reg1, value, address - jump to location in program given by l ( NOTE: there are no bltr/bler instructions because these are equivalent to bgtr/bger with the register operands swapped around.) sz reg1, reg2 - set reg1=1 if reg2==0, else 0 snz reg1, reg2 - set reg1=1 if reg2!=0, else 0 -seq reg1, reg2 - set reg1=1 if reg1 == reg2, else 0 -sne reg1, reg2 - set reg1=1 if reg1 != reg2, else 0 -slt reg1, reg2 - set reg1=1 if reg1 < reg2 (unsigned), else 0 -slts reg1, reg2 - set reg1=1 if reg1 < reg2 (signed), else 0 -sle reg1, reg2 - set reg1=1 if reg1 <= reg2 (unsigned), else 0 -sles reg1, reg2 - set reg1=1 if reg1 <= reg2 (signed), else 0 -sgt reg1, reg2 - set reg1=1 if reg1 > reg2 (unsigned), else 0 -sgts reg1, reg2 - set reg1=1 if reg1 > reg2 (signed), else 0 -sge reg1, reg2 - set reg1=1 if reg1 >= reg2 (unsigned), else 0 -sges reg1, reg2 - set reg1=1 if reg1 >= reg2 (signed), else 0 +seq reg1, reg2, reg3 - set reg1=1 if reg2 == reg3, else 0 +sne reg1, reg2, reg3 - set reg1=1 if reg2 != reg3, else 0 +slt reg1, reg2, reg3 - set reg1=1 if reg2 < reg3 (unsigned), else 0 +slts reg1, reg2, reg3 - set reg1=1 if reg2 < reg3 (signed), else 0 +sle reg1, reg2, reg3 - set reg1=1 if reg2 <= reg3 (unsigned), else 0 +sles reg1, reg2, reg3 - set reg1=1 if reg2 <= reg3 (signed), else 0 +sgt reg1, reg2, reg3 - set reg1=1 if reg2 > reg3 (unsigned), else 0 +sgts reg1, reg2, reg3 - set reg1=1 if reg2 > reg3 (signed), else 0 +sge reg1, reg2, reg3 - set reg1=1 if reg2 >= reg3 (unsigned), else 0 +sges reg1, reg2, reg3 - set reg1=1 if reg2 >= reg3 (signed), else 0 (note: on the M68k these instructions will set all bits to 1 (so value=-1 instead of 1), but the boolean logic here requires it to be 0 or 1 in this IR) @@ -220,7 +220,7 @@ sec - set Carry status bit nop - do nothing breakpoint - trigger a breakpoint msig [b, w] reg1, reg2 - reg1 becomes the most significant byte (or word) of the word (or int) in reg2 (.w not yet implemented; requires 32 bits regs) -concat [b, w] reg1, reg2 - reg1 = concatenated lsb/lsw of reg1 (as lsb) and lsb/lsw of reg2 (as msb) into word or int (int not yet implemented; requires 32bits regs) +concat [b, w] reg1, reg2, reg3 - reg1.w = concatenated lsb/lsw of reg2 (as lsb) and lsb/lsw of reg3 (as msb) into word or int (int not yet implemented; requires 32bits regs) push [b, w, f] reg1 - push value in reg1 on the stack pop [b, w, f] reg1 - pop value from stack into reg1 */ @@ -454,6 +454,7 @@ enum class OperandDirection { data class InstructionFormat(val datatype: IRDataType?, val reg1: OperandDirection, val reg2: OperandDirection, + val reg3: OperandDirection, val fpReg1: OperandDirection, val fpReg2: OperandDirection, val address: OperandDirection, @@ -466,6 +467,7 @@ data class InstructionFormat(val datatype: IRDataType?, for(part in spec.split('|').map{ it.trim() }) { var reg1 = OperandDirection.UNUSED var reg2 = OperandDirection.UNUSED + var reg3 = OperandDirection.UNUSED var fpreg1 = OperandDirection.UNUSED var fpreg2 = OperandDirection.UNUSED var address = OperandDirection.UNUSED @@ -480,6 +482,7 @@ data class InstructionFormat(val datatype: IRDataType?, ">r1" -> reg1 = OperandDirection.WRITE "<>r1" -> reg1 = OperandDirection.READWRITE " reg2 = OperandDirection.READ + " reg3 = OperandDirection.READ " fpreg1 = OperandDirection.READ ">fr1" -> fpreg1 = OperandDirection.WRITE "<>fr1" -> fpreg1 = OperandDirection.READWRITE @@ -496,13 +499,13 @@ data class InstructionFormat(val datatype: IRDataType?, } if(typespec=="N") - result[null] = InstructionFormat(null, reg1, reg2, fpreg1, fpreg2, address, immediate, funcCall, sysCall) + result[null] = InstructionFormat(null, reg1, reg2, reg3, fpreg1, fpreg2, address, immediate, funcCall, sysCall) if('B' in typespec) - result[IRDataType.BYTE] = InstructionFormat(IRDataType.BYTE, reg1, reg2, fpreg1, fpreg2, address, immediate, funcCall, sysCall) + result[IRDataType.BYTE] = InstructionFormat(IRDataType.BYTE, reg1, reg2, reg3, fpreg1, fpreg2, address, immediate, funcCall, sysCall) if('W' in typespec) - result[IRDataType.WORD] = InstructionFormat(IRDataType.WORD, reg1, reg2, fpreg1, fpreg2, address, immediate, funcCall, sysCall) + result[IRDataType.WORD] = InstructionFormat(IRDataType.WORD, reg1, reg2, reg3, fpreg1, fpreg2, address, immediate, funcCall, sysCall) if('F' in typespec) - result[IRDataType.FLOAT] = InstructionFormat(IRDataType.FLOAT, reg1, reg2, fpreg1, fpreg2, address, immediate, funcCall, sysCall) + result[IRDataType.FLOAT] = InstructionFormat(IRDataType.FLOAT, reg1, reg2, reg3, fpreg1, fpreg2, address, immediate, funcCall, sysCall) } return result } @@ -566,16 +569,16 @@ val instructionFormats = mutableMapOf( Opcode.BLES to InstructionFormat.from("BW,r1,r1,r1,r1,r1,r1,r1,r1,r1,r1,r1,r1,r1,r1,r1,r1,r1,r1,r1,r1,r1,r1,r1 | F,<>fr1"), Opcode.INCM to InstructionFormat.from("BW,<>a | F,<>a"), Opcode.DEC to InstructionFormat.from("BW,<>r1 | F,<>fr1"), @@ -663,7 +666,7 @@ val instructionFormats = mutableMapOf( Opcode.MSIG to InstructionFormat.from("BW,>r1,r1 | F,>fr1"), - Opcode.CONCAT to InstructionFormat.from("BW,<>r1,r1, throw IllegalArgumentException("reg2 can only be read") } + when (this.reg3direction) { + OperandDirection.UNUSED -> {} + OperandDirection.READ -> { + writeRegsCounts[this.reg3!!] = writeRegsCounts.getValue(this.reg3)+1 + if(type!=null) { + var types = regsTypes[this.reg3] + if(types==null) types = mutableSetOf() + types += type + regsTypes[this.reg3] = types + } + } + else -> throw IllegalArgumentException("reg3 can only be read") + } when (this.fpReg1direction) { OperandDirection.UNUSED -> {} OperandDirection.READ -> { @@ -927,6 +951,10 @@ data class IRInstruction( result.add("r$it") result.add(",") } + reg3?.let { + result.add("r$it") + result.add(",") + } fpReg1?.let { result.add("fr$it") result.add(",") diff --git a/intermediate/src/prog8/intermediate/Utils.kt b/intermediate/src/prog8/intermediate/Utils.kt index 472104780..728115fa9 100644 --- a/intermediate/src/prog8/intermediate/Utils.kt +++ b/intermediate/src/prog8/intermediate/Utils.kt @@ -115,6 +115,7 @@ fun parseIRCodeLine(line: String): Either { val operands = if(rest.isBlank()) emptyList() else rest.split(",").map{ it.trim() }.toMutableList() var reg1: Int? = null var reg2: Int? = null + var reg3: Int? = null var fpReg1: Int? = null var fpReg2: Int? = null var immediateInt: Int? = null @@ -143,6 +144,7 @@ fun parseIRCodeLine(line: String): Either { else if (oper[0] in "rR") { if (reg1 == null) reg1 = oper.substring(1).toInt() else if (reg2 == null) reg2 = oper.substring(1).toInt() + else if (reg3 == null) reg3 = oper.substring(1).toInt() else throw IRParseException("too many register operands") } else if (oper[0] in "fF" && oper[1] in "rR") { if (fpReg1 == null) fpReg1 = oper.substring(2).toInt() @@ -179,6 +181,8 @@ fun parseIRCodeLine(line: String): Either { throw IRParseException("needs reg1 for $line") if(format.reg2!=OperandDirection.UNUSED && reg2==null) throw IRParseException("needs reg2 for $line") + if(format.reg3!=OperandDirection.UNUSED && reg3==null) + throw IRParseException("needs reg3 for $line") if(format.fpReg1!=OperandDirection.UNUSED && fpReg1==null) throw IRParseException("needs fpReg1 for $line") if(format.fpReg2!=OperandDirection.UNUSED && fpReg2==null) @@ -189,6 +193,8 @@ fun parseIRCodeLine(line: String): Either { throw IRParseException("invalid reg1 for $line") if(format.reg2==OperandDirection.UNUSED && reg2!=null) throw IRParseException("invalid reg2 for $line") + if(format.reg3==OperandDirection.UNUSED && reg3!=null) + throw IRParseException("invalid reg3 for $line") if(format.fpReg1==OperandDirection.UNUSED && fpReg1!=null) throw IRParseException("invalid fpReg1 for $line") if(format.fpReg2==OperandDirection.UNUSED && fpReg2!=null) @@ -218,7 +224,7 @@ fun parseIRCodeLine(line: String): Either { throw IRParseException("labelsymbol confused with register?: $labelSymbol") } - return left(IRInstruction(opcode, type, reg1, reg2, fpReg1, fpReg2, immediateInt, immediateFp, address, labelSymbol = labelSymbol)) + return left(IRInstruction(opcode, type, reg1, reg2, reg3, fpReg1, fpReg2, immediateInt, immediateFp, address, labelSymbol = labelSymbol)) } private class ParsedCall( diff --git a/virtualmachine/src/prog8/vm/VirtualMachine.kt b/virtualmachine/src/prog8/vm/VirtualMachine.kt index f7761f991..d639f26c2 100644 --- a/virtualmachine/src/prog8/vm/VirtualMachine.kt +++ b/virtualmachine/src/prog8/vm/VirtualMachine.kt @@ -808,14 +808,24 @@ class VirtualMachine(irProgram: IRProgram) { } private fun InsSZ(i: IRInstruction) { - val (_: Int, right: Int) = getSetOnConditionOperands(i) + val right = when(i.type) { + IRDataType.BYTE -> registers.getSB(i.reg2!!).toInt() + IRDataType.WORD -> registers.getSW(i.reg2!!).toInt() + IRDataType.FLOAT -> throw IllegalArgumentException("can't use float here") + null -> throw IllegalArgumentException("need type for branch instruction") + } val value = if(right==0) 1 else 0 setResultReg(i.reg1!!, value, i.type!!) nextPc() } private fun InsSNZ(i: IRInstruction) { - val (_: Int, right: Int) = getSetOnConditionOperands(i) + val right = when(i.type) { + IRDataType.BYTE -> registers.getSB(i.reg2!!).toInt() + IRDataType.WORD -> registers.getSW(i.reg2!!).toInt() + IRDataType.FLOAT -> throw IllegalArgumentException("can't use float here") + null -> throw IllegalArgumentException("need type for branch instruction") + } val value = if(right!=0) 1 else 0 setResultReg(i.reg1!!, value, i.type!!) nextPc() @@ -889,7 +899,6 @@ class VirtualMachine(irProgram: IRProgram) { val value = if(left>=right) 1 else 0 setResultReg(i.reg1!!, value, i.type!!) nextPc() - } private fun InsINC(i: IRInstruction) { @@ -2106,8 +2115,8 @@ class VirtualMachine(irProgram: IRProgram) { private fun InsCONCAT(i: IRInstruction) { when(i.type!!) { IRDataType.BYTE -> { - val lsb = registers.getUB(i.reg1!!) - val msb = registers.getUB(i.reg2!!) + val lsb = registers.getUB(i.reg2!!) + val msb = registers.getUB(i.reg3!!) registers.setUW(i.reg1!!, ((msb.toInt() shl 8) or lsb.toInt()).toUShort()) } IRDataType.WORD -> throw IllegalArgumentException("concat.w not yet supported, requires 32-bits registers") @@ -2305,8 +2314,8 @@ class VirtualMachine(irProgram: IRProgram) { private fun getSetOnConditionOperands(ins: IRInstruction): Pair { return when(ins.type) { - IRDataType.BYTE -> Pair(registers.getSB(ins.reg1!!).toInt(), registers.getSB(ins.reg2!!).toInt()) - IRDataType.WORD -> Pair(registers.getSW(ins.reg1!!).toInt(), registers.getSW(ins.reg2!!).toInt()) + IRDataType.BYTE -> Pair(registers.getSB(ins.reg2!!).toInt(), registers.getSB(ins.reg3!!).toInt()) + IRDataType.WORD -> Pair(registers.getSW(ins.reg2!!).toInt(), registers.getSW(ins.reg3!!).toInt()) IRDataType.FLOAT -> { throw IllegalArgumentException("can't use float here") } @@ -2316,8 +2325,8 @@ class VirtualMachine(irProgram: IRProgram) { private fun getSetOnConditionOperandsU(ins: IRInstruction): Pair { return when(ins.type) { - IRDataType.BYTE -> Pair(registers.getUB(ins.reg1!!).toUInt(), registers.getUB(ins.reg2!!).toUInt()) - IRDataType.WORD -> Pair(registers.getUW(ins.reg1!!).toUInt(), registers.getUW(ins.reg2!!).toUInt()) + IRDataType.BYTE -> Pair(registers.getUB(ins.reg2!!).toUInt(), registers.getUB(ins.reg3!!).toUInt()) + IRDataType.WORD -> Pair(registers.getUW(ins.reg2!!).toUInt(), registers.getUW(ins.reg3!!).toUInt()) IRDataType.FLOAT -> { throw IllegalArgumentException("can't use float here") } From 10d0ff252b535df3ae9ba21bcaba3baf5cfd38a9 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 21 Jul 2023 00:14:06 +0200 Subject: [PATCH 3/6] ignore buildversion changes --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 452c2d8e2..b9a95beae 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ parsetab.py compiler/lib/ .gradle +**/BuildVersion.kt /prog8compiler.jar sd*.img *.d64 From 4575a8fffeb2679123b971f3df0375893607fb4b Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 21 Jul 2023 22:40:07 +0200 Subject: [PATCH 4/6] cx16: added cx16.vaddr_autoincr() and cx16.vaddr_autodecr() --- compiler/res/prog8lib/cx16/syslib.p8 | 80 ++++++++++++++++++++++++++++ docs/source/compiling.rst | 4 ++ examples/test.p8 | 30 +++++------ 3 files changed, 96 insertions(+), 18 deletions(-) diff --git a/compiler/res/prog8lib/cx16/syslib.p8 b/compiler/res/prog8lib/cx16/syslib.p8 index 6fbfb8b63..fed0675ae 100644 --- a/compiler/res/prog8lib/cx16/syslib.p8 +++ b/compiler/res/prog8lib/cx16/syslib.p8 @@ -539,6 +539,8 @@ asmsub vpeek(ubyte bank @A, uword address @XY) -> ubyte @A { asmsub vaddr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, byte autoIncrOrDecrByOne @Y) clobbers(A) { ; -- setup the VERA's data address register 0 or 1 + ; with optional auto increment or decrement of 1. + ; Note that the vaddr_autoincr() and vaddr_autodecr() routines allow to set all possible strides, not just 1. %asm {{ and #1 pha @@ -562,6 +564,84 @@ asmsub vaddr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, byte autoIncrO }} } +asmsub vaddr_autoincr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, uword autoIncrAmount @R2) clobbers(A,Y) { + ; -- setup the VERA's data address register 0 or 1 + ; including setting up optional auto increment amount. + %asm {{ + jsr _setup + lda cx16.r2H + ora cx16.r2L + beq + + jsr _determine_incr_bits ++ ora P8ZP_SCRATCH_REG + sta cx16.VERA_ADDR_H + rts + +_setup and #1 + sta P8ZP_SCRATCH_REG + lda cx16.r1 + and #1 + sta cx16.VERA_CTRL + lda cx16.r0 + sta cx16.VERA_ADDR_L + lda cx16.r0+1 + sta cx16.VERA_ADDR_M + rts + +_determine_incr_bits + lda cx16.r2H + bne _large + lda cx16.r2L + ldy #13 +- cmp _strides_lsb,y + beq + + dey + bpl - ++ tya + asl a + asl a + asl a + asl a + rts +_large ora cx16.r2L + cmp #1 ; 256 + bne + + lda #9<<4 + rts ++ cmp #2 ; 512 + bne + + lda #10<<4 + rts ++ cmp #65 ; 320 + bne + + lda #14<<4 + rts ++ cmp #130 ; 640 + bne + + lda #15<<4 + rts ++ lda #0 + rts +_strides_lsb .byte 0,1,2,4,8,16,32,64,128,255,255,40,80,160,255,255 + }} +} + +asmsub vaddr_autodecr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, uword autoDecrAmount @R2) clobbers(A,Y) { + ; -- setup the VERA's data address register 0 or 1 + ; including setting up optional auto decrement amount. + %asm {{ + jsr vaddr_autoincr._setup + lda cx16.r2H + ora cx16.r2L + beq + + jsr vaddr_autoincr._determine_incr_bits + ora #%00001000 ; autodecrement ++ ora P8ZP_SCRATCH_REG + sta cx16.VERA_ADDR_H + rts + }} +} + asmsub vpoke(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers(A) { ; -- write a single byte to VERA's video memory ; note: inefficient when writing multiple sequential bytes! diff --git a/docs/source/compiling.rst b/docs/source/compiling.rst index a2ab93515..bbd5f1801 100644 --- a/docs/source/compiling.rst +++ b/docs/source/compiling.rst @@ -301,6 +301,10 @@ Strange assembler error ^^^^^^^^^^^^^^^^^^^^^^^ If the compilation of your program fails in the assembly step, please check that you have the required version of the 64tass assembler installed. See :ref:`requirements`. +Also make sure that inside hand-written inlined assembly, +you don't use symbols named just a single letter (especially 'a', 'x' and 'y'). +Sometimes these are interpreted as the CPU register of that name. To avoid such confusions, +always use 2 or more letters for symbols in your assembly code. Community diff --git a/examples/test.p8 b/examples/test.p8 index 89dba048d..3c1496936 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,26 +1,20 @@ -%import textio -%zeropage basicsafe main { sub start() { - uword uw = 54321 - ubyte ub = 123 - word sw = -12345 - byte sb = -123 + cx16.set_screen_mode(128) - txt.print_uw(~ub as uword) ;132 - txt.nl() - txt.print_ub(~uw as ubyte) ;206 - txt.nl() - txt.print_uw(~sb as uword) ;122 - txt.nl() - txt.print_ub(~sw as ubyte) ;56 - txt.nl() - txt.print_w(-sb as word) ;123 - txt.nl() - txt.print_b(-sw as byte) ;57 - txt.nl() + cx16.vaddr_autoincr(0,320*100+100,1,2) + repeat 16 { + cx16.VERA_DATA1 = 0 + } + cx16.vaddr_autodecr(0,320*110+100,1,1) + repeat 16 { + cx16.VERA_DATA1 = 0 + } + + repeat { + } } } From 0e781d18fa65b0bf32419a4c8d2963eceaddb2d6 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 21 Jul 2023 23:04:21 +0200 Subject: [PATCH 5/6] cx16: added cx16.vaddr_autoincr() and cx16.vaddr_autodecr() --- compiler/res/prog8lib/cx16/syslib.p8 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/res/prog8lib/cx16/syslib.p8 b/compiler/res/prog8lib/cx16/syslib.p8 index fed0675ae..94dda0db4 100644 --- a/compiler/res/prog8lib/cx16/syslib.p8 +++ b/compiler/res/prog8lib/cx16/syslib.p8 @@ -567,6 +567,7 @@ asmsub vaddr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, byte autoIncrO asmsub vaddr_autoincr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, uword autoIncrAmount @R2) clobbers(A,Y) { ; -- setup the VERA's data address register 0 or 1 ; including setting up optional auto increment amount. + ; Specifiying an unsupported amount results in amount of zero. See the Vera docs about what amounts are possible. %asm {{ jsr _setup lda cx16.r2H @@ -629,6 +630,7 @@ _strides_lsb .byte 0,1,2,4,8,16,32,64,128,255,255,40,80,160,255,255 asmsub vaddr_autodecr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, uword autoDecrAmount @R2) clobbers(A,Y) { ; -- setup the VERA's data address register 0 or 1 ; including setting up optional auto decrement amount. + ; Specifiying an unsupported amount results in amount of zero. See the Vera docs about what amounts are possible. %asm {{ jsr vaddr_autoincr._setup lda cx16.r2H From 3bbc00cc8c454ec0fda6ef8f2df7f8dca8dc955d Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sat, 22 Jul 2023 23:22:06 +0200 Subject: [PATCH 6/6] more caution notices about symbols in inlined asm --- docs/source/syntaxreference.rst | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst index ecbe01d8e..1f7bf2c8e 100644 --- a/docs/source/syntaxreference.rst +++ b/docs/source/syntaxreference.rst @@ -168,6 +168,11 @@ Directives assembly into a prog8 block that already defines symbols itself. The compiler first looks for the file relative to the same directory as the module containing this statement is in, if the file can't be found there it is searched relative to the current directory. + + .. caution:: + Avoid using single-letter symbols in included assembly code, as they could be confused with CPU registers. + Also, note that all prog8 symbols are prefixed in assembly code, see :ref:`symbol-prefixing`. + Here is a small example program to show how to use labels to reference the included contents from prog8 code:: %import textio @@ -221,8 +226,11 @@ Directives If you use the correct scoping rules you can access symbols from the prog8 program from inside the assembly code. Sometimes you'll have to declare a variable in prog8 with `@shared` if it - is only used in such assembly code. For symbols just consisting of 3 letters, prog8 will - add a special prefix to them, read more about this in :ref:`symbol-prefixing`. + is only used in such assembly code. + + .. caution:: + Avoid using single-letter symbols in included assembly code, as they could be confused with CPU registers. + Also, note that all prog8 symbols are prefixed in assembly code, see :ref:`symbol-prefixing`. Identifiers