1
0
mirror of https://github.com/fachat/xa65.git synced 2024-06-01 07:41:52 +00:00
xa65/xa/tests/loader/loader.a65
Andre Fachat 77471ee3d9 beta 3
2023-11-02 10:12:39 +01:00

910 lines
12 KiB
Plaintext

/**************************************************************************
*
* Loader for 6502 relocatable binary format
*
* The loader supports 16 bit o65 version 1 files without undefined
* references. Also it doesn't like pagewise relocation and 65816
* code, because there are different/additional relocation entries.
*
* The support routines, that have to be changed are at the end of the
* file. The stuff in this file is in absolute format (well, you have to
* bootstrap from something :-)
* The support routines for the file handling are for the operating system
* OS/A65, as of version 1.3.10b. The first part of the file (wrapped in
* "#ifdef C64") shows how to use it with a C64, for example.
*
* The subroutine 'loader' is called with a file descriptor, that has a
* meaning for the support routines, in the X register.
* The file must already be open. Also binit must have been called before.
* The loader doesn't close the file.
*
* Support routines are:
* binit a = hi byte start address memory to handle,
* x = hi byte length of memory to handle
* balloc a/y = length of block -> x = memory descriptor
* bfree x = memory block descriptor to free
* getbadr x = memory descriptor -> a/y address of memory block
*
* zalloc a = length of needed zeropage block. returns a=address
* zfree a = address of block to free
*
* fgetc x = file descriptor, returns read byte (c=0) or error (c=1)
* The error is passed through; fgetc blocks if no data
* available
* fgetb x = filedescriptor, a/y = address of block descriptor,
* i.e. a word start address and a word length of block.
* returns (c=0) or error in accu (c=1).
*
**************************************************************************/
/**************************************************************************
* This part is a binding for a C64
* to show how it works
*/
#define C64
#ifdef C64
sysmem =$033c
syszp =$57
.word $0801
*=$801
.word nextl
.word 10
.byt $9e, "2080",0
nextl .word 0
.dsb 2080-*, 0
c64load .(
lda #>PRGEND+255
ldx #>$A000-PRGEND
jsr binit ; init memory handling
lda #7
ldx #<fname
ldy #>fname
jsr $ffbd ; setfnpar
lda #2
ldx #11
ldy #0
jsr $ffba ; setfpar
jsr $ffc0 ; open
bcs end
ldx #2
jsr $ffc6 ; chkin
bcs end
jsr loader ; don't care about x, chkin did it
php
pha
jsr $ffcc ; clrch
lda #2
jsr $ffc3 ; close
pla
plp
end rts
fname ;.asc "example",0
.byt $45, $58, $41, $4d, $50, $4c, $45, 0
.)
fgetc .(
jsr $ffcf
php
pha
bcc carry
lda #"C"
jsr $ffd2
pla
pha
carry
jsr hexout
lda #$20
jsr $ffd2
pla
plp
rts
.)
hexout .(
pha
lsr
lsr
lsr
lsr
jsr nibout
pla
nibout and #$0f
clc
adc #$30
cmp #$3a
bcc ok
adc #$41-$3a-1
ok jmp $ffd2
.)
zalloc .(
cmp #$80 ; save from $90 upward = OS, below is only basic
bcs end
lda #$10
end rts
.)
zfree .(
clc
rts
.)
#endif
/**************************************************************************
* Here is the real loader code
*/
#include "file.def"
#define E_NOMEM <-40
#define E_FVERSION <-41
loader .(
p1 =syszp
p2 =syszp+2
-syszp +=4
tmp =sysmem
file =sysmem+1
-sysmem +=2
header =sysmem
-sysmem +=HDR_LEN
textb =sysmem ; memory block ID
texta =sysmem+1 ; address of block
textl =sysmem+3 ; address of block
textd =sysmem+5 ; difference to assemble address
-sysmem +=7
datab =sysmem
dataa =sysmem+1
datal =sysmem+3
datad =sysmem+5
-sysmem +=7
bssb =sysmem
bssa =sysmem+1
bssd =sysmem+3
-sysmem +=5
zeroa =sysmem
zerod =sysmem+1
-sysmem +=3
stx file
jsr fgetc
bcs end
sta tmp
jsr fgetc
bcs end
tay
lda tmp
cpy #0
bne rt
cmp #1
beq load
rt lda #E_FVERSION ; ok, but not this version
end sec
rts
load .(
lda #<header
sta p1
lda #>header
sta p1+1
lda #<HDR_LEN
sta p1+2
lda #>HDR_LEN
sta p1+3
ldx file
lda #<p1
ldy #>p1
jsr fgetb
bcs end
; header loaded, check magic and version
lda header+HDR_MAGIC
cmp #$6f
bne end
lda header+HDR_MAGIC+1
cmp #"6"
bne end
lda header+HDR_MAGIC+2
cmp #"5"
bne end
lda header+HDR_VERSION
cmp #0
bne end
lda header+HDR_MODE+1
and #%11110000
bne end
; now allocate buffers
lda header+HDR_TLEN
ldy header+HDR_TLEN+1
sta textl
sty textl+1
jsr balloc
stx textb
bcs no_text2
jsr getbadr
sta texta
sty texta+1
sec
sbc header+HDR_TBASE
sta textd
tya
sbc header+HDR_TBASE+1
sta textd+1
lda header+HDR_DLEN
ldy header+HDR_DLEN+1
sta datal
sty datal+1
jsr balloc
stx datab
bcs no_data2
no_text2 bcs no_text1
jsr getbadr
sta dataa
sty dataa+1
sec
sbc header+HDR_DBASE
sta datad
tya
sbc header+HDR_DBASE+1
sta datad+1
lda header+HDR_BLEN
ldy header+HDR_BLEN+1
jsr balloc
stx bssb
bcs no_bss
no_text1 bcs no_text
no_data2 bcs no_data
jsr getbadr
sta bssa
sty bssa+1
sec
sbc header+HDR_BBASE
sta bssd
tya
sbc header+HDR_BBASE+1
sta bssd+1
lda header+HDR_ZLEN
jsr zalloc
bcs no_zero
sta zeroa
sec
sbc header+HDR_ZBASE
sta zerod
lda #0
sta zerod+1
jmp do_load
&no_file lda zeroa
jsr zfree
no_zero ldx bssb
jsr bfree
no_bss ldx datab
jsr bfree
no_data ldx textb
jsr bfree
no_text rts
do_load ; load options (i.e. ignore them now)
jsr fgetc
bcs no_file
cmp #0
beq load_text
tay
dey
optl jsr fgetc
bcs no_file
dey
bne optl
beq do_load
load_text ; load text segment
ldx file
lda #<texta
ldy #>texta
jsr fgetb
bcs no_file
ldx file
lda #<dataa
ldy #>dataa
jsr fgetb
bcs no_file
; check number of undefined references
ldx file
jsr fgetc
&no_file2 bcs no_file
cmp #0
bne no_file ; we have some -> not handled
ldx file
jsr fgetc
bcs no_file
cmp #0
bne no_file
; ok, text segments loaded, now relocate
lda texta
sec
sbc #1
sta p1
lda texta+1
sbc #0
sta p1+1
jsr trel
lda dataa
sec
sbc #1
sta p1
lda dataa+1
sbc #0
sta p1+1
jsr trel
lda texta ; return start of text segment
ldy texta+1
clc
rts
.)
trel .(
ldx file
jsr fgetc
no_file1 bcs no_file2
cmp #0
beq reloc_rts
cmp #255
bne t1
lda #254
clc
adc p1
sta p1
bcc trel
inc p1+1
jmp trel
t1 clc
adc p1
sta p1
bcc t1a
inc p1+1
t1a ; p1 is the relocation address
ldx file
jsr fgetc
bcs no_file1
tay
and #A_MASK
sta tmp
tya
and #A_FMASK
jsr getreldiff
ldy tmp
cpy #A_ADR
bne t2
ldy #0
clc
adc (p1),y
sta (p1),y
iny
txa
adc (p1),y
sta (p1),y
jmp trel
t2
cpy #A_LOW
bne t3
ldy #0
clc
adc (p1),y
sta (p1),y
jmp trel
t3
cpy #A_HIGH
bne trel
sta p2
stx p2+1
ldx file
jsr fgetc
clc
adc p2 ; just get the carry bit
ldy #0
lda p2+1 ; relocate high byte
adc (p1),y
sta (p1),y
jmp trel
reloc_rts
clc
rts
.)
getreldiff .( ; comparing with SEG_UNDEF would give a way
; to get label value here for undefined refs
cmp #SEG_TEXT
bne notext
lda textd
ldx textd+1
rts
notext cmp #SEG_DATA
bne nodata
lda datad
ldx datad+1
rts
nodata cmp #SEG_BSS
bne nobss
lda bssd
ldx bssd+1
rts
nobss cmp #SEG_ZERO
bne nozero
lda zerod
ldx zerod+1
nozero rts
.)
.)
/**************************************************************************
* Here come the support routines
*
* first is a simple and basic implementation of fgetb, just using fgetc
*/
fgetb .(
p =syszp
-syszp +=2
file =sysmem
l =sysmem+1
-sysmem +=3
stx file ; x=file, a=zp-adr of address, y=zp-adr of len
sta p
sty p+1
ldy #3
lda (p),y
sta l+1
dey
lda (p),y
sta l
dey
lda (p),y
pha
dey
lda (p),y
sta p
pla
sta p+1
loop ldx file
jsr fgetc ; this is a simple implementation
bcs end
ldy #0
sta (p),y
inc p
bne l0
inc p+1
l0
lda l
bne l1
dec l+1
l1 dec l
lda l
ora l+1
bne loop
clc
end
rts
.)
/**************************************************************************
* support for memory allocation
*
* These routines are taken from a preliminary SLIP implementation, as of
* OS/A65 version 1.3.10b
*/
#define MAXSLOT 8
/**********************************************************************/
/* New memory management for IP buffers */
/* exports */
/* binit */
/* balloc, bfree, btrunc, bsplit, brealloc */
/* getbadr, getblen */
#define MINBUF 8
#define MINMASK %11111000
.(
slotladr =sysmem
slothadr =sysmem+MAXSLOT
slotllen =sysmem+MAXSLOT*2
slothlen =sysmem+MAXSLOT*3
-sysmem +=MAXSLOT*4
flist =sysmem
slot =sysmem+2
-sysmem +=3
p =syszp
p2 =syszp+2
p3 =syszp+4
p4 =syszp+6
-syszp +=8
/* init memory management */
&binit .(
sta p+1 ; hi byte startadress buffer
stx p2+1 ; hi byte length of buffer
lda #0
tay
l0 sta slotladr,y
sta slothadr,y
iny
cpy #MAXSLOT
bcc l0
sta p
tay
sta (p),y
iny
sta (p),y
iny
sta (p),y
iny
lda p2+1
sta (p),y
lda p
sta flist
lda p+1
sta flist+1
clc
rts
.)
/* a/y = size of buffer to be allocated -> x buffer-ID */
&balloc .(
/* walk along freelist, and take first matching buffer
length is made a multiple of 8 (for freelist connectors */
pha
jsr getbslot
pla
bcc gotslot
lda #E_NOMEM
rts
gotslot
stx slot
adc #MINBUF-1
and #MINMASK
sta slotllen,x
tya
adc #0
sta slothlen,x
lda #0
sta p2
sta p2+1
lda flist
sta p
lda flist+1
sta p+1
l0
ldy #2
lda (p),y
sec
sbc slotllen,x
sta p3
iny
lda (p),y
sbc slothlen,x
sta p3+1
bcs found
lda p
sta p2
lda p+1
sta p2+1
ldy #1
lda (p2),y
sta p+1
dey
lda (p2),y
sta p
ora p+1
bne l0
oops lda #E_NOMEM
sec
rts
found
/* ok, we found a free buffer: p points to the buffer, p2 to the
previous one. p3 is the length of the free buffer minus the
needed size. If the buffer is longer than needed, create a
new free buffer, then link new buffer to freelist */
lda p /* save buffer address */
sta slotladr,x
lda p+1
sta slothadr,x
lda p3 /* check length */
ora p3+1
beq nocreate
lda p /* get address of new free buffer */
clc
adc slotllen,x
sta p4
lda p+1
adc slothlen,x
sta p4+1
ldy #0 /* copy next pointer */
lda (p),y
sta (p4),y
iny
lda (p),y
sta (p4),y
iny /* set new length */
lda slotllen,x
sta (p),y
lda p3
sta (p4),y
iny
lda slothlen,x
sta (p),y
lda p3+1
sta (p4),y
lda p4
sta p
lda p4+1
sta p+1
nocreate
lda p2
ora p2+1
beq freestart
ldy #0
lda p
sta (p2),y
iny
lda p+1
sta (p2),y
clc
bcc geta
freestart
lda p
sta flist
lda p+1
sta flist+1
clc
geta
lda slotladr,x
ldy slothadr,x
rts
.)
/* free buffer (ID=xr) */
&bfree .(
lda slothadr,x
sta p3+1
lda slotladr,x
sta p3
ora p3+1
beq end2
ldy #2
lda slotllen,x
sta (p3),y
iny
lda slothlen,x
sta (p3),y
lda #0
sta slothadr,x
sta slotladr,x
lda flist
ora flist+1
bne ok /* no free buffer so far? */
lda p3
sta flist
lda p3+1
sta flist+1
ldy #0
tya
sta (p3),y
iny
sta (p3),y
end2 clc
rts
ok
lda #0
sta p2
sta p2+1
lda flist
sta p
lda flist+1
sta p+1
/* we have to find the place where to put the buffer in the
ordered free list. Then we have to check if we can merge
free buffers */
loop
lda p3+1
cmp p+1
bcc found
bne next
lda p3
cmp p
bcc found
next
lda p
sta p2
lda p+1
sta p2+1
ldy #0
lda (p2),y
sta p
iny
lda (p2),y
sta p+1
ora p
bne loop
beq found
end
clc
rts
found /* p2 is the buffer before the one to be freed, p the one behind.
p3 is the buffer to be inserted */
lda p2
ora p2+1
bne fok
; insert before the first free buffer so far
ldy #0
lda flist
sta (p3),y
iny
lda flist+1
sta (p3),y
lda p3
sta flist
ldy p3+1
sty flist+1
jsr bmerge
clc
rts
fok ; insert to list
ldy #1
lda p+1 ;lda (p2),y
sta (p3),y
dey
lda p ;lda (p2),y
sta (p3),y
lda p3
sta (p2),y
iny
lda p3+1
sta (p2),y
lda p3
ldy p3+1
jsr bmerge
lda p2
ldy p2+1
jsr bmerge
clc
rts
.)
/* get adress of buffer */
&getbadr .(
lda slotladr,x
ldy slothadr,x
clc
rts
.)
/* get length of buffer */
&getblen .(
lda slotllen,x
ldy slothlen,x
clc
rts
.)
/* get free buffer-ID slot */
getbslot .(
ldx #0
l0
clc
lda slotladr,x
ora slothadr,x
beq found
inx
cpx #MAXSLOT
bcc l0
found
rts
.)
/* check if two buffers (i.e. a/y plus following) can be merged */
bmerge .(
sta p
sty p+1
ldy #2
clc
lda (p),y
adc p
sta p3
iny
lda (p),y
adc p+1
sta p3+1
ldy #0
lda (p),y
cmp p3
bne nomerge
iny
lda (p),y
cmp p3+1
bne nomerge
merge
ldy #2
clc
lda (p3),y
adc (p),y
sta (p),y
iny
lda (p3),y
adc (p),y
sta (p),y
ldy #0
lda (p3),y
sta (p),y
iny
lda (p3),y
sta (p),y
nomerge
clc
rts
.)
.)
PRGEND