From 2b9316c4fff5ef33340676da96a9c7ef57237bc0 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Wed, 23 Sep 2020 22:29:21 +0200 Subject: [PATCH] reworked program init logic so that it is included as the first thing inside main.start itself, to allow better stand alone asm --- compiler/res/prog8lib/c64/syslib.p8 | 11 +++ compiler/res/version.txt | 2 +- .../compiler/BeforeAsmGenerationAstChanger.kt | 10 ++ .../compiler/target/CompilationTarget.kt | 3 + .../compiler/target/c64/codegen/AsmGen.kt | 93 ++++++++++++------- docs/source/todo.rst | 3 +- examples/test.p8 | 3 +- 7 files changed, 84 insertions(+), 41 deletions(-) diff --git a/compiler/res/prog8lib/c64/syslib.p8 b/compiler/res/prog8lib/c64/syslib.p8 index 8af3f0754..b5744606b 100644 --- a/compiler/res/prog8lib/c64/syslib.p8 +++ b/compiler/res/prog8lib/c64/syslib.p8 @@ -270,6 +270,17 @@ asmsub reset_system() { }} } +asmsub disable_runstop_and_charsetswitch() { + %asm {{ + lda #$80 + lda #$80 + sta 657 ; disable charset switching + lda #239 + sta 808 ; disable run/stop key + rts + }} +} + asmsub set_irqvec_excl() clobbers(A) { %asm {{ sei diff --git a/compiler/res/version.txt b/compiler/res/version.txt index 69df05f33..2bc5dfacf 100644 --- a/compiler/res/version.txt +++ b/compiler/res/version.txt @@ -1 +1 @@ -4.3 +4.4-SNAPSHOT diff --git a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt index fe19849fd..fdc23c6b8 100644 --- a/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt +++ b/compiler/src/prog8/compiler/BeforeAsmGenerationAstChanger.kt @@ -8,6 +8,7 @@ import prog8.ast.expressions.* import prog8.ast.processing.AstWalker import prog8.ast.processing.IAstModification import prog8.ast.statements.* +import prog8.compiler.target.c64.codegen.programInitializationRoutineName internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: ErrorReporter) : AstWalker() { @@ -107,6 +108,15 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E mods += IAstModification.InsertAfter(outerStatements[subroutineStmtIdx - 1], returnStmt, outerScope as Node) } + // if it's the program's start subroutine, insert a call to the initalization logic (if it's not there already) + if(subroutine.name=="start" && subroutine.definingBlock().name=="main") { + val first = subroutine.statements.firstOrNull() as? FunctionCallStatement + if(first==null || first.target.nameInSource != listOf(programInitializationRoutineName)) { + val call = FunctionCallStatement(IdentifierReference(listOf(programInitializationRoutineName), subroutine.position), mutableListOf(), true, subroutine.position) + mods += IAstModification.InsertFirst(call, subroutine) + } + } + return mods } diff --git a/compiler/src/prog8/compiler/target/CompilationTarget.kt b/compiler/src/prog8/compiler/target/CompilationTarget.kt index 8baf073a3..b3876b7e4 100644 --- a/compiler/src/prog8/compiler/target/CompilationTarget.kt +++ b/compiler/src/prog8/compiler/target/CompilationTarget.kt @@ -19,6 +19,7 @@ internal interface CompilationTarget { fun asmGenerator(program: Program, errors: ErrorReporter, zp: Zeropage, options: CompilationOptions, path: Path): IAssemblyGenerator val initProcName: String? val resetProcName: String? + val disableRunStopProcName: String? companion object { lateinit var instance: CompilationTarget @@ -37,6 +38,7 @@ internal object C64Target: CompilationTarget { AsmGen(program, errors, zp, options, path) override val initProcName = "c64.init_system" override val resetProcName = "c64.reset_system" + override val disableRunStopProcName = "c64.disable_runstop_and_charsetswitch" } internal object Cx16Target: CompilationTarget { @@ -50,4 +52,5 @@ internal object Cx16Target: CompilationTarget { AsmGen(program, errors, zp, options, path) override val initProcName = "cx16.init_system" override val resetProcName = "cx16.reset_system" + override val disableRunStopProcName = null } diff --git a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt index fb61d722a..8673e3c0c 100644 --- a/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/codegen/AsmGen.kt @@ -29,6 +29,9 @@ import java.util.* import kotlin.math.absoluteValue +internal const val programInitializationRoutineName = "prog8_initialize_program" + + internal class AsmGen(private val program: Program, val errors: ErrorReporter, val zeropage: Zeropage, @@ -50,11 +53,13 @@ internal class AsmGen(private val program: Program, private val assignmentAsmGen = AssignmentAsmGen(program, this) private val expressionsAsmGen = ExpressionsAsmGen(program, this) internal val loopEndLabels = ArrayDeque() - internal val blockLevelVarInits = mutableMapOf>() + private val blockLevelVarInits = mutableMapOf>() + private val programInitLines = mutableListOf() override fun compileToAssembly(optimize: Boolean): IAssemblyProgram { assemblyLines.clear() loopEndLabels.clear() + programInitLines.clear() println("Generating assembly code... ") @@ -81,6 +86,7 @@ internal class AsmGen(private val program: Program, return AssemblyProgram(program.name, outputDir) } + private fun header() { val ourName = this.javaClass.name val cpu = when(CompilationTarget.instance.machine.cpu) { @@ -120,16 +126,12 @@ internal class AsmGen(private val program: Program, out(" .null $9e, format(' %d ', _prog8_entrypoint), $3a, $8f, ' prog8 by idj'") out("+\t.word 0") out("_prog8_entrypoint\t; assembly code starts here\n") - out(" tsx") - out(" stx prog8_lib.orig_stackpointer") if(!CompilationTarget.instance.initProcName.isNullOrEmpty()) out(" jsr ${CompilationTarget.instance.initProcName}") } options.output == OutputType.PRG -> { out("; ---- program without basic sys call ----") out("* = ${program.actualLoadAddress.toHex()}\n") - out(" tsx") - out(" stx prog8_lib.orig_stackpointer") if(!CompilationTarget.instance.initProcName.isNullOrEmpty()) out(" jsr ${CompilationTarget.instance.initProcName}") } @@ -138,37 +140,49 @@ internal class AsmGen(private val program: Program, out("* = ${program.actualLoadAddress.toHex()}\n") } } + out(" jmp main.start ; start program / force start proc to be included") if (zeropage.exitProgramStrategy != Zeropage.ExitProgramStrategy.CLEAN_EXIT) { - // disable shift-commodore charset switching and run/stop key - out(" lda #$80") - out(" lda #$80") - out(" sta 657\t; disable charset switching") - out(" lda #239") - out(" sta 808\t; disable run/stop key") + if(!CompilationTarget.instance.disableRunStopProcName.isNullOrEmpty()) + programInitLines.add(" jsr ${CompilationTarget.instance.disableRunStopProcName}") } - out(" ldx #\$ff\t; init estack pointer") - - out(" ; initialize the variables in each block that has globals") + programInitLines.add(" ; initialize the variables in each block that has globals") + programInitLines.add(" cld") + programInitLines.add(" clv") program.allBlocks().forEach { if(it.statements.filterIsInstance().any { vd->vd.value!=null && vd.type==VarDeclType.VAR && vd.datatype in NumericDatatypes}) - out(" jsr ${it.name}.prog8_init_vars") + programInitLines.add(" jsr ${it.name}.prog8_init_vars") } - - out(" clc") - when (zeropage.exitProgramStrategy) { - Zeropage.ExitProgramStrategy.CLEAN_EXIT -> { - out(" jmp main.start\t; jump to program entrypoint") - } - Zeropage.ExitProgramStrategy.SYSTEM_RESET -> { - out(" jsr main.start\t; call program entrypoint") - out(" jmp ${CompilationTarget.instance.resetProcName}") - } + if (zeropage.exitProgramStrategy == Zeropage.ExitProgramStrategy.SYSTEM_RESET) { + // push the reset routine onto the cpu stack so that it is executed when the program does an rts + programInitLines.addAll(listOf( + " tsx", + " lda $0102,x", + " pha", + " lda $0103,x", + " pha", + " lda #>(${CompilationTarget.instance.resetProcName}-1)", + " sta $0102,x", + " lda #<(${CompilationTarget.instance.resetProcName}-1)", + " sta $0103,x", + )) } + programInitLines.add(" ldx #\$ff\t; init estack pointer") + programInitLines.add(" rts") } private fun footer() { + // the program preamble or initialization code + out("") + out("; program initialization") + out(programInitializationRoutineName) + for(line in programInitLines) { + out(line) + } + out("") + + // the global list of all floating point constants for the whole program out("; global float constants") for (flt in globalFloatConsts) { @@ -618,18 +632,25 @@ $save .byte 0 is InlineAssembly -> translate(stmt) is FunctionCallStatement -> { val functionName = stmt.target.nameInSource.last() - val builtinFunc = BuiltinFunctions[functionName] - if(builtinFunc!=null) { - builtinFunctionsAsmGen.translateFunctioncallStatement(stmt, builtinFunc) + if(functionName == programInitializationRoutineName) { + out(""" + tsx + stx prog8_lib.orig_stackpointer + jsr $functionName""") } else { - functioncallAsmGen.translateFunctionCall(stmt) - // discard any results from the stack: - val sub = stmt.target.targetSubroutine(program.namespace)!! - val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters) - for((t, reg) in returns) { - if(reg.stack) { - if (t in IntegerDatatypes || t in PassByReferenceDatatypes) out(" inx") - else if (t == DataType.FLOAT) out(" inx | inx | inx") + val builtinFunc = BuiltinFunctions[functionName] + if (builtinFunc != null) { + builtinFunctionsAsmGen.translateFunctioncallStatement(stmt, builtinFunc) + } else { + functioncallAsmGen.translateFunctionCall(stmt) + // discard any results from the stack: + val sub = stmt.target.targetSubroutine(program.namespace)!! + val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters) + for ((t, reg) in returns) { + if (reg.stack) { + if (t in IntegerDatatypes || t in PassByReferenceDatatypes) out(" inx") + else if (t == DataType.FLOAT) out(" inx | inx | inx") + } } } } diff --git a/docs/source/todo.rst b/docs/source/todo.rst index bbd27dea7..e44eb4926 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,9 +3,8 @@ TODO ==== - get rid of all other TODO's in the code ;-) -- move the ldx #$ff | clc | cld from the startup logic into the start() function as first instructions - add an %option that omits the 'system-init' code at the start. Useful to create separate standalone routines that shouldn't re-init the whole machine every time they're called -- line-circle-gfx examples are now a few hundred bytes larger than before. Why is that, can it be fixed? +- line-circle-gfx examples are now a few hundred bytes larger than before (~4.0/4.1 version i think?). Why is that, can it be fixed? - until condition should be able to refer to variables defined IN the do-until block itself. - add support? example? for processing arguments to a sys call : sys 999, 1, 2, "aaa" - make it possible for array literals to not only contain compile time constants diff --git a/examples/test.p8 b/examples/test.p8 index 2897fac06..edca60988 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -4,10 +4,9 @@ %zeropage basicsafe -main { +main $0900{ sub start() { - ubyte v = 1 @($c000+v) = 10