mirror of
https://github.com/irmen/prog8.git
synced 2025-06-14 11:23:37 +00:00
Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
e55a675d2e | |||
6e07602d77 | |||
0eb2d437e2 | |||
d9e13201dd | |||
48864ad6cf | |||
b5255444cd | |||
8e5c67b4b2 | |||
c69c17de42 | |||
125ce3240f | |||
7215efe167 | |||
7ceb76cff5 | |||
7e734214dc | |||
dea7f37553 | |||
415c599310 | |||
70cd4fedbe | |||
1e6d7673bc |
2
.idea/kotlinc.xml
generated
2
.idea/kotlinc.xml
generated
@ -4,6 +4,6 @@
|
||||
<option name="jvmTarget" value="11" />
|
||||
</component>
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="1.8.20-release-327" />
|
||||
<option name="version" value="1.8.21-release-380" />
|
||||
</component>
|
||||
</project>
|
@ -1639,14 +1639,7 @@ $repeatLabel lda $counterVar
|
||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||
return
|
||||
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||
} else {
|
||||
pushCpuStack(DataType.UBYTE, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
out(" pla")
|
||||
}
|
||||
assignByteOperandsToAAndVar(left, right, "P8ZP_SCRATCH_B1")
|
||||
return code("P8ZP_SCRATCH_B1")
|
||||
}
|
||||
|
||||
@ -1680,14 +1673,7 @@ $repeatLabel lda $counterVar
|
||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||
return
|
||||
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||
} else {
|
||||
pushCpuStack(DataType.UBYTE, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
out(" pla")
|
||||
}
|
||||
assignByteOperandsToAAndVar(left, right, "P8ZP_SCRATCH_B1")
|
||||
return code("P8ZP_SCRATCH_B1")
|
||||
}
|
||||
|
||||
@ -1723,15 +1709,7 @@ $repeatLabel lda $counterVar
|
||||
if(wordJumpForSimpleRightOperands(left, right, ::code))
|
||||
return
|
||||
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
} else {
|
||||
pushCpuStack(DataType.UWORD, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
restoreRegisterStack(CpuRegister.Y, false)
|
||||
restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
assignWordOperandsToAYAndVar(left, right, "P8ZP_SCRATCH_W2")
|
||||
return out(" jsr prog8_lib.reg_less_uw | beq $jumpIfFalseLabel")
|
||||
}
|
||||
|
||||
@ -1769,15 +1747,7 @@ $repeatLabel lda $counterVar
|
||||
if(wordJumpForSimpleRightOperands(left, right, ::code))
|
||||
return
|
||||
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.WORD)
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
} else {
|
||||
pushCpuStack(DataType.WORD, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.WORD)
|
||||
restoreRegisterStack(CpuRegister.Y, false)
|
||||
restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
assignWordOperandsToAYAndVar(left, right, "P8ZP_SCRATCH_W2")
|
||||
return out(" jsr prog8_lib.reg_less_w | beq $jumpIfFalseLabel")
|
||||
}
|
||||
|
||||
@ -1816,14 +1786,7 @@ $repeatLabel lda $counterVar
|
||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||
return
|
||||
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||
} else {
|
||||
pushCpuStack(DataType.UBYTE, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
out(" pla")
|
||||
}
|
||||
assignByteOperandsToAAndVar(left, right, "P8ZP_SCRATCH_B1")
|
||||
return code("P8ZP_SCRATCH_B1")
|
||||
}
|
||||
|
||||
@ -1859,14 +1822,7 @@ $repeatLabel lda $counterVar
|
||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||
return
|
||||
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE)
|
||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||
} else {
|
||||
pushCpuStack(DataType.BYTE, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE)
|
||||
out(" pla")
|
||||
}
|
||||
assignByteOperandsToAAndVar(left, right, "P8ZP_SCRATCH_B1")
|
||||
return code("P8ZP_SCRATCH_B1")
|
||||
}
|
||||
|
||||
@ -1908,15 +1864,7 @@ $repeatLabel lda $counterVar
|
||||
if(wordJumpForSimpleRightOperands(left, right, ::code))
|
||||
return
|
||||
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
} else {
|
||||
pushCpuStack(DataType.UWORD, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
restoreRegisterStack(CpuRegister.Y, false)
|
||||
restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
assignWordOperandsToAYAndVar(left, right, "P8ZP_SCRATCH_W2")
|
||||
return code("P8ZP_SCRATCH_W2+1", "P8ZP_SCRATCH_W2")
|
||||
}
|
||||
|
||||
@ -1959,15 +1907,7 @@ $repeatLabel lda $counterVar
|
||||
if(wordJumpForSimpleLeftOperand(left, right, ::code))
|
||||
return
|
||||
|
||||
if(right.isSimple()) {
|
||||
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.WORD)
|
||||
assignExpressionToRegister(right, RegisterOrPair.AY)
|
||||
} else {
|
||||
pushCpuStack(DataType.WORD, right)
|
||||
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.WORD)
|
||||
restoreRegisterStack(CpuRegister.Y, false)
|
||||
restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
assignWordOperandsToAYAndVar(right, left, "P8ZP_SCRATCH_W2")
|
||||
return out(" jsr prog8_lib.reg_less_w | beq $jumpIfFalseLabel")
|
||||
}
|
||||
|
||||
@ -2007,14 +1947,7 @@ $repeatLabel lda $counterVar
|
||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||
return
|
||||
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||
} else {
|
||||
pushCpuStack(DataType.UBYTE, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
out(" pla")
|
||||
}
|
||||
assignByteOperandsToAAndVar(left, right, "P8ZP_SCRATCH_B1")
|
||||
return code("P8ZP_SCRATCH_B1")
|
||||
}
|
||||
|
||||
@ -2050,14 +1983,7 @@ $repeatLabel lda $counterVar
|
||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||
return
|
||||
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE)
|
||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||
} else {
|
||||
pushCpuStack(DataType.BYTE, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE)
|
||||
out(" pla")
|
||||
}
|
||||
assignByteOperandsToAAndVar(left, right, "P8ZP_SCRATCH_B1")
|
||||
return code("P8ZP_SCRATCH_B1")
|
||||
}
|
||||
|
||||
@ -2101,15 +2027,7 @@ $repeatLabel lda $counterVar
|
||||
if(wordJumpForSimpleRightOperands(left, right, ::code))
|
||||
return
|
||||
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
} else {
|
||||
pushCpuStack(DataType.UWORD, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
restoreRegisterStack(CpuRegister.Y, false)
|
||||
restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
assignWordOperandsToAYAndVar(left, right, "P8ZP_SCRATCH_W2")
|
||||
return out(" jsr prog8_lib.reg_lesseq_uw | beq $jumpIfFalseLabel")
|
||||
}
|
||||
|
||||
@ -2156,15 +2074,7 @@ $repeatLabel lda $counterVar
|
||||
return code(asmVariableName(left))
|
||||
}
|
||||
|
||||
if(right.isSimple()) {
|
||||
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.WORD)
|
||||
assignExpressionToRegister(right, RegisterOrPair.AY)
|
||||
} else {
|
||||
pushCpuStack(DataType.WORD, right)
|
||||
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.WORD)
|
||||
restoreRegisterStack(CpuRegister.Y, false)
|
||||
restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
assignWordOperandsToAYAndVar(right, left, "P8ZP_SCRATCH_W2")
|
||||
return out(" jsr prog8_lib.reg_lesseq_w | beq $jumpIfFalseLabel")
|
||||
}
|
||||
|
||||
@ -2200,14 +2110,7 @@ $repeatLabel lda $counterVar
|
||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||
return
|
||||
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||
} else {
|
||||
pushCpuStack(DataType.UBYTE, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
out(" pla")
|
||||
}
|
||||
assignByteOperandsToAAndVar(left, right, "P8ZP_SCRATCH_B1")
|
||||
return code("P8ZP_SCRATCH_B1")
|
||||
}
|
||||
|
||||
@ -2240,15 +2143,31 @@ $repeatLabel lda $counterVar
|
||||
if(byteJumpForSimpleRightOperand(left, right, ::code))
|
||||
return
|
||||
|
||||
assignByteOperandsToAAndVar(left, right, "P8ZP_SCRATCH_B1")
|
||||
return code("P8ZP_SCRATCH_B1")
|
||||
}
|
||||
|
||||
internal fun assignByteOperandsToAAndVar(left: PtExpression, right: PtExpression, rightVarName: String) {
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE)
|
||||
assignExpressionToVariable(right, rightVarName, DataType.UBYTE)
|
||||
assignExpressionToRegister(left, RegisterOrPair.A)
|
||||
} else {
|
||||
pushCpuStack(DataType.BYTE, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_B1", DataType.BYTE)
|
||||
pushCpuStack(DataType.UBYTE, left)
|
||||
assignExpressionToVariable(right, rightVarName, DataType.UBYTE)
|
||||
out(" pla")
|
||||
}
|
||||
return code("P8ZP_SCRATCH_B1")
|
||||
}
|
||||
|
||||
internal fun assignWordOperandsToAYAndVar(left: PtExpression, right: PtExpression, rightVarname: String) {
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, rightVarname, DataType.UWORD)
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
} else {
|
||||
pushCpuStack(DataType.UWORD, left)
|
||||
assignExpressionToVariable(right, rightVarname, DataType.UWORD)
|
||||
restoreRegisterStack(CpuRegister.Y, false)
|
||||
restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateUwordGreaterOrEqualOrJumpElsewhere(left: PtExpression, right: PtExpression, leftConstVal: PtNumber?, rightConstVal: PtNumber?, jumpIfFalseLabel: String) {
|
||||
@ -2282,15 +2201,7 @@ $repeatLabel lda $counterVar
|
||||
if(wordJumpForSimpleRightOperands(left, right, ::code))
|
||||
return
|
||||
|
||||
if(right.isSimple()) {
|
||||
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
assignExpressionToRegister(right, RegisterOrPair.AY)
|
||||
} else {
|
||||
pushCpuStack(DataType.UWORD, right)
|
||||
assignExpressionToVariable(left, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
restoreRegisterStack(CpuRegister.Y, false)
|
||||
restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
assignWordOperandsToAYAndVar(right, left, "P8ZP_SCRATCH_W2")
|
||||
return out(" jsr prog8_lib.reg_lesseq_uw | beq $jumpIfFalseLabel")
|
||||
}
|
||||
|
||||
@ -2328,15 +2239,7 @@ $repeatLabel lda $counterVar
|
||||
if(wordJumpForSimpleRightOperands(left, right, ::code))
|
||||
return
|
||||
|
||||
if(left.isSimple()) {
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.WORD)
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY)
|
||||
} else {
|
||||
pushCpuStack(DataType.WORD, left)
|
||||
assignExpressionToVariable(right, "P8ZP_SCRATCH_W2", DataType.WORD)
|
||||
restoreRegisterStack(CpuRegister.Y, false)
|
||||
restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
assignWordOperandsToAYAndVar(left, right, "P8ZP_SCRATCH_W2")
|
||||
return out(" jsr prog8_lib.reg_lesseq_w | beq $jumpIfFalseLabel")
|
||||
}
|
||||
|
||||
|
@ -96,8 +96,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
private fun funcDivmodW(fcall: PtBuiltinFunctionCall) {
|
||||
assignAsmGen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", DataType.UWORD)
|
||||
assignAsmGen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY, false)
|
||||
asmgen.assignWordOperandsToAYAndVar(fcall.args[1], fcall.args[0], "P8ZP_SCRATCH_W1")
|
||||
// math.divmod_uw_asm: -- divide two unsigned words (16 bit each) into 16 bit results
|
||||
// input: P8ZP_SCRATCH_W1 in ZP: 16 bit number, A/Y: 16 bit divisor
|
||||
// output: P8ZP_SCRATCH_W2 in ZP: 16 bit remainder, A/Y: 16 bit division result
|
||||
@ -114,8 +113,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
private fun funcStringCompare(fcall: PtBuiltinFunctionCall, resultToStack: Boolean) {
|
||||
assignAsmGen.assignExpressionToVariable(fcall.args[1], "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
assignAsmGen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY, false)
|
||||
asmgen.assignWordOperandsToAYAndVar(fcall.args[0], fcall.args[1], "P8ZP_SCRATCH_W2")
|
||||
asmgen.out(" jsr prog8_lib.strcmp_mem")
|
||||
if(resultToStack)
|
||||
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||
@ -207,27 +205,13 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||
asmgen.out(" cmp ${arg2.address.asConstInteger()!!.toHex()}")
|
||||
} else {
|
||||
if(arg1.isSimple()) {
|
||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||
} else {
|
||||
asmgen.pushCpuStack(DataType.UBYTE, arg1)
|
||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | cmp P8ZP_SCRATCH_B1")
|
||||
}
|
||||
asmgen.assignByteOperandsToAAndVar(arg1, arg2, "P8ZP_SCRATCH_B1")
|
||||
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
if(arg1.isSimple()) {
|
||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
|
||||
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||
} else {
|
||||
asmgen.pushCpuStack(DataType.UBYTE, arg1)
|
||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | cmp P8ZP_SCRATCH_B1")
|
||||
}
|
||||
asmgen.assignByteOperandsToAAndVar(arg1, arg2, "P8ZP_SCRATCH_B1")
|
||||
asmgen.out(" cmp P8ZP_SCRATCH_B1")
|
||||
}
|
||||
}
|
||||
} else
|
||||
@ -253,25 +237,12 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
+""")
|
||||
}
|
||||
else -> {
|
||||
if(arg1.isSimple()) {
|
||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD)
|
||||
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
cpy P8ZP_SCRATCH_W1+1
|
||||
bne +
|
||||
cmp P8ZP_SCRATCH_W1
|
||||
+""")
|
||||
} else {
|
||||
asmgen.pushCpuStack(DataType.UWORD, arg1)
|
||||
asmgen.assignExpressionToVariable(arg2, "P8ZP_SCRATCH_W1", DataType.UWORD)
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||
asmgen.out("""
|
||||
cpy P8ZP_SCRATCH_W1+1
|
||||
bne +
|
||||
cmp P8ZP_SCRATCH_W1
|
||||
+""")
|
||||
}
|
||||
asmgen.assignWordOperandsToAYAndVar(arg1, arg2, "P8ZP_SCRATCH_W1")
|
||||
asmgen.out("""
|
||||
cpy P8ZP_SCRATCH_W1+1
|
||||
bne +
|
||||
cmp P8ZP_SCRATCH_W1
|
||||
+""")
|
||||
}
|
||||
}
|
||||
} else
|
||||
@ -753,8 +724,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
// fall through method:
|
||||
asmgen.assignExpressionToVariable(fcall.args[0], "P8ZP_SCRATCH_W1", DataType.UWORD)
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY)
|
||||
asmgen.assignWordOperandsToAYAndVar(fcall.args[1], fcall.args[0], "P8ZP_SCRATCH_W1")
|
||||
asmgen.out(" jsr prog8_lib.func_pokew")
|
||||
}
|
||||
|
||||
|
@ -427,7 +427,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
return true
|
||||
}
|
||||
}
|
||||
assignExpressionWordOperandsLeftAYRightScratchW1(expr)
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1")
|
||||
when (expr.operator) {
|
||||
"&", "and" -> asmgen.out(" and P8ZP_SCRATCH_W1 | pha | tya | and P8ZP_SCRATCH_W1+1 | tay | pla")
|
||||
"|", "or" -> asmgen.out(" ora P8ZP_SCRATCH_W1 | pha | tya | ora P8ZP_SCRATCH_W1+1 | tay | pla")
|
||||
@ -469,7 +469,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
return true
|
||||
} else if(expr.left.type in WordDatatypes && expr.right.type in WordDatatypes &&
|
||||
expr.left.isSimple() && expr.right.isSimple()) {
|
||||
assignExpressionWordOperandsLeftAYRightScratchW1(expr)
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1")
|
||||
if(expr.operator=="==") {
|
||||
asmgen.out("""
|
||||
cmp P8ZP_SCRATCH_W1
|
||||
@ -537,7 +537,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
} else if(dt in WordDatatypes) {
|
||||
|
||||
fun doAddOrSubWordExpr() {
|
||||
assignExpressionWordOperandsLeftAYRightScratchW1(expr)
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1")
|
||||
if(expr.operator=="+")
|
||||
asmgen.out("""
|
||||
clc
|
||||
@ -733,7 +733,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
return true
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
assignExpressionWordOperandsLeftScratchW1RightAY(expr)
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
|
||||
asmgen.out("""
|
||||
jsr math.multiply_words
|
||||
lda math.multiply_words.result
|
||||
@ -793,13 +793,13 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
return true
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
assignExpressionWordOperandsLeftScratchW1RightAY(expr)
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
|
||||
asmgen.out(" jsr math.divmod_uw_asm")
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
return true
|
||||
}
|
||||
DataType.WORD -> {
|
||||
assignExpressionWordOperandsLeftScratchW1RightAY(expr)
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
|
||||
asmgen.out(" jsr math.divmod_w_asm")
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
return true
|
||||
@ -821,7 +821,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
return true
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
assignExpressionWordOperandsLeftScratchW1RightAY(expr)
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
|
||||
asmgen.out(" jsr math.divmod_uw_asm")
|
||||
assignVariableWord(assign.target, "P8ZP_SCRATCH_W2")
|
||||
return true
|
||||
@ -836,21 +836,9 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
private fun assignOptimizedComparisonBytes(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||
val signed = expr.left.type == DataType.BYTE || expr.right.type == DataType.BYTE
|
||||
|
||||
fun assignExpressionOperandsLeftScratchRightA() {
|
||||
if(expr.right.isSimple()) {
|
||||
assignExpressionToVariable(expr.left, "P8ZP_SCRATCH_B1", expr.left.type)
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.A, signed)
|
||||
} else {
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.A, signed)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, false)
|
||||
assignExpressionToVariable(expr.left, "P8ZP_SCRATCH_B1", expr.left.type)
|
||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
}
|
||||
|
||||
when(expr.operator) {
|
||||
"==" -> {
|
||||
assignExpressionOperandsLeftScratchRightA()
|
||||
asmgen.assignByteOperandsToAAndVar(expr.right, expr.left, "P8ZP_SCRATCH_B1")
|
||||
asmgen.out("""
|
||||
cmp P8ZP_SCRATCH_B1
|
||||
beq +
|
||||
@ -860,7 +848,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
+""")
|
||||
}
|
||||
"!=" -> {
|
||||
assignExpressionOperandsLeftScratchRightA()
|
||||
asmgen.assignByteOperandsToAAndVar(expr.right, expr.left, "P8ZP_SCRATCH_B1")
|
||||
asmgen.out("""
|
||||
cmp P8ZP_SCRATCH_B1
|
||||
bne +
|
||||
@ -870,7 +858,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
+""")
|
||||
}
|
||||
"<" -> {
|
||||
assignExpressionOperandsLeftScratchRightA()
|
||||
asmgen.assignByteOperandsToAAndVar(expr.right, expr.left, "P8ZP_SCRATCH_B1")
|
||||
if(signed)
|
||||
asmgen.out("""
|
||||
clc
|
||||
@ -892,7 +880,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
+""")
|
||||
}
|
||||
"<=" -> {
|
||||
assignExpressionOperandsLeftScratchRightA()
|
||||
asmgen.assignByteOperandsToAAndVar(expr.right, expr.left, "P8ZP_SCRATCH_B1")
|
||||
if(signed)
|
||||
asmgen.out("""
|
||||
sec
|
||||
@ -911,7 +899,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
rol a""")
|
||||
}
|
||||
">" -> {
|
||||
assignExpressionOperandsLeftScratchRightA()
|
||||
asmgen.assignByteOperandsToAAndVar(expr.right, expr.left, "P8ZP_SCRATCH_B1")
|
||||
if(signed)
|
||||
asmgen.out("""
|
||||
sec
|
||||
@ -931,7 +919,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
eor #1""")
|
||||
}
|
||||
">=" -> {
|
||||
assignExpressionOperandsLeftScratchRightA()
|
||||
asmgen.assignByteOperandsToAAndVar(expr.right, expr.left, "P8ZP_SCRATCH_B1")
|
||||
if(signed)
|
||||
asmgen.out("""
|
||||
clc
|
||||
@ -964,7 +952,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
val signed = expr.left.type == DataType.WORD || expr.right.type == DataType.WORD
|
||||
when(expr.operator) {
|
||||
"==" -> {
|
||||
assignExpressionWordOperandsLeftScratchW1RightAY(expr)
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
|
||||
asmgen.out("""
|
||||
cmp P8ZP_SCRATCH_W1
|
||||
bne +
|
||||
@ -976,7 +964,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
+""")
|
||||
}
|
||||
"!=" -> {
|
||||
assignExpressionWordOperandsLeftScratchW1RightAY(expr)
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
|
||||
asmgen.out("""
|
||||
cmp P8ZP_SCRATCH_W1
|
||||
bne +
|
||||
@ -989,7 +977,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
"<" -> {
|
||||
if(signed) {
|
||||
assignExpressionWordOperandsLeftAYRightScratchW1(expr)
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1")
|
||||
asmgen.out("""
|
||||
cmp P8ZP_SCRATCH_W1
|
||||
tya
|
||||
@ -1003,7 +991,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
+""")
|
||||
}
|
||||
else {
|
||||
assignExpressionWordOperandsLeftAYRightScratchW1(expr)
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1")
|
||||
asmgen.out("""
|
||||
cpy P8ZP_SCRATCH_W1+1
|
||||
bcc +
|
||||
@ -1018,7 +1006,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
"<=" -> {
|
||||
if(signed) {
|
||||
assignExpressionWordOperandsLeftScratchW1RightAY(expr)
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
|
||||
asmgen.out("""
|
||||
cmp P8ZP_SCRATCH_W1
|
||||
tya
|
||||
@ -1032,7 +1020,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
+""")
|
||||
}
|
||||
else {
|
||||
assignExpressionWordOperandsLeftScratchW1RightAY(expr)
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
|
||||
asmgen.out("""
|
||||
cpy P8ZP_SCRATCH_W1+1
|
||||
bcc ++
|
||||
@ -1047,7 +1035,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
">" -> {
|
||||
if(signed) {
|
||||
assignExpressionWordOperandsLeftScratchW1RightAY(expr)
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
|
||||
asmgen.out("""
|
||||
cmp P8ZP_SCRATCH_W1
|
||||
tya
|
||||
@ -1061,7 +1049,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
+""")
|
||||
}
|
||||
else {
|
||||
assignExpressionWordOperandsLeftScratchW1RightAY(expr)
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.right, expr.left, "P8ZP_SCRATCH_W1")
|
||||
asmgen.out("""
|
||||
cpy P8ZP_SCRATCH_W1+1
|
||||
bcc +
|
||||
@ -1076,7 +1064,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
">=" -> {
|
||||
if(signed) {
|
||||
assignExpressionWordOperandsLeftAYRightScratchW1(expr)
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1")
|
||||
asmgen.out("""
|
||||
cmp P8ZP_SCRATCH_W1
|
||||
tya
|
||||
@ -1090,7 +1078,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
+""")
|
||||
}
|
||||
else {
|
||||
assignExpressionWordOperandsLeftAYRightScratchW1(expr)
|
||||
asmgen.assignWordOperandsToAYAndVar(expr.left, expr.right, "P8ZP_SCRATCH_W1")
|
||||
asmgen.out("""
|
||||
cpy P8ZP_SCRATCH_W1+1
|
||||
bcc ++
|
||||
@ -1278,34 +1266,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignExpressionWordOperandsLeftScratchW1RightAY(expr: PtBinaryExpression) {
|
||||
if(expr.right.isSimple()) {
|
||||
assignExpressionToVariable(expr.left, "P8ZP_SCRATCH_W1", expr.left.type)
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.AY, expr.right.type in SignedDatatypes)
|
||||
} else {
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.AY, expr.right.type in SignedDatatypes)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, false)
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||
assignExpressionToVariable(expr.left, "P8ZP_SCRATCH_W1", expr.left.type)
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignExpressionWordOperandsLeftAYRightScratchW1(expr: PtBinaryExpression) {
|
||||
if(expr.left.isSimple()) {
|
||||
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", expr.left.type)
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.left.type in SignedDatatypes)
|
||||
} else {
|
||||
assignExpressionToRegister(expr.left, RegisterOrPair.AY, expr.left.type in SignedDatatypes)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, false)
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||
assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_W1", expr.left.type)
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.restoreRegisterStack(CpuRegister.A, false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignStatusFlagByte(target: AsmAssignTarget, statusflag: Statusflag) {
|
||||
when(statusflag) {
|
||||
Statusflag.Pc -> {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package prog8tests.codegencpu6502
|
||||
|
||||
import io.kotest.assertions.throwables.shouldNotThrowAny
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
import prog8.code.SymbolTableMaker
|
||||
@ -102,5 +103,14 @@ class TestCodegen: FunSpec({
|
||||
result.name shouldBe "test"
|
||||
Files.deleteIfExists(Path("${result.name}.asm"))
|
||||
}
|
||||
|
||||
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 result = proc.waitFor()
|
||||
result.shouldBe(0)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -157,6 +157,12 @@ class UnusedCodeRemover(private val program: Program,
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||
if(assignment.target isSameAs assignment.value)
|
||||
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun deduplicateAssignments(statements: List<Statement>, scope: IStatementContainer): List<IAstModification> {
|
||||
// removes 'duplicate' assignments that assign the same target directly after another, unless it is a function call
|
||||
val linesToRemove = mutableListOf<Assignment>()
|
||||
|
@ -97,8 +97,9 @@ psg {
|
||||
envelope_attacks[voice_num] = attack
|
||||
envelope_sustains[voice_num] = sustain
|
||||
envelope_releases[voice_num] = release
|
||||
if maxvolume<envelope_volumes[voice_num]
|
||||
envelope_volumes[voice_num] = maxvolume
|
||||
cx16.r0 = mkword(maxvolume, 0)
|
||||
if cx16.r0<envelope_volumes[voice_num]
|
||||
envelope_volumes[voice_num] = cx16.r0
|
||||
envelope_maxvolumes[voice_num] = maxvolume
|
||||
envelope_states[voice_num] = 0
|
||||
}
|
||||
|
@ -70,7 +70,10 @@ asmsub RDTIM16() -> uword @AY {
|
||||
; -- like RDTIM() but only returning the lower 16 bits in AY for convenience
|
||||
%asm {{
|
||||
phx
|
||||
php
|
||||
sei
|
||||
jsr c64.RDTIM
|
||||
plp
|
||||
pha
|
||||
txa
|
||||
tay
|
||||
@ -925,6 +928,7 @@ sys {
|
||||
asmsub wait(uword jiffies @AY) {
|
||||
; --- wait approximately the given number of jiffies (1/60th seconds) (N or N+1)
|
||||
; note: the system irq handler has to be active for this to work as it depends on the system jiffy clock
|
||||
; note: this routine cannot be used from inside a irq handler
|
||||
%asm {{
|
||||
phx
|
||||
sta P8ZP_SCRATCH_W1
|
||||
@ -936,9 +940,13 @@ _loop lda P8ZP_SCRATCH_W1
|
||||
plx
|
||||
rts
|
||||
|
||||
+ jsr c64.RDTIM
|
||||
+ sei
|
||||
jsr c64.RDTIM
|
||||
cli
|
||||
sta P8ZP_SCRATCH_B1
|
||||
- jsr c64.RDTIM
|
||||
- sei
|
||||
jsr c64.RDTIM
|
||||
cli
|
||||
cmp P8ZP_SCRATCH_B1
|
||||
beq -
|
||||
|
||||
|
@ -1 +1 @@
|
||||
8.13
|
||||
8.14
|
||||
|
@ -480,7 +480,9 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("target datatype is unknown", assignment.target.position)
|
||||
// otherwise, another error about missing symbol is already reported.
|
||||
} else {
|
||||
errors.err("type of value $valueDt doesn't match target $targetDt", assignment.value.position)
|
||||
// allow bitwise operations on different types as long as the size is the same
|
||||
if (!((assignment.value as? BinaryExpression)?.operator in BitwiseOperators && targetDt.isBytes && valueDt.isBytes || targetDt.isWords && valueDt.isWords))
|
||||
errors.err("type of value $valueDt doesn't match target $targetDt", assignment.value.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -958,11 +960,7 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
if(expr.operator in ComparisonOperators) {
|
||||
if(leftDt!=rightDt && !(leftDt in ByteDatatypes && rightDt in ByteDatatypes)) {
|
||||
throw FatalAstException("got comparison with different operand types: $leftDt ${expr.operator} $rightDt ${expr.position}")
|
||||
}
|
||||
} else {
|
||||
if(expr.operator !in ComparisonOperators) {
|
||||
if (leftDt == DataType.STR && rightDt == DataType.STR || leftDt in ArrayDatatypes && rightDt in ArrayDatatypes) {
|
||||
// str+str and str*number have already been const evaluated before we get here.
|
||||
errors.err("no computational or logical expressions with strings or arrays are possible", expr.position)
|
||||
@ -1611,8 +1609,11 @@ internal class AstChecker(private val program: Program,
|
||||
else if(sourceDatatype== DataType.FLOAT && targetDatatype in IntegerDatatypes)
|
||||
errors.err("cannot assign float to ${targetDatatype.name.lowercase()}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position)
|
||||
else {
|
||||
if(targetDatatype!=DataType.UWORD && sourceDatatype !in PassByReferenceDatatypes)
|
||||
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
|
||||
if(targetDatatype!=DataType.UWORD && sourceDatatype !in PassByReferenceDatatypes) {
|
||||
// allow bitwise operations on different types as long as the size is the same
|
||||
if (!((sourceValue as? BinaryExpression)?.operator in BitwiseOperators && targetDatatype.equalsSize(sourceDatatype)))
|
||||
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
|
@ -93,6 +93,11 @@ internal class BeforeAsmAstChanger(val program: Program,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if(binExpr.left isSameAs assignment.target)
|
||||
return noModifications
|
||||
val typeCast = binExpr.left as? TypecastExpression
|
||||
if(typeCast!=null && typeCast.expression isSameAs assignment.target)
|
||||
return noModifications
|
||||
val sourceDt = binExpr.left.inferType(program).getOrElse { throw AssemblyError("unknown dt") }
|
||||
val (_, left) = binExpr.left.typecastTo(assignment.target.inferType(program).getOrElse { throw AssemblyError(
|
||||
"unknown dt"
|
||||
@ -229,13 +234,13 @@ internal class BeforeAsmAstChanger(val program: Program,
|
||||
|
||||
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
val containingStatement = getContainingStatement(arrayIndexedExpression)
|
||||
if(getComplexArrayIndexedExpressions(containingStatement).size > 1) {
|
||||
errors.err("it's not possible to use more than one complex array indexing expression in a single statement; break it up via a temporary variable for instance", containingStatement.position)
|
||||
return noModifications
|
||||
}
|
||||
if(options.compTarget.name!=VMTarget.NAME) { // don't apply this optimization/check for Vm target
|
||||
val containingStatement = getContainingStatement(arrayIndexedExpression)
|
||||
if(getComplexArrayIndexedExpressions(containingStatement).size > 1) {
|
||||
errors.err("it's not possible to use more than one complex array indexing expression in a single statement; break it up via a temporary variable for instance", containingStatement.position)
|
||||
return noModifications
|
||||
}
|
||||
|
||||
if(options.compTarget.name!=VMTarget.NAME) { // don't apply this optimization for Vm target
|
||||
val index = arrayIndexedExpression.indexer.indexExpr
|
||||
if (index !is NumericLiteral && index !is IdentifierReference) {
|
||||
// replace complex indexing expression with a temp variable to hold the computed index first
|
||||
|
@ -133,9 +133,16 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
|
||||
}
|
||||
if(leftDt istype DataType.WORD && rightDt.oneOf(DataType.UBYTE, DataType.UWORD)) {
|
||||
// cast left to unsigned
|
||||
val cast = TypecastExpression(expr.left, rightDt.getOr(DataType.UNDEFINED), true, expr.left.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
|
||||
// cast left to unsigned word. Cast right to unsigned word if it is ubyte
|
||||
val mods = mutableListOf<IAstModification>()
|
||||
val cast = TypecastExpression(expr.left, DataType.UWORD, true, expr.left.position)
|
||||
mods += IAstModification.ReplaceNode(expr.left, cast, expr)
|
||||
if(rightDt istype DataType.UBYTE) {
|
||||
mods += IAstModification.ReplaceNode(expr.right,
|
||||
TypecastExpression(expr.right, DataType.UWORD, true, expr.right.position),
|
||||
expr)
|
||||
}
|
||||
return mods
|
||||
}
|
||||
if(rightDt istype DataType.BYTE && leftDt.oneOf(DataType.UBYTE, DataType.UWORD)) {
|
||||
// cast right to unsigned
|
||||
@ -143,9 +150,16 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, cast, expr))
|
||||
}
|
||||
if(rightDt istype DataType.WORD && leftDt.oneOf(DataType.UBYTE, DataType.UWORD)) {
|
||||
// cast right to unsigned
|
||||
val cast = TypecastExpression(expr.right, leftDt.getOr(DataType.UNDEFINED), true, expr.right.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, cast, expr))
|
||||
// cast right to unsigned word. Cast left to unsigned word if it is ubyte
|
||||
val mods = mutableListOf<IAstModification>()
|
||||
val cast = TypecastExpression(expr.right, DataType.UWORD, true, expr.right.position)
|
||||
mods += IAstModification.ReplaceNode(expr.right, cast, expr)
|
||||
if(leftDt istype DataType.UBYTE) {
|
||||
mods += IAstModification.ReplaceNode(expr.left,
|
||||
TypecastExpression(expr.left, DataType.UWORD, true, expr.left.position),
|
||||
expr)
|
||||
}
|
||||
return mods
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,89 +1,9 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
For next minor release
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
This is the maintenance branch for Prog8 version 8, only important bugfixes will be made here.
|
||||
New development for version 9 and up is taking place in the master branch.
|
||||
|
||||
TODO V8 maintenance fixes
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
|
||||
|
||||
For 9.0 major changes are being made in the "version_9" branch. Look there.
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
|
||||
Need help with
|
||||
^^^^^^^^^^^^^^
|
||||
- atari target: more details details about the machine, fixing library routines. I have no clue whatsoever.
|
||||
- see the :ref:`portingguide` for details on what information is needed.
|
||||
|
||||
|
||||
Future Things and Ideas
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Compiler:
|
||||
|
||||
- ir: idea: (but LLVM IR simply keeps the variables, so not a good idea then?...): replace all scalar variables by an allocated register. Keep a table of the variable to register mapping (including the datatype)
|
||||
global initialization values are simply a list of LOAD instructions.
|
||||
Variables replaced include all subroutine parameters! So the only variables that remain as variables are arrays and strings.
|
||||
- ir: mechanism to determine for chunks which registers are getting input values from "outside"
|
||||
- ir: mechanism to determine for chunks which registers are passing values out? (i.e. are used again in another chunk)
|
||||
- ir: peephole opt: (maybe just integrate this in the variable/register allocator though?) renumber registers in chunks to start with 1 again every time (but keep entry values in mind!)
|
||||
- ir: peephole opt: (maybe just integrate this in the variable/register allocator though?) reuse registers in chunks (but keep result registers in mind that pass values out! and don't renumber registers above SyscallRegisterBase!)
|
||||
- ir: add more optimizations in IRPeepholeOptimizer
|
||||
- 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)
|
||||
- try to optimize newexpr a bit more? Although maybe just spend effort on a new codegen based on the IR.
|
||||
- PtAst/IR: more complex common subexpression eliminations
|
||||
- generate WASM to eventually run prog8 on a browser canvas? Use binaryen toolkit or my binaryen kotlin library?
|
||||
- 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?)
|
||||
Once new codegen is written that is based on the IR, this point is mostly moot anyway as that will have its own dead code removal on the IR level.
|
||||
- Zig-like try-based error handling where the V flag could indicate error condition? and/or BRK to jump into monitor on failure? (has to set BRK vector for that) But the V flag is also set on certain normal instructions
|
||||
- For c128 target; put floating point variables in bank 1 to make the FP routines work (is this even worth it? very few people will use fp)
|
||||
|
||||
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?
|
||||
- add modes 3 and perhaps even 2 to gfx2 (lores 16 color and 4 color)?
|
||||
- add a flood fill (span fill/scanline fill) routine to gfx2?
|
||||
|
||||
|
||||
Expressions:
|
||||
|
||||
- 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.
|
||||
- Or rewrite expression tree evaluation such that it doesn't use an eval stack but flatten the tree into linear code
|
||||
that, for instance, uses a fixed number of predetermined value 'variables'?
|
||||
The VM IL solves this already (by using unlimited registers) but that still lacks a translation to 6502.
|
||||
- this removes the need for the BinExprSplitter? (which is problematic and very limited now)
|
||||
and perhaps the assignment splitting in BeforeAsmAstChanger too
|
||||
|
||||
Optimizations:
|
||||
|
||||
- VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served?
|
||||
for instance, vars used inside loops first, then loopvars, then uwords used as pointers, then the rest
|
||||
- various optimizers skip stuff if compTarget.name==VMTarget.NAME. Once 6502-codegen is done from IR code,
|
||||
those checks should probably be removed, or be made permanent
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
@ -9,17 +9,16 @@ main {
|
||||
|
||||
sub start() {
|
||||
|
||||
txt.print("ps2 custom key handler test - press keys! esc to quit!\n")
|
||||
txt.print("custom key handler test - press keys! esc to quit!\n")
|
||||
|
||||
sys.set_irqd()
|
||||
uword old_keyhdl = cx16.KEYHDL
|
||||
cx16.KEYHDL = &keyboard_scancode_handler
|
||||
sys.clear_irqd()
|
||||
|
||||
bool escape_pressed
|
||||
while not escape_pressed {
|
||||
handle_keyboard_event()
|
||||
while handle_keyboard_event() {
|
||||
}
|
||||
|
||||
sys.set_irqd()
|
||||
cx16.KEYHDL = old_keyhdl
|
||||
sys.clear_irqd()
|
||||
@ -30,70 +29,49 @@ main {
|
||||
; so that they won't get overwritten with initialization values every time.
|
||||
; The assembly keyboard handler will set these, prog8 will read them.
|
||||
bool @shared keyhdl_event ; is there a keyboard event to handle?
|
||||
ubyte @shared keyhdl_prefix
|
||||
ubyte @shared keyhdl_scancode
|
||||
ubyte @shared keyhdl_updown
|
||||
|
||||
sub handle_keyboard_event() {
|
||||
sub handle_keyboard_event() -> bool {
|
||||
; Potentially handle keyboard event.
|
||||
; Note that we do this from the program's main loop instead of
|
||||
; the actual keyboard handler routine itself.
|
||||
; The reason for this is documented below in the handler assembly routine.
|
||||
if not keyhdl_event
|
||||
return
|
||||
return true
|
||||
keyhdl_event = false
|
||||
txt.print_ubhex(keyhdl_prefix, true)
|
||||
txt.chrout(':')
|
||||
txt.print_ubhex(keyhdl_scancode, true)
|
||||
txt.spc()
|
||||
if keyhdl_updown
|
||||
if keyhdl_scancode & $80
|
||||
txt.chrout('u')
|
||||
else
|
||||
txt.chrout('d')
|
||||
txt.nl()
|
||||
if keyhdl_prefix==0 and keyhdl_scancode==119 and keyhdl_updown {
|
||||
; escape was pressed! exit back to basic
|
||||
main.start.escape_pressed = true
|
||||
}
|
||||
return keyhdl_scancode!=$6e ; escape breaks the loop
|
||||
}
|
||||
|
||||
asmsub keyboard_scancode_handler() {
|
||||
|
||||
; NOTE that the keyboard handler is an asm subroutine.
|
||||
; Unfortunately is it not possible to use prog8 code or calls here,
|
||||
; because the X register gets overwritten here (to store the prefix byte)
|
||||
; and prog8 uses the X register internally (for the evaluation stack).
|
||||
; because the X register gets overwritten here by the kernal.
|
||||
; Pog8 uses the X register internally (for the software eval stack).
|
||||
; So it is unsafe to call prog8 code from here because the evaluation stack pointer
|
||||
; will be invalid which produces undefined results.
|
||||
; So, instead, we store the various keyboard event bytes and signal
|
||||
; the main prog8 program that a keyboard event has occurred.
|
||||
; It then processes it independently from the assembly code here.
|
||||
;
|
||||
; Unfortunately this also means you cannot decide from that prog8 code
|
||||
; Unfortunately this also means you cannot decide easily from that prog8 code
|
||||
; if the keyboard press should be consumed/ignored or put into the keyboard queue
|
||||
; (this is controlled by returning 0 or 1 in register A here)
|
||||
;
|
||||
; see:
|
||||
; https://github.com/X16Community/x16-docs/blob/master/X16%20Reference%20-%2002%20-%20Editor.md#custom-keyboard-scancode-handler
|
||||
|
||||
%asm {{
|
||||
php
|
||||
pha
|
||||
phx
|
||||
stz keyhdl_updown
|
||||
bcc +
|
||||
inc keyhdl_updown
|
||||
+ stx keyhdl_prefix
|
||||
sta keyhdl_scancode
|
||||
lda #1
|
||||
sta keyhdl_event
|
||||
; we can do additional stuff here and decide if we want to
|
||||
; consume the key event or not (A=0 or A!=0)
|
||||
plx
|
||||
pla
|
||||
lda #0 ;By setting A=0 we will remove this key event for now
|
||||
tax
|
||||
plp
|
||||
|
||||
lda #0 ; By setting A=0 we will eat this key event. leave A unchanged to pass it through.
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
@ -1,16 +1,35 @@
|
||||
%import textio
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
ubyte @shared foo = derp(99)
|
||||
}
|
||||
ubyte ub = 123
|
||||
byte bb = -100
|
||||
uword uw = 12345
|
||||
word ww = -12345
|
||||
|
||||
asmsub derp(ubyte xx @Y) -> ubyte @ A {
|
||||
%asm {{
|
||||
rts
|
||||
ub |= 63 ; vm/c64 ok (127)
|
||||
bb |= 63 ; vm/c64 ok (-65)
|
||||
uw |= 63 ; vm/c64 ok (12351)
|
||||
ww |= 63 ; vm/c64 ok (-12289)
|
||||
|
||||
}}
|
||||
txt.print_ub(ub)
|
||||
txt.spc()
|
||||
txt.print_b(bb)
|
||||
txt.spc()
|
||||
txt.print_uw(uw)
|
||||
txt.spc()
|
||||
txt.print_w(ww)
|
||||
txt.nl()
|
||||
|
||||
uw |= 16384 ; vm/c64 ok (28735)
|
||||
ww |= 8192 ; vm/c64 ok (-4097)
|
||||
|
||||
txt.print_uw(uw)
|
||||
txt.spc()
|
||||
txt.print_w(ww)
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1270,7 +1270,7 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
else left / right
|
||||
}
|
||||
"%" -> {
|
||||
if(right==0.toUByte()) 0xffu
|
||||
if(right==0.toUByte()) 0u
|
||||
else left % right
|
||||
}
|
||||
else -> throw IllegalArgumentException("operator byte $operator")
|
||||
@ -1286,7 +1286,7 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
else left / value
|
||||
}
|
||||
"%" -> {
|
||||
if(value==0.toUByte()) 0xffu
|
||||
if(value==0.toUByte()) 0u
|
||||
else left % value
|
||||
}
|
||||
else -> throw IllegalArgumentException("operator byte $operator")
|
||||
@ -1298,7 +1298,7 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
val left = registers.getUB(reg1)
|
||||
val right = registers.getUB(reg2)
|
||||
val division = if(right==0.toUByte()) 0xffu else left / right
|
||||
val remainder = if(right==0.toUByte()) 0xffu else left % right
|
||||
val remainder = if(right==0.toUByte()) 0u else left % right
|
||||
valueStack.push(division.toUByte())
|
||||
valueStack.push(remainder.toUByte())
|
||||
}
|
||||
@ -1306,7 +1306,7 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
private fun divAndModConstUByte(reg1: Int, value: UByte) {
|
||||
val left = registers.getUB(reg1)
|
||||
val division = if(value==0.toUByte()) 0xffu else left / value
|
||||
val remainder = if(value==0.toUByte()) 0xffu else left % value
|
||||
val remainder = if(value==0.toUByte()) 0u else left % value
|
||||
valueStack.push(division.toUByte())
|
||||
valueStack.push(remainder.toUByte())
|
||||
}
|
||||
@ -1315,7 +1315,7 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
val left = registers.getUW(reg1)
|
||||
val right = registers.getUW(reg2)
|
||||
val division = if(right==0.toUShort()) 0xffffu else left / right
|
||||
val remainder = if(right==0.toUShort()) 0xffffu else left % right
|
||||
val remainder = if(right==0.toUShort()) 0u else left % right
|
||||
valueStack.pushw(division.toUShort())
|
||||
valueStack.pushw(remainder.toUShort())
|
||||
}
|
||||
@ -1323,7 +1323,7 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
private fun divAndModConstUWord(reg1: Int, value: UShort) {
|
||||
val left = registers.getUW(reg1)
|
||||
val division = if(value==0.toUShort()) 0xffffu else left / value
|
||||
val remainder = if(value==0.toUShort()) 0xffffu else left % value
|
||||
val remainder = if(value==0.toUShort()) 0u else left % value
|
||||
valueStack.pushw(division.toUShort())
|
||||
valueStack.pushw(remainder.toUShort())
|
||||
}
|
||||
@ -1337,7 +1337,7 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
else left / right
|
||||
}
|
||||
"%" -> {
|
||||
if(right==0.toUByte()) 0xffu
|
||||
if(right==0.toUByte()) 0u
|
||||
else left % right
|
||||
}
|
||||
else -> throw IllegalArgumentException("operator byte $operator")
|
||||
@ -1389,7 +1389,7 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
else left / right
|
||||
}
|
||||
"%" -> {
|
||||
if(right==0.toUShort()) 0xffffu
|
||||
if(right==0.toUShort()) 0u
|
||||
else left % right
|
||||
}
|
||||
else -> throw IllegalArgumentException("operator word $operator")
|
||||
@ -1405,7 +1405,7 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
else left / value
|
||||
}
|
||||
"%" -> {
|
||||
if(value==0.toUShort()) 0xffffu
|
||||
if(value==0.toUShort()) 0u
|
||||
else left % value
|
||||
}
|
||||
else -> throw IllegalArgumentException("operator word $operator")
|
||||
@ -1422,7 +1422,7 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
else left / right
|
||||
}
|
||||
"%" -> {
|
||||
if(right==0.toUShort()) 0xffffu
|
||||
if(right==0.toUShort()) 0u
|
||||
else left % right
|
||||
}
|
||||
else -> throw IllegalArgumentException("operator word $operator")
|
||||
|
Reference in New Issue
Block a user