avoid zp corruption issues and added zp mode for floating point (todo: allocate)

This commit is contained in:
Irmen de Jong
2019-02-03 00:14:56 +01:00
parent ae21e03e1d
commit 7ff1af3934
33 changed files with 217 additions and 83 deletions

View File

@@ -182,7 +182,7 @@ fun determineCompilationOptions(moduleAst: Module): CompilationOptions {
val floatsEnabled = options.any { it.name == "enable_floats" } val floatsEnabled = options.any { it.name == "enable_floats" }
val zpType: ZeropageType = val zpType: ZeropageType =
if (zpoption == null) if (zpoption == null)
if(floatsEnabled) ZeropageType.BASICSAFE else ZeropageType.KERNALSAFE if(floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE
else else
try { try {
ZeropageType.valueOf(zpoption) ZeropageType.valueOf(zpoption)

View File

@@ -174,9 +174,9 @@ interface IAstProcessor {
return functionCall return functionCall
} }
fun process(functionCall: FunctionCallStatement): IStatement { fun process(functionCallStatement: FunctionCallStatement): IStatement {
functionCall.arglist = functionCall.arglist.map { it.process(this) }.toMutableList() functionCallStatement.arglist = functionCallStatement.arglist.map { it.process(this) }.toMutableList()
return functionCall return functionCallStatement
} }
fun process(identifier: IdentifierReference): IExpression { fun process(identifier: IdentifierReference): IExpression {

View File

@@ -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.parent !is Module) err("this directive may only occur at module level")
if(directive.args.size!=1 || if(directive.args.size!=1 ||
directive.args[0].name != "basicsafe" && directive.args[0].name != "basicsafe" &&
directive.args[0].name != "floatsafe" &&
directive.args[0].name != "kernalsafe" && directive.args[0].name != "kernalsafe" &&
directive.args[0].name != "full") 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" -> { "%zpreserved" -> {
if(directive.parent !is Module) err("this directive may only occur at module level") 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", "&", "|", "^" -> { "and", "or", "xor", "&", "|", "^" -> {
// only integer numeric operands accepted // 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) val leftDt = expr.left.resultingDatatype(namespace, heap)
if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes) if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes)
checkResult.add(ExpressionError("logical or bitwise operator can only be used on integer operands", expr.right.position)) 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) return super.process(functionCall)
} }
override fun process(functionCall: FunctionCallStatement): IStatement { override fun process(functionCallStatement: FunctionCallStatement): IStatement {
val targetStatement = checkFunctionOrLabelExists(functionCall.target, functionCall) val targetStatement = checkFunctionOrLabelExists(functionCallStatement.target, functionCallStatement)
if(targetStatement!=null) if(targetStatement!=null)
checkFunctionCall(targetStatement, functionCall.arglist, functionCall.position) checkFunctionCall(targetStatement, functionCallStatement.arglist, functionCallStatement.position)
if(targetStatement is Subroutine && targetStatement.returntypes.isNotEmpty()) if(targetStatement is Subroutine && targetStatement.returntypes.isNotEmpty())
printWarning("result value of subroutine call is discarded", functionCall.position) printWarning("result value of subroutine call is discarded", functionCallStatement.position)
return super.process(functionCall) return super.process(functionCallStatement)
} }
private fun checkFunctionCall(target: IStatement, args: List<IExpression>, position: Position) { private fun checkFunctionCall(target: IStatement, args: List<IExpression>, position: Position) {

View File

@@ -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")) 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 { override fun process(functionCallStatement: FunctionCallStatement): IStatement {
val scope = functionCall.definingScope() val scope = functionCallStatement.definingScope()
val targetStatement = functionCall.target.targetStatement(namespace) val targetStatement = functionCallStatement.target.targetStatement(namespace)
if(targetStatement!=null) { if(targetStatement!=null) {
val targetScope = when (targetStatement) { val targetScope = when (targetStatement) {
is Subroutine -> targetStatement is Subroutine -> targetStatement
@@ -102,7 +102,7 @@ private class AstRecursionChecker(private val namespace: INameScope) : IAstProce
} }
callGraph.add(scope, targetScope) callGraph.add(scope, targetScope)
} }
return super.process(functionCall) return super.process(functionCallStatement)
} }
override fun process(functionCall: FunctionCall): IExpression { override fun process(functionCall: FunctionCall): IExpression {

View File

@@ -38,10 +38,10 @@ private class StatementReorderer(private val namespace: INameScope, private val
.filter { it.value is Block && !(it.value as Block).isInLibrary } .filter { it.value is Block && !(it.value as Block).isInLibrary }
.map { it.index to it.value } .map { it.index to it.value }
.reversed() .reversed()
for(blocks in nonLibraryBlocks) for(nonLibBlock in nonLibraryBlocks)
module.statements.removeAt(blocks.first) module.statements.removeAt(nonLibBlock.first)
for(blocks in nonLibraryBlocks) for(nonLibBlock in nonLibraryBlocks)
module.statements.add(0, blocks.second) module.statements.add(0, nonLibBlock.second)
val mainBlock = module.statements.single { it is Block && it.name=="main" } val mainBlock = module.statements.single { it is Block && it.name=="main" }
if((mainBlock as Block).address==null) { if((mainBlock as Block).address==null) {
module.statements.remove(mainBlock) module.statements.remove(mainBlock)

View File

@@ -132,6 +132,7 @@ enum class LauncherType {
enum class ZeropageType { enum class ZeropageType {
BASICSAFE, BASICSAFE,
FLOATSAFE,
KERNALSAFE, KERNALSAFE,
FULL FULL
} }

View File

@@ -6,7 +6,7 @@ import prog8.ast.*
class ZeropageDepletedError(message: String) : Exception(message) class ZeropageDepletedError(message: String) : Exception(message)
abstract class Zeropage(private val options: CompilationOptions) { abstract class Zeropage(protected val options: CompilationOptions) {
private val allocations = mutableMapOf<Int, Pair<String, DataType>>() private val allocations = mutableMapOf<Int, Pair<String, DataType>>()
val free = mutableListOf<Int>() // subclasses must set this to the appropriate free locations. val free = mutableListOf<Int>() // 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 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()) 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
} }

View File

@@ -194,7 +194,15 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
out(" jsr ${block.scopedname}.${initVarsLabel.name}") out(" jsr ${block.scopedname}.${initVarsLabel.name}")
} }
out(" clc") out(" clc")
when(zeropage.exitProgramStrategy) {
Zeropage.ExitProgramStrategy.CLEAN_EXIT -> {
out(" jmp main.start\t; jump to program entrypoint") 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("") out("")
// the global list of all floating point constants for the whole program // the global list of all floating point constants for the whole program

View File

@@ -33,7 +33,16 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
const val SCRATCH_W2 = 0xfd // $fd/$fe 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 { init {
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) { if(options.zeropage == ZeropageType.FULL) {
free.addAll(0x04 .. 0xf9) free.addAll(0x04 .. 0xf9)
free.add(0xff) free.add(0xff)
@@ -47,6 +56,9 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 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)) 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 // add the other free Zp addresses
// these are valid for the C-64 (when no RS232 I/O is performed): // 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, free.addAll(listOf(0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0d, 0x0e,

View File

@@ -114,39 +114,39 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
} }
override fun process(functionCall: FunctionCallStatement): IStatement { override fun process(functionCallStatement: FunctionCallStatement): IStatement {
if(functionCall.target.nameInSource.size==1 && functionCall.target.nameInSource[0] in BuiltinFunctions) { if(functionCallStatement.target.nameInSource.size==1 && functionCallStatement.target.nameInSource[0] in BuiltinFunctions) {
val functionName = functionCall.target.nameInSource[0] val functionName = functionCallStatement.target.nameInSource[0]
if (functionName in pureBuiltinFunctions) { if (functionName in pureBuiltinFunctions) {
printWarning("statement has no effect (function return value is discarded)", functionCall.position) printWarning("statement has no effect (function return value is discarded)", functionCallStatement.position)
statementsToRemove.add(functionCall) statementsToRemove.add(functionCallStatement)
return functionCall return functionCallStatement
} }
} }
if(functionCall.target.nameInSource==listOf("c64scr", "print") || if(functionCallStatement.target.nameInSource==listOf("c64scr", "print") ||
functionCall.target.nameInSource==listOf("c64scr", "print_p")) { functionCallStatement.target.nameInSource==listOf("c64scr", "print_p")) {
// printing a literal string of just 2 or 1 characters is replaced by directly outputting those characters // 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") 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) { if(stringVar!=null) {
val heapId = stringVar.heapId(namespace) val heapId = stringVar.heapId(namespace)
val string = heap.get(heapId).str!! val string = heap.get(heapId).str!!
if(string.length==1) { if(string.length==1) {
val petscii = Petscii.encodePetscii(string, true)[0] val petscii = Petscii.encodePetscii(string, true)[0]
functionCall.arglist.clear() functionCallStatement.arglist.clear()
functionCall.arglist.add(LiteralValue.optimalInteger(petscii, functionCall.position)) functionCallStatement.arglist.add(LiteralValue.optimalInteger(petscii, functionCallStatement.position))
functionCall.target = IdentifierReference(listOf("c64", "CHROUT"), functionCall.target.position) functionCallStatement.target = IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position)
optimizationsDone++ optimizationsDone++
return functionCall return functionCallStatement
} else if(string.length==2) { } else if(string.length==2) {
val petscii = Petscii.encodePetscii(string, true) val petscii = Petscii.encodePetscii(string, true)
val scope = AnonymousScope(mutableListOf(), functionCall.position) val scope = AnonymousScope(mutableListOf(), functionCallStatement.position)
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("c64", "CHROUT"), functionCall.target.position), scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position),
mutableListOf(LiteralValue.optimalInteger(petscii[0], functionCall.position)), functionCall.position)) mutableListOf(LiteralValue.optimalInteger(petscii[0], functionCallStatement.position)), functionCallStatement.position))
scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("c64", "CHROUT"), functionCall.target.position), scope.statements.add(FunctionCallStatement(IdentifierReference(listOf("c64", "CHROUT"), functionCallStatement.target.position),
mutableListOf(LiteralValue.optimalInteger(petscii[1], functionCall.position)), functionCall.position)) mutableListOf(LiteralValue.optimalInteger(petscii[1], functionCallStatement.position)), functionCallStatement.position))
optimizationsDone++ optimizationsDone++
return scope return scope
} }
@@ -156,20 +156,20 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
// if it calls a subroutine, // if it calls a subroutine,
// and the first instruction in the subroutine is a jump, call that jump target instead // 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 // 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) { if(subroutine!=null) {
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull() val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
if(first is Jump && first.identifier!=null) { if(first is Jump && first.identifier!=null) {
optimizationsDone++ optimizationsDone++
return FunctionCallStatement(first.identifier, functionCall.arglist, functionCall.position) return FunctionCallStatement(first.identifier, functionCallStatement.arglist, functionCallStatement.position)
} }
if(first is ReturnFromIrq || first is Return) { if(first is ReturnFromIrq || first is Return) {
optimizationsDone++ optimizationsDone++
return NopStatement(functionCall.position) return NopStatement(functionCallStatement.position)
} }
} }
return super.process(functionCall) return super.process(functionCallStatement)
} }
override fun process(functionCall: FunctionCall): IExpression { override fun process(functionCall: FunctionCall): IExpression {

View File

@@ -60,16 +60,20 @@ Directives
- style ``kernalsafe`` -- use the part of the ZP that is 'free' or only used by BASIC routines, - 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), 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. 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 - 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 touch change anything else. This allows full use of BASIC and KERNAL ROM routines including default IRQs
during normal system operation. 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, - 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. 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. 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``. 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. 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 many more variables can This option makes programs smaller and faster because even more variables can
be stored in the ZP, which is more efficient. be stored in the ZP (which allows for more efficient assembly code).
Also read :ref:`zeropage`. Also read :ref:`zeropage`.

View File

@@ -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 - it's more convenient and safe to let the compiler allocate these addresses for you and just
use symbolic names in the program code. 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. 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, 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 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. 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). 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. 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 IRQs and the ZeroPage

View File

@@ -1,4 +1,6 @@
%import c64utils %import c64utils
%zeropage basicsafe
~ main { ~ main {

View File

@@ -1,5 +1,6 @@
%import c64utils %import c64utils
%import c64flt %import c64flt
%zeropage basicsafe
~ main { ~ main {

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,6 @@
%import c64utils %import c64utils
%import c64flt %import c64flt
%zeropage basicsafe
~ main { ~ main {

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
%import c64utils %import c64utils
%zeropage basicsafe
; This example computes the first 20 values of the Fibonacci sequence. ; This example computes the first 20 values of the Fibonacci sequence.
; It uses the feature that variables that don't have an initialization value, ; It uses the feature that variables that don't have an initialization value,

View File

@@ -1,6 +1,7 @@
%import c64lib %import c64lib
%import c64utils %import c64utils
%import c64flt %import c64flt
%zeropage basicsafe
~ main { ~ main {

View File

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

View File

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

View File

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

100
examples/tehtriz.p8 Normal file
View File

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

View File

@@ -1,34 +1,20 @@
%import c64utils %zeropage basicsafe
~ main { ~ main {
; @todo fucks up basic - a few list: will corrupt the interpreter
ubyte dummy
ubyte dummy2
sub start() { sub start() {
ubyte qq
foo(1) ubyte qq2
bar(1,2) ubyte qq3
baz(3333) ubyte qq4
bzaz(60000) ubyte qq5
} c64scr.setcc(13, 10, 89, 11)
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
} }
} }

View File

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

View File

@@ -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; package prog8.parser;

View File

@@ -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; package prog8.parser;

View File

@@ -17,5 +17,6 @@
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Python 3.7 interpreter library" level="application" />
</component> </component>
</module> </module>