reworked program init logic so that it is included as the first thing inside main.start itself, to allow better stand alone asm

This commit is contained in:
Irmen de Jong 2020-09-23 22:29:21 +02:00
parent c50cbbb526
commit 2b9316c4ff
7 changed files with 84 additions and 41 deletions

View File

@ -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

View File

@ -1 +1 @@
4.3
4.4-SNAPSHOT

View File

@ -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
}

View File

@ -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
}

View File

@ -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<String>()
internal val blockLevelVarInits = mutableMapOf<Block, MutableSet<VarDecl>>()
private val blockLevelVarInits = mutableMapOf<Block, MutableSet<VarDecl>>()
private val programInitLines = mutableListOf<String>()
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<VarDecl>().any { vd->vd.value!=null && vd.type==VarDeclType.VAR && vd.datatype in NumericDatatypes})
out(" 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}")
programInitLines.add(" jsr ${it.name}.prog8_init_vars")
}
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,22 +632,29 @@ $save .byte 0
is InlineAssembly -> translate(stmt)
is FunctionCallStatement -> {
val functionName = stmt.target.nameInSource.last()
if(functionName == programInitializationRoutineName) {
out("""
tsx
stx prog8_lib.orig_stackpointer
jsr $functionName""")
} else {
val builtinFunc = BuiltinFunctions[functionName]
if(builtinFunc!=null) {
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) {
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")
}
}
}
}
}
is Assignment -> assignmentAsmGen.translate(stmt)
is Jump -> translate(stmt)
is PostIncrDecr -> postincrdecrAsmGen.translate(stmt)

View File

@ -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

View File

@ -4,10 +4,9 @@
%zeropage basicsafe
main {
main $0900{
sub start() {
ubyte v = 1
@($c000+v) = 10