mirror of
https://github.com/irmen/prog8.git
synced 2026-10-24 03:02:20 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f874942075 | ||
|
|
2cadb546d5 | ||
|
|
344a1b9eb8 | ||
|
|
3c77f8a020 | ||
|
|
8e00408e3e | ||
|
|
abcdfd8e28 | ||
|
|
b0f5b6925d | ||
|
|
ef79d0c43e | ||
|
|
78b4288005 | ||
|
|
680f5d21ee | ||
|
|
c71aa0895f | ||
|
|
9f8e61789a | ||
|
|
932035cdc5 | ||
|
|
ef198f1493 | ||
|
|
48ef856c0b | ||
|
|
9aea2b22c4 | ||
|
|
e0055bc431 | ||
|
|
9553248ed6 | ||
|
|
39d2194d8f | ||
|
|
0800033b47 | ||
|
|
64d8943b7d | ||
|
|
444e97b00b | ||
|
|
1816bda7ea | ||
|
|
d4a2031c07 | ||
|
|
8cf0b6cf51 | ||
|
|
f2010bf7a5 | ||
|
|
8f56a7fe69 | ||
|
|
64c132ee0a | ||
|
|
84a7e86fe3 | ||
|
|
a8c09d6144 | ||
|
|
87c46ba730 |
No files matched your search
@@ -138,16 +138,16 @@ class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) {
|
||||
|
||||
class PtArrayIndexer(elementType: DataType, position: Position): PtExpression(elementType, position) {
|
||||
val variable: PtIdentifier
|
||||
get() = children[0] as PtIdentifier
|
||||
get() {
|
||||
require((children[0] as? PtIdentifier)?.type in ArrayDatatypes+DataType.STR) // TODO remove
|
||||
return children[0] as PtIdentifier
|
||||
}
|
||||
val index: PtExpression
|
||||
get() = children[1] as PtExpression
|
||||
|
||||
val splitWords: Boolean
|
||||
get() = variable.type in SplitWordArrayTypes
|
||||
|
||||
val usesPointerVariable: Boolean
|
||||
get() = variable.type==DataType.UWORD
|
||||
|
||||
init {
|
||||
require(elementType in NumericDatatypes)
|
||||
}
|
||||
@@ -250,7 +250,10 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if(other==null || other !is PtNumber)
|
||||
return false
|
||||
return number==other.number
|
||||
else if(type!=DataType.BOOL && other.type!=DataType.BOOL)
|
||||
return number==other.number
|
||||
else
|
||||
return type==other.type && number==other.number
|
||||
}
|
||||
|
||||
operator fun compareTo(other: PtNumber): Int = number.compareTo(other.number)
|
||||
|
||||
@@ -25,7 +25,7 @@ class CompilationOptions(val output: OutputType,
|
||||
var slabsHighBank: Int? = null,
|
||||
var slabsGolden: Boolean = false,
|
||||
var splitWordArrays: Boolean = false,
|
||||
var breakpointCpuInstruction: Boolean = false,
|
||||
var breakpointCpuInstruction: String? = null,
|
||||
var outputDir: Path = Path(""),
|
||||
var symbolDefs: Map<String, String> = emptyMap()
|
||||
) {
|
||||
|
||||
@@ -69,7 +69,8 @@ sealed class SourceCode {
|
||||
* Turn a plain String into a [SourceCode] object.
|
||||
* [origin] will be something like `string:44c56085`.
|
||||
*/
|
||||
class Text(override val text: String): SourceCode() {
|
||||
class Text(origText: String): SourceCode() {
|
||||
override val text = origText.replace("\\R".toRegex(), "\n") // normalize line endings
|
||||
override val isFromResources = false
|
||||
override val isFromFilesystem = false
|
||||
override val origin = "$STRINGSOURCEPREFIX${System.identityHashCode(text).toString(16)}"
|
||||
@@ -95,7 +96,8 @@ sealed class SourceCode {
|
||||
val normalized = path.normalize()
|
||||
origin = relative(normalized).toString()
|
||||
try {
|
||||
text = Normalizer.normalize(normalized.readText(), Normalizer.Form.NFC)
|
||||
val contents = Normalizer.normalize(normalized.readText(), Normalizer.Form.NFC)
|
||||
text = contents.replace("\\R".toRegex(), "\n") // normalize line endings
|
||||
name = normalized.toFile().nameWithoutExtension
|
||||
} catch (nfx: java.nio.file.NoSuchFileException) {
|
||||
throw NoSuchFileException(normalized.toFile()).also { it.initCause(nfx) }
|
||||
@@ -127,7 +129,8 @@ sealed class SourceCode {
|
||||
)
|
||||
}
|
||||
val stream = object {}.javaClass.getResourceAsStream(normalized)
|
||||
text = stream!!.reader().use { Normalizer.normalize(it.readText(), Normalizer.Form.NFC) }
|
||||
val contents = stream!!.reader().use { Normalizer.normalize(it.readText(), Normalizer.Form.NFC) }
|
||||
text = contents.replace("\\R".toRegex(), "\n") // normalize line endings
|
||||
name = Path(pathString).toFile().nameWithoutExtension
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ private fun walkAst(root: PtNode, act: (node: PtNode, depth: Int) -> Boolean) {
|
||||
}
|
||||
|
||||
|
||||
private var tempVarCounter = 0
|
||||
|
||||
private fun optimizeCommonSubExpressions(program: PtProgram, errors: IErrorReporter): Int {
|
||||
|
||||
fun extractableSubExpr(expr: PtExpression): Boolean {
|
||||
@@ -74,7 +76,8 @@ private fun optimizeCommonSubExpressions(program: PtProgram, errors: IErrorRepor
|
||||
val occurrence1idx = occurrence1.parent.children.indexOf(occurrence1)
|
||||
val occurrence2idx = occurrence2.parent.children.indexOf(occurrence2)
|
||||
val containerScopedName = findScopeName(stmtContainer)
|
||||
val tempvarName = "subexprvar_line${binexpr.position.line}_${binexpr.hashCode().toUInt()}"
|
||||
tempVarCounter++
|
||||
val tempvarName = "prog8_subexprvar_$tempVarCounter"
|
||||
// TODO: some tempvars could be reused, if they are from different lines
|
||||
|
||||
val datatype = occurrence1.type
|
||||
@@ -95,7 +98,7 @@ private fun optimizeCommonSubExpressions(program: PtProgram, errors: IErrorRepor
|
||||
stmtContainer.children.add(stmtContainer.children.indexOf(stmt), tempassign)
|
||||
tempassign.parent = stmtContainer
|
||||
|
||||
val tempvar = PtVariable(tempvarName, datatype, ZeropageWish.DONTCARE, null, null, binexpr.position)
|
||||
val tempvar = PtVariable(tempvarName, datatype, ZeropageWish.NOT_IN_ZEROPAGE, null, null, binexpr.position)
|
||||
stmtContainer.add(0, tempvar)
|
||||
tempvar.parent = stmtContainer
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import prog8.code.SymbolTable
|
||||
import prog8.code.SymbolTableMaker
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.codegen.cpu6502.assignment.*
|
||||
import java.util.*
|
||||
import kotlin.io.path.Path
|
||||
@@ -223,7 +224,7 @@ class AsmGen6502Internal (
|
||||
internal val loopEndLabels = ArrayDeque<String>()
|
||||
private val zeropage = options.compTarget.machine.zeropage
|
||||
private val allocator = VariableAllocator(symbolTable, options, errors)
|
||||
private val assemblyLines = mutableListOf<String>()
|
||||
private val assembly = mutableListOf<String>()
|
||||
private val breakpointLabels = mutableListOf<String>()
|
||||
private val forloopsAsmGen = ForLoopsAsmGen(this, zeropage)
|
||||
private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this)
|
||||
@@ -235,7 +236,7 @@ class AsmGen6502Internal (
|
||||
|
||||
fun compileToAssembly(): IAssemblyProgram? {
|
||||
|
||||
assemblyLines.clear()
|
||||
assembly.clear()
|
||||
loopEndLabels.clear()
|
||||
|
||||
println("Generating assembly code... ")
|
||||
@@ -243,15 +244,22 @@ class AsmGen6502Internal (
|
||||
|
||||
if(errors.noErrors()) {
|
||||
val output = options.outputDir.resolve("${program.name}.asm")
|
||||
val asmLines = assembly.asSequence().flatMapTo(mutableListOf()) { it.split('\n') }
|
||||
if(options.compTarget.name==Cx16Target.NAME) {
|
||||
scanInvalid65816instructions(asmLines)
|
||||
if(!errors.noErrors()) {
|
||||
errors.report()
|
||||
return null
|
||||
}
|
||||
}
|
||||
if(options.optimize) {
|
||||
val separateLines = assemblyLines.flatMapTo(mutableListOf()) { it.split('\n') }
|
||||
assemblyLines.clear()
|
||||
while(optimizeAssembly(separateLines, options.compTarget.machine, symbolTable)>0) {
|
||||
while(optimizeAssembly(asmLines, options.compTarget.machine, symbolTable)>0) {
|
||||
// optimize the assembly source code
|
||||
}
|
||||
output.writeLines(separateLines)
|
||||
output.writeLines(asmLines)
|
||||
} else {
|
||||
output.writeLines(assemblyLines)
|
||||
// write the unmodified code
|
||||
output.writeLines(assembly)
|
||||
}
|
||||
return AssemblyProgram(program.name, options.outputDir, options.compTarget)
|
||||
} else {
|
||||
@@ -260,6 +268,22 @@ class AsmGen6502Internal (
|
||||
}
|
||||
}
|
||||
|
||||
private fun scanInvalid65816instructions(asmLines: MutableList<String>) {
|
||||
// The CommanderX16 ships with a WDC 65C02 CPU or a WDC 65816 CPU
|
||||
// The latter is compatible with the 65C02 except for 4 instructions: RMB, SMB, BBS, BBR.
|
||||
// We cannot set a different 6502 CPU target for the 64tass assembler, because we still need to support the STP and WAI instructions...
|
||||
// so we have to scan for these instructions ourselves.
|
||||
val invalid = Regex("""\s*((rmb\s|smb\s|bbs\s|bbr\s)|(rmb[0-7]|smb[0-7]|bbs[0-7]|bbr[0-7]))""", RegexOption.IGNORE_CASE)
|
||||
for((index, line) in asmLines.withIndex()) {
|
||||
if(line.length>=4 && invalid.matchesAt(line, 0)) {
|
||||
errors.err(
|
||||
"invalid assembly instruction used (not compatible with the 65816 CPU): ${line.trim()}",
|
||||
Position("<output-assemblycode>", index, 1, 1)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun isTargetCpu(cpu: CpuType) = options.compTarget.machine.cpu == cpu
|
||||
|
||||
private var lastSourceLineNumber: Int = -1
|
||||
@@ -283,9 +307,9 @@ class AsmGen6502Internal (
|
||||
for (line in fragment.splitToSequence('\n')) {
|
||||
val trimmed = if (line.startsWith(' ')) "\t" + line.trim() else line
|
||||
// trimmed = trimmed.replace(Regex("^\\+\\s+"), "+\t") // sanitize local label indentation
|
||||
assemblyLines.add(trimmed)
|
||||
assembly.add(trimmed)
|
||||
}
|
||||
} else assemblyLines.add(fragment)
|
||||
} else assembly.add(fragment)
|
||||
}
|
||||
|
||||
fun asmSymbolName(regs: RegisterOrPair): String =
|
||||
@@ -332,15 +356,20 @@ class AsmGen6502Internal (
|
||||
name
|
||||
}
|
||||
|
||||
|
||||
internal val tempVarsCounters = mutableMapOf(
|
||||
DataType.BYTE to 0,
|
||||
DataType.UBYTE to 0,
|
||||
DataType.WORD to 0,
|
||||
DataType.UWORD to 0,
|
||||
DataType.FLOAT to 0
|
||||
)
|
||||
|
||||
internal fun buildTempVarName(dt: DataType, counter: Int): String = "prog8_tmpvar_${dt.toString().lowercase()}_$counter"
|
||||
|
||||
internal fun getTempVarName(dt: DataType): String {
|
||||
return when(dt) {
|
||||
DataType.UBYTE -> "cx16.r9L"
|
||||
DataType.BYTE -> "cx16.r9sL"
|
||||
DataType.UWORD -> "cx16.r9"
|
||||
DataType.WORD -> "cx16.r9s"
|
||||
DataType.FLOAT -> "floats.floats_temp_var"
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
tempVarsCounters[dt] = tempVarsCounters.getValue(dt)+1
|
||||
return buildTempVarName(dt, tempVarsCounters.getValue(dt))
|
||||
}
|
||||
|
||||
internal fun loadByteFromPointerIntoA(pointervar: PtIdentifier): String {
|
||||
@@ -1056,7 +1085,7 @@ $repeatLabel""")
|
||||
if(asm.isIR)
|
||||
throw AssemblyError("%asm containing IR code cannot be translated to 6502 assembly")
|
||||
else
|
||||
assemblyLines.add(asm.assembly.trimEnd().trimStart('\r', '\n'))
|
||||
assembly.add(asm.assembly.trimEnd().trimStart('\r', '\n'))
|
||||
}
|
||||
|
||||
private fun translate(incbin: PtIncludeBinary) {
|
||||
@@ -1078,14 +1107,8 @@ $repeatLabel""")
|
||||
val label = "_prog8_breakpoint_${breakpointLabels.size+1}"
|
||||
breakpointLabels.add(label)
|
||||
out(label)
|
||||
if(options.breakpointCpuInstruction) {
|
||||
val instruction =
|
||||
when(options.compTarget.machine.cpu) {
|
||||
CpuType.CPU6502 -> "brk"
|
||||
CpuType.CPU65c02 -> "stp"
|
||||
else -> throw AssemblyError("invalid cpu type")
|
||||
}
|
||||
out(" $instruction")
|
||||
if(options.breakpointCpuInstruction!=null) {
|
||||
out(" ${options.breakpointCpuInstruction}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1169,7 +1192,7 @@ $repeatLabel""")
|
||||
return null
|
||||
}
|
||||
|
||||
internal fun tryOptimizedPointerAccessWithA(addressExpr: PtExpression, operator: String, write: Boolean): Boolean {
|
||||
internal fun tryOptimizedPointerAccessWithA(addressExpr: PtBinaryExpression, write: Boolean): Boolean {
|
||||
// optimize pointer,indexregister if possible
|
||||
|
||||
fun evalBytevalueWillClobberA(expr: PtExpression): Boolean {
|
||||
@@ -1185,7 +1208,7 @@ $repeatLabel""")
|
||||
}
|
||||
}
|
||||
|
||||
if(operator=="+") {
|
||||
if(addressExpr.operator=="+") {
|
||||
val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr)
|
||||
if(ptrAndIndex!=null) {
|
||||
val pointervar = ptrAndIndex.first as? PtIdentifier
|
||||
@@ -2896,7 +2919,7 @@ $repeatLabel""")
|
||||
}
|
||||
is PtBinaryExpression -> {
|
||||
val addrExpr = expr.address as PtBinaryExpression
|
||||
if(!tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
|
||||
if(!tryOptimizedPointerAccessWithA(addrExpr, false)) {
|
||||
assignViaExprEval()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -677,7 +677,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
is PtArrayIndexer -> {
|
||||
val indexer = fcall.args[0] as PtArrayIndexer
|
||||
require(!indexer.usesPointerVariable)
|
||||
val elementSize: Int
|
||||
val msbAdd: Int
|
||||
if(indexer.splitWords) {
|
||||
@@ -846,8 +845,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
}
|
||||
else -> {
|
||||
val tempvar = asmgen.getTempVarName(DataType.FLOAT)
|
||||
asmgen.assignExpressionTo(fcall.args[1],
|
||||
AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.FLOAT, fcall.definingISub(), fcall.position, tempvar, null, null, null, null))
|
||||
asmgen.assignExpressionToVariable(fcall.args[1], tempvar, DataType.FLOAT)
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
pha
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package prog8.codegen.cpu6502
|
||||
|
||||
import prog8.code.ast.PtIdentifier
|
||||
import prog8.code.ast.PtNumber
|
||||
import prog8.code.ast.PtPostIncrDecr
|
||||
import prog8.code.ast.PtProgram
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
@@ -37,6 +34,55 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
||||
}
|
||||
}
|
||||
targetMemory!=null -> {
|
||||
fun incDecViaExprEval() {
|
||||
asmgen.assignExpressionToRegister(targetMemory.address, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
}
|
||||
|
||||
fun tryOptimizedPointerIncDec(address: PtBinaryExpression): Boolean {
|
||||
if(address.operator=="+") {
|
||||
val offset = address.right.asConstInteger()
|
||||
if(offset!=null && offset<256) {
|
||||
// we have @(ptr + 255) ++ , or @(ptr+255)--
|
||||
asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY, false)
|
||||
asmgen.out("""
|
||||
sta (+) + 1
|
||||
sty (+) + 2
|
||||
ldx #$offset""")
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff,x\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff,x\t; modified")
|
||||
return true
|
||||
} else if(address.right.type in ByteDatatypes) {
|
||||
// we have @(ptr + bytevar) ++ , or @(ptr+bytevar)--
|
||||
asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY, false)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.assignExpressionToRegister(address.right, RegisterOrPair.X, false)
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff,x\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff,x\t; modified")
|
||||
return true
|
||||
} else if((address.right as? PtTypeCast)?.value?.type in ByteDatatypes) {
|
||||
// we have @(ptr + bytevar) ++ , or @(ptr+bytevar)--
|
||||
asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY, false)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
asmgen.assignExpressionToRegister((address.right as PtTypeCast).value, RegisterOrPair.X, false)
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff,x\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff,x\t; modified")
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
when (val addressExpr = targetMemory.address) {
|
||||
is PtNumber -> {
|
||||
val what = addressExpr.number.toHex()
|
||||
@@ -50,13 +96,13 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
}
|
||||
is PtBinaryExpression -> {
|
||||
if(!tryOptimizedPointerIncDec(addressExpr)) {
|
||||
incDecViaExprEval()
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
asmgen.assignExpressionToRegister(addressExpr, RegisterOrPair.AY)
|
||||
asmgen.out(" sta (+) + 1 | sty (+) + 2")
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
incDecViaExprEval()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,22 +137,7 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
||||
val indexValue = constIndex * program.memsizer.memorySize(elementDt)
|
||||
when(elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
if(targetArrayIdx.usesPointerVariable) {
|
||||
asmgen.out("""
|
||||
lda $asmArrayvarname
|
||||
clc
|
||||
adc #$indexValue
|
||||
sta (+) +1
|
||||
lda $asmArrayvarname+1
|
||||
adc #0
|
||||
sta (+) +2""")
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
} else {
|
||||
asmgen.out(if (incr) " inc $asmArrayvarname+$indexValue" else " dec $asmArrayvarname+$indexValue")
|
||||
}
|
||||
asmgen.out(if (incr) " inc $asmArrayvarname+$indexValue" else " dec $asmArrayvarname+$indexValue")
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
if(incr)
|
||||
@@ -130,22 +161,7 @@ internal class PostIncrDecrAsmGen(private val program: PtProgram, private val as
|
||||
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, CpuRegister.X)
|
||||
when(elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
if(targetArrayIdx.usesPointerVariable) {
|
||||
asmgen.out("""
|
||||
txa
|
||||
clc
|
||||
adc $asmArrayvarname
|
||||
sta (+) +1
|
||||
lda $asmArrayvarname+1
|
||||
adc #0
|
||||
sta (+) +2""")
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
} else {
|
||||
asmgen.out(if (incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x")
|
||||
}
|
||||
asmgen.out(if (incr) " inc $asmArrayvarname,x" else " dec $asmArrayvarname,x")
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
if(incr)
|
||||
|
||||
@@ -49,6 +49,7 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
|
||||
memorySlabs()
|
||||
tempVars()
|
||||
footer()
|
||||
}
|
||||
}
|
||||
@@ -165,6 +166,26 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
}
|
||||
|
||||
private fun tempVars() {
|
||||
asmgen.out("; expression temp vars\n .section BSS")
|
||||
for((dt, count) in asmgen.tempVarsCounters) {
|
||||
if(count>0) {
|
||||
for(num in 1..count) {
|
||||
val name = asmgen.buildTempVarName(dt, num)
|
||||
when (dt) {
|
||||
DataType.BYTE -> asmgen.out("$name .char ?")
|
||||
DataType.UBYTE -> asmgen.out("$name .byte ?")
|
||||
DataType.WORD -> asmgen.out("$name .sint ?")
|
||||
DataType.UWORD -> asmgen.out("$name .word ?")
|
||||
DataType.FLOAT -> asmgen.out("$name .fill ${options.compTarget.machine.FLOAT_MEM_SIZE}")
|
||||
else -> throw AssemblyError("weird dt for extravar $dt")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
asmgen.out(" .send BSS")
|
||||
}
|
||||
|
||||
private fun footer() {
|
||||
var relocateBssVars = false
|
||||
var relocateBssSlabs = false
|
||||
|
||||
@@ -64,32 +64,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
val value = assign.source.array!!
|
||||
val elementDt = assign.source.datatype
|
||||
val arrayVarName = asmgen.asmVariableName(value.variable)
|
||||
|
||||
if(value.usesPointerVariable) {
|
||||
if(elementDt !in ByteDatatypes)
|
||||
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||
if(value.type != DataType.UBYTE)
|
||||
throw AssemblyError("non-array var indexing requires bytes index")
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02) && (value.index as? PtNumber)?.number==0.0) {
|
||||
if (asmgen.isZpVar(value.variable)) {
|
||||
asmgen.out(" lda ($arrayVarName)")
|
||||
} else {
|
||||
asmgen.out(" lda $arrayVarName | sta P8ZP_SCRATCH_W1 | lda $arrayVarName+1 | sta P8ZP_SCRATCH_W1+1")
|
||||
asmgen.out(" lda (P8ZP_SCRATCH_W1)")
|
||||
}
|
||||
} else {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
|
||||
if (asmgen.isZpVar(value.variable)) {
|
||||
asmgen.out(" lda ($arrayVarName),y")
|
||||
} else {
|
||||
asmgen.out(" lda $arrayVarName | sta P8ZP_SCRATCH_W1 | lda $arrayVarName+1 | sta P8ZP_SCRATCH_W1+1")
|
||||
asmgen.out(" lda (P8ZP_SCRATCH_W1),y")
|
||||
}
|
||||
}
|
||||
assignRegisterByte(assign.target, CpuRegister.A, elementDt in SignedDatatypes, false)
|
||||
return
|
||||
}
|
||||
|
||||
val constIndex = value.index.asConstInteger()
|
||||
|
||||
if(value.splitWords) {
|
||||
@@ -170,7 +144,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
is PtBinaryExpression -> {
|
||||
val addrExpr = value.address as PtBinaryExpression
|
||||
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
|
||||
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, false)) {
|
||||
assignRegisterByte(assign.target, CpuRegister.A, false, true)
|
||||
} else {
|
||||
assignViaExprEval(value.address)
|
||||
@@ -798,34 +772,37 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
return true
|
||||
}
|
||||
else -> {
|
||||
val leftMemByte = expr.left as? PtMemoryByte
|
||||
val rightMemByte = expr.right as? PtMemoryByte
|
||||
val leftArrayIndexer = expr.left as? PtArrayIndexer
|
||||
val rightArrayIndexer = expr.right as? PtArrayIndexer
|
||||
if(rightArrayIndexer!=null && rightArrayIndexer.type in ByteDatatypes && left.type in ByteDatatypes) {
|
||||
// special optimization for bytevalue +/- bytearr[y] : no need to use a tempvar, just use adc array,y or sbc array,y or adc (ptr),y / sbc (ptr),y
|
||||
if(expr.operator=="+" && leftArrayIndexer!=null && leftArrayIndexer.type in ByteDatatypes && right.type in ByteDatatypes) {
|
||||
// special optimization for bytearray[y] + bytevalue : no need to use a tempvar, just use adc array,y
|
||||
assignExpressionToRegister(right, RegisterOrPair.A, right.type==DataType.BYTE)
|
||||
if(!leftArrayIndexer.index.isSimple()) asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(leftArrayIndexer.index, RegisterOrPair.Y, false)
|
||||
if(!leftArrayIndexer.index.isSimple()) asmgen.out(" pla")
|
||||
val arrayvarname = asmgen.asmSymbolName(leftArrayIndexer.variable)
|
||||
asmgen.out(" clc | adc $arrayvarname,y")
|
||||
assignRegisterByte(target, CpuRegister.A, dt in SignedDatatypes, true)
|
||||
} else if(rightArrayIndexer!=null && rightArrayIndexer.type in ByteDatatypes && left.type in ByteDatatypes) {
|
||||
// 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==DataType.BYTE)
|
||||
if(!rightArrayIndexer.index.isSimple()) asmgen.out(" pha")
|
||||
asmgen.assignExpressionToRegister(rightArrayIndexer.index, RegisterOrPair.Y, false)
|
||||
if(!rightArrayIndexer.index.isSimple()) asmgen.out(" pla")
|
||||
if(rightArrayIndexer.usesPointerVariable && !asmgen.isZpVar(rightArrayIndexer.variable)) {
|
||||
asmgen.out("""
|
||||
ldx ${rightArrayIndexer.variable.name}
|
||||
stx P8ZP_SCRATCH_W1
|
||||
ldx ${rightArrayIndexer.variable.name}+1
|
||||
stx P8ZP_SCRATCH_W1+1""")
|
||||
if (expr.operator == "+")
|
||||
asmgen.out(" clc | adc (P8ZP_SCRATCH_W1),y")
|
||||
else
|
||||
asmgen.out(" sec | sbc (P8ZP_SCRATCH_W1),y")
|
||||
} else {
|
||||
val arrayvarname = if (rightArrayIndexer.usesPointerVariable)
|
||||
"(${rightArrayIndexer.variable.name})"
|
||||
else
|
||||
asmgen.asmSymbolName(rightArrayIndexer.variable)
|
||||
if (expr.operator == "+")
|
||||
asmgen.out(" clc | adc $arrayvarname,y")
|
||||
else
|
||||
asmgen.out(" sec | sbc $arrayvarname,y")
|
||||
}
|
||||
val arrayvarname = asmgen.asmSymbolName(rightArrayIndexer.variable)
|
||||
if (expr.operator == "+")
|
||||
asmgen.out(" clc | adc $arrayvarname,y")
|
||||
else
|
||||
asmgen.out(" sec | sbc $arrayvarname,y")
|
||||
assignRegisterByte(target, CpuRegister.A, dt in SignedDatatypes, true)
|
||||
} else if(expr.operator=="+" && leftMemByte!=null && right.type in ByteDatatypes && optimizedPointerIndexPlusMinusByteIntoA(right, "+", leftMemByte)) {
|
||||
assignRegisterByte(target, CpuRegister.A, dt in SignedDatatypes, true)
|
||||
return true
|
||||
} else if(rightMemByte!=null && left.type in ByteDatatypes && optimizedPointerIndexPlusMinusByteIntoA(left, expr.operator, rightMemByte)) {
|
||||
assignRegisterByte(target, CpuRegister.A, dt in SignedDatatypes, true)
|
||||
return true
|
||||
} else {
|
||||
assignExpressionToRegister(left, RegisterOrPair.A, left.type==DataType.BYTE)
|
||||
if(directIntoY(right)) {
|
||||
@@ -1010,6 +987,47 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
return false
|
||||
}
|
||||
|
||||
private fun optimizedPointerIndexPlusMinusByteIntoA(value: PtExpression, operator: String, mem: PtMemoryByte): Boolean {
|
||||
// special optimization for bytevalue +/- pointervar[y] (actually: bytevalue +/- @(address) )
|
||||
val address = mem.address as? PtBinaryExpression
|
||||
if(address is PtBinaryExpression) {
|
||||
val ptrVar = address.left as? PtIdentifier
|
||||
if(ptrVar!=null && asmgen.isZpVar(ptrVar)) {
|
||||
assignExpressionToRegister(value, RegisterOrPair.A, false)
|
||||
val pointername = asmgen.asmVariableName(ptrVar)
|
||||
val constOffset = address.right.asConstInteger()
|
||||
if (constOffset != null && constOffset < 256) {
|
||||
// we have value + @(zpptr + 255), or value - @(zpptr+255)
|
||||
asmgen.out(" ldy #$constOffset")
|
||||
if (operator == "+")
|
||||
asmgen.out(" clc | adc ($pointername),y")
|
||||
else
|
||||
asmgen.out(" sec | sbc ($pointername),y")
|
||||
} else if (address.right.type in ByteDatatypes) {
|
||||
// we have @(ptr + bytevar) ++ , or @(ptr+bytevar)--
|
||||
asmgen.out(" pha")
|
||||
assignExpressionToRegister(address.right, RegisterOrPair.Y, false)
|
||||
asmgen.out(" pla")
|
||||
if (operator == "+")
|
||||
asmgen.out(" clc | adc ($pointername),y")
|
||||
else
|
||||
asmgen.out(" sec | sbc ($pointername),y")
|
||||
} else if ((address.right as? PtTypeCast)?.value?.type in ByteDatatypes) {
|
||||
// we have @(ptr + bytevar as uword) ++ , or @(ptr+bytevar as uword)--
|
||||
asmgen.out(" pha")
|
||||
assignExpressionToRegister((address.right as PtTypeCast).value, RegisterOrPair.Y, false)
|
||||
asmgen.out(" pla")
|
||||
if (operator == "+")
|
||||
asmgen.out(" clc | adc ($pointername),y")
|
||||
else
|
||||
asmgen.out(" sec | sbc ($pointername),y")
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun optimizedBitwiseExpr(expr: PtBinaryExpression, target: AsmAssignTarget): Boolean {
|
||||
if (expr.left.type in ByteDatatypes && expr.right.type in ByteDatatypes) {
|
||||
if (expr.right.isSimple()) {
|
||||
@@ -2034,7 +2052,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
is PtBinaryExpression -> {
|
||||
val addrExpr = value.address as PtBinaryExpression
|
||||
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, addrExpr.operator, false)) {
|
||||
if(asmgen.tryOptimizedPointerAccessWithA(addrExpr, false)) {
|
||||
asmgen.out(" ldy #0")
|
||||
assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||
} else {
|
||||
@@ -2836,21 +2854,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
storeRegisterAInMemoryAddress(target.memory!!)
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
if(assignsIndexedPointerVar(target)) {
|
||||
if (target.constArrayIndexValue==0u) {
|
||||
asmgen.out(" lda $sourceName")
|
||||
asmgen.storeAIntoPointerVar(target.origAstTarget!!.array!!.variable)
|
||||
} else {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, CpuRegister.Y)
|
||||
if (asmgen.isZpVar(target.origAstTarget!!.array!!.variable)) {
|
||||
asmgen.out(" lda $sourceName | sta (${target.asmVarname}),y")
|
||||
} else {
|
||||
asmgen.out(" lda ${target.asmVarname} | sta P8ZP_SCRATCH_W2 | lda ${target.asmVarname}+1 | sta P8ZP_SCRATCH_W2+1")
|
||||
asmgen.out(" lda $sourceName | sta (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if(target.array!!.splitWords)
|
||||
TODO("assign into split words ${target.position}")
|
||||
if (target.constArrayIndexValue!=null) {
|
||||
@@ -3256,87 +3259,29 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
if(target.array!!.splitWords)
|
||||
throw AssemblyError("cannot assign byte to split word array here ${target.position}")
|
||||
|
||||
if(assignsIndexedPointerVar(target)) {
|
||||
// assign byte to a byte array via an uword pointervariable instead of a regular array variable
|
||||
if (target.constArrayIndexValue!=null) {
|
||||
when (register) {
|
||||
CpuRegister.A -> {}
|
||||
CpuRegister.X -> asmgen.out(" txa")
|
||||
CpuRegister.Y -> asmgen.out(" tya")
|
||||
}
|
||||
if(asmgen.isZpVar(target.origAstTarget!!.array!!.variable)) {
|
||||
asmgen.out(" ldy #${target.constArrayIndexValue} | sta (${target.asmVarname}),y")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
ldy ${target.asmVarname}
|
||||
sty P8ZP_SCRATCH_W1
|
||||
ldy ${target.asmVarname}+1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #${target.constArrayIndexValue}
|
||||
sta (P8ZP_SCRATCH_W1),y""")
|
||||
}
|
||||
// assign regular array indexing
|
||||
if (target.constArrayIndexValue!=null) {
|
||||
when (register) {
|
||||
CpuRegister.A -> {}
|
||||
CpuRegister.X -> asmgen.out(" txa")
|
||||
CpuRegister.Y -> asmgen.out(" tya")
|
||||
}
|
||||
else {
|
||||
when (register) {
|
||||
CpuRegister.A -> {}
|
||||
CpuRegister.X -> asmgen.out(" txa")
|
||||
CpuRegister.Y -> asmgen.out(" tya")
|
||||
}
|
||||
val indexVar = target.array.index as? PtIdentifier
|
||||
if(indexVar!=null) {
|
||||
if(asmgen.isZpVar(target.origAstTarget!!.array!!.variable)) {
|
||||
asmgen.out(" ldy ${asmgen.asmVariableName(indexVar)} | sta (${target.asmVarname}),y")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
ldy ${target.asmVarname}
|
||||
sty P8ZP_SCRATCH_W1
|
||||
ldy ${target.asmVarname}+1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy ${asmgen.asmVariableName(indexVar)}
|
||||
sta (P8ZP_SCRATCH_W1),y""")
|
||||
}
|
||||
} else {
|
||||
asmgen.saveRegisterStack(register, false)
|
||||
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.Y, false)
|
||||
asmgen.out(" pla")
|
||||
if(asmgen.isZpVar(target.origAstTarget!!.array!!.variable)) {
|
||||
asmgen.out(" sta (${target.asmVarname}),y")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
ldx ${target.asmVarname}
|
||||
stx P8ZP_SCRATCH_W1
|
||||
ldx ${target.asmVarname}+1
|
||||
stx P8ZP_SCRATCH_W1+1
|
||||
sta (P8ZP_SCRATCH_W1),y""")
|
||||
}
|
||||
}
|
||||
asmgen.out(" sta ${target.asmVarname}+${target.constArrayIndexValue}")
|
||||
}
|
||||
else {
|
||||
when (register) {
|
||||
CpuRegister.A -> {}
|
||||
CpuRegister.X -> asmgen.out(" txa")
|
||||
CpuRegister.Y -> asmgen.out(" tya")
|
||||
}
|
||||
return
|
||||
} else {
|
||||
// assign regular array indexing
|
||||
if (target.constArrayIndexValue!=null) {
|
||||
when (register) {
|
||||
CpuRegister.A -> {}
|
||||
CpuRegister.X -> asmgen.out(" txa")
|
||||
CpuRegister.Y -> asmgen.out(" tya")
|
||||
}
|
||||
asmgen.out(" sta ${target.asmVarname}+${target.constArrayIndexValue}")
|
||||
}
|
||||
else {
|
||||
when (register) {
|
||||
CpuRegister.A -> {}
|
||||
CpuRegister.X -> asmgen.out(" txa")
|
||||
CpuRegister.Y -> asmgen.out(" tya")
|
||||
}
|
||||
val indexVar = target.array.index as? PtIdentifier
|
||||
if(indexVar!=null) {
|
||||
asmgen.out(" ldy ${asmgen.asmVariableName(indexVar)} | sta ${target.asmVarname},y")
|
||||
} else {
|
||||
require(target.array.index.type in ByteDatatypes)
|
||||
asmgen.saveRegisterStack(register, false)
|
||||
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.Y, false)
|
||||
asmgen.out(" pla | sta ${target.asmVarname},y")
|
||||
}
|
||||
val indexVar = target.array.index as? PtIdentifier
|
||||
if(indexVar!=null) {
|
||||
asmgen.out(" ldy ${asmgen.asmVariableName(indexVar)} | sta ${target.asmVarname},y")
|
||||
} else {
|
||||
require(target.array.index.type in ByteDatatypes)
|
||||
asmgen.saveRegisterStack(register, false)
|
||||
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.Y, false)
|
||||
asmgen.out(" pla | sta ${target.asmVarname},y")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3632,21 +3577,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
storeRegisterAInMemoryAddress(target.memory!!)
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
if(assignsIndexedPointerVar(target)) {
|
||||
if (target.constArrayIndexValue==0u) {
|
||||
asmgen.out(" lda #0")
|
||||
asmgen.storeAIntoPointerVar(target.origAstTarget!!.array!!.variable)
|
||||
} else {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, CpuRegister.Y)
|
||||
if (asmgen.isZpVar(target.origAstTarget!!.array!!.variable)) {
|
||||
asmgen.out(" lda #0 | sta (${target.asmVarname}),y")
|
||||
} else {
|
||||
asmgen.out(" lda ${target.asmVarname} | sta P8ZP_SCRATCH_W2 | lda ${target.asmVarname}+1 | sta P8ZP_SCRATCH_W2+1")
|
||||
asmgen.out(" lda #0 | sta (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if(target.array!!.splitWords)
|
||||
throw AssemblyError("cannot assign byte to split word array here ${target.position}")
|
||||
if (target.constArrayIndexValue!=null) {
|
||||
@@ -3686,21 +3616,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
storeRegisterAInMemoryAddress(target.memory!!)
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
if(assignsIndexedPointerVar(target)) {
|
||||
if (target.constArrayIndexValue==0u) {
|
||||
asmgen.out(" lda #${byte.toHex()}")
|
||||
asmgen.storeAIntoPointerVar(target.origAstTarget!!.array!!.variable)
|
||||
} else {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, CpuRegister.Y)
|
||||
if (asmgen.isZpVar(target.origAstTarget!!.array!!.variable)) {
|
||||
asmgen.out(" lda #${byte.toHex()} | sta (${target.asmVarname}),y")
|
||||
} else {
|
||||
asmgen.out(" lda ${target.asmVarname} | sta P8ZP_SCRATCH_W2 | lda ${target.asmVarname}+1 | sta P8ZP_SCRATCH_W2+1")
|
||||
asmgen.out(" lda #${byte.toHex()} | sta (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if(target.array!!.splitWords)
|
||||
TODO("assign into split words ${target.position}")
|
||||
if (target.constArrayIndexValue!=null) {
|
||||
@@ -3732,9 +3647,6 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignsIndexedPointerVar(target: AsmAssignTarget): Boolean =
|
||||
target.origAstTarget?.array?.variable?.type==DataType.UWORD
|
||||
|
||||
internal fun assignConstantFloat(target: AsmAssignTarget, float: Double) {
|
||||
if (float == 0.0) {
|
||||
// optimized case for float zero
|
||||
@@ -4007,7 +3919,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, addressExpr.operator, true))
|
||||
if(!asmgen.tryOptimizedPointerAccessWithA(addressExpr, true))
|
||||
storeViaExprEval()
|
||||
}
|
||||
else -> storeViaExprEval()
|
||||
@@ -4061,9 +3973,9 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
sta $addr""")
|
||||
}
|
||||
is PtIdentifier -> {
|
||||
val sourceName = asmgen.loadByteFromPointerIntoA(memory.address as PtIdentifier)
|
||||
asmgen.loadByteFromPointerIntoA(memory.address as PtIdentifier)
|
||||
asmgen.out(" eor #255")
|
||||
asmgen.out(" sta ($sourceName),y")
|
||||
asmgen.storeAIntoPointerVar(memory.address as PtIdentifier)
|
||||
}
|
||||
else -> {
|
||||
asmgen.assignExpressionToVariable(memory.address, "P8ZP_SCRATCH_W2", DataType.UWORD)
|
||||
|
||||
+162
-17
@@ -163,7 +163,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
asmgen.assignExpressionTo(memory.address, AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UWORD, memory.definingISub(), target.position, register = RegisterOrPair.AY))
|
||||
if(memory.address is PtBinaryExpression && tryOptimizedMemoryInplace(memory.address as PtBinaryExpression, operator, value))
|
||||
return
|
||||
asmgen.assignExpressionToRegister(memory.address, RegisterOrPair.AY, false)
|
||||
asmgen.saveRegisterStack(CpuRegister.A, true)
|
||||
asmgen.saveRegisterStack(CpuRegister.Y, true)
|
||||
asmgen.out(" jsr prog8_lib.read_byte_from_address_in_AY_into_A")
|
||||
@@ -191,12 +193,13 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" ldx P8ZP_SCRATCH_B1")
|
||||
}
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
asmgen.out(" sta P8ZP_SCRATCH_B1")
|
||||
val tempVar = asmgen.getTempVarName(DataType.UBYTE)
|
||||
asmgen.out(" sta $tempVar")
|
||||
if(value.expression is PtTypeCast)
|
||||
inplacemodificationByteVariableWithValue("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.expression)
|
||||
inplacemodificationByteVariableWithValue(tempVar, DataType.UBYTE, operator, value.expression)
|
||||
else
|
||||
inplacemodificationByteVariableWithValue("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value.expression!!)
|
||||
asmgen.out(" ldx P8ZP_SCRATCH_B1")
|
||||
inplacemodificationByteVariableWithValue(tempVar, DataType.UBYTE, operator, value.expression!!)
|
||||
asmgen.out(" ldx $tempVar")
|
||||
}
|
||||
}
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
||||
@@ -208,10 +211,23 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
TargetStorageKind.ARRAY -> {
|
||||
val indexNum = target.array!!.index as? PtNumber
|
||||
if (indexNum!=null) {
|
||||
val targetVarName = if(target.array.splitWords)
|
||||
"${target.asmVarname} + ${indexNum.number.toInt()}"
|
||||
else
|
||||
"${target.asmVarname} + ${indexNum.number.toInt()*program.memsizer.memorySize(target.datatype)}"
|
||||
val index = indexNum.number.toInt()
|
||||
if(target.array.splitWords) {
|
||||
when(value.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationSplitWordWithLiteralval(target.asmVarname, index, operator, value.number!!.number.toInt())
|
||||
else -> {
|
||||
// TODO: more optimized code for VARIABLE, REGISTER, MEMORY, ARRAY, EXPRESSION in the case of split-word arrays
|
||||
val scope = target.origAstTarget?.definingSub()
|
||||
val regTarget = AsmAssignTarget.fromRegisters(RegisterOrPair.R0, false, target.position, scope, asmgen)
|
||||
val assignToReg = AsmAssignment(value, regTarget, program.memsizer, target.position)
|
||||
assignmentAsmGen.translateNormalAssignment(assignToReg, scope)
|
||||
inplacemodificationSplitWordWithR0(target.asmVarname, index, operator)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
// normal array
|
||||
val targetVarName = "${target.asmVarname} + ${index*program.memsizer.memorySize(target.datatype)}"
|
||||
when (target.datatype) {
|
||||
in ByteDatatypes -> {
|
||||
when(value.kind) {
|
||||
@@ -312,13 +328,14 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
asmgen.out(" sta P8ZP_SCRATCH_B1")
|
||||
val tempVar = asmgen.getTempVarName(DataType.UBYTE)
|
||||
asmgen.out(" sta $tempVar")
|
||||
if(value.expression is PtTypeCast)
|
||||
inplacemodificationByteVariableWithValue("P8ZP_SCRATCH_B1", target.datatype, operator, value.expression)
|
||||
inplacemodificationByteVariableWithValue(tempVar, target.datatype, operator, value.expression)
|
||||
else
|
||||
inplacemodificationByteVariableWithValue("P8ZP_SCRATCH_B1", target.datatype, operator, value.expression!!)
|
||||
inplacemodificationByteVariableWithValue(tempVar, target.datatype, operator, value.expression!!)
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, false)
|
||||
asmgen.out(" lda P8ZP_SCRATCH_B1")
|
||||
asmgen.out(" lda $tempVar")
|
||||
}
|
||||
}
|
||||
asmgen.out(" sta ${target.array.variable.name},y")
|
||||
@@ -386,12 +403,13 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
SourceStorageKind.EXPRESSION -> {
|
||||
asmgen.out(" sta P8ZP_SCRATCH_W1 | stx P8ZP_SCRATCH_W1+1")
|
||||
val tempVar = asmgen.getTempVarName(DataType.UWORD)
|
||||
asmgen.out(" sta $tempVar | stx $tempVar+1")
|
||||
if(value.expression is PtTypeCast)
|
||||
inplacemodificationWordWithValue("P8ZP_SCRATCH_W1", target.datatype, operator, value.expression, block)
|
||||
inplacemodificationWordWithValue(tempVar, target.datatype, operator, value.expression, block)
|
||||
else
|
||||
inplacemodificationWordWithValue("P8ZP_SCRATCH_W1", target.datatype, operator, value.expression!!, block)
|
||||
asmgen.out(" lda P8ZP_SCRATCH_W1 | ldx P8ZP_SCRATCH_W1+1")
|
||||
inplacemodificationWordWithValue(tempVar, target.datatype, operator, value.expression!!, block)
|
||||
asmgen.out(" lda $tempVar | ldx $tempVar+1")
|
||||
}
|
||||
}
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, true)
|
||||
@@ -458,6 +476,133 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryOptimizedMemoryInplace(address: PtBinaryExpression, operator: String, value: AsmAssignSource): Boolean {
|
||||
if(value.datatype !in ByteDatatypes || operator !in "|&^+-")
|
||||
return false
|
||||
val rightTc = address.right as? PtTypeCast
|
||||
val constOffset = (address.right as? PtNumber)?.number?.toInt()
|
||||
if(address.operator=="+" && (address.right.type in ByteDatatypes || (rightTc!=null && rightTc.value.type in ByteDatatypes) || (constOffset!=null && constOffset<256)) ) {
|
||||
if(rightTc!=null)
|
||||
asmgen.assignExpressionToRegister(rightTc.value, RegisterOrPair.A, false)
|
||||
else if(constOffset!=null)
|
||||
asmgen.out(" lda #${constOffset}")
|
||||
else
|
||||
asmgen.assignExpressionToRegister(address.right, RegisterOrPair.A, false)
|
||||
asmgen.out(" pha") // offset on stack
|
||||
val zpPointerVarName: String
|
||||
if(address.left is PtIdentifier && asmgen.isZpVar(address.left as PtIdentifier)) {
|
||||
zpPointerVarName = (address.left as PtIdentifier).name
|
||||
} else {
|
||||
zpPointerVarName = "P8ZP_SCRATCH_W2"
|
||||
asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY, false)
|
||||
asmgen.out(" sta $zpPointerVarName | sty $zpPointerVarName+1")
|
||||
}
|
||||
// calculate value into A
|
||||
val assignValue = AsmAssignment(value,
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.UBYTE,
|
||||
address.definingISub(), Position.DUMMY, register = RegisterOrPair.A),
|
||||
program.memsizer, Position.DUMMY)
|
||||
assignmentAsmGen.translateNormalAssignment(assignValue, address.definingISub())
|
||||
asmgen.restoreRegisterStack(CpuRegister.Y, false) // offset into Y
|
||||
when(operator) {
|
||||
"|" -> asmgen.out(" ora ($zpPointerVarName),y")
|
||||
"&" -> asmgen.out(" and ($zpPointerVarName),y")
|
||||
"^" -> asmgen.out(" eor ($zpPointerVarName),y")
|
||||
"+" -> asmgen.out(" clc | adc ($zpPointerVarName),y")
|
||||
"-" -> asmgen.out(" sta P8ZP_SCRATCH_REG | lda ($zpPointerVarName),y | sec | sbc P8ZP_SCRATCH_REG")
|
||||
else -> throw AssemblyError("invalid op")
|
||||
}
|
||||
asmgen.out(" sta ($zpPointerVarName),y")
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun inplacemodificationSplitWordWithR0(arrayVar: String, index: Int, operator: String) {
|
||||
when (operator) {
|
||||
"+" -> {
|
||||
asmgen.out("""
|
||||
lda ${arrayVar}_lsb+$index
|
||||
clc
|
||||
adc cx16.r0L
|
||||
sta ${arrayVar}_lsb+$index
|
||||
lda ${arrayVar}_msb+$index
|
||||
adc cx16.r0H
|
||||
sta ${arrayVar}_msb+$index""")
|
||||
}
|
||||
"-" -> {
|
||||
asmgen.out("""
|
||||
lda ${arrayVar}_lsb+$index
|
||||
sec
|
||||
sbc cx16.r0L
|
||||
sta ${arrayVar}_lsb+$index
|
||||
lda ${arrayVar}_msb+$index
|
||||
sbc cx16.r0H
|
||||
sta ${arrayVar}_msb+$index""")
|
||||
}
|
||||
else -> TODO("in-place modify split-words array value for operator $operator")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplacemodificationSplitWordWithLiteralval(arrayVar: String, index: Int, operator: String, value: Int) {
|
||||
// note: this contains special optimized cases because we know the exact value. Don't replace this with another routine.
|
||||
when (operator) {
|
||||
"+" -> {
|
||||
when {
|
||||
value==0 -> {}
|
||||
value in 1..0xff -> asmgen.out("""
|
||||
lda ${arrayVar}_lsb+$index
|
||||
clc
|
||||
adc #$value
|
||||
sta ${arrayVar}_lsb+$index
|
||||
bcc +
|
||||
inc ${arrayVar}_msb+$index
|
||||
+""")
|
||||
value==0x0100 -> asmgen.out(" inc ${arrayVar}_msb+$index")
|
||||
value==0x0200 -> asmgen.out(" inc ${arrayVar}_msb+$index | inc ${arrayVar}_msb+$index")
|
||||
value==0x0300 -> asmgen.out(" inc ${arrayVar}_msb+$index | inc ${arrayVar}_msb+$index | inc ${arrayVar}_msb+$index")
|
||||
value==0x0400 -> asmgen.out(" inc ${arrayVar}_msb+$index | inc ${arrayVar}_msb+$index | inc ${arrayVar}_msb+$index | inc ${arrayVar}_msb+$index")
|
||||
value and 255==0 -> asmgen.out(" lda ${arrayVar}_msb+$index | clc | adc #>$value | sta ${arrayVar}_msb+$index")
|
||||
else -> asmgen.out("""
|
||||
lda ${arrayVar}_lsb+$index
|
||||
clc
|
||||
adc #<$value
|
||||
sta ${arrayVar}_lsb+$index
|
||||
lda ${arrayVar}_msb+$index
|
||||
adc #>$value
|
||||
sta ${arrayVar}_msb+$index""")
|
||||
}
|
||||
}
|
||||
"-" -> {
|
||||
when {
|
||||
value==0 -> {}
|
||||
value in 1..0xff -> asmgen.out("""
|
||||
lda ${arrayVar}_lsb+$index
|
||||
sec
|
||||
sbc #$value
|
||||
sta ${arrayVar}_lsb+$index
|
||||
bcs +
|
||||
dec ${arrayVar}_msb+$index
|
||||
+""")
|
||||
value==0x0100 -> asmgen.out(" dec ${arrayVar}_msb+$index")
|
||||
value==0x0200 -> asmgen.out(" dec ${arrayVar}_msb+$index | dec ${arrayVar}_msb+$index")
|
||||
value==0x0300 -> asmgen.out(" dec ${arrayVar}_msb+$index | dec ${arrayVar}_msb+$index | dec ${arrayVar}_msb+$index")
|
||||
value==0x0400 -> asmgen.out(" dec ${arrayVar}_msb+$index | dec ${arrayVar}_msb+$index | dec ${arrayVar}_msb+$index | dec ${arrayVar}_msb+$index")
|
||||
value and 255==0 -> asmgen.out(" lda ${arrayVar}_msb+$index | sec | sbc #>$value | sta ${arrayVar}_msb+$index")
|
||||
else -> asmgen.out("""
|
||||
lda ${arrayVar}_lsb+$index
|
||||
sec
|
||||
sbc #<$value
|
||||
sta ${arrayVar}_lsb+$index
|
||||
lda ${arrayVar}_msb+$index
|
||||
sbc #>$value
|
||||
sta ${arrayVar}_msb+$index""")
|
||||
}
|
||||
}
|
||||
else -> TODO("in-place modify split-words array value for operator $operator")
|
||||
}
|
||||
}
|
||||
|
||||
private fun inplacemodificationRegisterAXwithVariable(operator: String, variable: String, varDt: DataType): Boolean {
|
||||
when(operator) {
|
||||
"+" -> {
|
||||
|
||||
@@ -14,5 +14,6 @@
|
||||
<orderEntry type="module" module-name="intermediate" />
|
||||
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -1,12 +1,13 @@
|
||||
package prog8.codegen.intermediate
|
||||
|
||||
import com.github.michaelbull.result.Ok
|
||||
import com.github.michaelbull.result.Result
|
||||
import com.github.michaelbull.result.getOrElse
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.AssemblyError
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.PrefixOperators
|
||||
import prog8.code.core.SignedDatatypes
|
||||
import prog8.code.core.*
|
||||
import prog8.intermediate.*
|
||||
|
||||
|
||||
internal class AssignmentGen(private val codeGen: IRCodeGen, private val expressionEval: ExpressionGen) {
|
||||
|
||||
internal fun translate(assignment: PtAssignment): IRCodeChunks {
|
||||
@@ -22,95 +23,57 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
if(augAssign.target.children.single() is PtMachineRegister)
|
||||
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
|
||||
|
||||
val ident = augAssign.target.identifier
|
||||
val memory = augAssign.target.memory
|
||||
val array = augAssign.target.array
|
||||
val target = augAssign.target
|
||||
val targetDt = irType(target.type)
|
||||
val memTarget = target.memory
|
||||
val constAddress = (memTarget?.address as? PtNumber)?.number?.toInt()
|
||||
val symbol = target.identifier?.name
|
||||
val array = target.array
|
||||
val value = augAssign.value
|
||||
val signed = target.type in SignedDatatypes
|
||||
val result: Result<IRCodeChunks, NotImplementedError> = when(augAssign.operator) {
|
||||
"+=" -> expressionEval.operatorPlusInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"-=" -> expressionEval.operatorMinusInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"*=" -> expressionEval.operatorMultiplyInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"/=" -> expressionEval.operatorDivideInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
"|=" -> expressionEval.operatorOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"or=" -> expressionEval.operatorLogicalOrInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"&=" -> expressionEval.operatorAndInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"and=" -> expressionEval.operatorLogicalAndInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"^=", "xor=" -> expressionEval.operatorXorInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"<<=" -> expressionEval.operatorShiftLeftInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
">>=" -> expressionEval.operatorShiftRightInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
"%=" -> expressionEval.operatorModuloInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"==" -> expressionEval.operatorEqualsInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"!=" -> expressionEval.operatorNotEqualsInplace(symbol, array, constAddress, memTarget, targetDt, value)
|
||||
"<" -> expressionEval.operatorLessInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
">" -> expressionEval.operatorGreaterInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
"<=" -> expressionEval.operatorLessEqualInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
">=" -> expressionEval.operatorGreaterEqualInplace(symbol, array, constAddress, memTarget, targetDt, value, signed)
|
||||
in PrefixOperators -> inplacePrefix(augAssign.operator, symbol, array, constAddress, memTarget, targetDt)
|
||||
|
||||
val chunks = if(ident!=null) {
|
||||
assignVarAugmented(ident.name, augAssign)
|
||||
} else if(memory != null) {
|
||||
if(memory.address is PtNumber)
|
||||
assignMemoryAugmented((memory.address as PtNumber).number.toInt(), augAssign)
|
||||
else
|
||||
fallbackAssign(augAssign)
|
||||
} else if(array!=null) {
|
||||
// NOTE: naive fallback assignment here will sometimes generate code that loads the index value multiple times
|
||||
// in a register. It's way too much work to optimize that here - instead, we trust that the generated IL assembly
|
||||
// will be optimized later and have the double assignments removed.
|
||||
fallbackAssign(augAssign)
|
||||
} else {
|
||||
fallbackAssign(augAssign)
|
||||
else -> throw AssemblyError("invalid augmented assign operator ${augAssign.operator}")
|
||||
}
|
||||
|
||||
val chunks = result.getOrElse { fallbackAssign(augAssign) }
|
||||
chunks.filterIsInstance<IRCodeChunk>().firstOrNull()?.appendSrcPosition(augAssign.position)
|
||||
return chunks
|
||||
}
|
||||
|
||||
private fun assignMemoryAugmented(
|
||||
address: Int,
|
||||
assignment: PtAugmentedAssign
|
||||
): IRCodeChunks {
|
||||
val value = assignment.value
|
||||
val targetDt = irType(assignment.target.type)
|
||||
val signed = assignment.target.type in SignedDatatypes
|
||||
return when(assignment.operator) {
|
||||
"+=" -> expressionEval.operatorPlusInplace(address, null, targetDt, value)
|
||||
"-=" -> expressionEval.operatorMinusInplace(address, null, targetDt, value)
|
||||
"*=" -> expressionEval.operatorMultiplyInplace(address, null, targetDt, value)
|
||||
"/=" -> expressionEval.operatorDivideInplace(address, null, targetDt, signed, value)
|
||||
"|=" -> expressionEval.operatorOrInplace(address, null, targetDt, value)
|
||||
"&=" -> expressionEval.operatorAndInplace(address, null, targetDt, value)
|
||||
"^=" -> expressionEval.operatorXorInplace(address, null, targetDt, value)
|
||||
"<<=" -> expressionEval.operatorShiftLeftInplace(address, null, targetDt, value)
|
||||
">>=" -> expressionEval.operatorShiftRightInplace(address, null, targetDt, signed, value)
|
||||
"%=" -> expressionEval.operatorModuloInplace(address, null, targetDt, value)
|
||||
"==" -> expressionEval.operatorEqualsInplace(address, null, targetDt, value)
|
||||
"!=" -> expressionEval.operatorNotEqualsInplace(address, null, targetDt, value)
|
||||
"<" -> expressionEval.operatorLessInplace(address, null, targetDt, signed, value)
|
||||
">" -> expressionEval.operatorGreaterInplace(address, null, targetDt, signed, value)
|
||||
"<=" -> expressionEval.operatorLessEqualInplace(address, null, targetDt, signed, value)
|
||||
">=" -> expressionEval.operatorGreaterEqualInplace(address, null, targetDt, signed, value)
|
||||
in PrefixOperators -> inplacePrefix(assignment.operator, targetDt, address, null)
|
||||
|
||||
else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignVarAugmented(symbol: String, assignment: PtAugmentedAssign): IRCodeChunks {
|
||||
val value = assignment.value
|
||||
val signed = assignment.target.type in SignedDatatypes
|
||||
val targetDt = irType(assignment.target.type)
|
||||
return when(assignment.operator) {
|
||||
"+=" -> expressionEval.operatorPlusInplace(null, symbol, targetDt, value)
|
||||
"-=" -> expressionEval.operatorMinusInplace(null, symbol, targetDt, value)
|
||||
"*=" -> expressionEval.operatorMultiplyInplace(null, symbol, targetDt, value)
|
||||
"/=" -> expressionEval.operatorDivideInplace(null, symbol, targetDt, signed, value)
|
||||
"|=" -> expressionEval.operatorOrInplace(null, symbol, targetDt, value)
|
||||
"or=" -> expressionEval.operatorLogicalOrInplace(null, symbol, targetDt, value)
|
||||
"&=" -> expressionEval.operatorAndInplace(null, symbol, targetDt, value)
|
||||
"and=" -> expressionEval.operatorLogicalAndInplace(null, symbol, targetDt, value)
|
||||
"^=", "xor=" -> expressionEval.operatorXorInplace(null, symbol, targetDt, value)
|
||||
"<<=" -> expressionEval.operatorShiftLeftInplace(null, symbol, targetDt, value)
|
||||
">>=" -> expressionEval.operatorShiftRightInplace(null, symbol, targetDt, signed, value)
|
||||
"%=" -> expressionEval.operatorModuloInplace(null, symbol, targetDt, value)
|
||||
"==" -> expressionEval.operatorEqualsInplace(null, symbol, targetDt, value)
|
||||
"!=" -> expressionEval.operatorNotEqualsInplace(null, symbol, targetDt, value)
|
||||
"<" -> expressionEval.operatorLessInplace(null, symbol, targetDt, signed, value)
|
||||
">" -> expressionEval.operatorGreaterInplace(null, symbol, targetDt, signed, value)
|
||||
"<=" -> expressionEval.operatorLessEqualInplace(null, symbol, targetDt, signed, value)
|
||||
">=" -> expressionEval.operatorGreaterEqualInplace(null, symbol, targetDt, signed, value)
|
||||
in PrefixOperators -> inplacePrefix(assignment.operator, targetDt, null, symbol)
|
||||
else -> throw AssemblyError("invalid augmented assign operator ${assignment.operator}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun fallbackAssign(origAssign: PtAugmentedAssign): IRCodeChunks {
|
||||
val value: PtExpression
|
||||
if(origAssign.operator in PrefixOperators) {
|
||||
value = PtPrefix(origAssign.operator, origAssign.target.type, origAssign.value.position)
|
||||
value = PtPrefix(origAssign.operator, origAssign.value.type, origAssign.value.position)
|
||||
value.add(origAssign.value)
|
||||
} else {
|
||||
require(origAssign.operator.endsWith('='))
|
||||
value = PtBinaryExpression(origAssign.operator.dropLast(1), origAssign.target.type, origAssign.value.position)
|
||||
val operator = when(origAssign.operator) {
|
||||
in ComparisonOperators -> origAssign.operator
|
||||
else -> {
|
||||
require(origAssign.operator.endsWith('='))
|
||||
origAssign.operator.dropLast(1)
|
||||
}
|
||||
}
|
||||
value = PtBinaryExpression(operator, origAssign.value.type, origAssign.value.position)
|
||||
val left: PtExpression = origAssign.target.children.single() as PtExpression
|
||||
value.add(left)
|
||||
value.add(origAssign.value)
|
||||
@@ -121,28 +84,161 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
return translateRegularAssign(normalAssign)
|
||||
}
|
||||
|
||||
private fun inplacePrefix(operator: String, vmDt: IRDataType, address: Int?, symbol: String?): IRCodeChunks {
|
||||
val code= IRCodeChunk(null, null)
|
||||
private fun inplacePrefix(operator: String, symbol: String?, array: PtArrayIndexer?, constAddress: Int?, memory: PtMemoryByte?, vmDt: IRDataType): Result<IRCodeChunks, NotImplementedError> {
|
||||
if(operator=="+")
|
||||
return Ok(emptyList())
|
||||
|
||||
if(array!=null)
|
||||
return Ok(inplacePrefixArray(operator, array))
|
||||
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
if(constAddress==null && memory!=null) {
|
||||
val register = codeGen.registers.nextFree()
|
||||
val tr = expressionEval.translateExpression(memory.address)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
when(operator) {
|
||||
"-" -> {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADI, vmDt, reg1=register, reg2=tr.resultReg)
|
||||
it += IRInstruction(Opcode.NEG, vmDt, reg1=register)
|
||||
it += IRInstruction(Opcode.STOREI, vmDt, reg1=register, reg2=tr.resultReg)
|
||||
}
|
||||
}
|
||||
"~" -> {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADI, vmDt, reg1=register, reg2=tr.resultReg)
|
||||
it += IRInstruction(Opcode.INV, vmDt, reg1=register)
|
||||
it += IRInstruction(Opcode.STOREI, vmDt, reg1=register, reg2=tr.resultReg)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
when (operator) {
|
||||
"-" -> addInstr(result, IRInstruction(Opcode.NEGM, vmDt, address = constAddress, labelSymbol = symbol), null)
|
||||
"~" -> addInstr(result, IRInstruction(Opcode.INVM, vmDt, address = constAddress, labelSymbol = symbol), null)
|
||||
// TODO: in boolean branch, how is 'not' handled here?
|
||||
else -> throw AssemblyError("weird prefix operator")
|
||||
}
|
||||
}
|
||||
return Ok(result)
|
||||
}
|
||||
|
||||
private fun inplacePrefixArray(operator: String, array: PtArrayIndexer): IRCodeChunks {
|
||||
val eltSize = codeGen.program.memsizer.memorySize(array.type)
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
val vmDt = irType(array.type)
|
||||
val constIndex = array.index.asConstInteger()
|
||||
|
||||
fun loadIndex(): Int {
|
||||
val tr = expressionEval.translateExpression(array.index)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
if(!array.splitWords && eltSize>1)
|
||||
result += codeGen.multiplyByConst(IRDataType.BYTE, tr.resultReg, eltSize)
|
||||
return tr.resultReg
|
||||
}
|
||||
|
||||
if(array.splitWords) {
|
||||
// handle split LSB/MSB arrays
|
||||
when(operator) {
|
||||
"+" -> { }
|
||||
"-" -> {
|
||||
val skipCarryLabel = codeGen.createLabelName()
|
||||
if(constIndex!=null) {
|
||||
addInstr(result, IRInstruction(Opcode.NEGM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex), null)
|
||||
addInstr(result, IRInstruction(Opcode.BSTEQ, labelSymbol = skipCarryLabel), null)
|
||||
addInstr(result, IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex), null)
|
||||
addInstr(result, IRInstruction(Opcode.NEGM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex), skipCarryLabel)
|
||||
} else {
|
||||
val indexReg = loadIndex()
|
||||
val registerLsb = codeGen.registers.nextFree()
|
||||
val registerMsb = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = registerLsb, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb")
|
||||
it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1 = registerLsb)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = registerLsb, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = registerMsb, reg2 = indexReg, labelSymbol = array.variable.name+"_msb")
|
||||
it += IRInstruction(Opcode.NEG, IRDataType.BYTE, reg1 = registerMsb)
|
||||
it += IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1 = registerLsb, immediate = 0)
|
||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = skipCarryLabel)
|
||||
it += IRInstruction(Opcode.DEC, IRDataType.BYTE, reg1 = registerMsb)
|
||||
}
|
||||
result += IRCodeChunk(skipCarryLabel, null).also {
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = registerMsb, reg2 = indexReg, labelSymbol = array.variable.name+"_msb")
|
||||
}
|
||||
}
|
||||
}
|
||||
"~" -> {
|
||||
if(constIndex!=null) {
|
||||
addInstr(result, IRInstruction(Opcode.INVM, IRDataType.BYTE, labelSymbol = array.variable.name+"_lsb", symbolOffset = constIndex), null)
|
||||
addInstr(result, IRInstruction(Opcode.INVM, IRDataType.BYTE, labelSymbol = array.variable.name+"_msb", symbolOffset = constIndex), null)
|
||||
} else {
|
||||
val indexReg = loadIndex()
|
||||
val register = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb")
|
||||
it += IRInstruction(Opcode.INV, IRDataType.BYTE, reg1 = register)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_lsb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_msb")
|
||||
it += IRInstruction(Opcode.INV, IRDataType.BYTE, reg1 = register)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name+"_msb")
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird prefix operator")
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// normal array.
|
||||
|
||||
when(operator) {
|
||||
"+" -> { }
|
||||
"-" -> {
|
||||
code += if(address!=null)
|
||||
IRInstruction(Opcode.NEGM, vmDt, address = address)
|
||||
else
|
||||
IRInstruction(Opcode.NEGM, vmDt, labelSymbol = symbol)
|
||||
if(constIndex!=null) {
|
||||
addInstr(result, IRInstruction(Opcode.NEGM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null)
|
||||
} else {
|
||||
val indexReg = loadIndex()
|
||||
val register = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
|
||||
it += IRInstruction(Opcode.NEG, vmDt, reg1 = register)
|
||||
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
"~" -> {
|
||||
val regMask = codeGen.registers.nextFree()
|
||||
val mask = if(vmDt==IRDataType.BYTE) 0x00ff else 0xffff
|
||||
code += IRInstruction(Opcode.LOAD, vmDt, reg1=regMask, immediate = mask)
|
||||
code += if(address!=null)
|
||||
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, address = address)
|
||||
else
|
||||
IRInstruction(Opcode.XORM, vmDt, reg1=regMask, labelSymbol = symbol)
|
||||
if(constIndex!=null) {
|
||||
addInstr(result, IRInstruction(Opcode.INVM, vmDt, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize), null)
|
||||
} else {
|
||||
val indexReg = loadIndex()
|
||||
val register = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
|
||||
it += IRInstruction(Opcode.INV, vmDt, reg1 = register)
|
||||
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
"not" -> {
|
||||
// TODO: in boolean branch, is 'not' handled ok like this?
|
||||
val register = codeGen.registers.nextFree()
|
||||
if(constIndex!=null) {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, vmDt, reg1=register, immediate = 1)
|
||||
it += IRInstruction(Opcode.XORM, vmDt, reg1=register, labelSymbol = array.variable.name, symbolOffset = constIndex*eltSize)
|
||||
}
|
||||
} else {
|
||||
val indexReg = loadIndex()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
|
||||
it += IRInstruction(Opcode.XOR, vmDt, reg1 = register, immediate = 1)
|
||||
it += IRInstruction(Opcode.STOREX, vmDt, reg1 = register, reg2 = indexReg, labelSymbol = array.variable.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird prefix operator")
|
||||
}
|
||||
return listOf(code)
|
||||
return result
|
||||
}
|
||||
|
||||
private fun translateRegularAssign(assignment: PtAssignment): IRCodeChunks {
|
||||
@@ -206,25 +302,6 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val variable = targetArray.variable.name
|
||||
val itemsize = codeGen.program.memsizer.memorySize(targetArray.type)
|
||||
|
||||
if(targetArray.usesPointerVariable) {
|
||||
if(itemsize!=1)
|
||||
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||
if(targetArray.index.type!=DataType.UBYTE)
|
||||
throw AssemblyError("non-array var indexing requires bytes index")
|
||||
val tr = expressionEval.translateExpression(targetArray.index)
|
||||
val idxReg = tr.resultReg
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val code = IRCodeChunk(null, null)
|
||||
if(zero) {
|
||||
// there's no STOREZIX instruction
|
||||
valueRegister = codeGen.registers.nextFree()
|
||||
code += IRInstruction(Opcode.LOAD, targetDt, reg1=valueRegister, immediate = 0)
|
||||
}
|
||||
code += IRInstruction(Opcode.STOREIX, targetDt, reg1=valueRegister, reg2=idxReg, labelSymbol = variable)
|
||||
result += code
|
||||
return result
|
||||
}
|
||||
|
||||
val fixedIndex = constIntValue(targetArray.index)
|
||||
val arrayLength = codeGen.symbolTable.getLength(targetArray.variable.name)
|
||||
if(zero) {
|
||||
@@ -306,18 +383,47 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
val tr = expressionEval.translateExpression(targetMemory.address)
|
||||
val addressReg = tr.resultReg
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREZI, targetDt, reg1=addressReg) }
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREZI, targetDt, reg1=addressReg)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(targetMemory.address is PtNumber) {
|
||||
val chunk = IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREM, targetDt, reg1=valueRegister, address=(targetMemory.address as PtNumber).number.toInt()) }
|
||||
result += chunk
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(targetMemory.address)
|
||||
val addressReg = tr.resultReg
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also { it += IRInstruction(Opcode.STOREI, targetDt, reg1=valueRegister, reg2=addressReg) }
|
||||
val constAddress = targetMemory.address as? PtNumber
|
||||
if(constAddress!=null) {
|
||||
addInstr(result, IRInstruction(Opcode.STOREM, targetDt, reg1=valueRegister, address=constAddress.number.toInt()), null)
|
||||
return result
|
||||
}
|
||||
val ptrWithOffset = targetMemory.address as? PtBinaryExpression
|
||||
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
|
||||
if((ptrWithOffset.right as? PtNumber)?.number?.toInt() in 0..255) {
|
||||
// STOREIX only works with byte index.
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
val offsetReg = codeGen.registers.nextFree()
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = ptrWithOffset.right.asConstInteger())
|
||||
it += IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=valueRegister, reg2=offsetReg, labelSymbol = ptrName)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
val offsetTypecast = ptrWithOffset?.right as? PtTypeCast
|
||||
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier
|
||||
&& (ptrWithOffset.right.type in ByteDatatypes || offsetTypecast?.value?.type in ByteDatatypes)) {
|
||||
// STOREIX only works with byte index.
|
||||
val tr = if(offsetTypecast?.value?.type in ByteDatatypes)
|
||||
expressionEval.translateExpression(offsetTypecast!!.value)
|
||||
else
|
||||
expressionEval.translateExpression(ptrWithOffset.right)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
addInstr(result, IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=valueRegister, reg2=tr.resultReg, labelSymbol = ptrName), null)
|
||||
return result
|
||||
}
|
||||
val tr = expressionEval.translateExpression(targetMemory.address)
|
||||
val addressReg = tr.resultReg
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.STOREI, targetDt, reg1=valueRegister, reg2=addressReg), null)
|
||||
return result
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
@@ -705,7 +705,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
}
|
||||
}
|
||||
is PtArrayIndexer -> {
|
||||
require(!target.usesPointerVariable)
|
||||
if(target.splitWords) {
|
||||
// lsb/msb in split arrays, element index 'size' is always 1
|
||||
val constIndex = target.index.asConstInteger()
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -475,8 +475,8 @@ class IRCodeGen(
|
||||
val tmpRegLsb = registers.nextFree()
|
||||
val tmpRegMsb = registers.nextFree()
|
||||
val concatReg = registers.nextFree()
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2=indexReg, immediate = iterableLength, labelSymbol=iterable.name+"_msb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegLsb, reg2=indexReg, immediate = iterableLength, labelSymbol=iterable.name+"_lsb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegMsb, reg2=indexReg, labelSymbol=iterable.name+"_msb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=tmpRegLsb, reg2=indexReg, labelSymbol=iterable.name+"_lsb")
|
||||
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=concatReg, reg2=tmpRegMsb, reg3=tmpRegLsb)
|
||||
it += IRInstruction(Opcode.STOREM, irType(elementDt), reg1=concatReg, labelSymbol = loopvarSymbol)
|
||||
}
|
||||
@@ -1462,7 +1462,6 @@ class IRCodeGen(
|
||||
else -> throw AssemblyError("weird operator")
|
||||
}
|
||||
} else {
|
||||
val arrayLength = symbolTable.getLength(array.variable.name)
|
||||
val indexTr = expressionEval.translateExpression(array.index)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
val incReg = registers.nextFree()
|
||||
@@ -1470,28 +1469,28 @@ class IRCodeGen(
|
||||
when(postIncrDecr.operator) {
|
||||
"++" -> {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_lsb")
|
||||
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=incReg)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_lsb")
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel)
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_msb")
|
||||
it += IRInstruction(Opcode.INC, IRDataType.BYTE, reg1=incReg)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_msb")
|
||||
}
|
||||
result += IRCodeChunk(skipLabel, null)
|
||||
}
|
||||
"--" -> {
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_lsb")
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel)
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_msb")
|
||||
it += IRInstruction(Opcode.DEC, IRDataType.BYTE, reg1=incReg)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_msb")
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_msb")
|
||||
}
|
||||
result += IRCodeChunk(skipLabel, null).also {
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
|
||||
it += IRInstruction(Opcode.LOADX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_lsb")
|
||||
it += IRInstruction(Opcode.DEC, IRDataType.BYTE, reg1=incReg)
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, immediate = arrayLength, labelSymbol = "${variable}_lsb")
|
||||
it += IRInstruction(Opcode.STOREX, IRDataType.BYTE, reg1=incReg, reg2=indexTr.resultReg, labelSymbol = "${variable}_lsb")
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("weird operator")
|
||||
@@ -1517,38 +1516,60 @@ class IRCodeGen(
|
||||
if(ident!=null) {
|
||||
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol = ident.name), null)
|
||||
} else if(memory!=null) {
|
||||
if(memory.address is PtNumber) {
|
||||
val address = (memory.address as PtNumber).number.toInt()
|
||||
addInstr(result, IRInstruction(operationMem, irDt, address = address), null)
|
||||
} else {
|
||||
val tr = expressionEval.translateExpression(memory.address)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
val constAddress = memory.address as? PtNumber
|
||||
if(constAddress!=null) {
|
||||
addInstr(result, IRInstruction(operationMem, irDt, address = constAddress.number.toInt()), null)
|
||||
return result
|
||||
}
|
||||
val ptrWithOffset = memory.address as? PtBinaryExpression
|
||||
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier) {
|
||||
if((ptrWithOffset.right as? PtNumber)?.number?.toInt() in 0..255) {
|
||||
// LOADIX only works with byte index.
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
val offsetReg = registers.nextFree()
|
||||
val incReg = registers.nextFree()
|
||||
it += IRInstruction(Opcode.LOADI, irDt, reg1 = incReg, reg2 = tr.resultReg)
|
||||
it += IRInstruction(operationRegister, irDt, reg1 = incReg)
|
||||
it += IRInstruction(Opcode.STOREI, irDt, reg1 = incReg, reg2 = tr.resultReg)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=offsetReg, immediate = ptrWithOffset.right.asConstInteger())
|
||||
it += IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=incReg, reg2=offsetReg, labelSymbol = ptrName)
|
||||
it += IRInstruction(operationRegister, irDt, reg1 = incReg)
|
||||
it += IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=incReg, reg2=offsetReg, labelSymbol = ptrName)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
val offsetTypecast = ptrWithOffset?.right as? PtTypeCast
|
||||
if(ptrWithOffset!=null && ptrWithOffset.operator=="+" && ptrWithOffset.left is PtIdentifier
|
||||
&& (ptrWithOffset.right.type in ByteDatatypes || offsetTypecast?.value?.type in ByteDatatypes)) {
|
||||
// LOADIX only works with byte index.
|
||||
val tr = if(offsetTypecast?.value?.type in ByteDatatypes)
|
||||
expressionEval.translateExpression(offsetTypecast!!.value)
|
||||
else
|
||||
expressionEval.translateExpression(ptrWithOffset.right)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
val ptrName = (ptrWithOffset.left as PtIdentifier).name
|
||||
result += IRCodeChunk(null, null).also {
|
||||
val incReg = registers.nextFree()
|
||||
it += IRInstruction(Opcode.LOADIX, IRDataType.BYTE, reg1=incReg, reg2=tr.resultReg, labelSymbol = ptrName)
|
||||
it += IRInstruction(operationRegister, irDt, reg1 = incReg)
|
||||
it += IRInstruction(Opcode.STOREIX, IRDataType.BYTE, reg1=incReg, reg2=tr.resultReg, labelSymbol = ptrName)
|
||||
}
|
||||
return result
|
||||
}
|
||||
val tr = expressionEval.translateExpression(memory.address)
|
||||
addToResult(result, tr, tr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
val incReg = registers.nextFree()
|
||||
it += IRInstruction(Opcode.LOADI, irDt, reg1 = incReg, reg2 = tr.resultReg)
|
||||
it += IRInstruction(operationRegister, irDt, reg1 = incReg)
|
||||
it += IRInstruction(Opcode.STOREI, irDt, reg1 = incReg, reg2 = tr.resultReg)
|
||||
}
|
||||
return result
|
||||
} else if (array!=null) {
|
||||
val variable = array.variable.name
|
||||
val itemsize = program.memsizer.memorySize(array.type)
|
||||
val fixedIndex = constIntValue(array.index)
|
||||
if(fixedIndex!=null) {
|
||||
val offset = fixedIndex*itemsize
|
||||
val indexReg = registers.nextFree()
|
||||
val dataReg = registers.nextFree()
|
||||
if(array.usesPointerVariable) {
|
||||
// we don't have an indirect dec/inc so do it via an intermediate register
|
||||
result += IRCodeChunk(null,null).also {
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1 = indexReg, immediate = offset)
|
||||
it += IRInstruction(Opcode.LOADIX, irDt, reg1 = dataReg, reg2 = indexReg, labelSymbol = variable)
|
||||
it += IRInstruction(operationRegister, irDt, reg1=dataReg)
|
||||
it += IRInstruction(Opcode.STOREIX, irDt, reg1 = dataReg, reg2 = indexReg, labelSymbol = variable)
|
||||
}
|
||||
} else {
|
||||
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol = variable, symbolOffset = offset), null)
|
||||
}
|
||||
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol = variable, symbolOffset = fixedIndex*itemsize), null)
|
||||
} else {
|
||||
val indexTr = expressionEval.translateExpression(array.index)
|
||||
addToResult(result, indexTr, indexTr.resultReg, -1)
|
||||
@@ -1556,15 +1577,9 @@ class IRCodeGen(
|
||||
result += multiplyByConst(IRDataType.BYTE, indexTr.resultReg, itemsize)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
val incReg = registers.nextFree()
|
||||
if(array.usesPointerVariable) {
|
||||
it += IRInstruction(Opcode.LOADIX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
|
||||
it += IRInstruction(operationRegister, irDt, reg1=incReg)
|
||||
it += IRInstruction(Opcode.STOREIX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
|
||||
} else {
|
||||
it += IRInstruction(Opcode.LOADX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
|
||||
it += IRInstruction(operationRegister, irDt, reg1=incReg)
|
||||
it += IRInstruction(Opcode.STOREX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
|
||||
}
|
||||
it += IRInstruction(Opcode.LOADX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
|
||||
it += IRInstruction(operationRegister, irDt, reg1=incReg)
|
||||
it += IRInstruction(Opcode.STOREX, irDt, reg1=incReg, reg2=indexTr.resultReg, labelSymbol=variable)
|
||||
}
|
||||
}
|
||||
} else
|
||||
|
||||
@@ -4,10 +4,7 @@ import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.maySwapOperandOrder
|
||||
import prog8.ast.statements.ForLoop
|
||||
import prog8.ast.statements.RepeatLoop
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.ast.statements.VarDeclType
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.AssociativeOperators
|
||||
@@ -35,6 +32,19 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(numLiteral: NumericLiteral, parent: Node): Iterable<IAstModification> {
|
||||
if(parent is Assignment) {
|
||||
val iDt = parent.target.inferType(program)
|
||||
if(iDt.isKnown && !iDt.isBool && !iDt.istype(numLiteral.type)) {
|
||||
val casted = numLiteral.cast(iDt.getOr(DataType.UNDEFINED))
|
||||
if(casted.isValid) {
|
||||
return listOf(IAstModification.ReplaceNode(numLiteral, casted.value!!, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(containment: ContainmentCheck, parent: Node): Iterable<IAstModification> {
|
||||
val result = containment.constValue(program)
|
||||
if(result!=null)
|
||||
@@ -251,48 +261,6 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
}
|
||||
}
|
||||
|
||||
if(rightconst!=null && (expr.operator=="<<" || expr.operator==">>")) {
|
||||
val dt = expr.left.inferType(program)
|
||||
if(dt.isBytes && rightconst.number>=8) {
|
||||
if(dt.istype(DataType.UBYTE)) {
|
||||
val zeroUB = NumericLiteral(DataType.UBYTE, 0.0, expr.position)
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroUB, parent))
|
||||
} else {
|
||||
if(leftconst!=null) {
|
||||
val zeroB = NumericLiteral(DataType.BYTE, 0.0, expr.position)
|
||||
val minusoneB = NumericLiteral(DataType.BYTE, -1.0, expr.position)
|
||||
if(leftconst.number<0.0) {
|
||||
if(expr.operator=="<<")
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroB, parent))
|
||||
else
|
||||
modifications.add(IAstModification.ReplaceNode(expr, minusoneB, parent))
|
||||
} else {
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroB, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(dt.isWords && rightconst.number>=16) {
|
||||
if(dt.istype(DataType.UWORD)) {
|
||||
val zeroUW = NumericLiteral(DataType.UWORD, 0.0, expr.position)
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroUW, parent))
|
||||
} else {
|
||||
if(leftconst!=null) {
|
||||
val zeroW = NumericLiteral(DataType.WORD, 0.0, expr.position)
|
||||
val minusoneW = NumericLiteral(DataType.WORD, -1.0, expr.position)
|
||||
if(leftconst.number<0.0) {
|
||||
if(expr.operator=="<<")
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroW, parent))
|
||||
else
|
||||
modifications.add(IAstModification.ReplaceNode(expr, minusoneW, parent))
|
||||
} else {
|
||||
modifications.add(IAstModification.ReplaceNode(expr, zeroW, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modifications
|
||||
}
|
||||
|
||||
@@ -354,14 +322,14 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
if(stepLiteral!=null) {
|
||||
val stepCast = stepLiteral.cast(targetDt)
|
||||
if(stepCast.isValid)
|
||||
stepCast.valueOrZero()
|
||||
stepCast.value!!
|
||||
else
|
||||
range.step
|
||||
} else {
|
||||
range.step
|
||||
}
|
||||
|
||||
return RangeExpression(fromCast.valueOrZero(), toCast.valueOrZero(), newStep, range.position)
|
||||
return RangeExpression(fromCast.value!!, toCast.value!!, newStep, range.position)
|
||||
}
|
||||
|
||||
// adjust the datatype of a range expression in for loops to the loop variable.
|
||||
@@ -420,7 +388,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
if(decl.datatype!=DataType.BOOL || valueDt.isnot(DataType.UBYTE)) {
|
||||
val cast = numval.cast(decl.datatype)
|
||||
if (cast.isValid)
|
||||
return listOf(IAstModification.ReplaceNode(numval, cast.valueOrZero(), decl))
|
||||
return listOf(IAstModification.ReplaceNode(numval, cast.value!!, decl))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ class VarConstantValueTypeAdjuster(
|
||||
declConstValue.linkParents(decl)
|
||||
val cast = declConstValue.cast(decl.datatype)
|
||||
if (cast.isValid)
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, cast.valueOrZero(), decl))
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, cast.value!!, decl))
|
||||
}
|
||||
}
|
||||
} catch (x: UndefinedSymbolError) {
|
||||
|
||||
@@ -32,8 +32,8 @@ class ExpressionSimplifier(private val program: Program,
|
||||
val literal = typecast.expression as? NumericLiteral
|
||||
if (literal != null) {
|
||||
val newLiteral = literal.cast(typecast.type)
|
||||
if (newLiteral.isValid && newLiteral.valueOrZero() !== literal) {
|
||||
mods += IAstModification.ReplaceNode(typecast, newLiteral.valueOrZero(), parent)
|
||||
if (newLiteral.isValid && newLiteral.value!! !== literal) {
|
||||
mods += IAstModification.ReplaceNode(typecast, newLiteral.value!!, parent)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1172,6 +1172,47 @@ sub search_x16edit() -> ubyte {
|
||||
}}
|
||||
}
|
||||
|
||||
sub set_program_args(uword args_ptr, ubyte args_size) {
|
||||
; Set the inter-program arguments.
|
||||
; standardized way to pass arguments between programs is in ram bank 0, address $bf00-$bfff.
|
||||
; see https://github.com/X16Community/x16-docs/blob/master/X16%20Reference%20-%2007%20-%20Memory%20Map.md#bank-0
|
||||
sys.push(getrambank())
|
||||
rambank(0)
|
||||
sys.memcopy(args_ptr, $bf00, args_size)
|
||||
if args_size<255
|
||||
@($bf00+args_size) = 0
|
||||
rambank(sys.pop())
|
||||
}
|
||||
|
||||
asmsub get_program_args(uword buffer @R0, ubyte buf_size @R1, bool binary @Pc) {
|
||||
; Retrieve the inter-program arguments. If binary=false, it treats them as a string (stops copying at first zero).
|
||||
; standardized way to pass arguments between programs is in ram bank 0, address $bf00-$bfff.
|
||||
; see https://github.com/X16Community/x16-docs/blob/master/X16%20Reference%20-%2007%20-%20Memory%20Map.md#bank-0
|
||||
%asm {{
|
||||
lda #0
|
||||
rol a
|
||||
sta P8ZP_SCRATCH_REG
|
||||
lda $00
|
||||
pha
|
||||
stz $00
|
||||
stz P8ZP_SCRATCH_W1
|
||||
lda #$bf
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
ldy #0
|
||||
- lda (P8ZP_SCRATCH_W1),y
|
||||
sta (cx16.r0),y
|
||||
beq +
|
||||
_continue iny
|
||||
cpy cx16.r1L ; max size?
|
||||
bne -
|
||||
beq ++
|
||||
+ lda P8ZP_SCRATCH_REG ; binary?
|
||||
bne _continue
|
||||
+ pla
|
||||
sta $00
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sys {
|
||||
|
||||
@@ -52,7 +52,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
val splitWordArrays by cli.option(ArgType.Boolean, fullName = "splitarrays", description = "treat all word arrays as tagged with @split to make them lsb/msb split in memory")
|
||||
val printAst1 by cli.option(ArgType.Boolean, fullName = "printast1", description = "print out the compiler AST")
|
||||
val printAst2 by cli.option(ArgType.Boolean, fullName = "printast2", description = "print out the intermediate AST that is used for code generation")
|
||||
val breakpointCpuInstruction by cli.option(ArgType.Boolean, fullName = "breakinstr", description = "also use a CPU instruction for %breakpoint")
|
||||
val breakpointCpuInstruction by cli.option(ArgType.Choice(listOf("brk", "stp"), { it }), fullName = "breakinstr", description = "the CPU instruction to use as well for %breakpoint")
|
||||
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}', '${AtariTarget.NAME}', '${PETTarget.NAME}', '${VMTarget.NAME}') (required)")
|
||||
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "load and run a .p8ir IR source file in the VM")
|
||||
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
|
||||
@@ -158,7 +158,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
slabsGolden == true,
|
||||
compilationTarget!!,
|
||||
splitWordArrays == true,
|
||||
breakpointCpuInstruction = false,
|
||||
breakpointCpuInstruction,
|
||||
printAst1 == true,
|
||||
printAst2 == true,
|
||||
processedSymbols,
|
||||
@@ -236,7 +236,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
slabsGolden == true,
|
||||
compilationTarget!!,
|
||||
splitWordArrays == true,
|
||||
breakpointCpuInstruction == true,
|
||||
breakpointCpuInstruction,
|
||||
printAst1 == true,
|
||||
printAst2 == true,
|
||||
processedSymbols,
|
||||
|
||||
@@ -44,7 +44,7 @@ class CompilerArguments(val filepath: Path,
|
||||
val slabsGolden: Boolean,
|
||||
val compilationTarget: String,
|
||||
val splitWordArrays: Boolean,
|
||||
val breakpointCpuInstruction: Boolean,
|
||||
val breakpointCpuInstruction: String?,
|
||||
val printAst1: Boolean,
|
||||
val printAst2: Boolean,
|
||||
val symbolDefs: Map<String, String>,
|
||||
|
||||
@@ -140,6 +140,18 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val statements = (returnStmt.parent as IStatementContainer).statements
|
||||
val myIndex = statements.indexOf(returnStmt)
|
||||
if(myIndex>=0 && myIndex<statements.size-1) {
|
||||
val stmtAfterReturn = statements[myIndex+1]
|
||||
if(stmtAfterReturn.position.line==returnStmt.position.line) {
|
||||
// this error is not generated by the parser, unfortunately.
|
||||
// it parses "return somestatement" as: "return" "somestatement" (and this then only results in a warning about unreachable code).
|
||||
errors.err("a statement is not a return value", stmtAfterReturn.position)
|
||||
}
|
||||
}
|
||||
|
||||
super.visit(returnStmt)
|
||||
}
|
||||
|
||||
@@ -1328,7 +1340,7 @@ internal class AstChecker(private val program: Program,
|
||||
if(target !is VarDecl || target.type== VarDeclType.CONST) {
|
||||
errors.err("can only increment or decrement a variable", postIncrDecr.position)
|
||||
} else if(target.datatype !in NumericDatatypes) {
|
||||
errors.err("can only increment or decrement a byte/float/word variable", postIncrDecr.position)
|
||||
errors.err("cannot increment/decrement this", postIncrDecr.position)
|
||||
}
|
||||
}
|
||||
} else if(postIncrDecr.target.arrayindexed != null) {
|
||||
@@ -1339,8 +1351,8 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
else {
|
||||
val dt = (target as VarDecl).datatype
|
||||
if(dt !in NumericDatatypes && dt !in ArrayDatatypes)
|
||||
errors.err("can only increment or decrement a byte/float/word", postIncrDecr.position)
|
||||
if(dt !in NumericDatatypes && dt !in ArrayDatatypes && dt!=DataType.STR)
|
||||
errors.err("cannot increment/decrement this", postIncrDecr.position)
|
||||
}
|
||||
}
|
||||
// else if(postIncrDecr.target.memoryAddress != null) { } // a memory location can always be ++/--
|
||||
@@ -1538,6 +1550,25 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
when (targetDt) {
|
||||
DataType.STR -> return err("string value expected")
|
||||
DataType.ARRAY_BOOL -> {
|
||||
// value may be either a single byte, or a byte arraysize (of all constant values)\
|
||||
if(value.type istype targetDt) {
|
||||
if(!checkArrayValues(value, targetDt))
|
||||
return false
|
||||
val arraySpecSize = arrayspec.constIndex()
|
||||
val arraySize = value.value.size
|
||||
if(arraySpecSize!=null && arraySpecSize>0) {
|
||||
if(arraySpecSize>256)
|
||||
return err("boolean array length must be 1-256")
|
||||
val expectedSize = arrayspec.constIndex() ?: return err("array size specifier must be constant integer value")
|
||||
if (arraySize != expectedSize)
|
||||
return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)")
|
||||
return true
|
||||
}
|
||||
return err("invalid boolean array size, must be 1-256")
|
||||
}
|
||||
return err("invalid boolean array initialization value ${value.type}, expected $targetDt")
|
||||
}
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
// value may be either a single byte, or a byte arraysize (of all constant values), or a range
|
||||
if(value.type istype targetDt) {
|
||||
@@ -1660,15 +1691,15 @@ internal class AstChecker(private val program: Program,
|
||||
val array = value.value.map {
|
||||
when (it) {
|
||||
is NumericLiteral -> it.number.toInt()
|
||||
is AddressOf -> it.identifier.hashCode() and 0xffff
|
||||
is IdentifierReference -> it.hashCode() and 0xffff
|
||||
is AddressOf -> it.identifier.nameInSource.hashCode() and 0xffff
|
||||
is IdentifierReference -> it.nameInSource.hashCode() and 0xffff
|
||||
is TypecastExpression -> {
|
||||
val constVal = it.expression.constValue(program)
|
||||
val cast = constVal?.cast(it.type)
|
||||
if(cast==null || !cast.isValid)
|
||||
-9999999
|
||||
else
|
||||
cast.valueOrZero().number.toInt()
|
||||
cast.value!!.number.toInt()
|
||||
}
|
||||
else -> -9999999
|
||||
}
|
||||
@@ -1687,6 +1718,9 @@ internal class AstChecker(private val program: Program,
|
||||
DataType.ARRAY_W, DataType.ARRAY_W_SPLIT -> {
|
||||
correct = array.all { it in -32768..32767 }
|
||||
}
|
||||
DataType.ARRAY_BOOL -> {
|
||||
correct = array.all { it==0 || it==1 }
|
||||
}
|
||||
DataType.ARRAY_F -> correct = true
|
||||
else -> throw FatalAstException("invalid array type $type")
|
||||
}
|
||||
@@ -1730,6 +1764,9 @@ internal class AstChecker(private val program: Program,
|
||||
if((sourceDatatype== DataType.UWORD || sourceDatatype== DataType.WORD) && (targetDatatype== DataType.UBYTE || targetDatatype== DataType.BYTE)) {
|
||||
errors.err("cannot assign word to byte, use msb() or lsb()?", position)
|
||||
}
|
||||
else if(sourceDatatype in IterableDatatypes && targetDatatype in ByteDatatypes) {
|
||||
errors.err("cannot assign string or array to a byte", position)
|
||||
}
|
||||
else if(sourceDatatype== DataType.FLOAT && targetDatatype in IntegerDatatypes)
|
||||
errors.err("cannot assign float to ${targetDatatype.name.lowercase()}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position)
|
||||
else {
|
||||
|
||||
@@ -195,25 +195,19 @@ _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 indexerDt = indexExpr.inferType(program)
|
||||
if(indexerDt.isWords) {
|
||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
|
||||
if(arrayVar!=null && arrayVar.datatype==DataType.UWORD) {
|
||||
val add: Expression =
|
||||
if(indexExpr.constValue(program)?.number==0.0)
|
||||
arrayIndexedExpression.arrayvar.copy()
|
||||
else
|
||||
BinaryExpression(arrayIndexedExpression.arrayvar.copy(), "+", indexExpr, arrayIndexedExpression.position)
|
||||
return if(parent is AssignTarget) {
|
||||
// assignment to array
|
||||
val memwrite = DirectMemoryWrite(add, arrayIndexedExpression.position)
|
||||
val newtarget = AssignTarget(null, null, memwrite, arrayIndexedExpression.position)
|
||||
listOf(IAstModification.ReplaceNode(parent, newtarget, parent.parent))
|
||||
} else {
|
||||
// read from array
|
||||
val memread = DirectMemoryRead(add, arrayIndexedExpression.position)
|
||||
listOf(IAstModification.ReplaceNode(arrayIndexedExpression, memread, parent))
|
||||
}
|
||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
|
||||
if(arrayVar!=null && arrayVar.datatype==DataType.UWORD) {
|
||||
val wordIndex = TypecastExpression(indexExpr, DataType.UWORD, true, indexExpr.position)
|
||||
val address = BinaryExpression(arrayIndexedExpression.arrayvar.copy(), "+", wordIndex, arrayIndexedExpression.position)
|
||||
return if(parent is AssignTarget) {
|
||||
// assignment to array
|
||||
val memwrite = DirectMemoryWrite(address, arrayIndexedExpression.position)
|
||||
val newtarget = AssignTarget(null, null, memwrite, arrayIndexedExpression.position)
|
||||
listOf(IAstModification.ReplaceNode(parent, newtarget, parent.parent))
|
||||
} else {
|
||||
// read from array
|
||||
val memread = DirectMemoryRead(address, arrayIndexedExpression.position)
|
||||
listOf(IAstModification.ReplaceNode(arrayIndexedExpression, memread, parent))
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
|
||||
@@ -551,8 +551,10 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
|
||||
}
|
||||
|
||||
private fun transform(srcArr: ArrayIndexedExpression): PtArrayIndexer {
|
||||
val arrayVarType = srcArr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
|
||||
val array = PtArrayIndexer(arrayVarType, srcArr.position)
|
||||
if(srcArr.arrayvar.targetVarDecl(program)!!.datatype !in ArrayDatatypes + DataType.STR)
|
||||
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") }
|
||||
val array = PtArrayIndexer(eltType, srcArr.position)
|
||||
array.add(transform(srcArr.arrayvar))
|
||||
array.add(transformExpression(srcArr.indexer.indexExpr))
|
||||
return array
|
||||
|
||||
@@ -196,7 +196,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
if(valuetype in IterableDatatypes && targettype==DataType.UWORD)
|
||||
// special case, don't typecast STR/arrays to UWORD, we support those assignments "directly"
|
||||
return noModifications
|
||||
if((assignment.value as? BinaryExpression)?.operator in ComparisonOperators) {
|
||||
if((assignment.value as? BinaryExpression)?.operator in ComparisonOperators && targettype in IntegerDatatypes) {
|
||||
// special case, treat a boolean comparison result as the same type as the target value to avoid needless casts later
|
||||
return noModifications
|
||||
}
|
||||
@@ -207,7 +207,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
fun castLiteral(cvalue2: NumericLiteral): List<IAstModification.ReplaceNode> {
|
||||
val cast = cvalue2.cast(targettype)
|
||||
return if(cast.isValid)
|
||||
listOf(IAstModification.ReplaceNode(assignment.value, cast.valueOrZero(), assignment))
|
||||
listOf(IAstModification.ReplaceNode(assignment.value, cast.value!!, assignment))
|
||||
else
|
||||
emptyList()
|
||||
}
|
||||
@@ -314,7 +314,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
val dt = memread.addressExpression.inferType(program)
|
||||
if(dt.isKnown && dt.getOr(DataType.UWORD)!=DataType.UWORD) {
|
||||
val castedValue = (memread.addressExpression as? NumericLiteral)?.cast(DataType.UWORD)?.valueOrZero()
|
||||
val castedValue = (memread.addressExpression as? NumericLiteral)?.cast(DataType.UWORD)?.value
|
||||
if(castedValue!=null)
|
||||
modifications += IAstModification.ReplaceNode(memread.addressExpression, castedValue, memread)
|
||||
else
|
||||
@@ -328,7 +328,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
val dt = memwrite.addressExpression.inferType(program)
|
||||
if(dt.isKnown && dt.getOr(DataType.UWORD)!=DataType.UWORD) {
|
||||
val castedValue = (memwrite.addressExpression as? NumericLiteral)?.cast(DataType.UWORD)?.valueOrZero()
|
||||
val castedValue = (memwrite.addressExpression as? NumericLiteral)?.cast(DataType.UWORD)?.value
|
||||
if(castedValue!=null)
|
||||
modifications += IAstModification.ReplaceNode(memwrite.addressExpression, castedValue, memwrite)
|
||||
else
|
||||
@@ -349,9 +349,9 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
if (returnDt istype subReturnType or returnDt.isNotAssignableTo(subReturnType))
|
||||
return noModifications
|
||||
if (returnValue is NumericLiteral) {
|
||||
val cast = returnValue.cast(subroutine.returntypes.single())
|
||||
val cast = returnValue.cast(subReturnType)
|
||||
if(cast.isValid)
|
||||
returnStmt.value = cast.valueOrZero()
|
||||
returnStmt.value = cast.value
|
||||
} else {
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
addTypecastOrCastedValueModification(modifications, returnValue, subReturnType, returnStmt)
|
||||
@@ -402,9 +402,9 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
val castedValue = expressionToCast.cast(requiredType)
|
||||
if (castedValue.isValid) {
|
||||
val signOriginal = sign(expressionToCast.number)
|
||||
val signCasted = sign(castedValue.valueOrZero().number)
|
||||
val signCasted = sign(castedValue.value!!.number)
|
||||
if(signOriginal==signCasted) {
|
||||
modifications += IAstModification.ReplaceNode(expressionToCast, castedValue.valueOrZero(), parent)
|
||||
modifications += IAstModification.ReplaceNode(expressionToCast, castedValue.value!!, parent)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
if(typecast.expression is NumericLiteral) {
|
||||
val value = (typecast.expression as NumericLiteral).cast(typecast.type)
|
||||
if(value.isValid)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, value.valueOrZero(), parent))
|
||||
return listOf(IAstModification.ReplaceNode(typecast, value.value!!, parent))
|
||||
}
|
||||
|
||||
val sourceDt = typecast.expression.inferType(program)
|
||||
@@ -66,9 +66,9 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
}
|
||||
|
||||
// if the expression is a comparison expression, or a logical expression, it produces the
|
||||
// correct 'boolean' byte result so the cast can be removed.
|
||||
// correct 'boolean' byte result so the cast can be removed. Only if target is Integer.
|
||||
val binExpr = typecast.expression as? BinaryExpression
|
||||
if(binExpr!=null && binExpr.operator in ComparisonOperators + LogicalOperators) {
|
||||
if(binExpr!=null && binExpr.operator in ComparisonOperators + LogicalOperators && typecast.type in IntegerDatatypesNoBool) {
|
||||
return listOf(IAstModification.ReplaceNode(typecast, binExpr, parent))
|
||||
}
|
||||
|
||||
@@ -151,6 +151,27 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(expr.operator in LogicalOperators) {
|
||||
// remove redundant !=0 comparisons from logical expressions such as: a!=0 xor b --> a xor b (only for byte operands!)
|
||||
val leftExpr = expr.left as? BinaryExpression
|
||||
if(leftExpr != null &&
|
||||
leftExpr.operator == "!=" &&
|
||||
!leftExpr.left.isSimple &&
|
||||
leftExpr.left.inferType(program).isBytes &&
|
||||
leftExpr.right.constValue(program)?.number == 0.0) {
|
||||
return listOf(IAstModification.ReplaceNode(leftExpr, leftExpr.left, expr))
|
||||
}
|
||||
val rightExpr = expr.right as? BinaryExpression
|
||||
if(rightExpr != null &&
|
||||
rightExpr.operator == "!=" &&
|
||||
!rightExpr.left.isSimple &&
|
||||
rightExpr.left.inferType(program).isBytes &&
|
||||
rightExpr.right.constValue(program)?.number == 0.0) {
|
||||
return listOf(IAstModification.ReplaceNode(rightExpr, rightExpr.left, expr))
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
|
||||
slabsGolden = false,
|
||||
compilationTarget = target.name,
|
||||
splitWordArrays = false,
|
||||
breakpointCpuInstruction = false,
|
||||
breakpointCpuInstruction = null,
|
||||
printAst1 = false,
|
||||
printAst2 = false,
|
||||
symbolDefs = emptyMap(),
|
||||
|
||||
@@ -36,7 +36,7 @@ class TestCompilerOptionSourcedirs: FunSpec({
|
||||
slabsGolden = false,
|
||||
compilationTarget = Cx16Target.NAME,
|
||||
splitWordArrays = false,
|
||||
breakpointCpuInstruction = false,
|
||||
breakpointCpuInstruction = null,
|
||||
printAst1 = false,
|
||||
printAst2 = false,
|
||||
symbolDefs = emptyMap(),
|
||||
|
||||
@@ -817,4 +817,59 @@ main {
|
||||
if5.condition shouldBe instanceOf<BinaryExpression>()
|
||||
if6.condition shouldBe instanceOf<BinaryExpression>()
|
||||
}
|
||||
|
||||
test("funky bitshifts") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
const uword one = 1
|
||||
const uword two = 2
|
||||
uword @shared answer = one * two >> 8
|
||||
funcw(one * two >> 8)
|
||||
|
||||
const uword uw1 = 99
|
||||
const uword uw2 = 22
|
||||
uword @shared answer2 = uw1 * uw2 >> 8 ; optimized into msb(uw1*uw2) as uword
|
||||
funcw(uw1 * uw2 >> 8)
|
||||
|
||||
uword @shared uw3 = 99
|
||||
uword @shared uw4 = 22
|
||||
uword @shared answer3 = uw3 * uw4 >> 8 ; optimized into msb(uw1*uw2) as uword
|
||||
funcw(uw3 * uw4 >> 8)
|
||||
}
|
||||
|
||||
sub funcw(uword ww) {
|
||||
cx16.r0++
|
||||
}
|
||||
|
||||
}"""
|
||||
|
||||
val result = compileText(Cx16Target(), true, src, writeAssembly = false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 17
|
||||
|
||||
val answerValue = (st[3] as Assignment).value
|
||||
answerValue shouldBe NumericLiteral(DataType.UWORD, 0.0, Position.DUMMY)
|
||||
|
||||
val funcarg1 = (st[4] as FunctionCallStatement).args.single()
|
||||
funcarg1 shouldBe NumericLiteral(DataType.UWORD, 0.0, Position.DUMMY)
|
||||
|
||||
val answer2Value = (st[8] as Assignment).value
|
||||
answer2Value shouldBe NumericLiteral(DataType.UWORD, 8.0, Position.DUMMY)
|
||||
|
||||
val funcarg2 = (st[9] as FunctionCallStatement).args.single()
|
||||
funcarg2 shouldBe NumericLiteral(DataType.UWORD, 8.0, Position.DUMMY)
|
||||
|
||||
val answer3ValueTc = (st[15] as Assignment).value as TypecastExpression
|
||||
answer3ValueTc.type shouldBe DataType.UWORD
|
||||
val answer3Value = answer3ValueTc.expression as FunctionCallExpression
|
||||
answer3Value.target.nameInSource shouldBe listOf("msb")
|
||||
answer3Value.args.single() shouldBe instanceOf<BinaryExpression>()
|
||||
|
||||
val funcarg3tc = (st[16] as FunctionCallStatement).args.single() as TypecastExpression
|
||||
funcarg3tc.type shouldBe DataType.UWORD
|
||||
val funcarg3 = funcarg3tc.expression as FunctionCallExpression
|
||||
funcarg3.target.nameInSource shouldBe listOf("msb")
|
||||
funcarg3.args.single() shouldBe instanceOf<BinaryExpression>()
|
||||
}
|
||||
})
|
||||
@@ -1088,4 +1088,24 @@ main {
|
||||
errors.errors[3] shouldContain "overflow"
|
||||
}
|
||||
|
||||
test("type fitting of const assignment values") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
&ubyte mapped = 8000
|
||||
mapped = 6144 >> 9
|
||||
ubyte @shared ubb = 6144 >> 9
|
||||
bool @shared bb = 6144
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), true, src, writeAssembly = false) shouldNotBe null
|
||||
|
||||
val src2="""
|
||||
main {
|
||||
sub start() {
|
||||
ubyte @shared ubb = 6144 >> 2 ; should still be type error
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), true, src2, writeAssembly = false) shouldBe null
|
||||
}
|
||||
})
|
||||
@@ -174,4 +174,22 @@ main {
|
||||
compileText(Cx16Target(), false, text, writeAssembly = true) shouldNotBe null
|
||||
compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
|
||||
test("return with a statement instead of a value is a syntax error") {
|
||||
val src="""
|
||||
main {
|
||||
|
||||
sub invalid() {
|
||||
return cx16.r0++
|
||||
}
|
||||
|
||||
sub start() {
|
||||
invalid()
|
||||
}
|
||||
}"""
|
||||
val errors=ErrorReporterForTests()
|
||||
compileText(C64Target(), false, src, writeAssembly = false, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 1
|
||||
errors.errors[0] shouldContain "statement"
|
||||
}
|
||||
})
|
||||
@@ -3,13 +3,11 @@ package prog8tests.ast
|
||||
import io.kotest.assertions.throwables.shouldThrow
|
||||
import io.kotest.core.spec.style.AnnotationSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
import prog8.code.core.SourceCode
|
||||
import prog8.code.core.SourceCode.Companion.LIBRARYFILEPREFIX
|
||||
import prog8tests.helpers.assumeNotExists
|
||||
import prog8tests.helpers.assumeReadableFile
|
||||
import prog8tests.helpers.fixturesDir
|
||||
import prog8tests.helpers.resourcesDir
|
||||
import prog8tests.helpers.*
|
||||
import kotlin.io.path.Path
|
||||
|
||||
|
||||
@@ -29,6 +27,14 @@ class TestSourceCode: AnnotationSpec() {
|
||||
src.toString().startsWith("prog8.code.core.SourceCode") shouldBe true
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFromStringDOSLineEndings() {
|
||||
val text = "main {\r\nline2\r\nline3\r\n}\r\n"
|
||||
val src = SourceCode.Text(text)
|
||||
src.text shouldNotBe text // because normalized line endings!
|
||||
src.text.split('\r', '\n').size shouldBe 5
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFromPathWithNonExistingPath() {
|
||||
val filename = "i_do_not_exist.p8"
|
||||
@@ -60,6 +66,17 @@ class TestSourceCode: AnnotationSpec() {
|
||||
src.isFromFilesystem shouldBe true
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFromPathWithExistingPathDOSLineEndings() {
|
||||
val text = "main {\r\nline2\r\nline3\r\n}\r"
|
||||
val filePath = outputDir.resolve("on_the_fly_test_" + text.hashCode().toUInt().toString(16) + ".p8")
|
||||
filePath.toFile().writeText(text)
|
||||
val path = assumeReadableFile(fixturesDir, filePath)
|
||||
val src = SourceCode.File(path)
|
||||
src.text shouldNotBe path.toFile().readText() // should be normalized!
|
||||
src.text.split('\r', '\n').size shouldBe 5
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFromPathWithExistingNonNormalizedPath() {
|
||||
val filename = "ast_simple_main.p8"
|
||||
|
||||
@@ -58,6 +58,21 @@ main {
|
||||
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
|
||||
test("array init size mismatch error") {
|
||||
val text="""
|
||||
main {
|
||||
sub start() {
|
||||
ubyte[10] uba = [1,2,3]
|
||||
bool[10] bba = [true, false, true]
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), false, text, writeAssembly = false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "size mismatch"
|
||||
errors.errors[1] shouldContain "size mismatch"
|
||||
}
|
||||
|
||||
test("invalid && operator") {
|
||||
val text="""
|
||||
main {
|
||||
|
||||
@@ -38,7 +38,7 @@ internal fun compileFile(
|
||||
outputDir = outputDir,
|
||||
errors = errors ?: ErrorReporterForTests(),
|
||||
splitWordArrays = false,
|
||||
breakpointCpuInstruction = false,
|
||||
breakpointCpuInstruction = null,
|
||||
printAst1 = false,
|
||||
printAst2 = false
|
||||
)
|
||||
|
||||
@@ -288,9 +288,6 @@ mylabel:
|
||||
|
||||
test("nesting with overlapping names is ok (doesn't work for 64tass)") {
|
||||
val src="""
|
||||
%import textio
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
main()
|
||||
|
||||
@@ -366,7 +366,7 @@ class TypecastExpression(var expression: Expression, var type: DataType, val imp
|
||||
val cv = expression.constValue(program) ?: return null
|
||||
val cast = cv.cast(type)
|
||||
return if(cast.isValid) {
|
||||
val newval = cast.valueOrZero()
|
||||
val newval = cast.value!!
|
||||
newval.linkParents(parent)
|
||||
return newval
|
||||
}
|
||||
@@ -558,21 +558,19 @@ class NumericLiteral(val type: DataType, // only numerical types allowed
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if(other==null || other !is NumericLiteral)
|
||||
return false
|
||||
return number==other.number
|
||||
else if(type!=DataType.BOOL && other.type!=DataType.BOOL)
|
||||
return number==other.number
|
||||
else
|
||||
return type==other.type && number==other.number
|
||||
}
|
||||
|
||||
operator fun compareTo(other: NumericLiteral): Int = number.compareTo(other.number)
|
||||
|
||||
class ValueAfterCast(val isValid: Boolean, val whyFailed: String?, private val value: NumericLiteral?) {
|
||||
fun valueOrZero() = if(isValid) value!! else NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
|
||||
fun linkParent(parent: Node) {
|
||||
value?.linkParents(parent)
|
||||
}
|
||||
}
|
||||
data class ValueAfterCast(val isValid: Boolean, val whyFailed: String?, val value: NumericLiteral?)
|
||||
|
||||
fun cast(targettype: DataType): ValueAfterCast {
|
||||
val result = internalCast(targettype)
|
||||
result.linkParent(this.parent)
|
||||
result.value?.linkParents(this.parent)
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -867,7 +865,7 @@ class ArrayLiteral(val type: InferredTypes.InferredType, // inferred because
|
||||
val castArray = value.map {
|
||||
val cast = (it as NumericLiteral).cast(elementType)
|
||||
if(cast.isValid)
|
||||
cast.valueOrZero() as Expression
|
||||
cast.value!! as Expression
|
||||
else
|
||||
return null // abort
|
||||
}.toTypedArray()
|
||||
@@ -876,12 +874,12 @@ class ArrayLiteral(val type: InferredTypes.InferredType, // inferred because
|
||||
else if(elementType in WordDatatypes && value.all { it is NumericLiteral || it is AddressOf || it is IdentifierReference}) {
|
||||
val castArray = value.map {
|
||||
when(it) {
|
||||
is AddressOf -> it as Expression
|
||||
is IdentifierReference -> it as Expression
|
||||
is AddressOf -> it
|
||||
is IdentifierReference -> it
|
||||
is NumericLiteral -> {
|
||||
val numcast = it.cast(elementType)
|
||||
if(numcast.isValid)
|
||||
numcast.valueOrZero() as Expression
|
||||
numcast.value!!
|
||||
else
|
||||
return null // abort
|
||||
}
|
||||
|
||||
@@ -175,12 +175,13 @@ One or more .p8 module files
|
||||
``-check``
|
||||
Quickly check the program for errors. No output will be produced.
|
||||
|
||||
``-breakinstr``
|
||||
Also output a CPU instruction for a ``%breakpoint``, as well as the entry in the vice monitor list file.
|
||||
``-breakinstr <instruction>``
|
||||
Also output the specified CPU instruction for a ``%breakpoint``, as well as the entry in the vice monitor list file.
|
||||
This can be useful on emulators/systems that don't parse the breakpoint information in the list file,
|
||||
such as the X16Emu emulator for the Commander X16. Prog8 then uses a STP instruction (65c02) to trigger a
|
||||
breakpoint in the debugger (if enabled with -debug on the emulator). On a 6502 CPU, this option
|
||||
will output a BRK instruction instead.
|
||||
such as the X16Emu emulator for the Commander X16.
|
||||
Useful instructions to consider are ``brk`` and ``stp``.
|
||||
For example for the Commander X16 emulator, ``stp`` is useful because it can actually tyrigger
|
||||
a breakpoint halt in the debugger when this is enabled by running the emulator with -debug.
|
||||
|
||||
``-expericodegen``
|
||||
Use experimental code generation backend (*incomplete*).
|
||||
|
||||
@@ -102,7 +102,8 @@ sys (part of syslib)
|
||||
the irqd status bit if it was cleared before entering).
|
||||
|
||||
``progend ()``
|
||||
Returns the last address of the program in memory + 1.
|
||||
Returns the last address of the program in memory + 1. This means: the memory address directly after all the program code and variables,
|
||||
including the uninitialized ones ("BSS" variables) and the uninitialized memory blocks reserved by the `memory()` function.
|
||||
Can be used to load dynamic data after the program, instead of hardcoding something.
|
||||
|
||||
``wait (uword jiffies)``
|
||||
@@ -525,9 +526,13 @@ The compiler needs it for various built-in system routines.
|
||||
cx16
|
||||
----
|
||||
This is available on *all targets*, it is always imported as part of syslib.
|
||||
On the Commander X16 this module contains a whole bunch of things specific to that machine.
|
||||
On the Commander X16 this module contains a *whole bunch* of things specific to that machine.
|
||||
It's way too much to include here, you have to study the
|
||||
`source code <https://github.com/irmen/prog8/tree/master/compiler/res/prog8lib/cx16/syslib.p8>`_
|
||||
to see what is there.
|
||||
|
||||
On the other targets, it only contains the definition of the 16 memory mapped virtual registers
|
||||
(cx16.r0 - cx16.r15) and the following two utility routines:
|
||||
(cx16.r0 - cx16.r15) and the following utility routines:
|
||||
|
||||
``save_virtual_registers()``
|
||||
save the values of all 16 virtual registers r0 - r15 in a buffer. Might be useful in an IRQ handler to avoid clobbering them.
|
||||
@@ -535,6 +540,10 @@ On the other targets, it only contains the definition of the 16 memory mapped vi
|
||||
``restore_virtual_registers()``
|
||||
restore the values of all 16 virtual registers r0 - r15 from the buffer. Might be useful in an IRQ handler to avoid clobbering them.
|
||||
|
||||
``cpu_is_65816()``
|
||||
Returns true if the CPU in the computer is a 65816, false otherwise (6502 cpu).
|
||||
|
||||
|
||||
bmx (cx16 only)
|
||||
----------------
|
||||
Routines to load and save "BMX" files, the CommanderX16 bitmap file format.
|
||||
|
||||
@@ -232,7 +232,8 @@ Integers
|
||||
^^^^^^^^
|
||||
|
||||
Integers are 8 or 16 bit numbers and can be written in normal decimal notation,
|
||||
in hexadecimal and in binary notation. You can use underscores to group digits to make long numbers more readable.
|
||||
in hexadecimal and in binary notation. There is no octal notation.
|
||||
You can use underscores to group digits to make long numbers more readable.
|
||||
A single character in single quotes such as ``'a'`` is translated into a byte integer,
|
||||
which is the PETSCII value for that character.
|
||||
|
||||
|
||||
@@ -329,22 +329,21 @@ Variable declarations
|
||||
Variables should be declared with their exact type and size so the compiler can allocate storage
|
||||
for them. You can give them an initial value as well. That value can be a simple literal value,
|
||||
or an expression. If you don't provide an initial value yourself, zero will be used.
|
||||
Add a ``@zp`` zeropage-tag, to tell the compiler to prioritize it
|
||||
when selecting variables to be put into zeropage (but no guarantees). If the ZP is full,
|
||||
the variable will be allocated in normal memory elsewhere.
|
||||
Add a ``@requirezp`` tag to force the variable in zeropage, but if the ZP is full,
|
||||
the compilation will fail.
|
||||
Add a ``@shared`` shared-tag, to tell the compiler that the variable is shared
|
||||
with some assembly code and that it should not be optimized away if not used elsewhere.
|
||||
For (u)word arrays, add ``@split`` to make the array layout in memory as 2 split arrays
|
||||
one with the LSBs one with the MSBs of the word values.
|
||||
The syntax for variable declarations is::
|
||||
|
||||
<datatype> [ @tag ] <variable name> [ = <initial value> ]
|
||||
|
||||
The syntax is::
|
||||
Here are the tags you can add to a variable:
|
||||
|
||||
<datatype> [ @type-tag ] <variable name> [ = <initial value> ]
|
||||
========== ======
|
||||
Tag Effect
|
||||
========== ======
|
||||
@zp prioritize the variable for putting it into Zero page. No guarantees; if ZP is full the variable will be placed in another memory location.
|
||||
@requirezp force the variable into Zero page. If ZP is full, compilation will fail.
|
||||
@shared means the variable is shared with some assembly code and that it cannot be optimized away if not used elsewhere.
|
||||
@split (only valid on (u)word arrays) Makes the array to be placed in memory as 2 separate byte arrays; one with the LSBs one with the MSBs of the word values. May improve performance.
|
||||
========== ======
|
||||
|
||||
where type-tag is one of the tags mentioned earlier.
|
||||
|
||||
For boolean and numeric variables, you can actually declare them in one go by listing the names in a comma separated list.
|
||||
Type tags, and the optional initialization value, are applied equally to all variables in such a list.
|
||||
@@ -404,6 +403,8 @@ type identifier type storage size example var declara
|
||||
**arrays:** you can split an array initializer list over several lines if you want. When an initialization
|
||||
value is given, the array size in the declaration can be omitted.
|
||||
|
||||
**numbers:** unless prefixed for hex or binary as described below, all numbers are decimal numbers. There is no octal notation.
|
||||
|
||||
**hexadecimal numbers:** you can use a dollar prefix to write hexadecimal numbers: ``$20ac``
|
||||
|
||||
**binary numbers:** you can use a percent prefix to write binary numbers: ``%10010011``
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
(after merge in boolean): move all "OperatorXinplace" from expressionGen to AssignmentGen, see if we can get rid of the Result return type.
|
||||
|
||||
...
|
||||
|
||||
|
||||
@@ -41,7 +43,6 @@ Compiler:
|
||||
Once new codegen is written that is based on the IR, this point is mostly moot anyway as that will have its own dead code removal on the IR level.
|
||||
- Zig-like try-based error handling where the V flag could indicate error condition? and/or BRK to jump into monitor on failure? (has to set BRK vector for that) But the V flag is also set on certain normal instructions
|
||||
- generate WASM to eventually run prog8 on a browser canvas? Use binaryen toolkit and/or my binaryen kotlin library?
|
||||
- add Vic20 target?
|
||||
- split words arrays all()
|
||||
- split words arrays sort()
|
||||
|
||||
@@ -62,6 +63,7 @@ Optimizations:
|
||||
those checks should probably be removed, or be made permanent
|
||||
- optimizeCommonSubExpressions: currently only looks in expressions on a single line, could search across multiple expressions
|
||||
|
||||
|
||||
STRUCTS again?
|
||||
--------------
|
||||
|
||||
@@ -78,6 +80,7 @@ What if we were to re-introduce Structs in prog8? Some thoughts:
|
||||
Other language/syntax features to think about
|
||||
---------------------------------------------
|
||||
|
||||
- remove ++/-- (just use Pythonesque x+=1) OR make ++/-- into a postfix expression (but then we need a prefix variant of them too?) maybe via a new builtin function like postincr__w() etc?
|
||||
- support for assigning multiple return values from romsub/asmsub to multiple variables.
|
||||
- add (rom/ram)bank support to romsub. A call will then automatically switch banks, use callfar and something else when in banked ram.
|
||||
challenges: how to not make this too X16 specific? How does the compiler know what bank to switch (ram/rom)?
|
||||
|
||||
@@ -1,8 +1,115 @@
|
||||
%import textio
|
||||
%import floats
|
||||
%import math
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
; ubyte[10] az
|
||||
ubyte @shared offset=4
|
||||
uword @shared az = $4000
|
||||
ubyte @shared value = 22
|
||||
|
||||
az[20] |= 99
|
||||
az[21] &= 99
|
||||
az[22] ^= 99
|
||||
az[23] += 99
|
||||
az[24] -= 99
|
||||
; az[2000] |= 99
|
||||
; az[value] |= 99
|
||||
; az[cx16.r0] |= 99
|
||||
|
||||
; cx16.r0L = az[200]
|
||||
; cx16.r1L = az[2000]
|
||||
; cx16.r0L = az[value]
|
||||
; cx16.r0H = value*4 + az[value]
|
||||
; cx16.r0L = az[value] + value*4
|
||||
;
|
||||
; @($4004) = 99
|
||||
; az[4]--
|
||||
; @(az + offset)--
|
||||
; txt.print_ub(@($4004))
|
||||
; txt.nl()
|
||||
; az[4]++
|
||||
; @(az+offset)++
|
||||
; txt.print_ub(@($4004))
|
||||
; txt.nl()
|
||||
; cx16.r0L = az[4] + value*5
|
||||
; cx16.r1L = value*5 + az[4]
|
||||
|
||||
|
||||
; ubyte @shared xx
|
||||
; ubyte[3] ubarr
|
||||
; uword[3] @split uwarr
|
||||
; byte[3] sbarr
|
||||
; bool[3] barr
|
||||
; float[3] flarr
|
||||
; bool @shared bb
|
||||
; uword ptr = &ubarr
|
||||
;
|
||||
; ptr[1]++
|
||||
; ptr[1]++
|
||||
; ptr[1]--
|
||||
; txt.print_ub(ubarr[1])
|
||||
; txt.nl()
|
||||
; ptr[1]+=4
|
||||
; ptr[1]-=3
|
||||
; txt.print_ub(ubarr[1])
|
||||
; txt.nl()
|
||||
|
||||
; sbarr[1] = sbarr[1] == 0
|
||||
; sbarr[1] = sbarr[1] != 0
|
||||
; sbarr[1] = sbarr[1] < 0
|
||||
; sbarr[1] = sbarr[1] <= 0
|
||||
; sbarr[1] = sbarr[1] > 0
|
||||
; sbarr[1] = sbarr[1] >= 0
|
||||
;
|
||||
; xx = 1
|
||||
;
|
||||
; sbarr[xx] = sbarr[xx] == 0
|
||||
; sbarr[xx] = sbarr[xx] != 0
|
||||
; sbarr[xx] = sbarr[xx] < 0
|
||||
; sbarr[xx] = sbarr[xx] <= 0
|
||||
; sbarr[xx] = sbarr[xx] > 0
|
||||
; sbarr[xx] = sbarr[xx] >= 0
|
||||
|
||||
; sbarr[1] = sbarr[1] == 2
|
||||
; sbarr[1] = sbarr[1] != 2
|
||||
; sbarr[1] = sbarr[1] < 2
|
||||
; sbarr[1] = sbarr[1] <= 2
|
||||
; sbarr[1] = sbarr[1] > 2
|
||||
; sbarr[1] = sbarr[1] >= 2
|
||||
; xx = 1
|
||||
; sbarr[xx] = sbarr[xx] == 2
|
||||
; sbarr[xx] = sbarr[xx] != 2
|
||||
; sbarr[xx] = sbarr[xx] < 2
|
||||
; sbarr[xx] = sbarr[xx] <= 2
|
||||
; sbarr[xx] = sbarr[xx] > 2
|
||||
; sbarr[xx] = sbarr[xx] >= 2
|
||||
|
||||
; ubarr[1] = ubarr[1] == 2
|
||||
; ubarr[1] = ubarr[1] < 2
|
||||
; ubarr[1] = ubarr[1] <= 2
|
||||
; ubarr[1] = ubarr[1] > 3
|
||||
; ubarr[1] = ubarr[1] >= 3
|
||||
|
||||
; barr[1] = barr[0] and barr[2]
|
||||
; barr[1] = barr[0] or barr[2]
|
||||
; barr[1] = barr[0] xor barr[2]
|
||||
; barr[1] = not barr[0]
|
||||
;
|
||||
; ubarr[1] = 999
|
||||
; ubarr[1] = ubarr[1]==999
|
||||
; txt.print_uw(ubarr[1])
|
||||
;
|
||||
; barr[1] = barr[1] and bb
|
||||
; barr[1] = barr[1] or bb
|
||||
; barr[1] = barr[1] xor bb
|
||||
;
|
||||
; bb = bb and barr[1]
|
||||
; bb = bb or barr[1]
|
||||
; bb = bb xor barr[1]
|
||||
; bb = not bb
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -5,4 +5,4 @@ org.gradle.daemon=true
|
||||
kotlin.code.style=official
|
||||
javaVersion=11
|
||||
kotlinVersion=1.9.22
|
||||
version=10.0
|
||||
version=10.1
|
||||
@@ -42,7 +42,7 @@ class RequestParser : Take {
|
||||
asmListfile = false,
|
||||
experimentalCodegen = false,
|
||||
splitWordArrays = false,
|
||||
breakpointCpuInstruction = false,
|
||||
breakpointCpuInstruction = null,
|
||||
printAst1 = false,
|
||||
printAst2 = false,
|
||||
varsHighBank = null,
|
||||
|
||||
@@ -16,7 +16,7 @@ Program to execute is not stored in the system memory, it's just a separate list
|
||||
Value stack, max 128 entries of 1 byte each.
|
||||
Status flags: Carry, Zero, Negative. NOTE: status flags are only affected by the CMP instruction or explicit CLC/SEC,
|
||||
LOAD instructions DO affect the Z and N flags.
|
||||
INC/DEC instructions DO affect the Z and N flags,
|
||||
INC/DEC/NEG instructions DO affect the Z and N flags,
|
||||
other instructions only affect Z an N flags if the value in a result register is written.
|
||||
See OpcodesThatSetStatusbits
|
||||
|
||||
@@ -444,6 +444,8 @@ val OpcodesThatSetStatusbitsButNotCarry = arrayOf(
|
||||
Opcode.LOADX,
|
||||
Opcode.LOADIX,
|
||||
Opcode.LOADR,
|
||||
Opcode.NEG,
|
||||
Opcode.NEGM,
|
||||
Opcode.INC,
|
||||
Opcode.INCM,
|
||||
Opcode.DEC,
|
||||
@@ -456,7 +458,7 @@ val OpcodesThatSetStatusbitsButNotCarry = arrayOf(
|
||||
Opcode.OR,
|
||||
Opcode.XORM,
|
||||
Opcode.XORR,
|
||||
Opcode.XOR
|
||||
Opcode.XOR,
|
||||
)
|
||||
|
||||
val OpcodesThatDependOnCarry = arrayOf(
|
||||
|
||||
@@ -974,8 +974,16 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
|
||||
private fun InsNEG(i: IRInstruction) {
|
||||
when(i.type!!) {
|
||||
IRDataType.BYTE -> registers.setUB(i.reg1!!, (-registers.getUB(i.reg1!!).toInt()).toUByte())
|
||||
IRDataType.WORD -> registers.setUW(i.reg1!!, (-registers.getUW(i.reg1!!).toInt()).toUShort())
|
||||
IRDataType.BYTE -> {
|
||||
val value = -registers.getUB(i.reg1!!).toInt()
|
||||
registers.setUB(i.reg1!!, value.toUByte())
|
||||
statusbitsNZ(value, IRDataType.BYTE)
|
||||
}
|
||||
IRDataType.WORD -> {
|
||||
val value = -registers.getUW(i.reg1!!).toInt()
|
||||
registers.setUW(i.reg1!!, value.toUShort())
|
||||
statusbitsNZ(value, IRDataType.WORD)
|
||||
}
|
||||
IRDataType.FLOAT -> registers.setFloat(i.fpReg1!!, -registers.getFloat(i.fpReg1!!))
|
||||
}
|
||||
nextPc()
|
||||
@@ -984,8 +992,16 @@ class VirtualMachine(irProgram: IRProgram) {
|
||||
private fun InsNEGM(i: IRInstruction) {
|
||||
val address = i.address!!
|
||||
when(i.type!!) {
|
||||
IRDataType.BYTE -> memory.setUB(address, (-memory.getUB(address).toInt()).toUByte())
|
||||
IRDataType.WORD -> memory.setUW(address, (-memory.getUW(address).toInt()).toUShort())
|
||||
IRDataType.BYTE -> {
|
||||
val value = -memory.getUB(address).toInt()
|
||||
memory.setUB(address, value.toUByte())
|
||||
statusbitsNZ(value, IRDataType.BYTE)
|
||||
}
|
||||
IRDataType.WORD -> {
|
||||
val value = -memory.getUW(address).toInt()
|
||||
memory.setUW(address, value.toUShort())
|
||||
statusbitsNZ(value, IRDataType.WORD)
|
||||
}
|
||||
IRDataType.FLOAT -> memory.setFloat(address, -memory.getFloat(address))
|
||||
}
|
||||
nextPc()
|
||||
|
||||
Reference in new issue
Block a user