mirror of
https://github.com/irmen/prog8.git
synced 2024-09-27 11:56:10 +00:00
Got rid of problematic attempts to save status register after function calls. If you really need it (for instance for if_XX instructions) it's probably better to use a short asmsub wrapper.
For function calls, register saves go via stack (to allow nested saves) for simpler cases, registers are saved in a local variable. Fixed too agressive removal of sta-lda sequence if the lda is followed by a branching instruction. Insert missing cmp #0 after functioncall if the value of the A register is needed in a comparison expression (could otherwise test wrong status flag)
This commit is contained in:
parent
f1d55c688a
commit
928611eb20
@ -225,6 +225,27 @@ romsub $FFF3 = IOBASE() -> uword @ XY ; read base addr
|
||||
|
||||
; ---- end of C64 ROM kernal routines ----
|
||||
|
||||
; ---- utilities -----
|
||||
|
||||
asmsub STOP2() -> ubyte @A {
|
||||
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
|
||||
%asm {{
|
||||
txa
|
||||
pha
|
||||
jsr c64.STOP
|
||||
beq +
|
||||
pla
|
||||
tax
|
||||
lda #0
|
||||
rts
|
||||
+ pla
|
||||
tax
|
||||
lda #1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
|
||||
; ---- C64 specific system utility routines: ----
|
||||
|
||||
|
@ -56,6 +56,23 @@ romsub $FFED = SCREEN() -> ubyte @ X, ubyte @ Y ; read number of
|
||||
romsub $FFF0 = PLOT(ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y ; read/set position of cursor on screen. Use txt.plot for a 'safe' wrapper that preserves X.
|
||||
romsub $FFF3 = IOBASE() -> uword @ XY ; read base address of I/O devices
|
||||
|
||||
; ---- utility
|
||||
|
||||
asmsub STOP2() -> ubyte @A {
|
||||
; -- check if STOP key was pressed, returns true if so. More convenient to use than STOP() because that only sets the carry status flag.
|
||||
%asm {{
|
||||
phx
|
||||
jsr c64.STOP
|
||||
beq +
|
||||
plx
|
||||
lda #0
|
||||
rts
|
||||
+ plx
|
||||
lda #1
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cx16 {
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
; Note: this code is compatible with C64 and CX16.
|
||||
|
||||
; TODO directory() BROKEN ON C64, SEEMS TO WORK ON CX16
|
||||
|
||||
diskio {
|
||||
|
||||
|
||||
@ -30,16 +32,17 @@ diskio {
|
||||
txt.print_uw(mkword(high, low))
|
||||
txt.chrout(' ')
|
||||
ubyte @zp char
|
||||
do {
|
||||
repeat {
|
||||
char = c64.CHRIN()
|
||||
if char==0 ; TODO doesn't work???
|
||||
break
|
||||
txt.chrout(char)
|
||||
} until char==0
|
||||
}
|
||||
txt.chrout('\n')
|
||||
void c64.CHRIN() ; skip 2 bytes
|
||||
void c64.CHRIN()
|
||||
status = c64.READST()
|
||||
void c64.STOP()
|
||||
if_z
|
||||
if c64.STOP2()
|
||||
break
|
||||
}
|
||||
|
||||
@ -160,7 +163,7 @@ io_error:
|
||||
; read the filename
|
||||
repeat {
|
||||
ubyte char = c64.CHRIN()
|
||||
if_z
|
||||
if char==0
|
||||
break
|
||||
if char=='\"'
|
||||
break
|
||||
|
@ -757,7 +757,6 @@ class Subroutine(override val name: String,
|
||||
|
||||
fun regXasResult() = asmReturnvaluesRegisters.any { it.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
|
||||
fun regXasParam() = asmParameterRegisters.any { it.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
|
||||
fun shouldPreserveStatusRegisterAfterCall() = asmReturnvaluesRegisters.any { it.statusflag != null } || shouldSaveX()
|
||||
fun shouldSaveX() = CpuRegister.X in asmClobbers || regXasResult() || regXasParam()
|
||||
|
||||
fun amountOfRtsInAsm(): Int = statements
|
||||
|
@ -547,8 +547,20 @@ internal class AsmGen(private val program: Program,
|
||||
|
||||
private fun fixNameSymbols(name: String) = name.replace("<", "prog8_").replace(">", "") // take care of the autogenerated invalid (anon) label names
|
||||
|
||||
internal fun saveRegister(register: CpuRegister, dontUseStack: Boolean, scope: Subroutine) {
|
||||
if(dontUseStack) {
|
||||
internal fun saveRegister(register: CpuRegister, scope: Subroutine, useStack: Boolean) {
|
||||
if(useStack) {
|
||||
when (register) {
|
||||
CpuRegister.A -> out(" pha")
|
||||
CpuRegister.X -> {
|
||||
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" phx")
|
||||
else out(" sta P8ZP_SCRATCH_REG | txa | pha | lda P8ZP_SCRATCH_REG")
|
||||
}
|
||||
CpuRegister.Y -> {
|
||||
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" phy")
|
||||
else out(" sta P8ZP_SCRATCH_REG | tya | pha | lda P8ZP_SCRATCH_REG")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
when (register) {
|
||||
CpuRegister.A -> {
|
||||
out(" sta _prog8_regsaveA")
|
||||
@ -563,48 +575,28 @@ internal class AsmGen(private val program: Program,
|
||||
scope.asmGenInfo.usedRegsaveY = true
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
when (register) {
|
||||
CpuRegister.A -> out(" pha")
|
||||
CpuRegister.X -> {
|
||||
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" phx")
|
||||
else {
|
||||
out(" stx _prog8_regsaveX")
|
||||
scope.asmGenInfo.usedRegsaveX = true
|
||||
}
|
||||
}
|
||||
CpuRegister.Y -> {
|
||||
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" phy")
|
||||
else {
|
||||
out(" sty _prog8_regsaveY")
|
||||
scope.asmGenInfo.usedRegsaveY = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun restoreRegister(register: CpuRegister, dontUseStack: Boolean) {
|
||||
if(dontUseStack) {
|
||||
when (register) {
|
||||
CpuRegister.A -> out(" sta _prog8_regsaveA")
|
||||
CpuRegister.X -> out(" ldx _prog8_regsaveX")
|
||||
CpuRegister.Y -> out(" ldy _prog8_regsaveY")
|
||||
}
|
||||
|
||||
} else {
|
||||
internal fun restoreRegister(register: CpuRegister, useStack: Boolean) {
|
||||
if(useStack) {
|
||||
when (register) {
|
||||
CpuRegister.A -> out(" pla")
|
||||
CpuRegister.X -> {
|
||||
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" plx")
|
||||
else out(" ldx _prog8_regsaveX")
|
||||
else out(" pla | tax")
|
||||
}
|
||||
CpuRegister.Y -> {
|
||||
if (CompilationTarget.instance.machine.cpu == CpuType.CPU65c02) out(" ply")
|
||||
else out(" ldy _prog8_regsaveY")
|
||||
else out(" pla | tay")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
when (register) {
|
||||
CpuRegister.A -> out(" sta _prog8_regsaveA")
|
||||
CpuRegister.X -> out(" ldx _prog8_regsaveX")
|
||||
CpuRegister.Y -> out(" ldy _prog8_regsaveY")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -738,8 +730,8 @@ internal class AsmGen(private val program: Program,
|
||||
internal fun translateBuiltinFunctionCallExpression(functionCall: FunctionCall, signature: FSignature, resultToStack: Boolean) =
|
||||
builtinFunctionsAsmGen.translateFunctioncallExpression(functionCall, signature, resultToStack)
|
||||
|
||||
internal fun translateFunctionCall(functionCall: FunctionCall, preserveStatusRegisterAfterCall: Boolean) =
|
||||
functioncallAsmGen.translateFunctionCall(functionCall, preserveStatusRegisterAfterCall)
|
||||
internal fun translateFunctionCall(functionCall: FunctionCall) =
|
||||
functioncallAsmGen.translateFunctionCall(functionCall)
|
||||
|
||||
internal fun translateNormalAssignment(assign: AsmAssignment) =
|
||||
assignmentAsmGen.translateNormalAssignment(assign)
|
||||
|
@ -179,7 +179,7 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
|
||||
}
|
||||
|
||||
private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can be eliminated
|
||||
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated
|
||||
// TODO this is not true if X is not a regular RAM memory address (but instead mapped I/O or ROM)
|
||||
val mods = mutableListOf<Modification>()
|
||||
for (pair in linesByFour) {
|
||||
@ -196,10 +196,14 @@ private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>)
|
||||
(first.startsWith("sty ") && second.startsWith("ldy ")) ||
|
||||
(first.startsWith("stx ") && second.startsWith("ldx "))
|
||||
) {
|
||||
val firstLoc = first.substring(4).trimStart()
|
||||
val secondLoc = second.substring(4).trimStart()
|
||||
if (firstLoc == secondLoc) {
|
||||
mods.add(Modification(pair[1].index, true, null))
|
||||
val third = pair[2].value.trimStart()
|
||||
if(!third.startsWith("b")) {
|
||||
// no branch instruction follows, we can potentiall remove the load instruction
|
||||
val firstLoc = first.substring(4).trimStart()
|
||||
val secondLoc = second.substring(4).trimStart()
|
||||
if (firstLoc == secondLoc) {
|
||||
mods.add(Modification(pair[1].index, true, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -120,8 +120,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.R0)
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.R1)
|
||||
asmgen.assignExpressionToRegister(fcall.args[2], RegisterOrPair.A)
|
||||
val sub = (fcall as FunctionCallStatement).definingSubroutine()!!
|
||||
asmgen.saveRegister(CpuRegister.X, false, sub)
|
||||
asmgen.saveRegister(CpuRegister.X, scope, false)
|
||||
asmgen.out(" jsr cx16.memory_fill")
|
||||
asmgen.restoreRegister(CpuRegister.X, false)
|
||||
}
|
||||
@ -139,8 +138,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.R0)
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.R1)
|
||||
asmgen.assignExpressionToRegister(fcall.args[2], RegisterOrPair.R2)
|
||||
val sub = (fcall as FunctionCallStatement).definingSubroutine()!!
|
||||
asmgen.saveRegister(CpuRegister.X, false, sub)
|
||||
asmgen.saveRegister(CpuRegister.X, scope, false)
|
||||
asmgen.out(" jsr cx16.memory_copy")
|
||||
asmgen.restoreRegister(CpuRegister.X, false)
|
||||
}
|
||||
|
@ -74,6 +74,8 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
is FunctionCall -> {
|
||||
if(dt in ByteDatatypes) {
|
||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.A)
|
||||
if(left is FunctionCall)
|
||||
asmgen.out(" cmp #0")
|
||||
asmgen.out(" bne $jumpIfFalseLabel")
|
||||
return
|
||||
}
|
||||
@ -112,6 +114,8 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
is FunctionCall -> {
|
||||
if(dt in ByteDatatypes) {
|
||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.A)
|
||||
if(left is FunctionCall)
|
||||
asmgen.out(" cmp #0")
|
||||
asmgen.out(" beq $jumpIfFalseLabel")
|
||||
return
|
||||
}
|
||||
@ -1329,8 +1333,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
asmgen.translateBuiltinFunctionCallExpression(expression, builtinFunc, true)
|
||||
} else {
|
||||
sub as Subroutine
|
||||
val preserveStatusRegisterAfterCall = sub.shouldPreserveStatusRegisterAfterCall()
|
||||
asmgen.translateFunctionCall(expression, preserveStatusRegisterAfterCall)
|
||||
asmgen.translateFunctionCall(expression)
|
||||
val returns = sub.returntypes.zip(sub.asmReturnvaluesRegisters)
|
||||
for ((_, reg) in returns) {
|
||||
// result value in cpu or status registers, put it on the stack
|
||||
|
@ -15,22 +15,18 @@ import prog8.compiler.target.c64.codegen.assignment.*
|
||||
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||
|
||||
internal fun translateFunctionCallStatement(stmt: IFunctionCall) {
|
||||
val sub = stmt.target.targetSubroutine(program.namespace)!!
|
||||
val preserveStatusRegisterAfterCall = sub.shouldPreserveStatusRegisterAfterCall()
|
||||
translateFunctionCall(stmt, preserveStatusRegisterAfterCall)
|
||||
translateFunctionCall(stmt)
|
||||
// functioncalls no longer return results on the stack, so simply ignore the results in the registers
|
||||
if(preserveStatusRegisterAfterCall)
|
||||
asmgen.out(" plp") // restore status flags from call
|
||||
}
|
||||
|
||||
|
||||
internal fun translateFunctionCall(stmt: IFunctionCall, preserveStatusRegisterAfterCall: Boolean) {
|
||||
internal fun translateFunctionCall(stmt: IFunctionCall) {
|
||||
// output the code to setup the parameters and perform the actual call
|
||||
// does NOT output the code to deal with the result values!
|
||||
val sub = stmt.target.targetSubroutine(program.namespace) ?: throw AssemblyError("undefined subroutine ${stmt.target}")
|
||||
val saveX = sub.shouldSaveX()
|
||||
if(saveX)
|
||||
asmgen.saveRegister(CpuRegister.X, preserveStatusRegisterAfterCall, (stmt as Node).definingSubroutine()!!)
|
||||
asmgen.saveRegister(CpuRegister.X, (stmt as Node).definingSubroutine()!!, true)
|
||||
|
||||
val subName = asmgen.asmSymbolName(stmt.target)
|
||||
if(stmt.args.isNotEmpty()) {
|
||||
@ -67,15 +63,8 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
}
|
||||
}
|
||||
asmgen.out(" jsr $subName")
|
||||
|
||||
if(preserveStatusRegisterAfterCall) {
|
||||
asmgen.out(" php") // save status flags from call
|
||||
// note: the containing statement (such as the FunctionCallStatement or the Assignment or the Expression)
|
||||
// must take care of popping this value again at the end!
|
||||
}
|
||||
|
||||
if(saveX)
|
||||
asmgen.restoreRegister(CpuRegister.X, preserveStatusRegisterAfterCall)
|
||||
asmgen.restoreRegister(CpuRegister.X, true)
|
||||
}
|
||||
|
||||
private fun registerArgsViaStackEvaluation(stmt: IFunctionCall, sub: Subroutine) {
|
||||
|
@ -91,7 +91,7 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
||||
else
|
||||
{
|
||||
asmgen.loadScaledArrayIndexIntoRegister(targetArrayIdx, elementDt, CpuRegister.A)
|
||||
asmgen.saveRegister(CpuRegister.X, false, scope!!)
|
||||
asmgen.saveRegister(CpuRegister.X, scope!!, false)
|
||||
asmgen.out(" tax")
|
||||
when(elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
|
@ -142,8 +142,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
is FunctionCall -> {
|
||||
when (val sub = value.target.targetStatement(program.namespace)) {
|
||||
is Subroutine -> {
|
||||
val preserveStatusRegisterAfterCall = sub.shouldPreserveStatusRegisterAfterCall()
|
||||
asmgen.translateFunctionCall(value, preserveStatusRegisterAfterCall)
|
||||
asmgen.translateFunctionCall(value)
|
||||
val returnValue = sub.returntypes.zip(sub.asmReturnvaluesRegisters).single { it.second.registerOrPair!=null }
|
||||
when (returnValue.first) {
|
||||
DataType.STR -> {
|
||||
@ -182,8 +181,6 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
}
|
||||
}
|
||||
if (preserveStatusRegisterAfterCall)
|
||||
asmgen.out(" plp") // restore status flags from call
|
||||
}
|
||||
is BuiltinFunctionStatementPlaceholder -> {
|
||||
val signature = BuiltinFunctions.getValue(sub.name)
|
||||
@ -2003,9 +2000,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
asmgen.storeByteIntoPointer(addressExpr, null)
|
||||
}
|
||||
else -> {
|
||||
asmgen.saveRegister(register, false, memoryAddress.definingSubroutine()!!)
|
||||
asmgen.saveRegister(register, memoryAddress.definingSubroutine()!!, true)
|
||||
assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null)
|
||||
asmgen.restoreRegister(CpuRegister.A, false)
|
||||
asmgen.restoreRegister(CpuRegister.A, true)
|
||||
asmgen.out(" ldy #0 | sta (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
}
|
||||
|
@ -1417,7 +1417,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
|
||||
private fun inplaceModification_float_value_to_variable(name: String, operator: String, value: Expression, scope: Subroutine) {
|
||||
asmgen.assignExpressionToRegister(value, RegisterOrPair.FAC1)
|
||||
asmgen.saveRegister(CpuRegister.X, false, scope)
|
||||
asmgen.saveRegister(CpuRegister.X, scope, false)
|
||||
when (operator) {
|
||||
"**" -> {
|
||||
asmgen.out("""
|
||||
@ -1472,7 +1472,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
throw AssemblyError("float variable expected")
|
||||
|
||||
val otherName = asmgen.asmVariableName(ident)
|
||||
asmgen.saveRegister(CpuRegister.X, false, scope)
|
||||
asmgen.saveRegister(CpuRegister.X, scope, false)
|
||||
when (operator) {
|
||||
"**" -> {
|
||||
if(CompilationTarget.instance is Cx16Target) {
|
||||
@ -1550,7 +1550,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||
|
||||
private fun inplaceModification_float_litval_to_variable(name: String, operator: String, value: Double, scope: Subroutine) {
|
||||
val constValueName = asmgen.getFloatAsmConst(value)
|
||||
asmgen.saveRegister(CpuRegister.X, false, scope)
|
||||
asmgen.saveRegister(CpuRegister.X, scope, false)
|
||||
when (operator) {
|
||||
"**" -> {
|
||||
if(CompilationTarget.instance is Cx16Target) {
|
||||
|
@ -481,6 +481,12 @@ condition meaning
|
||||
|
||||
So ``if_cc goto target`` will directly translate into the single CPU instruction ``BCC target``.
|
||||
|
||||
.. caution::
|
||||
These special ``if_XX`` branching statements are only useful in certain specific situations where you are *certain*
|
||||
that the status register (still) contains the correct status bits.
|
||||
This is not always the case after a fuction call or other operations!
|
||||
If in doubt, check the generated assembly code!
|
||||
|
||||
.. note::
|
||||
For now, the symbols used or declared in the statement block(s) are shared with
|
||||
the same scope the if statement itself is in.
|
||||
|
@ -721,6 +721,13 @@ The XX corresponds to one of the eigth branching instructions so the possibiliti
|
||||
``if_cs``, ``if_cc``, ``if_eq``, ``if_ne``, ``if_pl``, ``if_mi``, ``if_vs`` and ``if_vc``.
|
||||
It can also be one of the four aliases that are easier to read: ``if_z``, ``if_nz``, ``if_pos`` and ``if_neg``.
|
||||
|
||||
.. caution::
|
||||
These special ``if_XX`` branching statements are only useful in certain specific situations where you are *certain*
|
||||
that the status register (still) contains the correct status bits.
|
||||
This is not always the case after a fuction call or other operations!
|
||||
If in doubt, check the generated assembly code!
|
||||
|
||||
|
||||
when statement ('jump table')
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
The structure of a when statement is like this::
|
||||
|
@ -7,6 +7,9 @@
|
||||
; staged speed increase
|
||||
; some simple sound effects
|
||||
|
||||
; TODO BROKEN: DOESN'T REGISTER KEYSTROKES ANYMORE (CAN'T START GAME)
|
||||
|
||||
|
||||
%target c64
|
||||
%import syslib
|
||||
%import textio
|
||||
|
@ -1,5 +1,5 @@
|
||||
%import textio
|
||||
;%import diskio
|
||||
%import diskio
|
||||
;%import floats
|
||||
;%import graphics
|
||||
%zeropage basicsafe
|
||||
@ -70,14 +70,24 @@ _y .byte 0
|
||||
}
|
||||
|
||||
sub start () {
|
||||
|
||||
while c64.CHRIN() {
|
||||
; read the rest of the entry until the end
|
||||
}
|
||||
|
||||
; cx16.r0 = 65535
|
||||
; set_8_pixels_opaque_OLD(111,222,33)
|
||||
; txt.chrout('\n')
|
||||
ubyte bb = 44
|
||||
set_8_pixels_opaque(bb,111,222,33)
|
||||
txt.chrout('\n')
|
||||
set_8_pixels_opaque(c64.CHRIN(),111,222,33)
|
||||
txt.chrout('\n')
|
||||
; ;ubyte qq=c64.CHKIN(3) ;; TODO fix compiler crash "can't translate zero return values in assignment"
|
||||
;
|
||||
; test_stack.test()
|
||||
; ubyte bb = 44
|
||||
; set_8_pixels_opaque(bb,111,222,33)
|
||||
; txt.chrout('\n')
|
||||
; test_stack.test()
|
||||
;
|
||||
; set_8_pixels_opaque(c64.CHRIN(),111,222,33)
|
||||
; txt.chrout('\n')
|
||||
|
||||
test_stack.test()
|
||||
}
|
||||
|
@ -10,6 +10,9 @@
|
||||
|
||||
; Note: this program is compatible with C64 and CX16.
|
||||
|
||||
; TODO BROKEN IN VICE WHEN LOADING WITHOUT DISK: PRINTS WEIRD CHARACTERS FOR IO-ERROR
|
||||
|
||||
|
||||
main {
|
||||
|
||||
const ubyte numforLave = 7 ; Lave is 7th generated planet in galaxy one
|
||||
|
Loading…
Reference in New Issue
Block a user