From 7ff1af393459f622e4e4db6a810caa5851656a67 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sun, 3 Feb 2019 00:14:56 +0100 Subject: [PATCH] avoid zp corruption issues and added zp mode for floating point (todo: allocate) --- compiler/src/prog8/CompilerMain.kt | 2 +- compiler/src/prog8/ast/AST.kt | 6 +- compiler/src/prog8/ast/AstChecker.kt | 15 +-- compiler/src/prog8/ast/AstRecursionChecker.kt | 8 +- compiler/src/prog8/ast/StmtReorderer.kt | 8 +- compiler/src/prog8/compiler/Compiler.kt | 1 + compiler/src/prog8/compiler/Zeropage.kt | 10 +- .../src/prog8/compiler/target/c64/AsmGen.kt | 10 +- .../prog8/compiler/target/c64/Commodore64.kt | 16 ++- .../prog8/optimizing/StatementOptimizer.kt | 46 ++++---- docs/source/syntaxreference.rst | 10 +- docs/source/targetsystem.rst | 6 +- examples/comparison_ifs_byte.p8 | 2 + examples/comparison_ifs_float.p8 | 1 + examples/comparison_ifs_ubyte.p8 | 1 + examples/comparison_ifs_uword.p8 | 1 + examples/comparison_ifs_word.p8 | 1 + examples/comparisons_byte.p8 | 1 + examples/comparisons_float.p8 | 1 + examples/comparisons_ubyte.p8 | 1 + examples/comparisons_uword.p8 | 1 + examples/comparisons_word.p8 | 1 + examples/fibonacci.p8 | 1 + examples/hello.p8 | 1 + examples/numbergame.p8 | 1 + examples/primes.p8 | 1 + examples/sprites.p8 | 1 + examples/tehtriz.p8 | 100 ++++++++++++++++++ examples/test.p8 | 40 +++---- examples/wizzine.p8 | 1 + parser/src/prog8/parser/prog8Lexer.java | 2 +- parser/src/prog8/parser/prog8Parser.java | 2 +- prog8.iml | 1 + 33 files changed, 217 insertions(+), 83 deletions(-) create mode 100644 examples/tehtriz.p8 diff --git a/compiler/src/prog8/CompilerMain.kt b/compiler/src/prog8/CompilerMain.kt index 51ce5c1d2..796338b7c 100644 --- a/compiler/src/prog8/CompilerMain.kt +++ b/compiler/src/prog8/CompilerMain.kt @@ -182,7 +182,7 @@ fun determineCompilationOptions(moduleAst: Module): CompilationOptions { val floatsEnabled = options.any { it.name == "enable_floats" } val zpType: ZeropageType = if (zpoption == null) - if(floatsEnabled) ZeropageType.BASICSAFE else ZeropageType.KERNALSAFE + if(floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE else try { ZeropageType.valueOf(zpoption) diff --git a/compiler/src/prog8/ast/AST.kt b/compiler/src/prog8/ast/AST.kt index ac5ad3dc1..cd4e37a89 100644 --- a/compiler/src/prog8/ast/AST.kt +++ b/compiler/src/prog8/ast/AST.kt @@ -174,9 +174,9 @@ interface IAstProcessor { return functionCall } - fun process(functionCall: FunctionCallStatement): IStatement { - functionCall.arglist = functionCall.arglist.map { it.process(this) }.toMutableList() - return functionCall + fun process(functionCallStatement: FunctionCallStatement): IStatement { + functionCallStatement.arglist = functionCallStatement.arglist.map { it.process(this) }.toMutableList() + return functionCallStatement } fun process(identifier: IdentifierReference): IExpression { diff --git a/compiler/src/prog8/ast/AstChecker.kt b/compiler/src/prog8/ast/AstChecker.kt index 5fe0bf326..ffe0c0327 100644 --- a/compiler/src/prog8/ast/AstChecker.kt +++ b/compiler/src/prog8/ast/AstChecker.kt @@ -592,9 +592,10 @@ private class AstChecker(private val namespace: INameScope, if(directive.parent !is Module) err("this directive may only occur at module level") if(directive.args.size!=1 || directive.args[0].name != "basicsafe" && + directive.args[0].name != "floatsafe" && directive.args[0].name != "kernalsafe" && directive.args[0].name != "full") - err("invalid zp type, expected basicsafe, kernalsafe, or full") + err("invalid zp type, expected basicsafe, floatsafe, kernalsafe, or full") } "%zpreserved" -> { if(directive.parent !is Module) err("this directive may only occur at module level") @@ -696,7 +697,7 @@ private class AstChecker(private val namespace: INameScope, } "and", "or", "xor", "&", "|", "^" -> { // only integer numeric operands accepted - val rightDt = expr.right?.resultingDatatype(namespace, heap) + val rightDt = expr.right.resultingDatatype(namespace, heap) val leftDt = expr.left.resultingDatatype(namespace, heap) if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes) checkResult.add(ExpressionError("logical or bitwise operator can only be used on integer operands", expr.right.position)) @@ -776,13 +777,13 @@ private class AstChecker(private val namespace: INameScope, return super.process(functionCall) } - override fun process(functionCall: FunctionCallStatement): IStatement { - val targetStatement = checkFunctionOrLabelExists(functionCall.target, functionCall) + override fun process(functionCallStatement: FunctionCallStatement): IStatement { + val targetStatement = checkFunctionOrLabelExists(functionCallStatement.target, functionCallStatement) if(targetStatement!=null) - checkFunctionCall(targetStatement, functionCall.arglist, functionCall.position) + checkFunctionCall(targetStatement, functionCallStatement.arglist, functionCallStatement.position) if(targetStatement is Subroutine && targetStatement.returntypes.isNotEmpty()) - printWarning("result value of subroutine call is discarded", functionCall.position) - return super.process(functionCall) + printWarning("result value of subroutine call is discarded", functionCallStatement.position) + return super.process(functionCallStatement) } private fun checkFunctionCall(target: IStatement, args: List, position: Position) { diff --git a/compiler/src/prog8/ast/AstRecursionChecker.kt b/compiler/src/prog8/ast/AstRecursionChecker.kt index 94a795e78..3b2714e29 100644 --- a/compiler/src/prog8/ast/AstRecursionChecker.kt +++ b/compiler/src/prog8/ast/AstRecursionChecker.kt @@ -92,9 +92,9 @@ private class AstRecursionChecker(private val namespace: INameScope) : IAstProce return listOf(AstException("Program contains recursive subroutine calls, this is not supported. Recursive chain:\n (a subroutine call in) $chain")) } - override fun process(functionCall: FunctionCallStatement): IStatement { - val scope = functionCall.definingScope() - val targetStatement = functionCall.target.targetStatement(namespace) + override fun process(functionCallStatement: FunctionCallStatement): IStatement { + val scope = functionCallStatement.definingScope() + val targetStatement = functionCallStatement.target.targetStatement(namespace) if(targetStatement!=null) { val targetScope = when (targetStatement) { is Subroutine -> targetStatement @@ -102,7 +102,7 @@ private class AstRecursionChecker(private val namespace: INameScope) : IAstProce } callGraph.add(scope, targetScope) } - return super.process(functionCall) + return super.process(functionCallStatement) } override fun process(functionCall: FunctionCall): IExpression { diff --git a/compiler/src/prog8/ast/StmtReorderer.kt b/compiler/src/prog8/ast/StmtReorderer.kt index 2f2445717..5ab5e68a3 100644 --- a/compiler/src/prog8/ast/StmtReorderer.kt +++ b/compiler/src/prog8/ast/StmtReorderer.kt @@ -38,10 +38,10 @@ private class StatementReorderer(private val namespace: INameScope, private val .filter { it.value is Block && !(it.value as Block).isInLibrary } .map { it.index to it.value } .reversed() - for(blocks in nonLibraryBlocks) - module.statements.removeAt(blocks.first) - for(blocks in nonLibraryBlocks) - module.statements.add(0, blocks.second) + for(nonLibBlock in nonLibraryBlocks) + module.statements.removeAt(nonLibBlock.first) + for(nonLibBlock in nonLibraryBlocks) + module.statements.add(0, nonLibBlock.second) val mainBlock = module.statements.single { it is Block && it.name=="main" } if((mainBlock as Block).address==null) { module.statements.remove(mainBlock) diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index a9a2d158a..9c35ec270 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -132,6 +132,7 @@ enum class LauncherType { enum class ZeropageType { BASICSAFE, + FLOATSAFE, KERNALSAFE, FULL } diff --git a/compiler/src/prog8/compiler/Zeropage.kt b/compiler/src/prog8/compiler/Zeropage.kt index a477fbafa..508dfe7b5 100644 --- a/compiler/src/prog8/compiler/Zeropage.kt +++ b/compiler/src/prog8/compiler/Zeropage.kt @@ -6,7 +6,7 @@ import prog8.ast.* class ZeropageDepletedError(message: String) : Exception(message) -abstract class Zeropage(private val options: CompilationOptions) { +abstract class Zeropage(protected val options: CompilationOptions) { private val allocations = mutableMapOf>() val free = mutableListOf() // subclasses must set this to the appropriate free locations. @@ -61,4 +61,12 @@ abstract class Zeropage(private val options: CompilationOptions) { private fun loneByte(address: Int) = address in free && address-1 !in free && address+1 !in free private fun sequentialFree(address: Int, size: Int) = free.containsAll((address until address+size).toList()) + + enum class ExitProgramStrategy { + CLEAN_EXIT, + SYSTEM_RESET + } + + abstract val exitProgramStrategy: ExitProgramStrategy + } diff --git a/compiler/src/prog8/compiler/target/c64/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/AsmGen.kt index ffc08940f..3af75b330 100644 --- a/compiler/src/prog8/compiler/target/c64/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/AsmGen.kt @@ -194,7 +194,15 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, out(" jsr ${block.scopedname}.${initVarsLabel.name}") } out(" clc") - out(" jmp main.start\t; jump to program entrypoint") + 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 (c64.RESET_VEC)\t; cold reset") + } + } out("") // the global list of all floating point constants for the whole program diff --git a/compiler/src/prog8/compiler/target/c64/Commodore64.kt b/compiler/src/prog8/compiler/target/c64/Commodore64.kt index 6ccb1586a..c65256c6f 100644 --- a/compiler/src/prog8/compiler/target/c64/Commodore64.kt +++ b/compiler/src/prog8/compiler/target/c64/Commodore64.kt @@ -33,20 +33,32 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) { const val SCRATCH_W2 = 0xfd // $fd/$fe } + override val exitProgramStrategy: ExitProgramStrategy = when(options.zeropage) { + ZeropageType.BASICSAFE, ZeropageType.FLOATSAFE -> ExitProgramStrategy.CLEAN_EXIT + ZeropageType.KERNALSAFE, ZeropageType.FULL -> ExitProgramStrategy.SYSTEM_RESET + } + + init { - if(options.zeropage== ZeropageType.FULL) { + if(options.floats && options.zeropage!=ZeropageType.FLOATSAFE && options.zeropage!=ZeropageType.BASICSAFE) + throw CompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe'") + + if(options.zeropage == ZeropageType.FULL) { free.addAll(0x04 .. 0xf9) free.add(0xff) free.removeAll(listOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_REG_X, SCRATCH_W1, SCRATCH_W1+1, SCRATCH_W2, SCRATCH_W2+1)) free.removeAll(listOf(0xa0, 0xa1, 0xa2, 0x91, 0xc0, 0xc5, 0xcb, 0xf5, 0xf6)) // these are updated by IRQ } else { - if(options.zeropage== ZeropageType.KERNALSAFE) { + if(options.zeropage == ZeropageType.KERNALSAFE) { // add the Zp addresses that are just used by BASIC routines to the free list free.addAll(listOf(0x09, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x51, 0x52, 0x53, 0x6f, 0x70)) } + else if(options.zeropage == ZeropageType.FLOATSAFE) { + TODO("reserve float zp locations") + } // add the other free Zp addresses // these are valid for the C-64 (when no RS232 I/O is performed): free.addAll(listOf(0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0d, 0x0e, diff --git a/compiler/src/prog8/optimizing/StatementOptimizer.kt b/compiler/src/prog8/optimizing/StatementOptimizer.kt index ec8185cab..02ea4b609 100644 --- a/compiler/src/prog8/optimizing/StatementOptimizer.kt +++ b/compiler/src/prog8/optimizing/StatementOptimizer.kt @@ -114,39 +114,39 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He } - override fun process(functionCall: FunctionCallStatement): IStatement { - if(functionCall.target.nameInSource.size==1 && functionCall.target.nameInSource[0] in BuiltinFunctions) { - val functionName = functionCall.target.nameInSource[0] + override fun process(functionCallStatement: FunctionCallStatement): IStatement { + if(functionCallStatement.target.nameInSource.size==1 && functionCallStatement.target.nameInSource[0] in BuiltinFunctions) { + val functionName = functionCallStatement.target.nameInSource[0] if (functionName in pureBuiltinFunctions) { - printWarning("statement has no effect (function return value is discarded)", functionCall.position) - statementsToRemove.add(functionCall) - return functionCall + printWarning("statement has no effect (function return value is discarded)", functionCallStatement.position) + statementsToRemove.add(functionCallStatement) + return functionCallStatement } } - if(functionCall.target.nameInSource==listOf("c64scr", "print") || - functionCall.target.nameInSource==listOf("c64scr", "print_p")) { + if(functionCallStatement.target.nameInSource==listOf("c64scr", "print") || + functionCallStatement.target.nameInSource==listOf("c64scr", "print_p")) { // printing a literal string of just 2 or 1 characters is replaced by directly outputting those characters - if(functionCall.arglist.single() is LiteralValue) + if(functionCallStatement.arglist.single() is LiteralValue) throw AstException("string argument should be on heap already") - val stringVar = functionCall.arglist.single() as? IdentifierReference + val stringVar = functionCallStatement.arglist.single() as? IdentifierReference if(stringVar!=null) { val heapId = stringVar.heapId(namespace) val string = heap.get(heapId).str!! if(string.length==1) { val petscii = Petscii.encodePetscii(string, true)[0] - functionCall.arglist.clear() - functionCall.arglist.add(LiteralValue.optimalInteger(petscii, functionCall.position)) - functionCall.target = IdentifierReference(listOf("c64", "CHROUT"), functionCall.target.position) + functionCallStatement.arglist.clear() + functionCallStatement.arglist.add(LiteralValue.optimalInteger(petscii, functionCallStatement.position)) + functionCallStatement.target = IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position) optimizationsDone++ - return functionCall + return functionCallStatement } else if(string.length==2) { val petscii = Petscii.encodePetscii(string, true) - val scope = AnonymousScope(mutableListOf(), functionCall.position) - scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("c64", "CHROUT"), functionCall.target.position), - mutableListOf(LiteralValue.optimalInteger(petscii[0], functionCall.position)), functionCall.position)) - scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("c64", "CHROUT"), functionCall.target.position), - mutableListOf(LiteralValue.optimalInteger(petscii[1], functionCall.position)), functionCall.position)) + val scope = AnonymousScope(mutableListOf(), functionCallStatement.position) + scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position), + mutableListOf(LiteralValue.optimalInteger(petscii[0], functionCallStatement.position)), functionCallStatement.position)) + scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position), + mutableListOf(LiteralValue.optimalInteger(petscii[1], functionCallStatement.position)), functionCallStatement.position)) optimizationsDone++ return scope } @@ -156,20 +156,20 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He // if it calls a subroutine, // and the first instruction in the subroutine is a jump, call that jump target instead // if the first instruction in the subroutine is a return statement, replace with a nop instruction - val subroutine = functionCall.target.targetStatement(namespace) as? Subroutine + val subroutine = functionCallStatement.target.targetStatement(namespace) as? Subroutine if(subroutine!=null) { val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull() if(first is Jump && first.identifier!=null) { optimizationsDone++ - return FunctionCallStatement(first.identifier, functionCall.arglist, functionCall.position) + return FunctionCallStatement(first.identifier, functionCallStatement.arglist, functionCallStatement.position) } if(first is ReturnFromIrq || first is Return) { optimizationsDone++ - return NopStatement(functionCall.position) + return NopStatement(functionCallStatement.position) } } - return super.process(functionCall) + return super.process(functionCallStatement) } override fun process(functionCall: FunctionCall): IExpression { diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst index 93da3fbd4..8527c7d9a 100644 --- a/docs/source/syntaxreference.rst +++ b/docs/source/syntaxreference.rst @@ -60,16 +60,20 @@ Directives - style ``kernalsafe`` -- use the part of the ZP that is 'free' or only used by BASIC routines, and don't change anything else. This allows full use of KERNAL ROM routines (but not BASIC routines), including default IRQs during normal system operation. + When the program exits, a system reset is performed (because BASIC will be in a corrupt state). + - style ``floatsafe`` -- like the previous one but also reserves the addresses that + are required to perform floating point operations (from the BASIC kernel). No clean exit is possible. - style ``basicsafe`` -- the most restricted mode; only use the handful 'free' addresses in the ZP, and don't touch change anything else. This allows full use of BASIC and KERNAL ROM routines including default IRQs during normal system operation. + When the program exits, it simply returns to the BASIC ready prompt. - style ``full`` -- claim the whole ZP for variables for the program, overwriting everything, except the few addresses mentioned above that are used by the system's IRQ routine. Even though the default IRQ routine is still active, it is impossible to use most BASIC and KERNAL ROM routines. This includes many floating point operations and several utility routines that do I/O, such as ``print_string``. - It is also not possible to cleanly exit the program, other than resetting the machine. - This option makes programs smaller and faster because many more variables can - be stored in the ZP, which is more efficient. + As with ``kernalsafe``, it is not possible to cleanly exit the program, other than to reset the machine. + This option makes programs smaller and faster because even more variables can + be stored in the ZP (which allows for more efficient assembly code). Also read :ref:`zeropage`. diff --git a/docs/source/targetsystem.rst b/docs/source/targetsystem.rst index a165fdadc..7dc02a402 100644 --- a/docs/source/targetsystem.rst +++ b/docs/source/targetsystem.rst @@ -77,11 +77,6 @@ Theoretically they can all be used in a program, with the follwoing limitations: - it's more convenient and safe to let the compiler allocate these addresses for you and just use symbolic names in the program code. -.. todo:: - There must be a way to tell the compiler which variables you require to be in Zeropage: - ``zeropage`` modifier keyword on vardecl perhaps? - - Prog8 knows what addresses are safe to use in the various ZP handling configurations. It will use the free ZP addresses to place its ZP variables in, until they're all used up. If instructed to output a program that takes over the entire @@ -93,6 +88,7 @@ treats the ZP for the program. The default is to be reasonably restrictive to us part of the ZP that is not used by the C64's kernal routines. It's possible to claim the whole ZP as well (by disabling the operating system or kernal). If you want, it's also possible to be more restricive and stay clear of the addresses used by BASIC routines too. +This allows the program to exit cleanly back to a BASIC ready prompt - something that is not possible in the other modes. IRQs and the ZeroPage diff --git a/examples/comparison_ifs_byte.p8 b/examples/comparison_ifs_byte.p8 index 07f6cdb10..b0a46e218 100644 --- a/examples/comparison_ifs_byte.p8 +++ b/examples/comparison_ifs_byte.p8 @@ -1,4 +1,6 @@ %import c64utils +%zeropage basicsafe + ~ main { diff --git a/examples/comparison_ifs_float.p8 b/examples/comparison_ifs_float.p8 index 604616da0..7cd1c2b78 100644 --- a/examples/comparison_ifs_float.p8 +++ b/examples/comparison_ifs_float.p8 @@ -1,5 +1,6 @@ %import c64utils %import c64flt +%zeropage basicsafe ~ main { diff --git a/examples/comparison_ifs_ubyte.p8 b/examples/comparison_ifs_ubyte.p8 index 9dc9b1abf..a8e918913 100644 --- a/examples/comparison_ifs_ubyte.p8 +++ b/examples/comparison_ifs_ubyte.p8 @@ -1,4 +1,5 @@ %import c64utils +%zeropage basicsafe ~ main { diff --git a/examples/comparison_ifs_uword.p8 b/examples/comparison_ifs_uword.p8 index b4247a90c..8ac8fe197 100644 --- a/examples/comparison_ifs_uword.p8 +++ b/examples/comparison_ifs_uword.p8 @@ -1,4 +1,5 @@ %import c64utils +%zeropage basicsafe ~ main { diff --git a/examples/comparison_ifs_word.p8 b/examples/comparison_ifs_word.p8 index 386a4976c..668c53555 100644 --- a/examples/comparison_ifs_word.p8 +++ b/examples/comparison_ifs_word.p8 @@ -1,4 +1,5 @@ %import c64utils +%zeropage basicsafe ~ main { diff --git a/examples/comparisons_byte.p8 b/examples/comparisons_byte.p8 index 70d535c40..7465864ba 100644 --- a/examples/comparisons_byte.p8 +++ b/examples/comparisons_byte.p8 @@ -1,4 +1,5 @@ %import c64utils +%zeropage basicsafe ~ main { diff --git a/examples/comparisons_float.p8 b/examples/comparisons_float.p8 index 7d48f3f64..f6dde83fe 100644 --- a/examples/comparisons_float.p8 +++ b/examples/comparisons_float.p8 @@ -1,5 +1,6 @@ %import c64utils %import c64flt +%zeropage basicsafe ~ main { diff --git a/examples/comparisons_ubyte.p8 b/examples/comparisons_ubyte.p8 index 3905f1210..de669caa8 100644 --- a/examples/comparisons_ubyte.p8 +++ b/examples/comparisons_ubyte.p8 @@ -1,4 +1,5 @@ %import c64utils +%zeropage basicsafe ~ main { diff --git a/examples/comparisons_uword.p8 b/examples/comparisons_uword.p8 index bd28e2dea..f3c43be05 100644 --- a/examples/comparisons_uword.p8 +++ b/examples/comparisons_uword.p8 @@ -1,4 +1,5 @@ %import c64utils +%zeropage basicsafe ~ main { diff --git a/examples/comparisons_word.p8 b/examples/comparisons_word.p8 index 946096ab9..2d3d98402 100644 --- a/examples/comparisons_word.p8 +++ b/examples/comparisons_word.p8 @@ -1,4 +1,5 @@ %import c64utils +%zeropage basicsafe ~ main { diff --git a/examples/fibonacci.p8 b/examples/fibonacci.p8 index eaea3697f..84683bd2e 100644 --- a/examples/fibonacci.p8 +++ b/examples/fibonacci.p8 @@ -1,4 +1,5 @@ %import c64utils +%zeropage basicsafe ; This example computes the first 20 values of the Fibonacci sequence. ; It uses the feature that variables that don't have an initialization value, diff --git a/examples/hello.p8 b/examples/hello.p8 index 40a8b56db..fe782fa58 100644 --- a/examples/hello.p8 +++ b/examples/hello.p8 @@ -1,6 +1,7 @@ %import c64lib %import c64utils %import c64flt +%zeropage basicsafe ~ main { diff --git a/examples/numbergame.p8 b/examples/numbergame.p8 index 00ad52fe9..74637f9fc 100644 --- a/examples/numbergame.p8 +++ b/examples/numbergame.p8 @@ -1,5 +1,6 @@ %import c64utils %import c64lib +%zeropage basicsafe ; The classic number guessing game. ; This version uses mostly high level subroutine calls and loops. diff --git a/examples/primes.p8 b/examples/primes.p8 index 9590140c0..660bdfb1b 100644 --- a/examples/primes.p8 +++ b/examples/primes.p8 @@ -1,4 +1,5 @@ %import c64utils +%zeropage basicsafe ~ main { diff --git a/examples/sprites.p8 b/examples/sprites.p8 index a50c249e9..95f84a616 100644 --- a/examples/sprites.p8 +++ b/examples/sprites.p8 @@ -1,5 +1,6 @@ %import c64utils %import c64lib +%zeropage basicsafe ~ spritedata $0a00 { diff --git a/examples/tehtriz.p8 b/examples/tehtriz.p8 new file mode 100644 index 000000000..585527391 --- /dev/null +++ b/examples/tehtriz.p8 @@ -0,0 +1,100 @@ +~ main { + + const ubyte boardOffsetX = 14 + const ubyte boardOffsetY = 3 + const ubyte boardWidth = 10 + const ubyte boardHeight = 20 + + ; 3x3, rotating around their center square: + ubyte[4] blockJ = [0, 4, 5, 6] + ubyte[4] blockL = [2, 4, 5, 6] + ubyte[4] blockS = [1, 2, 4, 5] + ubyte[4] blockT = [1, 4, 5, 6] + ubyte[4] blockZ = [0, 1, 5, 6] + ;4x4, rotating around center: + ubyte[4] blockI = [4, 5, 6, 7] + ubyte[4] blockO = [1, 2, 5, 6] + + ; block colors I, J, L, O, S, T, Z: cyan, blue, orange, yellow, green, purple, red + ubyte[7] blockColors = [3, 6, 8, 7, 5, 4, 2] + ubyte[7] blockSizes = [4, 3, 3, 4, 3, 3, 3] ; needed for proper rotation? (or just use block num?) + + ubyte[16] currentBlock + ubyte currentBlockSize ; 3 or 4 + ubyte currentBlockNum + + sub start() { + drawBoard() + + for ubyte b in 7 to 0 step -1 { + newCurrentBlock(b) + drawBlock(3, 2+b*3, 102) ; 102 = stipple + drawBlock(boardOffsetX+3, 1+b*3, 160) ; 160 = block, 32 = erase (space) + } + + while(true) { + ; loop + } + } + + sub drawBoard() { + c64scr.PLOT(1,1) + c64scr.print("teh▁triz") + c64scr.setcc(boardOffsetX-1, boardOffsetY+boardHeight, 124, 12) + c64scr.setcc(boardOffsetX+boardWidth, boardOffsetY+boardHeight, 126, 12) + ubyte i + for i in boardOffsetX+boardWidth-1 to boardOffsetX step -1 + c64scr.setcc(i, boardOffsetY+boardHeight, 69, 11) + for i in boardOffsetY+boardHeight-1 to boardOffsetY step -1 { + c64scr.setcc(boardOffsetX-1, i, 89, 11) + c64scr.setcc(boardOffsetX+boardWidth, i, 84, 11) + } + } + + + sub newCurrentBlock(ubyte block) { + memset(currentBlock, len(currentBlock), 0) + currentBlockNum = block + currentBlockSize = blockSizes[block] + + ; @todo would be nice to have an explicit pointer type to reference the array, and code the loop only once... + ubyte blockCol = blockColors[block] + ubyte i + if block==0 { ; I + for i in blockI + currentBlock[i] = blockCol + } + else if block==1 { ; J + for i in blockJ + currentBlock[i] = blockCol + } + else if block==2 { ; L + for i in blockL + currentBlock[i] = blockCol + } + else if block==3 { ; O + for i in blockO + currentBlock[i] = blockCol + } + else if block==4 { ; S + for i in blockS + currentBlock[i] = blockCol + } + else if block==5 { ; T + for i in blockT + currentBlock[i] = blockCol + } + else if block==6 { ; Z + for i in blockZ + currentBlock[i] = blockCol + } + } + + sub drawBlock(ubyte x, ubyte y, ubyte character) { + for ubyte i in 15 to 0 step -1 { + ubyte c=currentBlock[i] + if c + c64scr.setcc((i&3)+x, (i/4)+y, character, c) + } + } +} diff --git a/examples/test.p8 b/examples/test.p8 index 66f793941..d4bc0458c 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,34 +1,20 @@ -%import c64utils +%zeropage basicsafe + ~ main { + ; @todo fucks up basic - a few list: will corrupt the interpreter + + ubyte dummy + ubyte dummy2 + sub start() { - - foo(1) - bar(1,2) - baz(3333) - bzaz(60000) - } - - sub foo(byte arg) { - byte local = arg - A=44 - } - - sub bar(byte arg1, ubyte arg2) { - byte local1 = arg1 - ubyte local2 = arg2 - A=44 - } - - sub baz(word arg) { - word local=arg - A=44 - } - sub bzaz(uword arg) { - uword local=arg - A=44 + ubyte qq + ubyte qq2 + ubyte qq3 + ubyte qq4 + ubyte qq5 + c64scr.setcc(13, 10, 89, 11) } } - diff --git a/examples/wizzine.p8 b/examples/wizzine.p8 index db9ed4285..2ede8a07e 100644 --- a/examples/wizzine.p8 +++ b/examples/wizzine.p8 @@ -1,5 +1,6 @@ %import c64utils %import c64lib +%zeropage basicsafe ~ spritedata $0a00 { diff --git a/parser/src/prog8/parser/prog8Lexer.java b/parser/src/prog8/parser/prog8Lexer.java index 47f66e372..0b8da28a9 100644 --- a/parser/src/prog8/parser/prog8Lexer.java +++ b/parser/src/prog8/parser/prog8Lexer.java @@ -1,4 +1,4 @@ -// Generated from ./parser/antlr/prog8.g4 by ANTLR 4.7.2 +// Generated from prog8.g4 by ANTLR 4.7.2 package prog8.parser; diff --git a/parser/src/prog8/parser/prog8Parser.java b/parser/src/prog8/parser/prog8Parser.java index 5e1c76b65..1ab6baa85 100644 --- a/parser/src/prog8/parser/prog8Parser.java +++ b/parser/src/prog8/parser/prog8Parser.java @@ -1,4 +1,4 @@ -// Generated from ./parser/antlr/prog8.g4 by ANTLR 4.7.2 +// Generated from prog8.g4 by ANTLR 4.7.2 package prog8.parser; diff --git a/prog8.iml b/prog8.iml index a873d0513..c675202dc 100644 --- a/prog8.iml +++ b/prog8.iml @@ -17,5 +17,6 @@ + \ No newline at end of file