%output library header generation depends on compiler target

fileselector example tweaks
This commit is contained in:
Irmen de Jong
2025-02-02 20:46:49 +01:00
parent 75ddcda5f3
commit 74dd8fe80b
10 changed files with 59 additions and 73 deletions

View File

@@ -3,8 +3,10 @@ package prog8.codegen.cpu6502
import prog8.code.ast.PtLabel
import prog8.code.core.*
import prog8.code.target.AtariTarget
import prog8.code.target.C128Target
import prog8.code.target.C64Target
import prog8.code.target.Neo6502Target
import prog8.code.target.PETTarget
import java.nio.file.Path
@@ -56,8 +58,13 @@ internal class AssemblyProgram(
binFile
}
OutputType.LIBRARY -> {
command.add("--cbm-prg") // include the 2-byte PRG header on library .bins, so they can be easily loaded on the correct memory address even on C64
println("\nCreating binary library file for target ${compTarget.name}.")
if(compTarget.name in listOf(C64Target.NAME, C128Target.NAME, PETTarget.NAME)) {
println("\nCreating binary library file with header for target ${compTarget.name}.")
command.add("--cbm-prg")
} else {
println("\nCreating binary library file without header for target ${compTarget.name}.")
command.add("--nostart") // should be headerless bin, because basic has problems doing a normal LOAD"lib",8,1 - need to use BLOAD
}
binFile
}
else -> throw AssemblyError("invalid output type")

View File

@@ -275,7 +275,8 @@ class AstPreprocessor(val program: Program,
if(targetStatement is Subroutine) {
for(arg in call.args.zip(targetStatement.parameters)) {
if(arg.first.inferType(program).isBytes && arg.second.type.isString) {
errors.err("cannot use byte value for string parameter", arg.first.position)
if((arg.first as? NumericLiteral)?.number!=0.0)
errors.err("cannot use byte value for string parameter", arg.first.position)
}
}
}

View File

@@ -48,21 +48,22 @@ Binary output and loaded into a fixed memory address
it is not ran like a normal program.
Also, because it is not possible to create position independent code with prog8,
a fixed load address has to be decided on and the library must be compiled
with that address as the load address. For convenience (and compatibility with older CBM
target machines such as the C64 and C128) it's easiest if the resulting library
program includes a PRG load header: 2 bytes at the start of the library that contain
the load address. This allows BASIC to load the library via a simple ``LOAD "LIB.BIN",8,1`` for example.
with that address as the load address.
``%output library``
^^^^^^^^^^^^^^^^^^^
Most (but not all) of the above requirements can be fulfilled by setting various directives in your
Most of the above requirements can be fulfilled by setting various directives in your
source code such as %launcher, %zeropage and so on. But there is a single directive that does it correctly for you in one go
(and makes sure there won't be any initialization code left at all): ``%output library``
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,
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.
The entrypoint (= the start subroutine) that must be called to initialize the variables,
will be the very first thing at the beginning of the library.
@@ -89,21 +90,16 @@ But the users of the library are none the wiser and it just seems as if it is pa
Loading and using the library
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Assuming the load address of the library is $A000 (40960):
These examples below assume the target is the Commander X16.
Assuming the load address of the library is $A000:
**From BASIC** (the example is from the Commander X16)::
**From BASIC**::
LOAD "LIBRARY.BIN",8,1
BLOAD "LIBRARY.BIN",8,1,$A000
SYS $A000 : REM TO INITIALIZE VARIABLES, REQUIRED!
SYS $A004 : REM CALL FIRST ROUTINE
SYS $A008 : REM CALL SECOND ROUTINE, ETC.
For the Commodore 64 and such this works the same but you'll have to type the SYS addresses as decimal numbers.
The Commander X16 also has the BLOAD command to load binary data files, where you have to specify the memory
location where the file has to be loaded to. But for Prog8 library files you don't have to do that, just use LOAD;
the correct address is in the header of the library file. Loading the library to a different memory address
is not possible, because it will only work on the address it was compiled for (it's not possible to create
position independent code on the 6502).
**From Prog8**::
@@ -116,10 +112,12 @@ position independent code on the 6502).
extsub $A008 = lib_func2() clobbers(A,X,Y)
sub start() {
if diskio.load("library.bin", 0) != 0 {
if diskio.load_raw("library.bin", $a000) != 0 {
lib_init()
lib_func1()
lib_func2()
repeat { }
}
}
}
@@ -132,7 +130,11 @@ position independent code on the 6502).
void (*lib_init)(void) = (void (*)()) 0xa000;
void (*lib_func1)(void) = (void (*)()) 0xa004;
void (*lib_func2)(void) = (void (*)()) 0xa008;
cbm_load("library.bin", 8, 0);
cbm_k_setlfs(0, 8, 2);
cbm_k_setnam("library.bin");
cbm_k_load(0, 0xa000);
lib_init();
lib_func1();
lib_func2();
@@ -146,13 +148,13 @@ position independent code on the 6502).
ldx #<libname
lda #11
jsr $ffbd ; SETNAM
ldy #1
lda #0
ldx #8
lda #1
ldy #2 ; load address override
jsr $ffba ; SETLFS
lda #0
ldx #0
ldy #0
ldx #<$a000
ldy #>$a000
jsr $ffd5 ; LOAD
lda #13
jsr $ffd2 ; CHROUT

View File

@@ -419,7 +419,7 @@ Directives
- type ``raw`` : no header at all, just the raw machine code data
- type ``prg`` : C64 program (with load address header)
- type ``xex`` : Atari xex program
- type ``library`` : loadable library file (with CBM style load address header) See :ref:`loadable_library`.
- type ``library`` : loadable library file. See :ref:`loadable_library`.
.. data:: %zeropage <style>

View File

@@ -0,0 +1,11 @@
10 BLOAD"FSELECTOR-A000.BIN",8,1,$A000
20 SYS$A000 : REM INIT
30 GOSUB 1000
40 PRINT:PRINT:PRINT:PRINT"SELECTED: ";F$
50 END
1000 POKE$400,ASC("*"):POKE$401,0 : REM PATTERN
1010 POKE$30E,4:POKE$30C,0:SYS$A00C : REM SELECT
1020 ADDR=PEEK($30E)*256+PEEK($30C):F$="" : REM FILENAME
1030 C=PEEK(ADDR):IFC=0 THEN RETURN
1040 F$=F$+CHR$(C):ADDR=ADDR+1:GOTO 1030

View File

@@ -3,15 +3,16 @@
all: main.prg fselector.bin standalone.prg
clean:
rm -f main.asm main.vice* main.prg fselector.asm fselector.vice* fselector.bin standalone.asm standalone.vice* standalone.prg
rm -f main.asm main.vice* main.prg fselector.asm fselector.vice* fselector*.bin standalone.asm standalone.vice* standalone.prg
fselector.bin: fselector.p8
fselector-a000.bin: fselector.p8
prog8c -target cx16 $<
mv fselector.bin fselector-a000.bin
standalone.prg: standalone.p8
prog8c -target cx16 $<
main.prg: main.p8 fselector.bin
main.prg: main.p8 fselector-a000.bin
prog8c -target cx16 $<
run: main.prg

View File

@@ -36,7 +36,7 @@ main {
fileselector {
; these buffer sizes are chosen to fill up the rest of the hiram bank after the fileselector code
const uword filenamesbuf_size = $eb0
const uword filenamesbuf_size = $ea0
const ubyte max_num_files = 128
uword @shared filenamesbuffer = memory("filenames_buffer", filenamesbuf_size, 0)
@@ -71,6 +71,10 @@ fileselector {
}
sub select(str pattern) -> uword {
str defaultpattern="*"
if pattern==0
pattern = &defaultpattern
num_visible_files = 0
diskio.list_filename[0] = 0
name_ptr = diskio.diskname()

View File

@@ -5,7 +5,7 @@
main {
sub start() {
if diskio.load("fselector.bin", 0) != 0 {
if diskio.load_raw("fselector-a000.bin", $a000) != 0 {
fselector.init()
;; fselector.config_types(fselector.TYPE_ALL)
uword filename = fselector.select("*")
@@ -30,6 +30,6 @@ fselector {
; configure the position and appearance of the dialog
extsub $a008 = config_appearance(ubyte column @R0, ubyte row @R1, ubyte max_entries @R2, ubyte normalcolors @R3, ubyte selectedcolors @R4) clobbers(A)
; show the file selector dialog. Normal pattern would be "*" to include everything.
; show the file selector dialog. Normal pattern would be "*" to include everything. Returns the selected entry name, or 0 if error or nothing selected.
extsub $a00c = select(str pattern @AY) clobbers(X) -> uword @AY
}

View File

@@ -77,6 +77,9 @@ fileselector {
}
sub select(str pattern) -> uword {
str defaultpattern="*"
if pattern==0
pattern = &defaultpattern
sys.push(cx16.getrambank())
cx16.r0 = internal_select(pattern)
cx16.rambank(sys.pop())

View File

@@ -1,45 +1,3 @@
%import textio
%import diskio
%zeropage basicsafe
%option no_sysinit
main {
sub start() {
txt.lowercase()
diskio.directory_dirs()
txt.nl()
diskio.directory_files()
txt.nl()
diskio.lf_start_list_dirs(0)
while diskio.lf_next_entry() {
txt.print(diskio.list_filetype)
txt.spc()
txt.spc()
txt.print(diskio.list_filename)
txt.nl()
}
diskio.lf_end_list()
txt.nl()
txt.nl()
diskio.lf_start_list_files(0)
while diskio.lf_next_entry() {
txt.print(diskio.list_filetype)
txt.spc()
txt.spc()
txt.print(diskio.list_filename)
txt.nl()
}
diskio.lf_end_list()
}
}
/*
%address $A000
%memtop $C000
%output library
@@ -74,4 +32,3 @@ library {
txt.print("lib func 2\n")
}
}
*/