;================================================================
; North-Star MDS-A disk boot ROM disassembly (early version PROM)
;
; Disassembled by Martin Eberhard and Mike Douglas (borrowing
; from an unattributed disassembly called NSBOOT.LST 
; 'found on the internet'.)
;
; This early version of the boot ROM differs from the later
; version in two areas:
;
;    1) This version loops forever attempting to boot when
; 	a disk error occurs. The newer version tries 10 times
;	and then dies in a loop.
;
;    2) This version uses a sector timeout constant of 46h
; 	versus 8Ch used in the newer version. This timeout
;	is too short for a 4mhz processor. 
;
; The Northstar MDS-A disk controller works by reading and
; writing to memory addresses - no INs or OUTs occur. Commands
; are encoded into the low byte of the address. This onboard
; ROM is hard-coded for execution at E900. There is ROM space on
; the board for another copy of this code, which may be ORG'd at
; a different address.
;
; This code is written to work especially with NSDOS. As such,
; it references code in the RAM page at 2000h. When booting,
; this RAM page gets overwritten with the bootstrap code.
;
; Assembles with Digital Reasearch's ASM. The resulting assembly
; matches the older version ROMS found on an MDS-A controller.
;================================================================

;RAM allocation (Set up for NSDOS)

RAMBAS	equ	2000h		;RAM base address
DRVTAB	equ	RAMBAS		;Current track for each drive (1-3)
LODADR	equ	RAMBAS		;Load address
LASTDR	equ	RAMBAS+3	;Last used drive
EXEADR	equ	RAMBAS+4	;Execution address
DOSVER	equ	RAMBAS+7	;DOS verify operation
DOSWRT	equ	RAMBAS+0Ah	;DOS write operation

STACK	equ	RAMBAS+114h	;Initial stack

;Disk controller memory space allocation

CTLBAS	equ	0E800h		;Disk controller base address
ROMADR	equ	CTLBAS+100h	;Standard ROM address
CTLADR	equ	CTLBAS+300h	;Disk Control base address

;Controller commands (are at memory addresses)

CTLDS0	equ	CTLADR+00h	;Select drive 0
CTLDS1	equ	CTLADR+01h	;Select drive 1
CTLDS2	equ	CTLADR+02h	;Select drive 2
CTLDS3	equ	CTLADR+03h	;Select drive 3
CTLWRT	equ	CTLADR+04h	;Initiate write
CTLSTC	equ	CTLADR+08h	;Clear track-step f-f
CTLSTS	equ	CTLADR+09h	;Set track-step f-f
CTLDI	equ	CTLADR+0Ch	;Disarm interrupts
CTLEI	equ	CTLADR+0Dh	;Arm interrupts
CTLNOP	equ	CTLADR+10h	;No operation
CTLRSF	equ	CTLADR+14h	;Reset sector flag
CTLRES	equ	CTLADR+18h	;Reset controller
CTLSTO	equ	CTLADR+1Ch	;Select step out
CTLSTI	equ	CTLADR+1Dh	;Select step in
CTLBST	equ	CTLADR+20h	;Read B status
CTLRD	equ	CTLADR+40h	;Read Data
CTLMO	equ	CTLADR+80h	;Drive motors on command

;A-Status Bits

SATR0	equ	01h		;Track 0
SAWP	equ	02h		;Write protect
SABDY	equ	04h		;Sync chr found
SAWRT	equ	08h		;Ready for write data
SAMO	equ	10h		;Motor is on
SAWN	equ	40h		;Status was read during window
SASF	equ 	80h		;Sector flag

;B-Status Bits

SBSECT	equ	0Fh		;Sector position mask
SBMO	equ	10h		;Motor is on
SBWN	equ	40h		;Status was read during window
SBSF	equ 	80h		;Sector flag

UNINIT	equ	59h		;Uninitialized RAM value
BDRIVE	equ	1		;Boot drive (Code assumes this is 01.)
BTRACK	equ	0		;Boot track
BSECTR	equ	4		;Boot sector

;**************
;Main Boot Loop
;**************
	org	ROMADR

BOOTLP:	lxi	sp,STACK	;Init stack

	mvi	a,UNINIT	;Invalidate drive 1 track
	sta	DRVTAB
	sta	LASTDR		;Invalidate last drive

	lxi	b,BTRACK*256+BDRIVE   ;Boot track and drive
	mov	a,c		;a=1 (sector count)
	mvi	d,BSECTR	;Boot sector
	mov	e,c		;e = 01 (Read operation)
	lxi	h,LODADR	;Load address
	call	DISKOP		;Do disk operation now
	jnz	BOOTLP		;Failed, try again

	jmp	EXEADR		;Otherwise, launch in RAM

;***Subroutine**************************
; Disk Operation
; On Entry:
;   a = Sector count (bytes)
;   b = track (0-39)
;   c = drive (1-3)
;   d = sector
;   e = 01 Read/00 Write/03 Verify*
;   hl = load address
; * Read is native, Write/Verify jump back to NSDOS
;
; On Exit:
;   Z flag set = success
;   Z flag clear = error
;**************************************
DISKOP:	push	psw		;Block count
	push	h		;RAM address
	push	d		;Sector & Function
	push	b		;Track & drive 

;Turn on motor

MOTON:	mvi	b,CTLADR/256	;Controller access
	lda	CTLMO OR CTLNOP	;Motors on & A status
	ani 	SAMO		;Motor already ON?
	jnz	MOTON2		;Yes, short wait

	mvi	d,32h		;Wait 32 sector times
	call	WSECTS		;Wait
	jmp	MOTON3		;And select drive

MOTON2:	lda	LASTDR		;Get last drive
	cmp	c		;Same drive?
	jz	DRVRDY		;Yes, no need to wait

MOTON3:	ldax	b		;Select new drive
	mov	a,c		;Get drive number
	sta	LASTDR		;Set new flag
	mvi	d,0Dh		;Wait 13 sector tiems
	call	WSECTS

;Look up this drive's current track. Restore if uninitialized.

DRVRDY:	lxi	h,RAMBAS - CTLADR - 1   ;Drive status table
	dad	b		;Drive is in c
	pop	psw		;Desired track (from bc)
	mov	d,a		;D=desired track
	sub	m		;Compute difference
	mov	m,d		;Save new track
	jz	ONTRK		;Already there?

	lxi	h,CTLSTI	;Assume Step IN
	mov	c,a		;Number of tracks to step
	jp	STEP		;Positive diff = step in

	cma			;Absolute value
	inr	a

	mov	c,a		;Number of tracks to step
	lda	CTLNOP		;Read A-status
	ani	SATR0		;At track 0?
	jnz	ONTRK		;Can't step out further

	lxi	h,CTLSTO	;Set Step OUT

;Step C tracks. The direction command is in hl.

STEP:	mov	a,m		;Set direction

STPLUP:	lda	CTLSTS		;Set track-step flipflop
	xthl			;Delay
	xthl			;Delay
	lda  	CTLSTC		;Clear track-step flipflop

	mvi	d,2		;Wait 2 sector times
	call	WSECTS

	lda	CTLNOP		;Read A-status
	ani	SATR0		;At track 0?
	jz	STEP1		;No, keep stepping

	mvi  	c,1		;Force end

STEP1:	dcr	c		;More steps to do?
	jnz	STPLUP		;n: step again

;On desired track, hunt for desired sector

ONTRK:	pop	b		;Sector & function (from de)

HUNT:	call	W1SECT		;Wait one sector time

	lda	CTLBST OR CTLNOP   ;Read B-status
	ani	SBSECT		;Get sector number
	cmp	b		;Desired sector?
	jnz	hunt		;N: wait for it

	pop	h		;RAM address

;If the disk operation is anything but read sector then we can
;assume that NSDOS is intact - and so we let NSDOS handle these.

	dcr	c		;Test disk function
	jm	DOSWRT		;00=Write
	jnz	DOSVER		;<>01=Verify

;Disk function 01: Read one block of data from drive

GETSEC:	mvi	b,46h		;Timeout count (too short for 4mhz)
	lxi	d,CTLRD OR CTLNOP   ;Read data
	mvi	c,0		;Read 256 bytes

;Wait for the sector body

WATSEC:	lda	CTLNOP		;Get A-status
	ani	SABDY		;Wait for Body
	jnz	RDDATA		;Body - ready for data

	dcr	b		;Timeout?
	jnz	WATSEC		;n: keep waiting

;Timeout waiting for sector body

	mvi	a,01h		;Report TIMEOUT error

EREXIT:	pop	b		;Remove sector count
	ora	a		;Set Z flag based on A
	ret

;***Subroutine Continuation*****************
; Read data sector data from disk
; On Entry:
;    c = 0 (byte count=256)
;   de = CTLRD OR CTLNOP (Read Data command)
;   hl = memory address
;   top-of-stack = sector count
;*******************************************
RDDATA:	mov	b,c		;Zero CRC

RDLOOP:	ldax	d		;Read data byte
	mov	m,a		;Write to RAM

	xra	b		;Compute CRC
	rlc
	mov	b,a

	inx	h		;Next RAM address
	dcr	c		;Reduce count
	jnz	RDLOOP		;Read them all

	ldax	d		;Read CRC
	xra	b		;Does it match?
	jz	SECDON		;Y: sector data ok

	mov	a,b		;??
	mvi	a,02		;Report CRC error
	jmp	EREXIT		; And exit

SECDON:	pop	psw		;Sector count
	dcr	a		;Reduce count
	rz			;All done?

	push	psw		;Resave count
	call	W1SECT		;Wait 1 sector time
	jmp	GETSEC		;Go find next sector

;***Subroutine**************************
; Wait 1 sector time
; trashes d
;***************************************
W1SECT:	mvi	d,1		;Single sector time

;Fall into WSECTS

;***Subroutine**************************
; Wait d sector times
; On Entry:
;   d = number of sector times to wait
;***************************************
WSECTS:	lda 	CTLRSF		;Reset sector flag

WSECT:	lda	CTLMO OR CTLNOP	;Read A-status
	ani	SASF		;Sector pulse?
	jz	WSECT		;Wait for it
	dcr	d		;enough sectors?
	rz			;y: done

	jmp	WSECTS		;n: Keep waiting

	end
