;       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
