documented the romsub bank additions

This commit is contained in:
Irmen de Jong 2024-11-03 20:39:44 +01:00
parent 0fc9aa6b2d
commit cb47e2c149
7 changed files with 62 additions and 29 deletions

View File

@ -72,6 +72,7 @@ What does Prog8 provide?
- inline assembly allows you to have full control when every cycle or byte matters
- supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16 (also available on other targets)
- encode strings and characters into petscii or screencodes or even other encodings
- Automatic ROM/RAM bank switching on certain compiler targets when calling routines in other banks
*Rapid edit-compile-run-debug cycle:*

View File

@ -104,6 +104,7 @@ Features
- Easy and highly efficient integration with external subroutines and ROM routines on the target systems.
- Strings can contain escaped characters but also many symbols directly if they have a PETSCII equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \\, {, } and | are also accepted and converted to the closest PETSCII equivalents.
- Encode strings and characters into petscii or screencodes or even other encodings, as desired (C64/Cx16)
- Automatic ROM/RAM bank switching on certain compiler targets when calling routines in other banks
- Identifiers can contain Unicode Letters, so ``knäckebröd``, ``приблизительно``, ``見せしめ`` and ``π`` are all valid identifiers.
- Advanced code optimizations to make the resulting program smaller and faster
- Programs can be restarted after exiting (i.e. run them multiple times without having to reload everything), due to automatic variable (re)initializations.

View File

@ -805,7 +805,8 @@ Subroutines are parts of the code that can be repeatedly invoked using a subrout
Their definition, using the ``sub`` statement, includes the specification of the required parameters and return value.
Subroutines can be defined in a Block, but also nested inside another subroutine. Everything is scoped accordingly.
With ``asmsub`` you can define a low-level subroutine that is implemented directly in assembly and takes parameters
directly in registers.
directly in registers. Finally with ``romsub`` you can define an external subroutine that's implemented outside
of the program (for instance, a ROM routine, or a routine in a library loaded elsewhere in RAM).
Trivial ``asmsub`` routines can be tagged as ``inline`` to tell the compiler to copy their code
in-place to the locations where the subroutine is called, rather than inserting an actual call and return to the

View File

@ -781,10 +781,10 @@ The parameters is a (possibly empty) comma separated list of "<datatype> <parame
The return type has to be specified if the subroutine returns a value.
Assembly / ROM subroutines
^^^^^^^^^^^^^^^^^^^^^^^^^^^
External subroutines
^^^^^^^^^^^^^^^^^^^^
External subroutines implemented in ROM are usually defined by compiler library files, with the following syntax::
External subroutines 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
@ -793,9 +793,14 @@ This defines the ``LOAD`` subroutine at memory address $FFD5, taking arguments i
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.
**Banks:** it is possible to declare a non-standard ROM or RAM bank that the routine is living in, with ``@rombank`` or ``@rambank`` like this:
``romsub @rombank 10 $C09F = audio_init()`` to define a routine at $C09F in ROM bank 10.
See :ref:`banking` for more information.
.. 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.
``romsub`` is most often used to define ROM subroutines. But contrary to what the name may suggest,
it can also define an external subroutine elsewhere in normal RAM. It simply states the address
and signature of the subroutine; it doesn't care if the routine is in ROM or RAM address space.
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::

View File

@ -25,6 +25,44 @@ It is possible to relocate the BSS section using a compiler option
so that more system ram is available for the program code itself.
.. _banking:
ROM/RAM bank selection
----------------------
On certain systems prog8 provides support for managing the ROM or RAM banks that are active.
For example, on the Commander X16, you can use ``cx16.getrombank()`` to get the active ROM bank,
and ``cx16.rombank(10)`` to make rom bank 10 active. Likewise, ``cx16.getrambank()`` to get the active RAM bank,
and ``cx16.rambank(10)`` to make ram bank 10 active. This is explicit manual banking control.
However, Prog8 also provides something more sophisticated than this, when dealing with banked subroutines:
External subroutines defined with ``romsub`` can have a non-standard ROM or RAM bank specified as well.
The compiler will then transparently change a call to this routine so that the correct bank is activated
automatically before the normal jump to the subroutine (and switched back on return). The programmer doesn't
have to bother anymore with setting/resetting the banks manually, or having the program crash because
the routine is called in the wrong bank! You define such a routine by adding ``@rombank <bank>`` or ``@rambank <bank>``
to the romsub subroutine definition. This specifies the bank number where the subroutine is located in::
romsub @rombank 10 $C09F = audio_init()
When you then call this routine in your program as usual, the compiler will no longer generate a simple JSR instruction to the
routine. Instead it will generate a piece of code that automatically switches the ROM or RAM bank to the
correct value, does the call, and switches the bank back. The exact code will be different for different
compilation targets, and not all targets even have banking or support this. As an example,
on the Commander X16, prog8 will use the JSRFAR kernal routine for this. On the Commodore 128, a similar call exists.
Other compilation targets don't have banking or prog8 doesn't yet support automatic bank selection on them.
Notice that the symbol for this routine in the assembly source code will still be defined as usual.
The bank number is not translated into assembly (only as a comment)::
p8s_audio_init = $c09f ; @rombank 10
.. caution::
Calls with automatic bank switching like this are not safe to use from IRQ handlers. Don't use them there.
Instead change banks in a controlled manual way (or not at all).
.. _symbol-prefixing:
Symbol prefixing in generated Assembly code

View File

@ -1,14 +1,17 @@
TODO
====
add docs for @rombank @rambank on romsubs. Add promo in docs that prog8 does automatic bank switching when calling such a romsub (on cx16 and c128)
consolidate @rombank and @rambank into just @bank
rename 'romsub' to 'extsub' ?
add example for cx16 that compiles and loads libraries in different ram banks and calls romsub from rom and ram banks automatically
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
add support for banked romsubs on the C64 as well (banks basic/kernal rom in/out)
rename 'romsub' to 'extsub' ? keep romsub as alias?
regenerate symbol dump files
Improve register load order in subroutine call args assignments:
in certain situations, the "wrong" order of evaluation of function call arguments is done which results

View File

@ -3,27 +3,11 @@
%zeropage basicsafe
main {
romsub @rombank 10 $C09F = audio_init()
romsub @rambank 22 $A000 = hiram_routine()
sub start() {
block1.sub1()
block1.sub2()
audio_init()
hiram_routine()
}
}
block1 {
%option merge
sub sub1() {
txt.print("sub1")
}
}
block1 {
%option merge
sub sub2() {
txt.print("sub2")
}
}