From 2911e357bd549492309c607d002994e510fdea79 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 20 Jan 2019 17:45:57 +0100 Subject: [PATCH] restructured program init code and library imports a bit --- compiler/res/prog8lib/c64utils.p8 | 31 -------- compiler/res/prog8lib/prog8lib.p8 | 31 ++++++++ compiler/src/prog8/CompilerMain.kt | 71 +++++++++---------- compiler/src/prog8/ast/AST.kt | 7 +- .../src/prog8/compiler/target/c64/AsmGen.kt | 6 +- .../prog8/compiler/target/c64/AsmOptimizer.kt | 2 + .../prog8/optimizing/StatementOptimizer.kt | 2 +- compiler/src/prog8/parser/ModuleParsing.kt | 19 +++-- compiler/test/UnitTests.kt | 15 ++-- examples/bdmusic.p8 | 2 +- examples/cube3d-float.p8 | 1 + examples/cube3d-sprites.p8 | 1 + examples/cube3d-stackvm.p8 | 3 +- examples/cube3d.p8 | 1 + examples/mandelbrot-stackvm.p8 | 3 +- examples/mandelbrot.p8 | 1 + examples/numbergame.p8 | 1 + examples/rasterbars.p8 | 1 + examples/sprites.p8 | 4 +- examples/swirl-stackvm.p8 | 3 +- examples/test.p8 | 4 +- examples/wizzine.p8 | 1 + 22 files changed, 116 insertions(+), 94 deletions(-) diff --git a/compiler/res/prog8lib/c64utils.p8 b/compiler/res/prog8lib/c64utils.p8 index a523478fb..f22f60782 100644 --- a/compiler/res/prog8lib/c64utils.p8 +++ b/compiler/res/prog8lib/c64utils.p8 @@ -17,37 +17,6 @@ ; ----- utility functions ---- -asmsub init_system () -> clobbers(A,X,Y) -> () { - ; ---- initializes the machine to a sane starting state - ; This means that the BASIC, KERNAL and CHARGEN ROMs are banked in, - ; the VIC, SID and CIA chips are reset, screen is cleared, and the default IRQ is set. - ; Also a different color scheme is chosen to identify ourselves a little. - ; Uppercase charset is activated, and all three registers set to 0, status flags cleared. - %asm {{ - sei - cld - lda #%00101111 - sta $00 - lda #%00100111 - sta $01 - jsr c64.IOINIT - jsr c64.RESTOR - jsr c64.CINT - lda #6 - sta c64.EXTCOL - lda #7 - sta c64.COLOR - lda #0 - sta c64.BGCOL0 - tax - tay - clc - clv - cli - rts - }} -} - asmsub ubyte2decimal (ubyte value @ A) -> clobbers() -> (ubyte @ Y, ubyte @ X, ubyte @ A) { ; ---- A to decimal string in Y/X/A (100s in Y, 10s in X, 1s in A) %asm {{ diff --git a/compiler/res/prog8lib/prog8lib.p8 b/compiler/res/prog8lib/prog8lib.p8 index 1489bd1bf..5bc92765c 100644 --- a/compiler/res/prog8lib/prog8lib.p8 +++ b/compiler/res/prog8lib/prog8lib.p8 @@ -9,6 +9,37 @@ %asm {{ +init_system .proc + ; -- initializes the machine to a sane starting state + ; Called automatically by the loader program logic. + ; This means that the BASIC, KERNAL and CHARGEN ROMs are banked in, + ; the VIC, SID and CIA chips are reset, screen is cleared, and the default IRQ is set. + ; Also a different color scheme is chosen to identify ourselves a little. + ; Uppercase charset is activated, and all three registers set to 0, status flags cleared. + sei + cld + lda #%00101111 + sta $00 + lda #%00100111 + sta $01 + jsr c64.IOINIT + jsr c64.RESTOR + jsr c64.CINT + lda #6 + sta c64.EXTCOL + lda #7 + sta c64.COLOR + lda #0 + sta c64.BGCOL0 + tax + tay + clc + clv + cli + rts + .pend + + add_a_to_zpword .proc ; -- add ubyte in A to the uword in c64.SCRATCH_ZPWORD1 clc diff --git a/compiler/src/prog8/CompilerMain.kt b/compiler/src/prog8/CompilerMain.kt index ab6a5ac67..fba0caaf2 100644 --- a/compiler/src/prog8/CompilerMain.kt +++ b/compiler/src/prog8/CompilerMain.kt @@ -8,7 +8,6 @@ import prog8.optimizing.optimizeStatements import prog8.optimizing.simplifyExpressions import prog8.parser.ParsingFailedError import prog8.parser.importModule -import prog8.stackvm.StackVm import java.io.File import java.io.PrintStream import java.lang.Exception @@ -65,44 +64,10 @@ private fun compileMain(args: Array) { // determine special compiler options - val options = moduleAst.statements.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.toSet() - val outputType = (moduleAst.statements.singleOrNull { it is Directive && it.directive == "%output" } - as? Directive)?.args?.single()?.name?.toUpperCase() - val launcherType = (moduleAst.statements.singleOrNull { it is Directive && it.directive == "%launcher" } - as? Directive)?.args?.single()?.name?.toUpperCase() - moduleAst.loadAddress = (moduleAst.statements.singleOrNull { it is Directive && it.directive == "%address" } - as? Directive)?.args?.single()?.int ?: 0 - val zpoption: String? = (moduleAst.statements.singleOrNull { it is Directive && it.directive == "%zeropage" } - as? Directive)?.args?.single()?.name?.toUpperCase() - val zpType: ZeropageType = - if (zpoption == null) - ZeropageType.KERNALSAFE - else - try { - ZeropageType.valueOf(zpoption) - } catch (x: IllegalArgumentException) { - ZeropageType.KERNALSAFE - // error will be printed by the astchecker - } - val zpReserved = moduleAst.statements - .asSequence() - .filter { it is Directive && it.directive == "%zpreserved" } - .map { (it as Directive).args } - .map { it[0].int!!..it[1].int!! } - .toList() - - val compilerOptions = CompilationOptions( - if (outputType == null) OutputType.PRG else OutputType.valueOf(outputType), - if (launcherType == null) LauncherType.BASIC else LauncherType.valueOf(launcherType), - zpType, zpReserved, - options.any { it.name == "enable_floats" }) + val compilerOptions = determineCompilationOptions(moduleAst) if (compilerOptions.launcher == LauncherType.BASIC && compilerOptions.output != OutputType.PRG) throw ParsingFailedError("${moduleAst.position} BASIC launcher requires output type PRG.") - if (compilerOptions.output == OutputType.PRG || compilerOptions.launcher == LauncherType.BASIC) { - if (namespace.lookup(listOf("c64utils"), moduleAst.statements.first()) == null) - throw ParsingFailedError("${moduleAst.position} When using output type PRG and/or laucher BASIC, the 'c64utils' module must be imported.") - } // perform initial syntax checks and constant folding println("Syntax check...") @@ -186,6 +151,40 @@ private fun compileMain(args: Array) { } } +fun determineCompilationOptions(moduleAst: Module): CompilationOptions { + val options = moduleAst.statements.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.toSet() + val outputType = (moduleAst.statements.singleOrNull { it is Directive && it.directive == "%output" } + as? Directive)?.args?.single()?.name?.toUpperCase() + val launcherType = (moduleAst.statements.singleOrNull { it is Directive && it.directive == "%launcher" } + as? Directive)?.args?.single()?.name?.toUpperCase() + moduleAst.loadAddress = (moduleAst.statements.singleOrNull { it is Directive && it.directive == "%address" } + as? Directive)?.args?.single()?.int ?: 0 + val zpoption: String? = (moduleAst.statements.singleOrNull { it is Directive && it.directive == "%zeropage" } + as? Directive)?.args?.single()?.name?.toUpperCase() + val zpType: ZeropageType = + if (zpoption == null) + ZeropageType.KERNALSAFE + else + try { + ZeropageType.valueOf(zpoption) + } catch (x: IllegalArgumentException) { + ZeropageType.KERNALSAFE + // error will be printed by the astchecker + } + val zpReserved = moduleAst.statements + .asSequence() + .filter { it is Directive && it.directive == "%zpreserved" } + .map { (it as Directive).args } + .map { it[0].int!!..it[1].int!! } + .toList() + + return CompilationOptions( + if (outputType == null) OutputType.PRG else OutputType.valueOf(outputType), + if (launcherType == null) LauncherType.BASIC else LauncherType.valueOf(launcherType), + zpType, zpReserved, + options.any { it.name == "enable_floats" }) +} + private fun usage() { System.err.println("Missing argument(s):") System.err.println(" [--emu] auto-start the C64 emulator after successful compilation") diff --git a/compiler/src/prog8/ast/AST.kt b/compiler/src/prog8/ast/AST.kt index e726aab01..9240e5673 100644 --- a/compiler/src/prog8/ast/AST.kt +++ b/compiler/src/prog8/ast/AST.kt @@ -451,7 +451,8 @@ class BuiltinFunctionStatementPlaceholder(val name: String, override val positio class Module(override val name: String, override var statements: MutableList, - override val position: Position) : Node, INameScope { + override val position: Position, + val isLibraryModule: Boolean) : Node, INameScope { override lateinit var parent: Node override fun linkParents(parent: Node) { @@ -1741,8 +1742,8 @@ class RepeatLoop(var body: AnonymousScope, /***************** Antlr Extension methods to create AST ****************/ -fun prog8Parser.ModuleContext.toAst(name: String) : Module = - Module(name, modulestatement().asSequence().map { it.toAst() }.toMutableList(), toPosition()) +fun prog8Parser.ModuleContext.toAst(name: String, isLibrary: Boolean) : Module = + Module(name, modulestatement().asSequence().map { it.toAst() }.toMutableList(), toPosition(), isLibrary) private fun ParserRuleContext.toPosition() : Position { diff --git a/compiler/src/prog8/compiler/target/c64/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/AsmGen.kt index 514155d16..6073ef2b4 100644 --- a/compiler/src/prog8/compiler/target/c64/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/AsmGen.kt @@ -155,12 +155,12 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, 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(" jsr c64utils.init_system") + out(" jsr prog8_lib.init_system") } options.output == OutputType.PRG -> { - out("; ---- program without sys call ----") + out("; ---- program without basic sys call ----") out("* = ${program.loadAddress.toHex()}\n") - out(" jsr c64utils.init_system") + out(" jsr prog8_lib.init_system") } options.output == OutputType.RAW -> { out("; ---- raw assembler program ----") diff --git a/compiler/src/prog8/compiler/target/c64/AsmOptimizer.kt b/compiler/src/prog8/compiler/target/c64/AsmOptimizer.kt index 7139c2aeb..b66c0a01c 100644 --- a/compiler/src/prog8/compiler/target/c64/AsmOptimizer.kt +++ b/compiler/src/prog8/compiler/target/c64/AsmOptimizer.kt @@ -39,6 +39,8 @@ fun optimizeAssembly(lines: MutableList): Int { numberOfOptimizations++ } + // TODO more assembly optimizations? + return numberOfOptimizations } diff --git a/compiler/src/prog8/optimizing/StatementOptimizer.kt b/compiler/src/prog8/optimizing/StatementOptimizer.kt index f7db7016a..c213e90ae 100644 --- a/compiler/src/prog8/optimizing/StatementOptimizer.kt +++ b/compiler/src/prog8/optimizing/StatementOptimizer.kt @@ -84,7 +84,7 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He private fun returnregisters(subroutine: Subroutine): List { return when { - subroutine.returntypes.size==0 -> listOf() + subroutine.returntypes.isEmpty() -> listOf() subroutine.returntypes.size==1 && subroutine.returntypes[0] in setOf(DataType.BYTE, DataType.UBYTE) -> listOf(RegisterOrStatusflag(RegisterOrPair.A, null, null)) subroutine.returntypes.size==1 && subroutine.returntypes[0] in setOf(DataType.WORD, DataType.UWORD) -> listOf(RegisterOrStatusflag(RegisterOrPair.AY, null, null)) subroutine.returntypes.size==2 && subroutine.returntypes.all { it in setOf(DataType.BYTE, DataType.UBYTE)} -> listOf(RegisterOrStatusflag(RegisterOrPair.A, null, null), RegisterOrStatusflag(RegisterOrPair.Y, null, null)) diff --git a/compiler/src/prog8/parser/ModuleParsing.kt b/compiler/src/prog8/parser/ModuleParsing.kt index 5ec54b819..177da4341 100644 --- a/compiler/src/prog8/parser/ModuleParsing.kt +++ b/compiler/src/prog8/parser/ModuleParsing.kt @@ -2,6 +2,9 @@ package prog8.parser import org.antlr.v4.runtime.* import prog8.ast.* +import prog8.compiler.LauncherType +import prog8.compiler.OutputType +import prog8.determineCompilationOptions import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths @@ -22,7 +25,7 @@ private class LexerErrorListener: BaseErrorListener() { } -fun importModule(stream: CharStream, moduleName: String): Module { +fun importModule(stream: CharStream, moduleName: String, isLibrary: Boolean): Module { val lexer = prog8Lexer(stream) val lexerErrors = LexerErrorListener() lexer.addErrorListener(lexerErrors) @@ -37,14 +40,22 @@ fun importModule(stream: CharStream, moduleName: String): Module { // tokens.commentTokens().forEach { println(it) } // convert to Ast - val moduleAst = parseTree.toAst(moduleName) + val moduleAst = parseTree.toAst(moduleName, isLibrary) importedModules[moduleAst.name] = moduleAst // process imports val lines = moduleAst.statements.toMutableList() + if(!moduleAst.position.file.startsWith("c64utils.") && !moduleAst.isLibraryModule) { + // if the output is a PRG or BASIC program, include the c64utils library + val compilerOptions = determineCompilationOptions(moduleAst) + if(compilerOptions.launcher==LauncherType.BASIC || compilerOptions.output==OutputType.PRG) { + lines.add(0, Directive("%import", listOf(DirectiveArg(null, "c64utils", null, moduleAst.position)), moduleAst.position)) + } + } // always import the prog8 compiler library if(!moduleAst.position.file.startsWith("prog8lib.")) lines.add(0, Directive("%import", listOf(DirectiveArg(null, "prog8lib", null, moduleAst.position)), moduleAst.position)) + val imports = lines .asSequence() .mapIndexed { i, it -> Pair(i, it) } @@ -83,7 +94,7 @@ fun importModule(filePath: Path) : Module { val moduleName = filePath.fileName.toString().substringBeforeLast('.') val input = CharStreams.fromPath(filePath) - return importModule(input, moduleName) + return importModule(input, moduleName, filePath.parent==null) } @@ -123,7 +134,7 @@ private fun executeImportDirective(import: Directive, importedFrom: Path): Modul // load the module from the embedded resource resource.openStream().use { println("importing '$moduleName' (embedded library)") - importModule(CharStreams.fromStream(it), moduleName) + importModule(CharStreams.fromStream(it), moduleName, true) } } else { val modulePath = discoverImportedModuleFile(moduleName, importedFrom, import.position) diff --git a/compiler/test/UnitTests.kt b/compiler/test/UnitTests.kt index 0ce07131d..467e1291e 100644 --- a/compiler/test/UnitTests.kt +++ b/compiler/test/UnitTests.kt @@ -10,10 +10,7 @@ import prog8.compiler.* import prog8.compiler.intermediate.Value import prog8.compiler.target.c64.* import java.io.CharConversionException -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertFalse -import kotlin.test.assertTrue +import kotlin.test.* @TestInstance(TestInstance.Lifecycle.PER_CLASS) @@ -324,8 +321,8 @@ class TestPetscii { fun testLiteralValueComparisons() { val ten = LiteralValue(DataType.UWORD, wordvalue=10, position=Position("", 0 ,0 ,0)) val nine = LiteralValue(DataType.UBYTE, bytevalue=9, position=Position("", 0 ,0 ,0)) - assertTrue(ten == ten) - assertFalse(ten == nine) + assertEquals(ten, ten) + assertNotEquals(ten, nine) assertFalse(ten != ten) assertTrue(ten != nine) @@ -341,7 +338,7 @@ class TestPetscii { val abc = LiteralValue(DataType.STR, strvalue = "abc", position=Position("", 0 ,0 ,0)) val abd = LiteralValue(DataType.STR, strvalue = "abd", position=Position("", 0 ,0 ,0)) - assertTrue(abc==abc) + assertEquals(abc, abc) assertTrue(abc!=abd) assertFalse(abc!=abc) assertTrue(abc < abd) @@ -356,8 +353,8 @@ class TestPetscii { fun testStackvmValueComparisons() { val ten = Value(DataType.FLOAT, 10) val nine = Value(DataType.UWORD, 9) - assertTrue(ten == ten) - assertFalse(ten == nine) + assertEquals(ten, ten) + assertNotEquals(ten, nine) assertFalse(ten != ten) assertTrue(ten != nine) diff --git a/examples/bdmusic.p8 b/examples/bdmusic.p8 index 36e5a3814..4be1e9b15 100644 --- a/examples/bdmusic.p8 +++ b/examples/bdmusic.p8 @@ -1,4 +1,4 @@ -%import c64utils ; @todo make this import automatic if output is basic prg +%import c64lib ~ main { diff --git a/examples/cube3d-float.p8 b/examples/cube3d-float.p8 index 6a2570e14..7bf5d3cb2 100644 --- a/examples/cube3d-float.p8 +++ b/examples/cube3d-float.p8 @@ -1,3 +1,4 @@ +%import c64lib %import c64utils %import c64flt diff --git a/examples/cube3d-sprites.p8 b/examples/cube3d-sprites.p8 index f0059c013..2a4924496 100644 --- a/examples/cube3d-sprites.p8 +++ b/examples/cube3d-sprites.p8 @@ -1,3 +1,4 @@ +%import c64lib %import c64utils ~ spritedata $2000 { diff --git a/examples/cube3d-stackvm.p8 b/examples/cube3d-stackvm.p8 index a96a899d5..caffb9fe0 100644 --- a/examples/cube3d-stackvm.p8 +++ b/examples/cube3d-stackvm.p8 @@ -1,4 +1,5 @@ -%import c64utils +%output raw +%launcher none %import c64flt ~ irq { diff --git a/examples/cube3d.p8 b/examples/cube3d.p8 index 772c0c78a..9708daebe 100644 --- a/examples/cube3d.p8 +++ b/examples/cube3d.p8 @@ -1,3 +1,4 @@ +%import c64lib %import c64utils ~ main { diff --git a/examples/mandelbrot-stackvm.p8 b/examples/mandelbrot-stackvm.p8 index 739cbe593..753d7e3d4 100644 --- a/examples/mandelbrot-stackvm.p8 +++ b/examples/mandelbrot-stackvm.p8 @@ -1,4 +1,5 @@ -%import c64utils +%output raw +%launcher none %import c64flt ~ main { diff --git a/examples/mandelbrot.p8 b/examples/mandelbrot.p8 index 5a93c97e4..dc4aea70f 100644 --- a/examples/mandelbrot.p8 +++ b/examples/mandelbrot.p8 @@ -1,3 +1,4 @@ +%import c64lib %import c64utils %import c64flt diff --git a/examples/numbergame.p8 b/examples/numbergame.p8 index 40dc05b26..00ad52fe9 100644 --- a/examples/numbergame.p8 +++ b/examples/numbergame.p8 @@ -1,4 +1,5 @@ %import c64utils +%import c64lib ; The classic number guessing game. ; This version uses mostly high level subroutine calls and loops. diff --git a/examples/rasterbars.p8 b/examples/rasterbars.p8 index b9ef58e9d..f4ce5166f 100644 --- a/examples/rasterbars.p8 +++ b/examples/rasterbars.p8 @@ -1,4 +1,5 @@ %import c64utils +%import c64lib ~ main { diff --git a/examples/sprites.p8 b/examples/sprites.p8 index cd6d9b438..e23e8e10f 100644 --- a/examples/sprites.p8 +++ b/examples/sprites.p8 @@ -1,4 +1,5 @@ %import c64utils +%import c64lib ~ spritedata $0a00 { @@ -36,8 +37,7 @@ sub start() { - c64.STROUT("balloon sprites!\n") - c64.STROUT("...we are all floating...\n") + c64scr.print("balloon sprites!\n...we are all floating...\n") for ubyte i in 0 to 7 { c64.SPRPTR[i] = $0a00 / 64 diff --git a/examples/swirl-stackvm.p8 b/examples/swirl-stackvm.p8 index 44702e230..a129acb16 100644 --- a/examples/swirl-stackvm.p8 +++ b/examples/swirl-stackvm.p8 @@ -1,4 +1,5 @@ -%import c64utils +%output raw +%launcher none %import c64flt ~ main { diff --git a/examples/test.p8 b/examples/test.p8 index 9780ff953..6fc746631 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,4 +1,6 @@ -%import c64utils +%output raw +%launcher none + ~ main { diff --git a/examples/wizzine.p8 b/examples/wizzine.p8 index 091f730ed..43f2601fd 100644 --- a/examples/wizzine.p8 +++ b/examples/wizzine.p8 @@ -1,4 +1,5 @@ %import c64utils +%import c64lib ~ spritedata $0a00 {