;**************************************************************
; CCS 2422 Cold-start loader                                  *
;                                                             *
;REVISION HISTORY                                             *
; 1.00  21 February 2018  M. Eberhard                         *
;   Created                                                   *
;**************************************************************
;
; 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

;---------------
;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
;---------------------------------------
BCTRL	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

;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

;Control register 2 (BCTRL) output bits

BCEJECT	equ	00000100b	;remote eject control (PerSci)
BCFSEEK	equ	00010000b	;fast seek: voicecoil drives
BCSIDE0	equ	01000000b	;0=side 1, 1=side 0
BCROMEN	equ	10000000b	;enable onboard ROM

;Composite BCTRL values used to select disk side
; Note: ROM is actually disabled via the page register
; Note: fast seek disabled VIA hardware jumper

SIDE0	equ	BCROMEN+BCFSEEK+BCSIDE0
SIDE1	equ	BCROMEN+BCFSEEK

;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

;Status register 1 (DFLAG) input bits

DFINTRQ	equ	00000001b	;WD1793 INTRQ signal, active high
DFDS1	equ	00000010b	;DS1 in control reg 1
DFDS2	equ	00000100b	;DS2 in control reg 1
DFDS3	equ	00001000b	;DS3 in control reg 1
DFDS4	equ	00010000b	;DS4 in control reg 1
DFSHLD	equ	00100000b	;WD1793 HLD signal, active high
DFAUTOB	equ	01000000b	;autoboot jumper, 0=autoboot
DFSDRQ	equ	10000000b	;WD1793 DRQ signal, 1=ready

;Status reg 2 (BSTAT) input bits

BTRK0	equ	00000001b	;when on track 0: 1=8" disk
BMINI	equ	00000010b	;-DCTRL reg bit: 1=Minidisk
BWPRT	equ	00000100b	;0=disk is write-protected
BSIDE0	equ	00001000b	;SIDE0 bit in control register 2
BINDEX	equ	00010000b	;index signal, active low
BDDEN	equ	00100000b	;DDEN signal in control register 1
BSIDED	equ	01000000b	;0=selected disk is 2-sided

;*****************
;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

	call	CILPRT
	db	'Put boot disk in A and press Return',':'+80h

	mvi	a,CONIN		;Address offset
	call	GOBIOS		;get input chr
	ani	7Fh		;strip parity, ignore nulls

	cpi	CTRLC
	jz	WBOOT	

	cpi	CR		;wait for CR
	jnz	REBOOT

	mvi	a,DCDS1+DCMDMO
	out	DCTRL		;assume minidisk for now
	mov	d,a		;remember DCTRL state

	mvi	a,SIDE0		;set side 0
	out	BCTRL

	call	CILPRT
	db	'Booting',CR,LF+80h

	mvi	a,CCRSTOR	;issue command to WD1793
	out	DCMMD

;Wait for command to complete, with 2 second timeout

	lxi	h,799		;(2 second timeout)

REBO1:	dcx	h		;(6)
	mov	a,h		;(4)
	ora	l		;(4) clears carry too
	jz	DNRERR		;(10)timeout? drive not ready

REBO2:	dcr	c		;(4)
	jz	REBO1		;(10)inner loop: 9984 cycles

	in	DFLAG		;(11)wait for completion
	rar			;(4)1793 INTRQ flag?
	jnc	REBO2		;(10)y: done waiting, carry set

;Get controller status, test for not ready & errors

	in	DSTAT		;disk status
	xri	STRACK0		;so 0 means track 0
	ani	SNOTRDY+STRACK0+SBUSY

	jm	DNRERR		;drive not ready?
	jnz	BOTERR		;Other error during restore?

;Check for 8" disk

	in	BSTAT		;read BSTAT while on track 0
	ani	BTRK0		;1 if 8" disk while on track 0
	jz	REBO3

	mvi	a,DCDS1+DCMDMO+DCMAXI
	out	DCTRL		;set up for 8: disk
	mov	d,a		;remember DCTRL state
REBO3:

;Set up to read sector 1

	mvi	c,MAXTRYS+1	;sector retry counter

RBRTRY:	mvi	b,SECSIZ/4	;BPS/4

	mvi	a,BOTSEC	;Sector to boot
	out	DSCTR		;WD1793 sector port

	lxi	h,COMBUF	;Put boot sector here
	push	h		;execution address too

;Read sector 1 into RAM at hl

	mov	a,d		;(13)select
	ori	DCAUTOW		;(7)enable auto-wait
	out	DCTRL		;(11)output DCTRL value

	mvi	a,CCRDSEC	;read command
	out	DCMMD		;(11)disk command port

	dcx	h	;(6)undo initial inx

RDSLUP:	in	DDATA	;(11) 28 cycles in-to-in

	inx	h	;(6)
	mov	m,a	;(7)
	inx	h	;(6)
	in	DDATA	;(11) 30 cycles in-to-in

	mov	m,a	;(7)
	inx	h	;(6)
	in	DDATA	;(11) 24 cycles in-to-in

	mov	m,a	;(7)
	inx	h	;(6)
	dcr	b	;(4) set/clear Z for end of loop
	in	DDATA	;(11) 28 cycles in-to-in

	mov	m,a	;(7)
	jnz	RDSLUP	;(10)

;Check for all the possible errors during a sector read
;or a read address. b=0 here.

REBO4:	in	DFLAG			;(11)Wait for interupt bit
	rrc				;(4)1793 DFINTRQ flag set?
	jc	REBO6		 	;(10)y: command complete

	dcr	b			;(5) retry counter
	jnz	REBO4			;(10) keep waiting

;Controller failure: read/write/ID command did not complete

REBO5:	pop	h			;chuck address
	jmp	DNRERR

;Read completed. Check for valid read

REBO6:	in	DSTAT			;disk status
	ani	SNOTRDY+SRNFER+SCRCERR+SLOSTD+SRCTYF

	rz				;good read: go run it...

	jm	REBO5			;drive not ready?

;----------------------------------
;Read fail. Try again if possible.
;Give user feedback about retries.
;  c=retry count-down
;----------------------------------
	pop	h		;chuck address

	mvi	a,MAXTRYS+1
	cmp	c		;first retry?
	jnz	RBR1		;n: just print dot

	call	CILPRT
	db	'Retryin','g'+80h

RBR1:	call	ILPRNT
	db	'.'+80h

	dcr	c
	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
	push	b

IPLOOP:	mov	a,m
	ani	7Fh		;strip end marker
	call	PRINTA		;print byte
	mov	a,m		;end?
	inx	h		;Next byte
	rlc			;msb set?
	jnc	IPLOOP		;Do all bytes of msg

	pop	b
	xthl			;Restore hl,
				;..get return address
	ret			;with carry set

;***Subroutine*******************
; Print character in a on console
; trashes psw,bc
;********************************
PRINTA:	mov	c,a		;value to c for PRINTC
	mvi	a,CONOUT

; fall into GOBIOS

;***Subroutine**********************
; Go call a BIOS driver directly
; On Entry:
;   c=value for BIOS routine, if any
;   a = BIOS call address offset
; On Return:
;   psw as BIOS left it
;Trashes bc
;***********************************
GOBIOS:	push	h
	push	d

	call	DOBIOS

	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

;***Stack Space****
	ds	16
STACK:

	end
