mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 20:02:27 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d7e95e1b6 | ||
|
|
0816a57032 | ||
|
|
b89ad4b328 | ||
|
|
c112b327ab | ||
|
|
b19c282269 | ||
|
|
e520921746 | ||
|
|
970642244b | ||
|
|
3b90be2d9e | ||
|
|
2f756f1e3a | ||
|
|
78e84182f0 | ||
|
|
65a7a8caf8 | ||
|
|
4c6a2f5df9 | ||
|
|
3bbc00cc8c | ||
|
|
0e781d18fa | ||
|
|
4575a8fffe | ||
|
|
10d0ff252b | ||
|
|
c7d54570cc | ||
|
|
7136b33f2e | ||
|
|
5659742d97 | ||
|
|
450eaf7c4a | ||
|
|
c1aa5d4e47 | ||
|
|
b3cb9b7fe2 | ||
|
|
9cb61fa34d | ||
|
|
7c219d235c | ||
|
|
6938c79f88 | ||
|
|
b8284a147d |
No files matched your search
@@ -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")
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 *
|
||||
}}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
})
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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?
|
||||
@@ -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
@@ -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
@@ -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(",")
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user