;==============================================================
; BOOTMOD - BOOT MODULE FOR THE POLEX MONITOR,
;     POLYMORPHIC SYSTEMS POLY-88
;
; INSTALLS IN THE 3RD ROM SOCKET ON THE CPU BOARD TO PROVIDE A
; DISK BOOT COMMAND (BO) THAT WILL BOOT FROM A MICROPOLIS,
; ALTAIR, TARBELL, OR SINGLE OR DOUBLE DENSITY NORTH STAR DISK
; CONTROLLER.
;
; IN THE POLY-88, THE ALTAIR DISK CONTROLLER IS AT 80H-82H
; INSTEAD OF 08H-0AH TO AVOID CONFLICT WITH THE POLY-88
; REAL-TIME-CLOCK AT 08H. THE TARBELL DISK CONTROLLER IS AT
; E8H INSTEAD OF THE MORE COMMON F8H TO AVOID CONFLICT WITH THE
; POLY-88 KEYBOARD PORT. THE MICROPOLIS CONTROLLER IS AT FC00
; INSTEAD OF F800 TO AVOID CONFLICT WITH VIDEO RAM. THE BOOT PROM
; OF THE SINGLE DENSITY NORTH STAR CONTROLLER IS EXPECTED AT
; E900 AND THE DOUBLE DENSITY BOOT PROM IS EXPECTED AT E800.
;
; FOR THE ALTAIR BOOT LOADER, INSTEAD OF LOADING BOOT SECTORS
; INTO RAM AT ZERO LIKE THE ORIGINAL ALTAIR BOOT PROM, THE BOOT
; SECTORS ARE LOADED INTO RAM AT 100H TO PRESERVE THE PAGE ZERO
; INTERRUPT VECTORS USED BY POLEX. ALSO UNLIKE THE ORIGINAL BOOT
; PROM, JUST TWO BOOT SECTORS ARE READ (SECTORS 0 AND 2) 
; INDEPENDENT OF THE "LAST ADDRESS WORD" IN THE SECTOR HEADER.
;
; FOR THE TARBELL BOOT LOADER, INSTEAD OF LOADING THE BOOT SECTOR
; INTO RAM AT ZERO LIKE THE ORIGINAL TARBELL BOOT PROM, THE BOOT
; SECTOR IS LOADED INTO RAM AT 100H TO PRESERVE THE PAGE ZERO
; INTERRUPT VECTORS USED BY POLEX.
;
; BOOT MODULE COMMANDS:
;
;   BO [A|M|NS|ND|P|T]	BOOT FROM DISK. OPTIONAL PARAMETER FORCES
;			A PARTICULAR CONTROLLER:
;			    A = ALTAIR
;			    M = MICROPOLIS
;			    NS = NORTH STAR SINGLE DENSITY FDC
;			    ND = NORTH STAR DOUBLE DENSITY FDC
;			    P = POLYMORPHIC SINGLE DENSITY FDC
;			    T = TARBELL SINGLE DENSITY FDC
;
; WITH NO PARAMETERS, THE BOOT COMMAND ATTEMPTS TO AUTOMATICALLY
; DETERMINE THE TYPE OF DISK CONTROLLER AS FOLLOWS:
;
; 	IF MICROPOLIS PROM DETECTED
;	   JUMP TO FC00
;	ELSE IF DD NORTH STAR PROM DETECTED
;	   DISABLE INTERRUPTS THEN JUMP TO E800
;	ELSE IF SD NORTH STAR PROM DETECTED
;	   DISABLE INTERRUPTS THEN JUMP TO E900
;	ELSE IF TARBELL FDC PORT DETECTED (port E8h)
;	   EXECUTE TARBELL BOOT CODE IN THIS PROM
;       ELSE IF SD POLYMORPHIC FDC DETECTED (port 20h)
;	   EXECUTE POLY SDC BOOT CODE IN THIS PROM
;	ELSE
;	   EXECUTE ALTAIR BOOT CODE IN THIS PROM
;
; FORMATTED TO ASSEMBLE WITH DIGITAL RESEARCH'S ASM.
;==============================================================
; REVISION HISTORY
; VERS. 1.0 BY M. DOUGLAS  24 JUL 2016
;   CREATED FROM MICMOD WRITTEN BY M. EBERHARD
; VERS. 1.1 BY M. DOUGLAS  15 NOV 2017
;   ADD TARBELL SINGLE DENSITY CONTROLLER
; VERS. 1.2 BY M. DOUGLAS  16 FEB 2018
;   ADD POLYMORPHIC SINGLE DENSITY FDC
;==============================================================

;*******
; ASCII
;*******
CR	EQU	0DH
CURSOR	EQU	0FFH		;CURSOR IN VIDEO RAM

;*********************************************
; MEMORY LOCATION BEFORE & AFTER CODE IS MOVED
;*********************************************
ROMLOC	EQU	0800H		;BOOTMOD ROM ADDRESS
RAMLOC	EQU	0F000H		;LOCATION AFTER MOVED
PBASE	EQU	0F400H		;POLEX RAM BASE
ROF	EQU	RAMLOC-ROMLOC	;RAM OFFSET

;***********************************
; POLEX ENTRY POINTS
;***********************************
PXINIT	EQU	PBASE		;RESTART POLEX
PXCSTA	EQU	PBASE+03H	;CONSOLE STATUS
PXCIN	EQU	PBASE+06H	;CONSOLE INTO A
PXCOUT	EQU	PBASE+09H	;CONSOLE OUT C. A=C ON RET
PXSSTA	EQU	PBASE+0CH	;SERIAL PORT STATUS
PXSIN	EQU	PBASE+0FH	;SERIAL PORT IN
PXSOUT	EQU	PBASE+12H	;SERIAL PORT OUT
PXSOST	EQU	PBASE+15H	;SERIAL PORT OUTPUT STATUS
PXBAUD	EQU	PBASE+18H	;SET SERIAL PORT BAUD RATE
PXIHEX	EQU	PBASE+1BH	;SCAN PAST BLANKS, GET HEX VAL
				;HL=VALUE
				;DE ADVANCED PAST CHR
				;Z SET, CARRY CLEAR IF VALUE
				;CARRY SET IF NO VALUE FOUND
PXOHEX	EQU	PBASE+1EH	;PRINT A AS HEX ON THE CONSOLE
PXWARM	EQU	PBASE+21H	;WARM-START RE-ENTRY
PXCIN2	EQU	PBASE+24H	;TEST KBD STATUS, RZ IF NO CHR
				;IF CHR, ABORT IF CABKEY
				;ELSE RET WITH CHR IN A, NZ
PXGLIN	EQU	PBASE+27H	;GET A LINE OF USER INPUT
PTIMER  EQU	0F7FDh		;LSB of 60hz real time counter

;***********************************
; CONTROLLER EQUATES
;***********************************
MICRP	EQU	0FC00H		;BOOT PROM ON MICROPOLIS
NSSDP	EQU	0E900H		;NORTH STAR SINGLE DENSITY PROM
NSDDP	EQU	0E800H		;NORTH STAR DOUBLE DENSITY PROM	

;*********************************************
; ALTAIR EQUATES
;*********************************************
DMAADR	EQU	0100H		;LOAD ADDRESS
BUFFER	EQU	0200H		;SECTOR BUFFER
RETRIES	EQU	10H		;MAX NUMBER OF RETRIES

; DISK CONTROLLER INPUT EQUATES

DSTAT	EQU	80H		;DISK STATUS REGISTER
MOVEOK	EQU		02H	;HEAD MOVEMENT ALLOWED
ENABLD	EQU		08H	;0 MEANS CONTROLLER IS ENABLED
TRACK0	EQU		40H	;TRACK 0 DETECT

DSECTR	EQU	81H		;DISK SECTOR NUMBER
SECTRU	EQU		01H	;SECTOR VALUE IS TRUE

DDATA	EQU	82H		;READ DATA HERE

;DISK CONTROLLER OUTPUT EQUATES

DSLCT	EQU	80H		;SELECT DISK NO.
DISABL	EQU		80H	;DSLCT VALUE TO DISABLE ALL

DCTRL	EQU	81H		;DISK CONTROL REG
STEPIN	EQU		01H	;STEP IN
STEPOT	EQU		02H	;STEP OUT
HDLOAD	EQU		04H	;HEAD LOAD
HDUNLD	EQU		08H	;HEAD UNLOAD
INTEN	EQU		10H	;INTERRUPT ENABLE
INTDE	EQU		20H	;INTERRUPT DISABLE
HCS	EQU		40H	;HEAD CURRENT SWITCH
WRITEN	EQU		80H	;WRITE ENABLE

;*********************************************
; TARBELL EQUATES
;*********************************************
TBLOAD	EQU	0100H		;LOAD ADDRESS

TBBASE	EQU	0E8H		;BASE ADDRESS OF CONTROLLER
TBCOM	EQU	TBBASE+0	;DISK COMMAND PORT
TBSTAT	EQU	TBBASE+0	;DISK STATUS PORT
TBTRK	EQU	TBBASE+1	;DISK TRACK PORT
TBSEC	EQU	TBBASE+2	;DISK SECTOR PORT
TBDATA	EQU	TBBASE+3	;DISK DATA PORT
TBSEL	EQU	TBBASE+4	;DISK SELECT PORT
TBWAIT	EQU	TBBASE+4	;WAIT FOR DISK PORT

TBRESET	EQU	0D0H		;RESET 1771 TO IDLE
TBRSTOR	EQU	002H		;RESTORE TO TRACK ZERO, 10MS STEP
TBREAD	EQU	08CH		;LOAD HEAD, DELAY, READ
TBTRK0	EQU	004H		;TRACK ZERO BIT IN STATUS

;*********************************************
; POLY SDC EQUATES
;*********************************************
PLOAD	equ	0100h	;load address
MTOSTRT	equ	-60 AND 0FFh	;1 sec motor start count up @ 60hz
SYNC	equ	0E6h	;disk sync character

DCBASE	equ	20h	;disk controller base I/O address

DSKSCTL	equ	DCBASE+00h	;6852 Status/Control Register 1
			;6852 status bits:
RDA	equ	01h	;RX data available
TDRA	equ	02h	;TX data register available
DCDN	equ	04h	;active-low DCD
CTSN	equ	08h	;active-low CTS
TUF	equ	10h	;transmit underflow
RXOVRN	equ	20h	;receiver overrun
PE	equ	40h	;parity error
IRQ	equ	80h	;interrupt request

			;Control Register 1 Commands
CR2SEL	equ	0Bh	;select Control Reg 2, reset things
CR3SEL	equ	40h	;select Control Reg 3
SYNSEL	equ	80h	;select sync-code register
IDLE68	equ	0C0h	;no command, select data register
RSET68	equ	0CBh	;reset things, select data register

			;Control Register 2 Commands
CR2RES	equ	18h	;8 bits, no parity (see data sheet)

			;Control Register 2 Commands
CR3RES	equ	0Ch	;clear status, internal sync,
			;..2-sync chr mode

DSKSDAT	equ	DCBASE+01h	;6852 Data/Control

DSKSEL	equ	DCBASE+08h	;8255 Port A (output), drive select
			;<1:0> = Unit number
			;0 means no unit selected

DSKSEC	equ	DCBASE+09h	;8255 Port B (input), sector register
SECMASK	equ	0Fh	;bits 3:0 are the current sector
PTRACK0	equ	10h	;bit 4 is track 0 detect
WPROT	equ	20h	;bit 5 is write protect

DSKCTL	equ	DCBASE+0Ah	;8255 Port C write label
DSKSTAT	equ	DCBASE+0Ah	;8255 Port C read label
			;<0> = Sector pulse interrupt
			;      (Mode 1 interrupt output)
			;<1> = Sector pulse
			;      (Mode 1 input buffer full)
			;<2> = Sector int enable (internal write)
			;      (Mode 1 data strobe input)
			;<3> = Motor On
			;<4> = Enable Write
			;<5> = Enable Read
			;<6> = Direction Select
			;<7> = Step

SIMASK	equ	01h	;sector interrupt mask
SPMASK	equ	02h	;sector pulse mask
MOTMASK	equ	08h	;motor on/off mask

DSKCMD	equ	DCBASE+0Bh	;8255 Control 
			;Disk control commands:
SINTOFF	equ	04h	;Port C:2 (sector interrupt) off
SINTON	equ	05h	;Port C:2 (sector interrupt) on
MOTOFF	equ	06h	;Port C:3 (spindle motor) off
MOTON	equ	07h	;Port C:3 (spindle motor) on
WRDIS	equ	08h	;Port C:4 (enable write) off
WREN	equ	09h	;Port C:4 (enable write) on
RDDIS	equ	0Ah	;Port C:5 (enable read) off
RDEN	equ	0Bh	;Port C:5 (enable read) on
STPOUT	equ	0Ch	;Port C:6 low  (step out)
STPIN	equ	0Dh	;Port C:6 high (step in)
UNSTEP	equ	0Eh	;Port C:7 low (cancel step pulse)
STEP	equ	0Fh	;Port C:7 high (step)
INIT82	equ	86h	;initialize 8255:
			;  Group A: Mode 0, output
			;  Group B: Mode 1, input
			;  Port C set up for output on all bits
			;    but some bits forced to input for
			;    mode 1 handshake signals

;==============================================================
; START OF ROM CODE
; GETS CALLED BY POLEX DURING INITIALIZATION.
; COPIES THIS CODE FROM PROM INTO RAM AT F000.
; ON EXIT:
;   HL=ADDRESS OF COMMAND PROCESSOR IN RAM
;==============================================================
	ORG	RAMLOC		;NOTE ORG NOT AT PROM ADDRESS
	
	JMP	INIT-ROF	;MOVE PROM CODE TO RAM AND EXIT

;**********************************************
; CMDPROC - BOOTMOD COMMAND PROCESSOR
;
; SCAN FOR MODULE COMMANDS, AND EXECUTE THEM
;
; ON ENTRY:
;   DE POINTS TO POTENTIAL COMMAND
; ON EXIT:
;   Z CLEAR MEANS SUCCESSFUL COMMAND EXECUTION
;   Z SET MEANS NO COMMAND FOUND
;   SOME ERRORS EXIT DIRECTLY TO PXWARM
;**********************************************
CMDPROC: LDAX	D		;1ST CHR
	MOV	B,A		;1ST CHR INTO B
	INX	D
	LDAX	D		;2ND CHR
	MOV	C,A		;2ND CHR INTO C
	DCX	D		;FIX DE IN CASE NO MATCH

	LXI	H,COMTAB-1	;POINT TO COMMAND TABLE

;SEARCH THROUGH TABLE AT HL FOR A 2-CHR MATCH OF BC

NCOM:	INX	H		;GO TO NEXT ENTRY
	MOV	A,M		;1ST TABLE CHR
	ORA	A		;END OF TABLE?
	RZ			;Y: NOT A MODULE CMD

	INX	H
	CMP	B		;MATCH?
	MOV	A,M		;2ND TABLE CHR
	INX	H
	JNZ	NCOM		;N: KEEP LOOKING
	SUB	C		;MATCH?
	JNZ	NCOM		;N: KEEP LOOKING

;GOT A MATCH. COMPUTE ROUTINE ADDRESS AND PUT IT ON STACK
;A=0
;(HL)= COMMAND ADDRESS OFFSET
;DE=ADDRESS OF START OF 2-LETTER COMMAND

	MOV	L,M		;GET ADDRESS OFFSET
	MOV	H,A		;H=A=0

	LXI	B,CMDBAS	;BASE OF COMMAND ROUTINES
	DAD	B		;HL=ADDRESS OF ROUTINE

	PUSH	H		;GO ADDRESS ON STACK

	INX	D		;SKIP PAST 2-LETTER COMMAND
	INX	D

	RET			;GO TO COMMAND ROUTINE:
				;DE POINTS TO 1ST CHR
				;..AFTER COMMAND

;**************************************************
;INIT BANNER ROUTINE - MUST BE IN RAM TO USE CLPRNT
;**************************************************
BANNER:	CALL	CILPRT
	DB	'Disk Boot Module 1.','2'+80H
	RET

;==========================
; BASE ADDRESS FOR COMMANDS
;==========================
CMDBAS	EQU	$

;***COMMAND ROUTINE*********************
; BO (BOOT FROM DISK DRIVE)
;***************************************

BOOTCMD: CALL	SKIPB		;SKIP BLANKS BEFORE PARAMETER
	JZ	AUTOBT		;NO PARAMETER, DO AUTO DETECT

;USE COMMAND LINE PARAMETER TO DETERMINE WHICH CONTROLLER 
;   TO BOOT FROM

	ANI	5FH		;LOWER CASE TO UPPER CASE, STRIP MSB

	CPI	'A'		;ALTAIR REQUESTED?
	JZ	BALT		;YES, BOOT ALTAIR

	CPI	'M'		;MICROPOLIS REQUESTED?
	JZ	BMICR		;YES, BOOT MICROPOLIS
	
	CPI	'P'		;POLYMORPHIC REQUESTED?
	JZ	BTPOLY		;YES, BOOT POLY FDC

	CPI	'T'		;TARBELL REQUESTED?
	JZ	BTBELL		;YES, BOOT TARBELL

	CPI	'N'		;A NORTH STAR CONTROLLER REQUESTED?
	JNZ	BADPRM		;NO, INVALID PARAMETER SPECIFIED

;NORTH STAR CONTROLLER SPECIFIED. GET 2ND CHARACTER TO DETERMINE
;  WHETHER SINGLE OR DOUBLE DENSITY CONTROLLER.

	INX	D		;DE->2ND CHAR OF PARAMETER
	LDAX	D		;A=2ND CHARACTER OF PARAMETER
	ANI	5FH		;LOWER CASE TO UPPER CASE, STRIP MSB

	CPI	'D'		;DOUBLE DENSITY CONTROLLER?
	JZ	BNSDD		;YES

	CPI	'S'		;SINGLE DENSITY CONTROLLER?
	JZ	BNSSD		;YES

;BADPRM - DISPLAY INVALID BOOT DEVICE ERROR AND RETURN TO POLEX

BADPRM:	CALL	CILPRT		;CR FIRST
	DB	'Invalid boot devic','e'+80H

	JMP	PXWARM		;BACK TO MAIN

;---------------------------------------------------------------
; AUTOBT - AUTOMATICALLY DETERMINE THE TYPE OF CONTROLLER
;    TO BOOT FROM
;---------------------------------------------------------------
AUTOBT:	LXI	H,MICRP		;HL->MICROPOLIS PROM
	CALL	CHKPROM
	JNZ	BMICR		;BOOT MICROPOLIS DISK	

	LXI	H,NSDDP		;HL->NORTH STAR DOUBLE DENSITY PROM
	CALL	CHKPROM
	JNZ	BNSDD		;BOOT NORTH STAR DOUBLE DENSITY

	LXI	H,NSSDP		;HL->NORTH STAR SINGLE DENSITY PROM
	CALL	CHKPROM
	JNZ	BNSSD		;BOOT NORTH STAR SINGLE DENSITY
	
	IN	DSKSCTL		;READ POLY SERIAL CONTROL REGISTER
	INR	A		;TEST FOR FF (NOT THERE)
	JNZ	BTPOLY		;CONTROLLER PRESENT, GO BOOT POLY
	
	IN	TBSTAT		;READ TARBELL STATUS REGISTER
	INR	A		;TEST FOR FF (NOT THERE)
	JNZ	BTBELL		;CONTROLLER PRESENT, GO BOOT TARBELL
				;OTHERWISE, FALL THROUGH TO BOOT ALTAIR
;---------------------------------------------------------------
; BOOT ALTAIR DISK
;---------------------------------------------------------------
BALT:	CALL	DSPBT		;DISPLAY 'BOOTING'
	CALL	ILPRNT
	DB	'Altair..','.'+80H

;WAIT FOR CONTROLLER TO BE ENABLED (INCLUDING DOOR SHUT)

WAITEN:	XRA	A		;SELECT DRIVE 0
	OUT	DSLCT

	IN	DSTAT
	ANI	ENABLD		;THIS BIT 0 WHEN ENABLED
	JNZ	WAITEN		;KEEP WAITING

;LOAD THE HEAD

	DI
	MVI	A,HDLOAD
	OUT	DCTRL
	JMP	CHK00		;ALREADY AT TRACK 00?

; STEP OUT ONE TRACK AT A TIME TO SEEK TRACK 00

SEEK00:	IN	DSTAT		;WAIT FOR HEAD MOVEMENT ALLOWED
	ANI	MOVEOK
	JNZ	SEEK00		;KEEP WAITING

	MVI	A,STEPOT	;STEP OUT A TRACK
	OUT	DCTRL

CHK00:	IN	DSTAT		;ARE WE AT TRACK 0 ALREADY?
	ANI	TRACK0
	JNZ	SEEK00		;NO: KEEP STEPPING

	LXI	D,DMAADR	;PUT DISK DATA STARTING HERE

; READ 256 BYTE BOOT LOADER FROM SECTOR 0 AND 2. STORE AT (DE)

NXTRAC:	MVI	B,0		;INITIAL SECTOR NUMBER

NXTSEC:	MVI	A,RETRIES	;INITIALIZE RETRY COUNTER

;READ ONE SECTOR INTO THE BUFFER
; ON ENTRY:
;    A = RETRIES
;    B = SECTOR NUMBER
;   DE = MEMORY ADDRESS FOR SECTOR DATA

RDSECT:	PUSH	PSW		;SAVE RETRY COUNTER
	PUSH	D		;SAVE DEST ADDRESS FOR RETRY
	PUSH	B		;SAVE B=SECTOR NUMBER
	PUSH	D		;SAVE DEST ADDRESS FOR MOVE
	LXI	D,8086H		;E=BYTES PER SECTOR, D=JUNK
	LXI	H,BUFFER	;HL POINTS TO DISK BUFFER

; WAIT UNTIL THE RIGHT SECTOR

WSECT:	IN	DSECTR		;READ SECTOR STATUS
	RAR			;TEST BIT 0 = SECTRU
	JC	WSECT		;SPIN UNTIL SECTOR IS READY

	ANI	1FH		;GET THE SECTOR NUMBER
	CMP	B		;IS IT THE ONE WE WANT?
	JNZ	WSECT		;NO: WAIT FOR OUR SECTOR

; READ SECTOR FROM THE DISK AND PUT INTO THE RAM BUFFER. THIS
; READING IS DONE 2 BYTES AT A TIME FOR SPEED

DWAIT:	IN	DSTAT		;DATA READY?
	ORA	A		;MSB CLEARED WHEN READY
	JM	DWAIT

	IN	DDATA		;GET A BYTE OF DISK DATA
	MOV	M,A		;PUT IT IN MEMORY
	INX	H		;BUMP MEMORY POINTER
	DCR	E		;BUMP & TEST BYTE COUNT		.
	JZ	SECDON		;QUIT IF BYTE COUNT = 0

	NOP			;TIMING
	DCR	E		;DECREMENT COUNT FOR NEXT BYTE

	IN	DDATA		;GET ANOTHER BYTE OF DATA
	MOV	M,A		;PUT IT IN MEMORY
	INX	H		;BUMP MEMORY POINTER
	JNZ	DWAIT		;AGAIN, UNLESS BYTE COUNT = 0

; MOVE THE DATA TO ITS FINAL LOCATION, AND CHECK THE CHECKSUM AS
; WE MOVE THE DATA. ALSO VERIFY THE MEMORY WRITE.

SECDON:	POP	H		;RECOVER DEST ADDRESS
	LXI	D,BUFFER+3	;START OF DATA PAYLOAD
	LXI	B,0080H		;B=INITIAL CHECKSUM,
				;C=DATA BYTES/SECTOR

MOVLUP:	LDAX	D		;GET A BYTE FROM THE BUFFER		.
	MOV	M,A		;WRITE IT TO RAM
	CMP	M		;SUCCESSFUL WRITE TO RAM?
	JNZ	MEMERR		;NO: GIVE UP

	ADD	B		;COMPUTE CHECKSUM		.
	MOV	B,A

	INX	D		;BUMP SOURCE POINTER
	INX	H		;BUMP DESTINATION POINTER
	DCR	C		;NEXT BYTE
	JNZ	MOVLUP		;KEEP GOING THROUGH 128 BYTES


	LDAX	D		;THE NEXT BYTE MUST BE FF
	CPI	0FFH
	JNZ	RDDONE		;OTHERWISE IT'S A BAD READ

	INX	D		;THE NEXT BYTE IS THE CHECKSUM
	LDAX	D
	CMP	B		;MATCH THE COMPUTED CHECKSUM?

RDDONE:	POP	B		;RESTORE SECTOR NUMBER
	XCHG			;PUT MEMORY ADDRESS INTO DE
				;AND BUFFER POINTER INTO HL
	JNZ	BADSEC		;CHECKSUM ERROR OR MISSING FF?

	POP	PSW		;CHUCK OLD SECTOR NUMBER
	POP	PSW		;CHUCK OLD RAM ADDRESS

; SET UP FOR NEXT SECTOR
; THE DISK HAS A 2:1 SECTOR INTERLEAVE - 
; FIRST READ ALL THE EVEN SECTORS, THEN READ ALL THE ODD SECTORS

	INR	B		;BUMP SECTOR NUMBER BY 2
	INR	B
	MOV	A,B		;SECTOR 0 AND 2 READ ALREADY?
	CPI	4
	JC	NXTSEC		;NO: KEEP READING
	
	MVI	A,DISABL	;DISABLE DISKS
	OUT	DSLCT
	JMP	DMAADR		;GO EXECUTE WHAT WE LOADED

; SECTOR ERROR:
; RESTORE TO BEGINNING OF SECTOR AND SEE IF WE CAN RETRY

BADSEC:	POP	D		;RESTORE MEMORY ADDRESS
	POP	PSW		;GET RETRY COUNTER
	DCR	A		;BUMP RETRY COUNTER
	JNZ	RDSECT		;NOT ZERO: TRY AGAIN

; FALL INTO SECERR

;***ERROR EXIT**********
; SECTOR ERROR AND MEMORY ERROR EXIT
; PRINT ERROR MESSAGE EXIT VIA POLEX WARM START
;***********************
SECERR:	CALL	CILPRT		;CR FIRST
	DB	'Chksum Er','r'+80H
	JMP	ERREX

MEMERR:	CALL	CILPRT		;CR FIRST
	DB	'Mem Er','r'+80H
				;FALL INTO ERREX

ERREX:	MVI	A,DISABL	;DESELECT ALL DISKS (ALTAIR)
	OUT	DSLCT

	JMP	PXWARM		;RETURN TO POLEX

;---------------------------------------------------------------
; BOOT FROM MICROPOLIS CONTROLLER
;---------------------------------------------------------------
BMICR:	CALL	DSPBT		;DISPLAY 'BOOTING'
	CALL	ILPRNT
	DB	'Micropolis..','.'+80H
	DI
	JMP	MICRP		;JUMP TO MICROPOLIS BOOT ROM

;---------------------------------------------------------------
; BOOT FROM NORTH STAR SINGLE DENSITY CONTROLLER
;---------------------------------------------------------------
BNSSD:	CALL	DSPBT		;DISPLAY 'BOOTING'
	CALL	ILPRNT
	DB	'North Star SD..','.'+80H
	DI			;REQUIRED, NOT DONE BY PROM
	JMP	NSSDP		;JUMP TO NORTH STAR BOOT ROM

;---------------------------------------------------------------
; BOOT FROM NORTH STAR DOUBLE DENSITY CONTROLLER
;---------------------------------------------------------------
BNSDD:	CALL	DSPBT		;DISPLAY 'BOOTING'
	CALL	ILPRNT
	DB	'North Star DD..','.'+80H
	DI			;REQUIRED, NOT DONE BY PROM
	JMP	NSDDP		;JUMP TO NORTH STAR BOOT ROM

;---------------------------------------------------------------
; BOOT FROM TARBELL SINGLE DENSITY CONTROLLER
;---------------------------------------------------------------
BTBELL:	CALL	DSPBT		;DISPLAY 'BOOTING'
	CALL	ILPRNT
	DB	'Tarbell..','.'+80H

	MVI	A,0F2H		;SELECT DRIVE ZERO (DRV NUM INVERTED)
	OUT	TBSEL

	MVI	A,TBRESET	;CLEAR ANY PENDING COMMAND	
	OUT	TBCOM
	LHLD	HOME		;16 CYCLE "NOP" FOR TIMING

HOME:	IN	TBSTAT		;GET STATUS
	RRC
	JC	HOME		;WAIT FOR NOT BUSY COMPLETION

	MVI	A,TBRSTOR	;ISSUE RESTORE CMD 
	OUT	TBCOM
	LHLD	HOME		;16 CYCLE "NOP" FOR TIMING

	IN	TBWAIT		;WAIT FOR COMPLETION
	ORA	A		;SET FLAGS
	JM	DRQER		;DRIVE NOT READY

	IN	TBSTAT		;GET DISK STATUS
	ANI	TBTRK0		;TEST FOR TRACK 0
	JZ	TK0ER		;RESTORE FAILED

	MVI	A,1		;SPECIFY SECTOR 1
	OUT	TBSEC

	MVI	A,TBREAD	;ISSUE READ COMMAND
	OUT	TBCOM
	LHLD	HOME		;16 CYCLE "NOP" FOR TIMING
	
	DI			;DISABLE INTERRUPTS
	LXI	H,TBLOAD	;HL->START OF CP/M BOOT LOADER

; SECTOR READ LOOP

RLOOP:	IN	TBWAIT		;WAIT FOR DISK CONTROLLER
	ORA	A		;SET FLAGS
	JP	RDONE		;MSB SET WHEN READ IS FINISHED

	IN	TBDATA		;GET DATA FORM DISK
	MOV	M,A		;MOVE IT INTO MEMORY
	INX	H		;INCREMENT MEMORY POINTER
	JMP	RLOOP		;GO GET NEXT BYTE

RDONE:	IN	TBSTAT		;GET STATUS OF THE READ
	ORA	A		;CHECK FOR ERRORS
	JZ	TBLOAD		;IF NO ERROR, EXECUTE CODE JUST READ

; ERROR PROCESSING 

	MOV	B,A		;SAVE ERROR IN B
	CALL	CILPRT		;PRINT ERROR MESSAGE
	DB	'LOAD ERR','='+80H
	JMP	TBERR		;EXIT WITH ERROR

DRQER:	MOV	B,A		;SAVE ERROR IN B
	CALL	CILPRT
	DB	'CMD ERR','='+80H
	JMP	TBERR		;EXIT WITH ERROR

TK0ER:	MOV	B,A		;SAVE ERROR IN B
	CALL	CILPRT
	DB	'SEEK ERR','='+80H
				;FALL INTO TBERR
TBERR:	CALL	BYTEO		;DISPLAY ERROR FROM B
	JMP	PXWARM		;BACK TO POLEX COMMAND PROMPT

; CONVERT BYTE IN B TO HEX AND DISPLAY

BYTEO:	MOV	A,B		;A=BYTE TO CONVERT
	CALL	BYTO1		;DISPLAY MSN

	MOV	A,B		;A=BYTE TO CONVERT
	CALL	BYTO2		;DISPLAY LSN

	MVI	C,CR		;TRAILING CR
	JMP	PXCOUT		;DISPLAY AND EXIT

BYTO1:	RRC
	RRC
	RRC
	RRC

BYTO2:	ANI	0FH
	CPI	0AH
	JM	BYTO3
	ADI	7

BYTO3:	ADI	30H
	MOV	C,A		;C=CHARACTER TO DISPLAY
	JMP	PXCOUT		;DISPLAY AND EXIT
	
;---------------------------------------------------------------
; BOOT FROM POLYMORPHIC SINGLE DENSITY CONTROLLER
;---------------------------------------------------------------
BTPOLY:	call	DSPBT		;display 'Booting'
	call	ILPRNT
	db	'Poly SD..','.'+80H
	
; Initialize the 6852 serial adapter and 8852 on the controller

	mvi	a,CR2SEL	;Clear sync, Tx, Rx, select Control Reg 2
	out	DSKSCTL		;6852 Control Reg 1

	mvi	a,CR2RES	;8 bits, no parity
	out	DSKSDAT		;To 68B52 Control register 2

	mvi	a,CR3SEL	;Select Control Register 3
	out	DSKSCTL		;6852 Control Reg 1

	mvi	a,CR3RES	;Clear status, internal sync,
				;..2-sync chr mode
	out	DSKSDAT		;To 6852 Control Register 3

	mvi	a,SYNSEL	;Select sync-code register
	out	DSKSCTL		;6852 Control Reg 1

	mvi	a,SYNC		;Sync code
	out	DSKSDAT		;To 6852 sync code

	mvi	a,IDLE68	;Select Transmit Data Fifo
	out	DSKSCTL		;6852 Control Reg 1

; Select drive 1, start motor, wait for startup delay

	mvi	a,1		;select drive 1
	out	DSKSEL

	mvi	a,MOTON		;turn on motor
	out	DSKCMD
	
	mvi	a,MTOSTRT	;1 sec count up timer at 60hz
	sta	PTIMER		;starting count-up value in 60hz RTC
	mvi	b,0		;B=sector pulse counter
	
PSLOOP:	in	DSKSEC		;clear sector interrupt

PWSEC1:	lda	PTIMER		;timeout expire?
	ora	a		
	jz	PMTO		;yes, motor timeout done
	
	in	DSKSTAT		;look for sector pulse
	ani	SPMASK		;sector pulse set?
	jz	PWSEC1		;no, keep waiting

	inr	b		;count the sector pulse
	jmp	PSLOOP
	
PMTO:	mvi	a,8		;more than 8 sectors seen?
	cmp	b		;compare 8 - sector count
	jc	PSEEK0		;drive ready, do restore to track 0
	
	call	CILPRT		;print error message
	db	'No Dis','k'+80H
	jmp	PERR		;exit

; Restore to track zero. First, step in 3 tracks.

PSEEK0:	mvi	a,STPIN		;step direction is inward
	out	DSKCMD
	
	mvi	c,3		;C=step in 3 tracks

PSTPI:	call	DOSTEP		;issue step and then delay
	dcr	c		;decrement step count
	jnz	PSTPI

; Now step out looking for track zero

	mvi	a,STPOUT	;step direction is outward
	out	DSKCMD
	
	mvi	c,46		;try up to 45 steps (35 tracks)
	jmp	PCHK0		;jump to test at end of loop

PSTPO:	call	DOSTEP		;issue step and then delay
	
PCHK0:	in	DSKSEC		;reach track zero?
	ani	PTRACK0
	jnz	PTRK0		;yes
	
	dcr	c		;else, decrement step count
	jnz	PSTPO		;and keep stepping
	
	call	CILPRT		;print error message
	DB	'Seek Er','r'+80H
	jmp	PERR		;exit

; On track 0, now wait for and read sector zero

PTRK0:	mvi	a,5		;A=read retry counter

PRETRY:	push	psw		;save retry counter on stack

PWSEC2:	in	DSKSTAT		;look for sector pulse
	ani	SPMASK		;sector pulse set?
	jz	PWSEC2		;no, keep waiting

	in	DSKSEC		;look for sector nine, one
	ani	SECMASK		;   sector before sector 0
	cpi	9
	jnz	PWSEC2		;keep waiting for sector 9
	
	mvi	a,RSET68	;reset sync, Tx, Rx
	out	DSKSCTL		;6852 Control Register 1
	mvi	a,IDLE68	;..and release them
	out	DSKSCTL		;6852 Control Register 1 

	mvi	a,RDEN		;enable read on controller
	out	DSKCMD		;8255 Port C BSR mode control	

	lxi	d,PLOAD		;DE->address to put boot sector	
	lxi	h,0		;HL=initial checksum
	
; Wait for next sector pulse which will be sector 0

	di			;disable interrupts

PWSEC3:	in	DSKSTAT		;look for sector pulse
	ani	SPMASK		;sector pulse set?
	jz	PWSEC3		;no, keep waiting

; Wait up to 1.75ms for start of data

	mvi	c,83		;C=timeout counter (83*39 cycles)

PWDATA:	dcr	c		;(5) timeout?
	jz	PRDERR		;(10) yes, preamble error

	in	DSKSCTL		;(10) A=6852 status
	rrc			;(4) data byte present?
	jnc	PWDATA		;(10) wait for data

; Read and verify sector and track from disk

	in	DSKSDAT		;read disk data 1st byte (sector)
	cpi	80h		;sector zero?
	jnz	PRDERR		;no, have error
		
	in	DSKSDAT		;read disk data 2nd byte (track)
	ora	a		;track zero?
	jnz	PRDERR		;no, have error	
	
	mvi	a,128		;128 pairs of bytes/sector

; PRLOOP - Read sector data into memory at (DE)

PRLOOP:	push	psw		;save loop counter on stack

PRDWT1	in	DSKSCTL		;A=6852 status
	rrc			;data available?
	jnc	PRDWT1		;no, keep waiting
	
	in	DSKSDAT		;read disk data even byte
	mov	c,a		;C=even byte
	stax	d		;write to buffer
	inx	d
	
	in	DSKSDAT		;read disk data odd byte
	mov	b,a		;B=odd byte
	stax	d		;write to buffer
	inx	d

	dad	b		;update 16-bit checksum

	pop	psw		;A=byte pair count
	dcr	a		;decrement loop count
	jnz	PRLOOP		;loop through sector data

; Read and verify 1's complement 16-bit checksum from disk

PRDWT2	in	DSKSCTL		;A=6852 status
	rrc			;data available?
	jnc	PRDWT2		;no, keep waiting

	in	DSKSDAT		;read low byte of checksum
	mov	c,a		;C=checksum low byte
	in	DSKSDAT		;read high byte of checksum
	mov	b,a		;BC=1's complement checksum from disk

	mvi	a,RDDIS		;disable the read
	out	DSKCMD
		
	dad	b		;add BC to positive computed checksum
	inx	h		;plus 1 should give zero

	mov	a,h		;test for HL=0
	ora	l
	jz	PLOAD		;sector is good, jump to loaded sector
	
; PRDERR - Error while reading a sector. Decrement the retry counter
;     and try the read again.
	
PRDERR:	mvi	a,RDDIS		;disable the read
	out	DSKCMD
	
	ei			;re-enable interrupts
	pop	psw		;decrement retry counter
	dcr	a	
	jnz	PRETRY		;try the read again
	
	call	CILPRT		;print error message
	DB	'Read Er','r'+80H

; PERR - Poly boot error. Shut down the drive and exit to Polex.

PERR:	xra	a		;deselect drive
	out	DSKSEL

	mvi	a,MOTOFF	;turn off motor
	out	DSKCMD
	
	jmp	PXWARM		;return to Polex
	
;*****SUBROUTINE****************************************
; DOSTEP - Issue step to Poly SDC and then wait 40ms 
;    step time. Interrupts can occur during this
;    loop so the actual delay may be slightly longer.
; ON ENTRY
;    Step direction should already be set
; ON EXIT
;    A=0
;    Zero true
;    Clobbers A,D
;*******************************************************
DOSTEP:	mvi	a,STEP		;set step bit
	out	DSKCMD
	dcr	a		;clear step bit
	out	DSKCMD

	mvi	a,40		;delay step time of 40ms 

DLYMS:	mvi	d,(1843/15)+1	;15 cycles in the loop below

DLYLP:	dcr	d		;decrement inner loop counter
	jnz	DLYLP

	dcr	a		;decrement ms counter
	jnz	DLYMS

	ret
	
;*****SUBROUTINE****************************************
; CHKPROM - SEE IF BOOT PROM IS PRESENT
; ON ENTRY:
;    HL->ADDRESS TO CHECK FOR BOOT PROM
; ON EXIT:
;    Z SET IF BOOT PROM NOT PRESENT
;    Z CLEAR IF BOOT PROM PRESENT
;    CLOBBERS THE POSSIBLE RAM BYTE AT (HL)
;*******************************************************
CHKPROM: MOV	A,M		;SEE IF CONTROLLER PRESENT
	INR	A		;TEST FOR FF
	RZ			;CONTROLLER NOT THERE

	MOV	M,A		;SEE IF PROM OR RAM
	CMP	M		;READ AND COMPARE

	RET			;ZERO STATUS IF RAM, NON-ZERO IF PROM

;***SUBROUTINE************************
; SCAN PAST BLANK POSITIONS LOOKING
; FOR THE FIRST NON-BLANK CHARACTER
; ON EXIT:
;   Z SET IF NONE FOUND
;   Z CLEAR IF CHR FOUND
;   A=CHARACTER VALUE IF FOUND
;--> ENTRY IS AT SKIPB <--
;*************************************
SBLOOP:	INX	D		;NEXT SCAN ADDRESS

SKIPB:	LDAX	D		;GET NEXT CHARACTER

	CPI	' '+80H		;VIDEO RAM HAS MSB SET
	JZ	SBLOOP		;KEEP SKIPPING SPACES

	CPI	CURSOR		;END OF INPUT? (CURSOR=FF)
	RET			;Z SET IF NO PARAMETER 

;*****SUBROUTINE******************************
; DISPLAY 'BOOTING '
; A TRASHED, ALL OTHER REGISTERS PRESERVED
;*********************************************
DSPBT:	CALL	CILPRT
	DB	'Booting',' '+80h
	RET
	
;*****SUBROUTINE******************************
; PRINT CR THEN INLINE MESSAGE
; CALLS TO CILPRT ARE FOLLOWED BY THE STRING
; THE LAST STRING BYTE HAS ITS MSB SET
; A TRASHED, ALL OTHER REGISTERS PRESERVED
;*********************************************
CILPRT:	CALL	ILPRNT
	DB	CR+80H

; FALL INTO ILPRNT

;*****SUBROUTINE******************************
; PRINT INLINE MESSAGE
; CALLS TO ILPRNT ARE FOLLOWED BY THE STRING
; THE LAST STRING BYTE HAS ITS MSB SET
; Z CLEARED ON EXIT
; A TRASHED, ALL OTHER REGISTERS PRESERVED
;*********************************************
ILPRNT:	XTHL			;SAVE HL, GET MSG ADDR
	PUSH	B

IPLOOP:	MOV	A,M		;LOOP THROUGH MESSAGE
	ANI	7FH		;STRIP END-MARKER
	MOV	C,A		;PXCOUT WANTS CHR IN C
	CALL	PXCOUT
	CMP	M		;END?
	INX	H
	JZ	IPLOOP

	POP	B
	XTHL			;RESTORE HL
				;..GET RET ADDRESS
	RET

;********************************
; COMMAND TABLE
; NOTE THAT ASCII CHRS STORED IN
; VIDEO MEMORY HAVE THEIR MSB SET
;********************************
COMTAB:	DB	'B'+80H,'O'+80H	;BOOT FROM A DISK
	DB	BOOTCMD-CMDBAS
	DB	0		;END OF TABLE MARK

;*** END OF PROGRAM ***

RAMEND:				;END OF COPIED CODE


;==============================================================
; INIT - MOVE MODULE CODE FROM PROM INTO RAM AT F000
; GETS CALLED BY POLEX DURING INITIALIZATION.
; ON EXIT:
;   HL=ADDRESS OF COMMAND PROCESSOR IN RAM
;==============================================================

; INITIALIZE A POSSIBLE POLYMORPHIC SDC TO STOP THE MOTOR.

INIT:	MVI	A,INIT82	;Group A: Mode 0, output
				;Group B: Mode 1, input
				;Port C set up for output
	OUT	DSKCMD		;init the 8255

	XRA	A		;clear all outputs
	OUT	DSKSEL		;8255 Port A (drive select register)
	OUT	DSKCTL		;8255 Port C (drive control/command)	
 
;MOVE CODE INTO RAM, BACKWARDS, SO HL IS RIGHT AT THE END
	
MOVRAM:	LXI	B,RAMEND-CMDPROC	;B=COUNT OF BYTES TO MOVE
	LXI	D,RAMEND-ROF		;DE->END OF SOURCE IN PROM
	LXI	H,RAMEND		;HL->END OF DEST IN RAM

MOVER:	DCX	D
	DCX	H

	LDAX	D
	MOV	M,A

	DCX	B			;BUMP COUNT
	MOV	A,B
	ORA	C			;DONE?
	JNZ	MOVER-ROF	

;PRINT BANNER AND RETURN TO POLEX

	JMP	BANNER			;MUST BE IN RAM CODE

	END



