
; Test Program to interact with the CPM3 type BIOS for the S100Computers IDE/CF card and FPGA_DC + IDE board
; John Monahan San Ramon CA 94583.  
;==============================================================================
;
;	V1.7	3/1/2010	;Removed Z80 Code (so it can be translated to 8086 code later)
;	V2.0	1/23/2011	;Updated to accomidate two CF cards (Master/Slave) & better menu options
;				;Note I still have more work to do with this but what is here seems OK.
;	V2.1	2/5/2011	;Menu driven, and added code to copy & restore disk data from another
;				;"backup" partition on disk
;	V2.2	2/13/2011	;Added Sec++ & Sec--
;	V2.3	2/15/2011	;Re-did drive initilization (Pulse CF cards twice on reset line)
;	V2.4	2/16/2011	;Correct small error for end of drive track check
;	V2.5	3/14/2011	;Added BOOT CPM option, cleaned up some areas.
;	V2.6	3/15/2011	;Re-dis reset line pulse. Correct CPM boot to Track 0 sector 1
;	V2.7	4/26/2011	;Add code for two drive system	
;	V2.8	4/27/2011	;Format sectors with E5's and warn "formatting CURRENT drive".
;	V2.9	3/28/2011	;Fixed initilization hanging if no drive present in drive #0 or #1 positions
;	V2.91	12/23/2017	;Fixed (double shifting) error in ZBITS
;	V3.00	5/31/2019	;Moved stack before buffers, so that V and X commands work properly
;				;Fix issue in verify:  ANA => ORA in VERIFY$AB: at NEXTV:
;				;Fix issue in RAM Clear:  ANA => ORA, and clear A each time
;				;Changed drive selection code into subroutines
;				;Moved call to CLEAR$ID$BUFFER into routine driveid
;				;Display drive letters as they are initialized
;				;Display parameter info for both drives, if present (abort if no drive 0)
;				;Made max sector a stored variable, and treated as max "CPM" sector
;				;Fixed issue in read next sector ("I")  where condition code not set 
;				;Fixed issue where operations stopped one track short (after FE, rather than FF)
;				;Fixed issue where an invalid command char. took an unpredictable flying leap
;				;Added Hole/No Hole mode, and display mode
;				;Added ability to set the maximum track, and display current value
;				;Added report of LBA support in DriveID info printout
;				;Added check for busy in IDEInit - CF cards were not going into LBA mode
;				;Changed BOOTCPM to work properly in no hole mode.
;				;Added / moved checks for IDEWaitnotbusy into wrlba
;	V3.1	2/19/2021	;Whole Disk copy 0FFH sectors/track (0FFH Tracks).  Can set Sec/Track dynamically, Sect# start at 0 not 1
;	V3.11	3/5/2021	;Corrected 1st sector message (O CMD)			
;	V4.0	4/8/2021	;Converted menu etc. to be similar to MYSD.ASM
;
;	V4.3	5/16/2012	;Completely rearranged to work the FPGA_DC Boards (and the old Dual IDE board)
;	V5.0	5/31/2021	;Completely upgraded for FPGA_DC Board
;	V5.1    8/1/2021	;Split program into an IDE & FDC specific program
;	V5.2	9/2/2021	;For Web posting
;	V5.3	9/27/2021	;Fixed A: B: drive selection
;	V5.4    10/2/2021	:Fixed CPM Console Interface
;	V5.5    10/11/2021	:Fixed more CPM Console Interface

;
;	Ports for 8255 chip. Change these to specify where your 8255 is addressed,
;	The first three control which 8255 ports have the control signals,
;	upper and lower data bytes.  The last one (IDEportCtrl), is for mode setting for the
;	8255 to configure its actual I/O ports (A,B & C).  
;
;	Note most drives these days don't use the old Head,Track, Sector terminology. Instead
;	we use "Logical Block Addressing" or LBA. This is what we use below. LBA treats the drive
;	as one continous set of sectors, 0,1,2,3,... 3124,....etc.  However as seen below we need to 
;	convert this LBA to heads,tracks and sectors to be compatible with CPM & MSDOS.

;	NOTE: If you have only one drive/CF card, be sure it is in drive A: The IDE hardware gets 
;	confused if there is only a drive in slot B:

						;Equates for display on Propeller Console IO Board (Used In CPM Debugging mode only)
SCROLL			EQU	01H		;Set scrool direction UP.
LF			EQU	0AH
CR			EQU	0DH
BS			EQU	08H		;Back space (required for sector display)
BELL			EQU	07H
SPACE			EQU	20H
FAST			EQU	10H		;High speed scrool
TAB			EQU	09H		;TAB ACROSS (8 SPACES FOR SD Systems Video Board)
ESC			EQU	1BH
CLEAR			EQU	1CH		;<<< Propeller Video Board, Clear to EOL. (Use 80 spaces if EOL not available

FALSE			EQU	0
TRUE			EQU	NOT FALSE

CPM			EQU	TRUE		;TRUE if output via CPM, FALSE if direct to hardware
DEBUG			EQU	TRUE		;TRUE Show detailed Sector R/W error codes


STATUS$BUS$PORT		EQU	80H		;CMD Communications port with Z80 on FPGA_DC Board
DATA$BUS$PORT		EQU	81H		;CMD data is sent via this port
RELEASE$SD$PORT		EQU	82H		;A pulse out to this port will reset the SET_SD_GATE_PORT on the FPGA Z80
REBOOT$PORT		EQU	83H		;Outputting anything to this port reboots the Z80 on the FPGA_DC Board


;	Commands to the FPGA_DC Board:-
;	Note these commands are a sub-set of the ZFDC and ZFDC_II Board commands
;	Commands to the FPGA_DC Board:-
;	Note these commands are a sub-set of the old ZFDC and ZFDC_II Board commands

CMD$SEL$DRIVE		EQU	5H		;**** (Re)select an already initilized drive
CMD$SET$SIDE		EQU	8H		;**** This will set the active side for a floppy disk (only)
CMD$SET$TRK$SEC		EQU	35H		;Set new current TRACK+SECTOR on current drive (new)
CMD$READ$SECTOR		EQU	10H		;**** Read data from the CURRENT sector (on current track,drive).
CMD$WRITE$SECTOR	EQU	11H		;**** Write data to the CURRENT sector (on current track,drive).
CMD$SET$IDE$MODE	EQU	38H		;Set IDE drive hardware mode on FPG_DC Board (Internal or External)
						;Second Byte 0 = External IDE mode + Interrupt driven info (Default Mode). 
						;Second Byte 1 = External IDE mode + CMD driven info
						;Second Byte 2 = Local FPGA Board IDE mode + CMD driven info
CMD$FAST$COPY$AB	EQU	39H		;Special CMD to fast copy CF Card A:->B: (All within FPGA_DC Board software)

						
INFO$SEL$DRIVE		EQU	40H		;Send info, drive select (not currently used)
INFO$POST$MSG		EQU	41H		;Post a message on the USB Port Terminal

INFO$READ$SECTOR	EQU	42H		;Send info, read sector (Actual Sec Read done externally)
INFO$WRITE$SECTOR	EQU	43H		;Send info, write sector

CMD$BOARD$CONFIG	EQU	52H		;Get FPGA_DC Board hardware configuration

CMD$GET$TIME		EQU	80H		;Get Time and Data from FPGA DC Board RTC
CMD$SET$TIME		EQU	81H		;Set Time and Data on FPGA DC Board RTC

						;Bits 7&6 contain IDE & FDC circuit flags 
						;Bits 1&0 contain IDE Mode (0,1,2) 
						
CMD$FAILED		EQU	0FFH		;CMD failed to exacuite completely
CMD$COMPLETE		EQU	0F0H		;Flag returned by DC board to signal the previous CMD was completed OK
CMD$ABORT		EQU	0E0H		;CMD to abort the current process


;----------------------------------------------------------------------------------------------------------------------------

EXT$IDE$MODE		EQU	00H	;MODE 0, FPGA IDE mode set to external IDE ports etc, [Intererupt driven Hex display/info] 
LOCAL$IDE$MODE		EQU	01H	;MODE 1, FPGA Local IDE CMD mode   [CMD driven Hex display/info]
EXT$IDE$INFO$MODE	EQU	02H	;MODE 2, FPGA External IDE  + Info       [CMD driven Hex display/info]


IDE$BOARD		EQU	0H	;If the disk controller board is the Dual IDE/CF card board
FPGA$BOARD		EQU	1H	;If the controller is the FPGA_DC Board

IDEportA		EQU	030H		;Lower 8 bits of IDE interface (8255)
IDEportB		EQU	031H		;Upper 8 bits of IDE interface
IDEportC		EQU	032H		;Control lines for IDE interface
IDEportCtrl		EQU	033H		;8255 configuration port
IDEDrive		EQU	034H		;Bit 0 = 0 Drive A, =1 Drive B

IDE$CS$1		EQU	00000000B	; CS* select bit for IDE Card drive 1 
IDE$CS$2		EQU	00000001B	; CS* select bit for IDE Card drive 2 

READcfg8255		EQU	10010010b	;Set 8255 IDEportC to output, IDEportA/B input
WRITEcfg8255		EQU	10000000b	;Set all three 8255 ports to output mode

;IDE control lines for use with IDEportC.  

IDEa0line		EQU	01H		;direct from 8255 to IDE interface
IDEa1line		EQU	02H		;direct from 8255 to IDE interface
IDEa2line		EQU	04H		;direct from 8255 to IDE interface
IDEcs0line		EQU	08H		;inverter between 8255 and IDE interface
IDEcs1line		EQU	10H		;inverter between 8255 and IDE interface
IDEwrline		EQU	20H		;inverter between 8255 and IDE interface
IDErdline		EQU	40H		;inverter between 8255 and IDE interface
IDErstline		EQU	80H		;inverter between 8255 and IDE interface

;Symbolic constants for the IDE Drive registers, which makes the
;code more readable than always specifying the address bits

REGdata			EQU	IDEcs0line
REGerr			EQU	IDEcs0line + IDEa0line
REGseccnt		EQU	IDEcs0line + IDEa1line
REGsector		EQU	IDEcs0line + IDEa1line + IDEa0line
REGcylinderLSB		EQU	IDEcs0line + IDEa2line
REGcylinderMSB		EQU	IDEcs0line + IDEa2line + IDEa0line
REGshd			EQU	IDEcs0line + IDEa2line + IDEa1line		;(0EH)
REGcommand		EQU	IDEcs0line + IDEa2line + IDEa1line + IDEa0line	;(0FH)
REGstatus		EQU	IDEcs0line + IDEa2line + IDEa1line + IDEa0line
REGcontrol		EQU	IDEcs1line + IDEa2line + IDEa1line
REGastatus		EQU	IDEcs1line + IDEa2line + IDEa1line + IDEa0line

;IDE Command Constants.  These should never change.

COMMANDrecal		EQU	10H
COMMANDread		EQU	20H
COMMANDwrite		EQU	30H
COMMANDinit		EQU	91H
COMMANDid		EQU	0ECH
COMMANDspindown		EQU	0E0H
COMMANDspinup		EQU	0E1H


; IDE Status Register:
;  bit 7: Busy	1=busy, 0=not busy
;  bit 6: Ready 1=ready for command, 0=not ready yet
;  bit 5: DF	1=fault occured insIDE drive
;  bit 4: DSC	1=seek complete
;  bit 3: DRQ	1=data request ready, 0=not ready to xfer yet
;  bit 2: CORR	1=correctable error occured
;  bit 1: IDX	vendor specific
;  bit 0: ERR	1=error occured


				
SEC$SIZE		EQU	512	;Assume sector size as 512. (Not tested for other sizes)
MAXSEC			EQU	0FFH	;Default maximum "CPM" sector number (61, 3DH sec/track)
					;In "hole" mode, the "CPM" sector becomes the last 8 bits of the LBA
					;This default setting allows "CPM" sectors of 0-3DH				

					;In "no hole" mode, the "CPM" sector becomes the last 6 bits of the LBA
					;and we have 3F sectors/track.
					;
					;History:-
					;Sectors per track for CF card drives such as the Kingston CF 4G for CPM use 0-3DH.
					;This translates to LBA format of 0 to 3D sectors, for a total of 61 sectors/track.
					;This CF card actully has 3F sectors/track. I use 3D for my CPM3 system because
					;my origional Seagate IDE drive has 3D sectors/track. I don't want different CPM3.SYS 
					;files around so this program wold also work with a Seagate 6531 IDE drive (a once common HD). 
					;That was back in 2007.  Unfortunately this was a mistake. It complicates software. 
					;A better choice would be 0FFH sec/track but this gives rise to a different CPM.SYS
					;file spread through my disks so I still use 3DH. If you are starting off use 0FFH or 3F sec/track. 

INIT$FIRST$SEC		EQU	0	;Initial First sector number on each track

MAXTRK			EQU	0FFH	;CPM3 allows up to 8MG so 0-256 "tracks"
BUFFER$ORG		EQU	4000H	;<----- Will place all sector data here

CPM$BOOT$COUNT		EQU	12		;Allow up to 12 CPM sectors for CPMLDR
CPMLDR$ADDRESS		EQU	BUFFER$ORG	;We cannot load the CPMLDR at 100H in RAM since this is where this program resides

RDCON			EQU	1	;For CP/M I/O
WRCON			EQU	2
RESET$DISK		EQU	0DH	;Reset all CPM disks
PRINT			EQU	9
CONST			EQU	11	;CONSOLE STAT
BDOS			EQU	5


	; INCLUDE Z-80 MACRO LIBRARY:
	
	MACLIB	Z80			;For the Z80 DJNZ opcode




;--------------------------------------------------------------------------------------------
	ORG	100H			;<--- For CPM

START:	LXI	SP,STACK
        IF CPM
	LXI     D,SIGN$ON$CPM		;print a welcome message CPM IO
        ELSE
 	LXI     D,SIGN$ON$Z80		;print a welcome message Z80 monitor IO
        ENDIF	

	CALL	PSTRING
	
	IN	STATUS$BUS$PORT		;(80H) If FPGA_DC is active this will be X000000X
	CPI	0FFH
	JNZ	GOT$RESPONSE

	LXI	D,OLD$IDE$BOARD$MSG	;Dual IDE Board only
	CALL	PSTRING
	MVI	A,EXT$IDE$MODE		;MODE 0, for old dual IDE board
	STA	@CURRENT$IDE$MODE
	XRA	A
	STA	@BOARD$TYPE		;<<<< 0 for dual IDE Board >>>
	JP	BEGIN
	
GOT$RESPONSE:
	MVI	A,1
	STA	@BOARD$TYPE		;<<<< 1 for FPGA_DC Board >>>

;	OUT	REBOOT$PORT		;Just to be safer reboot the ZFDC_DC Z80 CPU
	LXI	D,FPGA$BOARD$MSG	;The FPGA_DC Board was detected.
	CALL	PSTRING
	
	MVI	A,CMD$BOARD$CONFIG	;52H, Get the current FPGA_DC Board configuration
	CALL	DATA$TO$FPGA
	
	CALL	GET$FPGA$TO$S100	;Wait for returned signal 

	STA	@CURRENT$BOARD$HARDEWARE ;10XXXXXX = IDE, 01XXXXXX = FDC, 11XXXXXX +IDE+FDC
	
	ANI	11000000B
	CPI	11000000B
	JZ	BOTH$CIRCUITS
	CPI	10000000B
	JZ	IDE$CIRCUIT
FDC$CIRCUIT:
	LXI	D,FDC$CIRCUIT$MSG	;FDC Board only
	CALL	PSTRING
	JR	OVER$CIRCUITS
IDE$CIRCUIT:
	LXI	D,IDE$CIRCUIT$MSG	;IDE Board only
	CALL	PSTRING
	JR	OVER$CIRCUITS
BOTH$CIRCUITS:
	LXI	D,BOTH$CIRCUITS$MSG	;IDE + FDC Board
	CALL	PSTRING

OVER$CIRCUITS:
	MVI	A,CMD$SET$IDE$MODE	;38H set IDE to External mode 
	CALL	DATA$TO$FPGA
	
;	MVI	A,EXT$IDE$MODE		;0H, Select External IDE FPGA circuit for drive A: or B: access, Interrupt driven
	MVI	A,EXT$IDE$INFO$MODE	;02H, FPGA External IDE + Info [CMD driven Hex display/info only]
;	MVI	A,LOCAL$IDE$MODE	;01H, FPGA Local IDE CMD mode   [CMD driven IDE Sec R/W (+ Hex display/info on IDE only Board))
	STA	@CURRENT$IDE$MODE	;Store in RAM

	CALL	DATA$TO$FPGA
;	MVI	A,EXT$IDE$MODE		;0H, Select External IDE FPGA circuit for drive A: or B: access, Interrupt driven
	MVI	A,EXT$IDE$INFO$MODE	;02H, FPGA External IDE + Info [CMD driven Hex display/info only]
;	MVI	A,LOCAL$IDE$MODE	;01H, FPGA Local IDE CMD mode   [CMD driven IDE Sec R/W (+ Hex display/info on IDE only Board))

					;--------------- Actual start of menu commands ------------------------------
BEGIN:	CALL	ZCRLF
	MVI	A,MAXSEC		;Initialize default max sector (assuming 0FF Sec/track)
	STA	@MAX$SEC
	MVI	A,INIT$FIRST$SEC
	STA	@FIRST$SEC		;Initilize to first sector numbered as 0 (not 1)
	LXI	H,0
	SHLD	@TRK			;Start with Track 0,sec 0
	XRA	A
	STA	@SEC
	
	
	CALL	SEL$IDE$A		;initialize the board and first drive. If there is no drive - abort
;	JNZ	NO$A$DRIVE

	CALL	IDEinit			;<<<< (RE)Initilize IDE CF Card A:  >>>>
	JZ	DRIVE$1OK		;Ret with NZ flag set if error (probably no drive)
NO$A$DRIVE:
	LXI	D,INIT$1$ERROR	
	CALL	PSTRING
	JMP	ABORT

DRIVE$1OK:
	CALL	SEL$IDE$B		;Initialize the second drive.  If there is no drive, continue

	CALL	IDEinit			;<<<< (RE)Initilize IDE CF Card B:
	JZ	DRIVE$2OK		;Second drive initialized OK

	LXI	D,INIT$2$ERROR		;Warn about second drive initilization failure, but continue
	CALL	PSTRING
	XRA	A
	STA	@Second$DriveBPresent

	CALL	SEL$IDE$A		
;	JNZ	NO$A$DRIVE
	JMP	MAINLOOP
	
DRIVE$2OK:
	MVI	A,1
	STA	@Second$DriveBPresent
	CALL	SEL$IDE$A		
;	JNZ	NO$A$DRIVE
	JMP	MAINLOOP		;And continue


ABORT:	CALL	SEL$IDE$A		;Make sure drive A is selected.
  
  IF	CPM
	MVI	C,RESET$DISK		;Reset All disks in CPM
	CALL	BDOS		
	JMP	0H			;Jump to CP/M cold start address
  ELSE
	LXI	D,HIT$RESET		;Please Hit the Reset button
	CALL	PSTRING
	CALl	ZCRLF
HOLD:	JMP	HOLD
  ENDIF
 

ERROR:	LXI     D, msgErr		;CMD error msg
	CALL	PSTRING
	JMP	MAINLOOP
	
					;COMMAND BRANCH TABLE
TBL$IDE:DW  IDE$MODE			; "A"  Set IDE Mode
	DW  ERROR			; "B"  
	DW  COPY$CARDS			; "C"  Copy A card
	DW  SELECT$CARD			; "D"  Select a new card
	DW  ERROR			; "E"  
	DW  FORMAT			; "F"  Format current card
	DW  FAST$COPY$AB		; "G"  Fast Copy A:->B:
	DW  ERROR			; "H" 
	DW  GET$IDE$ID			; "I"  Show CF card IDs for A: & B:
	DW  DO$REBOOT			; "J"  Reboot FPGA Z80
	DW  CPMBOOT			; "K"  LOAD CPM (If present) from A:
	DW  GET$TIME$DATE		; "L"  Get Time and Date from FPGA RTC  
	DW  SET$MAXS			; "M"  Set Maximum Sector
	DW  GET$NEXT$SEC		; "N"  Next Sector
	DW  FIRST$SET			; "O"  First sector# 0 or 1
	DW  GET$PREV$SEC		; "P"  Previous sector
	DW  TEST$POST			; "Q"  Test Post MSG
	DW  SEQ$RD			; "R"  Read sector(s) to data buffer
	DW  FILL$RAM			; "S"  Fill the DMA buffer with a HEX value
	DW  DO$SET$LBA			; "T"  Set LBA value (Set "CPM" Track,sector)
	DW  SET$TIME$DATE		; "U"  Reset RTC and update to a new Time & Date
	DW  VERIFY$CARDS		; "V"  Verify current card with another
	DW  WRITE$SEC			; "W"  Write data buffer to current sector
	DW  N$RD$SEC			; "X"  Read N sectors
	DW  N$WR$SEC			; "Y"  Write N sectors
	DW  ERROR			; "Z"  


;-------------------------- MAIN LOOP ------------------------------------------------------------

MAINLOOP:				;print main menu
	LXI	D,MENU$STRING$0		;IDE menu
	CALL	PSTRING
	LXI	H,TBL$IDE		;Get IDE Menu selection

	LDA	@CURRENT$IDE$MODE	;Get current IDE Mode
	CPI	1
	CNZ	wrlba			;Update LBA on drive if Mode 0 or 1

	CALL	IDE$PRINT$LBA		;Display current Track,sector,head#

OVER$IDE$MENU:
	LXI	D,Prompt		;Please Entar a cmd >
	CALL	PSTRING
	
					;----------------------------------------------------
;	CALL	GETCMD			;Simple UC character Input (Note, no fancy checking)
	CALL	ZCI
	CPI	ESC			;Abort if ESC
	JZ	ABORT

	CALL	upper
	CPI	'+'			;Allow + or - instead of N or P
	JNZ	NOT$PLUS
	MVI	A,'N'
	JP	OVER$PLUS
NOT$PLUS:
	CPI	'-'
	JNZ	OVER$PLUS
	MVI	A,'P'
OVER$PLUS:		
	CPI	'A'			;Must be >= 'A" (Otherwise it jumps off table)
	JC	ERROR
	CPI	'Z'+1			;Must be <= 'Z'
	JNC	ERROR
	CALL	ZCRLF
	
	SBI	'@'			;Adjust to 0,1AH
	
	LXI	H,TBL$IDE		;Get IDE Menu selection
	ADD	A			;X2
	ADD	L
	MOV	L,A
	MOV	A,M
	INX	H
	MOV	H,M
	MOV	L,A			;Jump to table pointer
	PCHL				;JMP (HL)



;----------------------------- SELECT CARD/DISK SECTOR ------------------------------------------------------	

SELECT$CARD:				; "D"  Select a Card/Disk	
	CALL	SEL$CURRENT$DRIVE	; New drive in @CURRENT$DRIVE
	JZ	MAINLOOP
	LXI	D,BAD$SEL$DR$MSG
	CALL	PSTRING
	JMP	MAINLOOP
	


;----------------------------- SEQ READ CF CARD SECTORS ------------------------------------------------------
	
SEQ$RD:					;Do sequential reads
	LXI	H,buffer		;Point to buffer
	SHLD	@DMA

	CALL	READSECTOR
	JZ	SEQ$READ$OK		;Z means the sector read was OK

	CALL	SHOW$SECTOR$READ$ERROR
	CALL	ZCRLF
	JMP	MAINLOOP
	
SEQ$READ$OK:	
	LXI     D,msgrd			;Sector read OK
	CALL	PSTRING
	
	LXI	H,buffer		;Point to buffer. Show sector data flag is on
	SHLD	@DMA
	CALL	HEXDUMP			;Show sector data
	CALL	IDE$PRINT$LBA
	CALL	ZCRLF
	
	LXI	D,CONTINUE$MSG		;"Hit keyboard for next sector. ESC to return to menu$ '
	CALL	PSTRING

	CALL	GETCMD			;Simple UC character Input (Note, no fancy checking)
	CPI	ESC			;Abort if ESC
	JZ	MAINLOOP
	CPI	'-'			;Special case, is a '-' then backup one character
	JNZ	READ$OK1
	CALL	PREV$SEC	
	JMP	SEQ$RD
READ$OK1:
	CALL	NEXT$SECT		;Call GET NEXT$SECTOR
	JMP	SEQ$RD



;----------------------------- WRITE SECTOR ------------------------------------------------------	

WRITE$SEC:				;Write data in RAM buffer to sector @ LBA
	LXI	H,buffer		;Point to buffer
	SHLD	@DMA

	CALL	WRITESECTOR		;Write to the current CF Card Sector
	JZ	WRITE$OK		;Z means the sector read was OK

	CALL	SHOW$SECTOR$WRITE$ERROR
	CALL	ZCRLF
	JMP	MAINLOOP

WRITE$OK:
	LXI     D,msgwr			;Sector written OK
	CALL	PSTRING
	
	LXI	H,buffer		;Point to buffer. Show sector data flag is on
	SHLD	@DMA
	CALL	HEXDUMP			;Show sector data
	LXI	D,WAIT$MSG		;'Hit any keyboard character to continue$'
	CALL	PSTRING
	CALL	ZCI
	CALL	ZCRLF
	JMP	MAINLOOP


;----------------------------- READ N SECTORS TO BUFFER ------------------------------------------------------	

N$RD$SEC:				;Read N sectors >>>> NOTE no check is made to not overwrite 
	LXI	D,ReadN$MSG		;CPM etc. in high RAM
	CALL	PSTRING
	CALL	GETHEX
	JC	BAD$RD$ENTRY		;Abort if ESC (C flag set)
	
	CALL	ZCRLF
	STA	@SECTOR$COUNT		;Store sector count
	STA	@SECTOR$COUNT1		;Store sector count
	
	LXI	H,buffer		;Point to buffer
	SHLD	@DMA

	LXI	D,ONE$MOMENT$MSG	;'One moment while data is collected'
	CALL	PSTRING

NextRSec:
	CALL	READSECTOR		;Read the current CF Card Sector
	SHLD	(@DMA)			;Update for next time
	JZ	N$READ$OK		;Z means the sector read was OK

	CALL	SHOW$SECTOR$READ$ERROR
	CALL	ZCRLF
	JMP	MAINLOOP

N$READ$OK:
	LDA	@SECTOR$COUNT
	DCR	A
	STA	@SECTOR$COUNT
	JZ	N$SEC$READ$DONE
	
	CALL	NEXT$SECT		;Point to the next sector
	JR	NextRSec
	

N$SEC$READ$DONE
	LDA	@SECTOR$COUNT1		;Get from store sector count
	CALL	HEXOUT
	LXI	D,N$SEC$READ$MSG	;'H Sectors read to RAM buffer'
	CALL	PSTRING
	PUSH	H
	LHLD	@DMA$STORE
	MOV	A,H
	CALL	HEXOUT
	MOV	A,L
	CALL	HEXOUT
	MVI	C,'H'
	CALL	ZCO
	MVI	C,'-'
	CALL	ZCO
	POP	H
	MOV	A,H
	CALL	HEXOUT
	MOV	A,L
	CALL	HEXOUT
	MVI	C,'H'
	CALL	ZCO
	MVI	C,')'
	CALL	ZCO
	CALL	ZCRLF
	LXI	H,buffer		;Point back to buffer
	SHLD	@DMA
	JMP	MAINLOOP

BAD$RD$ENTRY
	LXI	D,BAD$ENTRY$MSG		;'Bad data entry format'
	CALL	PSTRING
	CALL	ZCRLF
	LXI	H,buffer		;Point back to buffer
	SHLD	@DMA
	JMP	MAINLOOP



;----------------------------- WRITE N SECTORS FROM RAM ------------------------------------------------------	

N$WR$SEC:				;Read N sectors >>>> NOTE no check is made to not overwrite 
	LXI	D,WriteN$MSG		;'Write multiple sectors from RAM to the current CF card.'
	CALL	PSTRING			;'How many 512 byte sectores (xx HEX):$'
	CALL	GETHEX
	JC	BAD$WR$ENTRY		;Abort if ESC (C flag set)

	STA	@SECTOR$COUNT		;Store sector count
	STA	@SECTOR$COUNT1		;Store sector count
	
	LXI	D,DMA$N$MSG		;'Enter Starting RAM location (xxxxH):'
	CALL	PSTRING
	CALL	GET$HL$HEX		;Get 4 characters for HL address
	JC	BAD$WR$ENTRY		;Abort if ESC (C flag set)
	SHLD	@DMA$STORE
	
	LXI     D,msgsure		;Are you sure?
	CALL	PSTRING
	CALL	ZCI
	CALL	upper
IF CPM 
ELSE
	MOV	C,A			;Show selection
	CALL	ZCO
ENDIF	
	CPI	'Y'
	JZ	N$WR$SEC1
	JMP	MAINLOOP

N$WR$SEC1:	
	LXI	D,ONEW$MOMENT$MSG	;'One moment while data is collected'
	CALL	PSTRING

	SHLD	@DMA
NextWSec:
	CALL	WRITESECTOR		;Write the current CF Card Sector
	SHLD	(@DMA)			;Store for next time
	JZ	N$WRITE$OK		;Z means the sector read was OK
	
	CALL	SHOW$SECTOR$WRITE$ERROR
	CALL	ZCRLF
	JMP	MAINLOOP

N$WRITE$OK:
	LDA	@SECTOR$COUNT
	DCR	A
	STA	@SECTOR$COUNT
	JZ	N$SEC$WRITE$DONE

	CALL	NEXT$SECT		;Point to the next sector
	JR	NextWSec
	

N$SEC$WRITE$DONE
	LDA	@SECTOR$COUNT1		;Get from store sector count
	CALL	HEXOUT
	LXI	D,N$SEC$WRITE$MSG	;'H Sectors read to RAM buffer'
	CALL	PSTRING
	PUSH	H
	LHLD	@DMA$STORE
	MOV	A,H
	CALL	HEXOUT
	MOV	A,L
	CALL	HEXOUT
	MVI	C,'H'
	CALL	ZCO
	MVI	C,'-'
	CALL	ZCO
	POP	H
	MOV	A,H
	CALL	HEXOUT
	MOV	A,L
	CALL	HEXOUT
	MVI	C,'H'
	CALL	ZCO
	MVI	C,')'
	CALL	ZCO
	CALL	ZCRLF
	LXI	H,buffer		;Point back to buffer
	SHLD	@DMA
	JMP	MAINLOOP

BAD$WR$ENTRY
	LXI	D,BAD$ENTRY$MSG		;'Bad data entry format'
	CALL	PSTRING
	CALL	ZCRLF
	LXI	H,buffer		;Point back to buffer
	SHLD	@DMA
	JMP	MAINLOOP



;----------------------------- FORMAT A SD Card DRIVE  ------------------------------------------------------	
	
FORMAT:				;Format (Fill sectors with E5's for CPM directory empty)
	LDA	@BOARD$TYPE	;<<<< 1 for FPGA_DC Board >>>
	CPI	1
	JZ	FPGA$FORMAT
	
	LXI	D,FORMAT$MSG	;Slower Dual IDE Board
	CALL	PSTRING
	CALL	ZCI
	CALL	upper
IF CPM 
ELSE
	MOV	C,A			;Show selection
	CALL	ZCO
ENDIF	
	CPI	'Y'
	JNZ	MAINLOOP
	
	CALL	FILL$SECTOR	;Fill buffer with 0E5's (512 of them)
	CALL	ZCRLF

NEXT1$FORMAT:
	LXI	H,buffer
	SHLD	@DMA
	CALL	WRITESECTOR	;Will return error if there was one
	JZ	main19b		;Z means the sector write was OK
	CALL	ZCRLF
	JMP	MAINLOOP
main19b:
	CALL	ZEOL		;Clear line cursor is on
	CALL	FULL$position	;Display actual current Track,sector,head#
	CALL	ZCSTS		;Any keyboard character will stop display
	CPI	01H		;CPM Says something there
	JNZ	WRNEXTSEC1
	CALL	ZCI		;Flush character
	LXI	D,CONTINUE$MSG
	CALL	PSTRING
	CALL	ZCI
	CPI	ESC
	JZ	MAINLOOP
	CALL	ZCRLF
WRNEXTSEC1:
	LHLD	@SEC
	INX	H
	SHLD	@SEC		;0 to MAXSEC CPM Sectors
	MOV	A,L
	CPI	MAXSEC
	JNZ	NEXT1$FORMAT

	LXI	H,0		;Back to CPM sector 0
	SHLD	@SEC
	LHLD	@TRK		;Bump to next track
	INX	H
	SHLD	@TRK
	MOV	A,L		;0-FFH tracks (only)
;	CPI	2		;For testing
	CPI	MAXTRK
	JNZ	NEXT1$FORMAT	

	LXI	D,FormatDone	;Tell us we are all done.
	CALL	PSTRING
	JMP	BEGIN

FILL$SECTOR:	
	LXI	H,buffer		;Fill buffer with 0E5's (512 of them)
	MVI	B,0
Fill0:	MVI	A,0E5H			;<-- Sector fill character (0E5's for CPM)
	MOV	M,A
	INX	H
	MOV	M,A
	INX	H
	DJNZ	Fill0
	RET


;;;;;;;;;
FPGA$FORMAT:				;Format (Fill sectors with E5's for CPM directory empty)
	LXI	D,FORMAT$MSG		;'Warning: this will change data on the Card... Y/N 
	CALL	PSTRING
	CALL	ZCI			;Are you sure?
	CALL	upper
IF CPM 
ELSE
	PUSH	PSW
	PUSH	B
	MOV	C,A			;Show selection
	CALL	ZCO
	POP	B
	POP	PSW
ENDIF	
	CALL	ZCRLF			;No registerers changed
	CPI	'Y'
	JNZ	MAINLOOP
	
FORMAT1:
	LXI     D,QUICK$FORMAT		;Quick Format?
	CALL	PSTRING
	CALL	ZCI
	CALL	upper
IF CPM 
ELSE
	PUSH	PSW
	PUSH	B
	CALL	ZCO
	POP	B
	POP	PSW
ENDIF	
	CALL	ZCRLF
	CPI	'Y'
	JNZ	FULL$FORMAT
	MVI	A,2			;Two tracks only
	STA	(@TRACK$COUNT)
	JP	START$FORMAT
FULL$FORMAT:
	MVI	A,0FFH			;All Tracks tracks only
	STA	(@TRACK$COUNT)

START$FORMAT:
	LXI     D,TO$ABORT$F		;Remember you can hit ESC to abort the formatting process.
	CALL	PSTRING

	XRA	A			;Start with TRK/SEC 0,first
	STA	@TRK
	LDA	@FIRST$SEC
	STA	@SEC

	CALL	FILL$SECTOR
	CALL	ZCRLF

	MVI	A,CMD$SET$TRK$SEC	;35H  Set new current TRACK/SECTOR on current drive 
	CALL	DATA$TO$FPGA		;For FPGA_DC board (if present)
	MVI	A,0		
	CALL	DATA$TO$FPGA		;Always start at Track 0, Sec 0
	CALL	DATA$TO$FPGA
	CALL	ZCRLF

	CALL	FULL$position		;Display actual current Track,sector
	
NEXT$FORMAT:
	LXI	H,buffer
	SHLD	@DMA

	CALL	WRITESECTOR		;Write the current CF or SD card Sector
	JZ	main9b			;Z means the sector write was OK
	
	CALL	SHOW$SECTOR$WRITE$ERROR
	CALL	ZCRLF
	JMP	MAINLOOP
	
main9b:	CALL	ZCSTS			;Any keyboard character will stop display
	CPI	01H
	JNZ	WR1NEXTSEC1
	CALL	ZCI			;Flush character
	LXI	D,CONTINUE$MSG
	CALL	PSTRING
	CALL	ZCI	
	CPI	ESC
	JZ	NextFormatDone
	CALL	ZCRLF
	CALL	FULL$position		;Display actual current Track,sector

WR1NEXTSEC1:
	LDA	@SEC			;Current sector to A
	PUSH	B
	MOV	C,A
	LDA	@MAX$SEC
	CMP	C
	POP	B
	JZ	NEXT$TRK
	CALL	NEXT$SECT
	LXI     D,msgBS2		;BS,BS
	CALL	PSTRING
	LDA	@SEC
	CALL	HEXOUT
	JMP	NEXT$FORMAT
NEXT$TRK:
	POP	B
	LDA	(@TRACK$COUNT)
	DCR	A
	STA	(@TRACK$COUNT)
	JZ	NextFormatDone		
	CALL	NEXT$SECT		;Thie will increase TRK and reset SEC to 0
	LXI     D,msgBS5		;5X BS
	CALL	PSTRING			
	LDA	@TRK			;TRK = (8 bits)
	CALL	HEXOUT
	MVI	C,','
	CALL	ZCO
	LDA	@SEC
	CALL	HEXOUT			;XX,XX
	JMP	NEXT$FORMAT
	
NextFormatDone:
	LXI	D,FormatDone		;Tell us we are all done.
	CALL	PSTRING
	CALL	ZCRLF

	XRA	A			;Set "pointer" back to teh beginning of the disk
	STA	@TRK
	LDA	@FIRST$SEC
	STA	@SEC
	MVI	A,CMD$SET$TRK$SEC	;35H  Set new current TRACK/SECTOR on current drive
	CALL	DATA$TO$FPGA
	LDA	@TRK		
	CALL	DATA$TO$FPGA
	LDA	@SEC		
	CALL	DATA$TO$FPGA
	JMP	MAINLOOP


FULL$position:				
	LXI     D,msgTRK$SEC		;Card TRK,SEC = $'
	CALL	PSTRING			
	LDA	@TRK			;TRK = (8 bits)
	CALL	HEXOUT
	MVI	C,','
	CALL	ZCO
	LDA	@SEC
	CALL	HEXOUT			;XX,XX
	RET


;----------------------------- COPY CF Cards  ------------------------------------------------------	

COPY$CARDS:				; "Y"  Copy Drives
	LXI	D,DiskCopy1Msg
	CALL	PSTRING
	CALL	ZCI
	CALL	upper
IF CPM 
ELSE
	MOV	C,A			;Show selection
	CALL	ZCO
ENDIF	
	CPI	'Y'
	JNZ	MAINLOOP
	
	LXI	H,0			;Start with CPM sector 0
	SHLD	@SEC	
	SHLD	@TRK			;and track 0
	
	CALL	ZCRLF
	CALL	ZCRLF
	
NextDCopy1:	
	CALL	ZEOL			;Clear line cursor is on
	LXI	D,CopyTrk$MSG		;for each track update display
	CALL	PSTRING
	LDA	@TRK			;Low TRK byte
	CALL	hhexout

NextDCopy:	
	MVI	A,0			;Login drive A:
	STA	@CURRENT$DRIVE
	OUT	IDEDrive

	CALL	wrlba			;Update LBA on "A:" drive

	LXI	H,buffer		;Point to buffer
	SHLD	@DMA
	CALL	READSECTOR		;Get sector data from A: drive to buffer
	
	MVI	A,1			;Login drive B:
	STA	@CURRENT$DRIVE
	OUT	IDEDrive

	CALL	wrlba			;Update LBA on "B:" drive
	
	LXI	H,buffer		;Point to buffer
	SHLD	@DMA
	CALL	WRITESECTOR		;Write buffer data to sector on B: drive
	
	CALL	ZCSTS			;Any keyboard character will stop display
	CPI	01H			;CPM Says something there
	JNZ	BK$D$NEXTSEC1
	CALL	ZCI			;Flush character
	LXI	D,CONTINUE$MSG
	CALL	PSTRING
	CALL	ZCI
	CPI	ESC
	JNZ	BK$D$NEXTSEC1
	MVI	A,0			;Login drive A:
	STA	@CURRENT$DRIVE
	OUT	IDEDrive
	JMP	BEGIN

BK$D$NEXTSEC1:
	LHLD	@SEC
	INX	H
	SHLD	@SEC
	MOV	A,L			;0 to 62 CPM Sectors
	CPI	MAXSEC-1
	JNZ	NextDCopy

	LXI	H,0			;Back to CPM sector 0
	SHLD	@SEC
	
	LHLD	@TRK			;Bump to next track
	INX	H	
	SHLD	@TRK
					;Check if we are done
	MOV	A,L			;0-FFH tracks (only)
;	CPI	2			;For testing
	CPI	MAXTRK
	JNZ	NextDCopy1
	
	LXI	D,CopyDone		;Tell us we are all done.
	CALL	PSTRING
	MVI	A,0			;Login drive A:
	STA	@CURRENT$DRIVE
	OUT	IDEDrive

	XRA	A			;Start with Trk/Sec 0,0 in both cases
	STA	@TRK
	LDA	@FIRST$SEC
	STA	@SEC
	
	LDA	@BOARD$TYPE		;0= Dual IDE/CF Board, 1= FPGA_DC Board
	ORA	A
	JZ	BEGIN

	MVI	A,CMD$SET$TRK$SEC	;35H. Set new current TRACK/SECTOR on current drive
	CALL	DATA$TO$FPGA
	LDA	@TRK
	CALL	DATA$TO$FPGA
	LDA	@SEC
	CALL	DATA$TO$FPGA
	JMP	BEGIN


;--------------------------------- SPECIAL FAST COPY A:->B: -------------------------------------

FAST$COPY$AB:	
	LDA	@BOARD$TYPE		;<<<< 1 for FPGA_DC Board >>>
	CPI	1
	JNZ	WRONG$BOARD

	LDA	@Second$DriveBPresent	;Make sure B: is valid
	ORA	A
	JNZ	FAST2@OK
	LXI	D,NO$DRIVE$MSG		;A valid CF card is not present in B: 
	CALL	PSTRING
	JMP	MAINLOOP
	
FAST2@OK:
	MVI	A,'A'			;Force Drive select to A:
	CALL	RESELECT$DRIVE
	LDA	@CURRENT$DRIVE
	STA	@DRIVE1			;Select Card ASCII A
		
	LXI	D,DiskCopyMsg		; NOTE this copies the whole Disk/CF card across
	CALL	PSTRING			; Not just a partition
	CALL	ZCI
	CALL	upper
IF CPM 
ELSE
	PUSH	PSW
	PUSH	B
	CALL	ZCO
	POP	B
	POP	PSW
ENDIF	
	CPI	'Y'
	JNZ	MAINLOOP

	CALL	ZCRLF
	LXI	D,FAST$COPY$MSG		;Fast Copy of A:->B: Started. (Use HEX Display for status)
	CALL	PSTRING			
	LXI     D,TO$ABORT$C		;Remember you can hit ESC to abort the copy process.
	CALL	PSTRING

	MVI	A,CMD$FAST$COPY$AB	;39H send special Fast CF Card Copy CMD (Always A:->B:)
	CALL	DATA$TO$FPGA
	
FAST$COPY$LOOP:	
	CALL	ZCSTS			;Any keyboard character will stop display
	CPI	01H			;CPM Says something there
	JNZ	CHECK$DONE
	CALL	ZCI			;Flush character
	CPI	ESC
	JZ	STOP$FAST$COPY

CHECK$DONE:				;Are we done yet
	IN	STATUS$BUS$PORT		;Get S100 Status Port - Bit 0, 1 = FPGA data present, 0, no data present
	ANI	01H			;01H, Bit 0
	JZ	FAST$COPY$LOOP		;No data so far
	
	IN	DATA$BUS$PORT		;Return Data in [A], FPGA "hardware" will lower bit 7 of S100 Bus port 80H
	CPI	CMD$COMPLETE
	JZ	FC$DONE

	LXI	D,FAST$ERROR$MSG	;Fast Copy of CF Card A:->B: returned an error
	CALL	PSTRING
	JMP	OVER$ABORT
	
STOP$FAST$COPY
	MVI	A,CMD$ABORT		;Continue the FPGD_DC Board copy process
	CALL	DATA$TO$FPGA
	LXI	D,FAST$ABORT$MSG	;Fast Copy of CF Card A:->B: Aborted
	CALL	PSTRING
	IN	DATA$BUS$PORT		;Cleanup returned error from FPGA_DC Board

OVER$ABORT:
	LDA	@DRIVE1			;Select Card ASCII A,B,C....
	STA	@CURRENT$DRIVE		
	CALL	RESELECT$DRIVE		;Drive in @CURRENT$DRIVE
	
	XRA	A			;Start with Trk/Sec 0,0 in both cases
	STA	@TRK
	LDA	@FIRST$SEC
	STA	@SEC
	
	LDA	@BOARD$TYPE		;0= Dual IDE/CF Board, 1= FPGA_DC Board
	ORA	A
	JZ	FAST$SKIP$TRK$SEC$INFO

	MVI	A,CMD$SET$TRK$SEC	;35H. Set new current TRACK/SECTOR on current drive
	CALL	DATA$TO$FPGA
	LDA	@TRK
	CALL	DATA$TO$FPGA
	LDA	@SEC
	CALL	DATA$TO$FPGA
	
	
FAST$SKIP$TRK$SEC$INFO:
	CALL	ZCRLF
	CALL	ZCRLF	
	JMP	MAINLOOP

FC$DONE:
	CALL	ZCRLF	
	LXI	D,FC$DONE$MSG		;A valid CF card is not present in B: 
	CALL	PSTRING
	CALL	ZCRLF	
	JMP	OVER$ABORT



;----------------------------- VERIFY CF CARDS  ------------------------------------------------------	

VERIFY$CARDS:				;"V" verify data on both cards is the same
	LXI	D,DiskVerifyMsg
	CALL	PSTRING
	
	LXI	H,0		;Start with CPM sector 0
	SHLD	@SEC
	SHLD	@TRK		;and track 0
	
	CALL	ZCRLF
	CALL	ZCRLF
	
NextVCopy1:	
	CALL	ZEOL		;Clear line cursor is on
	LXI	D,VerifyTrk$MSG	;for each track update display
	CALL	PSTRING
	LDA	@TRK		;Low TRK byte
	CALL	HHEXOUT

NextVCopy:	
	MVI	A,0		;Login drive A:
	STA	@CURRENT$DRIVE
	OUT	IDEDrive

	CALL	wrlba		;Update LBA on "A:" drive

	LXI	H,buffer	;Point to buffer
	SHLD	@DMA
	CALL	READSECTOR	;Get sector data from buffer A: drive
	
	MVI	A,1		;Login drive B:
	STA	@CURRENT$DRIVE
	OUT	IDEDrive

	CALL	wrlba		;Update LBA on "B:" drive
	
	LXI	H,buffer2	;Point to buffer2
	SHLD	@DMA
	CALL	READSECTOR	;Read buffer data from sector of B drive
	
	LXI	BC,512		;Now check both buffers are identical
	LXI	H,buffer
	LXI	D,buffer2
NEXTV1:	LDAX	D
	CMP	M		;Is [DE] = [HL]
	JNZ	COMPARE1$ERROR
	INX	H
	INX	D
	DCX	B
	MOV	A,C
	ANA	B
	JZ	VERIFY1$OK
	JMP	NEXTV1

COMPARE1$ERROR:
	LXI	D,VERIFY$ERR	;Indicate an error
	CALL	PSTRING
	LDA	@TRK		;Low TRK byte
	CALL	HEXOUT
	LXI	D,SEC$Msg
	CALL	PSTRING
	LDA	@SEC		;Sector byte
	CALL	HHEXOUT
	JMP	VER$OK1
	
VERIFY1$OK:
	CALL	ZCSTS		;Any keyboard character will stop display
	CPI	01H		;CPM Says something there
	JNZ	BK$V$NEXTSEC1
	CALL	ZCI		;Flush character
VER$OK1:
	LXI	D,CONTINUE$MSG
	CALL	PSTRING
	CALL	ZCI
	CPI	ESC
	JNZ	BK$V$NEXTSEC1
	MVI	A,0		;Login drive A:
	STA	@CURRENT$DRIVE
	OUT	IDEDrive
	JMP	MAINLOOP

BK$V$NEXTSEC1:
	LHLD	@SEC
	INX	H
	SHLD	@SEC
	MOV	A,L		;0 to 62 CPM Sectors
	CPI	MAXSEC-1
	JNZ	NextVCopy

	LXI	H,0		;Back to CPM sector 0
	SHLD	@SEC
	
	LHLD	@TRK		;Bump to next track
	INX	H
	SHLD	@TRK
				;Check if we are done
	MOV	A,L		;0-FFH tracks (only)
;	CPI	2		;For testing
	CPI	MAXTRK		
	JNZ	NextVCopy1
	
	LXI	D,VerifyDone	;Tell us we are all done.
	CALL	PSTRING
	MVI	A,0		;Login drive A:
	STA	@CURRENT$DRIVE
	OUT	IDEDrive
	
	XRA	A			;Start with Trk/Sec 0,0 in both cases
	STA	@TRK
	LDA	@FIRST$SEC
	STA	@SEC
	
	LDA	@BOARD$TYPE		;0= Dual IDE/CF Board, 1= FPGA_DC Board
	ORA	A
	JZ	BEGIN

	MVI	A,CMD$SET$TRK$SEC	;35H. Set new current TRACK/SECTOR on current drive
	CALL	DATA$TO$FPGA
	LDA	@TRK
	CALL	DATA$TO$FPGA
	LDA	@SEC
	CALL	DATA$TO$FPGA
	JMP	BEGIN
	


;----------------------------------------------------


COMPARE$BUFFERS:
	LXI	BC,512			;Now check both buffers are identical
	LXI	H,buffer
	LXI	D,buffer2
NEXTV:	LDAX	D
	CMP	M			;Is [DE] = [HL]
	JNZ	COMPARE$ERROR
	INX	H
	INX	D
	DCX	B
	MOV	A,C
	ORA	B
	JNZ	NEXTV
	XRA	A
	RET				;Ret Z if OK

COMPARE$ERROR:
	LXI	D,VERIFY$ERR		;Indicate an error
	CALL	PSTRING
	LDA	@TRK			;TRK byte
	CALL	HEXOUT
	LXI	D,SEC$Msg
	CALL	PSTRING
	LDA	@SEC			;Sector byte
	CALL	HEXOUT

	LXI	D,CONTINUE$MSG
	CALL	PSTRING
	CALL	ZCI
	CPI	ESC
	JNZ	VERIFY$OK
	XRA	A
	DCR	A
	RET				;Ret NZ if abort requested 
	
VERIFY$OK:
	CALL	ZCRLF
	CALL	FULL$position		;Display actual current Track,sector
	XRA	A 
	RET				;RET Z if all OK
	
VERIFY$FINISHED:	
	LDA	@DRIVE1
	STA	@CURRENT$DRIVE		;Select Card #1
	CALL	RESELECT$DRIVE		;Drive in @CURRENT$DRIVE
	
	XRA	A			;Start with Trk/Sec 0,0 in both cases
	STA	@TRK
	LDA	@FIRST$SEC
	STA	@SEC
	
	LDA	@BOARD$TYPE		;0= Dual IDE/CF Board, 1= FPGA_DC Board
	ORA	A
	JZ	VERIFY$TRK$SEC$INFO

	MVI	A,CMD$SET$TRK$SEC	;35H. Set new current TRACK/SECTOR on current drive
	CALL	DATA$TO$FPGA
	LDA	@TRK
	CALL	DATA$TO$FPGA
	LDA	@SEC
	CALL	DATA$TO$FPGA

VERIFY$TRK$SEC$INFO:
	CALL	FULL$position		;Display actual current Track,sector
	CALL	ZCRLF
	LXI	D,VERIFY$COMPLET$MSG
	CALL	PSTRING
	CALL	ZCRLF
	CALL	ZCRLF	
	JMP	MAINLOOP


;------------------------------------- BOOT CPM ------------------------------------------
	
CPMBOOT:				;Boot CPM from IDE system tracks of A: (Always)
	XRA	A			;Load from track 0,sec 1, head 0 (Always)
	STA	@TRK+1
	STA	@TRK
	MVI	A,1			;Boot from Track 0, SEC 1 
	STA	@SEC

	MVI	A,CPM$BOOT$COUNT	;Count of CPMLDR sectors  (12)
	STA	@SECTOR$COUNT
	LXI	H,BUFFER		;DMA address where the CPMLDR will INITIALLY be placed in RAM
	SHLD	@DMA

	MVI	A,CMD$SET$IDE$MODE	;38H set IDE to External mode 
	CALL	DATA$TO$FPGA
	MVI	A,EXT$IDE$MODE		;MODE 0, FPGA External IDE         [INT display/info only]
	STA	@CURRENT$IDE$MODE	;Store new current mode
	CALL	DATA$TO$FPGA

NextRCPM:
	LHLD	@DMA
	SHLD	@DMA$STORE		;Store for HEXDUMP
	CALL	EXT$READSECTOR		;Read a sector in external IDE mode (Note @DMA is updated)
;	LHLD	@DMA$STORE
;	CALL	HEXDUMP			;<------- For Debugging	show contents @{HL]
;	CALL	IDE$PRINT$LBA
;	CALL	ZCRLF
	
	LDA	@SECTOR$COUNT
	DCR	A
	STA	@SECTOR$COUNT
	JZ	LOAD$DONE

	LDA	@SEC
	INR	A
	STA	@SEC			;Note we assume we alway will stay on track 0 in this special case
	MVI	C,'.'
	CALL	ZCO
	JMP	NextRCPM


LOAD$DONE:
	MVI	E,REGstatus		;Check the R/W status when done
	CALL	IDErd8D
	BIT	0,D
	JNZ	CPMLoadErr		;Z if no errors
;	JMP	0F000H			;<---- For debugging with Monitor M4000,6000,100. Then G100

	LXI	H,BUFFER
	MOV	A,M
	CPI	31H			;EXPECT TO HAVE 31H @80H IE. LD SP,80H
	JNZ	CPMLoadErr1		;Z if no errors
	
	LXI	D,MOVE$REQUEST		;Ask if we can move data to 100H overwriting this program
	CALL	PSTRING
	CALL	ZCI
	CALL	upper
IF CPM 
ELSE
	PUSH	PSW
	PUSH	B
	CALL	ZCO
	POP	B
	POP	PSW
ENDIF	
	CPI	'Y'
	JNZ	START
	
	LXI	H,CPM$MOVE$CODE		;Need to put memory move code out of the way.
	LXI	D,0H
	LXI	B,(CPM$MOVE$CODE$END-CPM$MOVE$CODE)
	LDIR
	NOP
	NOP
	NOP
	JMP	0H			;Now jump here where the code will move the CPMLDR currently @4000H to 100H
					;Note this will overwrite this code, no calls are valid after this! 
					
CPMLoadErr1:
	LXI	D,CPM$ERROR1		;Drive data error
	CALL	PSTRING
	JMP	MAINLOOP
	
CPMLoadErr:
	LXI	D,CPM$ERROR		;Drive Read Error
	CALL	PSTRING
	JMP	MAINLOOP


;----------------------------- SET LBA ------------------------------------------------------	

DO$SET$LBA:				;Set the logical block address
	LXI	D,LBA$FORMAT		;'Enter new Track,Sector as XX,XX (+CR):$'
	CALL	PSTRING
	CALL	SET$LBA			;Call the generalized routine
	JMP	MAINLOOP


;----------------------------- NEXT SEC ------------------------------------------------------	

GET$NEXT$SEC:				; "N"  Next Sector
	CALL	NEXT$SECT	
	JMP	MAINLOOP

	
NEXT$SECT:				;General callable routine
	PUSH	B
	LDA	@SEC
	MOV	C,A
	LDA	@MAX$SEC
	CMP	C			;At last sector on track
	JZ	EO$TRK
	JC	EO$TRK
	MOV	A,C
	INR	A			;Otherwise, on to the next sector
	STA	@SEC
	JMP	NEXT$SEC$DONE
EO$TRK:
	LDA	@FIRST$SEC
	STA	@SEC			;Back to the first ssector
	
	LDA	@TRK
	CPI	0FFH
	JZ	END$OF$CARD	
	INR	A
	STA	@TRK
	JMP	NEXT$SEC$DONE
END$OF$CARD:
	MVI	C,BELL
	CALL	ZCO
	LDA	@MAX$SEC
	STA	@SEC			;Set to FF,MAX$SEC
	MVI	A,0FFH
	STA	@TRK
NEXT$SEC$DONE:
	POP	B
;	MVI	A,CMD$SET$TRK$SEC	;35H  Set new current TRACK/SECTOR on current drive
;	CALL	DATA$TO$FPGA
;	LDA	@TRK		
;	CALL	DATA$TO$FPGA
;	LDA	@SEC		
;	CALL	DATA$TO$FPGA
	RET	
	
	
;----------------------------- PREVIOUS SECTOR ------------------------------------------------------	

GET$PREV$SEC:				; "P"  Previous Sector
	CALL	PREV$SEC	
	JMP	MAINLOOP
	
PREV$SEC:				;General callable routine
	PUSH	B
	LDA	@SEC
	MOV	C,A
	LDA	@FIRST$SEC
	CMP	C			
	JZ	BO$TRK
	MOV	A,C
	DCR	A
	STA	@SEC
	JMP	START$SEC$DONE
BO$TRK:
	LDA	@MAX$SEC
	STA	@SEC
	LDA	@TRK
	ORA	A
	JZ	START$OF$CARD
	DCR	A
	STA	@TRK
	JMP	START$SEC$DONE
START$OF$CARD:
	MVI	C,BELL
	CALL	ZCO
	LDA	@FIRST$SEC
	STA	@SEC
	XRA	A
	STA	@TRK
START$SEC$DONE:
	POP	B		
;	MVI	A,CMD$SET$TRK$SEC	;35H  Set new current TRACK/SECTOR on current drive
;	CALL	DATA$TO$FPGA
;	LDA	@TRK		
;	CALL	DATA$TO$FPGA
;	LDA	@SEC		
;	CALL	DATA$TO$FPGA
	RET
	
;----------------------------- SELECT DRIVES --------------------------------------------------	


DRIVE$A:
	CALL	SEL$IDE$A		;Select Drive A
	LXI     D,SETA$MSG	
	CALL	PSTRING
	CALL	ZCRLF
	jmp	MAINLOOP

DRIVE$B:
	CALL	SEL$IDE$B		;Select Drive B
	LXI     D,SETB$MSG	
	CALL	PSTRING
	CALL	ZCRLF
	jmp	MAINLOOP

;----------------------------- RAM CLEAR --------------------------------------------------	
	

FILL$RAM:				;Fill RAM buffer with a Byte value
	LXI	D,FILL$MSG		;CPM etc. in high RAM
	CALL	PSTRING
	CALL	GETHEX
	JC	BAD$ENTRY		;Abort if ESC (C flag set)

	MOV	B,A			;Store Fill character
	LXI	H,buffer		;Point to buffer
	LXI	D,512

CLEAR1:	MOV	M,B
	INX	H
	DCX	D
	MOV	A,E
	ORA	D
	JNZ	CLEAR1
	CALL	ZCRLF
	jmp	MAINLOOP

BAD$ENTRY
	LXI	D,BAD$ENTRY$MSG		;'Bad data entry format'
	CALL	PSTRING
	CALL	ZCRLF
	JMP	MAINLOOP


;----------------------------- SET FIRST SECTOR  ------------------------------------------------------	
	

FIRST$SET:
	LDA	@BOARD$TYPE		;<<<< 1 for FPGA_DC Board >>>
	CPI	1
	JNZ	WRONG$BOARD

	LXI	D,LAST$MSG		;Enter the last sector number on a track - in hardware. 
	LXI	D,FIRST$MSG		;Enter the starting sector number on a track - in hardware. (0 or 1)
	CALL	PSTRING
	CALL	GETHEX
	JC	BAD$ENTRY		;Abort if ESC (C flag set)
	CPI	1
	JNZ	IS$0$SEC
	STA	@FIRST$SEC
	STA	@SEC
	MVI	A,CMD$SET$TRK$SEC	;35H  Set new current TRACK/SECTOR on current drive
	CALL	DATA$TO$FPGA
	LDA	@TRK		
	CALL	DATA$TO$FPGA
	LDA	@SEC		
	CALL	DATA$TO$FPGA
	CALL	ZCRLF
	JMP	MAINLOOP
IS$0$SEC:	
	XRA	A
	STA	@FIRST$SEC
	STA	@SEC
	MVI	A,CMD$SET$TRK$SEC	;35H  Set new current TRACK/SECTOR on current drive
	CALL	DATA$TO$FPGA
	LDA	@TRK		
	CALL	DATA$TO$FPGA
	LDA	@SEC		
	CALL	DATA$TO$FPGA
	CALL	ZCRLF
	JMP	MAINLOOP

;----------------------------- SET MAX SECTOR  ------------------------------------------------------	

SET$MAXS:	
	LDA	@BOARD$TYPE		;<<<< 1 for FPGA_DC Board >>>
	CPI	1
	JNZ	WRONG$BOARD
	LXI	D,LAST$MSG		;Enter the last sector number on a track - in hardware. 
	CALL	PSTRING
	CALL	GETHEX
	JC	BAD$ENTRY		;Abort if ESC (C flag set)
	CPI	2
	JC	BAD$ENTRY		;Should be grater tha 2
	STA	@MAX$SEC
	LDA	@FIRST$SEC
	STA	@SEC
	MVI	A,CMD$SET$TRK$SEC	;35H  Set new current TRACK/SECTOR on current drive
	CALL	DATA$TO$FPGA
	LDA	@TRK		
	CALL	DATA$TO$FPGA
	LDA	@SEC		
	CALL	DATA$TO$FPGA
	CALL	ZCRLF
	JMP	MAINLOOP

WRONG$BOARD:
	LXI	D,WRONG$MSG		;This CMD only works for the FPGA_DC Board
	CALL	PSTRING
	CALL	ZCRLF
	JMP	MAINLOOP

;----------------------------- SHOW RAM  ------------------------------------------------------	

SHOW$RAM:				;Show RAM Buffer Contents
	LXI	H,buffer		;Point to buffer. Show sector data flag is on
	SHLD	@DMA
	CALL	HEXDUMP			;Show sector data
	LXI	D,DONE$MSG		;"Hit keyboard to return to menu$ '
	CALL	PSTRING
	CALL	ZCI
	JMP	MAINLOOP


;----------------------------- REBOOT FPGA_DC BOARD  ------------------------------------------------------	

DO$REBOOT:
	LXI     D,SURE$REBOOT		;'Are you sure you want to Reboot/Reset teh FPGA_DC Board (Y/N)$'
	CALL	PSTRING			
	CALL	ZCI			;Are you sure?
	CALL	UPPER
IF CPM 
ELSE
	PUSH	PSW
	PUSH	B
	CALL	ZCO
	POP	B
	POP	PSW
ENDIF	
	CPI	'Y'
	JZ	REBOOT1
	CALL	ZCRLF
	JMP	MAINLOOP
	
REBOOT1:
	CALL	ZCRLF
	OUT	REBOOT$PORT
	JMP	BEGIN


;----------------------------- GET IDE CARD ID  ------------------------------------------------------	

GET$IDE$ID:
	CALL	ZCRLF	
	CALL	driveid			;Get the drive ID info for drive 0. If there is no drive, abort
	JZ	INIT$OK1	
	LXI	D,ID$ERROR
	CALL	PSTRING
	JMP	ABORT

INIT$OK1:
	CALL	ID$PRINT		;Print the drive ID info for drive 0.  If invalid, abort
	JNC	INIT$OK2
	LXI	D,DRIVE0$IDMSG
	CALL	PSTRING
INIT$OK2:
	CALL	ZCRLF
	LDA	@Second$DriveBPresent	;Was there a second drive?
	ORA	A
	JZ	MAINLOOP		;Nope
	CALL	SEL$IDE$B			;Yes - select it.
	CALL	driveid			;Get the drive ID info for drive 1.  Continue even if none.
	JZ	INIT$OK3
	LXI	D,ID$ERROR
	CALL	PSTRING
	CALL	SEL$IDE$A
	JMP	MAINLOOP
INIT$OK3:
	CALL	ID$PRINT		;Print the drive ID info for drive 1.  Continue even if invalid.
	CALL	SEL$IDE$A
	JMP	MAINLOOP

	

;----------------------------- POST A MESSAGE ------------------------------------------------------	

	
TEST$POST:				;Undocumented	"Q" Menu command to test CMD 41H (Post a message)
	CALL	CHECK$BOARD$TYPE	;Must be FPGA DC Board. Will pop stack and return to main menu if not FPGA_DC Board
	LXI	D,TEXT$SENT		;The following Text was sent to the FPGA_DC IDE Board
	CALL	PSTRING
	MVI	A,INFO$POST$MSG		;41H Post a message on the USB Port Terminal
	CALL	DATA$TO$FPGA
	LXI	H,POST$TEST$MSG		;'This is a test of the Post Message CMD$'
	CALL	POS$MSG$STRING		;Send one character at a time to FPGA_DC Board
	CALL	ZCRLF
	JMP	MAINLOOP
	

;----------------------------- SET IDE MODE ------------------------------------------------------	

	
IDE$MODE:				;Undocumented	"Q" Menu command to test CMD 41H (Post a message)
	CALL	CHECK$BOARD$TYPE	;Must be FPGA DC Board
	LXI	D,SET$IDE$MODE$MSG	;'Set IDE Mode (0,1,2):'
	CALL	PSTRING

	CALL	GETCMD			;Simple UC character Input (Note, no fancy checking)
	CPI	ESC			;Abort if ESC
	JZ	MAINLOOP
	
	CPI	'0'
	JRZ	SET$0$MODE
	CPI	'1'
	JRZ	SET$1$MODE
	CPI	'2'
	JRZ	SET$2$MODE
	MVI	C,'?'
	CALL	ZCO
	MVI	C,BELL
	CALL	ZCO
	JMP	MAINLOOP

SET$0$MODE:	
	LXI	D,IDE$MODE$0$MSG	;'IDE Mode to Mode 0$'
	CALL	PSTRING
	MVI	A,EXT$IDE$MODE		;0H, Select External IDE FPGA circuit for drive A: or B: access, Interrupt driven
	STA	@CURRENT$IDE$MODE

	MVI	A,CMD$SET$IDE$MODE	;38H set IDE mode 
	CALL	DATA$TO$FPGA
	MVI	A,EXT$IDE$MODE		;MODE 0, Select External IDE FPGA circuit for drive A: or B: access, Interrupt driven
	CALL	DATA$TO$FPGA
	JMP	MAINLOOP

SET$1$MODE:	
	LXI	D,IDE$MODE$1$MSG	;'IDE Mode to Mode 1$'
	CALL	PSTRING
	MVI	A,LOCAL$IDE$MODE	;1H, Select Local IDE FPGA circuit for drive A: or B: access, Not Interrupt driven
	STA	@CURRENT$IDE$MODE

	MVI	A,CMD$SET$IDE$MODE	;38H set IDE mode 
	CALL	DATA$TO$FPGA
	MVI	A,LOCAL$IDE$MODE	;MODE 1, Select External IDE FPGA circuit for drive A: or B: access, Not Interrupt driven
	CALL	DATA$TO$FPGA
	JMP	MAINLOOP
	
SET$2$MODE:	
	LXI	D,IDE$MODE$2$MSG	;'IDE Mode to Mode 2$'
	CALL	PSTRING
	MVI	A,EXT$IDE$INFO$MODE	;2H, ;Select External IDE FPGA circuit + CMD Info
	STA	@CURRENT$IDE$MODE

	MVI	A,CMD$SET$IDE$MODE	;38H set IDE mode 
	CALL	DATA$TO$FPGA
	MVI	A,EXT$IDE$INFO$MODE	;MODE 2, ;Select External IDE FPGA circuit + CMD Info
	CALL	DATA$TO$FPGA
	JMP	MAINLOOP




;--------------------------------------------------------------------------------------
;---------------- Support Routines ----------------------------------------------------
;--------------------------------------------------------------------------------------

CHECK$BOARD$TYPE:			;Check we have a FPGA DC Board. If not abort
	LDA	@BOARD$TYPE		;1 for FPGA_DC Board >>>
	ORA	A
	RNZ
	LXI	D,BOARD$ERR$MSG		;Command only valid for FPGA DC Board
	CALL	PSTRING
	POP	PSW			;Balance up stack
	JMP	MAINLOOP

SEL$CURRENT$DRIVE:	
	LXI	D,CURRENT$DR$MSG	;Select Drive(A,B)
	CALL	PSTRING

	CALL	GETCMD			;Simple UC character Input (Note, no fancy checking)
	CPI	ESC			;Abort if ESC
	JZ	ABORT
	CALL	UPPER

RESELECT$DRIVE:				;Reselect drive in [A]
	MOV	B,A
	STA	@CURRENT$DRIVE		;Store ASCII for drive #
	
	CPI	'A'
	JZ      SEL$IDE$A
	CPI	'B'
	JZ      SEL$IDE$B
	
	CPI	'C'
	JZ      SEL$WITH$CMD$DRIVE
	CPI	'D'
	JZ      SEL$WITH$CMD$DRIVE
	CPI	'E'
	JZ      SEL$WITH$CMD$DRIVE
	CPI	'F'
	JZ      SEL$WITH$CMD$DRIVE
	JMP	BAD$A$SEL$DRIVE

SEL$WITH$CMD$DRIVE:
	MVI	A,CMD$SEL$DRIVE		;05H,  Select Drive CMD, drive in [B]
	CALL	DATA$TO$FPGA
	MOV	A,B			;Get selected drive
	CALL	DATA$TO$FPGA		
	CALL	GET$FPGA$TO$S100	;Wait for returned ACK signal 
	CMP	B 
	RET				;Returns with drive in A and Z if OK


SEL$IDE$A:
	LDA	@CURRENT$IDE$MODE	;Is it the old Duel IDE/CF card interface or this new interface
	CPI	LOCAL$IDE$MODE		;Is local control of IDE required
	JZ	SEL$WITH$CMD$DRIVE	;Is CMD approach 
					;Else must be External IDE Ports (Mode 0 or 2)
	MVI	A,'A'
	STA	@CURRENT$DRIVE		;Store it as A: Drive
	MVI	A,IDE$CS$1			
	STA	@CURRENT$DRIVE$PORT	;Select drive 0 (CF Card A:)
	OUT	IDEDrive		;Port 034H
	
	MOV	D,A
	MVI	E,REGsector		;Send info to drive on IDE LED circuit (on FPGA+IDE Board)
	CALL	IDEwr8D	

	LDA	@CURRENT$IDE$MODE	;Is CMD Info also required
	CPI	EXT$IDE$INFO$MODE	
	JNZ	SELECT$A$DONE		;Must be Mode 0	

	MVI	A,CMD$SEL$DRIVE		;05H  (Re)select an already initilized CF or SD Card
	CALL	DATA$TO$FPGA

	MVI	A,'A'
	MOV	B,A			;Store for below
	CALL	DATA$TO$FPGA		;Indicate Drive A is required
	CALL	GET$FPGA$TO$S100	;Get the returned confirm data
	CMP	B
	JNZ	BAD$A$SEL$DRIVE
	
SELECT$A$DONE:				;Ret Z drive letter in A	
	XRA	A
	MVI	A,'A'
	RET

BAD$A$SEL$DRIVE:
	MVI	A,'A'
	STA	@CURRENT$DRIVE		;Store it default A: Drive
	XRA	A
	DCR	A
	RET				;RET NZ if a problem

	
SEL$IDE$B:
	LDA	@CURRENT$IDE$MODE	;Is it the old Duel IDE/CF card interface or this new interface
	CPI	LOCAL$IDE$MODE		;Is local control of IDE required
	JZ	SEL$WITH$CMD$DRIVE	;Same CMD approach as SD cards
					;Must be External IDE Ports (Mode 0 or 2)
	MVI	A,'B'
	STA	@CURRENT$DRIVE		;Store it as B: Drive
	MVI	A,IDE$CS$2			
	STA	@CURRENT$DRIVE$PORT	;Select drive 1 (CF Card B:)
	OUT	IDEDrive		;Port 034H
	
	MOV	D,A
	MVI	E,REGsector		;Send info to drive on IDE LED circuit (on FPGA+IDE Board)
	CALL	IDEwr8D	

	LDA	@CURRENT$IDE$MODE	;Is CMD Info also required
	CPI	EXT$IDE$INFO$MODE	
	JNZ	SELECT$B$DONE		;Must be Mode 0		

	MVI	A,CMD$SEL$DRIVE		;05H  (Re)select an already initilized CF or SD Card
	CALL	DATA$TO$FPGA

	MVI	A,'B'
	MOV	B,A			;Store for below
	CALL	DATA$TO$FPGA		;Indicate Drive B is required
	CALL	GET$FPGA$TO$S100	;Get the returned confirm data
	CMP	B
	CALL	BAD$B$SEL$DRIVE

SELECT$B$DONE:
	XRA	A
	MVI	A,'B'
	RET
	
BAD$B$SEL$DRIVE:
	MVI	A,'B'
	STA	@CURRENT$DRIVE		;Store it default A: Drive
	XRA	A
	DCR	A
	RET				;RET NZ if a problem



SET$LBA:				;Set a new logical block address
	LDA	@TRK			;Store current data
	STA	@TRK1
	LDA	@SEC
	STA	@SEC1
	
	CALL	GETHEX			;TRACK LOW, get 2 more HEX digits
	JNC	SKIP$CR
	CPI	CR			;If CR here then no data update
	JZ	NO$UPDATE
	JMP	BAD$FORMAT$ENTRY	
SKIP$CR:	
	STA	@TRK

	CALL	ZCI			;Format is XX,XX
	CPI	','
	JNZ	BAD$FORMAT$ENTRY

IF CPM 
ELSE
	MVI	C,','
	CALL	ZCO
ENDIF
	call	GETHEX			;SECTOR, get 2 more HEX digits
	JC	NO$UPDATE

	
	MOV	C,A
	LDA	@FIRST$SEC
	ORA	A
	JZ	IS$0$FIRST		;Sec 0 is valid
	MOV	A,C
	ORA	A
	JZ	RANGE$ERROR		;Sectors numbered 1,2,3 

IS$0$FIRST:
	LDA	@MAXSEC
	CMP	C
	JC	RANGE$ERROR
	JZ	RANGE$ERROR
	MOV	A,C
	STA	@SEC
	JMP	UPDATE$OK

RANGE$ERROR:
	CALL	PAST$MAX
	LDA	@FIRST$SEC
	STA	@SEC	
	JMP	UPDATE$OK
 
BAD$FORMAT$ENTRY:
	LXI	D,BAD$LBA$MSG
	CALL	PSTRING
NO$UPDATE:	
	LDA	@TRK1			;Store current data
	STA	@TRK
	LDA	@SEC1
	STA	@SEC
	xra	A			;Ret NZ if no new data is NOT OK
	DCR	A
	RET
	
UPDATE$OK:
	CALL	ZCRLF
	xra	A			;Ret Z if no new data is OK
	RET

PAST$MAX:
	LXI	D,SEC$RANGE$MSG		;Error Sector range is
	CALL	PSTRING
	LDA	@FIRST$SEC
	CALL	HEXOUT
	MVI	C,'-'
	CALL	ZCO
	LDA	@MAXSEC
	CALL	HEXOUT
	CALL	ZCRLF
	RET	
	
;
GETHEX:
	call	ZCI			;GET A CHARACTER, convert to UC, ECHO it
	call	UPPER	
	cpI	ESC
	jz	HEXABORT$NO$ECHO
	cpi	'/'			;check 0-9, A-F
	jc	HEXABORT$NO$ECHO
	cpi	'F'+1
	jnc	HEXABORT$NO$ECHO
IF CPM 
ELSE
	PUSH	B
	PUSH	PSW
	MOV	C,A
	CALL	ZCO
	POP	PSW
	POP	B
ENDIF
	call	ASBIN			;Convert to binary
	rlc				;Shift to high nibble
	rlc
	rlc
	rlc
	MOV	b,A			;Store it
	
	call	ZCI			;GET second CHARACTER, convert to UC, ECHO it
	call	UPPER	
	cpi	ESC
	jz	HEXABORT$NO$ECHO
	CPI	CR
	JZ	ONE$DIGIT
	
	cpi	'/'			;check 0-9, A-F
	jc	HEXABORT$NO$ECHO
	cpi	'F'+1
	jnc	HEXABORT$NO$ECHO
IF CPM 
ELSE
	PUSH	B
	PUSH	PSW
	MOV	C,A
	CALL	ZCO
	POP	PSW
	POP	B
ENDIF
	call	ASBIN			;Convert to binary
	ora	B			;add in the first digit
	ora	A			;To return NC
	ret
HEXABORT$NO$ECHO:
	scf				;Set Carry flag
	ret
;
ONE$DIGIT:				;A CR wase ntered after only 1 digit
	MOV	A,B
	RAR				;Shift to high nibble back down
	RAR
	RAR
	RAR
	ANI	0FH
	ORA	A
	RET

GET$HL$HEX:
	CALL	GETCMD			;Get a 2 characters from keyboard for H
	CPI	ESC
	JZ	HEXABORT
	CPI	'/'			;check 0-9, A-F
	JC	HEXABORT
	CPI	'F'+1
	JNC	HEXABORT
	CALL	ASBIN			;Convert to binary
	RLC				;Shift to high nibble
	RLC
	RLC
	RLC
	MOV	B,A			;Store it
	CALL	GETCMD			;Get 2nd character from keyboard & ECHO
	CPI	CR
	JZ	HEXABORT
	CPI	ESC
	JZ	HEXABORT
	CPI	'/'			;check 0-9, A-F
	JC	HEXABORT
	CPI	'F'+1
	JNC	HEXABORT
	CALL	ASBIN			;Convert to binary
	ORA	B			;Add in the first digit
	MOV	H,A

	CALL	GETCMD			;Get a 2 characters from keyboard for L
	CPI	ESC
	JZ	HEXABORT
	CPI	'/'			;check 0-9, A-F
	JC	HEXABORT
	CPI	'F'+1
	JNC	HEXABORT
	CALL	ASBIN			;Convert to binary
	RLC				;Shift to high nibble
	RLC
	RLC
	RLC
	MOV	B,A			;Store it
	CALL	GETCMD			;Get 2nd character from keyboard & ECHO
	CPI	CR
	JZ	HEXABORT
	CPI	ESC
	JZ	HEXABORT
	CPI	'/'			;check 0-9, A-F
	JC	HEXABORT
	CPI	'F'+1
	JNC	HEXABORT
	CALL	ASBIN			;Convert to binary
	ORA	B			;Add in the first digit
	MOV	L,A
	ORA	A			;To return NC
	RET
HEXABORT:
	STC				;Set Carry flag 
	RET



				
driveid:
;	LDA	@CURRENT$IDE$MODE	;Get IDE Mode
;	CPI	LOCAL$IDE$MODE		;2H
;	JNZ	ALREADY$IDE		;External mode
	
;	MVI	A,CMD$SET$IDE$MODE	;38H set IDE temporly to EXTERNAL mode 
;	CALL	DATA$TO$FPGA
;	MVI	A,EXT$IDE$MODE		;0H, IDE to External IDE mode for Drive ID
;	CALL	DATA$TO$FPGA


ALREADY$IDE:
	CALL	IDEwaitnotbusy		;Do the IDEntify drive command, and return with the buffer
					;filled with info about the drive
	RC				;If Busy return NZ
	MVI	D,COMMANDid
	MVI	E,REGcommand
	CALL	IDEwr8D			;issue the command

	CALL	IDEwaitdrq		;Wait for Busy=0, DRQ=1
	JC	SHOWerrors
	CALL	CLEAR$ID$BUFFER		;Clear ID Buffer
	
	MVI	B,0			;256 words
	LXI     H,IDbuffer		;Store data here
	CALL	FAST$RD$SEC$ID		;Get 256 words of data from REGdata port to [HL]

;	LDA	@CURRENT$IDE$MODE	;Get IDE Mode
;	CPI	LOCAL$IDE$MODE		;2H
;	JNZ	driveid$Done		;If already external, just return
	
;	MVI	A,CMD$SET$IDE$MODE	;38H set IDE back to origional local mode 
;	CALL	DATA$TO$FPGA
;	MVI	A,LOCAL$IDE$MODE	;Get Store flag
;	CALL	DATA$TO$FPGA
driveid$Done
	XRA	A
	RET				;Ret Z
	


IDE$PRINT$LBA:
	LXI	D,DMA$STRING		;'Buffer='
	CALL	PSTRING
	LHLD	@DMA
	MOV	A,H
	CALL	HEXOUT
	MOV	A,L
	CALL	HEXOUT

	LXI	D,DRIVE$STRING		;'CF Card'
	CALL	PSTRING
	LDA	@CURRENT$DRIVE		;ASCII for DRIVE
	MOV	C,A
	CALL	ZCO
	
	LXI	D,MODE$STRING		;': IDE Mode='
	CALL	PSTRING
	LDA	@CURRENT$IDE$MODE
	ORA	A
	JZ	IS$MODE$0
	CPI	1
	JZ	IS$MODE$1
	MVI	C,'2'
	CALL	ZCO
	JR	OVER$MODE
IS$MODE$0:
	MVI	C,'0'
	CALL	ZCO
	JR	OVER$MODE
IS$MODE$1:
	MVI	C,'1'
	CALL	ZCO
OVER$MODE:	
	LXI	D,SEC$RANGE		;'(Sec Range '
	CALL	PSTRING
	LDA	@FIRST$SEC
	CALL	HEXOUT
	MVI	C,'-'			;'xx-xx
	CALL	ZCO
	LDA	@MAX$SEC
	CALL	HEXOUT
	LXI	D,TRACK$STRING		;' Track '
	CALL	PSTRING
	LDA	@TRK
	CALL	HEXOUT
	LXI	D,SECTOR$STRING		;' Sector '
	CALL	PSTRING
	LDA	@SEC
	CALL	HEXOUT
	RET

POS$MSG$STRING:				;Send character string message to the FPGA_DC Board
	MOV	C,M
	CALL	ZCO
	MOV	A,M			;UP TO THE FIRST 0H
	ORA	A
	RZ
	CALL	DATA$TO$FPGA		
	INX	H
	JR	POS$MSG$STRING


printname:			;Send text up to [B]	
	INX	H		;Text is low byte high byte format
	MOV	C,M
	CALL	ZCO	
	DCX	H
	MOV	C,M
	CALL	ZCO
	INX	H
	INX	H
	DCR	B
	JNZ	printname
	ret

ZCRLF:
	PUSH	PSW
	PUSH	BC
	MVI	C,CR
	CALL	ZCO
	MVI	C,LF
	CALL	ZCO
	POP	BC
	POP	PSW
	RET

ZEOL:				;CR and clear current line
	MVI	C,CR
	CALL	ZCO
	MVI	C,CLEAR		;Note hardware dependent, (Use 80 spaces if necessary)
	CALL	ZCO
	RET

ZCSTS:
  IF  CPM
	PUSH	B
	PUSH	D
	PUSH	H
	MVI	C,CONST
	CALL	BDOS		;Returns with 1 in [A] if character at keyboard
	POP	H
	POP	D
	POP	B
	CPI	1
	RET
  ELSE	
	IN	0H		;Get Character in [A]
	ANI	02H
	RZ
	MVI	A,01H
	ORA	A
	RET
  ENDIF
	
 
ZCO:				;Write character that is in [C]
  IF  CPM
	PUSH	PSW
	PUSH	B
	PUSH	D
	PUSH	H
	MOV	E,C
	MVI	C,WRCON
	CALL	BDOS
	POP	H
	POP	D
	POP	B
	POP	PSW
	RET
  ELSE	
	PUSH	PSW	
ZCO1:	IN   	0H		;Show Character
	ANI	04H
	JZ	ZCO1
	MOV	A,C
	OUT	1H
	POP	PSW
	RET
  ENDIF

ZCI:				;Return keyboard character in [A]
  IF CPM
	PUSH	B
	PUSH	D
	PUSH	H
	MVI	C,RDCON
	CALL	BDOS
	POP	H
	POP	D
	POP	B
	RET
  ELSE
ZCI1:	IN	0H		;Get Character in [A]
	ANI	02H
	JZ	ZCI1
	IN	01H
	RET
  ENDIF


;	Print a string in [DE] up to '$'

PSTRING:
 IF CPM
	MVI	C,PRINT
	JMP	BDOS		;PRINT MESSAGE, 
 ELSE
	PUSH	B
	PUSH	D
	PUSH	H
	XCHG
PSTRX:	MOV	A,M
	CPI	'$'
	JZ	DONEP
	MOV	C,A
	CALL	ZCO
	INX	H
	JMP	PSTRX
DONEP:	POP	H
	POP	D
	POP	B
	RET
  ENDIF


SHOWerrors:
   IF	NOT DEBUG
	ORA	A		;Set NZ flag
	STC			;Set Carry Flag
	RET
   ELSE
	CALL	ZCRLF
	MVI	E,REGstatus	;Get status in status register
	CALL	IDErd8D
	MOV	A,D
	ANI	1H
	JNZ	MoreError	;Go to  REGerr register for more info
				;All OK if 01000000
	PUSH	PSW		;save for return below
	ANI	80H
	JZ	NOT7
	LXI	D,DRIVE$BUSY	;Drive Busy (bit 7) stuck high.   Status = 
	CALL	PSTRING
	JMP	DONEERR
NOT7:	ANI	40H
	JNZ	NOT6
	LXI	D,DRIVE$NOT$READY  ;Drive Not Ready (bit 6) stuck low.  Status = 
	CALL	PSTRING
	JMP	DONEERR
NOT6:	ANI	20H
	JNZ	NOT5
	LXI	D,DRIVE$WR$FAULT  ;Drive write fault.    Status =
	CALL	PSTRING
	JMP	DONEERR
NOT5	LXI	D,UNKNOWN$ERROR
	CALL	PSTRING
	JMP	DONEERR

MoreError:			;Get here if bit 0 of the status register indicted a problem
	MVI	E,REGerr	;Get error code in REGerr
	CALL	IDErd8D
	MOV	A,D
	PUSH	PSW

	ANI	10H
	JZ	NOTE4
	LXI	D,SEC$NOT$FOUND
	CALL	PSTRING
	JMP	DONEERR

NOTE4:	ANI	80H
	JZ	NOTE7
	LXI	D,BAD$BLOCK
	CALL	PSTRING
	JMP	DONEERR
NOTE7:	ANI	40H
	JZ	NOTE6
	LXI	D,UNRECOVER$ERR
	CALL	PSTRING
	JMP	DONEERR
NOTE6:	ANI	4H
	JZ	NOTE2
	LXI	D,INVALID$CMD
	CALL	PSTRING
	JMP	DONEERR
NOTE2:	ANI	2H
	JZ	NOTE1
	LXI	D,TRK0$ERR
	CALL	PSTRING
	JMP	DONEERR
NOTE1:	LXI	D,UNKNOWN$ERROR1
	CALL	PSTRING
	JMP	DONEERR

DONEERR:POP	PSW
	PUSH	PSW
	CALL	ZBITS
	CALL	ZCRLF
	POP	PSW
	ORA	A		;Set NZ flag
	STC			;Set Carry flag
	RET
   ENDIF


;------------------------------------------------------------------
; Print a 16 bit number in RAM located @ [HL] 
; (Note Special Low Byte First. Used only for Drive ID)

printparm:
	INX	H	;Index to high byte first
	MOV	a,M
	CALL	HEXOUT
	DCX	H	;Now low byte
	MOV	a,M
	CALL	HEXOUT
	RET

HHEXOUT:				;No registers altered
	CALL	HEXOUT
	PUSH	PSW
	PUSH	B
	MVI	C,'H'
	CALL	ZCO
	POP	B
	POP	PSW
	RET
	
; Print an 8 bit number, located in [A]
HEXOUT:	PUSH	PSW			;No registers altered
	PUSH	B
	PUSH	PSW
	RRC
	RRC
	RRC
	RRC
	CALL	ZCONV
	POP	PSW
	CALL	ZCONV
	POP	B
	POP	PSW
	RET

ZCONV:	ANI	0FH		;HEX to ASCII and print it
	ADI	90H
	DAA
	ACI	40H
	DAA
	MOV	C,A
	CALL	ZCO
	RET

;DISPLAY BIT PATTERN IN [A]
ZBITS:	PUSH	PSW
	PUSH	B
	PUSH	D
	MOV	E,A		
	MVI	B,8
BQ2:	DB	0CBH,23H	;Z80 Op code for SLA A,E
	MVI	A,18H
	ADC	A
	MOV	C,A
	CALL	ZCO
	DJNZ	BQ2
	POP	D
	POP	B
	POP	PSW
	RET

GETCMD:	CALL	ZCI		;GET A CHARACTER, convert to UC, ECHO it
	CPI	ESC
	RZ			;Don't echo an ESC
	CALL	UPPER
  IF NOT CPM
	PUSH	PSW		;Save it
	PUSH	B
 	MOV	C,A
	CALL	ZCO		;Echo it
	POP	B
	POP	PSW		;get it back
 ENDIF
	RET

				;Convert LC to UC
UPPER:	CPI	'a'		;must be >= lowercase a
	RC			; else go back...
	CPI	'z'+1		;must be <= lowercase z
	RNC			; else go back...
	SUI	'a'-'A'		;subtract lowercase bias
	RET

				;ASCII TO BINARY CONVERSION ROUTINE
ASBIN:	SUI	30H 
	CPI	0AH 
	RM
	SUI	07H 
	RET


HEXDUMP:			;Print a hexdump of the data in the 512 byte buffer @[HL] (Note NOT @DMA)
	PUSH	PSW		;Save everything
	PUSH	B
	PUSH	D			 
	PUSH	H
	
	CALL	ZCRLF		;CR/LF first
	MVI	D,32		;Print 32 lines total
	MVI	B,16		;16 characters across
	SHLD	@StartLineHex	;Save the buffer location for ASCII display below
	LXI	H,0
	SHLD	@BYTE$COUNT
	
SF172:	CALL	ZCRLF
	LHLD	@BYTE$COUNT
	MOV	A,H
	CALL	HEXOUT		;Print byte count in sector
	MOV	A,L
	CALL	HEXOUT		
	PUSH	D
	LXI	D,16
	DAD	D
	POP	D
	SHLD	@BYTE$COUNT	;store for next time
	CALL	BLANK
	LHLD	@StartLineHex	;BUFFER LOCATION
	SHLD	@StartLineASCII	;Store for ASCII display below
SF175:	MOV	A,M
	CALL	LBYTE		;Display [A] on CRT/LCD
	INX	H
	DJNZ	SF175
	SHLD	@StartLineHex	;Save for next line later
	CALL	ShowAscii	;Now translate to ASCII and display
	MVI	B,16		;16 characters across for next line
	DCR	D
	JNZ	SF172		;Have we done all 32 lines
;
	CALL	ZCRLF
	POP   H			;Get back origional registers
	POP   D
	POP   B
	POP   PSW
	RET
	
ShowAscii:			;Now show as ascii info
	LHLD	@StartLineASCII
	MVI	B,16		;16 ASCII characters across
XF172:	CALL	BLANK		;send a space character
	CALL	BLANK
XF175:	MOV	A,M
	ANI	7FH
	CPI	' ' 		;FILTER OUT CONTROL CHARACTERS
	JNC	XT33
XT22:	MVI	A,'.'
XT33:	CPI	07CH
	JNC	XT22
	MOV	C,A		;SET UP TO SEND
	PUSH	B
	CALL	ZCO
	POP	B
	INX	H		;Next position in buffer
	DJNZ	XF175
	RET
;
BLANK:	PUSH	B
	PUSH	H
	MVI	C,' '
	CALL	ZCO
	POP	H
	POP	B
	RET
;
LBYTE:	PUSH	PSW
	RRC
	RRC
	RRC
	RRC
	CALL	SF598
	POP	PSW
SF598:	CALL	ZCONV
	RET

CHK$SEC:			;Compare current "CPM" sector to max "CPM" sector
	PUSH	B		;Save
	MOV	C,A		;C <- Current Sector
	LDA	@MAX$SEC	;Retrieve max sector number
	MOV	B,A		;B <- max sector number
	MOV	A,C		;Get current sector back in A for compare (and return with it in A)
	CMP	B		;Current : Max
	POP	B
	RET			;Return with compare status. (Carry => Max > Current)
;
;
;================================================================================================
;
;      IDE Drive BIOS Routines written in a format that can be used directly with CPM3 
;
;================================================================================================
;
IDEinit:
	PUSH	B		;Save used registers
	PUSH	D
				;Initialze the 8255 and drive then do a hard reset on the drive, 
	MVI	A,READcfg8255	;Config 8255 chip (10010010B), read mode on return
	OUT	IDEportCtrl	;Config 8255 chip, READ mode
				
				;Hard reset the disk drive 
				;For some reason some CF cards need to the RESET line 
				;pulsed very carefully. You may need to play around   
	MVI	A,IDErstline	;with the pulse length. Symptoms are: incorrect data comming
	OUT	IDEportC	;back from a sector read (often due to the wrong sector being read)
				;I have a (negative)pulse of 2.7uSec. (10Mz Z80, two IO wait states).
	MVI	B,20H		;Which seem to work for the 5 different CF cards I have.
ResetDelay:
	DCR	B
	JNZ	ResetDelay	;Delay (reset pulse width)
	XRA	A
	OUT	IDEportC	;No IDE control lines asserted (just bit 7 of port C)
	CALL	DELAY$SHORT	;Short Delay
	CALL	IDEwaitnotbusy	;Wait for drive

	JC	WaitInitErr
	MVI	D,11100000b	;Data for IDE SDH reg (512bytes, LBA mode,single drive,head 0000)
				;For Trk,Sec,head (non LBA) use 10100000
				;Note. Cannot get LBA mode to work with an old Seagate Medalist 6531 drive.
				;have to use the non-LBA mode. (Common for old hard disks).

	MVI	E,REGshd	;00001110,(0EH) for CS0,A2,A1,  
	CALL	IDEwr8D		;Write byte to select the MASTER device

	MVI	B,02H		;<<< Adjust delay time for hard disks to get up to speed (Currently ~ 2 seconds)
				;<<< This delay need to be much longer for actual Hard Disks, OK for CF Cards.
WaitInit:
	MVI	E,REGstatus	;Get status after initilization
	CALL	IDErd8D		;Check Status (info in [D])
	MOV	A,D
	ANI	80H
	JNZ	WaitInitL	;Need a longer wait...
	POP	D		;Restore registers
	POP	B
	RET			;Return. Well check for errors when we get back
WaitInitL:
	MVI	A,2
	CALL	DELAY$LONG	;Long delay, drive has to get up to speed
	DCR	B
	JNZ	WaitInit
	XRA	A
	DCR	A
	POP	D
	POP	B
	RET			;Return NZ. Well check for errors when we get back

;;;;;;
WaitInitErr:
	XRA	A
	DCR	A		;Return NZ (error)
	POP	D		;Restore Registers
	POP	B
	RET			;Return - check for errors there.
	
DELAY$LONG:			;Long delay (Seconds)
	STA	@DELAYStore
	PUSH	B
	LXI	B,0FFFFH	;<<< May need to adjust delay time to allow cold drive to
DELAY2:	LDA	@DELAYStore	;    get up to speed.
DELAY1:	DCR	A
	JNZ	DELAY1
	DCX	B
	MOV	A,C
	ORA	B
	JNZ	DELAY2
	POP	B
	RET


DELAY$SHORT:			;DELAY ~32 MS (DOES NOT SEEM TO BE CRITICAL)
	MVI	A,40
DELAY3:	MVI	B,0
M0:	DJNZ	M0
	DCR	A
	JNZ     DELAY3 
	RET

;-------------------------------------------------------------------------------------------------------------

EXT$READSECTOR:				;EXT$IDE$MODE Mode Sector Read @DMA. (Note @DMA is updated)
	call	SET$IDE$TRK$SEC		;Tell which sector we want to read from.
					;CPM TRK/SEC info to Drive TRK/SEC/Head
					;Send before error check so info is updated
	
	CALL	IDEwaitnotbusy		;Make sure drive is ready
	JC	SHOWerrors		;Returned with NZ set if error

	MVI	D,COMMANDread
	MVI	E,REGcommand
	CALL	IDEwr8D			;Send sec read command to drive.
	CALL	IDEwaitdrq		;wait until it's got the data
	JC	SHOWerrors
		
	LHLD  	@DMA			;DMA address
	PUSH	BC
	MVI	B,0			;Read 512 bytes to [HL] (256X2 bytes)
	MVI	C,IDEportA	

Ex$MoreRD16:
	MVI	A,REGdata		;REG regsiter address
	OUT	IDEportC	

	ORI	IDErdline		;08H+40H, Pulse RD line
	OUT	IDEportC
	
	INI				;Port A->(HL++)   (For speed, this is done 512 times)
	INR	C
	INI				;PORT B->(HL++)
	DCR	C
	MVI	A,REGdata		;Deassert RD line
	OUT	IDEportC
	DJNZ	Ex$MoreRD16

	SHLD  	@DMA			;Update the @DMA address
	POP	BC
	MVI	E,REGstatus
	CALL	IDErd8D
	MOV	A,D
	ANI	1H
	CNZ	SHOWerrors		;If error display status
	RET


;-------------------------------------------------------------------------------------------------------------

READSECTOR:				;Read a CF Card or SD Card Sector
	LDA	@CURRENT$IDE$MODE	;Is it the old Duel IDE/CF card interface or this new interface
	CPI	LOCAL$IDE$MODE		;01H, Is local/FPGAS_DC board control of IDE interface required
	JZ	READ$WITH$CMD$SECTOR	;If local mode, then use the FPGA DC local CMD driven mode

	LDA	@CURRENT$DRIVE		;Assume current drive is already selected
	CPI	'A'
	JZ	IDE$RD	
	CPI	'B'
	JZ	IDE$RD
	JP	READ$WITH$CMD$SECTOR	;Must be Floppy etc. (Pure CMD  mode)
	
IDE$RD:					;Read an IDE Card sector, specified by the 3 bytes in LBA
					;Z on success, NZ call error routine if problem

	call	SET$IDE$TRK$SEC		;Tell which sector we want to read from.
					;CPM TRK/SEC info to Drive TRK/SEC/Head
					;Send before error check so info is updated
	
	
	LDA	@BOARD$TYPE		;<<<< 1 for FPGA_DC Board >>>
	CPI	1
	JNZ	FAST$READ$IDE$SECTOR

	LDA	@CURRENT$IDE$MODE	;Is it the old Duel IDE/CF card interface or this new interface
	CPI	EXT$IDE$MODE		;Is INT driven Mode 0  control of IDE required
	JRZ	FAST$READ$IDE$SECTOR
	
	MVI	A,INFO$READ$SECTOR	;42H, Send info, reading sector to FPGA_DC Z80
	CALL	DATA$TO$FPGA
	LHLD  	@DMA			;DMA address
	MOV	A,H
	CALL	DATA$TO$FPGA
	MOV	A,L
	CALL	DATA$TO$FPGA
	
	LDA	@TRK			;Send current track
	CALL	DATA$TO$FPGA
	LDA	@SEC			;Send current sector
	CALL	DATA$TO$FPGA


FAST$READ$IDE$SECTOR:			;Fast Read no updates, Drive,track,Sec already setup.
	CALL	IDEwaitnotbusy		;make sure drive is ready
	JC	SHOWerrors		;Returned with NZ set if error
	MVI	D,COMMANDread
	MVI	E,REGcommand
	CALL	IDEwr8D			;Send sec read command to drive.
	CALL	IDEwaitdrq		;wait until it's got the data
	JC	SHOWerrors
		
	LHLD  	@DMA			;DMA address

FAST$RD$SEC$ID:				;HL already setup
	PUSH	BC
	MVI	B,0			;Read 512 bytes to [HL] (256X2 bytes)
	MVI	C,IDEportA	

MoreRD16:
	MVI	A,REGdata		;REG regsiter address
	OUT	IDEportC	

	ORI	IDErdline		;08H+40H, Pulse RD line
	OUT	IDEportC
	
	INI				;Port A, (C)->(HL++)   (For speed, this is done 512 times)
	INR	C
	INI				;PORT B, (C)->(HL++)
	DCR	C

	MVI	A,REGdata		;Deassert RD line
	OUT	IDEportC
	DJNZ	MoreRD16

	POP	BC
	MVI	E,REGstatus
	CALL	IDErd8D
	MOV	A,D
	ANI	1H
	CNZ	SHOWerrors		;If error display status
	RET


SHOW$SECTOR$READ$ERROR
	LXI	D,SR$ERR$MSG		;'Card sector READ error. TRK,SEC = $'
	CALL	PSTRING
	LDA	@TRK			;TRK = (8 bits)
	CALL	HEXOUT
	MVI	C,','
	CALL	ZCO
	LDA	@SEC
	CALL	HEXOUT			;XX,XX
	RET



READ$WITH$CMD$SECTOR:
	MVI	A,CMD$READ$SECTOR	;10H,  Send CMD_READ_SECTOR command 
	CALL	DATA$TO$FPGA
	
	LHLD  	@DMA			;To update USB Terminal about where the DMA address is
	MOV	A,H
	CALL	DATA$TO$FPGA
	MOV	A,L
	CALL	DATA$TO$FPGA

	LDA	@TRK			;Send Track
	CALL	DATA$TO$FPGA
	
	LDA	@SEC			;Send Sector
	CALL	DATA$TO$FPGA
	
	LHLD  	@DMA			;DMA address
	MVI	B,0			;Read 512 bytes to [HL] (256X2 bytes)

;------------------------

SD$SEC$RD$LOOP:				;<<<< LOOP CF/SD Card Sector READ >>>>
	CALL	GET$FPGA$TO$S100	;Get data in [A] back to S100 
	MOV	M,A
	INX	H
	CALL	GET$FPGA$TO$S100	;Get data in [A] back to S100 
	MOV	M,A
	INX	H
	DJNZ	SD$SEC$RD$LOOP		;Get all 2X255 bytes

;-------------------------
	CALL	GET$FPGA$TO$S100	;Get CMD$CPMPLETE/ERROR FLAG
	CPI	CMD$COMPLETE		;0F0H, Flag returned by DC board to signal the previous CMD was completed OK
	JNZ	SHOW$SECTOR$READ$ERROR	
	XRA	A
	RET				;Ret Z if all OK
	
	
;--------------------------------------------------------------------------------------------------------------------
WRITESECTOR:
	LDA	@CURRENT$IDE$MODE	;Is it the old Duel IDE/CF card interface or this new interface
	CPI	LOCAL$IDE$MODE		;Is local control of IDE required
	JZ	WRITE$WITH$CMD$SECTOR	;If local mode then send an IDE/CF Card the same as a SD Drive (Pure CMD  mode)

	LDA	@CURRENT$DRIVE		;Assume current drive is already selected
	CPI	'A'
	JZ	IDE$WR	
	CPI	'B'
	JZ	IDE$WR
	JP	WRITE$WITH$CMD$SECTOR	;Must be an Floppy Drive (Pure CMD  mode)
	
IDE$WR:					;Write a sector, specified by the 3 bytes in LBA 
					;Z on success, NZ to error routine if problem
	call	SET$IDE$TRK$SEC		;Tell which sector we want to read from.
					;CPM TRK/SEC info to Drive TRK/SEC/Head
					;Send before error check so info is updated

	LDA	@BOARD$TYPE		;<<<< 1 for FPGA_DC Board >>>
	CPI	1
	JNZ	FAST$WRITE$IDE$SECTOR

	MVI	A,INFO$WRITE$SECTOR	;42H, Send info, reading sector to FPGA_DC Z80
	CALL	DATA$TO$FPGA
	LHLD  	@DMA			;DMA address
	MOV	A,H
	CALL	DATA$TO$FPGA
	MOV	A,L
	CALL	DATA$TO$FPGA
	
	LDA	@TRK			;Send current track
	CALL	DATA$TO$FPGA
	LDA	@SEC			;Send current sector
	CALL	DATA$TO$FPGA

FAST$WRITE$IDE$SECTOR:			;Fasst Write no updates
	CALL	IDEwaitnotbusy		;make sure drive is ready
	JC	SHOWerrors

	MVI	D,COMMANDwrite
	MVI	E,REGcommand
	CALL	IDEwr8D			;tell drive to write a sector
	CALL	IDEwaitdrq		;wait unit it wants the data
	JC	SHOWerrors

	LHLD    @DMA
	MVI	B,0			;256X2 bytes

	MVI	A,WRITEcfg8255
	OUT	IDEportCtrl
	
	PUSH	BC
	MVI	C,IDEportA
WRSEC16:
	OUTI				;(HL++)->Port A  (For speed, this is done 512 times)
	INR	C
	OUTI				;(HL++)->Port B
	DCR	C
	
	MVI	A,REGdata
	PUSH	PSW
	OUT	IDEportC		;Send write command
	ORI	IDEwrline		;Send WR pulse
	OUT	IDEportC
	POP	PSW
	OUT	IDEportC
	DJNZ	WRSEC16
        POP	BC
	
	MVI	A,READcfg8255		;Set 8255 back to read mode
	OUT	IDEportCtrl	

	MVI	E,REGstatus
	CALL	IDErd8D
	MOV	A,D
	ANI	1H
	CNZ	SHOWerrors	;If error display status
	RET


SHOW$SECTOR$WRITE$ERROR:
	LXI	D,SR$ERR$MSG		;'CF card sector READ error. TRK,SEC = $'
	CALL	PSTRING
	LDA	@TRK			;TRK = (8 bits)
	CALL	HEXOUT
	MVI	C,','
	CALL	ZCO
	LDA	@SEC
	CALL	HEXOUT			;XX,XX
	CALL	ZCRLF
	RET

	
WRITE$WITH$CMD$SECTOR:
	MVI	A,CMD$WRITE$SECTOR	;11H,  Send CMD_WRITE_SECTOR command 
	CALL	DATA$TO$FPGA
	
	LHLD  	@DMA			;DMA address
	MOV	A,H
	CALL	DATA$TO$FPGA
	MOV	A,L
	CALL	DATA$TO$FPGA

	LDA	@TRK			;Send Track
	CALL	DATA$TO$FPGA
	
	LDA	@SEC			;Send Sector
	CALL	DATA$TO$FPGA
	
	LHLD  	@DMA			;DMA address
	MVI	B,0			;Read 512 bytes to [HL] (256X2 bytes)

;------------------------

SD$SEC$WR$LOOP:				;<<<< LOOP SD Card Sector WRITE >>>>
	MOV	A,M
	CALL	DATA$TO$FPGA		;Send data in [A] to FPGA_DC board
	INX	H
	MOV	A,M
	CALL	DATA$TO$FPGA		;Send data in [A] to FPGA_DC board
	INX	H
	DJNZ	SD$SEC$WR$LOOP		;Get all 2X255 bytes

;-------------------------
	CALL	GET$FPGA$TO$S100	;Get CMD$CPMPLETE/ERROR FLAG
	CPI	CMD$COMPLETE		;0F0H, Flag returned by DC board to signal the previous CMD was completed OK
	JNZ	SHOW$SECTOR$WRITE$ERROR	
	XRA	A
	RET				;Ret Z if all OK


	
;--------------------------------------------------------------------------------------------------------------------


SET$IDE$TRK$SEC:			;Tell which sector we want to read from.
	MVI	A,0			;Send High TRK#
	MOV	D,A
	MVI	E,REGcylinderMSB
	CALL	IDEwr8D

	LDA	@TRK			;We have are in LBA mode so send directly
	MOV	D,A			;Send Low TRK#
	MVI	E,REGcylinderLSB
	CALL	IDEwr8D

	LDA	@SEC			;Get CPM requested sector
	MOV	D,A
	MVI	E,REGsector		;Send info to drive
	CALL	IDEwr8D

	MVI	D,1			;For now, one sector at a time
	MVI	E,REGseccnt
	CALL	IDEwr8D
	RET


wrlba:
	CALL	IDEwaitnotbusy		;Make sure drive isn't busy...
	JC	SHOWErrors		;If error, display status
	LDA	@MAX$SEC		;Are we using the special "no holes" disk format or not?
	CPI	3FH	
	JZ	wrlbaNoHoles		;Special case "no holes"
					;Else any other format (3DH or 3FFH)

wrlbaHoles:				;Write the logical block address to the drive's registers
					;Note we do not need to set the upper nibble of the LBA
					;It will always be 0 for these small drives

	LDA	@SEC			;LBA mode Low sectors go directly 
	STA	@DRIVE$SEC		;For Diagnostic Display Only
	MOV	D,A
	MVI	E,REGsector		;Send info to drive
	CALL	IDEwr8D			;Note: For drive we will have 0 - MAXSEC sectors only
				
	LDA	@TRK	
	STA	@DRIVE$TRK
	MOV	D,L			;Send Low TRK#
	MVI	E,REGcylinderLSB
	CALL	IDEwr8D

	MVI	D,0			;Send High TRK# 0 always
	MVI	E,REGcylinderMSB
	CALL	IDEwr8D

	MVI	D,1			;For now, one sector at a time
	MVI	E,REGseccnt
	CALL	IDEwr8D
	RET


wrlbaNoHoles:
					;See http://s100computers.com/My%20System%20Pages/IDE%20Board/My%20IDE%20Card.htm
					;See bottom of page "CPM installation info"
					;Starting with LBA 0 and without leaving an "holes"
	LHLD	@TRK			;Get the "CPM" requested track High & Low
	MOV	A,L			;Get Low byte of track
	RRC				;Get bottom two bits in high bits of A
	RRC
	ANI	0C0H			;Just what were the bottom two bits (now at the top)
	MOV	C,A			;Save in C
	LDA	@SEC			;Sector number in A
	ANI	03FH			;Take only bottom 6 bits, just in case
	ORA	C			;Add in top 2 bits of track
	STA	@DRIVE$SEC		;For diagnostic display only
	MOV	D,A			;Send info to the drive
	MVI	E,REGsector
	CALL	IDEwr8D

	MOV	A,L			;Get low byte of track again
	RRC				;Extract out just the top 6 bits
	RRC
	ANI	03FH
	MOV	C,A			;Save in C
	MOV	A,H			;Get high byte of track.
	RRC				;Rotate twice, leaving low 2 bits
	RRC				;In upper bits of A
	ANI	0C0H			;Mask all but the two bits we want
	ORA	C			;Add in the top 6 bits of the first track byte
	STA	@DRIVE$TRK
	MOV	D,A			;Send Low TRK#
	MVI	E,REGcylinderLSB
	CALL	IDEwr8D
	
	MOV	A,H			;Get high byte of track
	RRC				;Just the top 6 bits
	RRC
	ANI	03FH
	STA	@DRIVE$TRK+1
	MOV	D,A			;Send High TRK#
	MVI	E,REGcylinderMSB
	CALL	IDEwr8D

	MVI	D,1			;For now, one sector at a time
	MVI	E,REGseccnt
	CALL	IDEwr8D
	RET

IDEwaitnotbusy:				;ie Drive READY if 01000000
	MVI	B,0FFH
	MVI	A,0FFH			;Delay, must be above 80H for 4MHz Z80. Leave longer for slower drives
	STA	@DELAYStore
MoreWait:
	MVI	E,REGstatus		;wait for RDY bit to be set
	CALL	IDErd8D
	MOV	A,D
	ANI	11000000B
	XRI	01000000B
	JZ	DoneNotbusy
	DCR	B	
	JNZ	MoreWait	
	LDA	@DELAYStore		;Check timeout delay
	DCR	A
	STA	@DELAYStore
	JNZ	MoreWait
	STC				;Set carry to indicate an error
	ret
DoneNotBusy:
	ORA	A			;Clear carry it indicate no error
	RET

					;Wait for the drive to be ready to transfer data.
					;Returns the drive's status in Acc
IDEwaitdrq:
	MVI	B,0FFH
	MVI	A,0FFH			;Delay, must be above 80H for 4MHz Z80. Leave longer for slower drives
	STA	@DELAYStore

MoreDRQ:
	MVI	E,REGstatus		;wait for DRQ bit to be set
	CALL	IDErd8D
	MOV	A,D
	ANI	10001000B
	CPI	00001000B
	JZ	DoneDRQ
	DCR	B
	JNZ	MoreDRQ
	LDA	@DELAYStore		;Check timeout delay
	DCR	A
	STA	@DELAYStore
	JNZ	MoreDRQ
	STC				;Set carry to indicate error
	RET
DoneDRQ:
	ORA	A			;Clear carry
	RET

CLEAR$ID$BUFFER:			;Clear the ID Buffer area
	LXI	H,IDBuffer
	LXI	B,512
CLEAR2:	MVI	A,' '
	MOV	M,A
	INX	H
	DCX	B
	MOV	A,C
	ORA	B
	JNZ	CLEAR2
	
	LXI	H,IDBuffer		;Put in 0's for cylinder,heads,sectors etc
	LXI	B,14
CLEAR3:	MVI	A,0
	MOV	M,A
	INX	H
	DCX	B
	MOV	A,C
	ORA	B
	JNZ	CLEAR3
	RET




;------------------------------------ Display CF Card ID --------------------------------
;
ID$PRINT:				;Print the drive model number etc.
	LXI     H,IDbuffer + 12
	MOV	A,M			;If there are zero sectors (High byte) then something wrong
	ORA	A
	JNZ	ID$PRINT2
	INX	H
	MOV	A,M			;(Low Byte)
	ORA	A
	JNZ	ID$PRINT2		;Looks like we have a valid IDE drive
	
	LXI	D,BAD$DRIVE
	CALL	PSTRING
	STC				;Indicate error
	RET
ID$PRINT2:
	LXI	D,DRIVE$INFO
	CALL	PSTRING
	LXI     D, msgmdl	
	CALL	PSTRING
	LXI     H,IDbuffer + 54
	MVI	B,20			;character count in words
	CALL	printname		;Print [HL], [B] X 2 characters
	CALL	ZCRLF
					; print the drive's serial number
	LXI     D, msgsn
	CALL	PSTRING
	LXI     H,IDbuffer + 20
	MVI	B,10			;Character count in words
	CALL	printname
	CALL	ZCRLF
					;Print the drive's firmware revision string
	LXI     D, msgrev
	CALL	PSTRING
	LXI     H,IDbuffer + 46
	MVI	B, 4
	CALL	printname		;Character count in words
	CALL	ZCRLF

					;Tell user whether or not LBA is supported
	LXI	D,msgLBAsup1		;First part of LBA support message
	CALL	PSTRING	
	LDA	IDbuffer+98+1		;Bits 15-10 reserved, 9 LBA, 8 DMA
	ANI	02H
	JNZ	ID$PRINT3		;LBA is supported
	LXI	D,msgLBAnot		;LBA is not supported
	CALL	PSTRING
ID$PRINT3:
	LXI	D,msgLBAsup2
	CALL	PSTRING

					;Print the drive's cylinder, head, and sector specs
	LXI     D, msgcy
	CALL	PSTRING
	LXI     H,IDbuffer + 2
	CALL	printparm
	LXI     D,msghd
	CALL	PSTRING
	LXI     H,IDbuffer + 6
	CALL	printparm
	LXI     D, msgsc
	CALL	PSTRING
	LXI     H,IDbuffer + 12
	CALL	printparm
	CALL	ZCRLF
					;Default position will be first block 
	LXI	H,0
	SHLD	@SEC			;Default to Track 0, Sec 0
	SHLD	@TRK
	LXI	H,buffer		;Set DMA address to buffer
	SHLD	@DMA

	CALL	IDEinit			;For some reason this need to be here after getting the drive ID.
					;Otherwise sector #'s are off by one!
					;Comment, perhaps this was because the card wasn't really in LBA mode?
	RET

;------------------------------------------------------------------
; Low Level 8 bit R/W to the drive controller.  These are the routines that talk
; directly to the drive controller registers, via the 8255 chip.  
; Note the 16 bit I/O to the drive (which is only for SEC R/W) is done directly 
; in the routines READSECTOR & WRITESECTOR for speed reasons.
;
IDErd8D:				;READ 8 bits from IDE register in [E], return info in [D]
	MOV	A,E
	OUT	IDEportC		;drive address onto control lines

	ORI	IDErdline		;RD pulse pin (40H)
	OUT	IDEportC		;assert read pin

	IN	IDEportA
	MOV	D,A			;return with data in [D]

	MOV	A,E			;<---Ken Robbins suggestion
	OUT	IDEportC		;deassert RD pin

	XRA	A
	OUT	IDEportC		;Zero all port C lines
	ret


IDEwr8D:				;WRITE Data in [D] to IDE register in [E]
	MVI	A,WRITEcfg8255		;Set 8255 to write mode
	OUT	IDEportCtrl

	MOV	A,D			;Get data (or CMD) put it in 8255 A port
	OUT	IDEportA

	MOV	A,E			;select IDE register
	OUT	IDEportC

	ORI	IDEwrline		;lower WR line
	OUT	IDEportC
	
	MOV	A,E			;<-- Ken Robbins suggestion, raise WR line
	OUT	IDEportC		;deassert RD pin

	XRA	A			;Deselect all lines including WR line
	OUT	IDEportC

	MVI	A,READcfg8255		;Config 8255 chip, read mode on return
	OUT	IDEportCtrl
	RET


CPM$MOVE$CODE:				;This code is written to reside at 0H. Will be relocated by this
	LXI	H,BUFFER		;this program to move the boot CPMLDR to 100H in RAM (Overwriting this program)
	LXI	D,100H
	LXI	B,(12*512)
	LDIR
	NOP
	NOP
	NOP
	JMP	100H
CPM$MOVE$CODE$END:



;-----------------------------------------------------------------------------------------------------
;------------------------------------- RTC ROUTINES --------------------------------------------------
;-----------------------------------------------------------------------------------------------------

GET$TIME$DATE:
	CALL	CHECK$BOARD$TYPE	;Must be FPGA DC Board

	LXI	D,Read$RTC		;Reading the Time  & Date from the FPGA DC Board RTC.
	CALL	PSTRING

	MVI	A,CMD$GET$TIME		;80H  Get Time CMD 
	CALL	DATA$TO$FPGA		;CMD to FPGA Board

	LXI	D,MSG20			; 'Date = '
	CALL	PSTRING

	CALL	GET$FPGA$TO$S100	;Wait for century (20)
	CALL	HEXOUT

	CALL	GET$FPGA$TO$S100	;Wait for year
	CALL	HEXOUT
	MVI	C,'/'
	CALL	ZCO


	CALL	GET$FPGA$TO$S100	;Wait for month
	CALL	HEXOUT
	MVI	C,'/'
	CALL	ZCO

	CALL	GET$FPGA$TO$S100	;Wait for day
	CALL	HEXOUT

	LXI	D,MSG19			; '  Time= '
	CALL	PSTRING

	CALL	GET$FPGA$TO$S100	;Wait for hour
	CALL	HEXOUT
	MVI	C,':'
	CALL	ZCO
	
	CALL	GET$FPGA$TO$S100	;Wait for minutes
	CALL	HEXOUT
	MVI	C,':'
	CALL	ZCO

	CALL	GET$FPGA$TO$S100	;Wait for seconds
	CALL	HEXOUT
	CALL	ZCRLF
	JMP	MAINLOOP


SET$TIME$DATE:
	CALL	CHECK$BOARD$TYPE	;Must be FPGA DC Board

	LXI	D,Set$RTC		;Set the Time & Date on the FPGA DC Board RTC.
	CALL	PSTRING

	MVI	A,CMD$SET$TIME		;81H  Set Time CMD 
	CALL	DATA$TO$FPGA		

	LXI	D,MSG12			;CR,LF,'Enter Year (00-99) '
	CALL	PSTRING
	CALL	GETHEX
	CALL	DATA$TO$FPGA		

	LXI	D,MSG13			;CR,LF,'Enter Month (01-12) '
	CALL	PSTRING
	CALL	GETHEX
	CALL	DATA$TO$FPGA		

	LXI	D,MSG14			;CR,LF,'Enter Date (01-31) '
	CALL	PSTRING
	CALL	GETHEX
	CALL	DATA$TO$FPGA		
	
	LXI	D,MSG15			;CR,LF,'Enter Hours (00-23) '
	CALL	PSTRING
	CALL	GETHEX
	CALL	DATA$TO$FPGA		

	LXI	D,MSG16			;CR,LF,'Enter Minutes (00-59) '
	CALL	PSTRING
	CALL	GETHEX		
	CALL	DATA$TO$FPGA		
	
	LXI	D,MSG17			;CR,LF,'Enter Seconds (00-59) '
	CALL	PSTRING
	CALL	GETHEX
	CALL	DATA$TO$FPGA		;Data to FPGA Board
	
	CALL	ZCRLF
	JMP	MAINLOOP



;-----------------------------------------------------------------------------------------------------------------------

DATA$TO$FPGA:				;Send CMD/data to FPGA_DC Z80
	PUSH	PSW
DATA$TO$FPGA1:
	IN	STATUS$BUS$PORT	
;	CPI	0FFH			;0FFH if we have the Dual IDE/CF Card 
;	JZ	NO$FPGA$DC
	ANI	80H
	JRNZ	DATA$TO$FPGA1		;Wait until bit 7 is 0 (ie. any previous data has been read by FPGA_DC board Z80)
	POP	PSW
	OUT	DATA$BUS$PORT		;This will cause the FPGA_DC board to set the S100$RTS bit
					;(Seems to be required for data to arrive valid on FPGA_DC Board)
DATA$TO$FPGA2:
	IN	STATUS$BUS$PORT	
	ANI	80H
	JRNZ	DATA$TO$FPGA2		;Wait until bit 7 is 0 
	RET				;Ret NZ if error
NO$FPGA$DC
	POP	PSW
	RET
	

GET$FPGA$TO$S100:			;Get data from FPGA
	IN	STATUS$BUS$PORT		;Get S100 Status Port - Bit 0, 1 = FPGA data present, 0, no data present
	ANI	01H			;01H, Bit 0
	JZ	GET$FPGA$TO$S100	;wait for DC Board to present data 
	IN	DATA$BUS$PORT		;Return Data in [A], FPGA "hardware" will lower bit 7 of S100 Bus port 80H
	RET


;--------------------------------------------------------------------------------------------------------
	
SIGN$ON$CPM:	DB	CR,LF,LF,'IDEDC_FPGA Disk Controller Test Program (V5.5) '
		DB	'using CPM3 Console BIOS Routines',CR,LF
		DB	'(Make sure the Disk Controller IOBYTE bit 7 is open).',CR,LF,'$'
SIGN$ON$Z80:	DB	CR,LF,LF,'IDE_FPGA Disk Controller Test Program (V5.5) '
		DB	'using the Z80 Monitor for IO',CR,LF
		DB	'(Make sure the Disk Controller IOBYTE bit 7 is open).',CR,LF,'$'
		DB	'Initilizing IDE Circuit, one moment please...',CR,LF,'$'
		
		
		DB	'Initilizing IDE Board, one moment please...',CR,LF,'$'
INIT$1$ERROR:	DB	'Initilizing of First Drive failed. Aborting Program.',BELL,CR,LF,LF,'$'
INIT$2$ERROR	DB	'Initilizing of Second Drive failed. (Possibly not present).',BELL,CR,LF,'$'
ID$ERROR:	DB	'Error obtaining Drive ID.',CR,LF,'$'
INIT$DR$OK:	DB	'Drive Initilized OK.',CR,LF,LF,'$'
BAD$DRIVE:	DB	CR,LF,'Drive ID Information appears invalid. '
		DB	'(Drive possibly not present).',CR,LF,'$'
DRIVE0$IDMSG:	DB	'Drive #0 - Invalid ID - Aborting',CR,LF,'$'
DRIVE$INFO:	DB	'Drive ID Paramater Information:-',CR,LF,'$'
msgmdl:		DB	'Model: $'
msgsn:		DB	'S/N:   $'
msgrev:		DB	'Rev:   $'
msgcy:		DB	'Cylinders: $'
msghd:		DB	', Heads: $'
msgsc:		DB	', Sectors: $'
msgCPMTRK:	DB	'CPM TRK = $'
msgCPMSEC:	DB	' CPM SEC = $'
msgLBA:		DB	'  (LBA = 00$'
MSGBracket	DB	')$'
msgLBAsup1:	DB	'LBA is $'
msgLBAnot:	DB	'NOT $'
msgLBAsup2	DB	'Supported',CR,LF,'$'


MENU$STRING$0:	DB  CR,LF,LF
		DB '(R) Read Sectors        (W) Write to a Sector    '
		DB '(D) Select Card',CR,LF
		DB '(X) Read N Sectors      (Y) Write N Sectors      '
		DB '(F) Format a Card',CR,LF
		DB '(+) Next Sector         (-) Previous Sector      '
		DB '(T) Set Trk#,Sec#',CR,LF
		DB '(C) Copy Card A:->B:    (V) Verify Cards         '        
		DB '(S) Fill Buffer with a byte',CR,LF
		DB '(G) Fast Copy A:->B:    (I) Show CF Card IDs     '        
		DB '(M) Set max Sec/Trk',CR,LF
		DB '(O) Set First Sector #  (K) Boot CPM from A:     '
		DB '(A) Set IDE Mode',CR,LF
		DB '(L) Get Date & Time     (U) Set Date & Time      '
		DB '(ESC) Quit',CR,LF,'$'

DMA$STRING	DB	CR,LF,'Buffer=$'
DRIVE$STRING	DB	'H Drive $'
MODE$STRING	DB	': IDE Mode=$'
SEC$RANGE	DB	' (Sector Range $'
TRACK$STRING	DB	') Track $'
SECTOR$STRING	DB	' Sector $'
	 
Prompt:		db	CR,LF,LF,'Please enter command > $'
msgsure:	DB	CR,LF,'Warning: this will change data on the drive, '
		DB	'are you sure? (Y/N)...$'
msgrd:		DB	CR,LF,'Sector Read OK',CR,LF,'$'
msgwr:		DB	CR,LF,'Sector Write OK',CR,LF,'$'
SEC$RW$ERROR	DB	'Drive Error, Status Register = $'
ERR$REG$DATA	DB	'Drive Error, Error Register = $'
ENTER$SECL	DB	'Starting sector number,(xxH) = $'
ENTER$TRKL	DB	'Track number (LOW byte, xxH) = $'
ENTER$TRKH	DB	'Track number (HIGH byte, xxH) = $'
ENTER$HEAD	DB	'Head number (01-0f) = $'
ENTER$COUNT	DB	'Number of sectors to R/W = $'
DRIVE$BUSY	DB	'Drive Busy (bit 7) stuck high.   Status = $'
DRIVE$NOT$READY	DB	'Drive Ready (bit 6) stuck low.  Status = $'
DRIVE$WR$FAULT	DB	'Drive write fault.    Status = $'
UNKNOWN$ERROR	DB	'Unknown error in status register.   Status = $'
BAD$BLOCK	DB	'Bad Sector ID.    Error Register = $'
UNRECOVER$ERR	DB	'Uncorrectable data error.  Error Register = $'
READ$ID$ERROR	DB	'Error setting up to read Drive ID',CR,LF,'$'
SEC$NOT$FOUND	DB	'Sector not found. Error Register = $'
INVALID$CMD	DB	'Invalid Command. Error Register = $'
TRK0$ERR	DB	'Track Zero not found. Error Register = $'
UNKNOWN$ERROR1	DB	'Unknown Error. Error Register = $'
CONTINUE$MSG	DB	CR,LF,'To Abort enter ESC. Any other key to continue. $'
FORMAT$MSG	DB	'FORMAT DISK. Fill all sectors with E5'
		DB	CR,LF,'Warning: this will change data on the Card, '
		DB	'are you sure? (Y/N)...$'
ReadN$MSG	DB	CR,LF,'Read multiple sectors from current disk/CF card to RAM buffer.'
		DB	CR,LF,'How many 512 byte sectores (xx HEX):$'
WriteN$MSG	DB	CR,LF,'Write multiple sectors RAM buffer CURRENT disk/CF card.'
		DB	CR,LF,'How many 512 byte sectores (xx HEX):$'
ReadingN$MSG	DB	CR,LF,'Reading Sector at:- $'
WritingN$MSG	DB	CR,LF,'Writing Sector at:- $'
msgErr		DB	CR,LF,'Sorry, that was not a valid menu option!$'
FormatDone	DB	CR,LF,'Disk Format Complete.',CR,LF,'$'
FC$DONE$MSG	DB	CR,LF,'Fast Copy of A:->B: Complete$'

RRANGE$MSG	DB	CR,LF,'Sector value out of range.',CR,LF,'$'
CPM$ERROR	DB	CR,LF,'Hardware error reading CPMLDR.',CR,LF,'$'
CPM$ERROR1	DB	CR,LF,'Data error reading CPMLDR. (The first byte loaded was not 31H).',CR,LF,'$'
MOVE$REQUEST	DB	CR,LF,'The CPMLDR image is now at 4000H in RAM. '
		DB	'To boot CPM you will have to'
		DB	CR,LF,'overwrite this program at 100H. Do you wish to do so (Y/N)...$'
SETA$MSG	DB	CR,LF,'Current Drive is now A:$'
SETB$MSG	DB	CR,LF,'Current Drive is now B:$'
FILL$MSG	DB	CR,LF,'Enter Fill Byte Character $'
		
;SET$NOHOLE$Msg	DB	'No Hole mode set.  Maximum sector set to default of 03FH',CR,LF,'$'
;SET$HOLE$Msg	DB	'Hole mode set.  Maximum sector left at current value',CR,LF,'$'
GET$MAX$SEC$Msg	DB	'Enter max sector/Track value (00 - FF): $'
GET$FIRST$SEC$Msg DB	'Sectors can be numbered 0,1,2,3... (On CF cards) or '
		DB	'1,2,3,4... (On Hard Disks).',CR,LF
		DB	'Enter the first sector number (00 or 01):$'  
FIRST$STRING	DB	', First Sec # = $'
LBA$FORMAT	DB	CR,LF,'Enter new Track,Sector as XX,XX $'
SEC$RANGE$MSG	DB	CR,LF,BELL,'Error. Sector range is $'
BAD$LBA$MSG	DB	CR,LF,BELL,'Data must be in the form XX,XX (Hex).$'
WAIT$MSG	DB	CR,LF,'Hit any keyboard character to continue$'
SR$ERR$MSG	DB	CR,LF,'Card sector READ error. TRK,SEC = $'
SW$ERR$MSG	DB	CR,LF,'Card sector WRITE error. TRK,SEC = $'
N$SEC$READ$MSG	DB	'H Sectors read to RAM ($'
ONE$MOMENT$MSG	DB	CR,LF,'One moment please while the data is collected',CR,LF,'$'
BAD$ENTRY$MSG	DB	CR,LF,BELL,'Bad data entry format$'
DMA$N$MSG	DB	CR,LF,'Enter Starting RAM location (xxxxH): $'
ONEW$MOMENT$MSG DB	CR,LF,'One moment please while the data is written',CR,LF,'$'
N$SEC$WRITE$MSG	DB	'H Sectors written to the Card from RAM ($'
Quick$Format	DB	CR,LF,'Do you want a "Quick Format", (Tracks 0 & 1 only) Y/N: $'
TO$ABORT$F	DB	CR,LF,'Remember you can hit ESC any time to abort'
		DB	' the formatting process.$'
msgTRK$SEC	DB	'Card TRK,SEC = $'
msgBS5		DB	BS,BS,BS,BS,BS,'$'
msgBS2		DB	BS,BS,'$'

DiskCopy1Msg	DB	CR,LF,'Copy disk partition of Drive A: to Drive B: (CF card).'
		DB	CR,LF,BELL,'Warning: This will delete all data on Drive B:, '
		DB	'are you sure? (Y/N)...$ '
		DB	'Is this OK (Y/N) $' 

NO$OF$V$TRACKS	DB	CR,LF,'How many tracks to verify? (xxH) $'
SECOND$V$DRIVE	DB	CR,LF,'Enter the Card you wish to verify the CURRENT Card against $'
TO$ABORT$V	DB	CR,LF,'Remember you can hit ESC to abort the verify process$'

DiskVerifyMsg	DB	CR,LF,'Verify disk partition Drive A: = Drive B: (CF card).$'
VerifyTrk$MSG	DB	'Verifying track: $'
VerifyDone	DB	CR,LF,'Verify CPM disk A: = B: complete.',CR,LF,'$'
Verify$ERR	DB	CR,LF,BELL,'Verify error on Track $'
SEC$Msg		DB	'H  Sector $'

VERIFY$COMPLET$MSG DB	CR,LF,BELL,'Verify of CF cards complete. They are the same.$'
WILL$VERIFY$MSG DB	CR,LF,'Will verify data on the current card is the same as on '
		DB            'another card.$'
TO$ABORT$C	DB	CR,LF,'Remember you can hit ESC to abort the copy process$'
DONE$MSG	DB	CR,LF,'Hit keyboard to return to menu$'
SURE$REBOOT	DB	CR,LF,'Are you sure you want to Reboot/Reset the FPGA_DC Board (Y/N) $'
LAST$MSG	DB	CR,LF,'Enter the last sector number on a track - in hardware. $'
FIRST$MSG	DB	CR,LF,'Enter the starting sector number on a track - '
		DB	'in hardware. (0 or 1) $'

FPGA$BOARD$MSG DB	CR,LF,'The FPGA_DC Board was detected.$'

OLD$IDE$BOARD$MSG   DB	CR,LF,'The FPGA_DC Board was NOT detected.'
		DB	CR,LF,'Only IDE/CF/SD cards in A: and B: can be accessed.$' 

CURRENT$DR$MSG	DB	CR,LF,'Select Drive(IDE/CF/SD Card A or B) $'
BAD$SEL$DR$MSG	DB	CR,LF,BELL,'Select Drive Error$'

DiskCopyMsg	DB	CR,LF,'Fast Copy IDE/CF Card A: to Drive B:'
		DB	CR,LF,BELL,'Warning: This will delete all data on CF Card B:, '
		DB	'are you sure? (Y/N)...$ '
CopyDone	DB	CR,LF,'Disk copy of CF Card A: to B: complete.',CR,LF,'$'
CopyTrk$MSG	DB	'Copying track: $'
FAST$COPY$MSG	DB	CR,LF,'Fast Copy of A:->B: Started. '
		DB	'(Use USB Port Terminal for status).$'
NO$DRIVE$MSG	DB	CR,LF,BELL,'A valid CF card is not present in B:',CR,LF,'$'
FAST$ERROR$MSG	DB	CR,LF,'Fast Copy of CF Card A:->B: returned an error',CR,LF,'$'
FAST$ABORT$MSG	DB	CR,LF,'Fast Copy of CF Card A:->B: Aborted.',CR,LF,'$'
POST$TEST$MSG	DB	'This is a test of the Post Message CMD',0
SET$IDE$MODE$MSG DB	CR,LF,'Set IDE Mode (0,1 or 2) $'
BOARD$ERR$MSG	DB	CR,LF,BELL,'Sorry that Command is valid only for the FPGA DC Board$'
IDE$MODE$0$MSG	DB	CR,LF,'IDE Mode to Mode 0$'
IDE$MODE$1$MSG	DB	CR,LF,'IDE Mode to Mode 1$'
IDE$MODE$2$MSG	DB	CR,LF,'IDE Mode to Mode 2$'
FDC$CIRCUIT$MSG	DB	CR,LF,'FDC circuit detected on FPGA_DC Board$'
IDE$CIRCUIT$MSG	DB	CR,LF,'IDE/CF card circuit detected on FPGA_DC Board$'
BOTH$CIRCUITS$MSG DB	CR,LF,'IDE/CF card and FDC circuits detected on FPGA_DC Board$'
TEXT$SENT	DB	CR,LF,'The following Text was sent to the FPGA_DC IDE Board:-',CR,LF,'$'
WRONG$MSG	DB	CR,LF,BELL,'Sorry this CMD only works for the FPGA_DC Board$'
HIT$RESET	DB	CR,LF,BELL,'Please Hit the Reset button$'

Read$RTC	DB	CR,LF,'Reading the Time & Date from the FPGA DC Board RTC.$'
MSG20:		DB	CR,LF,'Date = $'
MSG19:		DB	'  Time = $'
Set$RTC		DB	CR,LF,'Set the Time & Date on the FPGA DC Board RTC.$'
MSG12:		DB	CR,LF,'Enter Year (00-99) $'
MSG13:		DB	CR,LF,'Enter Month (01-12) $'
MSG14:		DB	CR,LF,'Enter Date (01-31) $'
MSG15:		DB	CR,LF,'Enter Hours (00-23) $'
MSG16:		DB	CR,LF,'Enter Minutes (00-59) $'
MSG17:		DB	CR,LF,'Enter Seconds (00-59) $'


; -------------------------- RAM usage -----------------------------------------

RAMAREA			DB	'           RAM STORE AREA -------->'		;useful for debugging
@DMA			DW	buffer
@DMA$STORE		DW	buffer
@DRIVE$SEC		DB	0H
@DRIVE$TRK		DW	0H
@SEC			DW	0H
@TRK			DW	0H

@DRIVE1			DW	0H
@SEC1			DW	0H		
@TRK1			DW	0H
@DRIVE2			DW	0H
@SEC2			DW	0H
@TRK2			DW	0H

@StartLineHex		DW	0H
@StartLineASCII		DW	0H
@BYTE$COUNT		DW	0H
@SECTOR$COUNT		DW	0H
@SECTOR$COUNT1		DW	0H
@CURRENT$DRIVE		DB	0H
@CURRENT$DRIVE$PORT	DB	0H		;0=IDE A: 1=IDE B:
@Second$DriveBPresent	DB	0H		;1 if second drive is present
@MAX$SEC		DB	3DH		;Max "CPM" sectors/track (default 3DH for "with holes" mode)
@FIRST$SEC		DB	0H
@TRACK$COUNT		DB	0H
@DELAYSTORE		DB	0H
@CURRENT$IDE$MODE	DB	0H		;0 = old IDE Board - must do things here. 
						;1 = Extenal IDE Ports  + CMD Info
						;2 = Local FPGA IDE Ports + CMD Info
@CURRENT$BOARD$HARDEWARE DB     0H		;0XXXXXX = IDE, 01XXXXXX = FDC, 11XXXXXX +IDE+FDC
@BOARD$TYPE		DB	0H		;0= Dual IDE/CF Board, 1= FPGA_DC Board

			DS	100H
STACK:			DW	0H
			DB	'<--End of MYIDE Code           '

		ORG	4000H	
BUFFER:			DS	512
IDBUFFER
BUFFER2:	DS	512
