;**************************************************************
; Cromemco 16FDC Cold-start loader                            *
;                                                             *
;REVISION HISTORY                                             *
; 1.00  13 May 2021  M. Eberhard                              *
;   Created from CCS REBOOT ver. 1.00                         *
;**************************************************************
;
; Written to assemble with Digital Research's ASM.COM.
;**************************************************************
VERSION	equ	1	;Major revision number
VERSMIN	equ	00	;Minor revision number

FALSE	equ	0
TRUE	equ	not FALSE
;---------------------------
;Debug option includes PAHEX
;---------------------------
DEBUG	equ	FALSE
;---------------
;Program Equates
;---------------
MAXTRYS	equ	16
SECSIZ	equ	128		;bytes/sector
BOTSEC	equ	1		;Boot sector

;--------------
; ASCII equates
;--------------
CTRLC	equ	3
CR	equ	0Dh
LF	equ	0Ah

;-------------------------
;Page 0 reserved locations
;-------------------------
WBOOT	equ	0		;CP/M warm boot entry
WBOOTA	equ	WBOOT+1		;address of BIOS
CDISK	equ	WBOOT+4		;current disk
BDOS	equ	WBOOT+5		;BDOS Entry Point

COMBUF	equ	80h		;Run boot sector here
TPA	equ	100h		;transient programming area

;---------------------------------------------------------
;BIOS entry points, relative to the base address in WBOOTA
;---------------------------------------------------------
CONST	equ	06h		;Get console status in a
				;(0=not ready, ffh=ready)
CONIN	equ	09h		;Get console chr into a
CONOUT	equ	0Ch		;Write c to console output

;---------------------------------------
;CCS 2422 floppy disk controller equates
;---------------------------------------
ADCMD	equ	4		;Control port 2
BSTAT	equ	4		;status port 2
DSTAT	equ	30h		;disk status port
DCMMD	equ	DSTAT		;disk command port
DTRCK	equ	DSTAT+1		;track port
DSCTR	equ	DSTAT+2		;sector port
DDATA	equ	DSTAT+3		;data port
DFLAG	equ	DSTAT+4		;disk flag port
DCTRL	equ	DSTAT+4		;disk control port

;WD1793 commands

RSTRCM	equ	00000000b	;(I) restore
SEEKCM	equ	00010000b	;(I) seek
STEPCM	equ	00100000b	;(I) step
STPICM	equ	01000000b	;(I) step-in
STPOCM	equ	01100000b	;(I) step-out
RSECCM	equ	10000000b	;(II) read sector
WSECCM	equ	10100000b	;(II) write sector
RADRCM	equ	11000000b	;(III) read address
RTRKCM	equ	11100000b	;(III) read track
WTRKCM	equ	11110000b	;(III) write track
FINTCM	equ	11010000b	;(IV) force inT

;WD1793 command options (1st letter matches WD1793 data sheet)

RS03MS	equ	00000000b	;(I) 3 mS step rate
RS06MS	equ	00000001b	;(I) 6 mS step rate
RS10MS	equ	00000010b	;(I) 10 mS step rate
RS15MS	equ	00000011b	;(I) 15 mS step rate
VERTRK	equ	00000100b	;(I) verify on destination track
HDLOAD	equ	00001000b	;(I) head load
UPDTTR	equ	00010000b	;(I) update track reg
F1SCMP	equ	00000010b	;(II)enable side compare
F2SCS1	equ	00001000b	;(II)side compare, side 1
MULTCM	equ	00010000b	;(II) read/write multiple
ED15MS	equ	00000100b	;(II & III) settling delay

;WD1793 step rates (note: Minidisk mode halves the clock rate):
;
;  value  Minidisk  8" disk
; RS03MS    6 mS     3 MS
; RS06MS   12 mS     6 MS
; RS10MS   20 mS    10 MS
; RS15MS   30 mS    15 MS

STEP5	equ	RS10MS		;SA400L Minidisk rate=20 mS
STEP8	equ	RS10MS		;SA800 8" 1-sided rate=8 mS

;Composite WD1793 commands used in this program

CCRSTOR	equ	RSTRCM+HDLOAD+STEP5	;restore command

				;These assume side 0:
CCRDSEC	equ	RSECCM+F1SCMP	;read sector command
CCRDADD	equ	RADRCM+ED15MS	;read addr, settling delay

;Control register 1 (DCTRL) output bits

DCDS1	equ	00000001b	;drive select 1
DCDS2	equ	00000010b	;drive select 2
DCDS3	equ	00000100b	;drive select 3
DCDS4	equ	00001000b	;drive select 4
DCMAXI	equ	00010000b	;0=Minidisk, 1=8" disk
DCMDMO	equ	00100000b	;Minidisk motor on
DCDDEN	equ	01000000b	;double density enable
DCAUTOW	equ	10000000b	;autowait

;Auxiliary Disk Command (ADCMD) output bits
;(All bits are active-low)

ADSIDE0	equ	00000010b	;0=side 1, 1=side 0
ADCTRL0	equ	00000100b	;-Control Out (test)
ADRSTOR	equ	00001000b	;-Restore (PerSci)
ADFSEKN	equ	00010000b	;-Fast Seek (PerSci)
ADDSO	equ	00100000b	;-Drive Select Override (test)
ADEJECT	equ	01000000b	;-Remote eject (PerSci)

;Composite ADCMD commands

SIDE0	equ	0FFh
SIDE1	equ	SIDE0 xor ADSIDE0

;WD1793 status (DSTAT) after a command

SBUSY	equ	00000001b	;(All) busy
SCRCERR	equ	00001000b	;(All) CRC error
SWRPROT	equ	01000000b	;(All) write protect
SNOTRDY	equ	10000000b	;(All) drive not ready
SINDEX	equ	00000010b	;(I) index
STRACK0	equ	00000100b	;(I) track 0
SSEEKER	equ	00010000b	;(I) seek error
SHEADLD	equ	00100000b	;(I) head loaded
SWFAULT	EQU	00100000B	;(Write cmds) write fault
SDATRQ	equ	00000010b	;(II & III) data request state
SLOSTD	equ	00000100b	;(II & III) lost data error
SRNFER	equ	00010000b	;(II & III) record not found
SRCTYF	equ	00100000b	;(II & III) record type fault
SWFALT	equ	00100000b	;(II & III) Write fault

;Disk Flag (DFLAG) input bits

DEOJ	equ	00000001b	;WD1793 INTRQ signal, active high
DAWTO	equ	00000010b	;Autowait Timeout
DMOTO	equ	00000100b	;Motor Timeout
DMOTON	equ	00001000b	;Motor On
DINHIN	equ	00010000b	;-Inhibit Init
DFSHLD	equ	00100000b	;WD1793 HLD, active high
DFAUTOB	equ	01000000b	;autoboot jumper, 0=autoboot
DFSDRQ	equ	10000000b	;WD1793 DRQ signal, 1=ready

;Auxiliary Status Register (ASTAT) input bits

BSW8	equ	00000001b	;Switch 8
BSW7	equ	00000010b	;Switch 7
BSW6	equ	00000100b	;Switch 6
BSW5	equ	00001000b	;Switch 5
BSIP	equ	01000000b	;(PerSci) Seek in Progress
BRTC	equ	10000000b	;512 m,Sec reat-time clock

;----------------------------
;Some Z80 instruction opcodes
;----------------------------
JR	equ	18h
JRNZ	equ	20h
JRZ	equ	28h
JRNC	equ	30h
JRC	equ	38h
DJNZ	equ	10h

;2-byte instructions have bytes reversed for dw

BIT7A	equ	07FCBh		;BIT 7,A
BIT4D	equ	062CBh		;BIT 4,D
BIT6D	equ	072CBh		;BIT 6,D
SET4D	equ	0E2CBh		;SET 4,D
SET6D	equ	0F2CBh		;SET 6,D
RES4D	equ	0A2CBh		;RES 4,D
SET4E	equ	0E3CBh		;SET 4,E
INI	equ	0A2EDh
INIR	equ	0b2EDh
;*****************
;Beginning of Code
;*****************
	org TPA

	lxi	sp,STACK		;local stack in COMBUF

;********************************
;Cold Boot from A
;Read track 0, sector 1 into RAM
;at COMBUF, and execute it there.
;********************************
REBOOT:	xra	a		;deselect drive to open door
	out	DCTRL

 if DEBUG
	call	CILPRT
	db	'(Debug',')'+80h
 endif ;DEBUG

	call	CILPRT
	db	'Put boot disk in A and press Return',':'+80h

	mvi	a,CONIN		;Address offset
	call	DOBIOS		;get input chr
	ani	7Fh		;strip parity, ignore nulls

	cpi	CTRLC
	jz	WBOOT	

	cpi	CR		;wait for CR
	db	JRNZ,(REBOOT-$-1) xor 0FF00h

	mvi	a,SIDE0		;set side 0
	out	ADCMD

	call	CILPRT
	db	'Booting',CR,LF+80h

	mvi	e,MAXTRYS+1	;retry counter

	mvi	a,DCDS1+DCMDMO+DCMAXI
				;assume maxidisk for now

RBRTRY:	out	DCTRL		;select drive
	mov	d,a		;remember DCTRL state

;Give PerSci 299 time to deassert Ready

	mvi	b,0
RDYWAT:	db	DJNZ,(RDYWAT-$-1) xor 0FF00h

;restore to track 0

	mvi	a,CCRSTOR	;issue command to WD1793
	out	DCMMD

;Wait for restore to complete, with 4 second timeout

	lxi	h,1800		;(>4 second timeout)

REBO1:	dcx	h		;(6)
	mov	a,h		;(4)
	ora	l		;(4) clears carry too
	db	JRZ,DNRERR-$-1	;(7)timeout? drive not ready

REBO2:	dcr	b		;(4)
	db	JRZ,(REBO1-$-1) xor 0FF00h
				;(11)inner loop: 9216 cycles

	in	DFLAG		;(11)wait for completion
	rar			;(4)1793 EOJ flag?
	db	JRNC,(REBO2-$-1) xor 0FF00h
				;(11)y: done waiting, carry set

;Get controller status, test for track 0 and not ready
;If the drive is not ready, try mini
;(minis will always be ready on the 16FDC)

	in	DSTAT		;disk status
	mov	c,a		;save for index test
	ani	SNOTRDY+STRACK0	;clears carry
	rlc			;not-ready bit to carry

	db	JRNC,REBO3-$-1	;not ready?
	mvi	d,DCDS1+DCMDMO	;y: try mini
REBO3:
	db	JRZ,BOTERR-$-1	;error unless track 0 found

;Check for index signal changing. Not ready if none.
;48 cycles per pass

	lxi	h,30000		;about 400 mS

INDLUP:	dcx	h		;(6)
	mov	a,h		;(4)
	ora	l		;(4)
	db	JRZ,DNRERR-$-1

	in	DSTAT		;(11)
	xra	c		;(4)compare
	ani	SINDEX		;(7)..just the index bit
	db	JRZ,(INDLUP-$-1) xor 0FF00h
				;(11)changed?

;Set up to read sector 1

	mvi	a,BOTSEC	;Sector to boot
	out	DSCTR		;WD1793 sector port

	mov	a,d		;select, set data rate
	ori	DCAUTOW		;enable auto-wait
	out	DCTRL		;output DCTRL value

	lxi	b,SECSIZ*256+DDATA
				;b=BPS, c=port address

	lxi	h,COMBUF	;Put boot sector here
	push	h		;execution address too

;Read sector 1 into RAM at hl

	mvi	a,CCRDSEC
	out	DCMMD		;(11)disk command port

RIDLUP:	in	DFLAG		;Auto-wait
	dw	INI		;read byte, write to RAM,
				;..inc address, dec byte count
	db	JRNZ,(RIDLUP-$-1) xor 0FF00h

;wait for read completion

DCWAIT:	in	DFLAG
	rar			;Test WD1793 INTRQ flag
	db	JRNC,(DCWAIT-$-1)  xor 0FF00h
				;INTRQ clears auto-wait

;Check for read errors, execute code if none

	in	DSTAT		;disk status, clear INTRQ
	ani	SNOTRDY+SRNFER+SCRCERR+SLOSTD+SRCTYF
	rz			;no errors: run it

	jm	DNRERR		;drive not ready?

;----------------------------------
;Read fail. Try again if possible.
;Give user feedback about retries.
;  e=retry count-down
;----------------------------------
	pop	h		;chuck address

	mvi	a,MAXTRYS+1
	cmp	e		;first retry?
	db	JRNZ,RBR1-$-1
				;n: just print dot
	call	CILPRT
	db	'Retryin','g'+80h

RBR1:	call	ILPRNT
	db	'.'+80h

	mov	a,d		;try other data rate
	xri	DCMAXI

	dcr	e
	jnz	RBRTRY

;---Fail-------------------------
;Fatal error when trying to boot.
;Ask for another disk.
;--------------------------------
BOTERR:	call	CILPRT
	db	'Boot faile','d'+80h
	jmp	REBOOT

;---Fail--------------
;Drive not ready.
;Ask for another disk.
;---------------------
DNRERR:	call	CILPRT		;returns with carry set
	db	'Drive A not read','y'+80h

	jmp	REBOOT

;***Subroutine****************************************
; Print CR, LF, then In-line Message
;  The call to CILPRT is followed by a message string.
;  The last message chr has its msb set.
; On Exit:
;   Trashes psw
;*****************************************************
CILPRT:	call	ILPRNT
	db	CR,LF+80H

; fall into ILPRNT

;***Subroutine****************************************
; Print In-line Message
;  The call to ILPRNT is followed by a message string.
;  The last message chr has its msb set.
; On Exit:
;   Trashes psw
;*****************************************************
ILPRNT:	xthl			;Save hl, get msg addr

IPLOOP:	mov	a,m
	ani	7Fh		;strip end marker
	call	PRINTA		;print byte
	mov	a,m		;end?
	inx	h		;Next byte
	rlc			;msb set?
	db	JRNC,(IPLOOP-$-1) xor 0FF00h
				;Do all bytes of msg

	xthl			;Restore hl,
				;..get return address
	ret			;with carry set

;***Subroutine*******************
; Print character in a on console
; trashes psw
;********************************
PRINTA:	push	h
	push	d
	push	b
	mov	c,a		;value to c for PRINTC
	mvi	a,CONOUT
	call	DOBIOS
	pop	b
	pop	d
	pop	h
	ret

;***Subroutine**********************
; Go call a BIOS driver directly
; On Entry:
;   c=value for BIOS routine, if any
;   a = BIOS call address offset
; On Return:
;   all regs as BIOS left them
;   (26 cycles + BIOS time)
;***********************************
DOBIOS:	lhld	WBOOTA		;(16)get BIOS base address
	mov	l,a		;(5)a has jump vector

	pchl			;(5) 'call' BIOS routine

 if DEBUG

;***DEBUG Subroutine****
;Print a as 2 hex digits
;All registers preserved
;***********************
PAHEX:	push psw
	rrc
	rrc
	rrc
	rrc
	call	PNHEX
	pop	psw
	push	psw
	call	PNHEX
	pop	psw
	ret

;***DEBUG Subroutine*********
;Print low nibble of a in hex
;Trashes psw
;****************************
PNHEX:	ani	0Fh		;get nibble
	adi	'0'		;Make ASCII
	cpi	'9'+1		;A-F?
	db	JRC,(PRINTA-$-1) xor 0FF00h
	adi	'A'-'9'-1

	db	JR,(PRINTA-$-1) xor 0FF00h

endif ;DEBUG

;***Stack Space****
	ds	24
STACK:

	end
