diff --git a/compiler/res/prog8lib/prog8lib.asm b/compiler/res/prog8lib/prog8lib.asm index 9276d695e..dc81452a9 100644 --- a/compiler/res/prog8lib/prog8lib.asm +++ b/compiler/res/prog8lib/prog8lib.asm @@ -35,6 +35,17 @@ init_system .proc rts .pend + +read_byte_from_address .proc + ; -- read the byte from the memory address on the top of the stack, return in A (stack remains unchanged) + lda c64.ESTACK_LO+1,x + ldy c64.ESTACK_HI+1,x + sta (+) +1 + sty (+) +2 ++ lda $ffff ; modified + rts + .pend + add_a_to_zpword .proc ; -- add ubyte in A to the uword in c64.SCRATCH_ZPWORD1 diff --git a/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt b/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt index 89689539a..0ee8e568b 100644 --- a/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt @@ -7,10 +7,12 @@ import prog8.ast.Program import prog8.ast.base.* import prog8.ast.expressions.* import prog8.ast.statements.* +import prog8.compiler.target.c64.AssemblyProgram import prog8.functions.BuiltinFunctions internal class AstIdentifiersChecker(private val program: Program) : IAstModifyingVisitor { + private val checkResult: MutableList = mutableListOf() private var blocks = mutableMapOf() @@ -64,6 +66,11 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi // the builtin functions can't be redefined checkResult.add(NameError("builtin function cannot be redefined", decl.position)) + if(decl.name in AssemblyProgram.reservedNames) + checkResult.add(NameError("can't use a symbol name reserved by the assembler program", decl.position)) + if(decl.name in AssemblyProgram.opcodeNames) + checkResult.add(NameError("can't use a cpu opcode name as a symbol", decl.position)) + // is it a struct variable? then define all its struct members as mangled names, // and include the original decl as well. if(decl.datatype==DataType.STRUCT) { @@ -102,8 +109,9 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi // the builtin functions can't be redefined checkResult.add(NameError("builtin function cannot be redefined", subroutine.position)) } else { - if (subroutine.parameters.any { it.name in BuiltinFunctions }) - checkResult.add(NameError("builtin function name cannot be used as parameter", subroutine.position)) + // already reported elsewhere: + // if (subroutine.parameters.any { it.name in BuiltinFunctions }) + // checkResult.add(NameError("builtin function name cannot be used as parameter", subroutine.position)) val existing = program.namespace.lookup(listOf(subroutine.name), subroutine) if (existing != null && existing !== subroutine) diff --git a/compiler/src/prog8/compiler/target/c64/AssemblyProgram.kt b/compiler/src/prog8/compiler/target/c64/AssemblyProgram.kt index 6e64ad1a6..533e88cc4 100644 --- a/compiler/src/prog8/compiler/target/c64/AssemblyProgram.kt +++ b/compiler/src/prog8/compiler/target/c64/AssemblyProgram.kt @@ -9,6 +9,25 @@ class AssemblyProgram(val name: String) { private val assemblyFile = "$name.asm" private val viceMonListFile = "$name.vice-mon-list" + companion object { + // reserved by the 64tass assembler (on top of prog8"s own reserved names) + val reservedNames = setOf("bit", "bits", "bool", "bytes", "code", "dict", "gap", "int", "list", "tuple", "type", + "trunc", " frac", "cbrt", "log10", "log", "exp", "pow", "asin", "sinh", "acos", "cosh", "tanh", "hypot", + "atan2", "sign", "binary", "format", "random", "range", "repr", "size", "sort") + + // 6502 opcodes (including aliases and illegal opcodes), these cannot be used as variable names either + val opcodeNames = setOf("adc", "ahx", "alr", "anc", "and", "ane", "arr", "asl", "asr", "axs", "bcc", "bcs", + "beq", "bge", "bit", "blt", "bmi", "bne", "bpl", "brk", "bvc", "bvs", "clc", + "cld", "cli", "clv", "cmp", "cpx", "cpy", "dcm", "dcp", "dec", "dex", "dey", + "eor", "gcc", "gcs", "geq", "gge", "glt", "gmi", "gne", "gpl", "gvc", "gvs", + "inc", "ins", "inx", "iny", "isb", "isc", "jam", "jmp", "jsr", "lae", "las", + "lax", "lda", "lds", "ldx", "ldy", "lsr", "lxa", "nop", "ora", "pha", "php", + "pla", "plp", "rla", "rol", "ror", "rra", "rti", "rts", "sax", "sbc", "sbx", + "sec", "sed", "sei", "sha", "shl", "shr", "shs", "shx", "shy", "slo", "sre", + "sta", "stx", "sty", "tas", "tax", "tay", "tsx", "txa", "txs", "tya", "xaa") + } + + fun assemble(options: CompilationOptions) { // add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch", "-Wall", "-Wno-strict-bool", @@ -41,7 +60,7 @@ class AssemblyProgram(val name: String) { private fun generateBreakpointList() { // builds list of breakpoints, appends to monitor list file val breakpoints = mutableListOf() - val pattern = Regex("""al (\w+) \S+_prog8_breakpoint_\d+.?""") // gather breakpoints by the source label that's generated for them + val pattern = Regex("""al (\w+) \S+_prog8_breakpoint_\d+.?""") // gather breakpoints by the source label that"s generated for them for(line in File(viceMonListFile).readLines()) { val match = pattern.matchEntire(line) if(match!=null) diff --git a/compiler/src/prog8/compiler/target/c64/codegen2/AsmGen2.kt b/compiler/src/prog8/compiler/target/c64/codegen2/AsmGen2.kt index 9bf15d52c..8eebcf652 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen2/AsmGen2.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen2/AsmGen2.kt @@ -644,19 +644,18 @@ internal class AsmGen2(val program: Program, val scopedParamVar = (sub.scopedname+"."+paramVar.name).split(".") val target = AssignTarget(null, IdentifierReference(scopedParamVar, sub.position), null, null, sub.position) target.linkParents(value.parent) - val literal = value as? NumericLiteralValue - when { - literal!=null -> { + when (value) { + is NumericLiteralValue -> { // optimize when the argument is a constant literal when(arg.value.type) { - in ByteDatatypes -> assignFromByteConstant(target, literal.number.toShort()) - in WordDatatypes -> assignFromWordConstant(target, literal.number.toInt()) - DataType.FLOAT -> assignFromFloatConstant(target, literal.number.toDouble()) + in ByteDatatypes -> assignFromByteConstant(target, value.number.toShort()) + in WordDatatypes -> assignFromWordConstant(target, value.number.toInt()) + DataType.FLOAT -> assignFromFloatConstant(target, value.number.toDouble()) in PassByReferenceDatatypes -> throw AssemblyError("can't pass string/array as arguments?") else -> throw AssemblyError("weird arg datatype") } } - value is IdentifierReference -> { + is IdentifierReference -> { // optimize when the argument is a variable when (arg.value.type) { in ByteDatatypes -> assignFromByteVariable(target, value) @@ -666,7 +665,29 @@ internal class AsmGen2(val program: Program, else -> throw AssemblyError("weird arg datatype") } } - else -> TODO("non-constant sub arg $arg") + is RegisterExpr -> { + assignFromRegister(target, value.register) + } + is DirectMemoryRead -> { + when(value.addressExpression) { + is NumericLiteralValue -> { + val address = (value.addressExpression as NumericLiteralValue).number.toInt() + assignFromMemoryByte(target, address, null) + } + is IdentifierReference -> { + assignFromMemoryByte(target, null, value.addressExpression as IdentifierReference) + } + else -> { + translateExpression(value.addressExpression) + out(" jsr prog8_lib.read_byte_from_address | inx") + assignFromRegister(target, Register.A) + } + } + } + else -> { + translateExpression(value) + assignFromEvalResult(target) + } } } else { // pass arg via a register parameter @@ -1149,7 +1170,21 @@ internal class AsmGen2(val program: Program, } private fun translateExpression(expr: DirectMemoryRead) { - TODO("memread $expr") + when(expr.addressExpression) { + is NumericLiteralValue -> { + val address = (expr.addressExpression as NumericLiteralValue).number.toInt() + out(" lda ${address.toHex()} | sta $ESTACK_LO_HEX,x | dex") + } + is IdentifierReference -> { + val sourceName = asmIdentifierName(expr.addressExpression as IdentifierReference) + out(" lda $sourceName | sta $ESTACK_LO_HEX,x | dex") + } + else -> { + translateExpression(expr.addressExpression) + out(" jsr prog8_lib.read_byte_from_address") + out(" sta $ESTACK_LO_PLUS1_HEX,x") + } + } } private fun translateExpression(expr: NumericLiteralValue) { @@ -1514,9 +1549,9 @@ internal class AsmGen2(val program: Program, out(""" inx lda $ESTACK_LO_HEX,x + ldy $ESTACK_HI_HEX,x sta (+) +1 - lda $ESTACK_HI_HEX,x - sta (+) +2 + sty (+) +2 lda $sourceName + sta ${65535.toHex()} ; modified """) @@ -1554,7 +1589,97 @@ internal class AsmGen2(val program: Program, } } } - else -> out("; TODO assign register $register to $target") // TODO + target.memoryAddress!=null -> { + val addressExpr = target.memoryAddress.addressExpression + val addressLv = addressExpr as? NumericLiteralValue + val registerName = register.name.toLowerCase() + when { + addressLv != null -> out(" st$registerName ${addressLv.number.toHex()}") + addressExpr is IdentifierReference -> { + val targetName = asmIdentifierName(addressExpr) + out(" st$registerName $targetName") + } + else -> { + translateExpression(addressExpr) + when (register) { + Register.A -> out(" tay") + Register.X -> throw AssemblyError("can't use X register here") + Register.Y -> { + } + } + out(""" + inx + lda $ESTACK_LO_HEX,x + sta (+) +1 + lda $ESTACK_HI_HEX,x + sta (+) +2 ++ sty ${65535.toHex()} ; modified + """) + } + } + } + targetArrayIdx!=null -> { + val index = targetArrayIdx.arrayspec.index + val targetName = asmIdentifierName(targetArrayIdx.identifier) + when (index) { + is NumericLiteralValue -> { + val memindex = index.number.toInt() + when(register) { + Register.A -> out(" sta $targetName+$memindex") + Register.X -> out(" stx $targetName+$memindex") + Register.Y -> out(" sty $targetName+$memindex") + } + } + is RegisterExpr -> { + when(register) { + Register.A -> out(" sta ${C64Zeropage.SCRATCH_B1}") + Register.X -> out(" stx ${C64Zeropage.SCRATCH_B1}") + Register.Y -> out(" sty ${C64Zeropage.SCRATCH_B1}") + } + when(index.register) { + Register.A -> {} + Register.X -> out(" txa") + Register.Y -> out(" tya") + } + out(""" + tay + lda ${C64Zeropage.SCRATCH_B1} + sta $targetName,y + """) + } + is IdentifierReference -> { + when(register) { + Register.A -> out(" sta ${C64Zeropage.SCRATCH_B1}") + Register.X -> out(" stx ${C64Zeropage.SCRATCH_B1}") + Register.Y -> out(" sty ${C64Zeropage.SCRATCH_B1}") + } + out(""" + lda ${asmIdentifierName(index)} + tay + lda ${C64Zeropage.SCRATCH_B1} + sta $targetName,y + """) + } + else -> { + saveRegister(register) + translateExpression(index) + restoreRegister(register) + when(register) { + Register.A -> out(" sta ${C64Zeropage.SCRATCH_B1}") + Register.X -> out(" stx ${C64Zeropage.SCRATCH_B1}") + Register.Y -> out(" sty ${C64Zeropage.SCRATCH_B1}") + } + out(""" + inx + lda $ESTACK_LO_HEX,x + tay + lda ${C64Zeropage.SCRATCH_B1} + sta $targetName,y + """) + } + } + } + else -> TODO("assign register $register to $target") } } diff --git a/examples/test.p8 b/examples/test.p8 index 83a69dae0..a5707135a 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -6,22 +6,27 @@ sub start() { - str s1 = "hello" - str s2 = "hello" - str s3 = "hello" - str s4 = "hello" + carry(1) + carry(0) +; ubyte bb = @($d020)+4 +; ubyte bb2 = @($d020+A)+4 +; +; subje(55) +; subje(@($d020+bb)) +; subje(A) +; subje(bb) +; subje(bb+43) + } - if true { - c64scr.print("irmen") - c64scr.print("hello") - c64scr.print("hello2") - } - - if true { - c64scr.print("irmen") - c64scr.print("hello") - c64scr.print("hello2") - } + sub carry(ubyte cc) { + A=cc + if A!=0 + c64scr.print("carry set\n") + else + c64scr.print("carry clear\n") + } + sub subje(ubyte arg) { + A=arg } }