From d0909d7810014f517570bea58e99491adebf7d53 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 24 Mar 2025 22:35:34 +0100 Subject: [PATCH] added diskio.loadlib() convenience function to load library blobs --- compiler/res/prog8lib/cx16/diskio.p8 | 6 +++++ compiler/res/prog8lib/shared_cbm_diskio.p8 | 6 +++++ compiler/test/TestCompilerOnExamples.kt | 5 +++-- docs/source/binlibrary.rst | 10 ++++++--- examples/c64/library/Makefile | 16 +++++++++++++ examples/c64/library/example.bas | 3 +++ examples/c64/library/main.p8 | 20 +++++++++++++++++ examples/c64/library/thelibrary.p8 | 26 ++++++++++++++++++++++ examples/cx16/fileselector/main.p8 | 2 +- 9 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 examples/c64/library/Makefile create mode 100644 examples/c64/library/example.bas create mode 100644 examples/c64/library/main.p8 create mode 100644 examples/c64/library/thelibrary.p8 diff --git a/compiler/res/prog8lib/cx16/diskio.p8 b/compiler/res/prog8lib/cx16/diskio.p8 index c7a85a8be..50e02442a 100644 --- a/compiler/res/prog8lib/cx16/diskio.p8 +++ b/compiler/res/prog8lib/cx16/diskio.p8 @@ -725,6 +725,12 @@ io_error: } + ; Load a prog8 compiled library binary blob at the given location into memory. + sub loadlib(uword libnameptr, uword libaddress) -> uword { + return internal_load_routine(libnameptr, libaddress, true) + } + + sub internal_load_routine(uword filenameptr, uword address_override, bool headerless) -> uword { cbm.SETNAM(strings.length(filenameptr), filenameptr) ubyte secondary = 1 diff --git a/compiler/res/prog8lib/shared_cbm_diskio.p8 b/compiler/res/prog8lib/shared_cbm_diskio.p8 index f97166381..7ff85284e 100644 --- a/compiler/res/prog8lib/shared_cbm_diskio.p8 +++ b/compiler/res/prog8lib/shared_cbm_diskio.p8 @@ -644,6 +644,12 @@ io_error: return load(filenameptr, start_address) } + ; Load a prog8 compiled library binary blob at the given location into memory. + sub loadlib(uword libnameptr, uword libaddress) -> uword { + return load(libnameptr, libaddress) + } + + sub delete(uword filenameptr) { ; -- delete a file on the drive list_filename[0] = 's' diff --git a/compiler/test/TestCompilerOnExamples.kt b/compiler/test/TestCompilerOnExamples.kt index d258dc225..693d0d2bf 100644 --- a/compiler/test/TestCompilerOnExamples.kt +++ b/compiler/test/TestCompilerOnExamples.kt @@ -91,6 +91,7 @@ class TestCompilerOnExamplesC64: FunSpec({ "cube3d", "cube3d-sprites", "fileselector", + "library/main", "plasma", "rasterbars", "sprites", @@ -134,8 +135,8 @@ class TestCompilerOnExamplesCx16: FunSpec({ "zsmkit_v1/demo2", "zsmkit_v2/demo", "banking/program", - // "fileselector/standalone.p8", - // "fileselector/main.p8", + "fileselector/standalone", + "fileselector/main", "amiga", "audioroutines", "automatons", diff --git a/docs/source/binlibrary.rst b/docs/source/binlibrary.rst index 8c029058f..9262dcf87 100644 --- a/docs/source/binlibrary.rst +++ b/docs/source/binlibrary.rst @@ -60,7 +60,7 @@ source code such as %launcher, %zeropage and so on. But there is a single direct Together with ``%address`` and possibly ``%memtop`` -to tell the compiler what the load address of the library should be- it will create a "library.bin" file that fulfills the requirements of a loadable binary library program as listed above. -For older CBM targets (C64, C128 and PET) the library file will have a load address header, +For older CBM targets (C64, C128 and PET) the library file *will* have a load address header, because these targets require a header to easily load files. For the other targets such as the Commander X16, the library will be a headerless binary file that can then be loaded given the correct load address. @@ -101,7 +101,11 @@ Assuming the load address of the library is $A000: SYS $A008 : REM CALL SECOND ROUTINE, ETC. -**From Prog8**:: +**From Prog8** +The ``diskio`` module actually provides a convenience routine called ``loadlib`` that loads a Prog8-compiled +library blob into memory. It internally automatically uses either regular load() or load_raw(), +as required by the compilation target (so you don't have to bother with target machine +differences if you want to write portable code):: %import diskio @@ -112,7 +116,7 @@ Assuming the load address of the library is $A000: extsub $A008 = lib_func2() clobbers(A,X,Y) sub start() { - if diskio.load_raw("library.bin", $a000) != 0 { + if diskio.loadlib("library.bin", $a000) != 0 { lib_init() lib_func1() lib_func2() diff --git a/examples/c64/library/Makefile b/examples/c64/library/Makefile new file mode 100644 index 000000000..0322053a7 --- /dev/null +++ b/examples/c64/library/Makefile @@ -0,0 +1,16 @@ +.PHONY: all clean zip run + +all: main.prg lib-6000.bin + +clean: + rm -f main.asm main.vice* main.prg thelibrary*.bin thelibrary*.asm thelibrary.vice* lib-6000.bin + +lib-6000.bin: thelibrary.p8 + prog8c -target c64 $< + mv thelibrary.bin lib-6000.bin + +main.prg: main.p8 lib-6000.bin + prog8c -target c64 $< + +run: main.prg + x64sc main.prg diff --git a/examples/c64/library/example.bas b/examples/c64/library/example.bas new file mode 100644 index 000000000..5d8167b34 --- /dev/null +++ b/examples/c64/library/example.bas @@ -0,0 +1,3 @@ +10 IFC=0THENC=1:LOAD"LIB-6000.BIN",8,1 +20 SYS 24576: REM INIT +30 SYS 24579: REM MESSAGE diff --git a/examples/c64/library/main.p8 b/examples/c64/library/main.p8 new file mode 100644 index 000000000..7e9c342a4 --- /dev/null +++ b/examples/c64/library/main.p8 @@ -0,0 +1,20 @@ +%import diskio +%zeropage basicsafe +%option no_sysinit + +main { + sub start() { + if diskio.loadlib("lib-6000.bin", $6000) != 0 { + thelibrary.init() + thelibrary.message() + } + } +} + +thelibrary { + ; initialize the library, required as first call + extsub $6000 = init() clobbers(A) + + ; the routine in the library + extsub $6003 = message() clobbers(A) +} diff --git a/examples/c64/library/thelibrary.p8 b/examples/c64/library/thelibrary.p8 new file mode 100644 index 000000000..cd4680ab8 --- /dev/null +++ b/examples/c64/library/thelibrary.p8 @@ -0,0 +1,26 @@ +%import textio +%output library +%address $6000 + +main { + ; Create a jump table as first thing in the library. + ; NOTE: the compiler has inserted a single JMP instruction at the start + ; of the 'main' block, that jumps to the start() routine. + ; This is convenient because the rest of the jump table simply follows it, + ; making the first jump neatly be the required initialization routine + ; for the library (initializing variables and BSS region). + ; Think of it as the implicit first entry of the jump table. + %jmptable ( + examplelib.message + ) + + sub start() { + ; has to remain here for initialization + } +} + +examplelib { + sub message() { + txt.print("hello from library!\n") + } +} diff --git a/examples/cx16/fileselector/main.p8 b/examples/cx16/fileselector/main.p8 index b3e596a1e..1c179d570 100644 --- a/examples/cx16/fileselector/main.p8 +++ b/examples/cx16/fileselector/main.p8 @@ -5,7 +5,7 @@ main { sub start() { - if diskio.load_raw("fselector-a000.bin", $a000) != 0 { + if diskio.loadlib("fselector-a000.bin", $a000) != 0 { fselector.init() fselector.config(8, fselector.TYPE_ALL) uword filename = fselector.select("*")