restructured program init code and library imports a bit

This commit is contained in:
Irmen de Jong 2019-01-20 17:45:57 +01:00
parent fc67ef8f21
commit 2911e357bd
22 changed files with 116 additions and 94 deletions

View File

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

View File

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

View File

@ -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<String>) {
// 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<String>) {
}
}
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")

View File

@ -451,7 +451,8 @@ class BuiltinFunctionStatementPlaceholder(val name: String, override val positio
class Module(override val name: String,
override var statements: MutableList<IStatement>,
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 {

View File

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

View File

@ -39,6 +39,8 @@ fun optimizeAssembly(lines: MutableList<String>): Int {
numberOfOptimizations++
}
// TODO more assembly optimizations?
return numberOfOptimizations
}

View File

@ -84,7 +84,7 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
private fun returnregisters(subroutine: Subroutine): List<RegisterOrStatusflag> {
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))

View File

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

View File

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

View File

@ -1,4 +1,4 @@
%import c64utils ; @todo make this import automatic if output is basic prg
%import c64lib
~ main {

View File

@ -1,3 +1,4 @@
%import c64lib
%import c64utils
%import c64flt

View File

@ -1,3 +1,4 @@
%import c64lib
%import c64utils
~ spritedata $2000 {

View File

@ -1,4 +1,5 @@
%import c64utils
%output raw
%launcher none
%import c64flt
~ irq {

View File

@ -1,3 +1,4 @@
%import c64lib
%import c64utils
~ main {

View File

@ -1,4 +1,5 @@
%import c64utils
%output raw
%launcher none
%import c64flt
~ main {

View File

@ -1,3 +1,4 @@
%import c64lib
%import c64utils
%import c64flt

View File

@ -1,4 +1,5 @@
%import c64utils
%import c64lib
; The classic number guessing game.
; This version uses mostly high level subroutine calls and loops.

View File

@ -1,4 +1,5 @@
%import c64utils
%import c64lib
~ main {

View File

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

View File

@ -1,4 +1,5 @@
%import c64utils
%output raw
%launcher none
%import c64flt
~ main {

View File

@ -1,4 +1,6 @@
%import c64utils
%output raw
%launcher none
~ main {

View File

@ -1,4 +1,5 @@
%import c64utils
%import c64lib
~ spritedata $0a00 {