zp allocations

This commit is contained in:
Irmen de Jong 2019-01-27 01:02:45 +01:00
parent 204cc03fc8
commit 3a8f069854
12 changed files with 229 additions and 108 deletions

View File

@ -3,6 +3,7 @@ package prog8
import prog8.ast.*
import prog8.compiler.*
import prog8.compiler.target.c64.AsmGen
import prog8.compiler.target.c64.C64Zeropage
import prog8.optimizing.constantFold
import prog8.optimizing.optimizeStatements
import prog8.optimizing.simplifyExpressions
@ -131,7 +132,9 @@ private fun compileMain(args: Array<String>) {
}
if(writeAssembly) {
val assembly = AsmGen(compilerOptions, intermediate, heap).compileToAssembly()
val zeropage = C64Zeropage(compilerOptions)
intermediate.allocateZeropage(zeropage)
val assembly = AsmGen(compilerOptions, intermediate, heap, zeropage).compileToAssembly()
assembly.assemble(compilerOptions)
programname = assembly.name
}
@ -176,9 +179,10 @@ fun determineCompilationOptions(moduleAst: Module): CompilationOptions {
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 floatsEnabled = options.any { it.name == "enable_floats" }
val zpType: ZeropageType =
if (zpoption == null)
ZeropageType.KERNALSAFE
if(floatsEnabled) ZeropageType.BASICSAFE else ZeropageType.KERNALSAFE
else
try {
ZeropageType.valueOf(zpoption)
@ -196,8 +200,8 @@ fun determineCompilationOptions(moduleAst: Module): CompilationOptions {
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" })
zpType, zpReserved, floatsEnabled
)
}
private fun usage() {

View File

@ -42,9 +42,14 @@ fun printWarning(msg: String, position: Position, detailInfo: String?=null) {
print("\n")
else
println(": $detailInfo")
print("\u001b[0m") // bright yellow
print("\u001b[0m") // normal
}
fun printWarning(msg: String) {
print("\u001b[93m") // bright yellow
print("Warning: $msg")
print("\u001b[0m") // normal
}
private class AstChecker(private val namespace: INameScope,
private val compilerOptions: CompilationOptions,
@ -131,9 +136,6 @@ private class AstChecker(private val namespace: INameScope,
}
override fun process(forLoop: ForLoop): IStatement {
if(forLoop.zeropage)
println("ZEROPAGE FORLOOP $forLoop") // TODO
if(forLoop.body.isEmpty())
printWarning("for loop body is empty", forLoop.position)
@ -484,9 +486,6 @@ private class AstChecker(private val namespace: INameScope,
checkResult.add(SyntaxError(msg, position ?: decl.position))
}
if(decl.zeropage)
println("ZEROPAGE VAR $decl") // TODO
// the initializer value can't refer to the variable itself (recursive definition)
if(decl.value?.referencesIdentifier(decl.name) == true || decl.arrayspec?.x?.referencesIdentifier(decl.name) == true) {
err("recursive var declaration")

View File

@ -155,7 +155,7 @@ internal class Compiler(private val rootModule: Module,
private val continueStmtLabelStack : Stack<String> = Stack()
fun compile(options: CompilationOptions) : IntermediateProgram {
println("\nCreating stackVM code...")
println("Creating stackVM code...")
process(rootModule)
return prog
}
@ -2103,7 +2103,6 @@ internal class Compiler(private val rootModule: Module,
prog.instr(Opcode.NOP)
breakStmtLabelStack.pop()
continueStmtLabelStack.pop()
}
private fun translate(stmt: RepeatLoop)

View File

@ -3,6 +3,9 @@ package prog8.compiler
import prog8.ast.*
class ZeropageDepletedError(message: String) : Exception(message)
abstract class Zeropage(private val options: CompilationOptions) {
private val allocations = mutableMapOf<Int, Pair<String, DataType>>()
@ -10,6 +13,9 @@ abstract class Zeropage(private val options: CompilationOptions) {
fun available() = free.size
fun allocate(name: String, type: DataType) =
allocate(VarDecl(VarDeclType.VAR, type, true, null, name, null, Position("",0,0,0)))
fun allocate(vardecl: VarDecl) : Int {
assert(vardecl.name.isEmpty() || !allocations.values.any { it.first==vardecl.name } ) {"same name can't be allocated twice"}
assert(vardecl.type== VarDeclType.VAR) {"can only allocate VAR type"}
@ -18,15 +24,15 @@ abstract class Zeropage(private val options: CompilationOptions) {
if(vardecl.arrayspec!=null) {
printWarning("allocating a large value (arrayspec) in zeropage", vardecl.position)
when(vardecl.datatype) {
DataType.UBYTE -> (vardecl.arrayspec.x as LiteralValue).asIntegerValue!!
DataType.UWORD -> (vardecl.arrayspec.x as LiteralValue).asIntegerValue!! * 2
DataType.UBYTE, DataType.BYTE -> (vardecl.arrayspec.x as LiteralValue).asIntegerValue!!
DataType.UWORD, DataType.UWORD -> (vardecl.arrayspec.x as LiteralValue).asIntegerValue!! * 2
DataType.FLOAT -> (vardecl.arrayspec.x as LiteralValue).asIntegerValue!! * 5
else -> throw CompilerException("array can only be of byte, word, float")
}
} else {
when (vardecl.datatype) {
DataType.UBYTE -> 1
DataType.UWORD -> 2
DataType.UBYTE, DataType.BYTE -> 1
DataType.UWORD, DataType.WORD -> 2
DataType.FLOAT -> {
if (options.floats) {
printWarning("allocating a large value (float) in zeropage", vardecl.position)
@ -51,17 +57,17 @@ abstract class Zeropage(private val options: CompilationOptions) {
}
}
throw CompilerException("ERROR: no free space in ZP to allocate $size sequential bytes")
throw ZeropageDepletedError("ERROR: no free space in ZP to allocate $size sequential bytes")
}
protected fun reserve(range: IntRange) = free.removeAll(range)
private fun makeAllocation(location: Int, size: Int, datatype: DataType, name: String?): Int {
free.removeAll(location until location+size)
allocations[location] = Pair(name ?: "<unnamed>", datatype)
return location
private fun makeAllocation(address: Int, size: Int, datatype: DataType, name: String?): Int {
free.removeAll(address until address+size)
allocations[address] = Pair(name ?: "<unnamed>", datatype)
return address
}
private fun loneByte(location: Int) = location in free && location-1 !in free && location+1 !in free
private fun sequentialFree(location: Int, size: Int) = free.containsAll((location until location+size).toList())
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())
}

View File

@ -3,6 +3,8 @@ package prog8.compiler.intermediate
import prog8.ast.*
import prog8.compiler.CompilerException
import prog8.compiler.HeapValues
import prog8.compiler.Zeropage
import prog8.compiler.ZeropageDepletedError
import java.io.PrintStream
import java.nio.file.Path
@ -22,8 +24,10 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
get() { return variables.size }
val numInstructions: Int
get() { return instructions.filter { it.opcode!= Opcode.LINE }.size }
val variablesMarkedForZeropage: MutableSet<String> = mutableSetOf()
}
val allocatedZeropageVariables = mutableMapOf<String, Pair<Int, DataType>>()
val blocks = mutableListOf<ProgramBlock>()
val memory = mutableMapOf<Int, List<Value>>()
private lateinit var currentBlock: ProgramBlock
@ -33,6 +37,30 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
val numInstructions: Int
get() = blocks.sumBy { it.numInstructions }
fun allocateZeropage(zeropage: Zeropage) {
// allocates all @zp marked variables on the zeropage (for all blocks, as long as there is space in the ZP)
var notAllocated = 0
for(block in blocks) {
val zpVariables = block.variables.filter { it.key in block.variablesMarkedForZeropage }
if (zpVariables.isNotEmpty()) {
for (variable in zpVariables) {
try {
val address = zeropage.allocate(variable.key, variable.value.type)
allocatedZeropageVariables[variable.key] = Pair(address, variable.value.type)
println("DEBUG: allocated on ZP: $variable @ $address") //TODO
block.variablesMarkedForZeropage.remove(variable.key)//TODO weg
} catch (x: ZeropageDepletedError) {
printWarning(x.toString() + " variable ${variable.key} type ${variable.value.type}")
notAllocated++
}
}
}
}
println("DEBUG: ${allocatedZeropageVariables.size} variables allocated in Zeropage") // TODO
if(notAllocated>0)
printWarning("$notAllocated variables marked for Zeropage could not be allocated there")
}
fun optimize() {
println("Optimizing stackVM code...")
// remove nops (that are not a label)
@ -328,6 +356,8 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
}
}
currentBlock.variables[scopedname] = value
if(decl.zeropage)
currentBlock.variablesMarkedForZeropage.add(scopedname)
}
VarDeclType.MEMORY -> {
// note that constants are all folded away, but assembly code may still refer to them

View File

@ -3,8 +3,7 @@ package prog8.compiler.target.c64
// note: to put stuff on the stack, we use Absolute,X addressing mode which is 3 bytes / 4 cycles
// possible space optimization is to use zeropage (indirect),Y which is 2 bytes, but 5 cycles
import prog8.ast.DataType
import prog8.ast.escape
import prog8.ast.*
import prog8.compiler.*
import prog8.compiler.intermediate.*
import prog8.stackvm.Syscall
@ -17,7 +16,7 @@ import kotlin.math.abs
class AssemblyError(msg: String) : RuntimeException(msg)
class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, val heap: HeapValues) {
class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, val heap: HeapValues, val zeropage: Zeropage) {
private val globalFloatConsts = mutableMapOf<Double, String>()
private val assemblyLines = mutableListOf<String>()
private lateinit var block: IntermediateProgram.ProgramBlock
@ -27,9 +26,11 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
// Because 64tass understands scoped names via .proc / .block,
// we'll strip the block prefix from all scoped names in the program.
// Also, convert invalid label names (such as "<<<anonymous-1>>>") to something that's allowed.
// Also have to do that for the variablesMarkedForZeropage!
val newblocks = mutableListOf<IntermediateProgram.ProgramBlock>()
for(block in program.blocks) {
val newvars = block.variables.map { symname(it.key, block) to it.value }.toMap().toMutableMap()
val newvarsZeropaged = block.variablesMarkedForZeropage.map{symname(it, block)}.toMutableSet()
val newlabels = block.labels.map { symname(it.key, block) to it.value}.toMap().toMutableMap()
val newinstructions = block.instructions.asSequence().map {
when {
@ -42,7 +43,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
}
}.toMutableList()
val newConstants = block.memoryPointers.map { symname(it.key, block) to it.value }.toMap().toMutableMap()
newblocks.add(IntermediateProgram.ProgramBlock(
val newblock = IntermediateProgram.ProgramBlock(
block.scopedname,
block.shortname,
block.address,
@ -50,7 +51,10 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
newvars,
newConstants,
newlabels,
force_output = block.force_output))
force_output = block.force_output)
newblock.variablesMarkedForZeropage.clear()
newblock.variablesMarkedForZeropage.addAll(newvarsZeropaged)
newblocks.add(newblock)
}
program.blocks.clear()
program.blocks.addAll(newblocks)
@ -66,7 +70,7 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
}
fun compileToAssembly(): AssemblyProgram {
println("\nGenerating assembly code from intermediate code... ")
println("Generating assembly code from intermediate code... ")
assemblyLines.clear()
header()
@ -189,9 +193,12 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
out(".cerror * > ${block.address?.toHex()}, 'block address overlaps by ', *-${block.address?.toHex()},' bytes'")
out("* = ${block.address?.toHex()}")
}
// @TODO allocate variables on the zeropage as long as there is space
out("\n; memdefs and kernel subroutines")
memdefs2asm(block)
out("\n; variables")
out("\n; non-zeropage variables")
vardecls2asm(block)
out("")
@ -219,7 +226,8 @@ class AsmGen(val options: CompilationOptions, val program: IntermediateProgram,
}
private fun vardecls2asm(block: IntermediateProgram.ProgramBlock) {
val sortedVars = block.variables.toList().sortedBy { it.second.type }
// these are the non-zeropage variables @todo is this correct now?
val sortedVars = block.variables.filter{it.key !in block.variablesMarkedForZeropage}.toList().sortedBy { it.second.type }
for (v in sortedVars) {
when (v.second.type) {
DataType.UBYTE -> out("${v.first}\t.byte 0")

View File

@ -52,7 +52,7 @@ val BuiltinFunctions = mapOf(
"round" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArgOutputWord(a, p, n, h, Math::round) },
"floor" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArgOutputWord(a, p, n, h, Math::floor) },
"ceil" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, n, h -> oneDoubleArgOutputWord(a, p, n, h, Math::ceil) },
"len" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", IterableDatatypes)), DataType.UBYTE, ::builtinLen),
"len" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", IterableDatatypes)), DataType.UWORD, ::builtinLen),
"any" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, n, h -> collectionArgOutputBoolean(a, p, n, h) { it.any { v -> v != 0.0} }},
"all" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, n, h -> collectionArgOutputBoolean(a, p, n, h) { it.all { v -> v != 0.0} }},
"lsb" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, n, h -> oneIntArgOutputInt(a, p, n, h) { x: Int -> x and 255 }},
@ -285,6 +285,7 @@ private fun builtinAvg(args: List<IExpression>, position: Position, namespace:IN
}
private fun builtinLen(args: List<IExpression>, position: Position, namespace:INameScope, heap: HeapValues): LiteralValue {
// note: in some cases the length is > 255 and so we have to return a UWORD type instead of a byte.
if(args.size!=1)
throw SyntaxError("len requires one argument", position)
var argument = args[0].constValue(namespace, heap)
@ -299,21 +300,21 @@ private fun builtinLen(args: List<IExpression>, position: Position, namespace:IN
return when(argument.type) {
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
val arraySize = argument.arrayvalue?.size ?: heap.get(argument.heapId!!).arraysize
if(arraySize>255)
if(arraySize>256)
throw CompilerException("array length exceeds byte limit ${argument.position}")
LiteralValue(DataType.UBYTE, bytevalue=arraySize.toShort(), position=args[0].position)
LiteralValue(DataType.UWORD, wordvalue=arraySize, position=args[0].position)
}
DataType.ARRAY_F -> {
val arraySize = argument.arrayvalue?.size ?: heap.get(argument.heapId!!).arraysize
if(arraySize>255)
if(arraySize>256)
throw CompilerException("array length exceeds byte limit ${argument.position}")
LiteralValue(DataType.UBYTE, bytevalue=arraySize.toShort(), position=args[0].position)
LiteralValue(DataType.UWORD, wordvalue=arraySize, position=args[0].position)
}
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> {
val str = argument.strvalue(heap)
if(str.length>255)
throw CompilerException("string length exceeds byte limit ${argument.position}")
LiteralValue(DataType.UBYTE, bytevalue=str.length.toShort(), position=args[0].position)
LiteralValue(DataType.UWORD, wordvalue=str.length, position=args[0].position)
}
DataType.UBYTE, DataType.BYTE,
DataType.UWORD, DataType.WORD,

View File

@ -44,13 +44,6 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
// remove empty subroutine
optimizationsDone++
statementsToRemove.add(subroutine)
} else if(subroutine.statements.size==1) {
val stmt = subroutine.statements[0]
if(stmt is ReturnFromIrq || stmt is Return) {
// remove empty subroutine
optimizationsDone++
statementsToRemove.add(subroutine)
}
}
}
@ -157,6 +150,7 @@ 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
if(subroutine!=null) {
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
@ -164,6 +158,10 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
optimizationsDone++
return FunctionCallStatement(first.identifier, functionCall.arglist, functionCall.position)
}
if(first is ReturnFromIrq || first is Return) {
optimizationsDone++
return NopStatement(functionCall.position)
}
}
return super.process(functionCall)
@ -172,6 +170,7 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
override fun process(functionCall: FunctionCall): IExpression {
// 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 with constant value, replace with the constant value
val subroutine = functionCall.target.targetStatement(namespace) as? Subroutine
if(subroutine!=null) {
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
@ -179,6 +178,11 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
optimizationsDone++
return FunctionCall(first.identifier, functionCall.arglist, functionCall.position)
}
if(first is Return && first.values.size==1) {
val constval = first.values[0].constValue(namespace, heap)
if(constval!=null)
return constval
}
}
return super.process(functionCall)
}
@ -255,8 +259,10 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
val constvalue = whileLoop.condition.constValue(namespace, heap)
if(constvalue!=null) {
return if(constvalue.asBooleanValue){
// always true -> print a warning, and optimize into body + jump
// always true -> print a warning, and optimize into body + jump (if there are no continue and break statements)
printWarning("condition is always true", whileLoop.position)
if(hasContinueOrBreak(whileLoop.body))
return whileLoop
val label = Label("__back", whileLoop.condition.position)
whileLoop.body.statements.add(0, label)
whileLoop.body.statements.add(Jump(null,
@ -284,8 +290,10 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
optimizationsDone++
repeatLoop.body
} else {
// always false -> print a warning, and optimize into body + jump
// always false -> print a warning, and optimize into body + jump (if there are no continue and break statements)
printWarning("condition is always false", repeatLoop.position)
if(hasContinueOrBreak(repeatLoop.body))
return repeatLoop
val label = Label("__back", repeatLoop.untilCondition.position)
repeatLoop.body.statements.add(0, label)
repeatLoop.body.statements.add(Jump(null,
@ -298,6 +306,31 @@ class StatementOptimizer(private val namespace: INameScope, private val heap: He
return repeatLoop
}
private fun hasContinueOrBreak(scope: INameScope): Boolean {
class Searcher:IAstProcessor
{
var count=0
override fun process(breakStmt: Break): IStatement {
count++
return super.process(breakStmt)
}
override fun process(contStmt: Continue): IStatement {
count++
return super.process(contStmt)
}
}
val s=Searcher()
for(stmt in scope.statements) {
stmt.process(s)
if(s.count>0)
return true
}
return s.count > 0
}
override fun process(jump: Jump): IStatement {
val subroutine = jump.identifier?.targetStatement(namespace) as? Subroutine
if(subroutine!=null) {

View File

@ -188,7 +188,7 @@ class TestZeropage {
assertEquals(19, zp.available())
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, false, null, "", null, dummypos))
assertFailsWith<CompilerException> {
assertFailsWith<ZeropageDepletedError> {
// in regular zp there aren't 5 sequential bytes free after we take the first sequence
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, false, null, "", null, dummypos))
}
@ -198,10 +198,10 @@ class TestZeropage {
assertTrue(loc > 0)
}
assertEquals(0, zp.available())
assertFailsWith<CompilerException> {
assertFailsWith<ZeropageDepletedError> {
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, "", null, dummypos))
}
assertFailsWith<CompilerException> {
assertFailsWith<ZeropageDepletedError> {
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UWORD, false, null, "", null, dummypos))
}
}
@ -221,7 +221,7 @@ class TestZeropage {
}
assertEquals(18,zp.available())
assertFailsWith<CompilerException> {
assertFailsWith<ZeropageDepletedError> {
// can't allocate because no more sequential bytes, only fragmented
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, false, null, "", null, dummypos))
}
@ -234,7 +234,7 @@ class TestZeropage {
assertEquals(2, zp.available())
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, "", null, dummypos))
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, "", null, dummypos))
assertFailsWith<CompilerException> {
assertFailsWith<ZeropageDepletedError> {
// no more space
zp.allocate(VarDecl(VarDeclType.VAR, DataType.UBYTE, false, null, "", null, dummypos))
}

33
examples/fibonacci.p8 Normal file
View File

@ -0,0 +1,33 @@
%import c64utils
; This example computes the first 20 values of the Fibonacci sequence.
; It uses the feature that variables that don't have an initialization value,
; will retain their previous value over multiple invocations of the program or subroutine.
; This is extremely handy for the Fibonacci sequence because it is defined
; in terms of 'the next value is the sum of the previous two values'
~ main {
sub start() {
c64scr.print("fibonacci sequence\n")
fib_setup()
for ubyte i in 0 to 20 {
c64scr.print_uw(fib_next())
c64.CHROUT('\n')
}
}
sub fib_setup() {
; (re)start the sequence
main.fib_next.prev=0
main.fib_next.current=1
}
sub fib_next() -> uword {
uword prev ; no init value so will retain previous value
uword current ; no init value so will retain previous value
uword new = current + prev
prev = current
current = new
return prev
}
}

41
examples/primes.p8 Normal file
View File

@ -0,0 +1,41 @@
%import c64utils
~ main {
sub start() {
; clear the sieve, and mark 0 and 1 as not prime.
memset(sieve, 256, false)
sieve[0] = true
sieve[1] = true
; calculate primes
c64scr.print("prime numbers up to 255:\n\n")
while true {
ubyte prime = find_next_prime()
if prime==0
break
c64scr.print_ub(prime)
c64scr.print(", ")
}
c64.CHROUT('\n')
}
ubyte[256] sieve
sub find_next_prime() -> ubyte {
for ubyte prime in 2 to 255 {
if not sieve[prime] {
; found one, mark the multiples and return it.
sieve[prime] = true
uword multiple = prime**2
while multiple < len(sieve) {
sieve[lsb(multiple)] = true
multiple += prime
}
return prime
}
}
return 0
}
}

View File

@ -1,68 +1,35 @@
%import c64utils
%import c64lib
~ spritedata $0a00 {
; this memory block contains the sprite data
; it must start on an address aligned to 64 bytes.
%option force_output ; make sure the data in this block appears in the resulting program
ubyte[63] balloonsprite = [ %00000000,%01111111,%00000000,
%00000001,%11111111,%11000000,
%00000011,%11111111,%11100000,
%00000011,%11100011,%11100000,
%00000111,%11011100,%11110000,
%00000111,%11011101,%11110000,
%00000111,%11011100,%11110000,
%00000011,%11100011,%11100000,
%00000011,%11111111,%11100000,
%00000011,%11111111,%11100000,
%00000010,%11111111,%10100000,
%00000001,%01111111,%01000000,
%00000001,%00111110,%01000000,
%00000000,%10011100,%10000000,
%00000000,%10011100,%10000000,
%00000000,%01001001,%00000000,
%00000000,%01001001,%00000000,
%00000000,%00111110,%00000000,
%00000000,%00111110,%00000000,
%00000000,%00111110,%00000000,
%00000000,%00011100,%00000000 ]
}
~ main {
sub start() {
for ubyte i in 0 to 7 {
c64.SPRPTR[i] = $0a00/64
while true {
A=99
}
c64.SPENA = 255 ; enable all sprites
c64utils.set_rasterirq(220) ; enable animation
}
}
~ irq {
sub irq() {
ubyte@zp angle ; no initialization value so it keeps the previous one.
c64.EXTCOL--
angle++
c64.MSIGX=0
for ubyte@zp i in 7 to 0 step -1 {
uword@zp x = sin8u(angle*2-i*16) as uword + 50
ubyte@zp y = cos8u(angle*3-i*16) / 2 + 70
c64.SPXYW[i] = mkword(lsb(x), y)
lsl(c64.MSIGX)
if msb(x) c64.MSIGX++
c64.EXTCOL++
while true {
A=99
if A>100
break
}
c64.EXTCOL-=7
repeat {
A=99
} until false
repeat {
A=99
if A>100
break
} until false
}
; @todo code for pow()
; @todo optimize code generation for "if blah ..." and "if not blah ..."
; @todo optimize vm
; push_byte ub:01
; jnz _prog8stmt_7_loop
}