;; An example ROM and how to override a firmware function with our own function
;;
;; Firmware jumpblock entries are 3 bytes long.
;;
;; Patch the function with a FAR CALL which is:
;; RST 3
;; defw address
;; 
;; The address should be in main RAM, preferably in an area that your ROM has reserved.
;; The address has 3 bytes: 
;; defw address_in_rom (e.g. &c000)
;; defb rom_number (e.g. &7)
;;
;; You can find the number of your ROM by calling KL CURR SELECTION when your start-up function
;; of your ROM has been called.
;;
;; calling the function then becomes:
;; call <firmware_function> -> far call -> your rom
;;
;; For a filesystem, we want to call our functions only if our filesystem is active, otherwise call
;; the previous functions
;;
;; AMSDOS uses a clever method when it patches the functions.. it looks at the return address
;; and uses that to go to the appropiate overridden function.

;; location for upper roms
org &c000

;; rom header
defb 1			;; type
defb 1			;; mark
defb 0			;; version
defb 0			;; modification
defw name_table
jp boot

;;-----------------------------------------------------------------------------

;; name table
name_table:
;; this is the start-up function
;; use a space in the name to stop basic from calling it
defb "NEW DO","S"+&80		
defb 0

;;------------------------------------------------------------------------------

boot_message:
defb "OUR NEW DOS",0

;;------------------------------------------------------------------------------

display_message:
ld a,(hl)
inc hl
or a
ret z
call txt_output
jr display_message

;;-----------------------------------------------------------------------------


;; functions we want to override
cas_in_open equ &bc77
cas_in_close equ &bc7a
cas_in_abandon equ &bc7d
cas_in_char equ &bc80
cas_in_direct equ &bc83
cas_return equ &bc86
cas_test_eof equ &bc89
cas_out_open equ &bc8c
cas_out_close equ &bc8f
cas_out_abandon equ &bc92
cas_out_char equ &bc95
cas_out_direct equ &bc98
cas_catalog equ &bc9b
kl_curr_selection equ &b912
txt_output equ &bb5a


rst_3_instruction equ &df

cas_range_size equ cas_catalog+3-cas_in_open

our_far_calls equ 0
old_calls_offset equ cas_range_size

boot:
;; allocate some space for:
;; 1. old calls
;; 2. our new far calls
or a
ld bc,cas_range_size+cas_range_size			;; reserve some bytes
sbc hl,bc
push hl
push de


push hl
ld bc,old_calls_offset		;; offset into our buffer for old calls
add hl,bc					;; base of our area
ex de,hl

;; DE = address in our reserved area to store old calls
ld hl,cas_in_open
;; length of all the calls we will patch
ld bc,cas_catalog+3-cas_in_open
ldir

;; setup some far calls 
;; we patch the firmware function with
;; rst and an address (within our reserved area)
;; which will call our rom functions

;; which rom are we?
call kl_curr_selection
ld c,a


;; offset into our data
ld e,(ix+store_offset+0)
ld d,(ix+store_offset+1)
add hl,de
ex de,hl
;; DE = address to write

;; function to copy
ld l,(ix+orig_function+0)
ld h,(ix+orig_function+1)
push hl
;; copy 3 bytes
ldi
ldi
ldi
pop hl

ld a,rst_3_instruction
ld (hl),a
inc hl
ld e,(ix+new_function+0)
ld d,(ix+new_function+1)
ld (hl),e
inc hl
ld (hl),d

inc de

pop hl
;; hl = address of our calls
ld bc,our_far_calls
add hl,bc
;; hl = address in ram of our far calls

ld ix,our_calls
ld b,num_our_calls

ld e,(ix+0)
ld d,(ix+1)


ld e,(ix+0)
ld d,(ix+1)
inc ix
inc ix
ld (hl),e				;; address
inc hl
ld (hl),d
inc hl
ld (hl),c				;; rom
inc hl

;; write into our ram
ld de,our_cas_in_open
call write_far_call


ld hl,cas_in_open
ld de,our_cas_in_open_farcall
call patch_firmware

;; we could try a crc and report an error and return bad initialisation...?

;; show startup message
ld hl,boot_message
call display_message

pop de
pop hl
;; initialisation successful
scf
ret

num_our_calls equ (end_our_calls-our_calls)/2

our_calls:
;; function to override
defw cas_in_open
;; our call
defw our_cas_in_open
;; offset to stored function
defw 0
;; offset to far call in our area
defw 0

defw cas_in_close
defw our_cas_in_close
defw cas_in_abandon
defw our_cas_in_abandon
defw cas_in_char
defw our_cas_in_char

defw our_cas_in_direct
defw our_cas_return
defw our_cas_test_eof
defw our_cas_out_open
defw our_cas_out_close
defw our_cas_out_abandon
defw our_cas_out_char
defw our_cas_out_direct
defw our_cas_catalog
end_our_calls:
;;-----------------------------------------------------------------------


;; B = length of filename
;; HL = points to filename
;; DE = 2k buffer

;; example AMSDOS filenames:
;; <user><drive>:<filename>
;; <drive>:<filename>
;; <user>:<filename>
;; <filename>
;;
;;

;; iy points to our area.
our_cas_in_open:
ret




;;-----------------------------------------------------------------------
;; HL = address in main ram to write far call
;; DE = function in our rom
;; C = rom select
;;
;; far call structure:
;; 2-byte address, 1-byte rom select
write_far_call:
ld (hl),e
inc hl
ld (hl),d
inc hl
ld (hl),c
inc hl
ret

;;-----------------------------------------------------------------------
;; HL = firmware function to patch
;; DE = far call address
;;
;; far call:
;; rst 3
;; 2-byte address of far call structure
patch_firmware:
ld (hl),rst_3_instruction					;; rst 3
inc hl
ld (hl),e
inc hl
ld (hl),d
inc hl
ret