;--------------------------------------------------------------------------------------------------------
;-
;- Name:            tzfs.asm
;- Created:         July 2019
;- Author(s):       Philip Smart
;- Description:     Sharp MZ series tzfs (tranZPUter Filing System).
;-                  This assembly language program is a branch from the original RFS written for the
;-                  MZ80A_RFS upgrade board. It is adapted to work within the similar yet different 
;-                  environment of the tranZPUter SW which has a large RAM capacity (512K) and an
;-                  I/O processor in the K64F/ZPU.
;-
;- Credits:         
;- Copyright:       (c) 2018-2023 Philip Smart <philip.smart@net2net.org>
;-
;- History:         May 2020  v1.0 - Branch taken from RFS v2.0 and adapted for the tranZPUter SW.
;-                  July 2020 v1.1 - Not many changes but updated version to v1.1 to coincide with the
;-                                   hardware v1.1 version, thus differentiating between v1.0 board and v1.1.
;-                  July 2020      - Updates to accomodate the v2.1 hardware. Additional commands and fixed a 
;-                                   few bugs like the load from card by name!
;-                  Dec 2020       - Updates to accommodate v1.3 of the tranZPUter SW-700 board where soft
;-                                   CPU's now become possible.
;-                  Jan 2021       - Additional changes to accommodate soft CPU's.
;-                  Mar 2021       - Bug fixes - MZ-700/MZ-80A address differences.
;-                                 - Added optional machine model code on load command to enable 700/800
;-                                   programs to be loaded without changing the MZ800 mode switch.
;-                  Apr 2021       - Added 40/80 Colour Card control. Reorganised to free up space.
;-                  Apr 2021 v1.5  - Updated to add ?RDI/?RDD/?WRI/?WRD/DIR/CD methods to ease conversion of
;-                                   programs from cassette storage to SD storage, first conversion being
;-                                   BASIC SA-5510.
;-                  Jul 2021 v1.6  - Updated the CMT routines to now be configurable so that all MZ series
;-                                   are catered for, ie. read/write 80B machines.
;-                  Feb 2023 v1.7  - TZFS now running on FusionX. Changes to ensure compatibility and
;-                                   addition of new commands, assemble, disassemble, fill, write I/O,
;-                                   read I/O.
;-                  May 2023 v1.8  - Added tape delay compensation command.
;-
;--------------------------------------------------------------------------------------------------------
;- 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/>.
;--------------------------------------------------------------------------------------------------------


            ; Configuration for TZFS variable and service structure memory allocation.
TZVARMEM:   EQU     0EC80H                                               ; Location where TZFS variables will be stored.  
TZVARSIZE:  EQU     00100H                                               ; Size of the TZFS variable area.
TZSVCMEM:   EQU     0ED80H                                               ; Start of a memory structure used to communicate with the K64F I/O processor for services such as disk access.

            ; Bring in additional resources.
            INCLUDE "tzfs_definitions.asm"
            INCLUDE "tzfs_variables.asm"
            INCLUDE "tzfs_mondef.asm"
            INCLUDE "tzfs_svcstruct.asm"


            ;============================================================
            ;
            ; USER ROM BANK - Main TZFS Entry point and common functions.
            ; TZFS BANK 1   -
            ;
            ;============================================================
            ORG     UROMADDR


            ;--------------------------------
            ; Startup code
            ;--------------------------------
TZFS:       NOP                                                          ; Nop is needed for monitor autostart.
            LD      A,(WARMSTART)                                        ; Is this the second time we are called? First time sets the memory map to TZMM_TZFS and enables the RAM based monitor.
            OR      A
            JP      NZ,TZFS_1                                            ; Already called so jump to the monitor component of TZFS.
            ;
            LD      A,TZMM_TZFS                
            LD      (MMCFGVAL),A                                         ; Store the value in a memory variable as we cant read the latch once programmed.
            OUT     (MMCFG),A                                            ; Switch to the TZFS memory mode, SA1510/monitor is now in RAM at 0000H
            ;
            LD      A,1                                                  ; TZMM_BOOT doesnt allow writes to User ROM space, so need to be in mode TZMM_TZFS first.
            LD      (WARMSTART),A                                        ; Set warm start flag so next invocation goes to monitor.
            JP      MROMADDR                                             ; Cold start the RAM based SA1510/monitor which in turn will recall us to warm start.
            

            ALIGN_NOPS UROMBSTBL

            ;------------------------------------------------------------------------------------------
            ; Bank switching code, allows a call to code in another bank.
            ; For TZFS, the area E800-EFFF is locked and the area F000-FFFF is paged as needed.
            ;------------------------------------------------------------------------------------------

            ; Methods to access public functions in paged area F000-FFFF. The memory mode is switched
            ; which means any access to F000-FFFF will be directed to a different portion of the 512K
            ; static RAM - this is coded into the FlashRAM decoder. Once the memory mode is switched a
            ; call is made to the required function and upon return the memory mode is returned to the
            ; previous value. The memory mode is stored on the stack and all registers are preserved for
            ; full re-entrant functionality.
BANKTOBANK_:JMPTOBNK

            ALIGN   TZFSJMPTABLE
            ORG     TZFSJMPTABLE


            ;------------------------------------------------------------------------------------------
            ; External function Jump table.
            ; This table is used by external (to TZFS) programs to invoke functionality as required.
            ; The entry point is fixed, starting at TZFSJMPTABLE incrementing by 3 for each call.
            ;------------------------------------------------------------------------------------------
CMT_RDINF:  JP       _CMT_RDINF                                          ; UROMADDR+80H - Tape/SD intercept handler - Read Header
CMT_RDDATA: JP       _CMT_RDDATA                                         ; UROMADDR+83H - Tape/SD intercept handler - Read Data
CMT_WRINF:  JP       _CMT_WRINF                                          ; UROMADDR+86H - Tape/SD intercept handler - Write Header
CMT_WRDATA: JP       _CMT_WRDATA                                         ; UROMADDR+89H - Tape/SD intercept handler - Write Data
CMT_VERIFY: JP       _CMT_VERIFY                                         ; UROMADDR+8CH - Tape/SD intercept handler - Verify Data
CMT_DIR:    JP       _CMT_DIR                                            ; UROMADDR+8FH - SD card directory listing command.
CMT_CD:     JP       _CMT_CD                                             ; UROMADDR+92H - SD change directory command.
SET_FREQ:   JP       ?SETFREQ                                            ; UROMADDR+95H - Set Frequency command.

            ;------------------------------------------------------------------------------------------
            ; Enhanced function Jump table.
            ; This table is generally used by a banked page to call a function within another banked
            ; page. The name is the same as the original function but prefixed by a ?.
            ; All registers are preserved going to the called function and returning from it.
            ;------------------------------------------------------------------------------------------
?PRINTMSG:  CALLBNK PRINTMSG,    TZMM_TZFS2
?PRINTASCII:CALLBNK PRINTASCII,  TZMM_TZFS2
?PRTFN:     CALLBNK PRTFN,       TZMM_TZFS2
?PRTSTR:    CALLBNK PRTSTR,      TZMM_TZFS2
?HELP:      CALLBNK HELP,        TZMM_TZFS2
?MCORX:     CALLBNK MCORX,       TZMM_TZFS3
?TAPECOMP:  CALLBNK TAPECOMP,    TZMM_TZFS3
?COPYM:     CALLBNK COPYM,       TZMM_TZFS3
?WRITEIO:   CALLBNK WRITEIO,     TZMM_TZFS3
?READIO:    CALLBNK READIO,      TZMM_TZFS3
?DUMPBC:    CALLBNK DUMPBC,      TZMM_TZFS3
?DUMPX:     CALLBNK DUMPX,       TZMM_TZFS3
?DUMP:      CALLBNK DUMP,        TZMM_TZFS3
?FILL:      CALLBNK FILL,        TZMM_TZFS3
?GETMEM:    CALLBNK GETMEM,      TZMM_TZFS
?SETMEM:    CALLBNK SETMEM,      TZMM_TZFS
?DASM:      CALLBNK DASM_MAIN,   TZMM_TZFS4
?ASM:       CALLBNK ASM_MAIN,    TZMM_TZFS4
            IF BUILD_FUSIONX = 0
?SETVMODE:    CALLBNK SETVMODE,  TZMM_TZFS3
?SETVGAMODE:  CALLBNK SETVGAMODE,TZMM_TZFS3
?SETVBORDER:  CALLBNK SETVBORDER,TZMM_TZFS3
            ENDIF   ; BUILD_FUSIONX
?SETFREQ:   CALLBNK SETFREQ,     TZMM_TZFS3
            IF BUILD_FUSIONX = 0
?SETT80:      CALLBNK SETT80,    TZMM_TZFS3
?SETZ80:      CALLBNK SETZ80,    TZMM_TZFS3
?SETZPUEVO:   CALLBNK SETZPUEVO, TZMM_TZFS3
            ENDIF   ; BUILD_FUSIONX
?TIMERTST:  CALLBNK TIMERTST,    TZMM_TZFS3
?PTESTX:    CALLBNK PTESTX,      TZMM_TZFS3
?GETMODEL:  CALLBNK GETMODEL,    TZMM_TZFS3
?PLTST:     CALLBNK PLTST,       TZMM_TZFS3
CNV_ATOS:   CALLBNK CNVSTR_AS,   TZMM_TZFS2                              ; 
?RDITZFS:   CALLBNK RDITZFS,     TZMM_TZFS3
?RDDTZFS:   CALLBNK RDDTZFS,     TZMM_TZFS3
?WRITZFS:   CALLBNK WRITZFS,     TZMM_TZFS3
?WRDTZFS:   CALLBNK WRDTZFS,     TZMM_TZFS3
            IF BUILD_FUSIONX = 0
?SETMZ80K:    CALLBNK SETMZ80K,  TZMM_TZFS3
?SETMZ80C:    CALLBNK SETMZ80C,  TZMM_TZFS3
?SETMZ1200:   CALLBNK SETMZ1200, TZMM_TZFS3
?SETMZ80A:    CALLBNK SETMZ80A,  TZMM_TZFS3
?SETMZ700:    CALLBNK SETMZ700,  TZMM_TZFS3
?SETMZ1500:   CALLBNK SETMZ1500, TZMM_TZFS3
?SETMZ800:    CALLBNK SETMZ800,  TZMM_TZFS3
?SETMZ80B:    CALLBNK SETMZ80B,  TZMM_TZFS3
?SETMZ2000:   CALLBNK SETMZ2000, TZMM_TZFS3
?SETMZ2200:   CALLBNK SETMZ2200, TZMM_TZFS3
?SETMZ2500:   CALLBNK SETMZ2500, TZMM_TZFS3
            ENDIF   ; BUILD_FUSIONX
            ;-----------------------------------------


            ;-----------------------------------------
            ; Initialisation and startup.
            ;-----------------------------------------
            ;
            ;
TZFS_1:     LD      SP,TZSTACK                                           ; Setup our stack.
            XOR     A                                                    ; Clear the variable and stack space.
            LD      B, TZSTACK - HLSAVE                                  ; Clear all the variable space except for the startup variables as a restart shouldnt clear them.
            LD      HL, HLSAVE
TZFS_2:     LD      (HL),A
            INC     HL
            DJNZ    TZFS_2              

            ;-------------------------------------------------------------------------------
            ; START OF TZFS INITIALISATION AND COMMAND ENTRY PROCESSOR FUNCTIONALITY.
            ;-------------------------------------------------------------------------------
            ;
            ; Replacement command processor in place of the ROM MONITOR command processor.
            ;
MONITOR:    LD      A, (SCRNMODE)
            LD      C, A
            BIT     1, A                                                 ; FPGA video enabled?
            JR      Z, MONITOR1
            ;
            IN      A,(CPLDCFG)
            OR      MODE_VIDEO_FPGA                                      ; Set the tranZPUter CPLD hardware to enable FPGA video.
            OUT     (CPLDCFG),A                                 
            ;
MONITOR1:   LD      HL,DSPCTL                                            ; Control register address for the 40/80 Colour Card.
            LD      A, C                                                 ; Recall screen mode.
            BIT     0, A
            JP      NZ, SET80CHAR
            JP      SET40CHAR
            ;
SIGNON:     LD      A,0C4h                                               ; Move cursor left to overwrite part of SA-1510/monitor banner.
            LD      E,004h                                               ; 4 times.
SIGNON1:    CALL    DPCT
            DEC     E
            JR      NZ,SIGNON1
            LD      DE,MSGSON                                            ; Sign on message,
            CALL    ?PRINTMSG
            IN      A,(CPUINFO)                                          ; Check to see if soft CPU's are available.
            AND     CPUMODE_IS_SOFT_MASK
            CP      CPUMODE_IS_SOFT_AVAIL
            JR      NZ,SIGNON2
            IN      A,(CPUSTATUS)                                        ; Check to see if the T80 is running.
            AND     CPUMODE_IS_T80                                       ; T80 running?
            JR      Z,SIGNON2
            IF BUILD_FUSIONX = 0
              LD    DE,MSGSONT80
              CALL  ?PRINTMSG
            ENDIF   ; BUILD_FUSIONX
SIGNON2:    LD      DE,MSGSONEND
            CALL    ?PRINTMSG

            ; Command processor, table based.
            ; A line is input then a comparison made with entries in the table. If a match is found then the bank and function
            ; address are extracted and a call to the function @ given bank made. The commands can be of variable length
            ; but important to not that longer commands using the same letters as shorter commands must appear first in the table.
            ;
ST1X:       CALL    NL                                                   ; Command line monitor extension.
            LD      A,'*'
            CALL    PRNT
            LD      DE,BUFER
            CALL    GETL
            ;
CMDCMP:     LD      HL,CMDTABLE
CMDCMP0:    LD      DE,BUFER+1                                           ; First command byte after the * prompt.
            LD      A,(HL)
            CP      000H
            JR      Z,ST1X                                               ; Skip processing on lines where just CR pressed.
            BIT     7,A                                                  ; Bit 7 set on command properties indicates table end, exit if needed.
            JR      NZ,CMDNOCMP
            LD      C,A                                                  ; Command properties into C
            SET     6,C                                                  ; Assume command match.
            AND     007H                                                 ; Mask out bytes in command mask.
            LD      B,A                                                  ; Number of bytes in command.
            INC     HL
CMDCMP1:    LD      A,(DE)                                               ; Compare all bytes and reset match bit if we find a difference.
            CP      (HL)
            JR      Z, CMDCMP2
            RES     6,C                                                  ; No command match.
CMDCMP2:    INC     DE
            INC     HL
            DJNZ    CMDCMP1
            BIT     6,C                                                  ; Bit 7 is still set then we have a command match.
            JR      NZ,CMDCMP3
            INC     HL
            INC     HL                                                   ; Skip over function address
            JR      CMDCMP0                                              ; Try match next command.
CMDCMP3:    LD      A,(HL)                                               ; Command function address into HL
            INC     HL
            LD      H,(HL)
            LD      L,A
            PUSH    HL
            LD      (TMPADR),DE                                          ; Store the key buffer location where arguments start.
            LD      HL,CMDCMPEND                                         ; Location to return to after function is called.
            EX      (SP),HL                                              ; Swap the return location with the location to call.
            PUSH    HL                                                   ; Put location to call at top of stack.
            RET                                                          ; Pop into PC and run.
            ;
CMDNOCMP:   LD      DE,MSGBADCMD
            CALL    ?PRINTMSG
CMDCMPEND:  JP      ST1X


            ;-------------------------------------------------------------------------------
            ; END OF TZFS INITIALISATION AND COMMAND ENTRY PROCESSOR FUNCTIONALITY.
            ;-------------------------------------------------------------------------------

            ;-------------------------------------------------------------------------------
            ; SERVICE COMMAND METHODS
            ;-------------------------------------------------------------------------------

            ; Method to send a command to the I/O processor and verify it is being acted upon.
            ; THe method, after sending the command, polls the service structure result to see if the I/O processor has updated it. If it doesnt update the result
            ; then after a period of time the command is resent. After a number of retries the command aborts with error. This is needed in case of the I/O processor crashing
            ; we dont want the host to lock up.
            ;
            ; Inputs:
            ;      A = Command.
            ; Outputs:
            ;      A = 0 - Success, command being processed.
            ;      A = 1 - Failure, no contact with I/O processor.
            ;      A = 2 - Failure, no result from I/O processor, it could have crashed or SD card removed!
SVC_CMD:    PUSH    BC
            LD      (TZSVCCMD), A                                        ; Load up the command into the service record.
            LD      A,TZSVC_STATUS_REQUEST
            LD      (TZSVCRESULT),A                                      ; Set the service structure result to REQUEST, if this changes then the K64 is processing.

            LD      BC, TZSVCWAITIORETRIES                               ; Safety in case the IO request wasnt seen by the I/O processor, if we havent seen a response in the service

SVC_CMD1:   PUSH    BC
            LD      A,(TZSVCCMD)
            OUT     (SVCREQ),A                                           ; Make the service request via the service request port.

            LD      BC,0
SVC_CMD2:   LD      A,(TZSVCRESULT)
            CP      TZSVC_STATUS_REQUEST                                 ; I/O processor when it recognises the request sets the status to PROCESSING or gives a result, if this hasnt occurred the the K64F hasnt begun processing.
            JR      NZ, SVC_CMD3
            DEC     BC
            LD      A,B
            OR      C
            JR      NZ, SVC_CMD2
            POP     BC
            DEC     BC
            LD      A,B
            OR      C
            JR      NZ,SVC_CMD1                                          ; Retry sending the I/O command.
            ;
            PUSH    DE
            LD      DE,SVCIOERR
            CALL    ?PRINTMSG
            POP     DE
            POP     BC
            LD      A,1                                                  ; No response, error.
            RET
SVC_CMD3:   POP     BC
            ;
            LD      BC,TZSVCWAITCOUNT                                    ; Number of loops to wait for a response before setting error.
SVC_CMD4:   PUSH    BC
            LD      BC,0
SVC_CMD5:   LD      A,(TZSVCRESULT)
            CP      TZSVC_STATUS_PROCESSING                              ; Wait until the I/O processor sets the result, again timeout in case it locks up.
            JR      NZ, SVC_CMD6
            DEC     BC
            LD      A,B
            OR      C
            JR      NZ,SVC_CMD5
            POP     BC
            DEC     BC
            LD      A,B
            OR      C
            JR      NZ,SVC_CMD4                                          ; Retry polling for result.
            ;
            PUSH    DE
            LD      DE,SVCRESPERR
            CALL    ?PRINTMSG
            POP     DE
            POP     BC
            LD      A,2
            RET
SVC_CMD6:   XOR     A                                                    ; Success.
            POP     BC
            POP     BC
            RET

            ;-------------------------------------------------------------------------------
            ; END OF SERVICE COMMAND METHODS
            ;-------------------------------------------------------------------------------

            ;-------------------------------------------------------------------------------
            ; UTILITIES
            ;-------------------------------------------------------------------------------

            ; Method to read 4 bytes from a buffer pointed to by DE and attempt to convert to a 16bit number. If it fails, print out an error
            ; message and return with C set.
            ;
            ; Input:  DE = Address of digits to conver.
            ; Output: HL = 16 bit number.

READ4HEX:   CALL    HLHEX
            JR      C,READ4HEXERR
            INC     DE
            INC     DE
            INC     DE
            INC     DE
            OR      A                                                    ; Clear carry flag.
            RET
READ4HEXERR:LD      DE,MSGREAD4HEX                                       ; Load up error message, print and exit.
            CALL    ?PRINTMSG
            SCF
            RET

            ;    SPACE PRINT AND DISP ACC
            ;    INPUT:HL=DISP. ADR.
SPHEX:      CALL    PRNTS                                                ; SPACE PRINT
            LD      A,(HL)
            CALL    PRTHX                                                ; DSP OF ACC (ASCII)
            LD      A,(HL)
            RET

            ;    NEW LINE AND PRINT HL REG (ASCII)
NLPHL:      CALL    NL
            CALL    PRTHL
            RET  

HEXIYX:     EX      (SP),IY
            POP     AF
            CALL    HLHEX
            JR      C,HEXIYX2
            JP      (IY)
HEXIYX2:    POP     AF                                                   ; Waste the intermediate caller address
            RET    

            ; Bring in additional resources.
            USE_CMPSTRING:    EQU   1
            USE_SUBSTRING:    EQU   0
            USE_INDEX:        EQU   0
            USE_STRINSERT:    EQU   0
            USE_STRDELETE:    EQU   0
            USE_CONCAT:       EQU   0
            USE_CNVUPPER:     EQU   1
            USE_CNVCHRTONUM:  EQU   1
            USE_ISNUMERIC:    EQU   1
            USE_CNVSTRTONUM:  EQU   1
            ;
            INCLUDE "macros.asm"
            INCLUDE "tzfs_utilities.asm"

            ;-------------------------------------------------------------------------------
            ; END OF UTILITIES
            ;-------------------------------------------------------------------------------


;-------------------------------------------------------------------------------------------
; RAM STORAGE AREA
;-------------------------------------------------------------------------------------------
            ;-------------------------------------------------------------------------------
            ; VARIABLES AND STACK SPACE
            ;-------------------------------------------------------------------------------
            ALIGN      TZVARMEM
            ALIGN_NOPS TZVARMEM + TZVARSIZE
            ;-------------------------------------------------------------------------------
            ; END OF VARIABLES AND STACK SPACE
            ;-------------------------------------------------------------------------------

            ;-------------------------------------------------------------------------------
            ; TZ SERVICE STRUCTURE AND VARIABLES
            ;-------------------------------------------------------------------------------
            ALIGN      TZSVCMEM
            ALIGN_NOPS TZSVCMEM + TZSVCSIZE
            ;-------------------------------------------------------------------------------
            ; END OF TZ SERVICE STRUCTURE AND VARIABLES
            ;-------------------------------------------------------------------------------
;-------------------------------------------------------------------------------------------
; END OF RAM STORAGE AREA
;-------------------------------------------------------------------------------------------


            ORG     BANKRAMADDR

            ;-------------------------------------------------------------------------------
            ; START OF MEMORY CMDLINE TOOLS FUNCTIONALITY
            ;-------------------------------------------------------------------------------

            ; Method to branch execution to a user given address.
            ;
GOTOX:      CALL    HEXIYX
            JP      (HL)


            ; Method to read a byte out of main memory when configured in default TZFS1 mode.
GETMEM:     LD      A,(HL)
            RET
            ; Method to write a byte into main memory when configured in default TZFS1 mode.
SETMEM:     LD      (HL),A
            RET


            ;====================================
            ;
            ; Screen Width and Mode Commands
            ;
            ;====================================

            ; Setup the screen as 40x25 chars.
SET40CHAR:  IN      A,(CPLDINFO)                                         ; Get configuration of hardware.
            BIT     3,A
            JR      Z,SET40_1
            ;
            AND     007H                                                 ; Get the base machine mode, use as the starting mode for the video.
            LD      D, A

            LD      A, C                                                 ; Check to see if a mode override has been set.
            BIT     2, A
            LD      A, VMMODE_VGA_OFF
            JR      Z, SET40_0
            ;
            LD      A, C                                                 ; Recall the stored mode and ready for register update.
            RRCA
            RRCA
            RRCA
            RRCA
            AND     00FH
            LD      D,A
            ;
            LD      A, (SCRNMODE2)                                       ; Get the VGA mode and set.
SET40_0:    OUT     (VMVGAMODE), A
            LD      A, D
            OUT     (VMCTRL),A                                           ; Activate.

            JR      SET40_2
            ;
SET40_1:    XOR     A                                                    ; 40 char mode.
            LD      E,(HL)                                               ; Dummy operation to enable latch write via multivibrator.
            LD      (HL), A
            ;
SET40_2:    XOR     A
            LD      (SPAGE), A                                           ; Allow MZ80A scrolling
            JP      SIGNON
            ;
            ; Setup the screen as 80x25 chars.
            ;
SET80CHAR:  IN      A,(CPLDINFO)                                         ; Get configuration of hardware.
            BIT     3,A
            JR      Z,SET80_1                                            ; No FPGA hardware so try and set set 80 char mode on the assumption the 40/80 Colour Card is installed.
            ;
            AND     007H
            OR      MODE_80CHAR                                          ; Set 80 char flag.
            LD      D, A
            ;
            LD      A, C                                                 ; Check to see if a mode override has been set.
            BIT     2, A
            LD      A, VMMODE_VGA_OFF
            JR      Z, SET80_0

            LD      A, C                                                 ; Recall the stored mode and ready for register update.
            RRCA
            RRCA
            RRCA
            RRCA
            AND     00FH
            OR      MODE_80CHAR                                          ; Set 80 char flag.
            LD      D,A
            ;
            LD      A, (SCRNMODE2)                                       ; Get the VGA mode and set.
SET80_0:    OUT     (VMVGAMODE),A
            LD      A, D
            OR      MODE_80CHAR                                          ; Set 80 char flag.
            OUT     (VMCTRL),A                                           ; Activate.
            LD      A, C                                                 ; Indicate we are using the FPGA video hardware.
            SET     1, A
            LD      (SCRNMODE), A
            JR      SET80_2
            ;
SET80_1:    LD      A, 128                                               ; 80 char mode.
            LD      E,(HL)                                               ; Dummy operation to enable latch write via multivibrator.
            LD      (HL), A
            ;
SET80_2:    LD      A, 0FFH
            LD      (SPAGE), A                                           ; MZ80K Scrolling in 80 column mode for time being.
            JP      SIGNON
            ;

            ; Commands to start the machine in its original mode loading either a 40 or 80 column BIOS as directed.
SETMODE40:  IN      A,(CPLDINFO)                                         ; Get configuration of hardware.
            AND     07H                                                  ; Get model info.
            CP      MODE_MZ80A
            JR      Z, SETMODE40A                                        ; MZ80A hardware so setup MZ80A 40 char mode.
            CP      MODE_MZ700
            JP      Z, SETMODE700                                        ; MZ700 hardware so setup MZ700 40 char mode.
            LD      DE, MSGUNKNHW                                        ; Indicate detected hardware not yet handled.
            CALL    ?PRINTMSG
            RET                                                          ; Return status to caller, 0 = success.

SETMODE80:  IN      A,(CPLDINFO)                                         ; Get configuration of hardware.
            AND     07H                                                  ; Get model info.
            CP      MODE_MZ80A
            JR      Z, SETMODE80A                                        ; MZ80A hardware so setup MZ80A 80 char mode.
            CP      MODE_MZ700
            JP      Z, SETMODE7008                                       ; MZ700 hardware so setup MZ700 80 char mode.
            LD      DE, MSGUNKNHW                                        ; Indicate detected hardware not yet handled.
            CALL    ?PRINTMSG
            RET                                                          ; Return status to caller, 0 = success.

            ; Start the machine as an MZ-80A in 40 char mode.
SETMODE40A: IN      A,(CPLDINFO)                                         ; Get configuration of hardware.
            BIT     3,A
            LD      A,MODE_MZ80A                                         ; Set the tranZPUter CPLD hardware translation to MZ80A mode.
            JR      NZ,SETMODE40_0
            LD      HL,DSPCTL                                            ; Assume the 40/80 card is installed, switch to 40char mode.
            XOR     A
            LD      E,(HL)
            LD      (HL),A
            JR      SETMODE40_2
            ;
SETMODE40_0:LD      A,VMMODE_MZ80A                                       ; Setup the display to 40 char MZ80A mode.
            OUT     (VMCTRL),A                                           ; Activate.
            LD      A, MODE_MZ80A + MODE_VIDEO_FPGA                      ; Set the tranZPUter CPLD hardware translation to MZ80A mode with FPGA video enabled.
SETMODE40_1:OUT     (CPLDCFG),A                                          ;
SETMODE40_2:XOR     A
            LD      (SPAGE), A                                           ; Allow MZ80A scrolling
            LD      A,(SCRNMODE)
            RES     0, A
            LD      (SCRNMODE),A                                         ; Bit 0 = 0 = 40char mode on reset.
            ;
            IN      A,(CPLDINFO)                                         ; Check to see if this is an MZ700, if it is not, setup the correct frequency.
            AND     007H
            CP      MODE_MZ80A
            JR      Z,SETMODE40_3
            LD      A,SYSMODE_MZ80A                                      ; Setup the board to run at 2MHz
            OUT     (SYSCTRL),A                                          ; Activate

SETMODE40_3:LD      A,TZSVC_CMD_LOAD40ABIOS                              ; Request the I/O processor loads the SA1510 40column BIOS into memory.
SETBIOS:    CALL    SVC_CMD                                              ; And make communications wit the I/O processor, returning with the result of load operation.
            OR      A
            JP      Z,MROMADDR
            LD      DE,MSGFAILBIOS
            CALL    ?PRINTMSG
            RET                                                          ; Return status to caller, 0 = success.

            ; Start the machine as an MZ-80A in 80 char mode.
SETMODE80A: IN      A,(CPLDINFO)                                         ; Get configuration of hardware.
            BIT     3,A
            LD      A, MODE_MZ80A                                        ; Set the tranZPUter CPLD hardware translation to MZ80A mode.
            JR      NZ,SETMODE80_0
            LD      HL,DSPCTL                                            ; Assume the 40/80 card is installed, switch to 80char mode.
            LD      A,128
            LD      E,(HL)
            LD      (HL),A
            JR      SETMODE80_2
            ;
SETMODE80_0:LD      A,VMMODE_MZ80A + MODE_80CHAR                         ; Setup the display to 80 char MZ80A mode.
            OUT     (VMCTRL),A                                           ; Activate.
            LD      A, MODE_MZ80A + MODE_VIDEO_FPGA                      ; Set the tranZPUter CPLD hardware translation to MZ80A mode with FPGA video enabled.
SETMODE80_1:OUT     (CPLDCFG),A                                          ;
SETMODE80_2:LD      A, 0FFH
            LD      (SPAGE), A                                           ; MZ80K Scrolling in 80 column mode for time being.
            LD      A,(SCRNMODE)
            SET     0, A                                                 ; Indicate 80 column mode for startup.
            LD      (SCRNMODE),A

            IN      A,(CPLDINFO)                                         ; Check to see if this is an MZ700, if it is not, setup the correct frequency.
            AND     007H
            CP      MODE_MZ80A
            JR      Z,SETMODE80_3
            LD      A,SYSMODE_MZ80A                                      ; Setup the board to run at 2MHz
            OUT     (SYSCTRL),A                                          ; Activate

SETMODE80_3:LD      A,TZSVC_CMD_LOAD80ABIOS                              ; Request the I/O processor loads the SA1510 80column BIOS into memory.
            JR      SETBIOS


            ; Commands to switch into MZ-700 compatible mode. This involves switching the CPLD mode which makes hardware keyboard mapping
            ; and changing the frequency, and also enabling of additional traps to detect and change memory mode which are catered for in 
            ; hardware on v2+ boards.
SETMODE700: IN      A,(CPLDINFO)                                         ; Get configuration of hardware.
            BIT     3,A
            LD      A, MODE_MZ700                                        ; Set the tranZPUter CPLD hardware translation to MZ700 mode.
            JR      Z,SETMODE7_1
            LD      A,VMMODE_MZ700                                       ; Setup the display to 40 char MZ700 mode.
            OUT     (VMCTRL),A                                           ; Activate.
            LD      A, MODE_MZ700 + MODE_VIDEO_FPGA                      ; Set the tranZPUter CPLD hardware translation to MZ700 mode with FPGA video enabled.
SETMODE7_1: OUT     (CPLDCFG),A                                          ;
            XOR     A
            LD      (SPAGE), A                                           ; Allow MZ80A scrolling
            LD      A,(SCRNMODE)
            RES     0, A
            LD      (SCRNMODE),A                                         ; 0 = 40char mode on reset.

            IN      A,(CPLDINFO)                                         ; Check to see if this is an MZ700, if it is not, setup the correct frequency.
            AND     007H
            CP      MODE_MZ700
            JR      Z,SETMODE7_2
            LD      A,SYSMODE_MZ700                                      ; Setup the board to run at 3.54MHz
            OUT     (SYSCTRL),A                                          ; Activate

SETMODE7_2: LD      A,TZSVC_CMD_LOAD700BIOS40                            ; Request the I/O processor loads the MZ700 1Z-013A 40column BIOS into memory.
            JR      SETBIOS

SETMODE7008:IN      A,(CPLDINFO)                                         ; Get configuration of hardware.
            BIT     3,A
            JR      Z,SETMODE7_1                                         ; No hardware so cannot select 80 char mode.
            LD      A,VMMODE_MZ700 + MODE_80CHAR                         ; Setup the display to 80char MZ700 mode.
            OUT     (VMCTRL),A                                           ; Activate.
            LD      A, MODE_MZ700 + MODE_VIDEO_FPGA                      ; Set the tranZPUter CPLD hardware translation to MZ700 mode with FPGA video enabled.
SETMODE78_1:OUT     (CPLDCFG),A                                          ;
            LD      A, 0FFH
            LD      (SPAGE), A                                           ; MZ80K Scrolling in 80 column mode for time being.
            LD      A,(SCRNMODE)
            SET     0, A                                                 ; Indicate 80 column mode for startup.
            LD      (SCRNMODE),A

            IN      A,(CPLDINFO)                                         ; Check to see if this is an MZ700, if it is not, setup the correct frequency.
            AND     007H
            CP      MODE_MZ700
            JR      Z,SETMODE78_2
            LD      A,SYSMODE_MZ700                                      ; Setup the board to run at 3.54MHz
            OUT     (SYSCTRL),A                                          ; Activate

SETMODE78_2:LD      A,TZSVC_CMD_LOAD700BIOS80                            ; Request the I/O processor loads the SA1510 80column BIOS into memory.
            JP      SETBIOS


            ; Command to switch into the Sharp MZ-80B compatible mode. This involves loading the IPL, switching
            ; the frequency to 4MHz and enabling of additional traps to detect and change memory mode.
SETMODE80B: IN      A,(CPLDINFO)                                         ; Get configuration of hardware.
            BIT     3,A
            JP      Z,SETMODE40_1                                        ; Without the video hardware cannot select 80B mode.
            LD      A,VMMODE_MZ80B + MODE_80CHAR                         ; Setup the display to 80char MZ80B mode.
            OUT     (VMCTRL),A                                           ; Activate.
            LD      A, MODE_MZ80B + MODE_VIDEO_FPGA                      ; Set the tranZPUter CPLD hardware translation to MZ80B mode with FPGA video enabled.
SETMODE8B_1:OUT     (CPLDCFG),A                                          ;
            LD      A, 0FFH
            LD      (SPAGE), A                                           ; MZ80K Scrolling in 80 column mode for time being.
            LD      A,(SCRNMODE)
            SET     0,A                                                  ; Indicate 80 column mode for startup.
            LD      (SCRNMODE),A

            IN      A,(CPLDINFO)                                         ; Check to see if this is an MZ700, if it is not, setup the correct frequency.
            AND     007H
            CP      MODE_MZ700
            JR      Z,SETMODE8B_2
            LD      A,SYSMODE_MZ700                                      ; Setup the board to run at 3.54MHz
            OUT     (SYSCTRL),A                                          ; Activate

SETMODE8B_2:LD      A,TZSVC_CMD_LOAD80BIPL                               ; Request the I/O processor loads the IPL and switches frequency.
            JP      SETBIOS

            ; Exit out of TZFS - This entails clearing the DRAM as it wont have been refreshed, switching memory mode and then branching to the monitor start entry point.
            ; This is all done by the I/O processor as we need to be in memory mode 0 where all tranZPUter memory is paged out.
            ;
EXITTZFS:   LD      A,TZSVC_CMD_EXIT                                     ; Request the I/O processor restarts the host in original mode.
            CALL    SVC_CMD                                              
            OR      A
            JP      Z,MROMADDR
            LD      DE,MSGFAILEXIT                                       ; Print message if the I/O processor cant process the command.
            CALL    ?PRINTMSG
            RET                                                          ; Return status to caller, 0 = success.


            ; Method to get the CMT parameters from the command line.
            ; The parameters which should be given are:
            ; XXXXYYYYZZZZ - where XXXX = Start Address, YYYY = End Address, ZZZZ = Execution Address.
            ; If the start, end and execution address parameters are correct, prompt for a filename which will be written into the CMT header.
            ; Output:  Reg C = 0 - Success
            ;                = 1 - Error.
GETCMTPARM: CALL    READ4HEX                                             ; Start address
            JR      C,GETCMT1
            LD      (DTADR),HL                                           ; data adress buffer
            LD      B,H
            LD      C,L
            CALL    READ4HEX                                             ; End address
            JR      C,GETCMT1
            SBC     HL,BC
            LD      (SIZE),HL                                            ; byte size buffer
            CALL    READ4HEX                                             ; Execution address
            JR      C,GETCMT1
            LD      (EXADR),HL                                           ; buffer
            CALL    ?GETMODEL                                            ; Get model if provided.
            LD      (HWMODEL),A
            PUSH    AF
            CALL    NL
            LD      DE,MSGSAVE                                           ; 'FILENAME? '
            CALL    ?PRINTMSG                                            ; Print out the filename.
            LD      DE,BUFER
            CALL    GETL
            LD      HL,BUFER+10
            LD      DE,NAME                                              ; name buffer
            LD      BC,FNSIZE
            LDIR                                                         ; C = 0 means success.
            POP     AF                                                   ; Model data returned in AF.
            SCF
            CCF
            RET 
GETCMT1:    SCF                                                          ; C = 1 means an error occured.
            RET 

    
            ;-------------------------------------------------------------------------------
            ; END OF MEMORY CMDLINE TOOLS FUNCTIONALITY
            ;-------------------------------------------------------------------------------

            ;-------------------------------------------------------------------------------
            ; START OF CMT CONTROLLER FUNCTIONALITY
            ;-------------------------------------------------------------------------------

            ; CMT Utility to Load a program from tape.
            ;
            ; Three entry points:
            ; LOADTAPE = Load the first program shifting to lo memory if required and execute.
            ; LOADTAPENX = Load the first program and return without executing.
            ; LOADTAPECP = Load the first program to address 0x1200 and return.
            ;
LOADTAPECP: LD      A,0FFH
            LD      (CMTAUTOEXEC),A
            JR      LOADTAPE2
LOADTAPENX: LD      A,0FFH
            JR      LOADTAPE1
LOADTAPE:   LD      A,000H
LOADTAPE1:  LD      (CMTAUTOEXEC),A
            XOR     A
LOADTAPE2:  LD      (CMTCOPY),A                                          ; Set cmt copy mode, 0xFF if we are copying.
            LD      A,0FFH                                               ; If called interbank, set a result code in memory to detect success.
            LD      (RESULT),A
            ;
            CALL    ?GETMODEL                                            ; Get machine model option.
            LD      (HWMODEL),A
            ;
            CALL    ?RDITZFS
            JP      C,?ERX2
            LD      DE,MSGLOAD                                           ; 'LOADING '
            LD      BC,NAME
            CALL    ?PRINTMSG
            XOR     A
            LD      (CMTLOLOAD),A

            LD      HL,(DTADR)                                           ; Common code, store load address in case we shift or manipulate loading.
            LD      (DTADRSTORE),HL

            LD      A,(CMTCOPY)                                          ; If were copying we always load at 0x1200
            OR      A
            JR      Z,LOADTAPE3
            LD      HL,01200H
LOADTAPE2A: LD      (DTADR),HL

LOADTAPE3:  LD      HL,(DTADR)                                           ; If were loading and the load address is below 0x1200, shift it to 0x1200 to load then move into correct location.
            LD      A,H
            OR      L
            JR      NZ,LOADTAPE4
            LD      A,0FFh
            LD      (CMTLOLOAD),A
            LD      HL,01200h
            LD      (DTADR),HL
            ;
LOADTAPE4:  CALL    ?RDDTZFS
            ;
LOADTAPEI1: JP      C,?ERX2
            LD      HL,(DTADRSTORE)                                      ; Restore the original load address into the CMT header.
            LD      (DTADR),HL
            LD      A,(CMTCOPY)
            OR      A
            JR      NZ,LOADTAPE6
LOADTAPE5:  LD      A,(CMTAUTOEXEC)                                      ; Get back the auto execute flag.
            OR      A
            JR      NZ,LOADTAPE6                                         ; Dont execute..
            LD      A,(CMTLOLOAD)
            CP      0FFh
            JR      Z,LOADTAPELM                                         ; Execute at low memory?
            LD      BC,00100h
            LD      HL,(EXADR)
            JP      (HL)
LOADTAPELM: LD      A,(MEMSW)                                            ; Perform memory switch, mapping out ROM from $0000 to $C000
            LD      HL,01200h                                            ; Shift the program down to RAM at $0000
            LD      DE,00000h
            LD      BC,(SIZE)
            LDIR
            LD      BC,00100h
            LD      HL,(EXADR)                                           ; Fetch exec address and run.
            JP      (HL)
LOADTAPE6:  LD      DE,MSGCMTDATA
            PUSH    HL                                                   ; Load address as parameter 2.
            LD      HL,(EXADR)
            PUSH    HL                                                   ; Execution address as parameter 1.
            LD      BC,(SIZE)                                            ; Size as BC parameter.
            CALL    ?PRINTMSG
            POP     BC
            POP     BC                                                   ; Waste parameters.
            XOR     A                                                    ; Success.
            LD      (RESULT),A
            RET

            ; Method to save an application stored in memory to a cassette in the CMT. The start, size and execution address are either given in BUFER via the 
            ; command line and the a filename is prompted for and read, or alternatively all the data is passed into the function already set in the CMT header.
            ; The tape is then opened and the header + data are written out.
            ;
SAVECMT:    LD      A,0FFH                                               ; Set SDCOPY to indicate this is a copy command and not a command line save.
            JR      SAVEX1
            ;
            ; Normal entry point, the cmdline contains XXXXYYYYZZZZ where XXXX=start, YYYY=size, ZZZZ=exec addr. A filenname is prompted for and read.
            ; The data is stored in the CMT header prior to writing out the header and data..
            ;
SAVEX:      CALL    GETCMTPARM                                           ; Get the CMT parameters.
            RET     C                                                    ; Exit if an error occurred.

            XOR     A
SAVEX1:     LD      (SDCOPY),A
            OR      A
            JR      NZ,SAVEX1_1                                          ; Dont set the attribute if copying, use the defined value.
            LD      A,OBJCD                                              ; Set attribute: OBJ
            LD      (ATRB),A
            ;
SAVEX1_1:   LD      A,0FFH
            LD      (RESULT),A                                           ; For interbank calls, pass result via a memory variable. Assume failure unless updated.
            ;
            CALL    ?WRITZFS
?ERX1:      JP      C,?ERX2
            ;
           ;LD      A,(SDCOPY)
           ;OR      A
           ;JR      Z,SAVEX2
            LD      DE,(DTADR)
            LD      A,D                                                  ; If copying and address is below 1001H, then data is held at 1200H so update header for write.
            CP      001H
            JR      NC,SAVEX2
            LD      DE,01200H
            LD      (DTADR),DE

SAVEX2:     CALL    ?WRDTZFS
            ;
WRITAPEE1:  JR      C,?ERX1
            LD      DE,MSGSAVEOK                                         ; 'OK!'
            CALL    ?PRINTMSG
            LD      A,0                                                  ; Success.
            LD      (RESULT),A
            RET
?ERX2:      LD      (RESULT),A                                           ; Set break key pressed code or error code.
            CP      002h
            JR      NZ,?ERX3
            RET
?ERX3:      LD      DE,MSGE1                                             ; 'CHECK SUM ER.'
            CALL    ?PRINTMSG
            LD      A,(RESULT)
            RET


            ; Method to verify that a tape write occurred free of error. After a write, the tape is read and compared with the memory that created it.
            ;
VRFYX:      CALL    ?VRFY
            JP      C,?ERX2
            LD      DE,MSGOK                                             ; 'OK!'
            CALL    ?PRINTMSG
            RET

            ; Method to toggle the audible key press sound, ie a beep when a key is pressed.
            ;
SGX:        LD      A,(SWRK)
            RRA
            CCF
            RLA
            LD      (SWRK),A
            RET

            ;-------------------------------------------------------------------------------
            ; END OF CMT CONTROLLER FUNCTIONALITY
            ;-------------------------------------------------------------------------------

            ; The FDC controller uses it's busy/wait signal as a ROM address line input, this
            ; causes a jump in the code dependent on the signal status. It gets around the 2MHz
            ; Z80 not being quick enough to process the signal by polling.
            ;------------ 0xF3CE -----------------------------------------------------------
            ALIGN_NOPS FDCJMP1
            ORG        FDCJMP1
FDCJMPL:    JP       (IX)    
            ;------------ 0xF400 -----------------------------------------------------------


            ;-------------------------------------------------------------------------------
            ; START OF MEMORY TEST FUNCTIONALITY
            ;-------------------------------------------------------------------------------

MEMTEST:    LD      B,240       ; Number of loops
LOOP:       LD      HL,MEMSTART ; Start of checked memory,
            LD      D,0CFh      ; End memory check CF00
LOOP1:      LD      A,000h
            CP      L
            JR      NZ,LOOP1b
            CALL    PRTHL       ; Print HL as 4digit hex.
            LD      A,0C4h      ; Move cursor left.
            LD      E,004h      ; 4 times.
LOOP1a:     CALL    DPCT
            DEC     E
            JR      NZ,LOOP1a
LOOP1b:     INC     HL
            LD      A,H
            CP      D           ; Have we reached end of memory.
            JR      Z,LOOP3     ; Yes, exit.
            LD      A,(HL)      ; Read memory location under test, ie. 0.
            CPL                 ; Subtract, ie. FF - A, ie FF - 0 = FF.
            LD      (HL),A      ; Write it back, ie. FF.
            SUB     (HL)        ; Subtract written memory value from A, ie. should be 0.
            JR      NZ,LOOP2    ; Not zero, we have an error.
            LD      A,(HL)      ; Reread memory location, ie. FF
            CPL                 ; Subtract FF - FF
            LD      (HL),A      ; Write 0
            SUB     (HL)        ; Subtract 0
            JR      Z,LOOP1     ; Loop if the same, ie. 0
LOOP2:      LD      A,16h
            CALL    PRNT        ; Print A
            CALL    PRTHX       ; Print HL as 4 digit hex.
            CALL    PRNTS       ; Print space.
            XOR     A
            LD      (HL),A
            LD      A,(HL)      ; Get into A the failing bits.
            CALL    PRTHX       ; Print A as 2 digit hex.
            CALL    PRNTS       ; Print space.
            LD      A,0FFh      ; Repeat but first load FF into memory
            LD      (HL),A
            LD      A,(HL)
            CALL    PRTHX       ; Print A as 2 digit hex.
            NOP
            JR      LOOP4

LOOP3:      CALL    PRTHL
            LD      DE,OKCHECK
            CALL    MSG          ; Print check message in DE
            LD      A,B          ; Print loop count.
            CALL    PRTHX
            LD      DE,OKMSG
            CALL    MSG          ; Print ok message in DE
            CALL    NL
            DEC     B
            JR      NZ,LOOP
            LD      DE,DONEMSG
            CALL    MSG          ; Print check message in DE
            JP      ST1X

LOOP4:      LD      B,09h
            CALL    PRNTS        ; Print space.
            XOR     A            ; Zero A
            SCF                  ; Set Carry
LOOP5:      PUSH    AF           ; Store A and Flags
            LD      (HL),A       ; Store 0 to bad location.
            LD      A,(HL)       ; Read back
            CALL    PRTHX        ; Print A as 2 digit hex.
            CALL    PRNTS        ; Print space
            POP     AF           ; Get back A (ie. 0 + C)
            RLA                  ; Rotate left A. Bit LSB becomes Carry (ie. 1 first instance), Carry becomes MSB
            DJNZ    LOOP5        ; Loop if not zero, ie. print out all bit locations written and read to memory to locate bad bit.
            XOR     A            ; Zero A, clears flags.
            LD      A,80h
            LD      B,08h
LOOP6:      PUSH    AF           ; Repeat above but AND memory location with original A (ie. 80) 
            LD      C,A          ; Basically walk through all the bits to find which one is stuck.
            LD      (HL),A
            LD      A,(HL)
            AND     C
            NOP
            JR      Z,LOOP8      ; If zero then print out the bit number
            NOP
            NOP
            LD      A,C
            CPL
            LD      (HL),A
            LD      A,(HL)
            AND     C
            JR      NZ,LOOP8     ; As above, if the compliment doesnt yield zero, print out the bit number.
LOOP7:      POP     AF
            RRCA
            NOP
            DJNZ    LOOP6
            JP      ST1X

LOOP8:      CALL    LETNL        ; New line.
            LD      DE,BITMSG    ; BIT message
            CALL    MSG          ; Print message in DE
            LD      A,B
            DEC     A
            CALL    PRTHX        ; Print A as 2 digit hex, ie. BIT number.
            CALL    LETNL        ; New line
            LD      DE,BANKMSG   ; BANK message
            CALL    MSG          ; Print message in DE
            LD      A,H
            CP      50h          ; 'P'
            JR      NC,LOOP9     ; Work out bank number, 1, 2 or 3.
            LD      A,01h
            JR      LOOP11

LOOP9:      CP      90h
            JR      NC,LOOP10
            LD      A,02h
            JR      LOOP11

LOOP10:     LD      A,03h
LOOP11:     CALL    PRTHX        ; Print A as 2 digit hex, ie. BANK number.
            JR      LOOP7

;DLY1S:      PUSH    AF
;            PUSH    BC
;            LD      C,10
;L0324:      CALL    DLY12
;            DEC     C
;            JR      NZ,L0324
;            POP     BC
;            POP     AF
;            RET
            
            ;-------------------------------------------------------------------------------
            ; END OF MEMORY TEST FUNCTIONALITY
            ;-------------------------------------------------------------------------------



            ; Method to print out an SDC directory entry name along with an incremental file number. The file number can be
            ; used as a quick reference to a file rather than the filename.
            ;
            ; Input: HL = Address of filename.
            ;         D = File number.
            ;         A = File type.
            ;
PRTDIR:     PUSH    BC
            PUSH    DE
            PUSH    HL
            LD      C,A                                                  ; Preserve the file type.
            ;
            LD      A,(SCRNMODE)
            BIT     0, A
            LD      H,47
            JR      Z,PRTDIR0
            LD      H,93
PRTDIR0:    LD      A,(TMPLINECNT)                                       ; Pause if we fill the screen.
            LD      E,A
            INC     E
            CP      H
            JR      NZ,PRTNOWAIT
            LD      E, 0
PRTDIRWAIT: CALL    GETKY
            CP      ' '
            JR      Z,PRTNOWAIT
            CP      'X'                                                  ; Exit from listing.
            LD      A,001H
            JR      Z,PRTDIR4
            JR      PRTDIRWAIT
PRTNOWAIT:  LD      A,E
            LD      (TMPLINECNT),A
            ;
            LD      A, D                                                 ; Print out file number and increment.
            CALL    PRTHX
            LD      A,C
            CP      OBJCD
            LD      A, '.'                                               ; File type is MACHINE CODE program.
            JR      Z,PRTDIR0A
            LD      A,C
            CP      BTX1CD
            LD      A,'-'                                                ; File type is BASIC.
            JR      Z,PRTDIR0A
            LD      A,C
            CP      BTX2CD
            LD      A,'_'                                                ; File type is BASIC.
            JR      Z,PRTDIR0A
            LD      A,'+'
PRTDIR0A:   CALL    PRNT
            POP     DE
            PUSH    DE                                                   ; Get pointer to the file name and print.

            CALL    ?PRTFN                                               ; Print out the filename.
            ;
            LD      HL, (DSPXY)
            ;
            LD      A,L
            CP      20
            LD      A,20
            JR      C, PRTDIR2
            ;
            LD      A,(SCRNMODE)                                         ; 40 Char mode? 2 columns of filenames displayed so NL.
            BIT     0,A
            JR      Z,PRTDIR1
            ;
            LD      A,L                                                  ; 80 Char mode we print 4 columns of filenames.
            CP      40
            LD      A,40
            JR      C, PRTDIR2
            ;
            LD      A,L
            CP      60
            LD      A,60
            JR      C, PRTDIR2
            ;
PRTDIR1:    CALL    NL
            JR      PRTDIR3
PRTDIR2:    LD      L,A
            LD      (DSPXY),HL
PRTDIR3:    XOR     A
PRTDIR4:    OR      A
            POP     HL
            POP     DE
            POP     BC
            RET



            ; Method to request a sector full of directory entries from the I/O processor.
            ;
            ; Inputs:
            ;      A = Director Sector number to request (set of directory entries in 512byte blocks).
            ; Outputs:
            ;      A = 0   - success, directory sector filled.
            ;      A = 255 - I/O Error.
            ;      A > 1   - Result from I/O processor, which is normally the error code.
            ;
SVC_GETDIR: LD      (TZSVCDIRSEC),A                                      ; Save the sector number into the service structure.
            ;
            OR      A                                                    ; Sector is 0 then setup for initial read.
            LD      A, TZSVC_CMD_READDIR                                 ; Readdir command opens the directory. The default directory and wildcard have either been placed in the
            JR      Z,SVC_GETD1                                          ; buffer by earlier commands or will be defaulted by the I/O processor.
            LD      A, TZSVC_CMD_NEXTDIR                                 ; Request the next directory sector. The I/O processor either gets the next block or uses the TZSVCDIRSEC value.
SVC_GETD1:  LD      (TZSVCCMD), A                                        ; Load up the command into the service record.
            CALL    SVC_CMD                                              ; And make communications wit the I/O processor, returning with the required record.
            OR      A
            LD      A,255                                                ; Report I/O error as 255.
            RET     NZ
            ;
            LD      A,(TZSVCRESULT)
            RET                                                          ; Return status to caller, 0 = success.

            ; Method to get an SD Directory entry.
            ; The I/O processor communications structure uses a 512 byte sector to pass SD data. A sector is cached and each call evaluates if the required request is in cache, if it is not,
            ; a new sector is read.
            ;
            ; Input:  D  = Directory entry number to retrieve.
            ; Output: HL = Address of directory entry.
            ;         A  = 0, no errors, A > 1 error.
GETSDDIRENT:PUSH    BC
            PUSH    DE;
            ;
            LD      A,D
            SRL     A
            SRL     A
            SRL     A
            SRL     A                                                    ; Divide by 16 to get sector number.
            LD      C,A
            LD      A,(TZSVCDIRSEC)                                      ; Do we have this sector in the buffer? If we do, use it.
            CP      C
            JR      Z,GETDIRSD0
            LD      A,TZSVC_FTYPE_MZF                                    ; Setup to filter on MZF type files.
            LD      (TZSVC_FILE_TYPE),A 
            LD      A,C
            LD      (TZSVCDIRSEC), A                                     ; Store the directory sector we need.
            ;
            CALL    SVC_GETDIR                                           ; Read a sector full of directory entries..
            ;
            OR      A
            JR      NZ,DIRSDERR
            ;
GETDIRSD0:  POP     DE
            PUSH    DE
            LD      A,D                                                  ; Retrieve the directory entry number required.
            AND     00FH
            LD      HL,TZSVCSECTOR
            JR      Z,GETDIRSD2
            LD      B,A
            LD      DE,TZSVCDIR_ENTSZ                                    ; Directory entry size
GETDIRSD1:  ADD     HL,DE                                                ; Directory entry address into HL.
            DJNZ    GETDIRSD1
GETDIRSD2:  POP     DE
            POP     BC
            LD      A,0
GETDIRSD3:  OR      A
            RET
            ;
DIRSDERR:   EX      DE,HL                                                ; Print error message, show HL as it contains sector number where error occurred.
            PUSH    HL
            POP     BC                                                   ; HL to BC as the call requires the value to be displayed in BC.
            LD      DE,MSGSDRERR
            CALL    ?PRINTMSG                                            ; Print out the filename.
            POP     DE
            POP     BC
            LD      A,1
            JR      GETDIRSD3

       ;PUSH HL
       ;POP BC
       ;LD DE, TESTMSG
       ;CALL ?PRINTMSG
       ;PUSH DE
       ;POP BC
       ;LD DE, TESTMSG2
       ;CALL ?PRINTMSG

            ; Method to set the file search wildcard prior to requesting a directory listing. The I/O processor applies this filter only returning directories
            ; which match the wildcard, ie. A* returns directories starting A...
            ;
            ; Inputs:
            ;     DE = Pointer to BUFER start of wildcard.
            ;
            ; HL and B are not preserved.

SETWILDCARD:LD      HL, TZSVCWILDC                                       ; Location of directory name in the service record.
            LD      B, TZSVCWILDSZ-1
            CALL    GETSTRING                                            ; Copy the string into the service record.
            RET

            ; Method to list the directory of the SD Card.
            ;
            ; This method creates a unique sequenced number starting at 0 and attaches to each successive valid directory entry
            ; The file number and file name are then printed out in tabular format. The file number can be used in Load/Save commands
            ; instead of the filename.
            ;
            ; Inputs:
            ;     DE = Pointer to BUFER start of wildcard if present.
            ;
DIRSDCARD:  CALL    SETWILDCARD
            LD      A,1                                                  ; Setup screen for printing, account for the title line. TMPLINECNT is used for page pause.
            LD      (TMPLINECNT),A
            LD      A,0FFH
            LD      (TZSVCDIRSEC),A                                      ; Reset the sector buffer in memory indicator, using 0xFF will force a reread..
            ;
DIRSD0:     LD      D,0                                                  ; Directory entry number
            LD      B,0
DIRSD1:     CALL    GETSDDIRENT                                          ; Get SD Directory entry details for directory entry number stored in D.
            RET     NZ
DIRSD2:     LD      A,(HL)
            INC     HL
            OR      A
            RET     Z
            CALL    PRTDIR                                               ; Valid entry so print directory number and name pointed to by HL.
            JR      NZ,DIRSD4
DIRSD3:     INC     D                                                    ; Onto next directory entry number.
            DJNZ    DIRSD1
DIRSD4:     RET
            ;

            ; Quick method to load the basic interpreter. So long as the filename doesnt change this method will load and boot Basic.
LOADBASIC:  
            IF BUILD_MZ80A > 0
              LD    DE,BASICFNM80A
            ENDIF
            IF BUILD_MZ700 > 0
              LD    DE,BASICFNM700
            ENDIF
            IF BUILD_MZ1500 > 0
              LD    DE,BASICFNM1500
            ENDIF
            JR      LOADSDCARD

            ; Quick method to load CPM. So long as the filename doesnt change this method will load and boot CPM.
LOADCPM:    
            IF BUILD_MZ80A > 0
              LD    DE,CPMFNAME80A
            ENDIF
            IF BUILD_MZ700 > 0
              LD    DE,CPMFNAME700
            ENDIF
            IF BUILD_MZ1500 > 0
              LD    DE,CPMFNAME1500
            ENDIF
            JR      LOADSDCARD

            ; Entry point when copying the SD file. Setup flags to indicate copying to effect any special processing.
            ; The idea is to load the file into memory, dont execute and pass back the parameters within the CMT header.
            ;
LOADSDCP:   LD      A,0FFH
            LD      (SDAUTOEXEC),A
            JR      LOADSD2

            ; Method/entry point to load a file header only. This method is for compatibility with the Sharp ?RDI/?RDD methods.
            ;
LOADSDINF:  LD      A,TZSVC_FTYPE_MZFHDR
            JR      LOADSD2C

            ; Method/entry point to load a file with header already preset. This method is for compatibility with the Sharp ?RDI/?RDD methods.
            ;
LOADSDDATA: LD      A,0FEH                                               ; Mark dont execute and dont print details.
            LD      (SDAUTOEXEC),A
            XOR     A
            LD      (SDCOPY),A   
            LD      A,TZSVC_FTYPE_MZF                                    ; Full file load.
            LD      (TZSVC_FILE_TYPE),A
            LD      HL,(DTADR)                                           ; Load address comes from the header as caller may have changed it, ie. BASIC.
            LD      (TZSVC_LOADADDR),HL
            LD      A,0FFH                                               ; Reset result code ready for new result.
            LD      (RESULT),A
            JR      LOADSD3A                                             ; Load program defined in CMT header.

            ; Load a program from the SD Card into RAM and/or execute it.
            ;
            ; DE points to a number or filename to load.
LOADSDCARDX:LD      A,0FFH
            JR      LOADSD1

LOADSDCARD: XOR     A
LOADSD1:    LD      (SDAUTOEXEC),A
            XOR     A                                                    ; Clear copying flag.
LOADSD2:    LD      (SDCOPY),A   
            LD      HL,0FFFFH                                            ; Setup the load address to 0xFFFF = load address definded in file MZF header.
LOADSD2D:   LD      (TZSVC_LOADADDR),HL
            ;
            LD      A,TZSVC_FTYPE_MZF                                    ; Default to full file load.
LOADSD2C:   LD      (TZSVC_FILE_TYPE),A
            ;
            PUSH    DE
            LD      A,0FFH                                               ; For interbank calls, save result in a memory variable.
            LD      (RESULT),A
            LD      (TZSVC_FILE_NO), A 
            CALL    _2HEX
            JR      C, LOADSD2A                                          ; 
            LD      (TZSVC_FILE_NO),A                                    ; A file number was found so store it in the service structure.
LOADSD2A:   CALL    ?GETMODEL                                            ; Get machine model option.
            LD      (HWMODEL),A
            CP      008H                                                 ; Original mainboard memory is target?
            LD      A,000H                                               ; tranZPUter.
            JR      C,LOADSD2B                                           ; MZ-80K .. MZ-2000 modes on tranZPUter.
            INC     A                                                    ; Mainboard mode.
LOADSD2B:   LD      (TZSVC_MEM_TARGET), A
            POP     HL
            LD      A,(TZSVC_FILE_NO)                                    ; Test to see if a file number was found, if none then a filename was given, so copy.
            CP      0FFH
            JR      NZ,LOADSD3A
LOADSD3:    LD      DE,TZSVC_FILENAME
            LD      BC,TZSVCFILESZ
            LDIR                                                         ; Copy in the MZF filename.
            ;
LOADSD3A:   LD      A,TZSVC_CMD_LOADFILE
            LD      (TZSVCCMD), A                                        ; Load up the command into the service record.
            CALL    SVC_CMD                                              ; And make communications with the I/O processor, returning with the required record.
            OR      A
            JR      Z, LOADSD4
            LD      A,255                                                ; Report I/O error as 255.
            JP      LOADSDX2

LOADSD4:    LD      A,(TZSVCRESULT)
            OR      A
            JR      NZ, LOADSD4A
            LD      A,(TZSVC_FILE_TYPE)                                  ; Check to see if we are making a full file load, if header only then return.
            CP      TZSVC_FTYPE_MZF
            JR      Z, LOADSD14
            JR      LOADSDX

LOADSD4A:   LD      DE,MSGNOTFND
            CALL    ?PRINTMSG                                            ; Print message that file wasnt found.
            LD      A,1
            JR      LOADSDX2

            ; The file has been found and loaded into memory by the I/O processor.
            LD      DE,MSGLOAD+1                                         ; Skip initial CR.
            LD      BC,NAME
            CALL    ?PRINTMSG                                            ; Print out the filename.

LOADSD14:   LD      A,(SDCOPY)                                           ; Copy mode?
            OR      A
            JR      NZ,LOADSD18
            LD      A,(SDAUTOEXEC)                                       ; Autoexecute turned off?
            CP      0FFh
            JP      Z,LOADSD15                                           ; Go back to monitor if it has been turned off, else execute.
            CP      0FEH                                                 ; Extension for ?RDD - if set to 0FEH then dont print information just return.
            JR      Z,LOADSDX       
            ;
            LD      A,(TZSVCSECTOR+TZFS_ATRB)                            ; Fetch the CMT data from the service sector.
            LD      HL,(TZSVCSECTOR+TZFS_EXADR)                          ; Save the execution address as it may not be in scope later on.
            LD      DE,(TZSVCSECTOR+TZFS_DTADR)                          ; Save the data address, used to check if it was a low memory load.
            ;
            CP      OBJCD                                                ; Standard binary file?
            JR      Z,LOADSD14A
            CP      TZOBJCD0                                             ; TZFS binary file for a particular bank?
            JR      C,LOADSD17

LOADSD14A:  LD      A,(TZSVC_MEM_TARGET)                                 ; Are we loading/executing in original host memory?
            OR      A
            JR      NZ,ORIPRGBOOT                                        ; Yes, then need special bootstrap.      
            ;
            IN      A,(CPLDINFO)                                         ; Check hardware, if not MZ700/MZ800 skip memory reconfig.
            AND     07H
            CP      MODE_MZ700
            JR      Z, LOADSD14B                                         ; 700 mode we just check if the lower bank needs paging in.
            CP      MODE_MZ800
            JR      NZ, LOADSD14C
            ;
            ; DE = Load Address
LOADSD14B:  LD      A,010H                                               ; Load address below 1000H? If so, page out monitor ROM.
            CP      D
            JR      C,LOADSD14C
            JR      NZ,LOADSD14C
            OUT     (MMIO0), A                                           ; Program loaded below 1000H so page in memory.
            ;
LOADSD14C:  LD      A,(HWMODEL)                                          ; Get hardware model the loaded program should execute under.
            CP      MODE_MZ800                                           ; Only interested in the MZ-800 at the moment, K to 700 models dont need special settings.
            JR      Z,LOADSD14D
            JR      LOADSD14E
            ;
LOADSD14D:  XOR     A                                            
            OUT     (GDCMD),A                                            ; Set MZ 800 mode
            CALL    ?PLTST                                               ; Set the palette.
            ;
            ; HL = Execution address.
LOADSD14E:  XOR     A
            JP      (HL)                                                 ; Execution address.

LOADSD15:   LD      DE,MSGCMTDATA                                        ; Indicate where the program was loaded and the execution address.
            LD      HL,(TZSVCSECTOR+TZFS_DTADR)
            PUSH    HL
            LD      HL,(TZSVCSECTOR+TZFS_EXADR)
            PUSH    HL
            LD      BC,(TZSVCSECTOR+TZFS_SIZE)
LOADSD16:   CALL    ?PRINTMSG                                            ; Print out the filename.
            POP     BC
            POP     BC                                                   ; Remove parameters off stack.
LOADSDX:    LD      A,0                                                  ; Non error exit.
            ;
LOADSDX2:   LD      (RESULT),A
            RET
            ;
LOADSD17:   LD      DE,MSGNOTBIN
            CALL    ?PRINTMSG                                            ; Print out the filename.
            LD      A,1
            JR      LOADSDX2
            ;
LOADSD18:   XOR     A
            JR      LOADSDX2

            ; Code to bootstrap a program residing in the host memory.
            ; It is assumed, running in TZFS, that the memory map is of TZFS and actions taken accordingly.
ORIPRGBOOT: LD      A,(TMPCNT)                                           ; Get requested mode before it goes out of scope and store.
            LD      C,A
            ;
            LD      A,TZMM_HOSTACCESS                                    ; Page in host memory so we can read the execution address and run the program.
            OUT     (MMCFG),A
            EXX                                                          ; Execution and load address are stored in HL/DE from the tape header.
            LD      DE,PRGBOOTJMP                                        ; Move the relocatable code into the safe area, in this case the upper attribute RAM.
            LD      HL,ORIBOOTREL
            LD      BC,ORIBOOTRELEND - ORIBOOTREL
            LDIR
            EXX
            JP      PRGBOOTJMP

            ; Relocatable code which operates in a safe area within original host memory. This code then switches memory mode
            ; and executes the stored code.
ORIBOOTREL: LD      SP,PRGBOOTJMP + (ORIBOOTRELEND - ORIBOOTREL)         ; Relative as this code block is moved.
            ;
            LD      A,TZMM_ORIG                                          ; Switch to original host mode.
            OUT     (MMCFG),A
            OUT     (MMIO4),A

            ; Determine if this program is loaded below 1200h? We cant initialise monitor variables for this case ; as it may corrupt the program.
            LD      A,D
            CP      011H
            JR      C,ORIBOOT1                                           ; Some programs load data in the tape header or scratch area so need to work on 1000H or below.
            ;
            PUSH    HL                                                   ; The loaded program may call or reference monitor api procedures and the monitor scratch area
            LD      HL,SWPW                                              ; state will be unknown, we therefore need to zero it to ensure correct loaded program execution.
            LD      B,BUFER - SWPW
            XOR     A
ORIBOOT0:   LD      (HL), A
            INC     HL
            DJNZ    ORIBOOT0
            LD      A,004H
            LD      (TEMPW),A                                            ; Set the sound tempo to default.
            ;
            LD      A,24                                                 ; Set the display coordinates to end of screen to not corrupt existing data.
            LD      (DSPXY+1),A
            POP     HL
            ;
ORIBOOT1:   IN      A,(CPLDINFO)                                         ; Check hardware, if not MZ700/MZ800 skip memory reconfig.
            AND     07H
            CP      MODE_MZ700
            JR      Z, ORIBOOT3                                          ; 700 mode we just check if the lower bank needs paging in.
            CP      MODE_MZ800
            JR      NZ, ORIBOOT4
            ;
ORIBOOT2:   LD      A,C                                                  ; Check user requested mode, original or alternative (ie. MZ-700 mode on an MZ-800).
            CP      09H
            JR      Z,ORIBOOT3
            ;
            XOR     A                                                    ; Set the graphics display gatearray command register to enable 800 mode.
            OUT     (GDCMD),A 
           ;OUT     (MMIO4),A

            ; Set the MZ-800 palette.
            PUSH    HL                                           
            LD      BC,05F0H                                             ; C=port (Pallet Write), B=count
            LD      HL,PRGBOOTJMP + (ORIPLTDT - ORIBOOTREL)              ; Data
            OTIR                                                                          
            XOR     A                                                                     
            LD      BC,06CFH                                             ; Border Black
            OUT     (C),A                                                ; Send to port.
            POP     HL                                           
            ;
ORIBOOT3:   LD      A,D                                                  ; Load address below 1000H? If so, page out monitor ROM.
            CP      011H
            JR      NC, ORIBOOT4
            OUT     (MMIO0), A                                           ; Page in lower DRAM as the program executes from below 0x1000.
            ;
ORIBOOT4:   JP      (HL)                                                 ; Execute program.

            ; Initialization table for Palette
            ;
ORIPLTDT:   DB      0,10H,20H,30H,40H    
            ; Temporary stack.
            DS      32
ORISTACK    EQU     $
            ;
ORIBOOTRELEND:

            ; The FDC controller uses it's busy/wait signal as a ROM address line input, this
            ; causes a jump in the code dependent on the signal status. It gets around the 2MHz
            ; Z80 not being quick enough to process the signal by polling.
            ;------------ 0xF7C0 -----------------------------------------------------------
            ALIGN_NOPS FDCJMP2
            ORG        FDCJMP2
FDCJMPH:    JP       (IY)    
            ;------------ 0xF800 -----------------------------------------------------------


            ; Method to erase a file on the SD. Details of the file are passed to the I/O processor and if the file is found
            ; it is deleted from the SD.
            ; Input:  DE = String containing filenumber or filename to erase.
            ; Output:  A = 0 Success, 1 = Fail.
ERASESD:    PUSH    DE
            LD      A,0FFh                                               ; Tag the filenumber as invalid.
            LD      (TZSVC_FILE_NO), A 
            CALL    _2HEX
            JR      C, ERASESD1                                          ; 
            LD      (TZSVC_FILE_NO),A                                    ; A file number was found so store it in the service structure.
ERASESD1:   POP     HL
            LD      A,(TZSVC_FILE_NO)                                    ; Test to see if a file number was found, if one wasnt then a filename was given, so copy.
            CP      0FFH
            JR      NZ,ERASESD2
            LD      DE,TZSVC_FILENAME
            LD      BC,TZSVCFILESZ
            LDIR                                                         ; Copy in the MZF filename.
ERASESD2:   LD      A,TZSVC_FTYPE_MZF                                    ; Set to MZF type files.
            LD      (TZSVC_FILE_TYPE),A
            LD      A,TZSVC_CMD_ERASEFILE
            LD      (TZSVCCMD), A                                        ; Load up the command into the service record.
            CALL    SVC_CMD                                              ; And make communications wit the I/O processor, returning with the required record.
            OR      A
            JR      Z, ERASESD3
            LD      A,255                                                ; Report I/O error as 255.
            RET
ERASESD3:   LD      A,(TZSVC_FILE_NO)                                    ; Get the file number for the message output.
            LD      C,A
            LD      B,0
            LD      A,(TZSVCRESULT)
            OR      A
            JR      Z, ERASESD4
            ;
            LD      DE,MSGERAFAIL                                        ; Fail, print out message.
            CALL    ?PRINTMSG                                             ; Print out the filename.
            LD      A,1
            RET
ERASESD4:   LD      DE,MSGERASEDIR
            CALL    ?PRINTMSG                                             ; Print out the filename.
            LD      A,0                                                  ; Success.
            RET


            ; Setup for saving an application to SD Card but using the CMT header. Also set the copy flag because the values in the header
            ; may not reflect where the image is stored (ie. CTM LOAD=0x0000 -> data is at 0x1200).
            ;
SAVESDCARDX:LD      A,0FFH    
            JR      SAVESD1

            ; Method to save a block of memory to the SD card as a program.
            ; The parameters which should be given are:
            ; XXXXYYYYZZZZ - where XXXX = Start Address, YYYY = End Address, ZZZZ = Execution Address.
            ; Prompt for a filename which will be written into the CMT header.
            ; All the values are stored in the CMT header and copied as needed into the SD file.
            ;
SAVESDCARD: CALL    GETCMTPARM                                           ; Get the CMT parameters.
            RET     C                                                    ; Exit if an error occurred.
            ;
            LD      A,OBJCD                                              ; Set attribute: OBJ
            LD      (ATRB),A
            ;
SAVESDDATA: XOR     A                                                    ; Disable the copy flag.
SAVESD1:    LD      (SDCOPY),A
            LD      A,0FFH                                               ; Interbank calls, pass result via a memory variable. Assume failure unless updated.
            LD      (RESULT),A

            ; Save the file by making a service call to the I/O processor, it will allocate a filename on the SD, read the tranZPUter memory directly based on the values in the
            ; service record.
SAVESD2:    LD      A,TZSVC_FTYPE_MZF                                    ; Set to MZF type files.
            LD      (TZSVC_FILE_TYPE),A
            LD      A,TZSVC_CMD_SAVEFILE
            LD      (TZSVCCMD), A                                        ; Load up the command into the service record.
            CALL    SVC_CMD                                              ; And make communications with the I/O processor, returning with the required record.
            OR      A
            JR      Z, SAVESD3
            LD      A,255                                                ; Report I/O error as 255.
            RET
SAVESD3:    LD      A,(TZSVC_FILE_NO)                                    ; Get the file number for the message output.
            LD      C,A
            LD      B,0
            LD      A,(TZSVCRESULT)
            OR      A
            JR      Z, SAVESD4
            ;
            LD      DE,MSGSVFAIL                                         ; Fail, print out message.
            CALL    ?PRINTMSG                                             ; Print out the filename.
            LD      A,1
            JR      SAVESD5
SAVESD4:    LD      A,0                                                  ; Success.
SAVESD5:    LD      (RESULT),A
            RET

            ; Method to change the directory on the SD card where files are loaded and saved into. This involves getting a name
            ; and storing it in the service command structure for the I/O processor to use when searching for a file to read or saving a file.
            ; If the cache is in operation it is flushed and reloaded, any errors are reported and the user has to correct the error before issuing further
            ; SD commands.
            ;
            ; Inputs:
            ;     DE = Pointer into BUFER where the string commences. Skip whitespace and copy upto the end marker 0x0D.
            ;
CHGSDDIR:   LD      HL, TZSVC_DIRNAME                                    ; Location of directory name in the service record.
            LD      B,TZSVCDIRSZ-1                                       ; Ensure we dont overflow the buffer.
            CALL    GETSTRING
            ;
            LD      A,TZSVC_FTYPE_MZF                                    ; Setup to filter on MZF type files.
            LD      (TZSVC_FILE_TYPE),A 
            LD      A,TZSVC_CMD_CHANGEDIR                                ; Inform I/O processor that a directory change has taken place, allows it to cache the new dir.
            LD      (TZSVCCMD), A                                        ; Load up the command into the service record.
            CALL    SVC_CMD                                              ; And make communications wit the I/O processor, returning with the required record.
            OR      A
            JR      Z, CHGDIR1
            LD      A,255                                                ; Report I/O error as 255.
            RET
CHGDIR1:    LD      A,(TZSVCRESULT)
            OR      A
            JR      Z, CHGDIR2                                           ; No errors.
            ;
            LD      DE,MSGCDFAIL                                         ; Fail, print out message.
            CALL    ?PRINTMSG                                            ; Print out the filename.
            LD      A,1
            JR      CHGDIR3
CHGDIR2:    LD      A,0                                                  ; Success.
CHGDIR3:    LD      (RESULT),A
            OR      A
            RET

            ;-------------------------------------------------------------------------------
            ; START OF TAPE/SD CMDLINE TOOLS FUNCTIONALITY
            ;-------------------------------------------------------------------------------

            ; Method to copy an application on a tape to an SD stored application. The tape drive is read and the first
            ; encountered program is loaded into memory at 0x1200. The CMT header is populated with the correct details (even if
            ; the load address isnt 0x1200, the CMT Header contains the correct value).
            ; A call is then made to write the application to the SD card.
            ; If the bulk method is called, we continue in a never ending loop, converting tape after tape.
            ;
TAPE2SDBULK:LD      A, 1                                                 ; Flag to indicate this is a bulk conversion.
            JR      TAPE2SD1
TAPE2SD:    XOR     A                                                    ; Flag to indicate this is a single conversion.
TAPE2SD1:   PUSH    AF                                                   ; Load from tape into memory, filling the tape CMT header and loading data into location 0x1200.
            PUSH    DE
            CALL    LOADTAPECP                                           ; Call the Loadtape command, non execute version to get the tape contents into memory.
            LD      A,(RESULT)
            OR      A
            JR      NZ,TAPE2SDERR
            ; Save to SD Card.
            CALL    SAVESDCARDX
            LD      A,(RESULT)
            OR      A
            JR      NZ,TAPE2SDERR
            LD      DE,MSGT2SDOK
            CALL    ?PRINTMSG
            POP     DE
            POP     AF
            OR      A
            JR      Z, TAPE2SDERR3
            JR      TAPE2SD1
TAPE2SDERR: CP      2                                                        ; Break key pressed?
            JR      Z,TAPE2SDERR2
            LD      DE,MSGT2SDERR
            CALL    ?PRINTMSG
TAPE2SDERR2:POP     DE
            POP     AF
TAPE2SDERR3:RET


            ; Method to copy an SD stored application to a Cassette tape in the CMT.
            ; The directory entry number or filename is passed to the command and the entry is located within the SD
            ; directory structure. The file is then loaded into memory and the CMT header populated. A call is then made
            ; to write out the data to tape.
            ;
SD2TAPE:    ; Load from SD, fill the CMT header then call CMT save.
            CALL    LOADSDCP
            LD      A,(RESULT)
            OR      A
            JR      NZ,SD2TAPEERR
            ; Tidy up header, original monitors expect CR in name for unused bytes.
            LD      HL,NAME
            LD      B,FNSIZE
SD2TAPE0:   LD      A,(HL)
            OR      A
            JR      NZ,SD2TAPE1
            LD      A,0DH
SD2TAPE1:   LD      (HL),A
            INC     HL
            DJNZ    SD2TAPE0
            ;
            CALL    SAVECMT
            LD      A,(RESULT)
            OR      A
            JR      NZ,SD2TAPEERR
            RET
SD2TAPEERR: LD      DE,MSGSD2TERR
            CALL    ?PRINTMSG
            RET

            ;-------------------------------------------------------------------------------
            ; END OF TAPE/SD CMDLINE TOOLS FUNCTIONALITY
            ;-------------------------------------------------------------------------------

            ;-------------------------------------------------------------------------------
            ; START OF FLOPPY DISK CONTROLLER FUNCTIONALITY
            ;-------------------------------------------------------------------------------

            ; Method to check if the floppy interface ROM is present and if it is, jump to its entry point.
            ;
FDCK:       CALL    FDCKROM                                              ; Check to see if the Floppy ROM is present, exit if it isnt.
            CALL    Z,FDCROMADDR
            RET                               ; JP       CMDCMPEND
FDCKROM:    LD      A,(FDCROMADDR)
            OR      A
            RET

FLOPPY:     PUSH    DE                                                   ; Preserve pointer to input buffer.
            LD      DE,BPARA                                             ; Copy disk parameter block into RAM work area. (From)
            LD      HL,PRMBLK                                            ; (To)
            LD      BC,0000BH                                            ; 11 bytes of config data.
            LDIR                                                         ; BC=0, HL=F0E8, DE=1013
            POP     DE                                                   ; init 1001-1005, port $DC mit $00
            LD      A,(DE)                                               ; If not at the end of the line, then process as the boot disk number.
            CP      00Dh                                                 ; 
            JR      NZ,GETBOOTDSK                                        ; 
            CALL    DSKINIT                                              ; Initialise disk and flags.
L000F:      LD      DE,MSGBOOTDRV                                        ; 
            CALL    ?PRINTMSG
            LD      DE,BUFER                                             ; 
            CALL    GETL                                                 ; 
            LD      A,(DE)                                               ; 
            CP      01BH                                                 ; Check input value is in range 1-4.
            RET     Z                                                    ; 
            LD      HL,MSGLOADERR - MSGBOOTDRV - 2                       ; Address of input location after printing out prompt.
            ADD     HL,DE                                                ; 
            LD      A,(HL)                                               ; 
            CP      00DH                                                 ; 
            JR      Z,L003A                                              ; 
GETBOOTDSK: CALL    HEX                                                  ; Convert number to binary
            JR      C,L000F                                              ; If illegal, loop back and re-prompt.
            DEC     A                                                    ; 
            CP      004H                                                 ; Check in range, if not loop back.
            JR      NC,L000F                                             ; 
            LD      (BPARA),A                                            ; Store in parameter block.
L003A:      LD      IX,BPARA                                             ; Point to drive number.,
            CALL    DSKREAD                                              ; Read sector 1 of trk 0
            LD      HL,0CE00H                                            ; Now compare the first 7 bytes of what was read to see if this is a bootable disk.
            LD      DE,DSKID                                             ; 
            LD      B,007H                                               ; 
L0049:      LD      C,(HL)                                               ; 
            LD      A,(DE)                                               ; 
            CP      C                                                    ; 
            JP      NZ,L008C                                             ; If NZ then this is not a master disk, ie not bootable, so error exit with message.
            INC     HL                                                   ; 
            INC     DE                                                   ; 
            DJNZ    L0049                                                ; 
            LD      DE,MSGIPLLOAD                                        ; 
            CALL    ?PRINTMSG
            LD      DE,0CE07H                                            ; Program name stored at 8th byte in boot sector.
            CALL    ?PRTFN
            LD      HL,(0CE16H)                                          ; Get the load address
            LD      (IX+005H),L                                          ; And store in parameter block at 100D/100E
            LD      (IX+006H),H                                          ; 
            INC     HL
            DEC     HL
            JR      NZ, NOTCPM                                           ; If load address is 0 then where loading CPM.
    ;        LD      A,(MEMSW)                                            ; Page out ROM.
NOTCPM:     LD      HL,(0CE14H)                                          ; Get the size
            LD      (IX+003H),L                                          ; And store in parameter block at 100B/100C
            LD      (IX+004H),H                                          ; 
            LD      HL,(0CE1EH)                                          ; Get logical sector number
            LD      (IX+001H),L                                          ; And store in parameter block at 1009/100A
            LD      (IX+002H),H                                          ; 
            CALL    DSKREAD                                              ; Read the required data and store in memory.
            CALL    DSKINIT                                              ; Reset the disk ready for next operation.
            LD      HL,(0CE18H)                                          ; Get the execution address
            JP      (HL)                                                 ; And execute.

DSKLOADERR: LD      DE,MSGLOADERR                                        ; Loading error message
            JR      L008F                                                ; (+003h)

L008C:      LD      DE,MSGDSKNOTMST                                      ; This is not a boot/master disk message.
L008F:      CALL    ?PRINTMSG
            LD      DE,ERRTONE                                           ; Play error tone.
            CALL    MELDY
            JP      ST1X                                                 ; Stack may be a mess due to the way the original AFI was written.

L0104:      LD      A,(MOTON)                                            ; motor on flag
            RRCA                                                         ; motor off?
            CALL    NC,DSKMOTORON                                        ; yes, set motor on and wait
            LD      A,(IX+000H)                                          ;drive no
            OR      084H                                                 ;
            OUT     (0DCH),A                                             ; Motor on for drive 0-3
            XOR     A                                                    ;
            LD      (FDCCMD),A                                           ; clr latest FDC command byte
            LD      HL,00000H                                            ;
L0119:      DEC     HL                                                   ;
            LD      A,H                                                  ;
            OR      L                                                    ;
            JP      Z,DSKERR                                             ; Reset and print message that this is not a bootable disk.
            IN      A,(0D8H)                                             ; Status register.
            CPL                                                          ;
            RLCA                                                         ;
            JR      C,L0119                                              ; Wait on motor off (bit 7)
            LD      C,(IX+000H)                                          ; Drive number
            LD      HL,TRK0FD1                                           ; 1 track 0 flag for each drive
            LD      B,000H                                               ;
            ADD     HL,BC                                                ; Compute related flag 1002/1003/1004/1005
            BIT     0,(HL)                                               ;
            JR      NZ,L0137                                             ; 
            CALL    DSKSEEKTK0                                           ; Seek track 0.
            SET     0,(HL)                                               ; Set bit 0 of trk 0 flag
L0137:      RET     

            ; Turn disk motor on.
DSKMOTORON: LD      A,080H
            OUT     (0DCH),A                                             ; Motor on
            LD      B,010H                                               ; 
L013E:      CALL    L02C7                                                ; 
            DJNZ    L013E                                                ; Wait until becomes ready.
            LD      A,001H                                               ; Set motor on flag.
            LD      (MOTON),A                                            ; 
            RET     

L0149:      LD      A,01BH
            CALL    DSKCMD
            AND     099H
            RET     

            ; Initialise drive and reset flags, Set motor off
DSKINIT:    XOR     A                                                    
            OUT     (0DCH),A                                             ; Motor on/off
            LD      (TRK0FD1),A                                          ; Track 0 flag drive 1
            LD      (TRK0FD2),A                                          ; Track 0 flag drive 2
            LD      (TRK0FD3),A                                          ; Track 0 flag drive 3
            LD      (TRK0FD4),A                                          ; Track 0 flag drive 4
            LD      (MOTON),A                                            ; Motor on flag
            RET     

DSKSEEKTK0: LD      A,00BH                                               ; Restore command, seek track 0.
            CALL    DSKCMD                                               ; Send command to FDC.
            AND     085H                                                 ; Process result.
            XOR     004H   
            RET     Z      
            JP      DSKERR

DSKCMD:     LD      (FDCCMD),A                                           ; Store latest FDC command.
            CPL                                                          ; Compliment it (FDC bit value is reversed).
            OUT     (0D8H),A                                             ; Send command to FDC.
            CALL    L017E                                                ; Wait to become ready.
            IN      A,(0D8H)                                             ; Get status register.
            CPL                                                          ; Inverse (FDC is reverse bit logic).
            RET     

L017E:      PUSH    DE
            PUSH    HL
            CALL    L02C0
            LD      E,007H
L0185:      LD      HL,00000H
L0188:      DEC     HL
            LD      A,H
            OR      L
            JR      Z,L0196                                              ; (+009h)
            IN      A,(0D8H)
            CPL     
            RRCA    
            JR      C,L0188                                              ; (-00bh)
            POP     HL
            POP     DE
            RET     

L0196:      DEC     E
            JR      NZ,L0185                                             ; (-014h)
            JP      DSKERR

L019C:      PUSH    DE
            PUSH    HL
            CALL    L02C0
            LD      E,007H
L01A3:      LD      HL,00000H
L01A6:      DEC     HL
            LD      A,H
            OR      L
            JR      Z,L01B4                                              ; (+009h)
            IN      A,(0D8H)
            CPL     
            RRCA    
            JR      NC,L01A6                                             ; (-00bh)
            POP     HL
            POP     DE
            RET     

L01B4:      DEC     E
            JR      NZ,L01A3                                             ; (-014h)
            JP      DSKERR

            ; Read disk starting at the first logical sector in param block 1009/100A
            ; Continue reading for the given size 100B/100C and store in the location 
            ; Pointed to by the address stored in the parameter block. 100D/100E
DSKREAD:    CALL    L0220                                                ; Compute logical sector-no to track-no & sector-no, retries=10
L01BD:      CALL    L0229                                                ; Set current track & sector, get load address to HL
L01C0:      CALL    L0249                                                ; Set side reg
            CALL    L0149                                                ; Command 1b output (seek)
            JR      NZ,L0216                                             ; 
            CALL    L0259                                                ; Set track & sector reg
            PUSH    IX                                                   ; Save 1008H
            LD      IX, FDCJMPL                                          ; As below. L03FE
            LD      IY,L01DF                                             ; Read sector into memory.
            DI      
            LD      A,094H                                               ; Latest FDC command byte
            CALL    L028A
L01DB:      LD      B,000H
            JP      (IX)

            ; Get data from disk sector to staging area (CE00).
L01DF:      INI     
            LD      A,(DE)                                               ; If not at the end of the line, then process as the boot disk number.
            JP      NZ, FDCJMPL                                          ; This is crucial, as the Z80 is running at 2MHz it is not fast enough so needs
                                                                         ; hardware acceleration in the form of a banked ROM, if disk not ready jumps to IX, if
                                                                         ; data ready, jumps to IY. L03FE
            POP     IX
            INC     (IX+008H)                                            ; Increment current sector number
            LD      A,(IX+008H)                                          ; Load current sector number
            PUSH    IX                                                   ; Save 1008H
            LD      IX, FDCJMPL                                          ; As above. L03FE
            CP      011H                                                 ; Sector 17? Need to loop to next track.
            JR      Z,L01FB                 
            DEC     D
            JR      NZ,L01DB                
            JR      L01FC                                                ; (+001h)

L01FB:      DEC     D
L01FC:      CALL    L0294
            CALL    L02D2
            POP     IX
            IN      A,(0D8H)
            CPL     
            AND     0FFH
            JR      NZ,L0216                                             ; (+00bh)
            CALL    L0278
            JP      Z,L021B
            LD      A,(IX+007H)
            JR      L01C0                                                ; (-056h)

L0216:      CALL    L026A
            JR      L01BD                                                ; (-05eh)

L021B:      LD      A,080H
            OUT     (0DCH),A                                             ; Motor on
            RET     

L0220:      CALL    L02A3                                                ; compute logical sector no to track no & sector no
            LD      A,00AH                                               ; 10 retries
            LD      (RETRIES),A
            RET     

            ; Set current track & sector, get load address to HL
L0229:      CALL    L0104
            LD      D,(IX+004H)                                          ; Number of sectors to read
            LD      A,(IX+003H)                                          ; Bytes to read
            OR      A                                                    ; 0?
            JR      Z,L0236                                              ; Yes
            INC     D                                                    ; Number of sectors to read + 1
L0236:      LD      A,(IX+00AH)                                          ; Start sector number
            LD      (IX+008H),A                                          ; To current sector number
            LD      A,(IX+009H)                                          ; Start track number
            LD      (IX+007H),A                                          ; To current track number
            LD      L,(IX+005H)                                          ; Load address low byte
            LD      H,(IX+006H)                                          ; Load address high byte
            RET     

            ; Compute side/head.
L0249:      SRL     A                                                    ; Track number even?
            CPL                                                          ; 
            OUT     (0DBH),A                                             ; Output track no.
            JR      NC,L0254                                             ; Yes, even, set side/head 1
            LD      A,001H                                               ; No, odd, set side/head 0
            JR      L0255                   

            ; Set side/head register.
L0254:      XOR     A                                                    ; Side 0
L0255:      CPL                                                          ; Side 1
            OUT     (0DDH),A                                             ; Side/head register.
            RET     

            ; Set track and sector register.
L0259:      LD      C,0DBH                  
            LD      A,(IX+007H)                                          ; Current track number
            SRL     A                       
            CPL                             
            OUT     (0D9H),A                                             ; Track reg
            LD      A,(IX+008H)                                          ; Current sector number
            CPL                            
            OUT     (0DAH),A                                             ; Sector reg
            RET                      

L026A:      LD      A,(RETRIES)
            DEC     A
            LD      (RETRIES),A
            JP      Z,DSKERR
            CALL    DSKSEEKTK0
            RET     

L0278:      LD      A,(IX+008H)
            CP      011H
            JR      NZ,L0287                                             ; (+008h)
            LD      A,001H
            LD      (IX+008H),A
            INC     (IX+007H)
L0287:      LD      A,D
            OR      A
            RET     

L028A:      LD      (FDCCMD),A
            CPL     
            OUT     (0D8H),A
            CALL    L019C
            RET     

L0294:      LD      A,0D8H
            CPL     
            OUT     (0D8H),A
            CALL    L017E
            RET     

DSKERR:     CALL    DSKINIT
            JP      DSKLOADERR

            ; Logical sector number to physical track and sector.
L02A3:      LD      B,000H
            LD      DE,00010H                                            ; No of sectors per trk (16)
            LD      L,(IX+001H)                                          ; Logical sector number
            LD      H,(IX+002H)                                          ; 2 bytes in length
            XOR     A
L02AF:      SBC     HL,DE                                                ; Subtract 16 sectors/trk 
            JR      C,L02B6                                              ; Yes, negative value
            INC     B                                                    ; Count track
            JR      L02AF                                                ; Loop
L02B6:      ADD     HL,DE                                                ; Reset HL to the previous
            LD      H,B                                                  ; Track
            INC     L                                                    ; Correction +1
            LD      (IX+009H),H                                          ; Start track no
            LD      (IX+00AH),L                                          ; Start sector no
            RET     

L02C0:      PUSH    DE
            LD      DE,00007H
            JP      L02CB

L02C7:      PUSH    DE
            LD      DE,01013H
L02CB:      DEC     DE
            LD      A,E
            OR      D
            JR      NZ,L02CB                                             ; (-005h)
            POP     DE
            RET     

L02D2:      PUSH    AF
            LD      A,(0119CH)
            CP      0F0H
            JR      NZ,L02DB                                             ; (+001h)
            EI      
L02DB:      POP     AF
            RET     

;wait on bit 0 and bit 1 = 0 of state reg
L0300:      IN      A,(0D8H)	                                 	     ; State reg
            RRCA    
            JR      C,L0300	                                             ; Wait on not busy
            RRCA    
            JR      C,L0300	                                             ; Wait on data reg ready
            JP      (IY)	                                             ; to f1df

            ;-------------------------------------------------------------------------------
            ; END OF FLOPPY DISK CONTROLLER FUNCTIONALITY
            ;-------------------------------------------------------------------------------

            ;-------------------------------------------------------------------------------
            ; START OF UTILITIES
            ;-------------------------------------------------------------------------------

            ; Method to get a string parameter and copy it into the provided buffer.
            ;
            ; Inputs:
            ;     DE = Pointer to BUFER where user entered data has been placed.
            ;     HL = Pointer to Destination buffer.
            ;     B  = Max number of characters to read.
            ; Outputs:
            ;     DE and HL point to end of bufer and buffer resepectively.
            ;     B  = Characters copied (ie. B - input B = no characters).
            ;
GETSTRING:  LD      A,(DE)                                               ; Skip white space before copy.
            CP      33
            JR      NC, GETSTR1
            CP      00DH
            JR      Z, GETSTR2                                           ; No directory means use the I/O set default.
            OR      A
            JR      Z, GETSTR2                                           
            INC     DE
            JR      GETSTRING
GETSTR1:    LD      (HL),A                                               ; Copy the name entered by user. Validation is done on the I/O processor, bad directory name will result in error next read/write.
            INC     DE
            INC     HL
            LD      A,(DE)                                               ; Get next char and check it isnt CR, end of input line character.
            CP      00DH
            JR      Z,GETSTR2                                            ; Finished if we encounter CR.
            DJNZ    GETSTR1                                              ; Loop until buffer is full, ignore characters beyond buffer limit.
GETSTR2:    XOR     A                                                    ; Place end of buffer terminator as I/O processor uses C strings.
            LD      (HL),A
            RET

            ;-------------------------------------------------------------------------------
            ; END OF UTILITIES
            ;-------------------------------------------------------------------------------

            ;-------------------------------------------------------------------------------
            ; API METHODS - Intercept handlers to provide enhanced services to 
            ;               existing MA-700 BIOS API functions.
            ;-------------------------------------------------------------------------------

            ; Method to check if the active drive is the CMT.
CHECKCMT:   LD      A,(CMTINACTIVE)                                      ; Test the flag to see if the default is to use the CMT for the CMT/SD intercept handlers.
            OR      A                                                    ; Flag set to > 0 if the CMT is not active.
            RET

            ; Convert the lower 4 bits of A into a Hex character.
TOHEXDIGIT: AND     00FH                                                 ; Simple logic, add 30H to get 0..9, add additional 7 if value >= 10 to get digits A..F.
            CP      00AH
            JR      C,NOADD                 
            ADD     A,007H
NOADD:      ADD     A,030H
            RET    

            ; Convert a number into Hex string and store in buffer pointed to by DE.
            ;
TOHEX:      PUSH    DE
            PUSH    AF                                                   ; Save AF to retrieve lower 4 bits.
            RRCA                                                         ; Shift upper 4 bits to lower to convert to hex.
            RRCA    
            RRCA    
            RRCA    
            CALL    TOHEXDIGIT
            LD      (DE),A                                               ; Store and convert lower 4 bits.
            INC     DE
            POP     AF
            CALL    TOHEXDIGIT
            LD      (DE),A
            INC     DE
            LD      A,CR                                                 ; Terminate with a CR.
            LD      (DE),A
            POP     DE                                                   ; DE back to start of string.
            RET

            ; Handler to intercept the CMT Read Header Information call.
            ; DE contains a pointer to memory containing the file to load. If (DE) = NULL then
            ; load the next sequential file from the SD card directory.
            ; DE = Filename.
            ;
            ; No registers or flags should be affected as we dont know the caller state.
_CMT_RDINF: CALL    CHECKCMT                                             ; If drive is set to the CMT Unit exit with Z set so that the original CMT handlers are called.
            JP      Z,?RDI
            LD      A,(DE)                                               ; Check to see if empty string given, if so expand the default Next file number into the buffer.
            CP      CR
            JR      NZ,_CMT_RDINF1
            LD      A,(CMTFILENO)                                        ; Get next sequential number and convert to hex.
            PUSH    AF
            CALL    TOHEX                         
            POP     AF
            INC     A                                                    ; Increment number so next call retrieves the next sequential file.
            LD      (CMTFILENO),A
            ;
_CMT_RDINF1:PUSH    DE
            CALL    LOADSDINF                                            ; DE already points to the filename, call LOADSDINF to locate it on the SD card and setup the header.
            POP     DE
            OR      A
            SCF
            RET     NZ                                                   ; > 0 = fail, return with carry set.

            ; Copy the filename into the Buffer provided allowing for file number to name expansion.
            LD      HL,NAME
            LD      BC,TZSVCFILESZ
            LDIR
            ;
            OR      A
            RET                                                          ; 0 = success, return with carry clear.

            ; Handler to intercept the CMT Read Data call and insert selectable SD Card
            ; Drive functionality. 
            ;
            ; No registers or flags should be affected as we dont know the caller state.
_CMT_RDDATA:CALL    LOADSDDATA
            OR      A
            JR      NZ,_CMT_RDERR
            RET
_CMT_RDERR: SCF
            RET

            ; Handler to intercept the CMT Write Header Information call and insert selectable
            ; SD Card RFS Drive functionality. 
            ;
            ; No registers or flags should be affected as we dont know the caller state.
            ;
            ; At the moment, the WRINF call only creates a filename if none specified. The actual write to file occurs in WRDATA. Once I have more understanding of
            ; how the sequential data mode works I can adapt it to be compatible.
_CMT_WRINF: LD      DE,NAME                                              ; Caller has already setup the CMT header so we use this for processing.
            ;
            CALL    CHECKCMT
            JP      Z,?WRI
            ;
            LD      A,(DE)                                               ; Check to see if empty string given, if so create a default name.
            CP      CR
            JR      NZ,_CMT_WRINF1
            ;
            LD      HL,DEFAULTFN
            LD      BC,DEFAULTFNE - DEFAULTFN
            LDIR
            LD      A,(CMTFILENO)                                        ; Get next sequential number and convert to hex.
            PUSH    AF
            CALL    TOHEX                         
            POP     AF
            INC     A                                                    ; Increment number so next call retrieves the next sequential file.
            LD      (CMTFILENO),A
            ;
_CMT_WRINF1:LD      A,0                                                  ; Always success as nothing is written.
            OR      A
            RET

            ; Handler to intercept the CMT Write Data call and insert selectable SD Card RFS
            ; Drive functionality. 
            ;
            ; No registers or flags should be affected as we dont know the caller state.
_CMT_WRDATA:CALL    SAVESDDATA
            LD      A,(RESULT)
            OR      A
            JR      NZ,_CMT_RDERR
            RET

            ; Handler to intercept the CMT Verify Data call and insert selectable SD Card
            ; Drive functionality. 
            ;
            ; No registers or flags should be affected as we dont know the caller state.
_CMT_VERIFY:CALL    CHECKCMT
            JR      Z,_VERIFY
            LD      DE,MSGNOVERIFY
            JR      SD_ERRMSG

_VERIFY:    JP      ?VRFY

SD_NOTFND:  LD      DE,MSGNOTFND
SD_ERRMSG:  CALL    ?PRINTMSG
            LD      A,1
            OR      A
            RET

            ; Method list the active directory contents.
_CMT_DIR:   CALL    CHECKCMT                                             ; Cannot DIR tape drive so give error.
            JP      Z,_CMT_NODIR
            CALL    DIRSDCARD                                            ; List the directory contents with optional directory/wildcard filter.
            RET
_CMT_NODIR: LD      DE,MSGNOCMTDIR
            JR      SD_ERRMSG

            ; Method to set the active directory or CMT unit/SD card. ie. \BAS to change to directory \BAS on the SD card, C to switch to CMT unit.
_CMT_CD:    LD      A,(DE)
            CP      'C'                                                  ; Check to see if we are enabling the CMT unit.
            JR      NZ,_CMT_CD2
            XOR     A                                                    ; CMT is now active so clear the inactive flag.
_CMT_CD1:   LD      (CMTINACTIVE),A
            RET
_CMT_CD2:   CALL    CHGSDDIR                                             ; Change directory, if not valid do not update the CMTINACTIVE flag.
            RET     NZ
            LD      A,1
            JR      _CMT_CD1

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

            ; A method used when testing hardware, scope and code will change but one of its purposes is to generate a scope signal pattern.
            ;
LOCALTEST:  LD      A,0
            LD      C,SVCREQ
            OUT     (C),A
            RET

            ; Quick load program names.
CPMFNAME80A:DB      "CPM223 MZ-80A-80", 000H
BASICFNM80A:DB      "BASIC-SA-5510-TZ", 000H
CPMFNAME700:DB      "CPM223 MZ-700-80", 000H
CPMFNAME1500:DB     "CPM223 MZ-1500-4", 000H
BASICFNM700:DB      "S-BASIC", 000H
BASICFNM1500:DB     "S-BASIC", 000H
DEFAULTFN:  DB      "DEFAULT"
DEFAULTFNE: EQU     $

            ; Error tone.
ERRTONE:    DB      "A0", 0D7H, "ARA", 0D7H, "AR", 00DH

            ; Identifier to indicate this is a valid boot disk
DSKID:      DB      002H, "IPLPRO"

            ; Parameter block to indicate configuration and load area.
PRMBLK:     DB      000H, 000H, 000H, 000H, 001H, 000H, 0CEH, 000H, 000H, 000H, 000H

            ; Monitor command table. This table contains the list of recognised commands along with the 
            ; handler function and bank in which it is located.
            ;
            ;         7     6     5:3    2:0
            ;        END  MATCH  UNUSED  SIZE 
CMDTABLE:  ;DB      000H | 000H | 000H | 003H
           ;DB      "40A"                                                ; Switch to the 40char Sharp MZ-80A compatbile mode.
           ;DW      SETMODE40A
           ;DB      000H | 000H | 000H | 003H
           ;DB      "80A"                                                ; Switch to the 80char Sharp MZ-80A compatbile mode.
           ;DW      SETMODE80A
           ;DB      000H | 000H | 000H | 003H
           ;DB      "80B"                                                ; Switch to the Sharp MZ-80B compatbile mode.
           ;DW      SETMODE80B
            DB      000H | 000H | 000H | 001H                            ; Bit 2:0 = Command Size, 5:3 = Bank, 6 = Command match, 7 = Command table end.
            DB      '4'                                                  ; 40 Char screen mode.
            DW      SETMODE40
            DB      000H | 000H | 000H | 001H
            DB      '8'                                                  ; 80 Char screen mode.
            DW      SETMODE80
           ;DB      000H | 000H | 000H | 004H
           ;DB      "7008"                                               ; Switch to 80 column MZ700 mode.
           ;DW      SETMODE7008
           ;DB      000H | 000H | 000H | 003H
           ;DB      "700"                                                ; Switch to 40 column MZ700 mode.
           ;DW      SETMODE700
            DB      000H | 000H | 000H | 003H
            DB      "ASM"                                                ; Assembler.
            DW      ?ASM
            DB      000H | 000H | 000H | 005H
            DB      "BASIC"                                              ; Load and run BASIC SA-5510.
            DW      LOADBASIC
            DB      000H | 000H | 000H | 001H
            DB      'B'                                                  ; Bell.
            DW      SGX
            DB      000H | 000H | 000H | 003H
            DB      "CPM"                                                ; Load and run CPM.
            DW      LOADCPM
            DB      000H | 000H | 000H | 002H
            DB      "CP"                                                 ; Copy memory
            DW      ?COPYM
            DB      000H | 000H | 000H | 002H
            DB      "CD"                                                 ; SD Card Directory change command.
            DW      CHGSDDIR
            DB      000H | 000H | 000H | 004H
            DB      "DASM"                                               ; Disassembler.
            DW      ?DASM
            DB      000H | 000H | 000H | 003H
            DB      "DIR"                                                ; List SD Card directory.
            DW      DIRSDCARD
            DB      000H | 000H | 000H | 001H
            DB      'D'                                                  ; Dump Memory.
            DW      ?DUMPX
            DB      000H | 000H | 000H | 002H
            DB      "EC"                                                 ; Erase file.
            DW      ERASESD
            DB      000H | 000H | 000H | 002H
            DB      "EX"                                                 ; Exit out of TZFS to original Monitor.
            DW      EXITTZFS 
            DB      000H | 000H | 000H | 004H
            DB      "FILL"                                               ; Fill Memory.
            DW      ?FILL
            DB      000H | 000H | 000H | 004H
            DB      "FREQ"                                               ; Set or change the CPU frequency.
            DW      ?SETFREQ
            DB      000H | 000H | 000H | 001H
            DB      'F'                                                  ; RFS Floppy boot code.
            DW      FLOPPY
            DB      000H | 000H | 000H | 001H
            DB      'H'                                                  ; Help screen.
            DW      ?HELP
            DB      000H | 000H | 000H | 001H
            DB      'J'                                                  ; Jump to address.
            DW      GOTOX
            DB      000H | 000H | 000H | 004H
            DB      "LTNX"                                               ; Load from CMT without auto execution.
            DW      LOADTAPENX
            DB      000H | 000H | 000H | 002H
            DB      "LT"                                                 ; Load from CMT
            DW      LOADTAPE
            DB      000H | 000H | 000H | 004H
            DB      "LCNX"                                               ; Load from SDCARD without auto execution.
            DW      LOADSDCARDX
            DB      000H | 000H | 000H | 002H
            DB      "LC"                                                 ; Load from SD CARD
            DW      LOADSDCARD
            DB      000H | 000H | 000H | 001H
            DB      "L"                                                  ; Original Load from CMT
            DW      LOADTAPE
            ;
            IF BUILD_FUSIONX = 0
              DB    000H | 000H | 000H | 005H
              DB    "MZ80K"                                              ; Invoke MZ80K Hardware Emulation.
              DW    ?SETMZ80K
              DB    000H | 000H | 000H | 005H
              DB    "MZ80C"                                              ; Invoke MZ80C Hardware Emulation.
              DW    ?SETMZ80C
              DB    000H | 000H | 000H | 006H
              DB    "MZ1200"                                             ; Invoke MZ1200 Hardware Emulation.
              DW    ?SETMZ1200
              DB    000H | 000H | 000H | 005H
              DB    "MZ80A"                                              ; Invoke MZ80A Hardware Emulation.
              DW    ?SETMZ80A
              DB    000H | 000H | 000H | 005H
              DB    "MZ700"                                              ; Invoke MZ700 Hardware Emulation.
              DW    ?SETMZ700
              DB    000H | 000H | 000H | 006H
              DB    "MZ1500"                                             ; Invoke MZ1500 Hardware Emulation.
              DW    ?SETMZ1500
              DB    000H | 000H | 000H | 005H
              DB    "MZ800"                                              ; Invoke MZ800 Hardware Emulation.
              DW    ?SETMZ800
              DB    000H | 000H | 000H | 005H
              DB    "MZ80B"                                              ; Invoke MZ80B Hardware Emulation.
              DW    ?SETMZ80B
              DB    000H | 000H | 000H | 006H
              DB    "MZ2000"                                             ; Invoke MZ2000 Hardware Emulation.
              DW    ?SETMZ2000
              DB    000H | 000H | 000H | 006H
              DB    "MZ2200"                                             ; Invoke MZ2200 Hardware Emulation.
              DW    ?SETMZ2200
              DB    000H | 000H | 000H | 006H
              DB    "MZ2500"                                             ; Invoke MZ2500 Hardware Emulation.
              DW    ?SETMZ2500
              DB    000H | 000H | 000H | 002H
              DB    "MZ"                                                 ; Invoke default MZ80A Hardware Emulation.
              DW    ?SETMZ80A
            ENDIF
            ;
            DB      000H | 000H | 000H | 001H
            DB      'M'                                                  ; Edit Memory.
            DW      ?MCORX
            DB      000H | 000H | 000H | 001H
            DB      'P'                                                  ; Printer test.
            DW      ?PTESTX
            DB      000H | 000H | 000H | 003H
            DB      "RIO"                                                ; Read I/O
            DW      ?READIO
            DB      000H | 000H | 000H | 001H
            DB      'R'                                                  ; Memory test.
            DW      MEMTEST
            DB      000H | 000H | 000H | 004H
            DB      "SD2T"                                               ; Copy SD Card to Tape.
            DW      SD2TAPE
            DB      000H | 000H | 000H | 002H
            DB      "SC"                                                 ; Save to SD CARD
            DW      SAVESDCARD
            DB      000H | 000H | 000H | 002H
            DB      "ST"                                                 ; Save to CMT
            DW      SAVEX
            DB      000H | 000H | 000H | 001H
            DB      'S'                                                  ; Save to CMT
            DW      SAVEX
            DB      000H | 000H | 000H | 004H
            DB      "TEST"                                               ; A test function used in debugging.
            DW      LOCALTEST
            DB      000H | 000H | 000H | 005H
            DB      "T2SDB"                                              ; Copy Tape to SD Card in bulk mode.
            DW      TAPE2SDBULK
            DB      000H | 000H | 000H | 004H
            DB      "T2SD"                                               ; Copy Tape to SD Card.
            DW      TAPE2SD
            IF BUILD_FUSIONX = 0
              DB    000H | 000H | 000H | 003H
              DB    "T80"                                                ; Switch to soft T80 CPU.
              DW    ?SETT80
            ENDIF
            DB      000H | 000H | 000H | 002H
            DB      "TC"                                                 ; Tape timing compensation.
            DW      ?TAPECOMP
            DB      000H | 000H | 000H | 001H
            DB      'T'                                                  ; Timer test.
            DW      ?TIMERTST
            IF BUILD_FUSIONX = 0
              DB    000H | 000H | 000H | 007H
              DB    "VBORDER"                                            ; Set VGA border colour.
              DW    ?SETVBORDER
              DB    000H | 000H | 000H | 005H
              DB    "VMODE"                                              ; Set VGA mode.
              DW    ?SETVMODE
              DB    000H | 000H | 000H | 003H
              DB    "VGA"                                                ; Set VGA mode.
              DW    ?SETVGAMODE
            ENDIF
            DB      000H | 000H | 000H | 001H
            DB      'V'                                                  ; Verify CMT Save.
            DW      VRFYX
            DB      000H | 000H | 000H | 003H
            DB      "WIO"                                                ; Write I/O
            DW      ?WRITEIO
            IF BUILD_FUSIONX = 0
              DB     000H | 000H | 000H | 003H
              DB    "Z80"                                                ; Switch to soft Z80 CPU.
              DW    ?SETZ80
              DB    000H | 000H | 000H | 003H
              DB    "ZPU"                                                ; Switch to soft ZPU Evolution CPU.
              DW    ?SETZPUEVO
              DB    000H | 000H | 000H | 001H
            ENDIF

            ;-------------------------------------------------------------------------------
            ; END OF TZFS COMMAND FUNCTIONS.
            ;-------------------------------------------------------------------------------

            ;
            ; Ensure we fill the entire 64K by padding with 00's.
            ;
            ALIGN_NOPS      10000H
MEND: 

            ;
            ; Include all other banks which make up the TZFS system.
            ;
            INCLUDE  "tzfs_bank2.asm"
            INCLUDE  "tzfs_bank3.asm"
            INCLUDE  "tzfs_bank4.asm"
