;       CBIOS for Z80-Simulator
;
;       Copyright (C) 1988 by Udo Munk
;
MSIZE   EQU     64              ;cp/m version memory size in kilobytes
;
;       "bias" is address offset from 3400H for memory systems
;       than 16K (referred to as "b" throughout the text).
;
BIAS    EQU     (MSIZE-20)*1024
CCP     EQU     3400H+BIAS      ;base of ccp
BDOS    EQU     CCP+806H        ;base of bdos
BIOS    EQU     CCP+1600H       ;base of bios
CDISK   EQU     0004H           ;current disk number 0=A,...,15=P
IOBYTE  EQU     0003H           ;intel i/o byte
;
;       I/O ports
;
CONSTA  EQU     0               ;console status port
CONDAT  EQU     1               ;console data port
PRTSTA  EQU     2               ;printer status port
PRTDAT  EQU     3               ;printer data port
AUXDAT  EQU     5               ;auxiliary data port
FDCD    EQU     10              ;fdc-port: # of drive
FDCT    EQU     11              ;fdc-port: # of track
FDCS    EQU     12              ;fdc-port: # of sector
FDCOP   EQU     13              ;fdc-port: command
FDCST   EQU     14              ;fdc-port: status
DMAL    EQU     15              ;dma-port: dma address low
DMAH    EQU     16              ;dma-port: dma address high
;
       ORG     BIOS            ;origin of this program
NSECTS  EQU     (BIOS-CCP)/128  ;warm start sector count
;
;       jump vector for individual subroutines
;
       JP      BOOT            ;cold start
WBOOTE: JP      WBOOT           ;warm start
       JP      CONST           ;console status
       JP      CONIN           ;console character in
       JP      CONOUT          ;console character out
       JP      LIST            ;list character out
       JP      PUNCH           ;punch character out
       JP      READER          ;reader character out
       JP      HOME            ;move head to home position
       JP      SELDSK          ;select disk
       JP      SETTRK          ;set track number
       JP      SETSEC          ;set sector number
       JP      SETDMA          ;set dma address
       JP      READ            ;read disk
       JP      WRITE           ;write disk
       JP      LISTST          ;return list status
       JP      SECTRAN         ;sector translate
;
;       fixed data tables for four-drive standard
;       IBM-compatible 8" disks
;
;       disk parameter header for disk 00
DPBASE: DEFW    TRANS,0000H
       DEFW    0000H,0000H
       DEFW    DIRBF,DPBLK
       DEFW    CHK00,ALL00
;       disk parameter header for disk 01
       DEFW    TRANS,0000H
       DEFW    0000H,0000H
       DEFW    DIRBF,DPBLK
       DEFW    CHK01,ALL01
;       disk parameter header for disk 02
       DEFW    TRANS,0000H
       DEFW    0000H,0000H
       DEFW    DIRBF,DPBLK
       DEFW    CHK02,ALL02
;       disk parameter header for disk 03
       DEFW    TRANS,0000H
       DEFW    0000H,0000H
       DEFW    DIRBF,DPBLK
       DEFW    CHK03,ALL03
;
;       sector translate vector
;
TRANS:  DEFB    1,7,13,19       ;sectors 1,2,3,4
       DEFB    25,5,11,17      ;sectors 5,6,7,8
       DEFB    23,3,9,15       ;sectors 9,10,11,12
       DEFB    21,2,8,14       ;sectors 13,14,15,16
       DEFB    20,26,6,12      ;sectors 17,18,19,20
       DEFB    18,24,4,10      ;sectors 21,22,23,24
       DEFB    16,22           ;sectors 25,26
;
;       disk parameter block, common to all disks
;
DPBLK:  DEFW    26              ;sectors per track
       DEFB    3               ;block shift factor
       DEFB    7               ;block mask
       DEFB    0               ;null mask
       DEFW    242             ;disk size-1
       DEFW    63              ;directory max
       DEFB    192             ;alloc 0
       DEFB    0               ;alloc 1
;       DEFW    16              ;check size
       DEFW    0               ;because medium not changable with z80sim!
       DEFW    2               ;track offset
;
;       signon message
;
SIGNON: DEFM    '64K CP/M Vers. 2.2'
       DEFB    0
;
;       end of fixed tables
;
;       individual subroutines to perform each function
;       simplest case is to just perform parameter initialization
;
BOOT:   LD      SP,80H          ;use space below buffer for stack
       LD      HL,SIGNON       ;print message
BOOTL:  LD      A,(HL)
       OR      A
       JP      Z,BOOTC
       LD      C,A
       CALL    CONOUT
       INC     HL
       JP      BOOTL
BOOTC:  XOR     A               ;zero in the accum
       LD      (IOBYTE),A      ;clear the iobyte
       LD      (CDISK),A       ;select disk zero
       JP      GOCPM           ;initialize and go to cp/m
;
;       simplest case is to read the disk until all sectors loaded
;
WBOOT:  LD      SP,80H          ;use space below buffer for stack
       LD      C,0             ;select disk 0
       CALL    SELDSK
       CALL    HOME            ;go to track 00
;
       LD      B,NSECTS        ;b counts # of sectors to load
       LD      C,0             ;c has the current track number
       LD      D,2             ;d has the next sector to read
;       note that we begin by reading track 0, sector 2 since sector 1
;       contains the cold start loader, which is skipped in a warm start
       LD      HL,CCP          ;base of cp/m (initial load point)
LOAD1:                          ;load one more sector
       PUSH    BC              ;save sector count, current track
       PUSH    DE              ;save next sector to read
       PUSH    HL              ;save dma address
       LD      C,D             ;get sector address to register c
       CALL    SETSEC          ;set sector address from register c
       POP     BC              ;recall dma address to b,c
       PUSH    BC              ;replace on stack for later recall
       CALL    SETDMA          ;set dma address from b,c
;       drive set to 0, track set, sector set, dma address set
       CALL    READ
       CP      00H             ;any errors?
       JP      NZ,WBOOT        ;retry the entire boot if an error occurs
;       no error, move to next sector
       POP     HL              ;recall dma address
       LD      DE,128          ;dma=dma+128
       ADD     HL,DE           ;new dma address is in h,l
       POP     DE              ;recall sector address
       POP     BC              ;recall number of sectors remaining, and current trk
       DEC     B               ;sectors=sectors-1
       JP      Z,GOCPM         ;transfer to cp/m if all have been loaded
;       more sectors remain to load, check for track change
       INC     D
       LD      A,D             ;sector=27?, if so, change tracks
       CP      27
       JP      C,LOAD1         ;carry generated if sector<27
;       end of current track, go to next track
       LD      D,1             ;begin with first sector of next track
       INC     C               ;track=track+1
;       save register state, and change tracks
       CALL    SETTRK          ;track address set from register c
       JP      LOAD1           ;for another sector
;       end of load operation, set parameters and go to cp/m
GOCPM:
       LD      A,0C3H          ;c3 is a jmp instruction
       LD      (0),A           ;for jmp to wboot
       LD      HL,WBOOTE       ;wboot entry point
       LD      (1),HL          ;set address field for jmp at 0
;
       LD      (5),A           ;for jmp to bdos
       LD      HL,BDOS         ;bdos entry point
       LD      (6),HL          ;address field of jump at 5 to bdos
;
       LD      BC,80H          ;default dma address is 80h
       CALL    SETDMA
;
       EI                      ;enable the interrupt system
       LD      A,(CDISK)       ;get current disk number
       LD      C,A             ;send to the ccp
       JP      CCP             ;go to cp/m for further processing
;
;
;       simple i/o handlers
;
;       console status, return 0ffh if character ready, 00h if not
;
CONST:  IN      A,(CONSTA)      ;get console status
       RET
;
;       console character into register a
;
CONIN:  IN      A,(CONDAT)      ;get character from console
       RET
;
;       console character output from register c
;
CONOUT: LD      A,C             ;get to accumulator
       OUT     (CONDAT),A      ;send character to console
       RET
;
;       list character from register c
;
LIST:   LD      A,C             ;character to register a
       OUT     (PRTDAT),A
       RET
;
;       return list status (0 if not ready, 1 if ready)
;
LISTST: IN      A,(PRTSTA)
       RET
;
;       punch character from register c
;
PUNCH:  LD      A,C             ;character to register a
       OUT     (AUXDAT),A
       RET
;
;       read character into register a from reader device
;
READER: IN      A,(AUXDAT)
       RET
;
;
;       i/o drivers for the disk follow
;
;       move to the track 00 position of current drive
;       translate this call into a settrk call with parameter 00
;
HOME:   LD      C,0             ;select track 0
       JP      SETTRK          ;we will move to 00 on first read/write
;
;       select disk given by register C
;
SELDSK: LD      HL,0000H        ;error return code
       LD      A,C
       CP      4               ;must be between 0 and 3
       RET     NC              ;no carry if 4,5,...
;       disk number is in the proper range
;       compute proper disk parameter header address
       OUT     (FDCD),A        ;selekt disk drive
       LD      L,A             ;L=disk number 0,1,2,3
       ADD     HL,HL           ;*2
       ADD     HL,HL           ;*4
       ADD     HL,HL           ;*8
       ADD     HL,HL           ;*16 (size of each header)
       LD      DE,DPBASE
       ADD     HL,DE           ;HL=.dpbase(diskno*16)
       RET
;
;       set track given by register c
;
SETTRK: LD      A,C
       OUT     (FDCT),A
       RET
;
;       set sector given by register c
;
SETSEC: LD      A,C
       OUT     (FDCS),A
       RET
;
;       translate the sector given by BC using the
;       translate table given by DE
;
SECTRAN:
       EX      DE,HL           ;HL=.trans
       ADD     HL,BC           ;HL=.trans(sector)
       LD      L,(HL)          ;L = trans(sector)
       LD      H,0             ;HL= trans(sector)
       RET                     ;with value in HL
;
;       set dma address given by registers b and c
;
SETDMA: LD      A,C             ;low order address
       OUT     (DMAL),A
       LD      A,B             ;high order address
       OUT     (DMAH),A        ;in dma
       RET
;
;       perform read operation
;
READ:   XOR     A               ;read command -> A
       JP      WAITIO          ;to perform the actual i/o
;
;       perform a write operation
;
WRITE:  LD      A,1             ;write command -> A
;
;       enter here from read and write to perform the actual i/o
;       operation.  return a 00h in register a if the operation completes
;       properly, and 01h if an error occurs during the read or write
;
;       in this case, we have saved the disk number in 'diskno' (0-3)
;                       the track number in 'track' (0-76)
;                       the sector number in 'sector' (1-26)
;                       the dma address in 'dmaad' (0-65535)
;
WAITIO: OUT     (FDCOP),A       ;start i/o operation
       IN      A,(FDCST)       ;status of i/o operation -> A
       RET
;
;       the remainder of the CBIOS is reserved uninitialized
;       data area, and does not need to be a part of the
;       system memory image (the space must be available,
;       however, between "begdat" and "enddat").
;
;       scratch ram area for BDOS use
;
BEGDAT  EQU     $               ;beginning of data area
DIRBF:  DEFS    128             ;scratch directory area
ALL00:  DEFS    31              ;allocation vector 0
ALL01:  DEFS    31              ;allocation vector 1
ALL02:  DEFS    31              ;allocation vector 2
ALL03:  DEFS    31              ;allocation vector 3
CHK00:  DEFS    16              ;check vector 0
CHK01:  DEFS    16              ;check vector 1
CHK02:  DEFS    16              ;check vector 2
CHK03:  DEFS    16              ;check vector 3
;
ENDDAT  EQU     $               ;end of data area
DATSIZ  EQU     $-BEGDAT        ;size of data area
       END