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

           ;============================================================
           ;
           ; USER ROM BANK 1 - Floppy Disk Controller functions.
           ;
           ;============================================================
           ORG      UROMADDR

           ;--------------------------------
           ; Common code spanning all banks.
           ;--------------------------------
           NOP
           LD      B,16                                                      ; If we read the bank control reset register 15 times then this will enable bank control and then the 16th read will reset all bank control registers to default.
ROMFS1_0:  LD      A,(BNKCTRLRST)
           DJNZ    ROMFS1_0                                                  ; Apply the default number of coded latch reads to enable the bank control registers.
           LD      A,BNKCTRLDEF                                              ; Set coded latch, SDCS high, BBMOSI to high and BBCLK to high which enables SDCLK.
           LD      (BNKCTRL),A
           NOP
           NOP
           NOP
           XOR     A                                                         ; We shouldnt arrive here after a reset, if we do, select UROM bank 0
           LD      (BNKSELMROM),A
           NOP
           NOP
           NOP
           LD      (BNKSELUSER),A                                            ; and start up - ie. SA1510 Monitor - this occurs as User Bank 0 is enabled and the jmp to 0 is coded in it.
           ;
           ; No mans land... this should have switched to Bank 0 and at this point there is a jump to 00000H.
           JP      00000H                                                    ; This is for safety!!

           ;------------------------------------------------------------------------------------------
           ; Bank switching code, allows a call to code in another bank.
           ; This code is duplicated in each bank such that a bank switch doesnt affect logic flow.
           ;------------------------------------------------------------------------------------------
           ALIGN_NOPS UROMBSTBL
           ;
BKSW1to0:  PUSH     AF
           LD       A, ROMBANK1                                              ; Calling bank (ie. us).
           PUSH     AF
           LD       A, ROMBANK0                                              ; Required bank to call.
           JR       BKSW1_0
BKSW1to1:  PUSH     AF
           LD       A, ROMBANK1                                              ; Calling bank (ie. us).
           PUSH     AF
           LD       A, ROMBANK1                                              ; Required bank to call.
           JR       BKSW1_0
BKSW1to2:  PUSH     AF
           LD       A, ROMBANK1                                              ; Calling bank (ie. us).
           PUSH     AF
           LD       A, ROMBANK2                                              ; Required bank to call.
           JR       BKSW1_0
BKSW1to3:  PUSH     AF
           LD       A, ROMBANK1                                              ; Calling bank (ie. us).
           PUSH     AF
           LD       A, ROMBANK3                                              ; Required bank to call.
           JR       BKSW1_0
BKSW1to4:  PUSH     AF
           LD       A, ROMBANK1                                              ; Calling bank (ie. us).
           PUSH     AF
           LD       A, ROMBANK4                                              ; Required bank to call.
           JR       BKSW1_0
BKSW1to5:  PUSH     AF
           LD       A, ROMBANK1                                              ; Calling bank (ie. us).
           PUSH     AF
           LD       A, ROMBANK5                                              ; Required bank to call.
           JR       BKSW1_0
BKSW1to6:  PUSH     AF
           LD       A, ROMBANK1                                              ; Calling bank (ie. us).
           PUSH     AF
           LD       A, ROMBANK6                                              ; Required bank to call.
           JR       BKSW1_0
BKSW1to7:  PUSH     AF
           LD       A, ROMBANK1                                              ; Calling bank (ie. us).
           PUSH     AF
           LD       A, ROMBANK7                                              ; Required bank to call.
           ;
BKSW1_0:   PUSH     HL                                                       ; Place function to call on stack
           LD       HL, BKSWRET1                                             ; Place bank switchers return address on stack.
           EX       (SP),HL
           LD       (TMPSTACKP),SP                                           ; Save the stack pointer as some old code corrupts it.
           LD       (BNKSELUSER), A                                          ; Repeat the bank switch B times to enable the bank control register and set its value.
           JP       (HL)                                                     ; Jump to required function.
BKSWRET1:  POP      AF                                                       ; Get bank which called us.
           LD       (BNKSELUSER), A                                          ; Return to that bank.
           POP      AF
           RET      

FDCCMD     EQU      01000H
MOTON      EQU      01001H
TRK0FD1    EQU      01002H
TRK0FD2    EQU      01003H
TRK0FD3    EQU      01004H
TRK0FD4    EQU      01005H
RETRIES    EQU      01006H
BPARA      EQU      01008H

           ;-------------------------------------------------------------------------------
           ; 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,0F000h
           RET                                ; JP       CMDCMPEND
FDCKROM:   LD       A,(0F000h)
           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                                            ; 
           LD       HL,PRINTMSG
           CALL     BKSW1to6
           LD       DE,011A3H                                                ; 
           CALL     GETL                                                     ; 
           LD       A,(DE)                                                   ; 
           CP       01BH                                                     ; Check input value is in range 1-4.
           JP       Z,SS                                                     ; 
           LD       HL,0000CH                                                ; 
           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                                            ; 
           LD       HL,PRINTMSG
           CALL     BKSW1to6
           LD       DE,0CE07H                                                ; Program name stored at 8th byte in boot sector.
           LD       HL,PRTFN
           CALL     BKSW1to6
           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:     LD       HL,PRINTMSG
           CALL     BKSW1to6
           LD       DE,ERRTONE                                               ; Play error tone.
           CALL     MELDY
           ;
           LD       SP,(TMPSTACKP)                                           ; Recover the correct stack pointer before exit.
           RET                                                               ; JP       SS

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, 0F3FEH                                               ; 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, 0F3FEH                                               ; 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, 0F3FEH                                               ; 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
           ;-------------------------------------------------------------------------------

           ;--------------------------------------
           ;
           ; Message table - Refer to bank 6 for
           ;                 all messages.
           ;
           ;--------------------------------------

           ; 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

           ; Ensure we fill the entire 2K by padding with FF's.
           ALIGN    0EBFDh
           DB       0FFh

L03FE:     JP       (IY)

           ALIGN    0EFFFh
           DB       0FFh
