Compare commits

...
26 Commits
Author SHA1 Message Date
Irmen de Jong 2d7e95e1b6 release 9.2.1 2023-07-30 17:39:18 +02:00
Irmen de Jong 0816a57032 never add rts to inline asmsubs and always inline them regardless of optimization setting
otherwise they can't specify a sequence of assembly instructions that should be inserted in-place, such as those that manipulate the cpu stack.
for instance cx16.irqsafe_set_irqd() / cx16.irqsafe_clear_irqd()
2023-07-30 14:52:37 +02:00
Irmen de Jong b89ad4b328 don't optimize empty where choice away! It would call the else clause incorrectly. 2023-07-29 18:25:52 +02:00
Irmen de Jong c112b327ab tiny optimization 2023-07-29 17:04:41 +02:00
Irmen de Jong b19c282269 release 9.2 2023-07-28 01:40:14 +02:00
Irmen de Jong e520921746 todo 2023-07-26 23:16:43 +02:00
Irmen de Jong 970642244b optimized gfx2.text() for hires 4c mode 2023-07-26 04:17:44 +02:00
Irmen de Jong 3b90be2d9e gfx2.text() per-pixel positioning implemented for screen modes 1 and 5 2023-07-25 00:43:45 +02:00
Irmen de Jong 2f756f1e3a fix and optimize inplace invert and negate 2023-07-24 23:28:32 +02:00
Irmen de Jong 78e84182f0 todo 2023-07-24 22:36:17 +02:00
Irmen de Jong 65a7a8caf8 fix and optimize gfx2.position2(), added cx16.vaddr_clone() 2023-07-24 00:04:47 +02:00
Irmen de Jong 4c6a2f5df9 emphasize index value size on pointer var indexing 2023-07-23 00:11:18 +02:00
Irmen de Jong 3bbc00cc8c more caution notices about symbols in inlined asm 2023-07-22 23:22:06 +02:00
Irmen de Jong 0e781d18fa cx16: added cx16.vaddr_autoincr() and cx16.vaddr_autodecr() 2023-07-21 23:04:21 +02:00
Irmen de Jong 4575a8fffe cx16: added cx16.vaddr_autoincr() and cx16.vaddr_autodecr() 2023-07-21 22:40:07 +02:00
Irmen de Jong 10d0ff252b ignore buildversion changes 2023-07-21 00:14:06 +02:00
Irmen de Jong c7d54570cc IR: sXX, CONCAT instructions now use 3 register format 2023-07-21 00:07:56 +02:00
Irmen de Jong 7136b33f2e cx16: change reset_system() to use Reset SMC sequence instead of hard reboot 2023-07-20 01:59:20 +02:00
Irmen de Jong 5659742d97 fixed assigning byte to word not clearing msb sometimes 2023-07-16 23:16:32 +02:00
Irmen de Jong 450eaf7c4a fixed lsb() to uword problem 2023-07-16 20:05:59 +02:00
Irmen de Jong c1aa5d4e47 IR: optimized when statement translation 2023-07-16 12:10:46 +02:00
Irmen de Jong b3cb9b7fe2 added optimizer to remove needless pha/pla pairs 2023-07-15 22:19:48 +02:00
Irmen de Jong 9cb61fa34d tweaks 2023-07-15 20:46:14 +02:00
Irmen de Jong 7c219d235c fixed possible type mismatch error in when statements 2023-07-14 23:35:58 +02:00
Irmen de Jong 6938c79f88 IR: added CMPI instruction 2023-07-14 23:17:29 +02:00
Irmen de Jong b8284a147d allow boolean when conditions, optimize into a regular if 2023-07-11 21:33:29 +02:00
34 changed files with 902 additions and 308 deletions

No files matched your search

+1
View File
@@ -30,6 +30,7 @@ parsetab.py
compiler/lib/
.gradle
**/BuildVersion.kt
/prog8compiler.jar
sd*.img
*.d64
@@ -43,7 +43,14 @@ internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefin
numberOfOptimizations++
}
mods= optimizeJsrRtsAndOtherCombinations(linesByFour)
mods = optimizeJsrRtsAndOtherCombinations(linesByFour)
if(mods.isNotEmpty()) {
apply(mods, lines)
linesByFour = getLinesBy(lines, 4)
numberOfOptimizations++
}
mods = optimizeUselessPushPopStack(linesByFour)
if(mods.isNotEmpty()) {
apply(mods, lines)
linesByFour = getLinesBy(lines, 4)
@@ -517,3 +524,40 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: List<List<IndexedVal
}
return mods
}
private fun optimizeUselessPushPopStack(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
val mods = mutableListOf<Modification>()
fun optimize(register: Char, lines: List<IndexedValue<String>>) {
if(lines[0].value.trimStart().startsWith("ph$register")) {
if(lines[2].value.trimStart().startsWith("pl$register")) {
val second = lines[1].value.trimStart().take(6).lowercase()
if(register!in second
&& !second.startsWith("jsr")
&& !second.startsWith("pl")
&& !second.startsWith("ph")) {
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[2].index, true, null))
}
}
else if (lines[3].value.trimStart().startsWith("pl$register")) {
val second = lines[1].value.trimStart().take(6).lowercase()
val third = lines[2].value.trimStart().take(6).lowercase()
if(register !in second && register !in third
&& !second.startsWith("jsr") && !third.startsWith("jsr")
&& !second.startsWith("pl") && !third.startsWith("pl")
&& !second.startsWith("ph") && !third.startsWith("ph")) {
mods.add(Modification(lines[0].index, true, null))
mods.add(Modification(lines[3].index, true, null))
}
}
}
}
for (lines in linesByFour) {
optimize('a', lines)
optimize('x', lines)
optimize('y', lines)
}
return mods
}
@@ -1160,9 +1160,27 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
// and will not generate another cmp when lsb() is directly used inside a comparison expression.
}
RegisterOrPair.Y -> {
asmgen.out(" pha")
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
asmgen.out(" tay | pla | cpy #0")
asmgen.out(" tay | cpy #0")
}
RegisterOrPair.AY -> {
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY)
asmgen.out(" ldy #0 | cmp #0")
}
RegisterOrPair.AX -> {
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AX)
asmgen.out(" ldx #0 | cmp #0")
}
RegisterOrPair.XY -> {
asmgen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.XY)
asmgen.out(" ldy #0 | cpx #0")
}
in Cx16VirtualRegisters -> {
asmgen.assignExpressionToRegister(fcall.args.single(), resultRegister)
val zero = PtNumber(DataType.UBYTE, 0.0, Position.DUMMY)
zero.parent=fcall
assignAsmGen.assignExpressionToVariable(zero, "cx16.${resultRegister.toString().lowercase()}H", DataType.UBYTE)
asmgen.out(" lda cx16.r0L")
}
else -> throw AssemblyError("invalid reg")
}
@@ -63,8 +63,8 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
if(sub is PtAsmSub) {
argumentsViaRegisters(sub, call)
if (sub.inline && asmgen.options.optimize) {
// inline the subroutine.
if (sub.inline) {
// inline the subroutine. (regardless of optimization settings!)
// we do this by copying the subroutine's statements at the call site.
// NOTE: *if* there is a return statement, it will be the only one, and the very last statement of the subroutine
// (this condition has been enforced by an ast check earlier)
@@ -271,9 +271,7 @@ internal class ProgramAndVarsGen(
internal fun translateAsmSubroutine(sub: PtAsmSub) {
if(sub.inline) {
if(options.optimize) {
return
}
return // subroutine gets inlined at call site.
}
asmgen.out("")
@@ -45,12 +45,12 @@ internal class AssignmentAsmGen(private val program: PtProgram,
val variable = assign.source.asmVarname
when (assign.target.datatype) {
DataType.UBYTE, DataType.BYTE -> assignVariableByte(assign.target, variable)
DataType.WORD -> assignVariableWord(assign.target, variable)
DataType.WORD -> assignVariableWord(assign.target, variable, assign.source.datatype)
DataType.UWORD -> {
if(assign.source.datatype in PassByReferenceDatatypes)
assignAddressOf(assign.target, variable)
else
assignVariableWord(assign.target, variable)
assignVariableWord(assign.target, variable, assign.source.datatype)
}
DataType.FLOAT -> assignVariableFloat(assign.target, variable)
DataType.STR -> assignVariableString(assign.target, variable)
@@ -288,20 +288,86 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
is PtPrefix -> {
if(assign.target.array==null) {
// First assign the value to the target then apply the operator in place on the target.
// This saves a temporary variable
translateNormalAssignment(
AsmAssignment(
AsmAssignSource.fromAstSource(value.value, program, asmgen),
assign.target, program.memsizer, assign.position
), scope
)
when (value.operator) {
"+" -> {}
"-" -> inplaceNegate(assign, true, scope)
"~" -> inplaceInvert(assign, scope)
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
else -> throw AssemblyError("invalid prefix operator")
if(assign.source.datatype==assign.target.datatype) {
if(assign.source.datatype in IntegerDatatypes) {
val signed = assign.source.datatype in SignedDatatypes
if(assign.source.datatype in ByteDatatypes) {
assignExpressionToRegister(value.value, RegisterOrPair.A, signed)
when(value.operator) {
"+" -> {}
"-" -> {
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" eor #255 | ina")
else
asmgen.out(" eor #255 | clc | adc #1")
}
"~" -> asmgen.out(" eor #255")
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
else -> throw AssemblyError("invalid prefix operator")
}
assignRegisterByte(assign.target, CpuRegister.A, signed)
} else {
assignExpressionToRegister(value.value, RegisterOrPair.AY, signed)
when(value.operator) {
"+" -> {}
"-" -> {
asmgen.out("""
sec
eor #255
adc #0
pha
tya
eor #255
adc #0
tay
pla""")
}
"~" -> asmgen.out(" pha | tya | eor #255 | tay | pla | eor #255")
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
else -> throw AssemblyError("invalid prefix operator")
}
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
}
} else {
// First assign the value to the target then apply the operator in place on the target.
// This saves a temporary variable
translateNormalAssignment(
AsmAssignment(
AsmAssignSource.fromAstSource(value.value, program, asmgen),
assign.target, program.memsizer, assign.position
), scope
)
when (value.operator) {
"+" -> {}
"-" -> inplaceNegate(assign, true, scope)
"~" -> inplaceInvert(assign, scope)
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
else -> throw AssemblyError("invalid prefix operator")
}
}
} else {
// use a temporary variable
val tempvar = if(value.type in ByteDatatypes) "P8ZP_SCRATCH_B1" else "P8ZP_SCRATCH_W1"
assignExpressionToVariable(value.value, tempvar, value.type)
when (value.operator) {
"+" -> {}
"-", "~" -> {
val assignTempvar = AsmAssignment(
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, value.type, variableAsmName = tempvar),
AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, value.type, scope, assign.position, variableAsmName = tempvar),
program.memsizer, assign.position)
if(value.operator=="-")
inplaceNegate(assignTempvar, true, scope)
else
inplaceInvert(assignTempvar, scope)
}
"not" -> throw AssemblyError("not should have been replaced in the Ast by ==0")
else -> throw AssemblyError("invalid prefix operator")
}
if(value.type in ByteDatatypes)
assignVariableByte(assign.target, tempvar)
else
assignVariableWord(assign.target, tempvar, value.type)
}
} else {
assignPrefixedExpressionToArrayElt(assign, scope)
@@ -338,7 +404,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.translateNormalAssignment(assignToTempvar, scope)
when(assign.target.datatype) {
in ByteDatatypes -> assignVariableByte(assign.target, tempvar)
in WordDatatypes -> assignVariableWord(assign.target, tempvar)
in WordDatatypes -> assignVariableWord(assign.target, tempvar, assign.source.datatype)
DataType.FLOAT -> assignVariableFloat(assign.target, tempvar)
else -> throw AssemblyError("weird dt")
}
@@ -827,7 +893,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
DataType.UWORD -> {
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
asmgen.out(" jsr math.divmod_uw_asm")
assignVariableWord(assign.target, "P8ZP_SCRATCH_W2")
assignVariableWord(assign.target, "P8ZP_SCRATCH_W2", DataType.UWORD)
return true
}
else -> return false
@@ -2049,19 +2115,29 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
}
private fun assignVariableWord(target: AsmAssignTarget, sourceName: String) {
private fun assignVariableWord(target: AsmAssignTarget, sourceName: String, sourceDt: DataType) {
require(sourceDt in WordDatatypes || sourceDt==DataType.UBYTE)
when(target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
lda $sourceName
ldy $sourceName+1
sta ${target.asmVarname}
sty ${target.asmVarname}+1""")
if(sourceDt==DataType.UBYTE) {
asmgen.out(" lda $sourceName | sta ${target.asmVarname}")
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz ${target.asmVarname}")
else
asmgen.out(" lda #0 | sta ${target.asmVarname}")
}
else
asmgen.out("""
lda $sourceName
ldy $sourceName+1
sta ${target.asmVarname}
sty ${target.asmVarname}+1""")
}
TargetStorageKind.MEMORY -> {
throw AssemblyError("assign word to memory ${target.memory} should have gotten a typecast")
}
TargetStorageKind.ARRAY -> {
if(sourceDt==DataType.UBYTE) TODO("assign byte to word array")
target.array!!
if(target.constArrayIndexValue!=null) {
val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype).toUInt()
@@ -2137,29 +2213,53 @@ internal class AssignmentAsmGen(private val program: PtProgram,
}
}
TargetStorageKind.REGISTER -> {
when(target.register!!) {
RegisterOrPair.AX -> asmgen.out(" ldx $sourceName+1 | lda $sourceName")
RegisterOrPair.AY -> asmgen.out(" ldy $sourceName+1 | lda $sourceName")
RegisterOrPair.XY -> asmgen.out(" ldy $sourceName+1 | ldx $sourceName")
in Cx16VirtualRegisters -> {
asmgen.out(
"""
lda $sourceName
sta cx16.${target.register.toString().lowercase()}
lda $sourceName+1
sta cx16.${target.register.toString().lowercase()}+1
""")
if(sourceDt==DataType.UBYTE) {
when(target.register!!) {
RegisterOrPair.AX -> asmgen.out(" ldx #0 | lda $sourceName")
RegisterOrPair.AY -> asmgen.out(" ldy #0 | lda $sourceName")
RegisterOrPair.XY -> asmgen.out(" ldy #0 | ldx $sourceName")
in Cx16VirtualRegisters -> {
asmgen.out(" lda $sourceName | sta cx16.${target.register.toString().lowercase()}")
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz cx16.${target.register.toString().lowercase()}+1")
else
asmgen.out(" lda #0 | sta cx16.${target.register.toString().lowercase()}+1")
}
else -> throw AssemblyError("can't load word in a single 8-bit register")
}
} else {
when(target.register!!) {
RegisterOrPair.AX -> asmgen.out(" ldx $sourceName+1 | lda $sourceName")
RegisterOrPair.AY -> asmgen.out(" ldy $sourceName+1 | lda $sourceName")
RegisterOrPair.XY -> asmgen.out(" ldy $sourceName+1 | ldx $sourceName")
in Cx16VirtualRegisters -> {
asmgen.out(
"""
lda $sourceName
sta cx16.${target.register.toString().lowercase()}
lda $sourceName+1
sta cx16.${target.register.toString().lowercase()}+1
""")
}
else -> throw AssemblyError("can't load word in a single 8-bit register")
}
else -> throw AssemblyError("can't load word in a single 8-bit register")
}
}
TargetStorageKind.STACK -> {
asmgen.out("""
lda $sourceName
sta P8ESTACK_LO,x
lda $sourceName+1
sta P8ESTACK_HI,x
dex""")
if(sourceDt==DataType.UBYTE) {
asmgen.out(" lda $sourceName | sta P8ESTACK_LO,x")
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz P8ESTACK_HI,x")
else
asmgen.out(" lda #0 | sta P8ESTACK_HI,x")
}
else
asmgen.out("""
lda $sourceName
sta P8ESTACK_LO,x
lda $sourceName+1
sta P8ESTACK_HI,x
dex""")
}
}
}
@@ -3598,11 +3698,18 @@ internal class AssignmentAsmGen(private val program: PtProgram,
DataType.BYTE -> {
when (target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
lda #0
sec
sbc ${target.asmVarname}
sta ${target.asmVarname}""")
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out("""
lda ${target.asmVarname}
eor #255
ina
sta ${target.asmVarname}""")
else
asmgen.out("""
lda #0
sec
sbc ${target.asmVarname}
sta ${target.asmVarname}""")
}
TargetStorageKind.REGISTER -> {
when(target.register!!) {
@@ -3611,7 +3718,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
asmgen.out(" eor #255 | ina")
else
asmgen.out(" eor #255 | clc | adc #1")
}
RegisterOrPair.X -> asmgen.out(" txa | eor #255 | tax | inx")
RegisterOrPair.Y -> asmgen.out(" tya | eor #255 | tay | iny")
+13 -1
View File
@@ -1,7 +1,9 @@
package prog8tests.codegencpu6502
import io.kotest.assertions.throwables.shouldNotThrowAny
import io.kotest.assertions.withClue
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual
import io.kotest.matchers.shouldBe
import prog8.code.SymbolTableMaker
import prog8.code.ast.*
@@ -107,9 +109,19 @@ class TestCodegen: FunSpec({
test("64tass assembler available? - if this fails you need to install 64tass in the path") {
val command = mutableListOf("64tass", "--version")
shouldNotThrowAny {
val proc = ProcessBuilder(command).inheritIO().start()
val proc = ProcessBuilder(command).start()
val output = String(proc.inputStream.readBytes())
val result = proc.waitFor()
result.shouldBe(0)
val (_, version) = output.split('V')
val (major, minor, _) = version.split('.')
val majorNum = major.toInt()
val minorNum = minor.toInt()
withClue("64tass version should be 1.58 or newer") {
majorNum shouldBeGreaterThanOrEqual 1
if (majorNum == 1)
minorNum shouldBeGreaterThanOrEqual 58
}
}
}
})
@@ -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 {
@@ -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<IRCodeChunkBase>()
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<IRCodeChunkBase>()
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<IRCodeChunkBase> {
val result = mutableListOf<IRCodeChunkBase>()
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)
}
}
@@ -177,6 +177,7 @@ class IRCodeGen(
old.type,
old.reg1,
old.reg2,
old.reg3,
old.fpReg1,
old.fpReg2,
immediate = immediateValue,
@@ -399,42 +400,42 @@ class IRCodeGen(
}
private fun translate(whenStmt: PtWhen): IRCodeChunks {
if(whenStmt.choices.children.isEmpty())
return emptyList()
val result = mutableListOf<IRCodeChunkBase>()
val valueDt = irType(whenStmt.value.type)
val valueTr = expressionEval.translateExpression(whenStmt.value)
addToResult(result, valueTr, valueTr.resultReg, -1)
val choices = whenStmt.choices.children.map {it as PtWhenChoice }
val choices = mutableListOf<Pair<String, PtWhenChoice>>()
val endLabel = createLabelName()
for (choice in choices) {
whenStmt.choices.children.forEach {
val choice = it as PtWhenChoice
if(choice.isElse) {
result += translateNode(choice.statements)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
} else {
val skipLabel = createLabelName()
val values = choice.values.children.map {it as PtNumber}
if(values.size==1) {
val chunk = IRCodeChunk(null, null)
chunk += IRInstruction(Opcode.BNE, valueDt, reg1=valueTr.resultReg, immediate = values[0].number.toInt(), labelSymbol = skipLabel)
result += chunk
result += translateNode(choice.statements)
if(choice.statements.children.last() !is PtReturn)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
if(choice.statements.children.isEmpty()) {
// no statements for this choice value, jump to the end immediately
choice.values.children.map { it as PtNumber }.sortedBy { it.number }.forEach { value ->
addInstr(result, IRInstruction(Opcode.BEQ, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt(), labelSymbol = endLabel), null)
}
} else {
val matchLabel = createLabelName()
val chunk = IRCodeChunk(null, null)
for (value in values) {
chunk += IRInstruction(Opcode.BEQ, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt(), labelSymbol = matchLabel)
val choiceLabel = createLabelName()
choices.add(choiceLabel to choice)
choice.values.children.map { it as PtNumber }.sortedBy { it.number }.forEach { value ->
addInstr(result, IRInstruction(Opcode.BEQ, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt(), labelSymbol = choiceLabel), null)
}
chunk += IRInstruction(Opcode.JUMP, labelSymbol = skipLabel)
result += chunk
result += labelFirstChunk(translateNode(choice.statements), matchLabel)
if(choice.statements.children.last() !is PtReturn)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
}
result += IRCodeChunk(skipLabel, null)
}
}
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
choices.forEach { (label, choice) ->
result += labelFirstChunk(translateNode(choice.statements), label)
val lastStatement = choice.statements.children.last()
if(lastStatement !is PtReturn && lastStatement !is PtJump)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = endLabel), null)
}
result += IRCodeChunk(endLabel, null)
return result
}
@@ -483,10 +484,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 {
@@ -47,6 +47,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
val changed = removeNops(chunk1, indexedInstructions)
|| removeDoubleLoadsAndStores(chunk1, indexedInstructions) // TODO not yet implemented
|| removeUselessArithmetic(chunk1, indexedInstructions)
|| removeNeedlessCompares(chunk1, indexedInstructions)
|| removeWeirdBranches(chunk1, chunk2, indexedInstructions)
|| removeDoubleSecClc(chunk1, indexedInstructions)
|| cleanupPushPop(chunk1, indexedInstructions)
@@ -266,6 +267,28 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
return changed
}
private fun removeNeedlessCompares(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
// a CMPI with 0, after an instruction like LOAD that already sets the status bits, can be removed.
// but only if the instruction after it is not using the Carry bit because that won't be set by a LOAD instruction etc.
var changed = false
indexedInstructions.reversed().forEach { (idx, ins) ->
if(idx>0 && idx<(indexedInstructions.size-1) && ins.opcode==Opcode.CMPI && ins.immediate==0) {
val previous = indexedInstructions[idx-1].value
if(previous.opcode in OpcodesThatSetStatusbitsIncludingCarry) {
chunk.instructions.removeAt(idx)
changed = true
} else if(previous.opcode in OpcodesThatSetStatusbitsButNotCarry) {
val next = indexedInstructions[idx+1].value
if(next.opcode !in arrayOf(Opcode.BSTCC, Opcode.BSTCS, Opcode.BSTPOS, Opcode.BSTNEG)) {
chunk.instructions.removeAt(idx)
changed = true
}
}
}
}
return changed
}
private fun removeUselessArithmetic(chunk: IRCodeChunk, indexedInstructions: List<IndexedValue<IRInstruction>>): Boolean {
// note: this is hard to solve for the non-immediate instructions atm because the values are loaded into registers first
var changed = false
@@ -391,4 +391,47 @@ class StatementOptimizer(private val program: Program,
else
noModifications
}
override fun after(whenStmt: When, parent: Node): Iterable<IAstModification> {
fun replaceWithIf(condition: Expression, trueBlock: AnonymousScope, elseBlock: AnonymousScope?): List<IAstModification> {
val ifStmt = IfElse(condition, trueBlock, elseBlock ?: AnonymousScope(mutableListOf(), whenStmt.position), whenStmt.position)
errors.warn("for boolean condition a normal if statement is preferred", whenStmt.position)
return listOf(IAstModification.ReplaceNode(whenStmt, ifStmt, parent))
}
if(whenStmt.condition.inferType(program).isBool) {
if(whenStmt.choices.all { it.values?.size==1 }) {
if (whenStmt.choices.all { it.values!!.single().constValue(program)!!.number in arrayOf(0.0, 1.0) }) {
// it's a when statement on booleans that can just be replaced by an if or if..else.
if (whenStmt.choices.size == 1) {
return if(whenStmt.choices[0].values!![0].constValue(program)!!.number==1.0) {
replaceWithIf(whenStmt.condition, whenStmt.choices[0].statements, null)
} else {
val notCondition = BinaryExpression(whenStmt.condition, "==", NumericLiteral(DataType.UBYTE, 0.0, whenStmt.condition.position), whenStmt.condition.position)
replaceWithIf(notCondition, whenStmt.choices[0].statements, null)
}
} else if (whenStmt.choices.size == 2) {
var trueBlock: AnonymousScope? = null
var elseBlock: AnonymousScope? = null
if(whenStmt.choices[0].values!![0].constValue(program)!!.number==1.0) {
trueBlock = whenStmt.choices[0].statements
} else {
elseBlock = whenStmt.choices[0].statements
}
if(whenStmt.choices[1].values!![0].constValue(program)!!.number==1.0) {
trueBlock = whenStmt.choices[1].statements
} else {
elseBlock = whenStmt.choices[1].statements
}
if(trueBlock!=null && elseBlock!=null) {
return replaceWithIf(whenStmt.condition, trueBlock, elseBlock)
}
}
}
}
}
return noModifications
}
}
+1 -2
View File
@@ -508,8 +508,7 @@ asmsub ubyte2decimal (ubyte value @A) -> ubyte @Y, ubyte @A, ubyte @X {
; ---- A to decimal string in Y/A/X (100s in Y, 10s in A, 1s in X)
%asm {{
ldy #uword2decimal.ASCII_0_OFFSET
bne uword2decimal.hex_try200
rts
jmp uword2decimal.hex_try200
}}
}
+2
View File
@@ -290,6 +290,7 @@ close_end:
sub f_read(uword bufferpointer, uword num_bytes) -> uword {
; -- read from the currently open file, up to the given number of bytes.
; returns the actual number of bytes read. (checks for End-of-file and error conditions)
; NOTE: cannot be used to load into VRAM. Use vload() or call cx16.macptr() yourself with the vera data register as address.
if not iteration_in_progress or not num_bytes
return 0
@@ -347,6 +348,7 @@ m_in_buffer sta $ffff
; optimized for Commander X16 to use MACPTR block read kernal call
sub f_read_all(uword bufferpointer) -> uword {
; -- read the full contents of the file, returns number of bytes read.
; NOTE: cannot be used to load into VRAM. Use vload() or call cx16.macptr() yourself with the vera data register as address.
if not iteration_in_progress
return 0
+164 -76
View File
@@ -13,13 +13,14 @@
; SCREEN MODE LIST:
; mode 0 = reset back to default text mode
; mode 1 = bitmap 320 x 240 monochrome
; mode 2 = bitmap 320 x 240 x 4c (TODO not yet implemented)
; mode 3 = bitmap 320 x 240 x 16c (TODO not yet implemented)
; mode 2 = bitmap 320 x 240 x 4c (not yet implemented: just use 256c, there's enough vram for that)
; mode 3 = bitmap 320 x 240 x 16c (not yet implemented: just use 256c, there's enough vram for that)
; mode 4 = bitmap 320 x 240 x 256c (like SCREEN $80 but using this api instead of kernal)
; mode 5 = bitmap 640 x 480 monochrome
; mode 6 = bitmap 640 x 480 x 4c
; higher color dephts in highres are not supported due to lack of VRAM
; TODO remove the phx/plx pairs in non-stack compiler version
gfx2 {
@@ -46,7 +47,7 @@ gfx2 {
height = 240
bpp = 1
}
; TODO modes 2, 3 not yet implemented
; TODO modes 2, 3
4 -> {
; lores 256c
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
@@ -109,7 +110,7 @@ gfx2 {
repeat 240/2/8
cs_innerloop640()
}
; TODO mode 2, 3
; TODO modes 2, 3
4 -> {
; lores 256c
repeat 240/2
@@ -240,8 +241,7 @@ _done
}}
}
6 -> {
; highres 4c
; TODO also mostly usable for lores 4c?
; highres 4c ....also mostly usable for mode 2, lores 4c?
color &= 3
ubyte[4] colorbits
ubyte ii
@@ -427,17 +427,17 @@ _done
}
word @zp d = 0
cx16.r13 = true ; 'positive_ix'
cx16.r1L = true ; 'positive_ix'
if dx < 0 {
dx = -dx
cx16.r13 = false
cx16.r1L = false
}
word @zp dx2 = dx*2
word @zp dy2 = dy*2
cx16.r14 = x1 ; internal plot X
if dx >= dy {
if cx16.r13 {
if cx16.r1L {
repeat {
plot(cx16.r14, y1, color)
if cx16.r14==x2
@@ -464,7 +464,7 @@ _done
}
}
else {
if cx16.r13 {
if cx16.r1L {
repeat {
plot(cx16.r14, y1, color)
if y1 == y2
@@ -600,7 +600,7 @@ _done
}}
}
}
; TODO mode 2,3
; TODO modes 2, 3
4 -> {
; lores 256c
void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
@@ -652,8 +652,7 @@ _done
}
}
6 -> {
; highres 4c
; TODO also mostly usable for lores 4c?
; highres 4c ....also mostly usable for mode 2, lores 4c?
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
cx16.r2L = lsb(x) & 3 ; xbits
; color &= 3
@@ -707,7 +706,7 @@ _done
+
}}
}
; TODO mode 2 and 3
; TODO modes 2, 3
4 -> {
; lores 256c
void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
@@ -881,19 +880,18 @@ skip:
}
sub position(uword @zp x, uword y) {
ubyte bank
when active_mode {
1 -> {
; lores monochrome
cx16.r0 = y*(320/8) + x/8
cx16.vaddr(0, cx16.r0, 0, 1)
}
; TODO modes 2,3
; TODO modes 2, 3
4 -> {
; lores 256c
void addr_mul_24_for_lores_256c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
bank = lsb(cx16.r1)
cx16.vaddr(bank, cx16.r0, 0, 1)
cx16.r2L = cx16.r1L
cx16.vaddr(cx16.r2L, cx16.r0, 0, 1)
}
5 -> {
; highres monochrome
@@ -903,24 +901,16 @@ skip:
6 -> {
; highres 4c
void addr_mul_24_for_highres_4c(y, x) ; 24 bits result is in r0 and r1L (highest byte)
bank = lsb(cx16.r1)
cx16.vaddr(bank, cx16.r0, 0, 1)
cx16.r2L = cx16.r1L
cx16.vaddr(cx16.r2L, cx16.r0, 0, 1)
}
}
}
sub position2(uword @zp x, uword y, bool also_port_1) {
position(x, y)
if also_port_1 {
when active_mode {
1, 5 -> cx16.vaddr(0, cx16.r0, 1, 1)
; TODO modes 2, 3
4, 6 -> {
ubyte bank = lsb(cx16.r1)
cx16.vaddr(bank, cx16.r0, 1, 1)
}
}
}
if also_port_1
cx16.vaddr_clone(0)
}
inline asmsub next_pixel(ubyte color @A) {
@@ -995,48 +985,96 @@ skip:
sub text(uword @zp x, uword y, ubyte color, uword sctextptr) {
; -- Write some text at the given pixel position. The text string must be in screencode encoding (not petscii!).
; You must also have called text_charset() first to select and prepare the character set to use.
; NOTE: in monochrome (1bpp) screen modes, x position is currently constrained to multiples of 8 ! TODO allow per-pixel horizontal positioning
; TODO draw whole horizontal spans using vera auto increment if possible, instead of per-character columns
uword chardataptr
ubyte[8] @shared char_bitmap_bytes_left
ubyte[8] @shared char_bitmap_bytes_right
when active_mode {
1, 5 -> {
; monochrome mode, either resolution
cx16.r2 = 40
if active_mode==5
cx16.r2 = 80
while @(sctextptr) {
chardataptr = charset_addr + (@(sctextptr) as uword)*8
cx16.vaddr(charset_bank, chardataptr, 1, 1)
position(x,y)
cx16.r3 = sctextptr
while @(cx16.r3) {
chardataptr = charset_addr + @(cx16.r3) * $0008
; copy the character bitmap into RAM
cx16.vaddr_autoincr(charset_bank, chardataptr, 0, 1)
%asm {{
lda cx16.VERA_ADDR_H
and #%111 ; don't auto-increment, we have to do that manually because of the ora
sta cx16.VERA_ADDR_H
lda color
; pre-shift the bits
phx ; TODO remove in non-stack version
lda text.x
and #7
sta P8ZP_SCRATCH_B1
ldy #8
- lda P8ZP_SCRATCH_B1
bne + ; white color, plot normally
lda cx16.VERA_DATA1
eor #255 ; black color, keep only the other pixels
and cx16.VERA_DATA0
bra ++
+ lda cx16.VERA_DATA0
ora cx16.VERA_DATA1
+ sta cx16.VERA_DATA0
lda cx16.VERA_ADDR_L
clc
adc cx16.r2
sta cx16.VERA_ADDR_L
bcc +
inc cx16.VERA_ADDR_M
+ inc x
bne +
inc x+1
+ dey
ldy #0
- lda cx16.VERA_DATA0
stz P8ZP_SCRATCH_REG
ldx P8ZP_SCRATCH_B1
cpx #0
beq +
- lsr a
ror P8ZP_SCRATCH_REG
dex
bne -
+ sta char_bitmap_bytes_left,y
lda P8ZP_SCRATCH_REG
sta char_bitmap_bytes_right,y
iny
cpy #8
bne --
plx ; TODO remove in non-stack version
}}
sctextptr++
; left part of shifted char
position2(x, y, true)
set_autoincrs_mode1_or_5()
if color {
%asm {{
ldy #0
- lda char_bitmap_bytes_left,y
ora cx16.VERA_DATA1
sta cx16.VERA_DATA0
iny
cpy #8
bne -
}}
} else {
%asm {{
ldy #0
- lda char_bitmap_bytes_left,y
eor #255
and cx16.VERA_DATA1
sta cx16.VERA_DATA0
iny
cpy #8
bne -
}}
}
; right part of shifted char
if lsb(x) & 7 {
position2(x+8, y, true)
set_autoincrs_mode1_or_5()
if color {
%asm {{
ldy #0
- lda char_bitmap_bytes_right,y
ora cx16.VERA_DATA1
sta cx16.VERA_DATA0
iny
cpy #8
bne -
}}
} else {
%asm {{
ldy #0
- lda char_bitmap_bytes_right,y
eor #255
and cx16.VERA_DATA1
sta cx16.VERA_DATA0
iny
cpy #8
bne -
}}
}
}
cx16.r3++
x += 8
}
}
4 -> {
@@ -1048,7 +1086,7 @@ skip:
position(x,y)
y++
%asm {{
phx
phx ; TODO remove in non-stack version
ldx color
lda cx16.VERA_DATA1
sta P8ZP_SCRATCH_B1
@@ -1060,7 +1098,7 @@ skip:
+ lda cx16.VERA_DATA0 ; don't write a pixel, but do advance to the next address
+ dey
bne -
plx
plx ; TODO remove in non-stack version
}}
}
x+=8
@@ -1072,30 +1110,80 @@ skip:
; hires 4c
; we're going to use a few cx16 registers to make sure every variable is in zeropage in the inner loop.
cx16.r11L = color
cx16.r8 = y
while @(sctextptr) {
chardataptr = charset_addr + (@(sctextptr) as uword)*8
cx16.vaddr(charset_bank, chardataptr, 1, true) ; for reading the chardata from Vera data channel 1
position(x, y) ; only calculated once, we update vera address in the loop instead
cx16.VERA_ADDR_H &= $0f ; no auto increment
repeat 8 {
; TODO rewrite this inner loop partly in assembly:
; requires expanding the charbits to 2-bits per pixel (based on color)
; also it's way more efficient to draw whole horizontal spans instead of per-character
cx16.r9L = cx16.VERA_DATA1 ; get the next 8 horizontal character bits
cx16.r10L = cx16.VERA_DATA1 ; get the next 8 horizontal character bits
cx16.r7 = x
repeat 8 {
cx16.r9L <<= 1
if_cs
plot(cx16.r7, cx16.r8, cx16.r11L)
cx16.r10L <<= 1
if_cs {
cx16.r2L = cx16.r7L & 3 ; xbits
when cx16.r11L & 3 {
1 -> cx16.r12L = gfx2.plot.shiftedleft_4c_1[cx16.r2L]
2 -> cx16.r12L = gfx2.plot.shiftedleft_4c_2[cx16.r2L]
3 -> cx16.r12L = gfx2.plot.shiftedleft_4c_3[cx16.r2L]
else -> cx16.r12L = 0
}
cx16.VERA_DATA0 = cx16.VERA_DATA0 & gfx2.plot.mask4c[cx16.r2L] | cx16.r12L
}
cx16.r7++
if (cx16.r7 & 3) == 0 {
; increment the pixel address by one
%asm {{
stz cx16.VERA_CTRL
clc
lda cx16.VERA_ADDR_L
adc #1
sta cx16.VERA_ADDR_L
lda cx16.VERA_ADDR_M
adc #0
sta cx16.VERA_ADDR_M
lda cx16.VERA_ADDR_H
adc #0
sta cx16.VERA_ADDR_H
}}
}
}
cx16.r8++
; increment pixel address to the next line
%asm {{
stz cx16.VERA_CTRL
clc
lda cx16.VERA_ADDR_L
adc #(640-8)/4
sta cx16.VERA_ADDR_L
lda cx16.VERA_ADDR_M
adc #0
sta cx16.VERA_ADDR_M
lda cx16.VERA_ADDR_H
adc #0
sta cx16.VERA_ADDR_H
}}
}
x+=8
cx16.r8-=8
sctextptr++
}
}
}
sub set_autoincrs_mode1_or_5() {
; set autoincrements to go to next pixel row (40 or 80 increment)
if active_mode==1 {
cx16.VERA_CTRL = 0
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & $0f | (11<<4)
cx16.VERA_CTRL = 1
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & $0f | (11<<4)
} else {
cx16.VERA_CTRL = 0
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & $0f | (12<<4)
cx16.VERA_CTRL = 1
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & $0f | (12<<4)
}
}
}
asmsub cs_innerloop640() clobbers(Y) {
+103 -2
View File
@@ -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,105 @@ asmsub vaddr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, byte autoIncrO
}}
}
asmsub vaddr_clone(ubyte port @A) clobbers (A,X,Y) {
; -- clones Vera addresses from the given source port to the other one.
; leaves CTRL on the destination port.
%asm {{
sta VERA_CTRL
ldx VERA_ADDR_L
ldy VERA_ADDR_H
phy
ldy VERA_ADDR_M
eor #1
sta VERA_CTRL
stx VERA_ADDR_L
sty VERA_ADDR_M
ply
sty VERA_ADDR_H
rts
}}
}
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
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.
; 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
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!
@@ -961,8 +1062,8 @@ asmsub set_rasterline(uword line @AY) {
%asm {{
sei
ldx #$42
ldy #1
tya
ldy #2
lda #0
jsr cx16.i2c_write_byte
bra *
}}
+1 -1
View File
@@ -238,7 +238,7 @@ _lx ldx #0 ; modified
}}
}
asmsub scroll_right() clobbers(A) {
asmsub scroll_right() clobbers(A,Y) {
; ---- scroll the whole screen 1 character to the right
; contents of the leftmost column are unchanged, you should clear/refill this yourself
%asm {{
@@ -5,11 +5,11 @@ package prog8.buildversion
*/
const val MAVEN_GROUP = "prog8"
const val MAVEN_NAME = "compiler"
const val VERSION = "9.1-SNAPSHOT"
const val GIT_REVISION = 3930
const val GIT_SHA = "b4e94ae4dd1c29a966bb5ea0454ab31f1e9863cb"
const val GIT_DATE = "2023-07-05T21:15:04Z"
const val VERSION = "9.2-SNAPSHOT"
const val GIT_REVISION = 3964
const val GIT_SHA = "aaa30e4a583fa7c8da61998cf7cbb2a60b52afb0"
const val GIT_DATE = "2023-07-16T21:16:18Z"
const val GIT_BRANCH = "master"
const val BUILD_DATE = "2023-07-05T21:15:07Z"
const val BUILD_UNIX_TIME = 1688591707834L
const val BUILD_DATE = "2023-07-16T21:16:23Z"
const val BUILD_UNIX_TIME = 1689542183583L
const val DIRTY = 1
@@ -1341,8 +1341,14 @@ internal class AstChecker(private val program: Program,
constvalue == null -> errors.err("choice value must be a constant", whenChoice.position)
constvalue.type !in IntegerDatatypes -> errors.err("choice value must be a byte or word", whenChoice.position)
conditionType isnot constvalue.type -> {
if(conditionType.isKnown)
errors.err("choice value datatype differs from condition value", whenChoice.position)
if(conditionType.isKnown) {
if(conditionType.istype(DataType.BOOL)) {
if(constvalue.number!=0.0 && constvalue.number!=1.0)
errors.err("choice value datatype differs from condition value", whenChoice.position)
} else {
errors.err("choice value datatype differs from condition value", whenChoice.position)
}
}
}
}
}
@@ -150,7 +150,7 @@ internal class BeforeAsmAstChanger(val program: Program,
}
}
if (!subroutine.inline || !options.optimize) {
if (!subroutine.inline) {
if (subroutine.isAsmSubroutine && subroutine.asmAddress==null && !subroutine.hasRtsInAsm()) {
// make sure the NOT INLINED asm subroutine actually has a rts at the end
// (non-asm routines get a Return statement as needed, above)
@@ -404,17 +404,35 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
}
override fun after(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> {
val conditionDt = (whenChoice.parent as When).condition.inferType(program)
if((parent as When).condition.inferType(program).isWords) {
val values = whenChoice.values
values?.toTypedArray()?.withIndex()?.forEach { (index, value) ->
val num = value.constValue(program)
if(num!=null && num.type in ByteDatatypes) {
val wordNum = NumericLiteral(if(num.type==DataType.UBYTE) DataType.UWORD else DataType.WORD, num.number, num.position)
wordNum.parent = num.parent
values[index] = wordNum
val valueDt = value.inferType(program)
if(valueDt!=conditionDt) {
val castedValue = (value as NumericLiteral).cast(conditionDt.getOr(DataType.UNDEFINED))
if(castedValue.isValid) {
values[index] = castedValue.valueOrZero()
} else {
errors.err("choice value datatype differs from condition value", value.position)
}
}
}
} else {
val values = whenChoice.values
values?.toTypedArray()?.withIndex()?.forEach { (index, value) ->
val valueDt = value.inferType(program)
if(valueDt!=conditionDt) {
val castedValue = (value as NumericLiteral).cast(conditionDt.getOr(DataType.UNDEFINED))
if(castedValue.isValid) {
values[index] = castedValue.valueOrZero()
} else {
errors.err("choice value datatype differs from condition value", value.position)
}
}
}
}
return noModifications
}
@@ -262,15 +262,5 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
}
return noModifications
}
override fun after(whenStmt: When, parent: Node): Iterable<IAstModification> {
val removals = mutableListOf<Int>()
whenStmt.choices.withIndex().forEach { (index, choice) ->
if(choice.statements.isEmpty())
removals.add(index)
}
removals.reversed().forEach { whenStmt.choices.removeAt(it) }
return noModifications
}
}
@@ -334,5 +334,22 @@ main {
}"""
compileText(VMTarget(), optimize=false, src, writeAssembly=false) shouldNotBe null
}
test("when on booleans") {
val src = """
main
{
sub start()
{
bool choiceVariable=true
when choiceVariable {
false -> cx16.r0++
true -> cx16.r1++
}
}
}"""
compileText(VMTarget(), optimize=false, src, writeAssembly=false) shouldNotBe null
}
})
@@ -285,4 +285,37 @@ derp {
compileText(VMTarget(), true, src, writeAssembly = true) shouldNotBe null
compileText(Cx16Target(), true, src, writeAssembly = true) shouldNotBe null
}
test("prefix expressions with typecasting") {
val src="""
main
{
sub start()
{
uword uw = 54321
ubyte ub = 123
word sw = -12345
byte sb = -123
func_uw(~ub as uword)
func_ub(~uw as ubyte)
func_uw(~sb as uword)
func_ub(~sw as ubyte)
func_w(-sb as word)
func_b(-sw as byte)
}
sub func_uw(uword arg) {
}
sub func_w(word arg) {
}
sub func_ub(ubyte arg) {
}
sub func_b(byte arg) {
}
}"""
compileText(VMTarget(), false, src, writeAssembly = true) shouldNotBe null
compileText(Cx16Target(), false, src, writeAssembly = true) shouldNotBe null
}
})
+4
View File
@@ -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
+7 -4
View File
@@ -324,7 +324,8 @@ This way you can set the second character on the second row from the top like th
An uword variable can be used in limited scenarios as a 'pointer' to a byte in memory at a specific,
dynamic, location. You can use array indexing on a pointer variable to use it as a byte array at
a dynamic location in memory: currently this is equivalent to directly referencing the bytes in
memory at the given index. See also :ref:`pointervars_programming`
memory at the given index. In contrast to a real array variable, the index value can be the size of a word.
See also :ref:`pointervars_programming`
**LSB/MSB split word arrays:**
For (u)word arrays, you can make the compiler layout the array in memory as two separate arrays,
@@ -445,7 +446,7 @@ without defining a memory mapped location, you can do so by enclosing the addres
This is the official syntax to 'dereference a pointer' as it is often named in other languages.
You can actually also use the array indexing notation for this. It will be silently converted into
the direct memory access expression as explained above. Note that this also means that unlike regular arrays,
the direct memory access expression as explained above. Note that unlike regular arrays,
the index is not limited to an ubyte value. You can use a full uword to index a pointer variable like this::
pointervar[999] = 0 ; set memory byte to zero at location pointervar + 999.
@@ -949,11 +950,13 @@ rsave, rsavex
It's not needed to rsave()/rsavex() before an asm subroutine that clobbers the X register
(which is used by prog8 as the internal evaluation stack pointer);
the compiler will take care of this situation automatically.
Note: the 16 bit 'virtual' registers of the Commander X16 are *not* saved.
Note: the 16 bit 'virtual' registers of the Commander X16 are *not* saved,
but you can use ``cx16.save_virtual_registers()`` for that.
rrestore, rrestorex
Restore all registers including status (or only X) back from the cpu hardware stack
Note: the 16 bit 'virtual' registers of the Commander X16 are *not* restored.
Note: the 16 bit 'virtual' registers of the Commander X16 are *not* restored,
but you can use ``cx16.restore_virtual_registers()`` for that.
Library routines
+13 -4
View File
@@ -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
@@ -406,7 +414,8 @@ directly access the memory. Enclose a numeric expression or literal with ``@(...
@($d020) = 0 ; set the c64 screen border to black ("poke 53280,0")
@(vic+$20) = 6 ; a dynamic expression to 'calculate' the address
The array indexing notation on a uword 'pointer variable' is syntactic sugar for such a direct memory access expression::
The array indexing notation on a uword 'pointer variable' is syntactic sugar for such a direct memory access expression,
and the index value can be larger than a byte in this case::
pointervar[999] = 0 ; equivalent to @(pointervar+999) = 0
@@ -458,7 +467,7 @@ Syntax is familiar with brackets: ``arrayvar[x]`` ::
Note: you can also use array indexing on a 'pointer variable', which is basically an uword variable
containing a memory address. Currently this is equivalent to directly referencing the bytes in
memory at the given index. See :ref:`pointervars`
memory at the given index (and allows index values of word size). See :ref:`pointervars`
String
^^^^^^
+8 -17
View File
@@ -1,7 +1,8 @@
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 such as BEQ, BEQR, etc (gradually), replace with CMP(I) + status branch instruction
- IR: reduce amount of CMP/CMPI after instructions that set the status bits correctly (LOADs? INC? etc), but only after setting the status bits is verified!
...
@@ -32,7 +33,6 @@ Compiler:
- ir: the @split arrays are currently also split in _lsb/_msb arrays in the IR, and operations take multiple (byte) instructions that may lead to verbose and slow operation and machine code generation down the line.
- ir: for expressions with array indexes that occur multiple times, can we avoid loading them into new virtualregs everytime and just reuse a single virtualreg as indexer? (simple form of common subexpression elimination)
- PtAst/IR: more complex common subexpression eliminations
- can we get rid of pieces of asmgen.AssignmentAsmGen by just reusing the AugmentableAssignment ? generated code should not suffer
- [problematic due to using 64tass:] better support for building library programs, where unused .proc shouldn't be deleted from the assembly?
Perhaps replace all uses of .proc/.pend/.endproc by .block/.bend will fix that with a compiler flag?
But all library code written in asm uses .proc already..... (textual search/replace when writing the actual asm?)
@@ -46,11 +46,9 @@ Libraries:
- fix the problems in atari target, and flesh out its libraries.
- c128 target: make syslib more complete (missing kernal routines)?
- c64: make the graphics.BITMAP_ADDRESS configurable (VIC banking)
- optimize several inner loops in gfx2 even further?
- actually implement modes 3 and perhaps even 2 to gfx2 (lores 16 color and 4 color)
Expressions:
Expressions: (see remove_evalstack branch):
- Once the evalstack-free expression codegen is in place, the Eval Stack can be removed from the compiler.
Machinedefinition, .p8 and .asm library files, all routines operationg on estack, and everything saving/restoring the X register related to this stack.
@@ -74,15 +72,8 @@ STRUCTS again?
What if we were to re-introduce Structs in prog8? Some thoughts:
- can contain only numeric types (byte,word,float) - no nested structs, no reference types (strings, arrays) inside structs
- is just some syntactic sugar for a scoped set of variables -> ast transform to do exactly this before codegen. Codegen doesn't know about struct.
- no arrays of struct -- because too slow on 6502 to access those, rather use struct of arrays instead.
can we make this a compiler/codegen only issue? i.e. syntax is just as if it was an array of structs?
or make it explicit in the syntax so that it is clear what the memory layout of it is.
- ability to assign struct variable to another? this is slow but can be quite handy sometimes.
however how to handle this in a function that gets the struct passed as reference? Don't allow it there? (there's no pointer dereferencing concept in prog8)
- ability to be passed as argument to a function (by reference)?
however there is no typed pointer in prog8 at the moment so this can't be implemented in a meaningful way yet,
because there is no way to reference it as the struct type again. (current ast gets the by-reference parameter
type replaced by uword)
So-- maybe don't replace the parameter type in the ast? Should fix that for str and array types as well then
- only as a reference type (uword pointer). This removes a lot of the problems related to introducing a variable length value type.
- arrays of struct is just an array of uword pointers. Can even be @split?
- need to introduce typed pointer datatype in prog8
- str is then syntactic sugar for pointer to character/byte?
- arrays are then syntactic sugar for pointer to byte/word/float?
+1 -1
View File
@@ -175,7 +175,7 @@ widget {
const ubyte height = 11
widget.highlightedrect(x+widget.window_close_icon.width, y, width-64, height, true, active)
gfx2.plot(x+widget.window_close_icon.width, y+height-1, 1) ; correct bottom left corner
gfx2.text(x+32, y+1, 1, titlestr)
gfx2.text(x+26, y+1, 1, titlestr)
widget.window_close_icon(x, y, active)
widget.window_order_icon(x+width-22, y, active)
widget.window_flipsize_icon(x+width-44, y, active)
+10 -26
View File
@@ -1,32 +1,16 @@
%import textio
%zeropage basicsafe
; (127 instructions in 15 chunks, 47 registers)
; 679 steps
main {
sub start() {
ubyte var = 0
sub start() {
uword i
uword n
repeat 10 {
txt.chrout('.')
}
txt.nl()
n=10
for i in 0 to n step 3 {
txt.print_uw(i)
txt.nl()
}
txt.nl()
n=0
for i in 10 downto n step -3 {
txt.print_uw(i)
txt.nl()
}
}
when var {
1 -> txt.print("one")
2 -> txt.print("two")
0 -> {
}
else -> txt.print("other")
}
}
}
+1 -1
View File
@@ -5,4 +5,4 @@ org.gradle.daemon=true
kotlin.code.style=official
javaVersion=11
kotlinVersion=1.9.0
version=9.1
version=9.2.1
@@ -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)
@@ -146,6 +146,7 @@ divmod reg1, value - unsigned division reg1/value, stor
sqrt reg1, reg2 - reg1 is the square root of reg2 (reg2 can be .w or .b, result type in reg1 is always .b) you can also use it with floating point types, fpreg1 and fpreg2 (result is also .f)
sgn reg1, reg2 - reg1 is the sign of reg2 (0.b, 1.b or -1.b)
cmp reg1, reg2 - set processor status bits C, N, Z according to comparison of reg1 with reg2. (semantics taken from 6502/68000 CMP instruction)
cmpi reg1, value - set processor status bits C, N, Z according to comparison of reg1 with immediate value. (semantics taken from 6502/68000 CMP instruction)
NOTE: because mul/div are constrained (truncated) to remain in 8 or 16 bits, there is NO NEED for separate signed/unsigned mul and div instructions. The result is identical.
@@ -219,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
*/
@@ -312,6 +313,7 @@ enum class Opcode {
SQRT,
SGN,
CMP,
CMPI,
EXT,
EXTS,
@@ -417,6 +419,24 @@ val OpcodesThatBranch = setOf(
Opcode.BLES
)
val OpcodesThatSetStatusbitsIncludingCarry = setOf(
Opcode.CMP,
Opcode.CMPI
)
val OpcodesThatSetStatusbitsButNotCarry = setOf(
Opcode.LOAD,
Opcode.LOADM,
Opcode.LOADI,
Opcode.LOADX,
Opcode.LOADIX,
Opcode.LOADR,
Opcode.INC,
Opcode.INCM,
Opcode.DEC,
Opcode.DECM
)
enum class IRDataType {
BYTE,
WORD,
@@ -434,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,
@@ -446,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
@@ -460,6 +482,7 @@ data class InstructionFormat(val datatype: IRDataType?,
">r1" -> reg1 = OperandDirection.WRITE
"<>r1" -> reg1 = OperandDirection.READWRITE
"<r2" -> reg2 = OperandDirection.READ
"<r3" -> reg3 = OperandDirection.READ
"<fr1" -> fpreg1 = OperandDirection.READ
">fr1" -> fpreg1 = OperandDirection.WRITE
"<>fr1" -> fpreg1 = OperandDirection.READWRITE
@@ -476,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
}
@@ -546,16 +569,16 @@ val instructionFormats = mutableMapOf(
Opcode.BLES to InstructionFormat.from("BW,<r1,<i,<a"),
Opcode.SZ to InstructionFormat.from("BW,>r1,<r2"),
Opcode.SNZ to InstructionFormat.from("BW,>r1,<r2"),
Opcode.SEQ to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.SNE to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.SLT to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.SLTS to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.SGT to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.SGTS to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.SLE to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.SLES to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.SGE to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.SGES to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.SEQ to InstructionFormat.from("BW,<>r1,<r2,<r3"),
Opcode.SNE to InstructionFormat.from("BW,<>r1,<r2,<r3"),
Opcode.SLT to InstructionFormat.from("BW,<>r1,<r2,<r3"),
Opcode.SLTS to InstructionFormat.from("BW,<>r1,<r2,<r3"),
Opcode.SGT to InstructionFormat.from("BW,<>r1,<r2,<r3"),
Opcode.SGTS to InstructionFormat.from("BW,<>r1,<r2,<r3"),
Opcode.SLE to InstructionFormat.from("BW,<>r1,<r2,<r3"),
Opcode.SLES to InstructionFormat.from("BW,<>r1,<r2,<r3"),
Opcode.SGE to InstructionFormat.from("BW,<>r1,<r2,<r3"),
Opcode.SGES to InstructionFormat.from("BW,<>r1,<r2,<r3"),
Opcode.INC to InstructionFormat.from("BW,<>r1 | F,<>fr1"),
Opcode.INCM to InstructionFormat.from("BW,<>a | F,<>a"),
Opcode.DEC to InstructionFormat.from("BW,<>r1 | F,<>fr1"),
@@ -584,6 +607,7 @@ val instructionFormats = mutableMapOf(
Opcode.DIVMODR to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.DIVMOD to InstructionFormat.from("BW,<>r1,<i"),
Opcode.CMP to InstructionFormat.from("BW,<r1,<r2"),
Opcode.CMPI to InstructionFormat.from("BW,<r1,<i"),
Opcode.EXT to InstructionFormat.from("BW,>r1,<r2"),
Opcode.EXTS to InstructionFormat.from("BW,>r1,<r2"),
Opcode.ANDR to InstructionFormat.from("BW,<>r1,<r2"),
@@ -642,7 +666,7 @@ val instructionFormats = mutableMapOf(
Opcode.MSIG to InstructionFormat.from("BW,>r1,<r2"),
Opcode.PUSH to InstructionFormat.from("BW,<r1 | F,<fr1"),
Opcode.POP to InstructionFormat.from("BW,>r1 | F,>fr1"),
Opcode.CONCAT to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.CONCAT to InstructionFormat.from("BW,<>r1,<r2,<r3"),
Opcode.CLC to InstructionFormat.from("N"),
Opcode.SEC to InstructionFormat.from("N"),
Opcode.BREAKPOINT to InstructionFormat.from("N"),
@@ -662,6 +686,7 @@ data class IRInstruction(
val type: IRDataType?=null,
val reg1: Int?=null, // 0-$ffff
val reg2: Int?=null, // 0-$ffff
val reg3: Int?=null, // 0-$ffff
val fpReg1: Int?=null, // 0-$ffff
val fpReg2: Int?=null, // 0-$ffff
val immediate: Int?=null, // 0-$ff or $ffff if word
@@ -675,6 +700,7 @@ data class IRInstruction(
// This knowledge is useful in IL assembly optimizers to see how registers are used.
val reg1direction: OperandDirection
val reg2direction: OperandDirection
val reg3direction: OperandDirection
val fpReg1direction: OperandDirection
val fpReg2direction: OperandDirection
@@ -682,9 +708,12 @@ data class IRInstruction(
require(labelSymbol?.first()!='_') {"label/symbol should not start with underscore $labelSymbol"}
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"}
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
val formats = instructionFormats.getValue(opcode)
require (type != null || formats.containsKey(null)) { "missing type" }
@@ -692,10 +721,12 @@ data class IRInstruction(
val format = formats.getOrElse(type) { throw IllegalArgumentException("type $type invalid for $opcode") }
if(format.reg1!=OperandDirection.UNUSED) require(reg1!=null) { "missing reg1" }
if(format.reg2!=OperandDirection.UNUSED) require(reg2!=null) { "missing reg2" }
if(format.reg3!=OperandDirection.UNUSED) require(reg3!=null) { "missing reg3" }
if(format.fpReg1!=OperandDirection.UNUSED) require(fpReg1!=null) { "missing fpReg1" }
if(format.fpReg2!=OperandDirection.UNUSED) require(fpReg2!=null) { "missing fpReg2" }
if(format.reg1==OperandDirection.UNUSED) require(reg1==null) { "invalid reg1" }
if(format.reg2==OperandDirection.UNUSED) require(reg2==null) { "invalid reg2" }
if(format.reg3==OperandDirection.UNUSED) require(reg3==null) { "invalid reg3" }
if(format.fpReg1==OperandDirection.UNUSED) require(fpReg1==null) { "invalid fpReg1" }
if(format.fpReg2==OperandDirection.UNUSED) require(fpReg2==null) { "invalid fpReg2" }
if(format.immediate) {
@@ -726,6 +757,7 @@ data class IRInstruction(
reg1direction = format.reg1
reg2direction = format.reg2
reg3direction = format.reg3
fpReg1direction = format.fpReg1
fpReg2direction = format.fpReg2
@@ -793,6 +825,19 @@ data class IRInstruction(
}
else -> 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 -> {
@@ -906,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(",")
+7 -1
View File
@@ -115,6 +115,7 @@ fun parseIRCodeLine(line: String): Either<IRInstruction, String> {
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<IRInstruction, String> {
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<IRInstruction, String> {
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<IRInstruction, String> {
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<IRInstruction, String> {
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(
+48 -9
View File
@@ -245,6 +245,7 @@ class VirtualMachine(irProgram: IRProgram) {
Opcode.DIVMOD -> InsDIVMOD(ins)
Opcode.SGN -> InsSGN(ins)
Opcode.CMP -> InsCMP(ins)
Opcode.CMPI -> InsCMPI(ins)
Opcode.SQRT -> InsSQRT(ins)
Opcode.EXT -> InsEXT(ins)
Opcode.EXTS -> InsEXTS(ins)
@@ -807,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()
@@ -888,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) {
@@ -1267,6 +1277,35 @@ class VirtualMachine(irProgram: IRProgram) {
nextPc()
}
private fun InsCMPI(i: IRInstruction) {
val comparison: Int
when(i.type!!) {
IRDataType.BYTE -> {
val reg1 = registers.getUB(i.reg1!!)
comparison = reg1.toInt() - (i.immediate!! and 255)
statusNegative = (comparison and 0x80)==0x80
}
IRDataType.WORD -> {
val reg1 = registers.getUW(i.reg1!!)
comparison = reg1.toInt() - (i.immediate!! and 65535)
statusNegative = (comparison and 0x8000)==0x8000
}
IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
}
if(comparison==0){
statusZero = true
statusCarry = true
} else if(comparison>0) {
statusZero = false
statusCarry = true
} else {
statusZero = false
statusCarry = false
}
nextPc()
}
private fun plusMinusMultAnyByte(operator: String, reg1: Int, reg2: Int) {
val left = registers.getUB(reg1)
val right = registers.getUB(reg2)
@@ -2076,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")
@@ -2275,8 +2314,8 @@ class VirtualMachine(irProgram: IRProgram) {
private fun getSetOnConditionOperands(ins: IRInstruction): Pair<Int, Int> {
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")
}
@@ -2286,8 +2325,8 @@ class VirtualMachine(irProgram: IRProgram) {
private fun getSetOnConditionOperandsU(ins: IRInstruction): Pair<UInt, UInt> {
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")
}