package prog8.codegen.intermediate import prog8.code.ast.* import prog8.code.core.AssemblyError import prog8.code.core.DataType import prog8.code.core.SignedDatatypes import prog8.code.core.SplitWordArrayTypes import prog8.intermediate.* internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGen: ExpressionGen) { fun translate(call: PtBuiltinFunctionCall): ExpressionCodeResult { return when(call.name) { "any" -> funcAny(call) "all" -> funcAll(call) "abs__byte", "abs__word", "abs__float" -> funcAbs(call) "cmp" -> funcCmp(call) "sgn" -> funcSgn(call) "sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(call) "divmod__ubyte" -> funcDivmod(call, IRDataType.BYTE) "divmod__uword" -> funcDivmod(call, IRDataType.WORD) "rsave", "rrestore" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore "callfar" -> funcCallfar(call) "call" -> funcCall(call) "msb" -> funcMsb(call) "lsb" -> funcLsb(call) "memory" -> funcMemory(call) "peek" -> funcPeek(call, IRDataType.BYTE) "peekw" -> funcPeek(call, IRDataType.WORD) "peekf" -> funcPeek(call, IRDataType.FLOAT) "poke" -> funcPoke(call, IRDataType.BYTE) "pokew" -> funcPoke(call, IRDataType.WORD) "pokef" -> funcPoke(call, IRDataType.FLOAT) "pokemon" -> funcPokemon(call) "mkword" -> funcMkword(call) "clamp__byte", "clamp__ubyte", "clamp__word", "clamp__uword" -> funcClamp(call) "min__byte", "min__ubyte", "min__word", "min__uword" -> funcMin(call) "max__byte", "max__ubyte", "max__word", "max__uword" -> funcMax(call) "sort" -> funcSort(call) "reverse" -> funcReverse(call) "setlsb" -> funcSetLsbMsb(call, false) "setmsb" -> funcSetLsbMsb(call, true) "rol" -> funcRolRor(call) "ror" -> funcRolRor(call) "rol2" -> funcRolRor(call) "ror2" -> funcRolRor(call) "prog8_lib_stringcompare" -> funcStringCompare(call) "prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE) "prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD) "prog8_lib_arraycopy" -> funcArrayCopy(call) else -> throw AssemblyError("missing builtinfunc for ${call.name}") } } private fun funcArrayCopy(call: PtBuiltinFunctionCall): ExpressionCodeResult { val source = call.args[0] as PtIdentifier val target = call.args[1] as PtIdentifier val sourceLength = codeGen.symbolTable.getLength(source.name)!! val targetLength = codeGen.symbolTable.getLength(target.name)!! require(sourceLength==targetLength) val result = mutableListOf() if(source.type in SplitWordArrayTypes && target.type in SplitWordArrayTypes) { // split words -> split words, copy lsb and msb arrays separately val fromReg = codeGen.registers.nextFree() val toReg = codeGen.registers.nextFree() val countReg = codeGen.registers.nextFree() result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name+"_lsb") it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name+"_lsb") it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=countReg, immediate = sourceLength) it += codeGen.makeSyscall(IMSyscall.MEMCOPY, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to toReg, IRDataType.WORD to countReg), returns = null) it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name+"_msb") it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name+"_msb") it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=countReg, immediate = sourceLength) it += codeGen.makeSyscall(IMSyscall.MEMCOPY, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to toReg, IRDataType.WORD to countReg), returns = null) } return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1) } else if(source.type in SplitWordArrayTypes) { // split -> normal words require(target.type==DataType.ARRAY_UW || target.type==DataType.ARRAY_W) TODO("split array to normal array copy $source -> $target") } else if(target.type in SplitWordArrayTypes) { // normal -> split words require(source.type==DataType.ARRAY_UW || source.type==DataType.ARRAY_W) TODO("normal array to split array copy $source -> $target") } else { // normal array to array copy (various element types) val fromReg = codeGen.registers.nextFree() val toReg = codeGen.registers.nextFree() val countReg = codeGen.registers.nextFree() val eltsize = codeGen.options.compTarget.memorySize(source.type) result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=fromReg, labelSymbol = source.name) it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=toReg, labelSymbol = target.name) it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=countReg, immediate = sourceLength * eltsize) } result += codeGen.makeSyscall(IMSyscall.MEMCOPY, listOf(IRDataType.WORD to fromReg, IRDataType.WORD to toReg, IRDataType.WORD to countReg), returns = null) return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1) } } private fun funcSquare(call: PtBuiltinFunctionCall, resultType: IRDataType): ExpressionCodeResult { val result = mutableListOf() val valueTr = exprGen.translateExpression(call.args[0]) addToResult(result, valueTr, valueTr.resultReg, valueTr.resultFpReg) return if(resultType==IRDataType.FLOAT) { val resultFpReg = codeGen.registers.nextFreeFloat() addInstr(result, IRInstruction(Opcode.SQUARE, resultType, fpReg1 = resultFpReg, fpReg2 = valueTr.resultFpReg), null) ExpressionCodeResult(result, resultType, -1, resultFpReg) } else { val resultReg = codeGen.registers.nextFree() addInstr(result, IRInstruction(Opcode.SQUARE, resultType, reg1 = resultReg, reg2 = valueTr.resultReg), null) ExpressionCodeResult(result, resultType, resultReg, -1) } } private fun funcCall(call: PtBuiltinFunctionCall): ExpressionCodeResult { val result = mutableListOf() val addressTr = exprGen.translateExpression(call.args[0]) addToResult(result, addressTr, addressTr.resultReg, -1) addInstr(result, IRInstruction(Opcode.CALLI, reg1 = addressTr.resultReg), null) if(call.void) return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1) else return ExpressionCodeResult(result, IRDataType.WORD, codeGen.registers.nextFree(), -1) // TODO actually the result is returned in CPU registers AY... } private fun funcCallfar(call: PtBuiltinFunctionCall): ExpressionCodeResult { val result = mutableListOf() addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null) val bankTr = exprGen.translateExpression(call.args[0]) val addressTr = exprGen.translateExpression(call.args[1]) val argumentwordTr = exprGen.translateExpression(call.args[2]) addToResult(result, bankTr, bankTr.resultReg, -1) addToResult(result, addressTr, addressTr.resultReg, -1) addToResult(result, argumentwordTr, argumentwordTr.resultReg, -1) result += codeGen.makeSyscall(IMSyscall.CALLFAR, listOf(IRDataType.BYTE to bankTr.resultReg, IRDataType.WORD to addressTr.resultReg, IRDataType.WORD to argumentwordTr.resultReg), IRDataType.WORD to argumentwordTr.resultReg) return ExpressionCodeResult(result, IRDataType.WORD, argumentwordTr.resultReg, -1) } private fun funcDivmod(call: PtBuiltinFunctionCall, type: IRDataType): ExpressionCodeResult { val result = mutableListOf() val number = call.args[0] val divident = call.args[1] val divisionReg: Int val remainderReg: Int if(divident is PtNumber) { val tr = exprGen.translateExpression(number) addToResult(result, tr, tr.resultReg, -1) addInstr(result, IRInstruction(Opcode.DIVMOD, type, reg1 = tr.resultReg, immediate = divident.number.toInt()), null) divisionReg = tr.resultReg remainderReg = codeGen.registers.nextFree() } else { val numTr = exprGen.translateExpression(number) addToResult(result, numTr, numTr.resultReg, -1) val dividentTr = exprGen.translateExpression(divident) addToResult(result, dividentTr, dividentTr.resultReg, -1) addInstr(result, IRInstruction(Opcode.DIVMODR, type, reg1 = numTr.resultReg, reg2=dividentTr.resultReg), null) divisionReg = numTr.resultReg remainderReg = dividentTr.resultReg } // DIVMOD result convention: on value stack, division and remainder on top. addInstr(result, IRInstruction(Opcode.POP, type, reg1=remainderReg), null) addInstr(result, IRInstruction(Opcode.POP, type, reg1=divisionReg), null) result += assignRegisterTo(call.args[2], divisionReg) result += assignRegisterTo(call.args[3], remainderReg) return ExpressionCodeResult(result, type, -1, -1) } private fun funcStringCompare(call: PtBuiltinFunctionCall): ExpressionCodeResult { val result = mutableListOf() addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null) val left = exprGen.translateExpression(call.args[0]) val right = exprGen.translateExpression(call.args[1]) addToResult(result, left, left.resultReg, -1) addToResult(result, right, right.resultReg, -1) result += codeGen.makeSyscall(IMSyscall.COMPARE_STRINGS, listOf(IRDataType.WORD to left.resultReg, IRDataType.WORD to right.resultReg), IRDataType.BYTE to left.resultReg) return ExpressionCodeResult(result, IRDataType.BYTE, left.resultReg, -1) } private fun funcCmp(call: PtBuiltinFunctionCall): ExpressionCodeResult { val result = mutableListOf() val leftTr = exprGen.translateExpression(call.args[0]) addToResult(result, leftTr, leftTr.resultReg, -1) val rightTr = exprGen.translateExpression(call.args[1]) addToResult(result, rightTr, rightTr.resultReg, -1) val dt = irType(call.args[0].type) result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.CMP, dt, reg1=leftTr.resultReg, reg2=rightTr.resultReg) } return ExpressionCodeResult(result, dt, leftTr.resultReg, -1) } private fun funcAny(call: PtBuiltinFunctionCall): ExpressionCodeResult { val arrayName = call.args[0] as PtIdentifier val arrayLength = codeGen.symbolTable.getLength(arrayName.name) val result = mutableListOf() val lengthReg = codeGen.registers.nextFree() if(arrayName.type in SplitWordArrayTypes) { // any(lsb-array) or any(msb-array) addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null) val trLsb = exprGen.translateExpression(PtIdentifier(arrayName.name+"_lsb", DataType.ARRAY_UB, call.position)) addToResult(result, trLsb, trLsb.resultReg, -1) addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null) result += codeGen.makeSyscall(IMSyscall.ANY_BYTE, listOf(IRDataType.WORD to trLsb.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to trLsb.resultReg) val shortcircuitLabel = codeGen.createLabelName() result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = trLsb.resultReg, immediate = 0) it += IRInstruction(Opcode.BSTNE, labelSymbol = shortcircuitLabel) it += IRInstruction(Opcode.PREPARECALL, immediate = 2) } val trMsb = exprGen.translateExpression(PtIdentifier(arrayName.name+"_msb", DataType.ARRAY_UB, call.position)) addToResult(result, trMsb, trMsb.resultReg, -1) addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null) result += codeGen.makeSyscall(IMSyscall.ANY_BYTE, listOf(IRDataType.WORD to trMsb.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to trMsb.resultReg) addInstr(result, IRInstruction(Opcode.ORR, IRDataType.BYTE, reg1=trLsb.resultReg, reg2=trMsb.resultReg), null) result += IRCodeChunk(shortcircuitLabel, null) return ExpressionCodeResult(result, IRDataType.BYTE, trLsb.resultReg, -1) } val syscall = when (arrayName.type) { DataType.ARRAY_UB, DataType.ARRAY_B -> IMSyscall.ANY_BYTE DataType.ARRAY_UW, DataType.ARRAY_W -> IMSyscall.ANY_WORD DataType.ARRAY_F -> IMSyscall.ANY_FLOAT else -> throw IllegalArgumentException("weird type") } addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null) val tr = exprGen.translateExpression(arrayName) addToResult(result, tr, tr.resultReg, -1) addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null) result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg) return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1) } private fun funcAll(call: PtBuiltinFunctionCall): ExpressionCodeResult { val arrayName = call.args[0] as PtIdentifier val arrayLength = codeGen.symbolTable.getLength(arrayName.name) if(arrayName.type in SplitWordArrayTypes) { // this is a bit complicated to calculate.... have to check all recombined (lsb,msb) words for $0000 TODO("all(split words $arrayName)") } val syscall = when(arrayName.type) { DataType.ARRAY_UB, DataType.ARRAY_B -> IMSyscall.ALL_BYTE DataType.ARRAY_UW, DataType.ARRAY_W -> IMSyscall.ALL_WORD DataType.ARRAY_F -> IMSyscall.ALL_FLOAT else -> throw IllegalArgumentException("weird type") } val result = mutableListOf() addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null) val tr = exprGen.translateExpression(arrayName) addToResult(result, tr, tr.resultReg, -1) val lengthReg = codeGen.registers.nextFree() addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = arrayLength), null) result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to tr.resultReg) return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1) } private fun funcAbs(call: PtBuiltinFunctionCall): ExpressionCodeResult { val sourceDt = call.args.single().type val result = mutableListOf() if(sourceDt==DataType.UWORD) return ExpressionCodeResult.EMPTY val tr = exprGen.translateExpression(call.args[0]) addToResult(result, tr, tr.resultReg, -1) when (sourceDt) { DataType.BYTE -> { val notNegativeLabel = codeGen.createLabelName() val compareReg = codeGen.registers.nextFree() result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.LOADR, IRDataType.BYTE, reg1=compareReg, reg2=tr.resultReg) it += IRInstruction(Opcode.BSTPOS, labelSymbol = notNegativeLabel) it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1=tr.resultReg) } result += IRCodeChunk(notNegativeLabel, null) return ExpressionCodeResult(result, IRDataType.BYTE, tr.resultReg, -1) } DataType.WORD -> { val notNegativeLabel = codeGen.createLabelName() val compareReg = codeGen.registers.nextFree() result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.LOADR, IRDataType.WORD, reg1=compareReg, reg2=tr.resultReg) it += IRInstruction(Opcode.BSTPOS, labelSymbol = notNegativeLabel) it += IRInstruction(Opcode.NEG, IRDataType.WORD, reg1=tr.resultReg) } result += IRCodeChunk(notNegativeLabel, null) return ExpressionCodeResult(result, IRDataType.WORD, tr.resultReg, -1) } DataType.FLOAT -> { val resultFpReg = codeGen.registers.nextFreeFloat() addInstr(result, IRInstruction(Opcode.FABS, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg), null) return ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpReg) } else -> throw AssemblyError("weird dt") } } private fun funcSgn(call: PtBuiltinFunctionCall): ExpressionCodeResult { val result = mutableListOf() val tr = exprGen.translateExpression(call.args.single()) val resultReg = codeGen.registers.nextFree() if(tr.dt==IRDataType.FLOAT) { addToResult(result, tr, -1, tr.resultFpReg) result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.SGN, tr.dt, reg1 = resultReg, fpReg1 = tr.resultFpReg) } } else { addToResult(result, tr, tr.resultReg, -1) result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.SGN, tr.dt, reg1 = resultReg, reg2 = tr.resultReg) } } return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1) } private fun funcSqrt(call: PtBuiltinFunctionCall): ExpressionCodeResult { val result = mutableListOf() val tr = exprGen.translateExpression(call.args.single()) val dt = call.args[0].type when(dt) { DataType.UBYTE -> { addToResult(result, tr, tr.resultReg, -1) val resultReg = codeGen.registers.nextFree() result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.SQRT, IRDataType.BYTE, reg1=resultReg, reg2=tr.resultReg) } return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1) } DataType.UWORD -> { addToResult(result, tr, tr.resultReg, -1) val resultReg = codeGen.registers.nextFree() result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultReg, reg2=tr.resultReg) } return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1) } DataType.FLOAT -> { addToResult(result, tr, -1, tr.resultFpReg) val resultFpReg = codeGen.registers.nextFreeFloat() result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.SQRT, IRDataType.FLOAT, fpReg1 = resultFpReg, fpReg2 = tr.resultFpReg) } return ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpReg) } else -> throw AssemblyError("invalid dt") } } private fun funcReverse(call: PtBuiltinFunctionCall): ExpressionCodeResult { val arrayName = call.args[0] as PtIdentifier val arrayLength = codeGen.symbolTable.getLength(arrayName.name) val lengthReg = codeGen.registers.nextFree() val result = mutableListOf() if(arrayName.type in SplitWordArrayTypes) { // reverse the lsb and msb arrays both, independently addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null) val trLsb = exprGen.translateExpression(PtIdentifier(arrayName.name+"_lsb", DataType.ARRAY_UB, call.position)) addToResult(result, trLsb, trLsb.resultReg, -1) addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null) result += codeGen.makeSyscall(IMSyscall.REVERSE_BYTES, listOf(IRDataType.WORD to trLsb.resultReg, IRDataType.BYTE to lengthReg), null) val trMsb = exprGen.translateExpression(PtIdentifier(arrayName.name+"_msb", DataType.ARRAY_UB, call.position)) addToResult(result, trMsb, trMsb.resultReg, -1) addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null) result += codeGen.makeSyscall(IMSyscall.REVERSE_BYTES, listOf(IRDataType.WORD to trMsb.resultReg, IRDataType.BYTE to lengthReg), null) return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1) } val syscall = when(arrayName.type) { DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> IMSyscall.REVERSE_BYTES DataType.ARRAY_UW, DataType.ARRAY_W -> IMSyscall.REVERSE_WORDS DataType.ARRAY_F -> IMSyscall.REVERSE_FLOATS else -> throw IllegalArgumentException("weird type to reverse") } addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null) val tr = exprGen.translateExpression(arrayName) addToResult(result, tr, tr.resultReg, -1) addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null) result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), null) return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1) } private fun funcSort(call: PtBuiltinFunctionCall): ExpressionCodeResult { val arrayName = call.args[0] as PtIdentifier val arrayLength = codeGen.symbolTable.getLength(arrayName.name) val syscall = when(arrayName.type) { DataType.ARRAY_UB -> IMSyscall.SORT_UBYTE DataType.ARRAY_B -> IMSyscall.SORT_BYTE DataType.ARRAY_UW -> IMSyscall.SORT_UWORD DataType.ARRAY_W -> IMSyscall.SORT_WORD DataType.STR -> IMSyscall.SORT_UBYTE DataType.ARRAY_F -> throw IllegalArgumentException("sorting a floating point array is not supported") in SplitWordArrayTypes -> TODO("split word sort") else -> throw IllegalArgumentException("weird type to sort") } val result = mutableListOf() addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null) val tr = exprGen.translateExpression(arrayName) addToResult(result, tr, tr.resultReg, -1) val lengthReg = codeGen.registers.nextFree() addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = lengthReg, immediate = if(arrayName.type==DataType.STR) arrayLength!!-1 else arrayLength), null) result += codeGen.makeSyscall(syscall, listOf(IRDataType.WORD to tr.resultReg, IRDataType.BYTE to lengthReg), null) return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1) } private fun funcMkword(call: PtBuiltinFunctionCall): ExpressionCodeResult { val result = mutableListOf() val resultReg = codeGen.registers.nextFree() if((call.args[0] as? PtNumber)?.number == 0.0) { // msb is 0, use EXT val lsbTr = exprGen.translateExpression(call.args[1]) addToResult(result, lsbTr, lsbTr.resultReg, -1) addInstr(result, IRInstruction(Opcode.EXT, IRDataType.BYTE, reg1=resultReg, reg2 = lsbTr.resultReg), null) } else { val msbTr = exprGen.translateExpression(call.args[0]) addToResult(result, msbTr, msbTr.resultReg, -1) val lsbTr = exprGen.translateExpression(call.args[1]) addToResult(result, lsbTr, lsbTr.resultReg, -1) addInstr(result, IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=resultReg, reg2 = msbTr.resultReg, reg3 = lsbTr.resultReg), null) } return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1) } private fun funcClamp(call: PtBuiltinFunctionCall): ExpressionCodeResult { val result = mutableListOf() addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null) val type = irType(call.type) val valueTr = exprGen.translateExpression(call.args[0]) val minimumTr = exprGen.translateExpression(call.args[1]) val maximumTr = exprGen.translateExpression(call.args[2]) result += valueTr.chunks result += minimumTr.chunks result += maximumTr.chunks if(type==IRDataType.FLOAT) { result += codeGen.makeSyscall( IMSyscall.CLAMP_FLOAT, listOf( valueTr.dt to valueTr.resultFpReg, minimumTr.dt to minimumTr.resultFpReg, maximumTr.dt to maximumTr.resultFpReg, ), type to valueTr.resultFpReg ) return ExpressionCodeResult(result, type, -1, valueTr.resultFpReg) } else { val syscall = when(call.type) { DataType.UBYTE -> IMSyscall.CLAMP_UBYTE DataType.BYTE -> IMSyscall.CLAMP_BYTE DataType.UWORD -> IMSyscall.CLAMP_UWORD DataType.WORD -> IMSyscall.CLAMP_WORD else -> throw AssemblyError("invalid dt") } result += codeGen.makeSyscall(syscall, listOf( valueTr.dt to valueTr.resultReg, minimumTr.dt to minimumTr.resultReg, maximumTr.dt to maximumTr.resultReg, ), type to valueTr.resultReg ) return ExpressionCodeResult(result, type, valueTr.resultReg, -1) } } private fun funcMin(call: PtBuiltinFunctionCall): ExpressionCodeResult { val type = irType(call.type) val result = mutableListOf() val leftTr = exprGen.translateExpression(call.args[0]) addToResult(result, leftTr, leftTr.resultReg, -1) val rightTr = exprGen.translateExpression(call.args[1]) addToResult(result, rightTr, rightTr.resultReg, -1) val comparisonOpcode = if(call.type in SignedDatatypes) Opcode.BGTSR else Opcode.BGTR val after = codeGen.createLabelName() result += IRCodeChunk(null, null).also { it += IRInstruction(comparisonOpcode, type, reg1 = rightTr.resultReg, reg2 = leftTr.resultReg, labelSymbol = after) // right <= left, take right it += IRInstruction(Opcode.LOADR, type, reg1=leftTr.resultReg, reg2=rightTr.resultReg) it += IRInstruction(Opcode.JUMP, labelSymbol = after) } result += IRCodeChunk(after, null) return ExpressionCodeResult(result, type, leftTr.resultReg, -1) } private fun funcMax(call: PtBuiltinFunctionCall): ExpressionCodeResult { val type = irType(call.type) val result = mutableListOf() val leftTr = exprGen.translateExpression(call.args[0]) addToResult(result, leftTr, leftTr.resultReg, -1) val rightTr = exprGen.translateExpression(call.args[1]) addToResult(result, rightTr, rightTr.resultReg, -1) val comparisonOpcode = if(call.type in SignedDatatypes) Opcode.BGTSR else Opcode.BGTR val after = codeGen.createLabelName() result += IRCodeChunk(null, null).also { it += IRInstruction(comparisonOpcode, type, reg1 = leftTr.resultReg, reg2 = rightTr.resultReg, labelSymbol = after) // right >= left, take right it += IRInstruction(Opcode.LOADR, type, reg1=leftTr.resultReg, reg2=rightTr.resultReg) it += IRInstruction(Opcode.JUMP, labelSymbol = after) } result += IRCodeChunk(after, null) return ExpressionCodeResult(result, type, leftTr.resultReg, -1) } private fun funcPoke(call: PtBuiltinFunctionCall, dt: IRDataType): ExpressionCodeResult { val result = mutableListOf() if(codeGen.isZero(call.args[1])) { if (call.args[0] is PtNumber) { val address = (call.args[0] as PtNumber).number.toInt() result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, dt, address = address) } } else { val tr = exprGen.translateExpression(call.args[0]) addToResult(result, tr, tr.resultReg, -1) result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZI, dt, reg1 = tr.resultReg) } } } else { if (call.args[0] is PtNumber) { val address = (call.args[0] as PtNumber).number.toInt() val tr = exprGen.translateExpression(call.args[1]) if(dt==IRDataType.FLOAT) { addToResult(result, tr, -1, tr.resultFpReg) result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, dt, fpReg1 = tr.resultFpReg, address = address) } } else { addToResult(result, tr, tr.resultReg, -1) result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, dt, reg1 = tr.resultReg, address = address) } } } else { val addressTr = exprGen.translateExpression(call.args[0]) addToResult(result, addressTr, addressTr.resultReg, -1) val valueTr = exprGen.translateExpression(call.args[1]) if(dt==IRDataType.FLOAT) { addToResult(result, valueTr, -1, valueTr.resultFpReg) result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREI, IRDataType.FLOAT, reg1 = addressTr.resultReg, fpReg1 = valueTr.resultFpReg) } } else { addToResult(result, valueTr, valueTr.resultReg, -1) result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREI, dt, reg1 = valueTr.resultReg, reg2 = addressTr.resultReg) } } } } return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1) } private fun funcPeek(call: PtBuiltinFunctionCall, dt: IRDataType): ExpressionCodeResult { val result = mutableListOf() return if(dt==IRDataType.FLOAT) { if(call.args[0] is PtNumber) { val resultFpRegister = codeGen.registers.nextFreeFloat() val address = (call.args[0] as PtNumber).number.toInt() result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1 = resultFpRegister, address = address) } ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpRegister) } else { val tr = exprGen.translateExpression(call.args.single()) addToResult(result, tr, tr.resultReg, -1) val resultFpReg = codeGen.registers.nextFreeFloat() result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.LOADI, IRDataType.FLOAT, reg1 = tr.resultReg, fpReg1 = resultFpReg) } ExpressionCodeResult(result, IRDataType.FLOAT, -1, resultFpReg) } } else { if (call.args[0] is PtNumber) { val resultRegister = codeGen.registers.nextFree() val address = (call.args[0] as PtNumber).number.toInt() result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.LOADM, dt, reg1 = resultRegister, address = address) } ExpressionCodeResult(result, dt, resultRegister, -1) } else { val tr = exprGen.translateExpression(call.args.single()) addToResult(result, tr, tr.resultReg, -1) val resultReg = codeGen.registers.nextFree() result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.LOADI, dt, reg1 = resultReg, reg2 = tr.resultReg) } ExpressionCodeResult(result, dt, resultReg, -1) } } } private fun funcPokemon(call: PtBuiltinFunctionCall): ExpressionCodeResult { val result = mutableListOf() val address = call.args[0] fun pokeM(result: MutableList, address: Int, value: PtExpression) { if(codeGen.isZero(value)) { result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, address = address) } } else { val tr = exprGen.translateExpression(value) addToResult(result, tr, tr.resultReg, -1) result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = tr.resultReg, address = address) } } } fun pokeI(result: MutableList, register: Int, value: PtExpression) { if(codeGen.isZero(value)) { result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg1 = register) } } else { val valueTr = exprGen.translateExpression(value) addToResult(result, valueTr, valueTr.resultReg, -1) result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREI, IRDataType.BYTE, reg1 = valueTr.resultReg, reg2 = register) } } } return if(address is PtNumber) { val resultRegister = codeGen.registers.nextFree() val addressNum = address.number.toInt() result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1 = resultRegister, address = addressNum) } pokeM(result, addressNum, call.args[1]) ExpressionCodeResult(result, IRDataType.BYTE, resultRegister, -1) } else { val addressTr = exprGen.translateExpression(address) addToResult(result, addressTr, addressTr.resultReg, -1) val resultReg = codeGen.registers.nextFree() result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.LOADI, IRDataType.BYTE, reg1 = resultReg, reg2 = addressTr.resultReg) } pokeI(result, addressTr.resultReg, call.args[1]) ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1) } } private fun funcMemory(call: PtBuiltinFunctionCall): ExpressionCodeResult { val name = (call.args[0] as PtString).value val code = IRCodeChunk(null, null) val resultReg = codeGen.registers.nextFree() code += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1=resultReg, labelSymbol = "prog8_slabs.prog8_memoryslab_$name") return ExpressionCodeResult(code, IRDataType.BYTE, resultReg, -1) } private fun funcLsb(call: PtBuiltinFunctionCall): ExpressionCodeResult { return exprGen.translateExpression(call.args.single()) // note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here. // ....To be more strict, maybe we should introduce a new result register that is of type .b? } private fun funcMsb(call: PtBuiltinFunctionCall): ExpressionCodeResult { val result = mutableListOf() val tr = exprGen.translateExpression(call.args.single()) addToResult(result, tr, tr.resultReg, -1) val resultReg = codeGen.registers.nextFree() addInstr(result, IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = resultReg, reg2 = tr.resultReg), null) // note: if a word result is needed, the upper byte is cleared by the typecast that follows. No need to do it here. return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1) } private fun funcRolRor(call: PtBuiltinFunctionCall): ExpressionCodeResult { val result = mutableListOf() val arg = call.args[0] val vmDt = irType(arg.type) val opcodeMemAndReg = when(call.name) { "rol" -> Opcode.ROXLM to Opcode.ROXL "ror" -> Opcode.ROXRM to Opcode.ROXR "rol2" -> Opcode.ROLM to Opcode.ROL "ror2" -> Opcode.RORM to Opcode.ROR else -> throw AssemblyError("wrong func") } val ident = arg as? PtIdentifier if(ident!=null) { addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, labelSymbol = ident.name), null) return ExpressionCodeResult(result, vmDt, -1, -1) } val memAddr = (arg as? PtMemoryByte)?.address?.asConstInteger() if(memAddr!=null) { addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, address = memAddr), null) return ExpressionCodeResult(result, vmDt, -1, -1) } val arr = (arg as? PtArrayIndexer) val index = arr?.index?.asConstInteger() if(arr!=null && index!=null) { val variable = arr.variable.name val itemsize = codeGen.program.memsizer.memorySize(arr.type) addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, labelSymbol = variable, symbolOffset = index*itemsize), null) return ExpressionCodeResult(result, vmDt, -1, -1) } val opcode = opcodeMemAndReg.second val saveCarry = opcode in OpcodesThatDependOnCarry && !arg.isSimple() if(saveCarry) addInstr(result, IRInstruction(Opcode.PUSHST), null) // save Carry val tr = exprGen.translateExpression(arg) addToResult(result, tr, tr.resultReg, -1) if(saveCarry) addInstr(result, IRInstruction(Opcode.POPST), null) addInstr(result, IRInstruction(opcode, vmDt, reg1 = tr.resultReg), null) if(saveCarry) addInstr(result, IRInstruction(Opcode.PUSHST), null) // save Carry result += assignRegisterTo(arg, tr.resultReg) if(saveCarry) addInstr(result, IRInstruction(Opcode.POPST), null) return ExpressionCodeResult(result, vmDt, -1, -1) } private fun funcSetLsbMsb(call: PtBuiltinFunctionCall, msb: Boolean): ExpressionCodeResult { val result = mutableListOf() val target = call.args[0] val isConstZeroValue = call.args[1].asConstInteger()==0 when(target) { is PtIdentifier -> { if(isConstZeroValue) { result += IRCodeChunk(null, null).also { val pointerReg = codeGen.registers.nextFree() it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1 = pointerReg, labelSymbol = target.name) if (msb) it += IRInstruction(Opcode.INC, IRDataType.WORD, reg1 = pointerReg) it += IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg1 = pointerReg) } } else { val valueTr = exprGen.translateExpression(call.args[1]) addToResult(result, valueTr, valueTr.resultReg, -1) result += IRCodeChunk(null, null).also { val pointerReg = codeGen.registers.nextFree() it += IRInstruction(Opcode.LOAD, IRDataType.WORD, reg1 = pointerReg, labelSymbol = target.name) if (msb) it += IRInstruction(Opcode.INC, IRDataType.WORD, reg1 = pointerReg) it += IRInstruction(Opcode.STOREI, IRDataType.BYTE, reg1 = valueTr.resultReg, reg2 = pointerReg) } } } is PtArrayIndexer -> { if(target.splitWords) { // lsb/msb in split arrays, element index 'size' is always 1 val constIndex = target.index.asConstInteger() val varName = target.variable.name + if(msb) "_msb" else "_lsb" if(isConstZeroValue) { if(constIndex!=null) { val offsetReg = codeGen.registers.nextFree() result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = constIndex) it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=offsetReg, labelSymbol = varName) } } else { val indexTr = exprGen.translateExpression(target.index) addToResult(result, indexTr, indexTr.resultReg, -1) result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexTr.resultReg, labelSymbol = varName) } } } else { val valueTr = exprGen.translateExpression(call.args[1]) addToResult(result, valueTr, valueTr.resultReg, -1) if(constIndex!=null) { val offsetReg = codeGen.registers.nextFree() result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = constIndex) it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = varName) } } else { val indexTr = exprGen.translateExpression(target.index) addToResult(result, indexTr, indexTr.resultReg, -1) result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexTr.resultReg, labelSymbol = varName) } } } } else { val eltSize = codeGen.program.memsizer.memorySize(target.type) val constIndex = target.index.asConstInteger() if(isConstZeroValue) { if(constIndex!=null) { val offsetReg = codeGen.registers.nextFree() val offset = eltSize*constIndex + if(msb) 1 else 0 result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = offset) it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=offsetReg, labelSymbol = target.variable.name) } } else { val indexTr = exprGen.translateExpression(target.index) addToResult(result, indexTr, indexTr.resultReg, -1) result += IRCodeChunk(null, null).also { if(eltSize>1) it += codeGen.multiplyByConst(IRDataType.BYTE, indexTr.resultReg, eltSize) if(msb) it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg) it += IRInstruction(Opcode.STOREZX, IRDataType.BYTE, reg1=indexTr.resultReg, labelSymbol = target.variable.name) } } } else { val valueTr = exprGen.translateExpression(call.args[1]) addToResult(result, valueTr, valueTr.resultReg, -1) if(constIndex!=null) { val offsetReg = codeGen.registers.nextFree() val offset = eltSize*constIndex + if(msb) 1 else 0 result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = offset) it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=offsetReg, labelSymbol = target.variable.name) } } else { val indexTr = exprGen.translateExpression(target.index) addToResult(result, indexTr, indexTr.resultReg, -1) result += IRCodeChunk(null, null).also { if(eltSize>1) it += codeGen.multiplyByConst(IRDataType.BYTE, indexTr.resultReg, eltSize) if(msb) it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=indexTr.resultReg) it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=valueTr.resultReg, reg2=indexTr.resultReg, labelSymbol = target.variable.name) } } } } } else -> throw AssemblyError("weird target for setlsb/setmsb: $target") } return ExpressionCodeResult(result, IRDataType.WORD, -1, -1) } private fun assignRegisterTo(target: PtExpression, register: Int): IRCodeChunks { val assignment = PtAssignment(target.position) val assignTarget = PtAssignTarget(target.position) assignTarget.children.add(target) assignment.children.add(assignTarget) assignment.children.add(PtMachineRegister(register, target.type, target.position)) val result = mutableListOf() result += codeGen.translateNode(assignment) return result } }