From 127c470746efbe3d0344b8c72d4e66f170f105a8 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sat, 12 Jun 2021 15:48:04 +0200 Subject: [PATCH 1/2] add some explanation about Cx16 v38 - v39 issue --- docs/source/index.rst | 17 +++++-- docs/source/todo.rst | 2 - examples/test.p8 | 106 +++++++++++++++++++++++++++--------------- 3 files changed, 83 insertions(+), 42 deletions(-) diff --git a/docs/source/index.rst b/docs/source/index.rst index 33a355cc1..4b2090f37 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -165,10 +165,21 @@ For Windows it's possible to get that as well; check out `AdoptOpenJDK `_. +In C64 mode, the compiler assumes the presence of the `Vice emulator `_. If you're targeting the CommanderX16 instead, there's the `x16emu `_. -Make sure you use cx16 emulator and roms **V39 or newer**! Starting from version 6.5, prog8 targets that system version. -Your program may work on V38 but that will only be by luck. + +.. attention:: **Commander-X16 V38 vs V39** + Starting with Prog8 7.0 the Commander-X16 support targets the upcoming v39 version of the emulator + and roms, which reflects the current state of the hardware design. + Unfortunately, these have not yet been officially released (at the time of writing, v38 is still + the latest official release). So you have to either compile them from source yourself + or obtain a precompiled version from someone else. + Your cx16 program compiled by prog8 7.0 is meant for v39 but *may* still work on the older v38 release of the emulator. + For this to work you should make sure that the program is not using floating point, nor the ram/rom bank switching logic provided by the libraries. + You can also choose to just stick with Prog8 6.4 (which still targets cx16 v38) and wait it out till + the emulator v39 is officially released - but you won't be able to benefit from the compiler improvements + made for prog 7.0 this way. + .. toctree:: diff --git a/docs/source/todo.rst b/docs/source/todo.rst index ae227a5f1..53f70b5b9 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -4,8 +4,6 @@ TODO - fix scope compilation errors for sub with same name as block (kefrenbars example, 'irq') -- add example in documentation for %asminclude and %asmbinary on how to refer to its contents via label or whatever - - test all examples (including imgviewer, assembler and petaxian) before release of the new version - simplify cx16.joystick_get2() once this cx16 rom issue is resolved: https://github.com/commanderx16/x16-rom/issues/203 diff --git a/examples/test.p8 b/examples/test.p8 index 0fda1c93c..50f1e98cf 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,44 +1,76 @@ %import textio %zeropage dontuse - main { - -label: sub start() { - - sub2(&label) - sub2(&label_local) - sub2(&main.sub2.label_in_sub2) - uword xx = &label_local - txt.print_uwhex(xx, true) - txt.nl() - xx = &label - txt.print_uwhex(xx, true) - txt.nl() - xx = &main.label - txt.print_uwhex(xx, true) - txt.nl() - xx = &main.sub2.label_in_sub2 - txt.print_uwhex(xx, true) - txt.nl() - xx = main.sub2.sub2var - txt.print_uwhex(xx, true) - txt.nl() - xx = &main.start.label_local - txt.print_uwhex(xx, true) - txt.nl() - -label_local: - return - } - - sub sub2(uword ad) { - uword sub2var = 42 - - txt.print_uwhex(ad,true) - txt.nl() -label_in_sub2: - txt.nl() + irq.irq() } } + + +; TODO FIX SCOPE ERRORS (caused by sub with same name as block) + +irq { + ubyte[32] pixels + + sub irq() { + ubyte xx +; if xx > 4 { +; xx++ +; } else { + xx = pixels[2] ; OK + calc(pixels[2]) ; FAIL on 'calc' + calc2(pixels) ; FAIL on 'pixels' and 'calc2' +; } + } + + sub calc2(uword adr) { + adr++ + } + + sub calc(ubyte aa) { + aa++ + } +} + + +;main { +; +;label: +; sub start() { +; +; sub2(&label) +; sub2(&label_local) +; sub2(&main.sub2.label_in_sub2) +; uword xx = &label_local +; txt.print_uwhex(xx, true) +; txt.nl() +; xx = &label +; txt.print_uwhex(xx, true) +; txt.nl() +; xx = &main.label +; txt.print_uwhex(xx, true) +; txt.nl() +; xx = &main.sub2.label_in_sub2 +; txt.print_uwhex(xx, true) +; txt.nl() +; xx = main.sub2.sub2var +; txt.print_uwhex(xx, true) +; txt.nl() +; xx = &main.start.label_local +; txt.print_uwhex(xx, true) +; txt.nl() +; +;label_local: +; return +; } +; +; sub sub2(uword ad) { +; uword sub2var = 42 +; +; txt.print_uwhex(ad,true) +; txt.nl() +;label_in_sub2: +; txt.nl() +; } +;} From fd2bbd2b5947353a4b88a91a32aabcd197a3a41b Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sat, 12 Jun 2021 17:31:09 +0200 Subject: [PATCH 2/2] no longer allow subroutine name same as its block name due to asm symbol scoping issues --- .../astprocessing/AstIdentifiersChecker.kt | 6 + .../compiler/target/cpu6502/codegen/AsmGen.kt | 4 +- .../cpu6502/codegen/ExpressionsAsmGen.kt | 2 +- .../cpu6502/codegen/FunctionCallAsmGen.kt | 7 +- .../codegen/assignment/AssignmentAsmGen.kt | 8 +- .../assignment/AugmentableAssignmentAsmGen.kt | 4 +- docs/source/todo.rst | 2 - examples/balloonflight.p8 | 4 +- examples/bdmusic-irq.p8 | 4 +- examples/cx16/kefrenbars.p8 | 4 +- examples/cx16/multipalette.p8 | 4 +- examples/cx16/rasterbars.p8 | 4 +- examples/rasterbars.p8 | 4 +- examples/sprites.p8 | 4 +- examples/test.p8 | 105 ++++++------------ examples/wizzine.p8 | 4 +- 16 files changed, 70 insertions(+), 100 deletions(-) diff --git a/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt index 5bbe337a7..9d93627a2 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstIdentifiersChecker.kt @@ -106,6 +106,12 @@ internal class AstIdentifiersChecker(private val program: Program, private val e if(subroutine.isAsmSubroutine && subroutine.statements.any{it !is InlineAssembly}) { errors.err("asmsub can only contain inline assembly (%asm)", subroutine.position) } + + if(subroutine.name == subroutine.definingBlock().name) { + // subroutines cannot have the same name as their enclosing block, + // because this causes symbol scoping issues in the resulting assembly source + nameError(subroutine.name, subroutine.position, subroutine.definingBlock()) + } } super.visit(subroutine) diff --git a/compiler/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt b/compiler/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt index fe3cd9007..212911571 100644 --- a/compiler/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt @@ -1502,7 +1502,7 @@ $label nop""") val saveA = evalBytevalueWillClobberA(ptrAndIndex.first) || evalBytevalueWillClobberA(ptrAndIndex.second) if(saveA) out(" pha") - assignExpressionToVariable(ptrAndIndex.first, asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null) + assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD, null) assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y) if(saveA) out(" pla") @@ -1514,7 +1514,7 @@ $label nop""") out(" lda (${asmSymbolName(pointervar)}),y") } else { // copy the pointer var to zp first - assignExpressionToVariable(ptrAndIndex.first, asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null) + assignExpressionToVariable(ptrAndIndex.first, "P8ZP_SCRATCH_W2", DataType.UWORD, null) assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.Y) out(" lda (P8ZP_SCRATCH_W2),y") } diff --git a/compiler/src/prog8/compiler/target/cpu6502/codegen/ExpressionsAsmGen.kt b/compiler/src/prog8/compiler/target/cpu6502/codegen/ExpressionsAsmGen.kt index 2a5c22c1c..7a53daff6 100644 --- a/compiler/src/prog8/compiler/target/cpu6502/codegen/ExpressionsAsmGen.kt +++ b/compiler/src/prog8/compiler/target/cpu6502/codegen/ExpressionsAsmGen.kt @@ -1696,7 +1696,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge internal fun translateDirectMemReadExpression(expr: DirectMemoryRead, pushResultOnEstack: Boolean) { fun assignViaExprEval() { - asmgen.assignExpressionToVariable(expr.addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null) + asmgen.assignExpressionToVariable(expr.addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD, null) if (asmgen.isTargetCpu(CpuType.CPU65c02)) { if (pushResultOnEstack) { asmgen.out(" lda (P8ZP_SCRATCH_W2) | dex | sta P8ESTACK_LO+1,x") diff --git a/compiler/src/prog8/compiler/target/cpu6502/codegen/FunctionCallAsmGen.kt b/compiler/src/prog8/compiler/target/cpu6502/codegen/FunctionCallAsmGen.kt index c0b877803..f44866ef1 100644 --- a/compiler/src/prog8/compiler/target/cpu6502/codegen/FunctionCallAsmGen.kt +++ b/compiler/src/prog8/compiler/target/cpu6502/codegen/FunctionCallAsmGen.kt @@ -319,10 +319,9 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg register!! if(requiredDt largerThan valueDt) { // we need to sign extend the source, do this via temporary word variable - val scratchVar = asmgen.asmVariableName("P8ZP_SCRATCH_W1") - asmgen.assignExpressionToVariable(value, scratchVar, DataType.UBYTE, sub) - asmgen.signExtendVariableLsb(scratchVar, valueDt) - asmgen.assignVariableToRegister(scratchVar, register) + asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_W1", DataType.UBYTE, sub) + asmgen.signExtendVariableLsb("P8ZP_SCRATCH_W1", valueDt) + asmgen.assignVariableToRegister("P8ZP_SCRATCH_W1", register) } else { val target: AsmAssignTarget = if(parameter.value.type in ByteDatatypes && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters)) diff --git a/compiler/src/prog8/compiler/target/cpu6502/codegen/assignment/AssignmentAsmGen.kt b/compiler/src/prog8/compiler/target/cpu6502/codegen/assignment/AssignmentAsmGen.kt index 5750cf73a..5b80f89c5 100644 --- a/compiler/src/prog8/compiler/target/cpu6502/codegen/assignment/AssignmentAsmGen.kt +++ b/compiler/src/prog8/compiler/target/cpu6502/codegen/assignment/AssignmentAsmGen.kt @@ -115,7 +115,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen } SourceStorageKind.MEMORY -> { fun assignViaExprEval(expression: Expression) { - assignExpressionToVariable(expression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, assign.target.scope) + assignExpressionToVariable(expression, "P8ZP_SCRATCH_W2", DataType.UWORD, assign.target.scope) if (asmgen.isTargetCpu(CpuType.CPU65c02)) asmgen.out(" lda (P8ZP_SCRATCH_W2)") else @@ -320,7 +320,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen if(targetDt in WordDatatypes) { fun assignViaExprEval(addressExpression: Expression) { - asmgen.assignExpressionToVariable(addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null) + asmgen.assignExpressionToVariable(addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD, null) if (asmgen.isTargetCpu(CpuType.CPU65c02)) asmgen.out(" lda (P8ZP_SCRATCH_W2)") else @@ -2089,7 +2089,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen fun storeViaExprEval() { when(addressExpr) { is NumericLiteralValue, is IdentifierReference -> { - assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null) + assignExpressionToVariable(addressExpr, "P8ZP_SCRATCH_W2", DataType.UWORD, null) if (asmgen.isTargetCpu(CpuType.CPU65c02)) asmgen.out(" sta (P8ZP_SCRATCH_W2)") else @@ -2098,7 +2098,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen else -> { // same as above but we need to save the A register asmgen.out(" pha") - assignExpressionToVariable(addressExpr, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, null) + assignExpressionToVariable(addressExpr, "P8ZP_SCRATCH_W2", DataType.UWORD, null) asmgen.out(" pla") if (asmgen.isTargetCpu(CpuType.CPU65c02)) asmgen.out(" sta (P8ZP_SCRATCH_W2)") diff --git a/compiler/src/prog8/compiler/target/cpu6502/codegen/assignment/AugmentableAssignmentAsmGen.kt b/compiler/src/prog8/compiler/target/cpu6502/codegen/assignment/AugmentableAssignmentAsmGen.kt index 4af648264..ba60bee07 100644 --- a/compiler/src/prog8/compiler/target/cpu6502/codegen/assignment/AugmentableAssignmentAsmGen.kt +++ b/compiler/src/prog8/compiler/target/cpu6502/codegen/assignment/AugmentableAssignmentAsmGen.kt @@ -1778,7 +1778,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, asmgen.out(" sta (P8ZP_SCRATCH_W1),y") } else -> { - asmgen.assignExpressionToVariable(mem.addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, target.scope) + asmgen.assignExpressionToVariable(mem.addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD, target.scope) asmgen.out(""" ldy #0 lda (P8ZP_SCRATCH_W2),y @@ -1846,7 +1846,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program, asmgen.out(" sta (P8ZP_SCRATCH_W1),y") } else -> { - asmgen.assignExpressionToVariable(memory.addressExpression, asmgen.asmVariableName("P8ZP_SCRATCH_W2"), DataType.UWORD, target.scope) + asmgen.assignExpressionToVariable(memory.addressExpression, "P8ZP_SCRATCH_W2", DataType.UWORD, target.scope) asmgen.out(""" ldy #0 lda (P8ZP_SCRATCH_W2),y diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 53f70b5b9..c8d812cb4 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -2,8 +2,6 @@ TODO ==== -- fix scope compilation errors for sub with same name as block (kefrenbars example, 'irq') - - test all examples (including imgviewer, assembler and petaxian) before release of the new version - simplify cx16.joystick_get2() once this cx16 rom issue is resolved: https://github.com/commanderx16/x16-rom/issues/203 diff --git a/examples/balloonflight.p8 b/examples/balloonflight.p8 index 1b7fe3a5d..a39eac281 100644 --- a/examples/balloonflight.p8 +++ b/examples/balloonflight.p8 @@ -23,7 +23,7 @@ main { c64.SCROLX &= %11110111 ; 38 column mode - c64.set_rasterirq(&irq.irq, 200, false) ; enable animation via raster interrupt + c64.set_rasterirq(&irq.irqhandler, 200, false) ; enable animation via raster interrupt ubyte target_height = 10 ubyte active_height = 24 @@ -130,7 +130,7 @@ spritedata $0f00 { irq { ubyte smoothx=0 - sub irq() { + sub irqhandler() { smoothx = (smoothx-1) & 7 main.perform_scroll = smoothx==7 c64.SCROLX = (c64.SCROLX & %11111000) | smoothx diff --git a/examples/bdmusic-irq.p8 b/examples/bdmusic-irq.p8 index 1d74a5dff..2bb1fb30c 100644 --- a/examples/bdmusic-irq.p8 +++ b/examples/bdmusic-irq.p8 @@ -7,7 +7,7 @@ main { sub start() { txt.print("playing the music from boulderdash,\nmade in 1984 by peter liepa.\n\n") - c64.set_rasterirq(&irq.irq, 60, true) ; enable playback via raster irq + c64.set_rasterirq(&irq.irqhandler, 60, true) ; enable playback via raster irq } } @@ -16,7 +16,7 @@ irq { ubyte note_index = 0 ubyte delay = 0 - sub irq() { + sub irqhandler() { c64.EXTCOL++ delay++ if delay >= 8 { diff --git a/examples/cx16/kefrenbars.p8 b/examples/cx16/kefrenbars.p8 index ed9c586d5..a3c6ce6be 100644 --- a/examples/cx16/kefrenbars.p8 +++ b/examples/cx16/kefrenbars.p8 @@ -26,7 +26,7 @@ main { cx16.screen_set_mode(128) ; low-res bitmap 256 colors cx16.FB_init() cx16.VERA_DC_VSCALE = 0 ; display trick spoiler.......: stretch display all the way to the bottom - cx16.set_rasterirq(&irq.irq, 0) + cx16.set_rasterirq(&irq.irqhandler, 0) repeat { ; don't exit @@ -44,7 +44,7 @@ irq { ubyte[32] pixels = 0 to 31 - sub irq() { + sub irqhandler() { next_irq_line += 6 anim1 += 4 anim2 += 6 diff --git a/examples/cx16/multipalette.p8 b/examples/cx16/multipalette.p8 index d09b202c0..a4801d102 100644 --- a/examples/cx16/multipalette.p8 +++ b/examples/cx16/multipalette.p8 @@ -31,7 +31,7 @@ main { palette.set_color(0, 0) palette.set_color(16, 0) - cx16.set_rasterirq(&irq.irq, 0) + cx16.set_rasterirq(&irq.irqhandler, 0) repeat { ; don't exit @@ -45,7 +45,7 @@ irq { uword next_rasterline = 0 const ubyte increment = 4 ; 4 scanlines = 2 lores pixels per color swap (2 scanlines is too tight) - sub irq() { + sub irqhandler() { if phase & 1 == 0 { %asm {{ lda #0 ; activate palette #0 (first set of colors) diff --git a/examples/cx16/rasterbars.p8 b/examples/cx16/rasterbars.p8 index 9ec647070..df73756bc 100644 --- a/examples/cx16/rasterbars.p8 +++ b/examples/cx16/rasterbars.p8 @@ -13,7 +13,7 @@ main { txt.plot(14,14) txt.print("raster bars!") - cx16.set_rasterirq(&irq.irq, 0) + cx16.set_rasterirq(&irq.irqhandler, 0) repeat { ; don't exit @@ -39,7 +39,7 @@ irq { ubyte yanim = 0 const ubyte barheight = 4 - sub irq() { + sub irqhandler() { uword c = colors[color_idx] color_idx++ color_idx &= 31 diff --git a/examples/rasterbars.p8 b/examples/rasterbars.p8 index 5f80d4192..6057810de 100644 --- a/examples/rasterbars.p8 +++ b/examples/rasterbars.p8 @@ -5,7 +5,7 @@ main { sub start() { c64.SCROLY &= %11101111 ; blank the screen - c64.set_rasterirq(&irq.irq, 40, false) ; register exclusive raster irq handler + c64.set_rasterirq(&irq.irqhandler, 40, false) ; register exclusive raster irq handler repeat { ; enjoy the moving bars :) @@ -22,7 +22,7 @@ irq { ubyte color = 0 ubyte yanim = 0 - sub irq() { + sub irqhandler() { if color!=len(colors) { c64.EXTCOL = colors[color] c64.RASTER += barheight ; next raster Irq for next color diff --git a/examples/sprites.p8 b/examples/sprites.p8 index 938204527..d336e6a65 100644 --- a/examples/sprites.p8 +++ b/examples/sprites.p8 @@ -46,14 +46,14 @@ main { } c64.SPENA = 255 ; enable all sprites - c64.set_rasterirq(&irq.irq, 255, true) ; enable animation + c64.set_rasterirq(&irq.irqhandler, 255, true) ; enable animation } } irq { - sub irq() { + sub irqhandler() { c64.EXTCOL-- ; float up & wobble horizontally diff --git a/examples/test.p8 b/examples/test.p8 index 50f1e98cf..043f30c2c 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -2,75 +2,42 @@ %zeropage dontuse main { + +label: sub start() { - irq.irq() + + sub2(&label) + sub2(&label_local) + sub2(&main.sub2.label_in_sub2) + uword xx = &label_local + txt.print_uwhex(xx, true) + txt.nl() + xx = &label + txt.print_uwhex(xx, true) + txt.nl() + xx = &main.label + txt.print_uwhex(xx, true) + txt.nl() + xx = &main.sub2.label_in_sub2 + txt.print_uwhex(xx, true) + txt.nl() + xx = main.sub2.sub2var + txt.print_uwhex(xx, true) + txt.nl() + xx = &main.start.label_local + txt.print_uwhex(xx, true) + txt.nl() + +label_local: + return + } + + sub sub2(uword ad) { + uword sub2var = 42 + + txt.print_uwhex(ad,true) + txt.nl() +label_in_sub2: + txt.nl() } } - - -; TODO FIX SCOPE ERRORS (caused by sub with same name as block) - -irq { - ubyte[32] pixels - - sub irq() { - ubyte xx -; if xx > 4 { -; xx++ -; } else { - xx = pixels[2] ; OK - calc(pixels[2]) ; FAIL on 'calc' - calc2(pixels) ; FAIL on 'pixels' and 'calc2' -; } - } - - sub calc2(uword adr) { - adr++ - } - - sub calc(ubyte aa) { - aa++ - } -} - - -;main { -; -;label: -; sub start() { -; -; sub2(&label) -; sub2(&label_local) -; sub2(&main.sub2.label_in_sub2) -; uword xx = &label_local -; txt.print_uwhex(xx, true) -; txt.nl() -; xx = &label -; txt.print_uwhex(xx, true) -; txt.nl() -; xx = &main.label -; txt.print_uwhex(xx, true) -; txt.nl() -; xx = &main.sub2.label_in_sub2 -; txt.print_uwhex(xx, true) -; txt.nl() -; xx = main.sub2.sub2var -; txt.print_uwhex(xx, true) -; txt.nl() -; xx = &main.start.label_local -; txt.print_uwhex(xx, true) -; txt.nl() -; -;label_local: -; return -; } -; -; sub sub2(uword ad) { -; uword sub2var = 42 -; -; txt.print_uwhex(ad,true) -; txt.nl() -;label_in_sub2: -; txt.nl() -; } -;} diff --git a/examples/wizzine.p8 b/examples/wizzine.p8 index f56eedd3d..f50946f07 100644 --- a/examples/wizzine.p8 +++ b/examples/wizzine.p8 @@ -38,7 +38,7 @@ main { c64.SPRPTR[i] = $0a00/64 } c64.SPENA = 255 ; enable all sprites - c64.set_rasterirq(&irq.irq, 230, true) ; enable animation + c64.set_rasterirq(&irq.irqhandler, 230, true) ; enable animation } } @@ -46,7 +46,7 @@ main { irq { ubyte angle - sub irq() { + sub irqhandler() { angle++ c64.MSIGX=0 ubyte @zp spri