mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7eb079050c | ||
|
|
2fdd5543b2 | ||
|
|
d04164c0a6 | ||
|
|
b047731f82 | ||
|
|
4d91f92a2e | ||
|
|
98505d27b1 | ||
|
|
cd63a58ad9 | ||
|
|
170f8dd092 | ||
|
|
619dcb6a84 | ||
|
|
99ae8ea52e | ||
|
|
dc031c30eb | ||
|
|
1e702439b7 | ||
|
|
8debc42381 | ||
|
|
532d719089 | ||
|
|
b40860aca4 | ||
|
|
2cbe6b5f7f | ||
|
|
d2cc7ccdfa | ||
|
|
2cb183c6d8 |
No files matched your search
+2
-3
@@ -68,6 +68,7 @@
|
||||
<inspection_tool class="BlockingMethodInNonBlockingContext" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="BooleanConstructor" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="BooleanExpressionMayBeConditional" enabled="false" level="INFORMATION" enabled_by_default="false" />
|
||||
<inspection_tool class="BooleanLiteralArgument" enabled="true" level="TEXT ATTRIBUTES" enabled_by_default="true" editorAttributes="CONSIDERATION_ATTRIBUTES" />
|
||||
<inspection_tool class="BooleanMethodIsAlwaysInverted" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="BoxingBoxedValue" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="BusyWait" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
@@ -765,7 +766,6 @@
|
||||
<inspection_tool class="MathRoundingWithIntArgument" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="MavenCoroutinesDeprecation" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="MeaninglessRecordAnnotationInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="MemberVisibilityCanBePrivate" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="MetaAnnotationWithoutRuntimeRetention" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="MethodCanBeVariableArityMethod" enabled="false" level="INFORMATION" enabled_by_default="false" />
|
||||
<inspection_tool class="MethodNameSameAsClassName" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
@@ -917,7 +917,6 @@
|
||||
<inspection_tool class="PrimitiveArrayArgumentToVariableArgMethod" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="ProblematicWhitespace" enabled="true" level="WEAK WARNING" enabled_by_default="true" editorAttributes="INFO_ATTRIBUTES" />
|
||||
<inspection_tool class="ProtectedMemberInFinalClass" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PublicApiImplicitType" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PublicField" enabled="false" level="INFORMATION" enabled_by_default="false">
|
||||
<option name="ignoreEnums" value="false" />
|
||||
<option name="ignorableAnnotations">
|
||||
@@ -995,7 +994,7 @@
|
||||
<option name="ignoreEmptySuperMethods" value="false" />
|
||||
<option name="onlyReportWhenAnnotated" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="RegExpAnonymousGroup" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="RegExpAnonymousGroup" enabled="true" level="WEAK WARNING" enabled_by_default="true" editorAttributes="INFO_ATTRIBUTES" />
|
||||
<inspection_tool class="RemoveLiteralUnderscores" enabled="false" level="INFORMATION" enabled_by_default="false" />
|
||||
<inspection_tool class="ReplaceAllDot" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="ReplaceAssignmentWithOperatorAssignment" enabled="false" level="INFORMATION" enabled_by_default="false">
|
||||
|
||||
@@ -14,5 +14,5 @@ interface IErrorReporter {
|
||||
|
||||
fun noErrorForLine(position: Position): Boolean
|
||||
|
||||
fun print_single_error(errormessage: String)
|
||||
fun printSingleError(errormessage: String)
|
||||
}
|
||||
@@ -656,16 +656,16 @@ class AsmGen6502Internal (
|
||||
}
|
||||
|
||||
if(expr.splitWords) {
|
||||
assignExpressionToRegister(expr.index, RegisterOrPair.fromCpuRegister(register), false)
|
||||
assignExpressionToRegister(expr.index, RegisterOrPair.fromCpuRegister(register))
|
||||
return
|
||||
}
|
||||
|
||||
when {
|
||||
expr.type.isByteOrBool -> {
|
||||
assignExpressionToRegister(expr.index, RegisterOrPair.fromCpuRegister(register), false)
|
||||
assignExpressionToRegister(expr.index, RegisterOrPair.fromCpuRegister(register))
|
||||
}
|
||||
expr.type.isWord -> {
|
||||
assignExpressionToRegister(expr.index, RegisterOrPair.A, false)
|
||||
assignExpressionToRegister(expr.index, RegisterOrPair.A)
|
||||
out(" asl a")
|
||||
when (register) {
|
||||
CpuRegister.A -> {}
|
||||
@@ -675,7 +675,7 @@ class AsmGen6502Internal (
|
||||
}
|
||||
expr.type.isFloat -> {
|
||||
require(options.compTarget.FLOAT_MEM_SIZE == 5) {"invalid float size ${expr.position}"}
|
||||
assignExpressionToRegister(expr.index, RegisterOrPair.A, false)
|
||||
assignExpressionToRegister(expr.index, RegisterOrPair.A)
|
||||
out("""
|
||||
sta P8ZP_SCRATCH_REG
|
||||
asl a
|
||||
@@ -750,7 +750,7 @@ class AsmGen6502Internal (
|
||||
TargetStorageKind.REGISTER -> {
|
||||
val zero = PtNumber(BaseDataType.UBYTE, 0.0, value.position)
|
||||
zero.parent = value
|
||||
assignExpressionToRegister(zero, target.register!!, false)
|
||||
assignExpressionToRegister(zero, target.register!!)
|
||||
return
|
||||
}
|
||||
else -> { }
|
||||
@@ -1084,12 +1084,19 @@ $repeatLabel""")
|
||||
else {
|
||||
if(evaluateAddressExpression) {
|
||||
val arrayIdx = jump.target as? PtArrayIndexer
|
||||
if(arrayIdx!=null && !arrayIdx.splitWords) {
|
||||
// if the jump target is an address in a non-split array (like a jump table of only pointers),
|
||||
// more optimal assembly can be generated using JMP address,X
|
||||
assignExpressionToRegister(arrayIdx.index, RegisterOrPair.A)
|
||||
out(" asl a | tax")
|
||||
return JumpTarget(asmSymbolName(arrayIdx.variable), true, true, false)
|
||||
if (isTargetCpu(CpuType.CPU65C02) && arrayIdx!=null) {
|
||||
if (!arrayIdx.splitWords) {
|
||||
// if the jump target is an address in a non-split array (like a jump table of only pointers),
|
||||
// on the 65c02, more optimal assembly can be generated using JMP address,X
|
||||
assignExpressionToRegister(arrayIdx.index, RegisterOrPair.A)
|
||||
out(" asl a | tax")
|
||||
return JumpTarget(asmSymbolName(arrayIdx.variable), true, true, false)
|
||||
} else {
|
||||
// print a message when more optimal code is possible
|
||||
val variable = symbolTable.lookup(arrayIdx.variable.name)!!
|
||||
if(variable is StStaticVariable && variable.length!!<=128)
|
||||
errors.info("the jump address array is @split, but @nosplit would create more efficient code here", jump.position)
|
||||
}
|
||||
}
|
||||
// we can do the address evaluation right now and just use a temporary pointer variable
|
||||
assignExpressionToVariable(jump.target, "P8ZP_SCRATCH_W1", DataType.UWORD)
|
||||
@@ -1279,7 +1286,7 @@ $repeatLabel""")
|
||||
}
|
||||
|
||||
if(addressExpr.operator=="+") {
|
||||
val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr, false)
|
||||
val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr)
|
||||
if (ptrAndIndex == null) return false
|
||||
|
||||
if(write) {
|
||||
@@ -1569,7 +1576,7 @@ $repeatLabel""")
|
||||
val compare = if(useSbc) "sec | sbc" else "cmp"
|
||||
fun cmpViaScratch() {
|
||||
if(assignmentAsmGen.directIntoY(value)) {
|
||||
assignExpressionToRegister(value, RegisterOrPair.Y, false)
|
||||
assignExpressionToRegister(value, RegisterOrPair.Y)
|
||||
out(" sty P8ZP_SCRATCH_REG")
|
||||
} else {
|
||||
out(" pha")
|
||||
@@ -1628,7 +1635,7 @@ $repeatLabel""")
|
||||
}
|
||||
|
||||
internal fun assignConditionValueToRegisterAndTest(condition: PtExpression) {
|
||||
assignExpressionToRegister(condition, RegisterOrPair.A, false)
|
||||
assignExpressionToRegister(condition, RegisterOrPair.A)
|
||||
when(condition) {
|
||||
is PtNumber,
|
||||
is PtBool,
|
||||
|
||||
@@ -508,6 +508,7 @@ private fun optimizeIncDec(linesByFour: Sequence<List<IndexedValue<String>>>): L
|
||||
|
||||
private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
|
||||
// jsr Sub + rts -> jmp Sub
|
||||
// jmp Sub + rts -> jmp Sub
|
||||
// rts + jmp -> remove jmp
|
||||
// rts + bxx -> remove bxx
|
||||
// lda + cmp #0 -> remove cmp, same for cpy and cpx.
|
||||
@@ -520,7 +521,10 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<Indexe
|
||||
val third = lines[2].value
|
||||
|
||||
if(!haslabel(second)) {
|
||||
if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
|
||||
if ((" jmp" in first || "\tjmp" in first ) && (" rts" in second || "\trts" in second)) {
|
||||
mods += Modification(lines[1].index, true, null)
|
||||
}
|
||||
else if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
|
||||
if("floats.pushFAC" !in first && "floats.popFAC" !in first) { // these 2 routines depend on being called with JSR!!
|
||||
mods += Modification(lines[0].index, false, lines[0].value.replace("jsr", "jmp"))
|
||||
mods += Modification(lines[1].index, true, null)
|
||||
|
||||
@@ -53,7 +53,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
val memread = PtMemoryByte(fcall.position)
|
||||
memread.add(fcall.args[0])
|
||||
memread.parent = fcall
|
||||
asmgen.assignExpressionToRegister(memread, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(memread, RegisterOrPair.A)
|
||||
asmgen.out(" pha")
|
||||
val memtarget = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.position, memory=memread)
|
||||
asmgen.assignExpressionTo(fcall.args[1], memtarget)
|
||||
@@ -676,7 +676,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
if(fcall.args[1].asConstInteger() == 0) {
|
||||
assignAsmGen.assignConstantByte(target, 0)
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
|
||||
assignAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
|
||||
}
|
||||
}
|
||||
@@ -815,7 +815,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" jsr floats.MOVFM")
|
||||
if(resultRegister!=null) {
|
||||
assignAsmGen.assignFAC1float(
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, fcall.definingISub(), fcall.position, null, null, null, resultRegister, null))
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, fcall.definingISub(), fcall.position))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ internal class ForLoopsAsmGen(
|
||||
// cannot use self-modifying code, cannot use cpu stack (because loop can be interrupted halfway)
|
||||
// so we need to store the loop end value in a newly allocated temporary variable
|
||||
val toValueVar = asmgen.getTempVarName(iterableDt.elementType().base)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A)
|
||||
asmgen.out(" sta $toValueVar")
|
||||
// pre-check for end already reached
|
||||
if(iterableDt.isSignedByteArray) {
|
||||
@@ -136,7 +136,7 @@ internal class ForLoopsAsmGen(
|
||||
|
||||
// use self-modifying code to store the loop end comparison value
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A)
|
||||
// pre-check for end already reached
|
||||
if(iterableDt.isSignedByteArray) {
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
@@ -186,7 +186,7 @@ $modifiedLabel cmp #0 ; modified
|
||||
val stepsize = range.step.asConstInteger()!!
|
||||
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A)
|
||||
// pre-check for end already reached
|
||||
if(iterableDt.isSignedByteArray) {
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
|
||||
@@ -234,9 +234,9 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
val param = sub.parameters[it]
|
||||
val arg = call.args[it]
|
||||
registersUsed += if(usesOtherRegistersWhileEvaluating(arg)) {
|
||||
if(!registersUsed.any{it.statusflag!=null || it.registerOrPair in CpuRegisters})
|
||||
if(!registersUsed.any{r -> r.statusflag!=null || r.registerOrPair in CpuRegisters})
|
||||
argumentViaRegister(sub, IndexedValue(it, param.second), arg)
|
||||
else if(registersUsed.any {it.statusflag!=null}) {
|
||||
else if(registersUsed.any { r-> r.statusflag!=null }) {
|
||||
throw AssemblyError("call argument evaluation problem: can't save cpu statusregister parameter ${call.position}")
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -154,7 +154,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
val elseLabel = asmgen.makeLabel("else")
|
||||
asmgen.out(" $elseBranchInstr $elseLabel")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -171,7 +171,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
if(target.indirect) {
|
||||
asmgen.out(" $falseBranch +")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -286,7 +286,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
if(target.indirect) {
|
||||
asmgen.out(" bmi + | beq +")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jumpAfterIf, true)
|
||||
target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -306,7 +306,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
val elseLabel = asmgen.makeLabel("else")
|
||||
asmgen.out(" bmi $elseLabel | beq $elseLabel")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -353,7 +353,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
if(target.indirect) {
|
||||
asmgen.out(" bmi + | bne ++")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jumpAfterIf, true)
|
||||
target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -375,7 +375,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
bpl $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -427,14 +427,14 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
else
|
||||
translateIfElseBodies("bpl", stmt)
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A)
|
||||
asmgen.cmpAwithByteValue(condition.right, false)
|
||||
if(jumpAfterIf!=null) {
|
||||
var target = asmgen.getJumpTarget(jumpAfterIf, false)
|
||||
if(target.indirect) {
|
||||
asmgen.out(" bcc + | beq +")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jumpAfterIf, true)
|
||||
target = asmgen.getJumpTarget(jumpAfterIf)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -453,7 +453,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
val elseLabel = asmgen.makeLabel("else")
|
||||
asmgen.out(" bcc $elseLabel | beq $elseLabel")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -535,7 +535,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
eor #128
|
||||
+ bpl +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -562,7 +562,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
eor #128
|
||||
+ bmi $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -590,7 +590,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
|
||||
cmp $valueLsb
|
||||
bcs +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
asmgen.out("""
|
||||
_jump jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -615,7 +615,7 @@ _jump jmp (${target.asmLabel})
|
||||
sbc $valueMsb
|
||||
bcs $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -667,7 +667,7 @@ _jump jmp (${target.asmLabel})
|
||||
eor #128
|
||||
+ bpl +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -694,7 +694,7 @@ _jump jmp (${target.asmLabel})
|
||||
eor #128
|
||||
+ bmi $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -721,7 +721,7 @@ _jump jmp (${target.asmLabel})
|
||||
sbc $valueMsb
|
||||
bcc +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -744,7 +744,7 @@ _jump jmp (${target.asmLabel})
|
||||
sbc $valueMsb
|
||||
bcc $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -829,7 +829,7 @@ _jump jmp (${target.asmLabel})
|
||||
lda $valueLsb
|
||||
bne ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -857,7 +857,7 @@ _jump jmp (${target.asmLabel})
|
||||
bne $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -908,7 +908,7 @@ _jump jmp (${target.asmLabel})
|
||||
cmp #0
|
||||
bne ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -936,7 +936,7 @@ _jump jmp (${target.asmLabel})
|
||||
bne $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -973,7 +973,7 @@ _jump jmp (${target.asmLabel})
|
||||
lda $valueLsb
|
||||
beq ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -1001,7 +1001,7 @@ _jump jmp (${target.asmLabel})
|
||||
beq $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -1053,7 +1053,7 @@ _jump jmp (${target.asmLabel})
|
||||
cmp #0
|
||||
beq ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -1081,7 +1081,7 @@ _jump jmp (${target.asmLabel})
|
||||
beq $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -1196,7 +1196,7 @@ _jump jmp (${target.asmLabel})
|
||||
cpy $valueMsb
|
||||
beq ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -1221,7 +1221,7 @@ _jump jmp (${target.asmLabel})
|
||||
beq $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -1248,7 +1248,7 @@ _jump jmp (${target.asmLabel})
|
||||
cpy $valueMsb
|
||||
bne +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -1273,7 +1273,7 @@ _jump jmp (${target.asmLabel})
|
||||
cpy $valueMsb
|
||||
bne $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -1302,7 +1302,7 @@ _jump jmp (${target.asmLabel})
|
||||
cmp ${right.name}+1
|
||||
beq ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -1331,7 +1331,7 @@ _jump jmp (${target.asmLabel})
|
||||
beq $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -1361,7 +1361,7 @@ _jump jmp (${target.asmLabel})
|
||||
cmp ${right.name}+1
|
||||
bne +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -1390,7 +1390,7 @@ _jump jmp (${target.asmLabel})
|
||||
cmp ${right.name}+1
|
||||
bne $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -1423,7 +1423,7 @@ _jump jmp (${target.asmLabel})
|
||||
cmp #>$value
|
||||
beq ++""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
asmgen.out("""
|
||||
+ jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -1452,7 +1452,7 @@ _jump jmp (${target.asmLabel})
|
||||
beq $elseLabel
|
||||
+""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
@@ -1481,7 +1481,7 @@ _jump jmp (${target.asmLabel})
|
||||
cmp #>$value
|
||||
bne +""")
|
||||
if(target.needsExpressionEvaluation)
|
||||
target = asmgen.getJumpTarget(jump, true)
|
||||
target = asmgen.getJumpTarget(jump)
|
||||
asmgen.out("""
|
||||
jmp (${target.asmLabel})
|
||||
+""")
|
||||
@@ -1510,7 +1510,7 @@ _jump jmp (${target.asmLabel})
|
||||
cmp #>$value
|
||||
bne $elseLabel""")
|
||||
asmgen.translate(stmt.ifScope)
|
||||
asmgen.jmp(afterIfLabel, false)
|
||||
asmgen.jmp(afterIfLabel)
|
||||
asmgen.out(elseLabel)
|
||||
asmgen.translate(stmt.elseScope)
|
||||
} else {
|
||||
|
||||
@@ -15,18 +15,18 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
evalIfExpressionConditonAndBranchWhenFalse(expr.condition, falseLabel)
|
||||
when {
|
||||
expr.type.isByteOrBool -> {
|
||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A)
|
||||
asmgen.jmp(endLabel)
|
||||
asmgen.out(falseLabel)
|
||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A)
|
||||
asmgen.out(endLabel)
|
||||
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
|
||||
}
|
||||
expr.type.isWord || expr.type.isString -> {
|
||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY)
|
||||
asmgen.jmp(endLabel)
|
||||
asmgen.out(falseLabel)
|
||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY)
|
||||
asmgen.out(endLabel)
|
||||
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
}
|
||||
@@ -195,7 +195,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
beq $falseLabel
|
||||
+""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
cmp $varRight
|
||||
bne +
|
||||
@@ -219,7 +219,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
cmp $varRight+1
|
||||
bne $falseLabel""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
cmp $varRight
|
||||
bne $falseLabel
|
||||
@@ -242,7 +242,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
beq $falseLabel
|
||||
+""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
cmp #<$number
|
||||
bne +
|
||||
@@ -265,7 +265,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
cmp #>$number
|
||||
bne $falseLabel""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||
asmgen.out( """
|
||||
cmp #<$number
|
||||
bne $falseLabel
|
||||
@@ -284,7 +284,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
ora $varname+1
|
||||
beq $falseLabel""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | beq $falseLabel")
|
||||
}
|
||||
}
|
||||
@@ -299,7 +299,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
|
||||
ora $varname+1
|
||||
bne $falseLabel""")
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
|
||||
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | bne $falseLabel")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,7 +353,9 @@ internal class ProgramAndVarsGen(
|
||||
initializers.forEach { assign ->
|
||||
if((assign.value as? PtNumber)?.number != 0.0 || allocator.isZpVar(assign.target.identifier!!.name))
|
||||
asmgen.translate(assign)
|
||||
// the other variables that should be set to zero are done so as part of the BSS section.
|
||||
else
|
||||
throw AssemblyError("non-zp variable should not be initialized to zero; it will be zeroed as part of BSS clear")
|
||||
// the other variables that should be set to zero are done so as part of the BSS section clear.
|
||||
}
|
||||
asmgen.out(" rts\n .bend")
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ internal class AnyExprAsmGen(
|
||||
private fun assignByteBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
|
||||
when(expr.operator) {
|
||||
"+" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | clc | adc P8ZP_SCRATCH_B1")
|
||||
@@ -61,7 +61,7 @@ internal class AnyExprAsmGen(
|
||||
return true
|
||||
}
|
||||
"-" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | sec | sbc P8ZP_SCRATCH_B1")
|
||||
@@ -76,7 +76,7 @@ internal class AnyExprAsmGen(
|
||||
"and" -> TODO("logical and (with optional shortcircuit) ${expr.position}")
|
||||
"or" -> TODO("logical or (with optional shortcircuit) ${expr.position}")
|
||||
"&" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | and P8ZP_SCRATCH_B1")
|
||||
@@ -84,7 +84,7 @@ internal class AnyExprAsmGen(
|
||||
return true
|
||||
}
|
||||
"|" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | ora P8ZP_SCRATCH_B1")
|
||||
@@ -92,7 +92,7 @@ internal class AnyExprAsmGen(
|
||||
return true
|
||||
}
|
||||
"^", "xor" -> {
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
|
||||
asmgen.out(" pha")
|
||||
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
|
||||
asmgen.out(" pla | eor P8ZP_SCRATCH_B1")
|
||||
|
||||
@@ -1313,7 +1313,7 @@ internal class AssignmentAsmGen(
|
||||
// special optimization for bytearray[y] + bytevalue : no need to use a tempvar, just use adc array,y
|
||||
assignExpressionToRegister(right, RegisterOrPair.A, right.type.isSigned)
|
||||
if(!leftArrayIndexer.index.isSimple()) asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(leftArrayIndexer.index, RegisterOrPair.Y, false)
|
||||
asmgen.assignExpressionToRegister(leftArrayIndexer.index, RegisterOrPair.Y)
|
||||
if(!leftArrayIndexer.index.isSimple()) asmgen.out(" pla")
|
||||
val arrayvarname = asmgen.asmSymbolName(leftArrayIndexer.variable)
|
||||
asmgen.out(" clc | adc $arrayvarname,y")
|
||||
@@ -1322,7 +1322,7 @@ internal class AssignmentAsmGen(
|
||||
// special optimization for bytevalue +/- bytearray[y] : no need to use a tempvar, just use adc array,y or sbc array,y
|
||||
assignExpressionToRegister(left, RegisterOrPair.A, left.type.isSigned)
|
||||
if(!rightArrayIndexer.index.isSimple()) asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(rightArrayIndexer.index, RegisterOrPair.Y, false)
|
||||
asmgen.assignExpressionToRegister(rightArrayIndexer.index, RegisterOrPair.Y)
|
||||
if(!rightArrayIndexer.index.isSimple()) asmgen.out(" pla")
|
||||
val arrayvarname = asmgen.asmSymbolName(rightArrayIndexer.variable)
|
||||
if (expr.operator == "+")
|
||||
@@ -2855,7 +2855,7 @@ $endLabel""")
|
||||
jsr floats.MOVMF""")
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A)
|
||||
asmgen.out("""
|
||||
ldy #<${target.asmVarname}
|
||||
sty P8ZP_SCRATCH_W1
|
||||
@@ -2887,7 +2887,7 @@ $endLabel""")
|
||||
TargetStorageKind.ARRAY -> {
|
||||
asmgen.out(" pha")
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A)
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.out(" pla")
|
||||
asmgen.out("""
|
||||
@@ -2924,7 +2924,7 @@ $endLabel""")
|
||||
jsr floats.copy_float""")
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A)
|
||||
asmgen.out("""
|
||||
ldy #<$sourceName
|
||||
sty P8ZP_SCRATCH_W1
|
||||
@@ -3397,7 +3397,7 @@ $endLabel""")
|
||||
} else {
|
||||
require(target.array.index.type.isByteOrBool)
|
||||
asmgen.saveRegisterStack(register, false)
|
||||
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.Y, false)
|
||||
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.Y)
|
||||
asmgen.out(" pla | sta ${target.asmVarname},y")
|
||||
}
|
||||
}
|
||||
@@ -3712,7 +3712,7 @@ $endLabel""")
|
||||
asmgen.out(" stz ${target.asmVarname}+$indexValue")
|
||||
}
|
||||
else {
|
||||
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.X, false)
|
||||
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.X)
|
||||
asmgen.out(" stz ${target.asmVarname},x")
|
||||
}
|
||||
}
|
||||
@@ -3802,7 +3802,7 @@ $endLabel""")
|
||||
sta ${target.asmVarname}+4""")
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A)
|
||||
asmgen.out("""
|
||||
ldy #<${target.asmVarname}
|
||||
sty P8ZP_SCRATCH_W1
|
||||
@@ -3836,7 +3836,7 @@ $endLabel""")
|
||||
jsr floats.copy_float""")
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A)
|
||||
asmgen.out("""
|
||||
ldy #<${constFloat}
|
||||
sty P8ZP_SCRATCH_W1
|
||||
|
||||
@@ -170,7 +170,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
if(memory.address is PtBinaryExpression && tryOptimizedMemoryInplace(memory.address as PtBinaryExpression, operator, value))
|
||||
return
|
||||
// slower method to calculate and use the pointer to access the memory with:
|
||||
asmgen.assignExpressionToRegister(memory.address, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(memory.address, RegisterOrPair.AY)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, true)
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, true)
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65C02))
|
||||
@@ -583,7 +583,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
if(address.left is PtIdentifier && asmgen.isZpVar(address.left as PtIdentifier)) {
|
||||
return (address.left as PtIdentifier).name
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY, false)
|
||||
asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY)
|
||||
asmgen.out(" sta P8ZP_SCRATCH_W2 | sty P8ZP_SCRATCH_W2+1")
|
||||
return "P8ZP_SCRATCH_W2"
|
||||
}
|
||||
@@ -635,9 +635,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
return true
|
||||
}
|
||||
if(rightTc!=null)
|
||||
asmgen.assignExpressionToRegister(rightTc.value, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(rightTc.value, RegisterOrPair.A)
|
||||
else
|
||||
asmgen.assignExpressionToRegister(address.right, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(address.right, RegisterOrPair.A)
|
||||
asmgen.out(" pha") // offset on stack
|
||||
val zpPointerVarName = addrIntoZpPointer()
|
||||
assignValueToA()
|
||||
@@ -2740,7 +2740,7 @@ $shortcutLabel:""")
|
||||
"+" -> {
|
||||
// name += byteexpression
|
||||
if(valueDt.isUnsignedByte) {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, false)
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
|
||||
asmgen.out("""
|
||||
clc
|
||||
adc $name
|
||||
|
||||
@@ -68,7 +68,7 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
||||
|
||||
override fun noErrors(): Boolean = errors.isEmpty()
|
||||
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
|
||||
override fun print_single_error(errormessage: String) { /* prints nothing in tests */ }
|
||||
override fun printSingleError(errormessage: String) { /* prints nothing in tests */ }
|
||||
|
||||
override fun report() {
|
||||
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package prog8.codegen.experimental
|
||||
|
||||
import prog8.code.IAssemblyProgram
|
||||
import prog8.code.ICodeGeneratorBackend
|
||||
import prog8.code.SymbolTable
|
||||
import prog8.code.ast.PtProgram
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.IAssemblyProgram
|
||||
import prog8.code.ICodeGeneratorBackend
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.codegen.intermediate.IRCodeGen
|
||||
import prog8.intermediate.IRFileWriter
|
||||
@@ -26,7 +26,8 @@ class ExperiCodeGen: ICodeGeneratorBackend {
|
||||
// this stub only writes the IR program to disk but doesn't generate anything else.
|
||||
IRFileWriter(irProgram, null).write()
|
||||
|
||||
println("** experimental codegen stub: no assembly generated **")
|
||||
if(!options.quiet)
|
||||
println("** experimental codegen stub: no assembly generated **")
|
||||
return EmptyProgram
|
||||
}
|
||||
}
|
||||
@@ -34,7 +35,8 @@ class ExperiCodeGen: ICodeGeneratorBackend {
|
||||
private object EmptyProgram : IAssemblyProgram {
|
||||
override val name = "<Empty Program>"
|
||||
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
|
||||
println("** nothing assembled **")
|
||||
if(!options.quiet)
|
||||
println("** nothing assembled **")
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
|
||||
private fun funcCallfar(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val bankTr = exprGen.translateExpression(call.args[0])
|
||||
val addressTr = exprGen.translateExpression(call.args[1])
|
||||
val argumentwordTr = exprGen.translateExpression(call.args[2])
|
||||
@@ -91,7 +90,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
|
||||
private fun funcCallfar2(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val bankTr = exprGen.translateExpression(call.args[0])
|
||||
val addressTr = exprGen.translateExpression(call.args[1])
|
||||
val argumentA = exprGen.translateExpression(call.args[2])
|
||||
@@ -143,7 +141,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
|
||||
private fun funcStringCompare(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val left = exprGen.translateExpression(call.args[0])
|
||||
val right = exprGen.translateExpression(call.args[1])
|
||||
addToResult(result, left, left.resultReg, -1)
|
||||
@@ -240,11 +237,11 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
}
|
||||
BaseDataType.UWORD -> {
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val resultReg = codeGen.registers.next(IRDataType.WORD)
|
||||
val resultReg = codeGen.registers.next(IRDataType.BYTE) // sqrt of a word still produces just a byte result
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultReg, reg2=tr.resultReg)
|
||||
}
|
||||
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
BaseDataType.FLOAT -> {
|
||||
addToResult(result, tr, -1, tr.resultFpReg)
|
||||
@@ -278,7 +275,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
|
||||
private fun funcClamp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val type = irType(call.type)
|
||||
val valueTr = exprGen.translateExpression(call.args[0])
|
||||
val minimumTr = exprGen.translateExpression(call.args[1])
|
||||
|
||||
@@ -289,7 +289,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
val haystackVar = check.haystackHeapVar!!
|
||||
when {
|
||||
haystackVar.type.isString -> {
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val elementTr = translateExpression(check.needle)
|
||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||
val iterableTr = translateExpression(haystackVar)
|
||||
@@ -300,7 +299,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
haystackVar.type.isByteArray -> {
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val elementTr = translateExpression(check.needle)
|
||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||
val iterableTr = translateExpression(haystackVar)
|
||||
@@ -314,7 +312,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
haystackVar.type.isWordArray -> {
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val elementTr = translateExpression(check.needle)
|
||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||
val iterableTr = translateExpression(haystackVar)
|
||||
@@ -328,7 +325,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
haystackVar.type.isFloatArray -> {
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val elementTr = translateExpression(check.needle)
|
||||
addToResult(result, elementTr, -1, elementTr.resultFpReg)
|
||||
val iterableTr = translateExpression(haystackVar)
|
||||
@@ -618,7 +614,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
when (callTarget) {
|
||||
is StSub -> {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = callTarget.parameters.size), null)
|
||||
// assign the arguments
|
||||
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
|
||||
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
||||
@@ -670,7 +665,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
is StExtSub -> {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = callTarget.parameters.size), null)
|
||||
// assign the arguments
|
||||
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
|
||||
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
|
||||
@@ -709,9 +703,21 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
Opcode.CALL,
|
||||
address = address.address.toInt(),
|
||||
fcallArgs = FunctionCallArgs(argRegisters, returnRegs))
|
||||
}
|
||||
else {
|
||||
TODO("callfar into another bank is not implemented for the selected compilation target")
|
||||
} else if(address.constbank!=null) {
|
||||
IRInstruction(
|
||||
Opcode.CALLFAR,
|
||||
address = address.address.toInt(),
|
||||
immediate = address.constbank!!.toInt()
|
||||
)
|
||||
} else {
|
||||
val tr = translateExpression(address.varbank!!)
|
||||
require(tr.dt==IRDataType.BYTE)
|
||||
result += tr.chunks
|
||||
IRInstruction(
|
||||
Opcode.CALLFARVB,
|
||||
address = address.address.toInt(),
|
||||
reg1 = tr.resultReg
|
||||
)
|
||||
}
|
||||
}
|
||||
addInstr(result, call, null)
|
||||
|
||||
@@ -48,6 +48,7 @@ class IRCodeGen(
|
||||
irProg.linkChunks()
|
||||
irProg.convertAsmChunks()
|
||||
|
||||
// the optimizer also does 1 essential step regardless of optimizations: joining adjacent chunks.
|
||||
val optimizer = IRPeepholeOptimizer(irProg)
|
||||
optimizer.optimize(options.optimize, errors)
|
||||
irProg.validate()
|
||||
@@ -64,15 +65,17 @@ class IRCodeGen(
|
||||
if(variable.uninitialized && variable.parent.type==StNodeType.BLOCK) {
|
||||
val block = variable.parent.astNode as PtBlock
|
||||
val initialization = (block.children.firstOrNull {
|
||||
it is PtAssignment && it.target.identifier?.name==variable.scopedName
|
||||
it is PtAssignment && it.isVarInitializer && it.target.identifier?.name==variable.scopedName
|
||||
} as PtAssignment?)
|
||||
val initValue = initialization?.value
|
||||
when(initValue){
|
||||
is PtBool -> {
|
||||
require(initValue.asInt()!=0) { "boolean var should not be initialized with false, it wil be set to false as part of BSS clear, initializer=$initialization" }
|
||||
variable.setOnetimeInitNumeric(initValue.asInt().toDouble())
|
||||
initsToRemove += block to initialization
|
||||
}
|
||||
is PtNumber -> {
|
||||
require(initValue.number!=0.0) { "variable should not be initialized with 0, it will already be zeroed as part of BSS clear, initializer=$initialization" }
|
||||
variable.setOnetimeInitNumeric(initValue.number)
|
||||
initsToRemove += block to initialization
|
||||
}
|
||||
@@ -194,10 +197,7 @@ class IRCodeGen(
|
||||
old.fpReg1,
|
||||
old.fpReg2,
|
||||
immediate = immediateValue,
|
||||
null,
|
||||
address = addressValue,
|
||||
null,
|
||||
null
|
||||
address = addressValue
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -425,19 +425,19 @@ class IRCodeGen(
|
||||
} else {
|
||||
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 ->
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
|
||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = endLabel)
|
||||
choice.values.children.map { v -> v as PtNumber }.sortedBy { v -> v.number }.forEach { value ->
|
||||
result += IRCodeChunk(null, null).also { chunk ->
|
||||
chunk += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
|
||||
chunk += IRInstruction(Opcode.BSTEQ, labelSymbol = endLabel)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val choiceLabel = createLabelName()
|
||||
choices.add(choiceLabel to choice)
|
||||
choice.values.children.map { it as PtNumber }.sortedBy { it.number }.forEach { value ->
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
|
||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = choiceLabel)
|
||||
choice.values.children.map { v -> v as PtNumber }.sortedBy { v -> v.number }.forEach { value ->
|
||||
result += IRCodeChunk(null, null).also { chunk ->
|
||||
chunk += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
|
||||
chunk += IRInstruction(Opcode.BSTEQ, labelSymbol = choiceLabel)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1070,7 +1070,7 @@ class IRCodeGen(
|
||||
}
|
||||
// evaluate jump address expression into a register and jump indirectly to it
|
||||
val tr = expressionEval.translateExpression(goto.target)
|
||||
for(i in tr.chunks.flatMap { it.instructions }) {
|
||||
for(i in tr.chunks.flatMap { c -> c.instructions }) {
|
||||
it += i
|
||||
}
|
||||
it += IRInstruction(Opcode.JUMPI, reg1 = tr.resultReg)
|
||||
|
||||
@@ -23,6 +23,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
}
|
||||
|
||||
private fun optimizeOnlyJoinChunks() {
|
||||
// this chunk-joining is REQUIRED (optimization or no) to end up with a structurally sound chunk list
|
||||
irprog.foreachSub { sub ->
|
||||
joinChunks(sub)
|
||||
removeEmptyChunks(sub)
|
||||
@@ -84,7 +85,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
/*
|
||||
Empty Code chunk with label ->
|
||||
If next chunk has no label -> move label to next chunk, remove original
|
||||
If next chunk has label -> label name should be the same, remove original, otherwise merge both labels into 1.
|
||||
If next chunk has label -> label name should be the same, in which case remove original, otherwise leave everything untouched.
|
||||
If is last chunk -> keep chunk in place because of the label.
|
||||
Empty Code chunk without label ->
|
||||
should not have been generated! ERROR.
|
||||
@@ -111,14 +112,6 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
|
||||
if(index>0) {
|
||||
if (chunk.label == nextchunk.label)
|
||||
removeChunks += index
|
||||
else {
|
||||
removeChunks += index
|
||||
replaceLabels[chunk.label!!] = nextchunk.label!!
|
||||
replaceLabels.entries.forEach { (key, value) ->
|
||||
if (value == chunk.label)
|
||||
replaceLabels[key] = nextchunk.label!!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
||||
|
||||
override fun noErrors(): Boolean = errors.isEmpty()
|
||||
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
|
||||
override fun print_single_error(errormessage: String) { /* prints nothing in tests */ }
|
||||
override fun printSingleError(errormessage: String) { /* prints nothing in tests */ }
|
||||
|
||||
override fun report() {
|
||||
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
|
||||
|
||||
@@ -53,7 +53,7 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
irProg.chunks().single().instructions.size shouldBe 1
|
||||
}
|
||||
|
||||
test("remove jmp to label below") {
|
||||
test("remove jmp to label below but keep labels") {
|
||||
val c1 = IRCodeChunk("main.start", null)
|
||||
c1 += IRInstruction(Opcode.JUMP, labelSymbol = "label")
|
||||
val c2 = IRCodeChunk("label", null)
|
||||
@@ -69,13 +69,16 @@ class TestIRPeepholeOpt: FunSpec({
|
||||
irProg.chunks().flatMap { it.instructions }.size shouldBe 5
|
||||
val opt = IRPeepholeOptimizer(irProg)
|
||||
opt.optimize(true, ErrorReporterForTests())
|
||||
irProg.chunks().size shouldBe 3
|
||||
irProg.chunks()[0].label shouldBe "main.start"
|
||||
irProg.chunks()[1].label shouldBe "label2"
|
||||
irProg.chunks()[2].label shouldBe "label3"
|
||||
irProg.chunks()[0].isEmpty() shouldBe true
|
||||
irProg.chunks()[1].isEmpty() shouldBe false
|
||||
irProg.chunks()[2].isEmpty() shouldBe true
|
||||
val chunks = irProg.chunks()
|
||||
chunks.size shouldBe 4
|
||||
chunks[0].label shouldBe "main.start"
|
||||
chunks[1].label shouldBe "label"
|
||||
chunks[2].label shouldBe "label2"
|
||||
chunks[3].label shouldBe "label3"
|
||||
chunks[0].isEmpty() shouldBe true
|
||||
chunks[1].isEmpty() shouldBe true
|
||||
chunks[2].isEmpty() shouldBe false
|
||||
chunks[3].isEmpty() shouldBe true
|
||||
val instr = irProg.chunks().flatMap { it.instructions }
|
||||
instr.size shouldBe 2
|
||||
instr[0].opcode shouldBe Opcode.JUMP
|
||||
|
||||
@@ -554,11 +554,8 @@ class TestVmCodeGen: FunSpec({
|
||||
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
|
||||
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||
irChunks.size shouldBe 1
|
||||
irChunks[0].instructions.size shouldBe 2
|
||||
val preparecallInstr = irChunks[0].instructions[0]
|
||||
preparecallInstr.opcode shouldBe Opcode.PREPARECALL
|
||||
preparecallInstr.immediate shouldBe 0
|
||||
val callInstr = irChunks[0].instructions[1]
|
||||
irChunks[0].instructions.size shouldBe 1
|
||||
val callInstr = irChunks[0].instructions[0]
|
||||
callInstr.opcode shouldBe Opcode.CALL
|
||||
callInstr.address shouldBe 0x5000
|
||||
}
|
||||
|
||||
@@ -5,10 +5,7 @@ import prog8.ast.FatalAstException
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.expressions.FunctionCallExpression
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.core.isInteger
|
||||
import prog8.code.core.isIntegerOrBool
|
||||
import prog8.code.core.*
|
||||
import kotlin.math.*
|
||||
|
||||
|
||||
@@ -70,52 +67,45 @@ class ConstExprEvaluator {
|
||||
}
|
||||
|
||||
private fun bitwiseXor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) {
|
||||
if(right.type.isIntegerOrBool) {
|
||||
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() xor (right.number.toInt() and 255)).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type==BaseDataType.UWORD) {
|
||||
if(right.type.isInteger) {
|
||||
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() xor right.number.toInt() and 65535).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type==BaseDataType.LONG) {
|
||||
if(right.type.isInteger) {
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() xor right.number.toInt()).toDouble(), left.position)
|
||||
if(right.type.isIntegerOrBool) {
|
||||
val leftDt = left.type
|
||||
if(leftDt.isByteOrBool)
|
||||
return NumericLiteral(BaseDataType.UBYTE, ((left.number.toInt() xor right.number.toInt()) and 255).toDouble(), left.position)
|
||||
else if(leftDt.isInteger) {
|
||||
if (leftDt == BaseDataType.UWORD || leftDt == BaseDataType.WORD)
|
||||
return NumericLiteral(BaseDataType.UWORD, ((left.number.toInt() xor right.number.toInt()) and 65535).toDouble(), left.position)
|
||||
else if (leftDt == BaseDataType.LONG)
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() xor right.number.toInt()).toDouble(), left.position)
|
||||
}
|
||||
}
|
||||
|
||||
throw ExpressionError("cannot calculate $left ^ $right", left.position)
|
||||
}
|
||||
|
||||
private fun bitwiseOr(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) {
|
||||
if(right.type.isIntegerOrBool) {
|
||||
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type==BaseDataType.UWORD) {
|
||||
if(right.type.isInteger) {
|
||||
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() or right.number.toInt() and 65535).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type==BaseDataType.LONG) {
|
||||
if(right.type.isInteger) {
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() or right.number.toInt()).toDouble(), left.position)
|
||||
if(right.type.isIntegerOrBool) {
|
||||
val leftDt = left.type
|
||||
if(leftDt.isByteOrBool)
|
||||
return NumericLiteral(BaseDataType.UBYTE, ((left.number.toInt() or right.number.toInt()) and 255).toDouble(), left.position)
|
||||
else if(leftDt.isInteger) {
|
||||
if (leftDt == BaseDataType.UWORD || leftDt == BaseDataType.WORD)
|
||||
return NumericLiteral(BaseDataType.UWORD, ((left.number.toInt() or right.number.toInt()) and 65535).toDouble(), left.position)
|
||||
else if (leftDt == BaseDataType.LONG)
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() or right.number.toInt()).toDouble(), left.position)
|
||||
}
|
||||
}
|
||||
throw ExpressionError("cannot calculate $left | $right", left.position)
|
||||
}
|
||||
|
||||
private fun bitwiseAnd(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) {
|
||||
if(right.type.isIntegerOrBool) {
|
||||
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() and (right.number.toInt() and 255)).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type==BaseDataType.UWORD) {
|
||||
if(right.type.isInteger) {
|
||||
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() and right.number.toInt() and 65535).toDouble(), left.position)
|
||||
}
|
||||
} else if(left.type==BaseDataType.LONG) {
|
||||
if(right.type.isInteger) {
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() and right.number.toInt()).toDouble(), left.position)
|
||||
if(right.type.isIntegerOrBool) {
|
||||
val leftDt = left.type
|
||||
if(leftDt.isByteOrBool)
|
||||
return NumericLiteral(BaseDataType.UBYTE, ((left.number.toInt() and right.number.toInt()) and 255).toDouble(), left.position)
|
||||
else if(leftDt.isInteger) {
|
||||
if (leftDt == BaseDataType.UWORD || leftDt == BaseDataType.WORD)
|
||||
return NumericLiteral(BaseDataType.UWORD, ((left.number.toInt() and right.number.toInt()) and 65535).toDouble(), left.position)
|
||||
else if (leftDt == BaseDataType.LONG)
|
||||
return NumericLiteral.optimalNumeric((left.number.toInt() and right.number.toInt()).toDouble(), left.position)
|
||||
}
|
||||
}
|
||||
throw ExpressionError("cannot calculate $left & $right", left.position)
|
||||
|
||||
@@ -129,7 +129,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
return listOf(IAstModification.ReplaceNode(expr, newArray, parent))
|
||||
}
|
||||
else {
|
||||
val leftTarget = (expr.left as? IdentifierReference)?.targetVarDecl(program)
|
||||
val leftTarget = (expr.left as? IdentifierReference)?.targetVarDecl()
|
||||
if(leftTarget!=null && leftTarget.origin==VarDeclOrigin.ARRAYLITERAL)
|
||||
throw FatalAstException("shouldn't see an array literal converted to an autovar here")
|
||||
}
|
||||
@@ -332,7 +332,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
|
||||
val constIndex = arrayIndexedExpression.indexer.constIndex()
|
||||
if (constIndex != null) {
|
||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
|
||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl()
|
||||
if(arrayVar!=null) {
|
||||
val array =arrayVar.value as? ArrayLiteral
|
||||
if(array!=null) {
|
||||
@@ -387,7 +387,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
val rangeTo = iterableRange.to as? NumericLiteral
|
||||
if(rangeFrom==null || rangeTo==null) return noModifications
|
||||
|
||||
val loopvar = forLoop.loopVar.targetVarDecl(program) ?: return noModifications
|
||||
val loopvar = forLoop.loopVar.targetVarDecl() ?: return noModifications
|
||||
|
||||
val stepLiteral = iterableRange.step as? NumericLiteral
|
||||
require(loopvar.datatype.sub == null)
|
||||
|
||||
@@ -560,7 +560,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
}
|
||||
}
|
||||
else if(functionCallExpr.target.nameInSource == listOf("strings", "contains")) {
|
||||
val target = (functionCallExpr.args[0] as? IdentifierReference)?.targetVarDecl(program)
|
||||
val target = (functionCallExpr.args[0] as? IdentifierReference)?.targetVarDecl()
|
||||
if(target?.value is StringLiteral) {
|
||||
errors.info("for actual strings, use a regular containment check instead: 'char in string'", functionCallExpr.position)
|
||||
val contains = ContainmentCheck(functionCallExpr.args[1], functionCallExpr.args[0], functionCallExpr.position)
|
||||
|
||||
@@ -16,7 +16,7 @@ import prog8.code.target.VMTarget
|
||||
private fun isEmptyReturn(stmt: Statement): Boolean = stmt is Return && stmt.values.isEmpty()
|
||||
|
||||
|
||||
// inliner potentially enables *ONE LINED* subroutines, wihtout to be inlined.
|
||||
// inliner potentially enables *ONE LINED* subroutines, without to be inlined.
|
||||
|
||||
class Inliner(private val program: Program, private val options: CompilationOptions): AstWalker() {
|
||||
|
||||
@@ -105,7 +105,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
||||
|
||||
if (subroutine.inline && subroutine.statements.size > 1) {
|
||||
require(subroutine.statements.size == 2 && isEmptyReturn(subroutine.statements[1]))
|
||||
subroutine.statements.removeLast() // get rid of the Return, to be able to inline the (single) statement preceding it.
|
||||
subroutine.statements.removeLastOrNull() // get rid of the Return, to be able to inline the (single) statement preceding it.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,7 +122,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
||||
|
||||
private fun makeFullyScoped(call: FunctionCallStatement) {
|
||||
makeFullyScoped(call.target)
|
||||
call.target.targetSubroutine(program)?.let { sub ->
|
||||
call.target.targetSubroutine()?.let { sub ->
|
||||
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
||||
val scopedArgs = makeScopedArgs(call.args)
|
||||
if(scopedArgs.any()) {
|
||||
@@ -134,7 +134,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
|
||||
|
||||
private fun makeFullyScoped(call: FunctionCallExpression) {
|
||||
makeFullyScoped(call.target)
|
||||
call.target.targetSubroutine(program)?.let { sub ->
|
||||
call.target.targetSubroutine()?.let { sub ->
|
||||
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
||||
val scopedArgs = makeScopedArgs(call.args)
|
||||
if(scopedArgs.any()) {
|
||||
|
||||
@@ -33,8 +33,8 @@ class StatementOptimizer(private val program: Program,
|
||||
} else {
|
||||
arg as? IdentifierReference
|
||||
}
|
||||
if(stringVar!=null && stringVar.wasStringLiteral(program)) {
|
||||
val string = stringVar.targetVarDecl(program)?.value as? StringLiteral
|
||||
if(stringVar!=null && stringVar.wasStringLiteral()) {
|
||||
val string = stringVar.targetVarDecl()?.value as? StringLiteral
|
||||
if(string!=null) {
|
||||
val pos = functionCallStatement.position
|
||||
if (string.value.length == 1) {
|
||||
@@ -152,7 +152,7 @@ class StatementOptimizer(private val program: Program,
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
|
||||
}
|
||||
}
|
||||
val iterable = (forLoop.iterable as? IdentifierReference)?.targetVarDecl(program)
|
||||
val iterable = (forLoop.iterable as? IdentifierReference)?.targetVarDecl()
|
||||
if(iterable!=null) {
|
||||
if(iterable.datatype.isString) {
|
||||
val sv = iterable.value as StringLiteral
|
||||
|
||||
@@ -8,9 +8,9 @@ import prog8.ast.expressions.TypecastExpression
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.INTERNED_STRINGS_MODULENAME
|
||||
import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.INTERNED_STRINGS_MODULENAME
|
||||
import prog8.compiler.CallGraph
|
||||
|
||||
|
||||
@@ -27,10 +27,10 @@ class UnusedCodeRemover(private val program: Program,
|
||||
|
||||
program.allBlocks.singleOrNull { it.name=="sys" } ?.let {
|
||||
val subroutines = it.statements.filterIsInstance<Subroutine>()
|
||||
val push = subroutines.single { it.name == "push" }
|
||||
val pushw = subroutines.single { it.name == "pushw" }
|
||||
val pop = subroutines.single { it.name == "pop" }
|
||||
val popw = subroutines.single { it.name == "popw" }
|
||||
val push = subroutines.single { s -> s.name == "push" }
|
||||
val pushw = subroutines.single { s -> s.name == "pushw" }
|
||||
val pop = subroutines.single { s -> s.name == "pop" }
|
||||
val popw = subroutines.single { s -> s.name == "popw" }
|
||||
neverRemoveSubroutines.add(push)
|
||||
neverRemoveSubroutines.add(pushw)
|
||||
neverRemoveSubroutines.add(pop)
|
||||
@@ -39,8 +39,8 @@ class UnusedCodeRemover(private val program: Program,
|
||||
|
||||
program.allBlocks.singleOrNull { it.name=="floats" } ?.let {
|
||||
val subroutines = it.statements.filterIsInstance<Subroutine>()
|
||||
val push = subroutines.single { it.name == "push" }
|
||||
val pop = subroutines.single { it.name == "pop" }
|
||||
val push = subroutines.single { s -> s.name == "push" }
|
||||
val pop = subroutines.single { s -> s.name == "pop" }
|
||||
neverRemoveSubroutines.add(push)
|
||||
neverRemoveSubroutines.add(pop)
|
||||
}
|
||||
|
||||
@@ -102,43 +102,35 @@ _sinecosR8 .char trunc(127.0 * sin(range(180+45) * rad(360.0/180.0)))
|
||||
}
|
||||
|
||||
sub randrange(ubyte n) -> ubyte {
|
||||
; -- return random number uniformly distributed from 0 to n-1 (compensates for divisibility bias).
|
||||
; NOTE: does not work for code in ROM
|
||||
cx16.r0H = 255 / n * n
|
||||
do {
|
||||
cx16.r0L = math.rnd()
|
||||
} until cx16.r0L < cx16.r0H
|
||||
return cx16.r0L % n
|
||||
; -- return random number uniformly distributed from 0 to n-1
|
||||
; NOTE: does not work for code in ROM, use randrange_rom instead for that
|
||||
; why this works: https://www.youtube.com/watch?v=3DvlLUWTNMY&t=347s
|
||||
cx16.r0 = math.rnd() * (n as uword)
|
||||
return cx16.r0H
|
||||
}
|
||||
|
||||
sub randrange_rom(ubyte n) -> ubyte {
|
||||
; -- return random number uniformly distributed from 0 to n-1 (compensates for divisibility bias).
|
||||
; NOTE: works for code in ROM, make sure to initialize seed using rndseed_rom
|
||||
cx16.r0H = 255 / n * n
|
||||
do {
|
||||
cx16.r0L = math.rnd_rom()
|
||||
} until cx16.r0L < cx16.r0H
|
||||
return cx16.r0L % n
|
||||
; -- return random number uniformly distributed from 0 to n-1
|
||||
; NOTE: works for code in ROM, make sure you have initialized the seed using rndseed_rom
|
||||
; why this works: https://www.youtube.com/watch?v=3DvlLUWTNMY&t=347s
|
||||
cx16.r0 = math.rnd_rom() * (n as uword)
|
||||
return cx16.r0H
|
||||
}
|
||||
|
||||
sub randrangew(uword n) -> uword {
|
||||
; -- return random number uniformly distributed from 0 to n-1 (compensates for divisibility bias)
|
||||
; -- return random number uniformly distributed from 0 to n-1
|
||||
; NOTE: does not work for code in ROM
|
||||
cx16.r1 = 65535 / n * n
|
||||
do {
|
||||
cx16.r0 = math.rndw()
|
||||
} until cx16.r0 < cx16.r1
|
||||
return cx16.r0 % n
|
||||
; why this works: https://www.youtube.com/watch?v=3DvlLUWTNMY&t=347s
|
||||
cx16.r0 = math.rndw() * n
|
||||
return math.mul16_last_upper()
|
||||
}
|
||||
|
||||
sub randrangew_rom(uword n) -> uword {
|
||||
; -- return random number uniformly distributed from 0 to n-1 (compensates for divisibility bias)
|
||||
; NOTE: works for code in ROM, make sure to initialize seed using rndseed_rom
|
||||
cx16.r1 = 65535 / n * n
|
||||
do {
|
||||
cx16.r0 = math.rndw_rom()
|
||||
} until cx16.r0 < cx16.r1
|
||||
return cx16.r0 % n
|
||||
; -- return random number uniformly distributed from 0 to n-1
|
||||
; NOTE: works for code in ROM, make sure you have initialized the seed using rndseed_rom
|
||||
; why this works: https://www.youtube.com/watch?v=3DvlLUWTNMY&t=347s
|
||||
cx16.r0 = math.rndw_rom() * n
|
||||
return math.mul16_last_upper()
|
||||
}
|
||||
|
||||
asmsub rndseed(uword seed1 @AY, uword seed2 @R0) clobbers(A,Y) {
|
||||
|
||||
@@ -391,16 +391,18 @@ fail clc ; yes, no match found, return with c=0
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub hash(str string @R0) -> ubyte @A {
|
||||
asmsub hash(str string @AY) -> ubyte @A {
|
||||
; experimental 8 bit hashing function.
|
||||
; hash(-1)=179; clear carry; hash(i) = ROL hash(i-1) XOR string[i]
|
||||
; On the English word list in /usr/share/dict/words it seems to have a pretty even distribution
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
lda #179
|
||||
sta P8ZP_SCRATCH_REG
|
||||
ldy #0
|
||||
clc
|
||||
- lda (cx16.r0),y
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
beq +
|
||||
rol P8ZP_SCRATCH_REG
|
||||
eor P8ZP_SCRATCH_REG
|
||||
|
||||
@@ -177,21 +177,17 @@ math {
|
||||
}
|
||||
|
||||
sub randrange(ubyte n) -> ubyte {
|
||||
; -- return random number uniformly distributed from 0 to n-1 (compensates for divisibility bias)
|
||||
cx16.r0H = 255 / n * n
|
||||
do {
|
||||
cx16.r0L = math.rnd()
|
||||
} until cx16.r0L < cx16.r0H
|
||||
return cx16.r0L % n
|
||||
; -- return random number uniformly distributed from 0 to n-1
|
||||
; why this works: https://www.youtube.com/watch?v=3DvlLUWTNMY&t=347s
|
||||
cx16.r0 = math.rnd() * (n as uword)
|
||||
return cx16.r0H
|
||||
}
|
||||
|
||||
sub randrangew(uword n) -> uword {
|
||||
; -- return random number uniformly distributed from 0 to n-1 (compensates for divisibility bias)
|
||||
cx16.r1 = 65535 / n * n
|
||||
do {
|
||||
cx16.r0 = math.rndw()
|
||||
} until cx16.r0 < cx16.r1
|
||||
return cx16.r0 % n
|
||||
; -- return random number uniformly distributed from 0 to n-1
|
||||
; why this works: https://www.youtube.com/watch?v=3DvlLUWTNMY&t=347s
|
||||
cx16.r0 = math.rndw() * n
|
||||
return math.mul16_last_upper()
|
||||
}
|
||||
|
||||
sub rndseed(uword seed1, uword seed2) {
|
||||
|
||||
@@ -179,7 +179,7 @@ strings {
|
||||
sub hash(str st) -> ubyte {
|
||||
; experimental 8 bit hashing function.
|
||||
; hash(-1)=179; hash(i) = ROL hash(i-1) XOR string[i]
|
||||
; (experimental because the quality of the resulting hash value still has to be determined)
|
||||
; On the English word list in /usr/share/dict/words it seems to have a pretty even distribution
|
||||
ubyte hashcode = 179
|
||||
ubyte ix
|
||||
sys.clear_carry()
|
||||
|
||||
@@ -57,7 +57,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
val plainText by cli.option(ArgType.Boolean, fullName = "plaintext", description = "output only plain text, no colors or fancy symbols")
|
||||
val printAst1 by cli.option(ArgType.Boolean, fullName = "printast1", description = "print out the internal compiler AST")
|
||||
val printAst2 by cli.option(ArgType.Boolean, fullName = "printast2", description = "print out the simplified AST that is used for code generation")
|
||||
val quietAll by cli.option(ArgType.Boolean, fullName = "quiet", description = "don't print compiler and assembler messages")
|
||||
val quietAll by cli.option(ArgType.Boolean, fullName = "quiet", description = "don't print compiler and assembler messages, except warnings and errors")
|
||||
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler messages")
|
||||
val slabsGolden by cli.option(ArgType.Boolean, fullName = "slabsgolden", description = "put memory() slabs in 'golden ram' memory area instead of at the end of the program. On the cx16 target this is $0400-07ff. This is unavailable on other systems.")
|
||||
val slabsHighBank by cli.option(ArgType.Int, fullName = "slabshigh", description = "put memory() slabs in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.")
|
||||
@@ -70,7 +70,8 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "run a .p8ir IR source file in the embedded VM")
|
||||
val warnSymbolShadowing by cli.option(ArgType.Boolean, fullName = "warnshadow", description="show assembler warnings about symbol shadowing")
|
||||
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
|
||||
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
|
||||
val version by cli.option(ArgType.Boolean, fullName = "version", description = "print compiler version and exit")
|
||||
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").optional().multiple(999)
|
||||
|
||||
try {
|
||||
cli.parse(args)
|
||||
@@ -80,6 +81,11 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
if(version==true) {
|
||||
banner()
|
||||
return true
|
||||
}
|
||||
|
||||
if(quietAll!=true)
|
||||
banner()
|
||||
|
||||
@@ -123,6 +129,11 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
if(moduleFiles.isEmpty()) {
|
||||
System.err.println("No module file(s) specified")
|
||||
return false
|
||||
}
|
||||
|
||||
if(varsHighBank==0 && compilationTarget==Cx16Target.NAME) {
|
||||
System.err.println("On the Commander X16, HiRAM bank 0 is used by the kernal and can't be used.")
|
||||
return false
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package prog8.compiler
|
||||
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.AstException
|
||||
import prog8.ast.FatalAstException
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.SyntaxError
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.VarDecl
|
||||
@@ -119,12 +119,13 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
private fun builtinLen(args: List<Expression>, position: Position, program: Program): NumericLiteral {
|
||||
// note: in some cases the length is > 255, and then we have to return a UWORD type instead of a UBYTE.
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("len requires one argument", position)
|
||||
|
||||
val directMemVar = ((args[0] as? DirectMemoryRead)?.addressExpression as? IdentifierReference)?.targetVarDecl(program)
|
||||
val directMemVar = ((args[0] as? DirectMemoryRead)?.addressExpression as? IdentifierReference)?.targetVarDecl()
|
||||
var arraySize = directMemVar?.arraysize?.constIndex()
|
||||
if(arraySize != null)
|
||||
return NumericLiteral.optimalInteger(arraySize, position)
|
||||
@@ -134,7 +135,7 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
|
||||
return NumericLiteral.optimalInteger((args[0] as StringLiteral).value.length, position)
|
||||
if(args[0] !is IdentifierReference)
|
||||
throw SyntaxError("len argument should be an identifier", position)
|
||||
val target = (args[0] as IdentifierReference).targetVarDecl(program)
|
||||
val target = (args[0] as IdentifierReference).targetVarDecl()
|
||||
?: throw CannotEvaluateException("len", "no target vardecl")
|
||||
|
||||
return when {
|
||||
|
||||
@@ -200,7 +200,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
}
|
||||
return CompilationResult(resultingProgram!!, ast, compilationOptions, importedFiles)
|
||||
} catch (px: ParseError) {
|
||||
args.errors.print_single_error("${px.position.toClickableStr()} parse error: ${px.message}".trim())
|
||||
args.errors.printSingleError("${px.position.toClickableStr()} parse error: ${px.message}".trim())
|
||||
} catch (ac: ErrorsReportedException) {
|
||||
if(args.printAst1 && resultingProgram!=null) {
|
||||
println("\n*********** COMPILER AST *************")
|
||||
@@ -217,17 +217,17 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
}
|
||||
}
|
||||
if(!ac.message.isNullOrEmpty()) {
|
||||
args.errors.print_single_error(ac.message!!)
|
||||
args.errors.printSingleError(ac.message!!)
|
||||
}
|
||||
} catch (nsf: NoSuchFileException) {
|
||||
args.errors.print_single_error("File not found: ${nsf.message}")
|
||||
args.errors.printSingleError("File not found: ${nsf.message}")
|
||||
} catch (ax: AstException) {
|
||||
args.errors.print_single_error(ax.toString())
|
||||
args.errors.printSingleError(ax.toString())
|
||||
} catch (x: Exception) {
|
||||
args.errors.print_single_error("\ninternal error")
|
||||
args.errors.printSingleError("\ninternal error")
|
||||
throw x
|
||||
} catch (x: NotImplementedError) {
|
||||
args.errors.print_single_error("\ninternal error: missing feature/code")
|
||||
args.errors.printSingleError("\ninternal error: missing feature/code")
|
||||
throw x
|
||||
}
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ internal class ErrorReporter(val colors: IConsoleColors): IErrorReporter {
|
||||
override fun noErrors() = messages.none { it.severity==MessageSeverity.ERROR }
|
||||
override fun noErrorForLine(position: Position) = !messages.any { it.position.line==position.line && it.severity!=MessageSeverity.INFO }
|
||||
|
||||
override fun print_single_error(errormessage: String) {
|
||||
override fun printSingleError(errormessage: String) {
|
||||
System.out.flush()
|
||||
colors.error(System.err)
|
||||
System.err.println(errormessage)
|
||||
|
||||
@@ -39,7 +39,7 @@ class ModuleImporter(private val program: Program,
|
||||
println("Compiling program ${Path("").absolute().relativize(programPath)}")
|
||||
println("Compiler target: $compilationTargetName")
|
||||
}
|
||||
val source = ImportFileSystem.getFile(programPath, false)
|
||||
val source = ImportFileSystem.getFile(programPath)
|
||||
return Ok(importModule(source))
|
||||
}
|
||||
}
|
||||
@@ -158,7 +158,7 @@ class ModuleImporter(private val program: Program,
|
||||
|
||||
normalLocations.forEach {
|
||||
try {
|
||||
return Ok(ImportFileSystem.getFile(it.resolve(fileName), false))
|
||||
return Ok(ImportFileSystem.getFile(it.resolve(fileName)))
|
||||
} catch (_: NoSuchFileException) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ internal class AstChecker(private val program: Program,
|
||||
} else if(!(iterableDt.isIterable) && forLoop.iterable !is RangeExpression) {
|
||||
errors.err("can only loop over an iterable type", forLoop.position)
|
||||
} else {
|
||||
val loopvar = forLoop.loopVar.targetVarDecl(program)
|
||||
val loopvar = forLoop.loopVar.targetVarDecl()
|
||||
if(loopvar==null || loopvar.type== VarDeclType.CONST) {
|
||||
errors.err("for loop requires a variable to loop with", forLoop.position)
|
||||
} else {
|
||||
@@ -392,7 +392,7 @@ internal class AstChecker(private val program: Program,
|
||||
err("variable bank extsub has no romable code-generation for the required jsrfar call, stick to constant bank, or create a system-ram trampoline")
|
||||
}
|
||||
|
||||
if(varbank.targetVarDecl(program)?.datatype?.isUnsignedByte!=true)
|
||||
if(varbank.targetVarDecl()?.datatype?.isUnsignedByte!=true)
|
||||
err("bank variable must be ubyte")
|
||||
}
|
||||
if(subroutine.inline && subroutine.asmAddress!=null)
|
||||
@@ -558,7 +558,7 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
val ident = repeatLoop.iterations as? IdentifierReference
|
||||
if(ident!=null) {
|
||||
val targetVar = ident.targetVarDecl(program)
|
||||
val targetVar = ident.targetVarDecl()
|
||||
if(targetVar==null)
|
||||
errors.err("invalid assignment value, maybe forgot '&' (address-of)", ident.position)
|
||||
}
|
||||
@@ -598,7 +598,7 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
val fcall = assignment.value as? IFunctionCall
|
||||
val fcallTarget = fcall?.target?.targetSubroutine(program)
|
||||
val fcallTarget = fcall?.target?.targetSubroutine()
|
||||
if(assignment.target.multi!=null) {
|
||||
checkMultiAssignment(assignment, fcall, fcallTarget)
|
||||
} else if(fcallTarget!=null) {
|
||||
@@ -681,8 +681,7 @@ internal class AstChecker(private val program: Program,
|
||||
if (assignment.value !is BinaryExpression && assignment.value !is PrefixExpression && assignment.value !is ContainmentCheck && assignment.value !is IfExpression)
|
||||
errors.err("invalid assignment value, maybe forgot '&' (address-of)", assignment.value.position)
|
||||
} else {
|
||||
checkAssignmentCompatible(assignTarget, targetDatatype.getOrUndef(),
|
||||
sourceDatatype.getOrUndef(), assignment.value)
|
||||
checkAssignmentCompatible(targetDatatype.getOrUndef(),sourceDatatype.getOrUndef(), assignment.value.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -691,7 +690,7 @@ internal class AstChecker(private val program: Program,
|
||||
fun checkRomTarget(target: AssignTarget) {
|
||||
val idx=target.arrayindexed
|
||||
if(idx!=null) {
|
||||
val decl = idx.arrayvar.targetVarDecl(program)!!
|
||||
val decl = idx.arrayvar.targetVarDecl()!!
|
||||
if(decl.type!=VarDeclType.MEMORY && decl.zeropage!=ZeropageWish.REQUIRE_ZEROPAGE) {
|
||||
// memory mapped arrays are assumed to be in RAM. If they're not.... well, POOF
|
||||
errors.err("cannot assign to an array or string that is located in ROM (option romable is enabled)", assignTarget.position)
|
||||
@@ -709,7 +708,7 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
override fun visit(addressOf: AddressOf) {
|
||||
checkLongType(addressOf)
|
||||
val variable=addressOf.identifier.targetVarDecl(program)
|
||||
val variable=addressOf.identifier.targetVarDecl()
|
||||
if (variable!=null) {
|
||||
if (variable.type == VarDeclType.CONST && addressOf.arrayIndex == null)
|
||||
errors.err("invalid pointer-of operand type", addressOf.position)
|
||||
@@ -1124,14 +1123,6 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("array literal for iteration must contain constants. Try using a separate array variable instead?", array.position)
|
||||
}
|
||||
|
||||
if(array.parent is Assignment) {
|
||||
val arraydt = array.inferType(program)
|
||||
val assignTarget = (array.parent as Assignment).target
|
||||
val targetDt = assignTarget.inferType(program)
|
||||
if(arraydt!=targetDt)
|
||||
errors.err("value has incompatible type ($arraydt) for the variable ($targetDt)", array.position)
|
||||
}
|
||||
|
||||
super.visit(array)
|
||||
}
|
||||
|
||||
@@ -1162,7 +1153,7 @@ internal class AstChecker(private val program: Program,
|
||||
override fun visit(expr: PrefixExpression) {
|
||||
|
||||
if(expr.expression is IFunctionCall) {
|
||||
val targetStatement = (expr.expression as IFunctionCall).target.targetSubroutine(program)
|
||||
val targetStatement = (expr.expression as IFunctionCall).target.targetSubroutine()
|
||||
if(targetStatement?.returntypes?.isEmpty()==true) {
|
||||
errors.err("subroutine doesn't return a value", expr.expression.position)
|
||||
}
|
||||
@@ -1610,7 +1601,7 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
private fun checkPointer(pointervar: IdentifierReference) {
|
||||
val vardecl = pointervar.targetVarDecl(program)
|
||||
val vardecl = pointervar.targetVarDecl()
|
||||
if(vardecl?.zeropage == ZeropageWish.NOT_IN_ZEROPAGE)
|
||||
errors.info("pointer variable should preferrably be in zeropage but is marked nozp", vardecl.position)
|
||||
}
|
||||
@@ -1623,7 +1614,7 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("indexing requires an iterable or address uword variable", arrayIndexedExpression.position)
|
||||
val indexVariable = arrayIndexedExpression.indexer.indexExpr as? IdentifierReference
|
||||
if(indexVariable!=null) {
|
||||
if(indexVariable.targetVarDecl(program)?.datatype?.isSigned==true) {
|
||||
if(indexVariable.targetVarDecl()?.datatype?.isSigned==true) {
|
||||
errors.err("variable array indexing can't be performed with signed variables", indexVariable.position)
|
||||
return
|
||||
}
|
||||
@@ -2015,25 +2006,22 @@ internal class AstChecker(private val program: Program,
|
||||
return correct
|
||||
}
|
||||
|
||||
private fun checkAssignmentCompatible(target: AssignTarget,
|
||||
targetDatatype: DataType,
|
||||
private fun checkAssignmentCompatible(targetDatatype: DataType,
|
||||
sourceDatatype: DataType,
|
||||
sourceValue: Expression) : Boolean {
|
||||
val position = sourceValue.position
|
||||
position: Position) : Boolean {
|
||||
|
||||
if (targetDatatype.isArray) {
|
||||
if(sourceValue.inferType(program).isArray)
|
||||
errors.err("cannot assign arrays directly. Maybe use sys.memcopy instead.", target.position)
|
||||
if(sourceDatatype.isArray)
|
||||
errors.err("cannot assign arrays directly. Maybe use sys.memcopy instead.", position)
|
||||
else
|
||||
errors.err("cannot assign value to array. Maybe use sys.memset/memsetw instead.", target.position)
|
||||
errors.err("cannot assign value to array. Maybe use sys.memset/memsetw instead.", position)
|
||||
return false
|
||||
}
|
||||
if (sourceValue is ArrayLiteral) {
|
||||
errors.err("cannot assign array", target.position)
|
||||
return false
|
||||
}
|
||||
if(sourceValue is RangeExpression) {
|
||||
errors.err("can't assign a range value to something else", position)
|
||||
if (sourceDatatype.isArray) {
|
||||
if(targetDatatype.isUnsignedWord)
|
||||
errors.err("invalid assignment value, maybe forgot '&' (address-of)", position)
|
||||
else
|
||||
errors.err("cannot assign array", position) // also includes range expressions
|
||||
return false
|
||||
}
|
||||
if(sourceDatatype.isUndefined) {
|
||||
@@ -2056,38 +2044,17 @@ internal class AstChecker(private val program: Program,
|
||||
if(result)
|
||||
return true
|
||||
|
||||
if(sourceDatatype.isWord && targetDatatype.isByte) {
|
||||
errors.err("cannot assign word to byte, use msb() or lsb()?", position)
|
||||
}
|
||||
else if(sourceDatatype.isIterable && targetDatatype.isByte) {
|
||||
errors.err("cannot assign string or array to a byte", position)
|
||||
}
|
||||
if(sourceDatatype.isWord && targetDatatype.isByte)
|
||||
errors.err("cannot assign word to byte, maybe use msb() or lsb()", position)
|
||||
else if(sourceDatatype.isFloat&& targetDatatype.isInteger)
|
||||
errors.err("cannot assign float to ${targetDatatype}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position)
|
||||
else if((sourceValue as? BinaryExpression)?.operator in BitwiseOperators && targetDatatype.equalsSize(sourceDatatype)) {
|
||||
// this is allowed: bitwise operation between different types as long as they're the same size.
|
||||
}
|
||||
else if(targetDatatype.isUnsignedWord && sourceDatatype.isPassByRef) {
|
||||
// this is allowed: a pass-by-reference datatype into an uword (pointer value).
|
||||
}
|
||||
else if(sourceDatatype.isArray && targetDatatype.isArray) {
|
||||
// this is allowed (assigning array to array)
|
||||
}
|
||||
else if(sourceDatatype.isBool && !targetDatatype.isBool) {
|
||||
else if(targetDatatype.isString && sourceDatatype.isUnsignedWord)
|
||||
errors.err("can't assign uword to str. If the source is a string pointer and you actually want to overwrite the target string, use an explicit strings.copy(src,tgt) instead.", position)
|
||||
else
|
||||
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
|
||||
}
|
||||
else if(targetDatatype.isBool && !sourceDatatype.isBool) {
|
||||
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
|
||||
}
|
||||
else if(targetDatatype.isString) {
|
||||
if(sourceDatatype.isUnsignedWord)
|
||||
errors.err("can't assign UWORD to STR. If the source is a string and you actually want to overwrite the target string, use an explicit strings.copy(src,tgt) instead.", position)
|
||||
else
|
||||
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
|
||||
}
|
||||
else {
|
||||
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -3,11 +3,7 @@ package prog8.compiler.astprocessing
|
||||
import prog8.ast.IStatementContainer
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.expressions.BinaryExpression
|
||||
import prog8.ast.expressions.CharLiteral
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.ast.expressions.StringLiteral
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
@@ -185,8 +181,8 @@ internal fun Program.moveMainBlockAsFirst(target: ICompilationTarget) {
|
||||
}
|
||||
}
|
||||
|
||||
internal fun IdentifierReference.isSubroutineParameter(program: Program): Boolean {
|
||||
val vardecl = this.targetVarDecl(program)
|
||||
internal fun IdentifierReference.isSubroutineParameter(): Boolean {
|
||||
val vardecl = this.targetVarDecl()
|
||||
if(vardecl!=null && vardecl.origin==VarDeclOrigin.SUBROUTINEPARAM) {
|
||||
return vardecl.definingSubroutine?.parameters?.any { it.name==vardecl.name } == true
|
||||
}
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
package prog8.compiler.astprocessing
|
||||
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.FatalAstException
|
||||
import prog8.ast.SyntaxError
|
||||
import prog8.ast.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.findParentNode
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
@@ -181,7 +176,7 @@ class AstPreprocessor(val program: Program,
|
||||
val nextAssignment = decl.nextSibling() as? Assignment
|
||||
if(nextAssignment!=null && nextAssignment.origin!=AssignmentOrigin.VARINIT) {
|
||||
// check if the following assignment initializes the variable
|
||||
if(decl.value==null && nextAssignment.target.identifier?.targetVarDecl(program)===decl) {
|
||||
if(decl.value==null && nextAssignment.target.identifier?.targetVarDecl()===decl) {
|
||||
if(!nextAssignment.value.referencesIdentifier(nextAssignment.target.identifier!!.nameInSource))
|
||||
nextAssignment.origin = AssignmentOrigin.VARINIT
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
|
||||
if(typecast.type == BaseDataType.UWORD) {
|
||||
val identifier = typecast.expression as? IdentifierReference
|
||||
if(identifier!=null) {
|
||||
return if(identifier.isSubroutineParameter(program)) {
|
||||
return if(identifier.isSubroutineParameter()) {
|
||||
listOf(
|
||||
IAstModification.ReplaceNode(
|
||||
typecast,
|
||||
|
||||
@@ -205,7 +205,7 @@ _after:
|
||||
// replace pointervar[word] by @(pointervar+word) to avoid the
|
||||
// "array indexing is limited to byte size 0..255" error for pointervariables.
|
||||
val indexExpr = arrayIndexedExpression.indexer.indexExpr
|
||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
|
||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl()
|
||||
if(arrayVar!=null && arrayVar.datatype.isUnsignedWord) {
|
||||
val wordIndex = TypecastExpression(indexExpr, BaseDataType.UWORD, true, indexExpr.position)
|
||||
val address = BinaryExpression(arrayIndexedExpression.arrayvar.copy(), "+", wordIndex, arrayIndexedExpression.position)
|
||||
@@ -279,7 +279,7 @@ _after:
|
||||
val addressOf = expr.left as? AddressOf
|
||||
val offset = (expr.right as? NumericLiteral)?.number?.toInt()
|
||||
if(addressOf!=null && offset==1) {
|
||||
val variable = addressOf.identifier.targetVarDecl(program)
|
||||
val variable = addressOf.identifier.targetVarDecl()
|
||||
if(variable!=null && variable.datatype.isWord) {
|
||||
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), memread.position), mutableListOf(addressOf.identifier), memread.position)
|
||||
return listOf(IAstModification.ReplaceNode(memread, msb, parent))
|
||||
|
||||
@@ -9,7 +9,7 @@ import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.ast.PtContainmentCheck
|
||||
import prog8.code.core.*
|
||||
import prog8.code.core.IErrorReporter
|
||||
|
||||
|
||||
internal class LiteralsToAutoVars(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
||||
@@ -83,7 +83,7 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
if(decl.names.size>1) {
|
||||
|
||||
val fcallTarget = (decl.value as? IFunctionCall)?.target?.targetSubroutine(program)
|
||||
val fcallTarget = (decl.value as? IFunctionCall)?.target?.targetSubroutine()
|
||||
if(fcallTarget!=null) {
|
||||
// ubyte a,b,c = multi() --> ubyte a,b,c / a,b,c = multi()
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
|
||||
@@ -164,6 +164,9 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
}
|
||||
}
|
||||
|
||||
if(srcAssign.origin == AssignmentOrigin.VARINIT && srcAssign.parent is Block && srcAssign.value.constValue(program)?.number==0.0)
|
||||
throw FatalAstException("should not have a redundant block-level variable=0 assignment; it will be zeroed as part of BSS clear")
|
||||
|
||||
val assign = PtAssignment(srcAssign.position, srcAssign.origin==AssignmentOrigin.VARINIT)
|
||||
val multi = srcAssign.target.multi
|
||||
if(multi==null) {
|
||||
@@ -417,7 +420,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
if(binexpr.operator=="==" || binexpr.operator=="!=") {
|
||||
val fcall = binexpr.left as? FunctionCallExpression
|
||||
if(fcall!=null) {
|
||||
val returnRegs = fcall.target.targetSubroutine(program)?.asmReturnvaluesRegisters
|
||||
val returnRegs = fcall.target.targetSubroutine()?.asmReturnvaluesRegisters
|
||||
if(returnRegs!=null && returnRegs.size==1 && returnRegs[0].statusflag!=null) {
|
||||
return codeForStatusflag(fcall, returnRegs[0].statusflag!!, binexpr.operator == "==")
|
||||
}
|
||||
@@ -426,7 +429,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
} else {
|
||||
val fcall = srcIf.condition as? FunctionCallExpression
|
||||
if (fcall != null) {
|
||||
val returnRegs = fcall.target.targetSubroutine(program)?.asmReturnvaluesRegisters
|
||||
val returnRegs = fcall.target.targetSubroutine()?.asmReturnvaluesRegisters
|
||||
if(returnRegs!=null && returnRegs.size==1 && returnRegs[0].statusflag!=null) {
|
||||
return codeForStatusflag(fcall, returnRegs[0].statusflag!!, false)
|
||||
}
|
||||
@@ -436,7 +439,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
if(prefix!=null && prefix.operator=="not") {
|
||||
val prefixedFcall = prefix.expression as? FunctionCallExpression
|
||||
if (prefixedFcall != null) {
|
||||
val returnRegs = prefixedFcall.target.targetSubroutine(program)?.asmReturnvaluesRegisters
|
||||
val returnRegs = prefixedFcall.target.targetSubroutine()?.asmReturnvaluesRegisters
|
||||
if (returnRegs != null && returnRegs.size == 1 && returnRegs[0].statusflag != null) {
|
||||
return codeForStatusflag(prefixedFcall, returnRegs[0].statusflag!!, true)
|
||||
}
|
||||
@@ -619,7 +622,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
}
|
||||
|
||||
private fun transform(srcArr: ArrayIndexedExpression): PtArrayIndexer {
|
||||
val dt = srcArr.arrayvar.targetVarDecl(program)!!.datatype
|
||||
val dt = srcArr.arrayvar.targetVarDecl()!!.datatype
|
||||
if(!dt.isArray && !dt.isString)
|
||||
throw FatalAstException("array indexing can only be used on array or string variables ${srcArr.position}")
|
||||
val eltType = srcArr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
|
||||
|
||||
@@ -52,7 +52,8 @@ internal class StatementReorderer(
|
||||
if (decl.datatype.isNumericOrBool) {
|
||||
if(decl !in declsProcessedWithInitAssignment) {
|
||||
declsProcessedWithInitAssignment.add(decl)
|
||||
if (decl.value == null) {
|
||||
if (decl.value == null || decl.value?.constValue(program)?.number==0.0) {
|
||||
decl.value = null
|
||||
if (decl.origin==VarDeclOrigin.USERCODE && decl.allowInitializeWithZero) {
|
||||
if(decl.dirty) {
|
||||
// no initialization at all!
|
||||
@@ -62,11 +63,9 @@ internal class StatementReorderer(
|
||||
// unless there's already an assignment below it, that initializes the value (or a for loop that uses it as loopvar).
|
||||
// This allows you to restart the program and have the same starting values of the variables
|
||||
// So basically consider 'ubyte xx' as a short form for 'ubyte xx; xx=0'
|
||||
decl.value = null
|
||||
val canskip = canSkipInitializationWith0(decl)
|
||||
if (!canskip) {
|
||||
// (this explicit initialization assignment to 0 is not required for global variables in the block scope, these are zeroed as part of the BSS section clear)
|
||||
if (!canSkipInitializationWith0(decl)) {
|
||||
// Add assignment to initialize with zero
|
||||
// Note: for block-level vars, this will introduce assignments in the block scope. These have to be dealt with correctly later.
|
||||
val identifier = IdentifierReference(listOf(decl.name), decl.position)
|
||||
val assignzero = Assignment(AssignTarget(identifier, null, null, null, false, decl.position),
|
||||
decl.zeroElementValue(), AssignmentOrigin.VARINIT, decl.position)
|
||||
@@ -96,7 +95,7 @@ internal class StatementReorderer(
|
||||
// (that code only triggers on regular assignment, not on variable initializers)
|
||||
val ident = decl.value as? IdentifierReference
|
||||
if(ident!=null) {
|
||||
val target = ident.targetVarDecl(program)
|
||||
val target = ident.targetVarDecl()
|
||||
if(target!=null && target.isArray) {
|
||||
val pos = decl.value!!.position
|
||||
val identifier = IdentifierReference(listOf(decl.name), pos)
|
||||
@@ -115,6 +114,11 @@ internal class StatementReorderer(
|
||||
}
|
||||
|
||||
private fun canSkipInitializationWith0(decl: VarDecl): Boolean {
|
||||
// if the variable is declared in a block, we can omit the init with 0 because
|
||||
// the variable will be initialized to zero when the BSS section is cleared as a whole.
|
||||
if(decl.parent is Block)
|
||||
return true
|
||||
|
||||
// if there is an assignment to the variable below it (regular assign, or For loop),
|
||||
// and there is nothing important in between, we can skip the initialization.
|
||||
val statements = (decl.parent as? IStatementContainer)?.statements ?: return false
|
||||
@@ -123,14 +127,14 @@ internal class StatementReorderer(
|
||||
when(stmt) {
|
||||
is Assignment -> {
|
||||
if (!stmt.isAugmentable) {
|
||||
val assignTargets = stmt.target.multi?.mapNotNull { it.identifier?.targetVarDecl(program) }
|
||||
val assignTargets = stmt.target.multi?.mapNotNull { it.identifier?.targetVarDecl() }
|
||||
if(assignTargets!=null) {
|
||||
if(decl in assignTargets) {
|
||||
stmt.origin = AssignmentOrigin.VARINIT
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
val assignTgt = stmt.target.identifier?.targetVarDecl(program)
|
||||
val assignTgt = stmt.target.identifier?.targetVarDecl()
|
||||
if (assignTgt == decl) {
|
||||
stmt.origin = AssignmentOrigin.VARINIT
|
||||
return true
|
||||
@@ -143,11 +147,11 @@ internal class StatementReorderer(
|
||||
is ChainedAssignment -> {
|
||||
var chained: ChainedAssignment? = stmt
|
||||
while(chained!=null) {
|
||||
val assignTgt = chained.target.identifier?.targetVarDecl(program)
|
||||
val assignTgt = chained.target.identifier?.targetVarDecl()
|
||||
if (assignTgt == decl)
|
||||
return true
|
||||
if(chained.nested is Assignment) {
|
||||
if ((chained.nested as Assignment).target.identifier?.targetVarDecl(program) == decl) {
|
||||
if ((chained.nested as Assignment).target.identifier?.targetVarDecl() == decl) {
|
||||
(chained.nested as Assignment).origin = AssignmentOrigin.VARINIT
|
||||
return true
|
||||
}
|
||||
@@ -325,7 +329,7 @@ internal class StatementReorderer(
|
||||
|
||||
private fun checkCopyArrayValue(assign: Assignment) {
|
||||
val identifier = assign.target.identifier!!
|
||||
val targetVar = identifier.targetVarDecl(program)!!
|
||||
val targetVar = identifier.targetVarDecl()!!
|
||||
|
||||
if(targetVar.arraysize==null) {
|
||||
errors.err("array has no defined size", assign.position)
|
||||
@@ -340,7 +344,7 @@ internal class StatementReorderer(
|
||||
}
|
||||
|
||||
val sourceIdent = assign.value as IdentifierReference
|
||||
val sourceVar = sourceIdent.targetVarDecl(program)!!
|
||||
val sourceVar = sourceIdent.targetVarDecl()!!
|
||||
if(!sourceVar.isArray) {
|
||||
errors.err("value must be an array", sourceIdent.position)
|
||||
} else {
|
||||
|
||||
@@ -185,6 +185,16 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if shifts have a positive integer shift type
|
||||
if(expr.operator=="<<" || expr.operator==">>") {
|
||||
if(rightDt.isInteger) {
|
||||
val rconst = expr.right.constValue(program)
|
||||
if(rconst!=null && rconst.number<0)
|
||||
errors.err("can only shift by a positive amount", expr.right.position)
|
||||
} else
|
||||
errors.err("right operand of bit shift must be an integer", expr.right.position)
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
@@ -251,7 +261,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
if(number!=null) {
|
||||
addTypecastOrCastedValueModification(modifications, it.second, targetDt, call as Node)
|
||||
} else if(identifier!=null && targetDt==BaseDataType.UWORD && argDt.isPassByRef) {
|
||||
if(!identifier.isSubroutineParameter(program)) {
|
||||
if(!identifier.isSubroutineParameter()) {
|
||||
// We allow STR/ARRAY values for UWORD parameters.
|
||||
// If it's an array (not STR), take the address.
|
||||
if(!argDt.isString) {
|
||||
|
||||
@@ -84,12 +84,12 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
changeSplit = SplitWish.NOSPLIT
|
||||
}
|
||||
else {
|
||||
changeDataType = if(decl.datatype.isSplitWordArray) null else DataType.arrayFor(decl.datatype.elementType().base, true)
|
||||
changeDataType = if(decl.datatype.isSplitWordArray) null else DataType.arrayFor(decl.datatype.elementType().base)
|
||||
changeSplit = SplitWish.SPLIT
|
||||
}
|
||||
}
|
||||
SplitWish.SPLIT -> {
|
||||
changeDataType = if(decl.datatype.isSplitWordArray) null else DataType.arrayFor(decl.datatype.elementType().base, true)
|
||||
changeDataType = if(decl.datatype.isSplitWordArray) null else DataType.arrayFor(decl.datatype.elementType().base)
|
||||
}
|
||||
SplitWish.NOSPLIT -> {
|
||||
changeDataType = if(decl.datatype.isSplitWordArray) DataType.arrayFor(decl.datatype.elementType().base, false) else null
|
||||
@@ -285,7 +285,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
|
||||
// replace x==1 or x==2 or x==3 with a containment check x in [1,2,3]
|
||||
val valueCopies = values.sortedBy { it.number }.map { it.copy() }
|
||||
val arrayType = DataType.arrayFor(elementType.base, true)
|
||||
val arrayType = DataType.arrayFor(elementType.base)
|
||||
val valuesArray = ArrayLiteral(InferredTypes.InferredType.known(arrayType), valueCopies.toTypedArray(), expr.position)
|
||||
val containment = ContainmentCheck(needle, valuesArray, expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, containment, parent))
|
||||
@@ -394,7 +394,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||
val index = arrayIndexedExpression.indexer.constIndex()
|
||||
if(index!=null && index<0) {
|
||||
val target = arrayIndexedExpression.arrayvar.targetVarDecl(program)
|
||||
val target = arrayIndexedExpression.arrayvar.targetVarDecl()
|
||||
val arraysize = target?.arraysize?.constIndex()
|
||||
if(arraysize!=null) {
|
||||
if(arraysize+index < 0) {
|
||||
|
||||
@@ -5,8 +5,8 @@ import prog8.ast.Program
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
import prog8.code.core.*
|
||||
import prog8.code.INTERNED_STRINGS_MODULENAME
|
||||
import prog8.code.core.*
|
||||
|
||||
internal class VerifyFunctionArgTypes(val program: Program, val options: CompilationOptions, val errors: IErrorReporter) : IAstVisitor {
|
||||
|
||||
@@ -31,7 +31,7 @@ internal class VerifyFunctionArgTypes(val program: Program, val options: Compila
|
||||
}
|
||||
|
||||
override fun visit(identifier: IdentifierReference) {
|
||||
if(identifier.wasStringLiteral(program)) {
|
||||
if(identifier.wasStringLiteral()) {
|
||||
allStringRefs.add(identifier.nameInSource)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.types.instanceOf
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.IStatementContainer
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.ast.statements.Assignment
|
||||
@@ -31,11 +30,11 @@ class TestCompilerOnCharLit: FunSpec({
|
||||
|
||||
val outputDir = tempdir().toPath()
|
||||
|
||||
fun findInitializer(vardecl: VarDecl, program: Program): Assignment? =
|
||||
fun findInitializer(vardecl: VarDecl): Assignment? =
|
||||
(vardecl.parent as IStatementContainer).statements
|
||||
.asSequence()
|
||||
.filterIsInstance<Assignment>()
|
||||
.singleOrNull { it.origin== AssignmentOrigin.VARINIT && it.target.identifier?.targetVarDecl(program) === vardecl }
|
||||
.singleOrNull { it.origin== AssignmentOrigin.VARINIT && it.target.identifier?.targetVarDecl() === vardecl }
|
||||
|
||||
|
||||
test("testCharLitAsExtsubArg") {
|
||||
@@ -79,14 +78,14 @@ class TestCompilerOnCharLit: FunSpec({
|
||||
|
||||
funCall.args[0] shouldBe instanceOf<IdentifierReference>()
|
||||
val arg = funCall.args[0] as IdentifierReference
|
||||
val decl = arg.targetVarDecl(program)!!
|
||||
val decl = arg.targetVarDecl()!!
|
||||
decl.type shouldBe VarDeclType.VAR
|
||||
decl.datatype shouldBe DataType.UBYTE
|
||||
|
||||
withClue("initializer value should have been moved to separate assignment"){
|
||||
decl.value shouldBe null
|
||||
}
|
||||
val assignInitialValue = findInitializer(decl, program)!!
|
||||
val assignInitialValue = findInitializer(decl)!!
|
||||
assignInitialValue.target.identifier!!.nameInSource shouldBe listOf("ch")
|
||||
withClue("char literal should have been replaced by ubyte literal") {
|
||||
assignInitialValue.value shouldBe instanceOf<NumericLiteral>()
|
||||
@@ -115,7 +114,7 @@ class TestCompilerOnCharLit: FunSpec({
|
||||
// Now, both is ok for the arg: a) still the IdRef or b) replaced by numeric literal
|
||||
when (val arg = funCall.args[0]) {
|
||||
is IdentifierReference -> {
|
||||
val decl = arg.targetVarDecl(program)!!
|
||||
val decl = arg.targetVarDecl()!!
|
||||
decl.type shouldBe VarDeclType.CONST
|
||||
decl.datatype shouldBe DataType.UBYTE
|
||||
(decl.value as NumericLiteral).number shouldBe platform.encodeString("\n", Encoding.PETSCII)[0]
|
||||
|
||||
@@ -34,7 +34,7 @@ class TestCompilerOnImportsAndIncludes: FunSpec({
|
||||
val strLits = startSub.statements
|
||||
.filterIsInstance<FunctionCallStatement>()
|
||||
.map { it.args[0] as IdentifierReference }
|
||||
.map { it.targetVarDecl(program)!!.value as StringLiteral }
|
||||
.map { it.targetVarDecl()!!.value as StringLiteral }
|
||||
|
||||
strLits[0].value shouldBe "main.bar"
|
||||
strLits[1].value shouldBe "foo.bar"
|
||||
@@ -57,7 +57,7 @@ class TestCompilerOnImportsAndIncludes: FunSpec({
|
||||
.filterIsInstance<FunctionCallStatement>()
|
||||
.map { it.args[0] }
|
||||
|
||||
val str0 = (args[0] as IdentifierReference).targetVarDecl(program)!!.value as StringLiteral
|
||||
val str0 = (args[0] as IdentifierReference).targetVarDecl()!!.value as StringLiteral
|
||||
str0.value shouldBe "main.bar"
|
||||
str0.definingScope.name shouldBe "main"
|
||||
|
||||
|
||||
@@ -406,4 +406,66 @@ main {
|
||||
errors.errors[0] shouldContain(":4:37: no cast available")
|
||||
errors.errors[1] shouldContain(":5:37: no cast available")
|
||||
}
|
||||
|
||||
test("const evaluation of signed bitwise operations") {
|
||||
val src="""
|
||||
%import textio
|
||||
main {
|
||||
sub start() {
|
||||
byte @shared a = -1
|
||||
byte @shared b = -15
|
||||
ubyte @shared ub = 2
|
||||
const byte ca = -1
|
||||
const byte cb = -15
|
||||
const ubyte cub = 2
|
||||
|
||||
txt.print_ub( a & b )
|
||||
txt.spc()
|
||||
txt.print_ub( a | b )
|
||||
txt.spc()
|
||||
txt.print_ub( a ^ b )
|
||||
txt.spc()
|
||||
txt.print_b( a << ub )
|
||||
txt.spc()
|
||||
txt.print_b( a >> ub )
|
||||
txt.nl()
|
||||
txt.print_ub( ca & cb )
|
||||
txt.spc()
|
||||
txt.print_ub( ca | cb )
|
||||
txt.spc()
|
||||
txt.print_ub( ca ^ cb )
|
||||
txt.spc()
|
||||
txt.print_b( ca << cub )
|
||||
txt.spc()
|
||||
txt.print_b( ca >> cub )
|
||||
txt.nl()
|
||||
|
||||
word @shared aw = -1
|
||||
word @shared bw = -15
|
||||
uword @shared uw = 2
|
||||
const word caw = -1
|
||||
const word cbw = -15
|
||||
const uword cuw = 2
|
||||
|
||||
txt.print_uw( aw & bw )
|
||||
txt.spc()
|
||||
txt.print_uw( aw | bw )
|
||||
txt.spc()
|
||||
txt.print_uw( aw ^ bw )
|
||||
txt.nl()
|
||||
txt.print_uw( caw & cbw )
|
||||
txt.spc()
|
||||
txt.print_uw( caw | cbw )
|
||||
txt.spc()
|
||||
txt.print_uw( caw ^ cbw )
|
||||
txt.nl()
|
||||
txt.print_w( cbw << cuw )
|
||||
txt.spc()
|
||||
txt.print_w( cbw >> cuw )
|
||||
txt.nl()
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), false, src, outputDir, writeAssembly = false) shouldNotBe null
|
||||
|
||||
}
|
||||
})
|
||||
@@ -62,12 +62,12 @@ class TestIdentifierRef: FunSpec({
|
||||
val wwref = (stmts[0] as Assignment).target.identifier!!
|
||||
val mainref = ((stmts[1] as Assignment).value as AddressOf).identifier
|
||||
wwref.nameInSource shouldBe listOf("ww")
|
||||
wwref.wasStringLiteral(program) shouldBe false
|
||||
wwref.wasStringLiteral() shouldBe false
|
||||
wwref.targetStatement(program) shouldBe instanceOf<VarDecl>()
|
||||
wwref.targetVarDecl(program)!!.name shouldBe "ww"
|
||||
wwref.targetVarDecl(program)!!.parent shouldBe instanceOf<Block>()
|
||||
wwref.targetVarDecl()!!.name shouldBe "ww"
|
||||
wwref.targetVarDecl()!!.parent shouldBe instanceOf<Block>()
|
||||
mainref.nameInSource shouldBe listOf("main")
|
||||
mainref.wasStringLiteral(program) shouldBe false
|
||||
mainref.wasStringLiteral() shouldBe false
|
||||
mainref.targetStatement(program) shouldBe instanceOf<Block>()
|
||||
}
|
||||
})
|
||||
@@ -225,8 +225,8 @@ main {
|
||||
val rept2strcopy = stmts[4] as IFunctionCall
|
||||
val name2 = name2strcopy.args.first() as IdentifierReference
|
||||
val rept2 = rept2strcopy.args.first() as IdentifierReference
|
||||
(name2.targetVarDecl(result.compilerAst)!!.value as StringLiteral).value shouldBe "xx1xx2"
|
||||
(rept2.targetVarDecl(result.compilerAst)!!.value as StringLiteral).value shouldBe "xyzxyzxyzxyz"
|
||||
(name2.targetVarDecl()!!.value as StringLiteral).value shouldBe "xx1xx2"
|
||||
(rept2.targetVarDecl()!!.value as StringLiteral).value shouldBe "xyzxyzxyzxyz"
|
||||
}
|
||||
|
||||
test("char as str param is error") {
|
||||
|
||||
@@ -403,45 +403,6 @@ main {
|
||||
cx16.r2 = &addresses
|
||||
}
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
|
||||
compileText(C64Target(), optimize=false, src, outputDir, writeAssembly=true, errors=errors) shouldNotBe null
|
||||
errors.errors.size shouldBe 0
|
||||
errors.warnings.size shouldBe 0
|
||||
}
|
||||
|
||||
test("assigning address of split arrays without explicitly taking address works") {
|
||||
val src="""
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
uword[] texts1 = [ 1,2,3 ]
|
||||
uword[] @nosplit texts2 = [ 1,2,3 ]
|
||||
|
||||
cx16.r4 = texts1
|
||||
cx16.r5 = texts2
|
||||
|
||||
; txt.print_uwhex(&texts1, true)
|
||||
; txt.spc()
|
||||
; txt.print_uwhex(cx16.r4, true)
|
||||
; txt.nl()
|
||||
; txt.print_uwhex(&texts2, true)
|
||||
; txt.spc()
|
||||
; txt.print_uwhex(cx16.r5, true)
|
||||
; txt.nl()
|
||||
|
||||
cx16.r4 = &texts1
|
||||
cx16.r5 = &texts2
|
||||
|
||||
; txt.print_uwhex(&texts1, true)
|
||||
; txt.spc()
|
||||
; txt.print_uwhex(cx16.r4, true)
|
||||
; txt.nl()
|
||||
; txt.print_uwhex(&texts2, true)
|
||||
; txt.spc()
|
||||
; txt.print_uwhex(cx16.r5, true)
|
||||
; txt.nl()
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
|
||||
compileText(C64Target(), optimize=false, src, outputDir, writeAssembly=true, errors=errors) shouldNotBe null
|
||||
|
||||
@@ -125,29 +125,24 @@ main {
|
||||
|
||||
val result = compileText(C64Target(), true, src, outputDir, writeAssembly = true)!!.compilerAst
|
||||
val main = result.allBlocks.first { it.name=="main" }
|
||||
main.statements.size shouldBe 17
|
||||
main.statements.size shouldBe 15
|
||||
val assigns = main.statements.filterIsInstance<Assignment>()
|
||||
assigns[0].target.identifier?.nameInSource shouldBe listOf("value1")
|
||||
assigns[1].target.identifier?.nameInSource shouldBe listOf("value2")
|
||||
assigns[2].target.identifier?.nameInSource shouldBe listOf("value3")
|
||||
assigns[3].target.identifier?.nameInSource shouldBe listOf("value4")
|
||||
assigns[4].target.identifier?.nameInSource shouldBe listOf("value5")
|
||||
assigns[5].target.identifier?.nameInSource shouldBe listOf("value6")
|
||||
assigns[6].target.identifier?.nameInSource shouldBe listOf("value7")
|
||||
assigns.size shouldBe 5
|
||||
assigns[0].target.identifier?.nameInSource shouldBe listOf("value2")
|
||||
assigns[1].target.identifier?.nameInSource shouldBe listOf("value3")
|
||||
assigns[2].target.identifier?.nameInSource shouldBe listOf("value5")
|
||||
assigns[3].target.identifier?.nameInSource shouldBe listOf("value6")
|
||||
assigns[4].target.identifier?.nameInSource shouldBe listOf("value7")
|
||||
assigns[0].origin shouldBe AssignmentOrigin.VARINIT
|
||||
assigns[1].origin shouldBe AssignmentOrigin.VARINIT
|
||||
assigns[2].origin shouldBe AssignmentOrigin.VARINIT
|
||||
assigns[3].origin shouldBe AssignmentOrigin.VARINIT
|
||||
assigns[4].origin shouldBe AssignmentOrigin.VARINIT
|
||||
assigns[5].origin shouldBe AssignmentOrigin.VARINIT
|
||||
assigns[6].origin shouldBe AssignmentOrigin.VARINIT
|
||||
assigns[0].value.constValue(result)?.number shouldBe 0
|
||||
assigns[1].value.constValue(result)?.number shouldBe 1
|
||||
assigns[2].value.constValue(result)?.number shouldBe 1
|
||||
assigns[3].value.constValue(result)?.number shouldBe 0
|
||||
assigns[0].value.constValue(result)?.asBooleanValue shouldBe true
|
||||
assigns[1].value.constValue(result)?.asBooleanValue shouldBe true
|
||||
assigns[2].value.constValue(result) shouldBe null
|
||||
assigns[3].value.constValue(result)?.number shouldBe 4242
|
||||
assigns[4].value.constValue(result) shouldBe null
|
||||
assigns[5].value.constValue(result)?.number shouldBe 4242
|
||||
assigns[6].value.constValue(result) shouldBe null
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
|
||||
|
||||
override fun noErrors(): Boolean = errors.isEmpty()
|
||||
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
|
||||
override fun print_single_error(errormessage: String) { /* prints nothing in tests */ }
|
||||
override fun printSingleError(errormessage: String) { /* prints nothing in tests */ }
|
||||
|
||||
override fun report() {
|
||||
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
|
||||
|
||||
@@ -126,7 +126,7 @@ class Program(val name: String,
|
||||
private class StringSearch(val program: Program): IAstVisitor {
|
||||
val removals = mutableListOf<List<String>>()
|
||||
override fun visit(identifier: IdentifierReference) {
|
||||
if(identifier.wasStringLiteral(program))
|
||||
if(identifier.wasStringLiteral())
|
||||
removals.add(identifier.nameInSource)
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ private fun StatementContext.toAst() : Statement {
|
||||
val pos = it.toPosition()
|
||||
// print("\u001b[92mINFO\u001B[0m ") // bright green
|
||||
// println("${pos}: ++ and -- will be removed in a future version, please use +=1 or -=1 instead.") // .... if we decode to remove them one day
|
||||
val addSubOne = BinaryExpression(tgt.toExpression(), if(operator=="++") "+" else "-", NumericLiteral.optimalInteger(1, pos), pos, false)
|
||||
val addSubOne = BinaryExpression(tgt.toExpression(), if(operator=="++") "+" else "-", NumericLiteral.optimalInteger(1, pos), pos)
|
||||
return Assignment(tgt, addSubOne, AssignmentOrigin.USERCODE, pos)
|
||||
}
|
||||
|
||||
@@ -174,7 +174,8 @@ private fun AsmsubroutineContext.toAst(): Subroutine {
|
||||
val statements = statement_block()?.toAst() ?: mutableListOf()
|
||||
return Subroutine(subdecl.name, subdecl.parameters.toMutableList(), subdecl.returntypes.toMutableList(),
|
||||
subdecl.asmParameterRegisters, subdecl.asmReturnvaluesRegisters,
|
||||
subdecl.asmClobbers, null, true, inline, false, statements, toPosition())
|
||||
subdecl.asmClobbers, null, true, inline, statements = statements, position = toPosition()
|
||||
)
|
||||
}
|
||||
|
||||
private fun ExtsubroutineContext.toAst(): Subroutine {
|
||||
|
||||
@@ -1164,14 +1164,14 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
||||
|
||||
override val isSimple = true
|
||||
|
||||
fun targetStatement(program: Program) =
|
||||
if(nameInSource.singleOrNull() in program.builtinFunctions.names)
|
||||
fun targetStatement(program: Program?) =
|
||||
if(program!=null && nameInSource.singleOrNull() in program.builtinFunctions.names)
|
||||
BuiltinFunctionPlaceholder(nameInSource[0], position, parent)
|
||||
else
|
||||
definingScope.lookup(nameInSource)
|
||||
|
||||
fun targetVarDecl(program: Program): VarDecl? = targetStatement(program) as? VarDecl
|
||||
fun targetSubroutine(program: Program): Subroutine? = targetStatement(program) as? Subroutine
|
||||
fun targetVarDecl(): VarDecl? = targetStatement(null) as? VarDecl
|
||||
fun targetSubroutine(): Subroutine? = targetStatement(null) as? Subroutine
|
||||
|
||||
fun targetNameAndType(program: Program): Pair<String, DataType> {
|
||||
val target = targetStatement(program) as? INamedStatement ?: throw FatalAstException("can't find target for $nameInSource")
|
||||
@@ -1241,8 +1241,8 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
||||
}
|
||||
}
|
||||
|
||||
fun wasStringLiteral(program: Program): Boolean {
|
||||
val decl = targetVarDecl(program)
|
||||
fun wasStringLiteral(): Boolean {
|
||||
val decl = targetVarDecl()
|
||||
if(decl == null || decl.origin!=VarDeclOrigin.STRINGLITERAL)
|
||||
return false
|
||||
|
||||
|
||||
@@ -84,9 +84,9 @@ object InferredTypes {
|
||||
type.isLong -> InferredType.known(BaseDataType.LONG)
|
||||
type.isSplitWordArray -> {
|
||||
when(type.sub) {
|
||||
BaseDataType.UWORD -> InferredType.known(DataType.arrayFor(BaseDataType.UWORD, true))
|
||||
BaseDataType.WORD -> InferredType.known(DataType.arrayFor(BaseDataType.WORD, true))
|
||||
BaseDataType.STR -> InferredType.known(DataType.arrayFor(BaseDataType.STR, true))
|
||||
BaseDataType.UWORD -> InferredType.known(DataType.arrayFor(BaseDataType.UWORD))
|
||||
BaseDataType.WORD -> InferredType.known(DataType.arrayFor(BaseDataType.WORD))
|
||||
BaseDataType.STR -> InferredType.known(DataType.arrayFor(BaseDataType.STR))
|
||||
else -> throw IllegalArgumentException("invalid sub type")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -647,7 +647,7 @@ data class AssignTarget(var identifier: IdentifierReference?,
|
||||
return target.isIOAddress(addr.number.toUInt())
|
||||
return when (memAddr.addressExpression) {
|
||||
is IdentifierReference -> {
|
||||
val decl = (memAddr.addressExpression as IdentifierReference).targetVarDecl(definingModule.program)
|
||||
val decl = (memAddr.addressExpression as IdentifierReference).targetVarDecl()
|
||||
val result = if ((decl?.type == VarDeclType.MEMORY || decl?.type == VarDeclType.CONST) && decl.value is NumericLiteral)
|
||||
target.isIOAddress((decl.value as NumericLiteral).number.toUInt())
|
||||
else
|
||||
@@ -658,7 +658,7 @@ data class AssignTarget(var identifier: IdentifierReference?,
|
||||
}
|
||||
}
|
||||
arrayIdx != null -> {
|
||||
val targetStmt = arrayIdx.arrayvar.targetVarDecl(definingModule.program)
|
||||
val targetStmt = arrayIdx.arrayvar.targetVarDecl()
|
||||
return if (targetStmt?.type == VarDeclType.MEMORY) {
|
||||
val addr = targetStmt.value as? NumericLiteral
|
||||
if (addr != null)
|
||||
@@ -668,7 +668,7 @@ data class AssignTarget(var identifier: IdentifierReference?,
|
||||
} else false
|
||||
}
|
||||
ident != null -> {
|
||||
val decl = ident.targetVarDecl(definingModule.program) ?: throw FatalAstException("invalid identifier ${ident.nameInSource}")
|
||||
val decl = ident.targetVarDecl() ?: throw FatalAstException("invalid identifier ${ident.nameInSource}")
|
||||
return if (decl.type == VarDeclType.MEMORY && decl.value is NumericLiteral)
|
||||
target.isIOAddress((decl.value as NumericLiteral).number.toUInt())
|
||||
else
|
||||
|
||||
@@ -78,7 +78,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
||||
}
|
||||
|
||||
override fun visit(functionCallExpr: FunctionCallExpression) {
|
||||
val otherSub = functionCallExpr.target.targetSubroutine(program)
|
||||
val otherSub = functionCallExpr.target.targetSubroutine()
|
||||
if (otherSub != null) {
|
||||
val definingSub = functionCallExpr.definingSubroutine
|
||||
if(definingSub!=null) {
|
||||
@@ -90,7 +90,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
||||
}
|
||||
|
||||
override fun visit(functionCallStatement: FunctionCallStatement) {
|
||||
val otherSub = functionCallStatement.target.targetSubroutine(program)
|
||||
val otherSub = functionCallStatement.target.targetSubroutine()
|
||||
if (otherSub != null) {
|
||||
functionCallStatement.definingSubroutine?.let { thisSub ->
|
||||
calls[thisSub] = calls.getValue(thisSub) + otherSub
|
||||
@@ -101,12 +101,12 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
||||
}
|
||||
|
||||
override fun visit(addressOf: AddressOf) {
|
||||
addressOf.identifier.targetSubroutine(program)?.let { notCalledButReferenced.add(it) }
|
||||
addressOf.identifier.targetSubroutine()?.let { notCalledButReferenced.add(it) }
|
||||
super.visit(addressOf)
|
||||
}
|
||||
|
||||
override fun visit(jump: Jump) {
|
||||
val otherSub = (jump.target as? IdentifierReference)?.targetSubroutine(program)
|
||||
val otherSub = (jump.target as? IdentifierReference)?.targetSubroutine()
|
||||
if (otherSub != null) {
|
||||
jump.definingSubroutine?.let { thisSub ->
|
||||
calls[thisSub] = calls.getValue(thisSub) + otherSub
|
||||
@@ -126,7 +126,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
||||
|
||||
// if it's a scoped identifier, the subroutines in the name are also referenced!
|
||||
val scope = identifier.definingScope
|
||||
val name = identifier.nameInSource.toMutableList()
|
||||
val name = ArrayDeque(identifier.nameInSource)
|
||||
while(name.size>1) {
|
||||
name.removeLast()
|
||||
val scopeTarget = scope.lookup(name)
|
||||
@@ -225,8 +225,6 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
inline fun unused(label: Label) = false // just always output labels
|
||||
|
||||
fun unused(stmt: INamedStatement): Boolean {
|
||||
return when(stmt) {
|
||||
is Subroutine -> unused(stmt)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v11.3 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v11.3.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-c128.p8
|
||||
@@ -238,7 +238,7 @@ strings {
|
||||
endswith (str st, str suffix) -> bool
|
||||
find (uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc
|
||||
findstr (str haystack, str needle) -> ubyte
|
||||
hash (str string @R0) -> ubyte @A
|
||||
hash (str string @AY) -> ubyte @A
|
||||
isdigit (ubyte petsciichar @A) -> bool @Pc
|
||||
isletter (ubyte petsciichar @A) -> bool @Pc
|
||||
islower (ubyte petsciichar @A) -> bool @Pc
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v11.3 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v11.3.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-c64.p8
|
||||
@@ -365,7 +365,7 @@ strings {
|
||||
endswith (str st, str suffix) -> bool
|
||||
find (uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc
|
||||
findstr (str haystack, str needle) -> ubyte
|
||||
hash (str string @R0) -> ubyte @A
|
||||
hash (str string @AY) -> ubyte @A
|
||||
isdigit (ubyte petsciichar @A) -> bool @Pc
|
||||
isletter (ubyte petsciichar @A) -> bool @Pc
|
||||
islower (ubyte petsciichar @A) -> bool @Pc
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v11.3 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v11.3.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-cx16.p8
|
||||
@@ -673,7 +673,7 @@ strings {
|
||||
endswith (str st, str suffix) -> bool
|
||||
find (uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc
|
||||
findstr (str haystack, str needle) -> ubyte
|
||||
hash (str string @R0) -> ubyte @A
|
||||
hash (str string @AY) -> ubyte @A
|
||||
isdigit (ubyte petsciichar @A) -> bool @Pc
|
||||
isletter (ubyte petsciichar @A) -> bool @Pc
|
||||
islower (ubyte petsciichar @A) -> bool @Pc
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v11.3 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v11.3.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-pet32.p8
|
||||
@@ -189,7 +189,7 @@ strings {
|
||||
endswith (str st, str suffix) -> bool
|
||||
find (uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc
|
||||
findstr (str haystack, str needle) -> ubyte
|
||||
hash (str string @R0) -> ubyte @A
|
||||
hash (str string @AY) -> ubyte @A
|
||||
isdigit (ubyte petsciichar @A) -> bool @Pc
|
||||
isletter (ubyte petsciichar @A) -> bool @Pc
|
||||
islower (ubyte petsciichar @A) -> bool @Pc
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v11.3 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v11.3.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-virtual.p8
|
||||
|
||||
@@ -145,8 +145,7 @@ One or more .p8 module files
|
||||
a breakpoint halt in the debugger when this is enabled by running the emulator with -debug.
|
||||
|
||||
``-bytes2float <bytes>``
|
||||
convert a comma separated list of bytes from the target system to a float value.
|
||||
NOTE: you need to supply a target option too, and also still have to supply a dummy module file name as well!
|
||||
convert a comma separated list of bytes from the specified target system to a float value.
|
||||
Also see -float2bytes
|
||||
|
||||
``-check``
|
||||
@@ -176,8 +175,7 @@ One or more .p8 module files
|
||||
Use experimental code generation backend (*incomplete*).
|
||||
|
||||
``-float2bytes <number>``
|
||||
convert floating point number to a list of bytes for the target system.
|
||||
NOTE: you need to supply a target option too, and also still have to supply a dummy module file name as well!
|
||||
convert floating point number to a list of bytes for the specified target system.
|
||||
Also see -bytes2float
|
||||
|
||||
``-ignorefootguns``
|
||||
@@ -259,6 +257,9 @@ One or more .p8 module files
|
||||
(it's called 'BSS' section or Gap at the address mentioned above).
|
||||
Assembling the program will fail if there are too many variables to fit in a single high ram bank.
|
||||
|
||||
``-version``
|
||||
Just print the compiler version and copyright message, and exit.
|
||||
|
||||
``-vm``
|
||||
load and run a 'p8ir' intermediate representation file in the internal VirtualMachine instead of compiling a prog8 program file.
|
||||
|
||||
|
||||
@@ -190,6 +190,7 @@ memory (name, size, alignment)
|
||||
with the given name. The name must be a string literal, it cannot be empty or be a variable.
|
||||
The block is *uninitialized memory*; unlike other variables in Prog8 it is *not* set to zero at the start of the program!
|
||||
(if that is required, you can do so yourself using ``memset``).
|
||||
No *dynamic* allocation is done; the block with this name is placed in memory only once!
|
||||
If you specify an alignment value >1, it means the block of memory will
|
||||
be aligned to such a dividable address in memory, for instance an alignment of $100 means the
|
||||
memory block is aligned on a page boundary, and $2 means word aligned (even addresses).
|
||||
|
||||
@@ -923,6 +923,8 @@ arithmetic: ``+`` ``-`` ``*`` ``/`` ``%``
|
||||
bitwise arithmetic: ``&`` ``|`` ``^`` ``~`` ``<<`` ``>>``
|
||||
``&`` is bitwise and, ``|`` is bitwise or, ``^`` is bitwise xor, ``~`` is bitwise invert (this one is an unary operator)
|
||||
``<<`` is bitwise left shift and ``>>`` is bitwise right shift (both will not change the datatype of the value)
|
||||
While the operands can be signed integers (the expression will just consider the underlying bit patterns),
|
||||
the result value of a bitwise expression is always unsigned.
|
||||
|
||||
assignment: ``=``
|
||||
Sets the target on the LHS (left hand side) of the operator to the value of the expression on the RHS (right hand side).
|
||||
|
||||
@@ -237,6 +237,9 @@ Some notes and references into the compiler's source code modules:
|
||||
is more or less a machine code language for a virtual machine - and indeed this is what the built-in
|
||||
prog8 VM will execute if you use the 'virtual' compilation target and use ``-emu`` to launch the VM.
|
||||
(``intermediate`` and ``codeGenIntermediate`` modules, and ``virtualmachine`` module for the VM related stuff)
|
||||
Note that this IR is still *targeted to one specific compilation target only*; various properties and all library
|
||||
code for the selected target machine is encoded into the IR. It is *not possible* to eventually create a C64 program
|
||||
from an IR file crated for the CommanderX16 target.
|
||||
#. The code generator backends all implement a common interface ``ICodeGeneratorBackend`` defined in the ``codeCore`` module.
|
||||
Currently they get handed the program Ast, Symboltable and several other things.
|
||||
If the code generator wants it can use the ``IRCodeGen`` class from the ``codeGenIntermediate`` module
|
||||
|
||||
+10
-17
@@ -1,17 +1,20 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
|
||||
...
|
||||
|
||||
|
||||
Future Things and Ideas
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- STRUCTS: are now being developed in their own separate branch "structs". This will be for the next major version of the compiler (v12)
|
||||
- is "checkAssignmentCompatible" redundant (gets called just 1 time!) when we also have "checkValueTypeAndRange" ?
|
||||
- romable: should we have a way to explicitly set the memory address for the BSS area (instead of only the highram bank number on X16, allow a memory address too for the -varshigh option?)
|
||||
- Kotlin: can we use inline value classes in certain spots?
|
||||
- add float support to the configurable compiler targets
|
||||
- Improve the SublimeText syntax file for prog8, you can also install this for 'bat': https://github.com/sharkdp/bat?tab=readme-ov-file#adding-new-syntaxes--language-definitions
|
||||
- Change scoping rules for qualified symbols so that they don't always start from the root but behave like other programming languages (look in local scope first)
|
||||
- Change scoping rules for qualified symbols so that they don't always start from the root but behave like other programming languages (look in local scope first), maybe only when qualified symbol starts with '.' such as: .local.value = 33
|
||||
- something to reduce the need to use fully qualified names all the time. 'with' ? Or 'using <prefix>'?
|
||||
- Improve register load order in subroutine call args assignments:
|
||||
in certain situations (need examples!), the "wrong" order of evaluation of function call arguments is done which results
|
||||
@@ -19,7 +22,7 @@ Future Things and Ideas
|
||||
Maybe this routine can be made more intelligent. See usesOtherRegistersWhileEvaluating() and argumentsViaRegisters().
|
||||
- Does it make codegen easier if everything is an expression? Start with the PtProgram ast classes, change statements to expressions that have (new) VOID data type
|
||||
- Can we support signed % (remainder) somehow?
|
||||
- Multidimensional arrays and chained indexing, purely as syntactic sugar over regular arrays. Probaby only useful if we have typed pointers.
|
||||
- Multidimensional arrays and chained indexing, purely as syntactic sugar over regular arrays. Probaby only useful if we have typed pointers. (addressed in 'struct' branch)
|
||||
- make a form of "manual generics" possible like: varsub routine(T arg)->T where T is expanded to a specific type
|
||||
(this is already done hardcoded for several of the builtin functions)
|
||||
- [much work:] more support for (64tass) SEGMENTS in the prog8 syntax itself?
|
||||
@@ -30,7 +33,8 @@ Future Things and Ideas
|
||||
IR/VM
|
||||
-----
|
||||
- getting it in shape for code generation...: the IR file should be able to encode every detail about a prog8 program (the VM doesn't have to actually be able to run all of it though!)
|
||||
- fix call() return value handling
|
||||
- fix call() return value handling (... what's wrong with it again?)
|
||||
- encode asmsub/extsub clobber info in the call , or maybe include these definitions in the p8ir file itself too. (return registers are already encoded in the CALL instruction)
|
||||
- proper code gen for the CALLI instruction and that it (optionally) returns a word value that needs to be assigned to a reg
|
||||
- implement fast code paths for TODO("inplace split....
|
||||
- implement more TODOs in AssignmentGen
|
||||
@@ -40,6 +44,8 @@ IR/VM
|
||||
cx16.r0sL = cx16.r1s as byte }
|
||||
- do something with the 'split' tag on split word arrays
|
||||
- add more optimizations in IRPeepholeOptimizer
|
||||
- reduce register usage via linear-scan algorithm (based on live intervals) https://anoopsarkar.github.io/compilers-class/assets/lectures/opt3-regalloc-linearscan.pdf
|
||||
don't forget to take into account the data type of the register when it's going to be reused!
|
||||
- idea: (but LLVM IR simply keeps the variables, so not a good idea then?...): replace all scalar variables by an allocated register. Keep a table of the variable to register mapping (including the datatype)
|
||||
global initialization values are simply a list of LOAD instructions.
|
||||
Variables replaced include all subroutine parameters! So the only variables that remain as variables are arrays and strings.
|
||||
@@ -51,6 +57,7 @@ IR/VM
|
||||
Libraries
|
||||
---------
|
||||
- Add split-word array sorting routines to sorting module?
|
||||
- cx16: _irq_dispatcher now only dispatches a single irq source, better to ROL/BCC to handle *all* possible (multiple) sources.
|
||||
- See if the raster interrupt handler on the C64 can be tweaked to be a more stable raster irq
|
||||
- pet32 target: make syslib more complete (missing kernal routines)?
|
||||
- need help with: PET disk routines (OPEN, SETLFS etc are not exposed as kernal calls)
|
||||
@@ -67,17 +74,3 @@ Optimizations
|
||||
for instance, vars used inside loops first, then loopvars, then uwords used as pointers (or these first??), then the rest
|
||||
- various optimizers skip stuff if compTarget.name==VMTarget.NAME. Once 6502-codegen is done from IR code,
|
||||
those checks should probably be removed, or be made permanent
|
||||
|
||||
|
||||
STRUCTS?
|
||||
--------
|
||||
|
||||
- declare struct *type*, or directly declare the variable itself? Problem with the latter is: you cannot easily define multiple variables of the same struct type.
|
||||
- can contain only numeric types (byte,word,float) - no nested structs, no reference types (strings, arrays) inside structs, max 1 page of memory total size to allow regular indexing?
|
||||
- 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 remains the type for a string literal (so we can keep doing register-indexed addressing directly on it)
|
||||
- ARRAY remains the type for an array literal (so we can keep doing register-indexed addressing directly on it)
|
||||
- we probably need to have a STRBYREF and ARRAYBYREF if we deal with a pointer to a string / array (such as when passing it to a function)
|
||||
the subtype of those should include the declared element type and the declared length of the string / array
|
||||
+2
-2
@@ -26,8 +26,8 @@ main {
|
||||
BX[lp] = math.rnd() % txt.DEFAULT_WIDTH
|
||||
BY[lp] = math.rnd() % txt.DEFAULT_HEIGHT
|
||||
BC[lp] = math.rnd() & 15
|
||||
DX[lp] = math.rnd() & 1
|
||||
DY[lp] = math.rnd() & 1
|
||||
DX[lp] = math.rnd() & 1 == 1
|
||||
DY[lp] = math.rnd() & 1 == 1
|
||||
}
|
||||
|
||||
; display balls
|
||||
|
||||
+8
-19
@@ -1,26 +1,15 @@
|
||||
%import textio
|
||||
%import diskio
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
%import textio
|
||||
%import strings
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
uword[] @nosplit ptrs = [one, two, three]
|
||||
str name1 = sc:"irmen de jong 123456789 the quick brown fox"
|
||||
str name2 = sc:"jumps over the lazy frog"
|
||||
|
||||
ubyte @shared x =1
|
||||
|
||||
goto ptrs[x]
|
||||
txt.print_ub(strings.hash(name1))
|
||||
txt.spc()
|
||||
txt.print_ub(strings.hash(name2))
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
sub one() {
|
||||
txt.print("one\n")
|
||||
}
|
||||
sub two() {
|
||||
txt.print("two\n")
|
||||
}
|
||||
sub three() {
|
||||
txt.print("three\n")
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -3,4 +3,4 @@ org.gradle.console=rich
|
||||
org.gradle.parallel=true
|
||||
org.gradle.daemon=true
|
||||
kotlin.code.style=official
|
||||
version=11.3
|
||||
version=11.3.1
|
||||
@@ -39,8 +39,8 @@ All have type b or w or f.
|
||||
load reg1, value - load immediate value into register. If you supply a symbol, loads the *address* of the symbol! (variable values are loaded from memory via the loadm instruction)
|
||||
loadm reg1, address - load reg1 with value at memory address
|
||||
loadi reg1, reg2 - load reg1 with value at memory indirect, memory pointed to by reg2
|
||||
loadx reg1, reg2, address - load reg1 with value at memory address indexed by value in reg2 (only the lsb part used for indexing)
|
||||
loadix reg1, reg2, pointeraddr - load reg1 with value at memory indirect, pointed to by pointeraddr indexed by value in reg2 (only the lsb part used for indexing)
|
||||
loadx reg1, reg2, address - load reg1 with value at memory address indexed by value in reg2 (0-255, a byte)
|
||||
loadix reg1, reg2, pointeraddr - load reg1 with value at memory indirect, pointed to by pointeraddr indexed by value in reg2 (0-255, a byte)
|
||||
loadr reg1, reg2 - load reg1 with value in register reg2, "reg1 = reg2"
|
||||
loadha reg1 - load cpu hardware register A into reg1.b
|
||||
loadhx reg1 - load cpu hardware register X into reg1.b
|
||||
@@ -52,11 +52,11 @@ loadfaczero fpreg1 - load "cpu hardware register" fac0 into f
|
||||
loadfacone fpreg1 - load "cpu hardware register" fac1 into freg1.f
|
||||
storem reg1, address - store reg1 at memory address
|
||||
storei reg1, reg2 - store reg1 at memory indirect, memory pointed to by reg2
|
||||
storex reg1, reg2, address - store reg1 at memory address, indexed by value in reg2 (only the lsb part used for indexing)
|
||||
storeix reg1, reg2, pointeraddr - store reg1 at memory indirect, pointed to by pointeraddr indexed by value in reg2 (only the lsb part used for indexing)
|
||||
storex reg1, reg2, address - store reg1 at memory address, indexed by value in reg2 (0-255, a byte)
|
||||
storeix reg1, reg2, pointeraddr - store reg1 at memory indirect, pointed to by pointeraddr indexed by value in reg2 (0-255, a byte)
|
||||
storezm address - store zero at memory address
|
||||
storezi reg1 - store zero at memory pointed to by reg1
|
||||
storezx reg1, address - store zero at memory address, indexed by value in reg1 (only the lsb part used for indexing)
|
||||
storezx reg1, address - store zero at memory address, indexed by value in reg1 (0-255, a byte)
|
||||
storeha reg1 - store reg1.b into cpu hardware register A
|
||||
storehx reg1 - store reg1.b into cpu hardware register X
|
||||
storehy reg1 - store reg1.b into cpu hardware register Y
|
||||
@@ -71,7 +71,6 @@ CONTROL FLOW
|
||||
------------
|
||||
jump location - continue running at instruction at 'location' (label/memory address)
|
||||
jumpi reg1 - continue running at memory address in reg1 (indirect jump)
|
||||
preparecall numparams - indicator that the next instructions are the param setup and function call/syscall with <numparams> parameters, does nothing by itself
|
||||
calli reg1 - calls a subroutine (without arguments and without return valus) at memory addres in reg1 (indirect jsr)
|
||||
call label(argument register list) [: resultreg.type]
|
||||
- calls a subroutine with the given arguments and return value (optional).
|
||||
@@ -80,11 +79,13 @@ call label(argument register list) [: resultreg.type]
|
||||
If the call is to a rom-routine, 'label' will be a hexadecimal address instead such as $ffd2
|
||||
If the arguments should be passed in CPU registers, they'll have a @REGISTER postfix.
|
||||
For example: call $ffd2(r5.b@A)
|
||||
Always preceded by parameter setup and preparecall instructions
|
||||
Always preceded by parameter setup
|
||||
callfar bank, address Call a subroutine at the given memory address, in the given RAM/ROM bank (switches both banks at the same time)
|
||||
callfarvb reg1 address Call a subroutine at the given memory address, in the RAM/ROM bank in reg1.b (switches both banks at the same time)
|
||||
syscall number (argument register list) [: resultreg.type]
|
||||
- do a systemcall identified by number, result value(s) are pushed on value stack by the syscall code so
|
||||
will be POPped off into the given resultregister if any.
|
||||
Always preceded by parameter setup and preparecall instructions.
|
||||
Always preceded by parameter setup
|
||||
All register types (arguments + result register) are ALWAYS WORDS.
|
||||
return - restore last saved instruction location and continue at that instruction. No return value.
|
||||
returnr reg1 - like return, but also returns the value in reg1 to the caller
|
||||
@@ -281,9 +282,10 @@ enum class Opcode {
|
||||
|
||||
JUMP,
|
||||
JUMPI,
|
||||
PREPARECALL,
|
||||
CALLI,
|
||||
CALL,
|
||||
CALLFAR,
|
||||
CALLFARVB,
|
||||
SYSCALL,
|
||||
RETURN,
|
||||
RETURNR,
|
||||
@@ -428,6 +430,8 @@ val OpcodesThatBranch = arrayOf(
|
||||
Opcode.RETURNI,
|
||||
Opcode.CALLI,
|
||||
Opcode.CALL,
|
||||
Opcode.CALLFAR,
|
||||
Opcode.CALLFARVB,
|
||||
Opcode.SYSCALL,
|
||||
Opcode.BSTCC,
|
||||
Opcode.BSTCS,
|
||||
@@ -620,9 +624,10 @@ val instructionFormats = mutableMapOf(
|
||||
Opcode.STOREHFACONE to InstructionFormat.from("F,<fr1"),
|
||||
Opcode.JUMP to InstructionFormat.from("N,<a"),
|
||||
Opcode.JUMPI to InstructionFormat.from("N,<r1"),
|
||||
Opcode.PREPARECALL to InstructionFormat.from("N,<i"),
|
||||
Opcode.CALLI to InstructionFormat.from("N,<r1"),
|
||||
Opcode.CALL to InstructionFormat.from("N,call"),
|
||||
Opcode.CALLFAR to InstructionFormat.from("N,<i,<a"),
|
||||
Opcode.CALLFARVB to InstructionFormat.from("N,<r1,<a"),
|
||||
Opcode.SYSCALL to InstructionFormat.from("N,syscall"),
|
||||
Opcode.RETURN to InstructionFormat.from("N"),
|
||||
Opcode.RETURNR to InstructionFormat.from("BW,<r1 | F,<fr1"),
|
||||
@@ -890,7 +895,7 @@ data class IRInstruction(
|
||||
val existingType = regsTypes[reg1]
|
||||
if (existingType!=null) {
|
||||
if (existingType != actualtype)
|
||||
throw IllegalArgumentException("register $reg1 assigned multiple types! $existingType and $actualtype in label ${chunk?.label} chunk $chunk")
|
||||
throw IllegalArgumentException("register $reg1 given multiple types! $existingType and $actualtype in label ${chunk?.label} chunk $chunk")
|
||||
} else
|
||||
regsTypes[reg1] = actualtype
|
||||
}
|
||||
@@ -902,7 +907,7 @@ data class IRInstruction(
|
||||
val existingType = regsTypes[reg1]
|
||||
if (existingType!=null) {
|
||||
if (existingType != actualtype)
|
||||
throw IllegalArgumentException("register $reg1 assigned multiple types! $existingType and $actualtype in label ${chunk?.label} chunk $chunk")
|
||||
throw IllegalArgumentException("register $reg1 given multiple types! $existingType and $actualtype in label ${chunk?.label} chunk $chunk")
|
||||
} else
|
||||
regsTypes[reg1] = actualtype
|
||||
|
||||
@@ -916,7 +921,7 @@ data class IRInstruction(
|
||||
val existingType = regsTypes[reg1]
|
||||
if (existingType!=null) {
|
||||
if (existingType != actualtype)
|
||||
throw IllegalArgumentException("register $reg1 assigned multiple types! $existingType and $actualtype in label ${chunk?.label} chunk $chunk")
|
||||
throw IllegalArgumentException("register $reg1 given multiple types! $existingType and $actualtype in label ${chunk?.label} chunk $chunk")
|
||||
} else
|
||||
regsTypes[reg1] = actualtype
|
||||
|
||||
@@ -932,7 +937,7 @@ data class IRInstruction(
|
||||
val existingType = regsTypes[reg2]
|
||||
if (existingType!=null) {
|
||||
if (existingType != actualtype)
|
||||
throw IllegalArgumentException("register $reg2 assigned multiple types! $existingType and $actualtype in label ${chunk?.label} chunk $chunk")
|
||||
throw IllegalArgumentException("register $reg2 given multiple types! $existingType and $actualtype in label ${chunk?.label} chunk $chunk")
|
||||
} else
|
||||
regsTypes[reg2] = actualtype
|
||||
}
|
||||
@@ -948,7 +953,7 @@ data class IRInstruction(
|
||||
val existingType = regsTypes[reg3]
|
||||
if (existingType!=null) {
|
||||
if (existingType != actualtype)
|
||||
throw IllegalArgumentException("register $reg3 assigned multiple types! $existingType and $actualtype in label ${chunk?.label} chunk $chunk")
|
||||
throw IllegalArgumentException("register $reg3 given multiple types! $existingType and $actualtype in label ${chunk?.label} chunk $chunk")
|
||||
} else
|
||||
regsTypes[reg3] = actualtype
|
||||
}
|
||||
@@ -981,7 +986,7 @@ data class IRInstruction(
|
||||
val existingType = regsTypes[it.registerNum]
|
||||
if (existingType!=null) {
|
||||
if (existingType != it.dt)
|
||||
throw IllegalArgumentException("register ${it.registerNum} assigned multiple types! $existingType and ${it.dt} in label ${chunk?.label} chunk $chunk")
|
||||
throw IllegalArgumentException("register ${it.registerNum} given multiple types! $existingType and ${it.dt} in label ${chunk?.label} chunk $chunk")
|
||||
} else
|
||||
regsTypes[it.registerNum] = it.dt
|
||||
}
|
||||
@@ -994,7 +999,7 @@ data class IRInstruction(
|
||||
val existingType = regsTypes[it.reg.registerNum]
|
||||
if (existingType!=null) {
|
||||
if (existingType != it.reg.dt)
|
||||
throw IllegalArgumentException("register ${it.reg.registerNum} assigned multiple types! $existingType and ${it.reg.dt} in label ${chunk?.label} chunk $chunk")
|
||||
throw IllegalArgumentException("register ${it.reg.registerNum} given multiple types! $existingType and ${it.reg.dt} in label ${chunk?.label} chunk $chunk")
|
||||
} else
|
||||
regsTypes[it.reg.registerNum] = it.reg.dt
|
||||
}
|
||||
@@ -1005,7 +1010,7 @@ data class IRInstruction(
|
||||
private fun determineReg1Type(): IRDataType? {
|
||||
if(type==IRDataType.FLOAT) {
|
||||
// some float instructions have an integer register as well.
|
||||
return if(opcode in arrayOf(Opcode.FFROMUB, Opcode.FFROMSB, Opcode.FTOUB, Opcode.FTOSB, Opcode.FCOMP))
|
||||
return if(opcode in arrayOf(Opcode.FFROMUB, Opcode.FFROMSB, Opcode.FTOUB, Opcode.FTOSB, Opcode.FCOMP, Opcode.LOADIX, Opcode.LOADX, Opcode.STOREIX, Opcode.STOREX, Opcode.STOREZX))
|
||||
IRDataType.BYTE
|
||||
else
|
||||
IRDataType.WORD
|
||||
@@ -1088,7 +1093,7 @@ data class IRInstruction(
|
||||
}
|
||||
}
|
||||
if(result.last().endsWith(',')) {
|
||||
result.add(result.removeLast().trimEnd(','))
|
||||
result.add(result.removeLastOrNull()!!.trimEnd(','))
|
||||
}
|
||||
result.add(")")
|
||||
val returns = fcallArgs.returns
|
||||
@@ -1158,7 +1163,7 @@ data class IRInstruction(
|
||||
}
|
||||
}
|
||||
if(result.last() == ",")
|
||||
result.removeLast()
|
||||
result.removeLastOrNull()
|
||||
return result.joinToString("").trimEnd()
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,15 @@ import prog8.code.core.*
|
||||
|
||||
/*
|
||||
|
||||
note: all symbol names are flattened so that they're a single string that is globally unique.
|
||||
Intermediate Represenation of the compiled program.
|
||||
|
||||
Note that even though it is called IR, it is not fully "intermediate".
|
||||
This IR program is targeted to a single chosen target machine; some code in it
|
||||
and all included library files are for this specific single chosen compilation target.
|
||||
It will *not* be possible to create a binary program for a different target machine
|
||||
from this IR than the one initially chosen!
|
||||
|
||||
Note: all symbol names are flattened so that they're a single string that is globally unique.
|
||||
|
||||
|
||||
PROGRAM:
|
||||
@@ -57,9 +65,11 @@ class IRProgram(val name: String,
|
||||
val blocks = mutableListOf<IRBlock>()
|
||||
|
||||
fun allSubs(): Sequence<IRSubroutine> = blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }
|
||||
fun allAsmSubs(): Sequence<IRAsmSubroutine> = blocks.asSequence().flatMap { it.children.filterIsInstance<IRAsmSubroutine>() }
|
||||
fun foreachSub(operation: (sub: IRSubroutine) -> Unit) = allSubs().forEach { operation(it) }
|
||||
fun foreachCodeChunk(operation: (chunk: IRCodeChunkBase) -> Unit) {
|
||||
allSubs().flatMap { it.chunks }.forEach { operation(it) }
|
||||
allAsmSubs().forEach { operation(it.asmChunk) }
|
||||
operation(globalInits)
|
||||
}
|
||||
fun getChunkWithLabel(label: String): IRCodeChunkBase {
|
||||
@@ -69,6 +79,10 @@ class IRProgram(val name: String,
|
||||
return chunk
|
||||
}
|
||||
}
|
||||
for(sub in allAsmSubs()) {
|
||||
if(sub.asmChunk.label==label)
|
||||
return sub.asmChunk
|
||||
}
|
||||
throw NoSuchElementException("no chunk with label '$label'")
|
||||
}
|
||||
|
||||
@@ -209,7 +223,7 @@ class IRProgram(val name: String,
|
||||
if(chunk is IRInlineAsmChunk)
|
||||
require(!chunk.isIR) { "inline IR-asm should have been converted into regular code chunk"}
|
||||
}
|
||||
chunk.instructions.withIndex().forEach { (index, instr) ->
|
||||
chunk.instructions.forEach { instr ->
|
||||
if(instr.labelSymbol!=null && instr.opcode in OpcodesThatBranch) {
|
||||
if(instr.opcode==Opcode.JUMPI) {
|
||||
when(val pointervar = st.lookup(instr.labelSymbol)!!) {
|
||||
@@ -221,26 +235,6 @@ class IRProgram(val name: String,
|
||||
else if(!instr.labelSymbol.startsWith('$') && !instr.labelSymbol.first().isDigit())
|
||||
require(instr.branchTarget != null) { "branching instruction to label should have branchTarget set" }
|
||||
}
|
||||
|
||||
if(instr.opcode==Opcode.PREPARECALL) {
|
||||
var i = index+1
|
||||
var instr2 = chunk.instructions[i]
|
||||
val registers = mutableSetOf<Int>()
|
||||
while(instr2.opcode!=Opcode.SYSCALL && instr2.opcode!=Opcode.CALL && i<chunk.instructions.size-1) {
|
||||
if(instr2.reg1direction==OperandDirection.WRITE || instr2.reg1direction==OperandDirection.READWRITE) registers.add(instr2.reg1!!)
|
||||
if(instr2.reg2direction==OperandDirection.WRITE || instr2.reg2direction==OperandDirection.READWRITE) registers.add(instr2.reg2!!)
|
||||
if(instr2.reg3direction==OperandDirection.WRITE || instr2.reg3direction==OperandDirection.READWRITE) registers.add(instr2.reg3!!)
|
||||
if(instr2.fpReg1direction==OperandDirection.WRITE || instr2.fpReg1direction==OperandDirection.READWRITE) registers.add(instr2.fpReg1!!)
|
||||
if(instr2.fpReg2direction==OperandDirection.WRITE || instr2.fpReg2direction==OperandDirection.READWRITE) registers.add(instr2.fpReg2!!)
|
||||
i++
|
||||
instr2 = chunk.instructions[i]
|
||||
}
|
||||
// it could be that the actual call is only in another code chunk, so IF we find one, we can check, otherwise just skip the check...
|
||||
if(chunk.instructions[i].fcallArgs!=null) {
|
||||
val expectedRegisterLoads = chunk.instructions[i].fcallArgs!!.arguments.map { it.reg.registerNum }
|
||||
require(registers.containsAll(expectedRegisterLoads)) { "not all argument registers are given a value in the preparecall-call sequence" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,7 +271,7 @@ class IRProgram(val name: String,
|
||||
val existingType = regsTypes[reg]
|
||||
if (existingType!=null) {
|
||||
if (existingType != type)
|
||||
throw IllegalArgumentException("register $reg assigned multiple types! $existingType and $type ${this.name}<--${child.label ?: child}")
|
||||
throw IllegalArgumentException("register $reg given multiple types! $existingType and $type ${this.name}<--${child.label ?: child}")
|
||||
} else
|
||||
regsTypes[reg] = type
|
||||
}
|
||||
|
||||
@@ -198,6 +198,8 @@ class StStaticVariable(name: String,
|
||||
// This has to do with the way Prog8 does the (re)initialization of such variables: via code assignment statements.
|
||||
// Certain codegens might want to put them back into the variable directly.
|
||||
// For strings and arrays this doesn't occur - these are always already specced at creation time.
|
||||
|
||||
require(number!=0.0) { "variable should not be initialized with 0, it will already be zeroed as part of BSS clear" }
|
||||
initializationNumericValue = number
|
||||
}
|
||||
|
||||
|
||||
@@ -39,8 +39,8 @@ sealed interface IPtSubroutine {
|
||||
val availableFloatRegisters = mutableListOf(RegisterOrPair.FAC1) // just one value is possible
|
||||
val others = returns.drop(1).map { type ->
|
||||
when {
|
||||
type.isFloat -> RegisterOrStatusflag(availableFloatRegisters.removeLast(), null) to type
|
||||
type.isIntegerOrBool -> RegisterOrStatusflag(availableIntegerRegisters.removeLast(), null) to type
|
||||
type.isFloat -> RegisterOrStatusflag(availableFloatRegisters.removeLastOrNull()!!, null) to type
|
||||
type.isIntegerOrBool -> RegisterOrStatusflag(availableIntegerRegisters.removeLastOrNull()!!, null) to type
|
||||
else -> throw AssemblyError("unsupported return type $type")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,9 +216,9 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
Opcode.STOREHFACONE-> InsSTOREHFACONE(ins)
|
||||
Opcode.JUMP -> InsJUMP(ins)
|
||||
Opcode.JUMPI -> InsJUMPI(ins)
|
||||
Opcode.PREPARECALL -> nextPc()
|
||||
Opcode.CALLI -> throw IllegalArgumentException("VM cannot run code from memory bytes")
|
||||
Opcode.CALL -> InsCALL(ins)
|
||||
Opcode.CALLFAR, Opcode.CALLFARVB -> throw IllegalArgumentException("VM cannot run code from another ram/rombank")
|
||||
Opcode.SYSCALL -> InsSYSCALL(ins)
|
||||
Opcode.RETURN -> InsRETURN()
|
||||
Opcode.RETURNR -> InsRETURNR(ins)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package prog8.vm
|
||||
|
||||
import prog8.Either
|
||||
import prog8.code.core.DataType
|
||||
import prog8.intermediate.*
|
||||
import prog8.left
|
||||
import prog8.right
|
||||
import prog8.intermediate.*
|
||||
import prog8.code.core.DataType
|
||||
|
||||
class VmProgramLoader {
|
||||
private val placeholders = mutableMapOf<Pair<IRCodeChunk, Int>, String>() // program chunk+index to symbolname
|
||||
@@ -223,6 +223,15 @@ class VmProgramLoader {
|
||||
else -> throw IRParseException("invalid dt")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
when {
|
||||
variable.dt.isUnsignedByte || variable.dt.isBool -> memory.setUB(addr, 0u)
|
||||
variable.dt.isSignedByte -> memory.setSB(addr, 0)
|
||||
variable.dt.isUnsignedWord -> memory.setUW(addr, 0u)
|
||||
variable.dt.isSignedWord -> memory.setSW(addr, 0)
|
||||
variable.dt.isFloat -> memory.setFloat(addr, 0.0)
|
||||
else -> throw IRParseException("invalid dt")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user