diff --git a/compiler/res/prog8lib/cx16/syslib.p8 b/compiler/res/prog8lib/cx16/syslib.p8 index c021e1fd7..28e79344d 100644 --- a/compiler/res/prog8lib/cx16/syslib.p8 +++ b/compiler/res/prog8lib/cx16/syslib.p8 @@ -734,8 +734,9 @@ asmsub vpeek(ubyte bank @A, uword address @XY) -> ubyte @A { } asmsub vaddr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, byte autoIncrOrDecrByOne @Y) clobbers(A) { - ; -- setup the VERA's data address register 0 or 1 - ; with optional auto increment or decrement of 1. + ; -- setup the VERA's data address register 0 or 1 with optional auto increment or decrement of 1. + ; This is a convenience routine, and not very efficient if you call it often; + ; it's usually better to write a tailor made version of it that accounts for the repeated values. ; Note that the vaddr_autoincr() and vaddr_autodecr() routines allow to set all possible strides, not just 1. ; Note also that Vera's addrset is reset to 0 on exit, even if you set port #1's address. %asm {{ @@ -764,6 +765,8 @@ asmsub vaddr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, byte autoIncrO asmsub vaddr_clone(ubyte port @A) clobbers (A,X,Y) { ; -- clones Vera addresses from the given source port to the other one. + ; This is a convenience routine, and not very efficient if you call it often; + ; it's usually better to write a tailor made version of it that accounts for the repeated values. %asm {{ sta VERA_CTRL ldx VERA_ADDR_L @@ -783,9 +786,10 @@ asmsub vaddr_clone(ubyte port @A) clobbers (A,X,Y) { } asmsub vaddr_autoincr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, uword autoIncrAmount @R2) clobbers(A,Y) { - ; -- setup the VERA's data address register 0 or 1 - ; including setting up optional auto increment amount. + ; -- setup the VERA's data address register 0 or 1, including setting up optional auto increment amount. ; Specifiying an unsupported amount results in amount of zero. See the Vera docs about what amounts are possible. + ; This is a convenience routine, and not very efficient if you call it often; + ; it's usually better to write a tailor made version of it that accounts for the repeated values. %asm {{ jsr _setup lda cx16.r2H @@ -847,9 +851,10 @@ _strides_lsb .byte 0,1,2,4,8,16,32,64,128,255,255,40,80,160,255,255 } asmsub vaddr_autodecr(ubyte bank @A, uword address @R0, ubyte addrsel @R1, uword autoDecrAmount @R2) clobbers(A,Y) { - ; -- setup the VERA's data address register 0 or 1 - ; including setting up optional auto decrement amount. + ; -- setup the VERA's data address register 0 or 1 including setting up optional auto decrement amount. ; Specifiying an unsupported amount results in amount of zero. See the Vera docs about what amounts are possible. + ; This is a convenience routine, and not very efficient if you call it often; + ; it's usually better to write a tailor made version of it that accounts for the repeated values. %asm {{ jsr vaddr_autoincr._setup lda cx16.r2H diff --git a/compiler/src/prog8/compiler/astprocessing/BlockMerger.kt b/compiler/src/prog8/compiler/astprocessing/BlockMerger.kt index 5757471c0..9dd19771a 100644 --- a/compiler/src/prog8/compiler/astprocessing/BlockMerger.kt +++ b/compiler/src/prog8/compiler/astprocessing/BlockMerger.kt @@ -11,6 +11,8 @@ class BlockMerger(val errors: IErrorReporter) { // will be joined into a block with the same name, coming from a library. // (or a normal block if no library block with that name was found) + private val mergedBlocks = mutableSetOf() // to make sure blocks aren't merged more than once + fun visit(program: Program) { val allBlocks = program.allBlocks for(block in allBlocks) { @@ -31,6 +33,9 @@ class BlockMerger(val errors: IErrorReporter) { } private fun merge(block: Block, target: Block) { + if(block===target || block in mergedBlocks || target in mergedBlocks) + return + val named = target.statements.filterIsInstance().associateBy { it.name } for(stmt in block.statements.filter { it !is Directive }) { @@ -47,7 +52,9 @@ class BlockMerger(val errors: IErrorReporter) { target.statements.add(stmt) stmt.parent = target } + block.statements.clear() block.definingScope.remove(block) + mergedBlocks.add(block) } } diff --git a/compiler/test/TestImportedModulesOrderAndOptions.kt b/compiler/test/TestImportedModulesOrderAndOptions.kt index 0769d200a..fe86aa6f3 100644 --- a/compiler/test/TestImportedModulesOrderAndOptions.kt +++ b/compiler/test/TestImportedModulesOrderAndOptions.kt @@ -135,5 +135,34 @@ main { compileText(VMTarget(), optimize = false, src) shouldNotBe null } + test("double merge works") { + val src=""" +main { + + sub start() { + block1.sub1() + block1.sub2() + } +} + +block1 { + %option merge + + sub sub1() { + cx16.r1++ + } +} + + +block1 { + %option merge + + sub sub2() { + cx16.r2++ + } +}""" + compileText(VMTarget(), optimize = false, src) shouldNotBe null + } + }) diff --git a/docs/source/syntaxreference.rst b/docs/source/syntaxreference.rst index f3f92abd9..75a30e361 100644 --- a/docs/source/syntaxreference.rst +++ b/docs/source/syntaxreference.rst @@ -724,7 +724,7 @@ Multiple return values ^^^^^^^^^^^^^^^^^^^^^^ Normal subroutines can only return zero or one return values. However, the special ``asmsub`` routines (implemented in assembly code) or ``romsub`` routines -(referencing an external routine in ROM or elsewhere in memory) can return more than one return value. +(referencing an external routine in ROM or elsewhere in RAM) can return more than one return value. For example a status in the carry bit and a number in A, or a 16-bit value in A/Y registers and some more values in R0 and R1. In all of these cases, you have to "multi assign" all return values of the subroutine call to something. You simply write the assignment targets as a comma separated list, @@ -784,14 +784,19 @@ The return type has to be specified if the subroutine returns a value. Assembly / ROM subroutines ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -External subroutines implemented in ROM (or elsewhere in memory) are usually defined by compiler library files, with the following syntax:: +External subroutines implemented in ROM are usually defined by compiler library files, with the following syntax:: - romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) -> clobbers() -> bool @Pc, ubyte @ A, ubyte @ X, ubyte @ Y + romsub $FFD5 = LOAD(ubyte verify @ A, uword address @ XY) clobbers() + -> bool @Pc, ubyte @ A, ubyte @ X, ubyte @ Y This defines the ``LOAD`` subroutine at memory address $FFD5, taking arguments in all three registers A, X and Y, and returning stuff in several registers as well. The ``clobbers`` clause is used to signify to the compiler what CPU registers are clobbered by the call instead of being unchanged or returning a meaningful result value. +.. note:: + Unlike what it's name may suggest, ``romsub`` can also define an external subroutine elsewhere in normal RAM. + It's just that you explicitly define the memory address where it is located and it doesn't matter if that is in ROM or in RAM. + User-written subroutines in the program source code itself, implemented purely in assembly and which have an assembly calling convention (i.e. the parameters are strictly passed via cpu registers), are defined with ``asmsub`` like this:: diff --git a/docs/source/todo.rst b/docs/source/todo.rst index e782dfa16..51e2492c8 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,8 +1,6 @@ TODO ==== -merge problem: if 2 library modules both have merge, stuff breaks (math & prog8_math where prog8_math used to have math block.... didn't work) - for releasenotes: gfx2.width and gfx2.height got renamed as gfx_lores.WIDTH/HEIGHT or gfx_hires4.WIDTH/HEIGTH constants. Screen mode routines also renamed. regenerate symbol dump files diff --git a/examples/test.p8 b/examples/test.p8 index e8a25229d..739c51c2e 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -5,62 +5,25 @@ main { sub start() { - cx16.r0=0 - - if cx16.r0==0 - cx16.r1L=42 - else - cx16.r1L=99 - - cx16.r2L = if cx16.r0==0 42 else 99 - - if cx16.r0==33 - cx16.r1L=42 - else - cx16.r1L=99 - - cx16.r2L = if cx16.r0==33 42 else 99 - - if cx16.r0!=3333 - cx16.r1L=42 - else - cx16.r1L=99 - - cx16.r2L = if cx16.r0!=3333 42 else 99 - - if cx16.r0!=0 - cx16.r1L=42 - else - cx16.r1L=99 - - cx16.r2L = if cx16.r0!=0 42 else 99 - - if cx16.r0!=33 - cx16.r1L=42 - else - cx16.r1L=99 - - cx16.r2L = if cx16.r0!=0 42 else 99 - - if cx16.r0==cx16.r9 - cx16.r1L=42 - else - cx16.r1L=99 - - cx16.r2L = if cx16.r0==cx16.r9 42 else 99 - - if cx16.r0!=cx16.r9 - cx16.r1L=42 - else - cx16.r1L=99 - - cx16.r2L = if cx16.r0!=cx16.r9 42 else 99 - - if cx16.r0>cx16.r1 - cx16.r1L=42 - else - cx16.r1L=99 - - cx16.r2L = if cx16.r0>cx16.r1 42 else 99 + block1.sub1() + block1.sub2() } } + +block1 { + %option merge + + sub sub1() { + txt.print("sub1") + } +} + + +block1 { + %option merge + + sub sub2() { + txt.print("sub2") + } +} +