;--------------------------------------------------------------------------------------------------------
;-
;- Name:            rfs_bank9.asm
;- Created:         July 2019
;- Author(s):       Philip Smart
;- Description:     Sharp MZ series Rom Filing System.
;-                  This assembly language program is written to utilise the banked flashroms added with
;-                  the MZ-80A RFS hardware upgrade.
;-
;- Credits:
;- Copyright:       (c) 2018-2026 Philip Smart <philip.smart@net2net.org>
;-
;- History:         July 2019 - Merged 2 utilities to create this compilation.
;-                  May 2020  - Bank switch changes with release of v2 pcb with coded latch. The coded
;-                              latch adds additional instruction overhead as the control latches share
;-                              the same address space as the Flash RAMS thus the extra hardware to
;-                              only enable the control registers if a fixed number of reads is made
;-                              into the upper 8 bytes which normally wouldnt occur. Caveat - ensure
;-                              that no loop instruction is ever placed into EFF8H - EFFFH.
;-                  Aug 2023  - Updates to make RFS run under the SFD700 Floppy Disk Interface board.
;-                              UROM remains the same, a 2K paged ROM, MROM is located at F000 when
;-                              RFS is built for the SFD700.
;-                  Mar 2026  - Moved ROM directory, find, load and print functions from bank 0 for
;-                              picoZ80/RomDisk builds to free space in bank 0.
;-
;--------------------------------------------------------------------------------------------------------
;- This source file is free software: you can redistribute it and-or modify
;- it under the terms of the GNU General Public License as published
;- by the Free Software Foundation, either version 3 of the License, or
;- (at your option) any later version.
;-
;- This source file is distributed in the hope that it will be useful,
;- but WITHOUT ANY WARRANTY; without even the implied warranty of
;- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;- GNU General Public License for more details.
;-
;- You should have received a copy of the GNU General Public License
;- along with this program.  If not, see <http://www.gnu.org/licenses/>.
;--------------------------------------------------------------------------------------------------------

            ;===========================================================
            ;
            ; USER ROM BANK 9 -
            ;
            ;===========================================================
            ORG     UROMADDR

            ;--------------------------------
            ; Common code spanning all banks.
            ;--------------------------------
            NOP
            HWSELROM2                                                        ; Select the first ROM page.
            ;
            ; No mans land... this should have switched to Bank 0 and at this point there is a jump to 00000H.
            JP      00000H                                                   ; This is for safety!!


            ;------------------------------------------------------------------------------------------
            ; Bank switching code, allows a call to code in another bank.
            ; This code is duplicated in each bank such that a bank switch doesnt affect logic flow.
            ;------------------------------------------------------------------------------------------
            ALIGN_NOPS UROMBSTBL
            ;
BKSW9to0:   PUSH    AF
            LD      A, ROMBANK9                                              ; Calling bank (ie. us).
            PUSH    AF
            LD      A, ROMBANK0                                              ; Required bank to call.
            JR      BKSW9_0
BKSW9to1:   PUSH    AF
            LD      A, ROMBANK9                                              ; Calling bank (ie. us).
            PUSH    AF
            LD      A, ROMBANK1                                              ; Required bank to call.
            JR      BKSW9_0
BKSW9to2:   PUSH    AF
            LD      A, ROMBANK9                                              ; Calling bank (ie. us).
            PUSH    AF
            LD      A, ROMBANK2                                              ; Required bank to call.
            JR      BKSW9_0
BKSW9to3:   PUSH    AF
            LD      A, ROMBANK9                                              ; Calling bank (ie. us).
            PUSH    AF
            LD      A, ROMBANK3                                              ; Required bank to call.
            JR      BKSW9_0
BKSW9to4:   PUSH    AF
            LD      A, ROMBANK9                                              ; Calling bank (ie. us).
            PUSH    AF
            LD      A, ROMBANK4                                              ; Required bank to call.
            JR      BKSW9_0
BKSW9to5:   PUSH    AF
            LD      A, ROMBANK9                                              ; Calling bank (ie. us).
            PUSH    AF
            LD      A, ROMBANK5                                              ; Required bank to call.
            JR      BKSW9_0
BKSW9to6:   PUSH    AF
            LD      A, ROMBANK9                                              ; Calling bank (ie. us).
            PUSH    AF
            LD      A, ROMBANK6                                              ; Required bank to call.
            JR      BKSW9_0
BKSW9to7:   PUSH    AF
            LD      A, ROMBANK9                                              ; Calling bank (ie. us).
            PUSH    AF
            LD      A, ROMBANK7                                              ; Required bank to call.
            JR      BKSW9_0
BKSW9to8:   PUSH    AF
            LD      A, ROMBANK9                                              ; Calling bank (ie. us).
            PUSH    AF
            LD      A, ROMBANK8                                              ; Required bank to call.
            JR      BKSW9_0
BKSW9to9:   PUSH    AF
            LD      A, ROMBANK9                                              ; Calling bank (ie. us).
            PUSH    AF
            LD      A, ROMBANK9                                              ; Required bank to call.
            JR      BKSW9_0
BKSW9to10:  PUSH    AF
            LD      A, ROMBANK9                                              ; Calling bank (ie. us).
            PUSH    AF
            LD      A, ROMBANK10                                             ; Required bank to call.
            JR      BKSW9_0
BKSW9to11:  PUSH    AF
            LD      A, ROMBANK9                                              ; Calling bank (ie. us).
            PUSH    AF
            LD      A, ROMBANK11                                             ; Required bank to call.
            ;
BKSW9_0:    PUSH    HL                                                       ; Place function to call on stack
            LD      HL, BKSWRET8                                             ; Place bank switchers return address on stack.
            EX      (SP),HL
            LD      (TMPSTACKP),SP                                           ; Save the stack pointer as some old code corrupts it.
            BNKSWSEL
            JP      (HL)                                                     ; Jump to required function.
BKSWRET9:   POP     AF                                                       ; Get bank which called us.
            BNKSWSELRET
            POP     AF
            RET

            ;-------------------------------------------------------------------------------
            ; START OF METHODS
            ;-------------------------------------------------------------------------------

            IF BUILD_ROMDISK+BUILD_PICOZ80 = 1

            ;------------------------------------------------------------------------------------------
            ; Jump table for functions accessed from monitor ROM.
            ; PRTMZF and PRTDBG are at fixed offsets from UROMADDR (0xA0 and 0xA3).
            ;------------------------------------------------------------------------------------------
            ALIGN_NOPS RFSJMPTABLE
            ORG     RFSJMPTABLE
PRTMZF9:    JP      _PRTMZF9
PRTDBG9:    JP      _PRTDBG9


            ;------------------------------------------------------------------------------------------
            ; Function:     ISMZF9
            ; Description:  Check if the data pointed to by HL looks like a valid MZF header.
            ;               On return Z flag set = valid MZF, NZ = not valid.
            ;------------------------------------------------------------------------------------------
ISMZF9:     PUSH    BC
            PUSH    DE
            PUSH    HL
            ;
            LD      A,(HL)
            CP      OBJCD                                                ; Only interested in machine code images.
            JR      NZ, ISMZFNOT9
            ;
            INC     HL
            LD      DE,NAME                                              ; Checks to confirm this is an MZF header.
            LD      B,FNSIZE                                             ; Maximum of 17 characters, including terminator in filename.
ISMZFNXT9:  LD      A,(HL)
            LD      (DE),A
            CP      00DH                                                 ; If we find a terminator then this indicates potentially a valid name.
            JR      Z, ISMZFNXT39
            CP      000H                                                 ; Same applies for NULL terminator.
            JR      Z, ISMZFNXT39
            CP      020H                                                 ; >= Space
            JR      C, ISMZFNOT9
            CP      05DH                                                 ; =< ]
            JR      C, ISMZFNXT39
ISMZFNXT29: CP      091H
            JR      C, ISMZFNOT9                                         ; DEL or > 0x7F, cant be a valid filename so this is not an MZF header.
ISMZFNXT39: INC     DE
            INC     HL
            DJNZ    ISMZFNXT9
ISMZFYES9:  CP      A                                                    ; Set zero flag to indicate match.

ISMZFNOT9:  POP     HL
            POP     DE
            POP     BC
            RET


            ;------------------------------------------------------------------------------------------
            ; Function:     _PRTDBG9
            ; Description:  Debug output, conditional on ENADEBUG=1.
            ;------------------------------------------------------------------------------------------
_PRTDBG9:   IF ENADEBUG = 1
            PUSH    HL
            PUSH    DE
            PUSH    BC
            PUSH    AF
            IF BUILD_ROMDISK+BUILD_PICOZ80 = 1
              LD    A,(ROMBK1)
              HWSELMROM                                                  ; Set the MROM bank back to original.
            ENDIF
            CALL    PRTHL                                                ; HL
            LD      A, ' '
            CALL    PRNT
            LD      H,B
            LD      L,C
            CALL    PRTHL                                                ; BC
            LD      A, ' '
            CALL    PRNT
            LD      H,D
            LD      L,E
            CALL    PRTHL                                                ; DE
            LD      A, ' '
            CALL    PRNT
            POP     HL                                                   ; Get AF into HL.
            PUSH    HL
            CALL    PRTHL                                                ; AF
            LD      A, ' '
            CALL    PRNT
            LD      A, ':'
            CALL    PRNT
            LD      A, ' '
            CALL    PRNT
      ;     CALL    NL
      ; CALL GETKY
            IF BUILD_ROMDISK+BUILD_PICOZ80 = 1
              LD    A,(WRKROMBK1)
              HWSELMROM                                                  ; Set the MROM bank back to scanned bank.
            ENDIF
            POP     AF
            POP     BC
            POP     DE
            POP     HL
            RET
            ENDIF


            ;------------------------------------------------------------------------------------------
            ; Function:     _PRTMZF9
            ; Description:  Print MZF directory entry with pagination.
            ;               D = file sequence number.
            ;               On return NZ = user pressed X to exit listing.
            ;------------------------------------------------------------------------------------------
_PRTMZF9:   PUSH    BC
            PUSH    DE
            PUSH    HL
            ;
            IF BUILD_ROMDISK+BUILD_PICOZ80 = 1
              LD    A,(ROMBK1)                                           ; Ensure main MROM is switched in.
              HWSELMROM
            ENDIF
            ;
            LD      A,(SCRNMODE)
            CP      0
            LD      H,47
            JR      Z,PRTMZF09
            LD      H,93
PRTMZF09:   LD      A,(TMPLINECNT)                                       ; Pause if we fill the screen.
            LD      E,A
            INC     E
            CP      H
            JR      NZ,PRTNOPAUSE9
            LD      E, 0
PRTPAUSE9:  CALL    GETKY
            CP      ' '
            JR      Z,PRTNOPAUSE9
            CP      'X'                                                  ; Exit from listing.
            LD      A,001H
            JR      Z,PRTMZF49
            JR      PRTPAUSE9
PRTNOPAUSE9:LD      A,E
            LD      (TMPLINECNT),A
            ;
            LD      A, D                                                 ; Print out file number and increment.
            CALL    PRTHX
            LD      A, '.'                                               ; File type is MACHINE CODE program.
            CALL    PRNT
            LD      DE,NAME                                              ; Print out filename.
            LD      B,FNSIZE                                             ; Maximum size of filename.
_PRTMSG9:   LD      A,(DE)
            INC     DE
            CP      000H
            JR      Z,_PRTMSGE9
            CP      00DH
            JR      Z,_PRTMSGE9
            CALL    PRNT
            DJNZ    _PRTMSG9
            ;
_PRTMSGE9:  LD      HL, (DSPXY)
            ;
            LD      A,L
            CP      20
            LD      A,20
            JR      C, PRTMZF29
            ;
            LD      A,(SCRNMODE)                                         ; 40 Char mode? 2 columns of filenames displayed so NL.
            CP      0
            JR      Z,PRTMZF19
            ;
            LD      A,L                                                  ; 80 Char mode we print 4 columns of filenames.
            CP      40
            LD      A,40
            JR      C, PRTMZF29
            ;
            LD      A,L
            CP      60
            LD      A,60
            JR      C, PRTMZF29
            ;
PRTMZF19:   CALL    NL
            JR      PRTMZF39
PRTMZF29:   LD      L,A
            LD      (DSPXY),HL
PRTMZF39:   XOR     A
PRTMZF49:   OR      A
            PUSH    AF
            IF BUILD_ROMDISK+BUILD_PICOZ80 = 1
              LD    A, (WRKROMBK1)
              HWSELMROM
            ENDIF
            POP     AF
            POP     HL
            POP     DE
            POP     BC
            RET


            ;------------------------------------------------------------------------------------------
            ; Function:     DIRROM9
            ; Description:  ROM directory listing command.
            ;------------------------------------------------------------------------------------------
DIRROM9:   ;DI                                                           ; Disable interrupts as we are switching out the main rom.
            ;
            LD      A,1                                                  ; Account for the title.
            LD      (TMPLINECNT),A
            ;
            LD      DE,MSGRDIRLST                                        ; Print out header.
            LD      HL,PRINTMSG
            CALL    BKSW9to6

            ; D = File sequence number.
            LD      D,0                                                  ; File numbering start.

            ;
            ; Get directory of User ROM (RomDisk).
            ;
            IF BUILD_ROMDISK+BUILD_PICOZ80 = 1
              ; Set ROMBK2 to bank 9 so MROM code (_DIRMROM, _PRTMZF) restores
              ; UROM to this bank on exit. Our return addresses are in bank 9.
              LD    A,ROMBANK9
              LD    (ROMBK2),A
              ;
              LD    A,ROMBANK3
              LD    (WRKROMBK1),A
              HWSELMROM
              CALL  DIRMROM
              ;
              ; Restore ROMBK2 to default.
              LD    A,ROMBANK0
              LD    (ROMBK2),A
            ENDIF
            ;
            ; Scan MROM Banks (16..127) for application files stored in spare banks.
            ;
            LD      B,MROMPAGES                                          ; First set of pages are reserved in MROM bank.
            LD      C,0                                                  ; Block in page.
            ;
DIRNXTPG9:  LD      A,B
            LD      (WRKROMBK1), A
            HWSELMROM                                                    ; Select bank.

            PUSH    BC                                                   ; Preserve bank count/block number.
            PUSH    DE                                                   ; Preserve file numbering.
            LD      A,C
            IF RFSSECTSZ >= 512
              RLCA
            ENDIF
            IF RFSSECTSZ >= 1024
              RLCA
            ENDIF
            LD      B,A
            LD      C,0
            LD      HL,MROMSTART                                         ; Add block offset to get the valid block address.
            ADD     HL,BC
            CALL    ISMZF9
            POP     DE
            POP     BC
            JR      NZ, DIRNOTMZF9
            ;
            CALL    PRTMZF9
            JR      NZ,DIRNXTPGX9
            INC     D                                                    ; Next file sequence number.
            ;
DIRNOTMZF9: INC     C                                                    ; Next block.
            LD      A,C
            CP      MROMSIZE/RFSSECTSZ                                   ; Max blocks per page reached?
            JR      C, DIRNXTPG29
            LD      C,0
            INC     B
DIRNXTPG29: LD      A,B
            CP      080h                                                 ; MROM has 128 banks of 4K, so stop when we reach 128.
            JR      NZ, DIRNXTPG9

DIRNXTPGX9: LD      A,(ROMBK1)
            HWSELMROM                                                    ; Set the MROM bank back to original.
           ;EI                                                           ; No need to block interrupts now as MROM bank restored.
            RET                                                          ; End of scan, return to monitor


            ;------------------------------------------------------------------------------------------
            ; Function:     FINDMZF9
            ; Description:  Find a file in the ROM banks by name or file number.
            ;               HL = pointer to filename or file number string.
            ;                D = file sequence number start.
            ;               On return Z = found, NZ = not found.
            ;               B = bank, C = block where file resides.
            ;------------------------------------------------------------------------------------------
FINDMZF9:   PUSH    DE
            LD      (TMPADR), HL                                         ; Save name of program to load.
            EX      DE, HL                                               ; String needed in DE for conversion.
            LD      HL,0FFFFh                                            ; Tag the filenumber as invalid.
            LD      (TMPCNT), HL
            CALL    ConvertStringToNumber9                               ; See if a file number was given instead of a filename.
            JR      NZ, FINDMZF09                                        ;
            LD      (TMPCNT), HL                                         ; Store filenumber making load by filenumber valid.
            ;
            ; Scan MROM Bank
            ; B = Bank Page
            ; C = Block in page
            ;
FINDMZF09:  POP     DE                                                   ; Get file sequence number in D.
            LD      B,MROMPAGES                                          ; First set of pages are reserved in User ROM bank.
            LD      C,0                                                  ; Block in page.
FINDMZF19:  LD      A,B
            LD      (WRKROMBK1), A
            HWSELMROM                                                    ; Select bank.
FINDMZF29:  PUSH    BC                                                   ; Preserve bank count/block number.
            PUSH    DE                                                   ; Preserve file numbering.
            LD      HL,MROMSTART                                         ; Add block offset to get the valid block.
            LD      A,C
            IF RFSSECTSZ >= 512
              RLCA
            ENDIF
            IF RFSSECTSZ >= 1024
              RLCA
            ENDIF
            LD      B,A
            LD      C,0
            ADD     HL,BC

            CALL    ISMZF9
            POP     DE
            POP     BC
            LD      A,(ROMBK1)
            HWSELMROM                                                    ; Set the MROM bank back to original.
            JR      NZ, FINDMZF49                                        ; Z set if we found an MZF record.
            INC     HL                                                   ; Save address of filename.
            PUSH    HL
          ; CALL    PRTMZF9                                              ; Print out for confirmation.
            LD      HL,(TMPCNT)
            LD      A,H
            CP      0FFh                                                 ; If TMPCNT tagged as 0xFF then we dont have a filenumber so must match filename.
            JR      Z, FINDMZF39
            LD      A,L                                                  ; Check file number, load if match
            CP      D
            JR      NZ, FINDMZF39                                        ; Check name just in case.
            POP     HL
            JR      FINDMZFYES9                                          ; Else the filenumber matches so load the file.

FINDMZF39:  POP     HL
            PUSH    DE
            PUSH    BC
            LD      DE,(TMPADR)                                          ; Original DE put onto stack, original filename into HL
            LD      BC,FNSIZE
            LD      A,(WRKROMBK1)
            HWSELMROM                                                    ; Select correct bank for comparison.
            CALL    CMPSTRING9
            POP     BC
            POP     DE
            JR      Z, FINDMZFYES9
            INC     D                                                    ; Next file sequence number.
FINDMZF49:  INC     C
            LD      A,C
            CP      MROMSIZE/RFSSECTSZ                                   ; Max blocks per page reached?
            JR      C, FINDMZF59
            LD      C,0
            INC     B
FINDMZF59:  LD      A,B
            CP      080h                                                 ; MROM has 128 banks of 4K, so stop when we get to 128.
            JR      NZ, FINDMZF19
            INC     B
            JR      FINDMZFNO9

FINDMZFYES9:                                                             ; Flag set by previous test.
FINDMZFNO9: PUSH    AF
            LD      A,(ROMBK1)
            HWSELMROM                                                    ; Set the MROM bank back to original.
            POP     AF
            RET


            ;------------------------------------------------------------------------------------------
            ; Function:     LOADROMNX9 / LOADROM9
            ; Description:  Load a program from ROM banks.
            ;               LOADROMNX9 = load without auto-execute.
            ;               LOADROM9   = load with auto-execute.
            ;               DE = pointer to filename.
            ;------------------------------------------------------------------------------------------
LOADROMNX9: LD      L,0FFH
            JR      LOADROM19
LOADROM9:   LD      L,000H
LOADROM19: ;DI
            PUSH    HL                                                   ; Preserve execute flag.
            EX      DE,HL                                                ; User ROM expects HL to have the filename pointer.

            ; D = File sequence number.
            LD      D,0                                                  ; File numbering start.
            ;
            IF BUILD_ROMDISK+BUILD_PICOZ80 = 1
              ; Set ROMBK2 to bank 9 so MROM code restores UROM to this bank on exit.
              LD      A,ROMBANK9
              LD      (ROMBK2),A
              ;
              PUSH    HL                                                 ; Save pointer to filename for FINDMZF in Monitor ROM.
              LD      A,ROMBANK3                                         ; Activate the RFS Utilities MROM bank.
              LD      (WRKROMBK1), A
              HWSELMROM
              CALL    MFINDMZF                                           ; Try and find the file in User ROM via MROM utility.
              POP     HL
              JR      Z,MROMLOAD09
              ;
              ; Not found via MROM — restore ROMBK2 to default before trying FINDMZF9.
              LD      A,ROMBANK0
              LD      (ROMBK2),A
            ENDIF
            ;
            CALL    FINDMZF9                                             ; Find the bank and block where the file resides. HL = filename.
            JR      Z, LROMLOAD9
            ;
            JR      LROMNTFND9                                           ; Requested file not found.
            ;
MROMLOAD09: PUSH    BC                                                   ; Preserve bank and block where MZF file found.
            PUSH    AF                                                   ; Preserve 7:6 of A which is upper bank select.
            LD      A,(ROMBK1)                                           ; Page in monitor so we can print a message.
            HWSELMROM
            LD      DE,MSGLOAD+1                                         ; Skip initial file type identifier.
            LD      BC,NAME
            LD      HL,PRINTMSG
            CALL    BKSW9to6
            LD      A,(WRKROMBK1)                                        ; Revert to MROM bank to load the application.
            HWSELMROM
            POP     AF
            POP     BC
            ;
            CALL    MROMLOAD                                             ; Load the file from User ROM via MROM utility.
            ; Restore ROMBK2 to default. LD does not affect flags so Z from MROMLOAD is preserved.
            LD      A,ROMBANK0
            LD      (ROMBK2),A
            JP      Z, LROMLOAD59

LROMNTFND9: POP     HL                                                   ; Dont need execute flag anymore so waste it.
            LD      A,(ROMBK1)
            HWSELMROM
            LD      HL,PRINTMSG
            LD      DE,MSGNOTFND                                         ; Not found
            CALL    BKSW9to6
LOADROMEND9:;EI
            RET

            ;
            ; Load program from RFS Bank 1 (MROM Bank)
            ;
LROMLOAD9:  PUSH    BC
            ;
            PUSH    BC                                                   ; Print Loading <file>
            LD      DE,MSGLOAD+1
            LD      BC,NAME
            LD      HL,PRINTMSG
            CALL    BKSW9to6
            POP     BC
            ;
            LD      A,B
            LD      (WRKROMBK1),A
            HWSELMROM
            ;
            LD      DE, IBUFE                                            ; Copy the header into the work area.
            LD      HL, MROMSTART                                        ; Add block offset to get the valid block.
            LD      A,C
            IF RFSSECTSZ >= 512
              RLCA
            ENDIF
            IF RFSSECTSZ >= 1024
              RLCA
            ENDIF
            LD      B,A
            LD      C,0
            ADD     HL,BC
            LD      BC, MZFHDRSZ
            LDIR

            PUSH    HL
            LD      DE, (DTADR)
            LD      HL, (SIZE)
            LD      BC, RFSSECTSZ - MZFHDRSZ
            SBC     HL, BC
            JR      NC, LROMLOAD49
            LD      HL, (SIZE)
            JR      LROMLOAD49

            ; HL = address in active block to read.
            ;  B = Bank
            ;  C = Block
LROMLOAD29: LD      A, B
            LD      (WRKROMBK1), A
            HWSELMROM

LROMLOAD39: PUSH    BC
            LD      HL, MROMSTART
            LD      A, C
            IF RFSSECTSZ >= 512
              RLCA
            ENDIF
            IF RFSSECTSZ >= 1024
              RLCA
            ENDIF
            LD      B, A
            LD      C, 0
            ADD     HL,BC
            PUSH    HL

            LD      DE, (TMPADR)
            LD      HL, (TMPSIZE)
            LD      BC, RFSSECTSZ
            SBC     HL, BC
            JR      NC, LROMLOAD49
            LD      BC, (TMPSIZE)
            LD      HL, 0
LROMLOAD49: LD      (TMPSIZE), HL                                        ; HL contains remaining amount of bytes to load.
            POP     HL
            ;
            LD      A, B                                                 ; Pre check to ensure BC is not zero.
            OR      C
            JR      Z, LROMLOAD89
            LDIR
            LD      BC, (TMPSIZE)
            LD      A, B                                                 ; Post check to ensure we still have bytes
            OR      C
            JR      Z, LROMLOAD89
            ;
            LD      (TMPADR),DE                                          ; Address we are loading into.
            POP     BC
LROMLOAD69: INC     C
            LD      A, C
            CP      MROMSIZE/RFSSECTSZ                                   ; Max blocks per page reached?
            JR      C, LROMLOAD79
            LD      C, 0
            INC     B
            ;
LROMLOAD79: LD      A, B
            CP      080h                                                 ; MROM has 128 banks of 4K, stop at 128.
            JR      Z, LROMLOAD59
            JR      LROMLOAD29
            ;
LROMLOAD89: POP     BC
LROMLOAD59: POP     HL                                                   ; Retrieve execute flag.
            LD      A,(ROMBK1)
            HWSELMROM                                                    ; Set the MROM bank back to original.
            LD      A,L                                                  ; Autoexecute turned off?
            CP      0FFh
            JP      Z,LROMLOAD99                                         ; Go back to monitor if it has been, else execute.
            ; Signal the command dispatcher (in bank 0) to execute the loaded program.
            ; We cannot JP (HL) directly from bank 9 because the UROM bank would still
            ; be 9 when BASIC starts — the BIOS jump table at E8A0 needs bank 0.
            ; Setting RESULT to 0FEH and returning via BKSWRET restores bank 0 first,
            ; then the command dispatcher sees 0FEH and does JP (HL) from bank 0.
            LD      A,0FEH
            LD      (RESULT),A
            ;JP     LROMLOAD99                                            ; Fall through to RET.
LROMLOAD99: RET


            ; Quick method to load CPM. So long as the filename doesnt change this method will load and boot CPM.
LOADCPM9:   LD      DE,CPMFN489                                          ; Load up the 48K version of CPM
LOADPRGNM9: PUSH    HL
            LD      HL,BUFER
LOADPRGNM19:LD      A,(DE)
            LD      (HL),A
            CP      CR
            JR      Z,LOADPRGNM29
            INC     DE
            INC     HL
            JR      LOADPRGNM19
LOADPRGNM29:POP     HL
            LD      DE,BUFER
            JP      LOADROM9

            ; Quick method to load the basic interpreter. So long as the filename doesnt change this method will load and boot Basic.
LOADBASIC9: LD      DE,BASICFILENM9
            JR      LOADPRGNM9


            ; Quick load program names.
CPMFN489:   DB      "CPM223RFS",        00DH
BASICFILENM9:DB     "BASIC SA-5510RFS", 00DH


            ;------------------------------------------------------------------------------------------
            ; Utility functions (local copies for bank 9, suffixed to avoid label conflicts).
            ; These are duplicated from rfs_utilities.asm because the bank 0 copies are not
            ; accessible when bank 9 is active.
            ;------------------------------------------------------------------------------------------

            ; Compare two strings.
            ;   HL = pointer to string 1, DE = pointer to string 2, BC = max length.
            ;   On exit: Z = match, NZ = no match.
CMPSTRING9: PUSH     HL
            PUSH     DE
CMPSTR19:   LD       A, (DE)
            CP       000h
            JR       Z,  CMPSTR39
            CP       00Dh
            JR       Z,  CMPSTR39
            CPI
            JR       NZ, CMPSTR29
            INC      DE
            JP       PE, CMPSTR19
CMPSTR29:   DEC      HL
            CP       (HL)
CMPSTR49:   POP      DE
            POP      HL
            RET
CMPSTR39:   LD       A, (HL)
            CP       000h
            JR       Z, CMPSTR49
            CP       00Dh
            JR       Z, CMPSTR49
            SCF
            JR       CMPSTR49

            ; Convert character to upper case.
ConvertCharToUCase9:
            CP       'a'
            RET      C
            CP       'z'+1
            RET      NC
            SUB      'a'-'A'
            RET

            ; Convert character to numeric value (0-15).
ConvertCharToNumber9:
            CALL     ConvertCharToUCase9
            CP       '0'
            JR       C,@Bad9
            CP       '9'+1
            JR       C,@OK9
            CP       'A'
            JR       C,@Bad9
            CP       'F'+1
            JR       C,@OK9
@Bad9:      LD       A,0FFh
            OR       A
            RET
@OK9:       SUB      '0'
            CP       00Ah
            JR       C,@Fin9
            SUB      007h
@Fin9:      CP       A
            RET

            ; Is character numeric ('0'-'9')?
IsCharNumeric9:
            CP       '0'
            JR       C,@Not9
            CP       '9'+1
            RET      C
@Not9:      OR       A
            RET

            ; Convert hexadecimal or decimal string to number.
            ;   DE = pointer to string. On exit: Z+HL=number, or NZ=failure.
ConvertStringToNumber9:
            PUSH     BC
            LD       HL,0
            LD       A,(DE)
            CP       '+'
            JR       Z,@Dec9
            CP       '$'
            JR       Z,@Hdec9
            CP       39
            JR       Z,@Char9
            CP       '"'
            JR       Z,@Char9
            CP       '0'
            JR       NZ,@HxNx9
            INC      DE
            LD       A,(DE)
            CALL     ConvertCharToUCase9
            CP       'X'
            JR       NZ,@HxNx9
@Hdec9:     INC      DE
@HxNx9:    LD       A,(DE)
            CP       ')'
            JR       Z,@Succ9
            CP       32+1
            JR       C,@Succ9
            CALL     ConvertCharToNumber9
            JR       NZ,@Fail9
            INC      DE
            ADD      HL,HL
            ADD      HL,HL
            ADD      HL,HL
            ADD      HL,HL
            OR       L
            LD       L,A
            JR       @HxNx9
@Dec9:      INC      DE
@DcNx9:    LD       A,(DE)
            CP       ')'
            JR       Z,@Succ9
            CP       32+1
            JR       C,@Succ9
            CALL     IsCharNumeric9
            JR       NC,@Fail9
            CALL     ConvertCharToNumber9
            JR       NZ,@Fail9
            INC      DE
            PUSH     DE
            LD       B,9
            LD       D,H
            LD       E,L
@DcLp9:    ADD      HL,DE
            DJNZ     @DcLp9
            POP      DE
            ADD      A,L
            LD       L,A
            JR       NC,@DcNx9
            INC      H
            JR       @DcNx9
@Char9:     INC      DE
            LD       A,(DE)
            LD       L,A
            LD       H,0
@Succ9:     POP      BC
            XOR      A
            RET
@Fail9:     POP      BC
            LD       A,0FFh
            OR       A
            RET

            ENDIF

            ;-------------------------------------------------------------------------------
            ; END OF METHODS
            ;-------------------------------------------------------------------------------

            ; RomDisk - Pad to EFFF boundary.
            IF      BUILD_ROMDISK+BUILD_PICOZ80 = 1
              ALIGN 0EFF8h
              ORG   0EFF8h
              DB    0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh
            ENDIF
            ; SFD700 - Pad to 10000H
            IF      BUILD_SFD700 = 1
              ALIGN 10000H
            ENDIF
