;	CP/M Boot Loader reader for my 8080 microcomputer
;   Gets the boot loader from disk into $1000
;   Used for troubleshooting the boot process
;	JWD - v1.0   09/28/2025   
;
	CPU 8080        ; Specify architecture
	RELAXED ON      ; Accept $BEEF for hex: default Intel is 0BEEFH
	PAGE 0
;
; Equates for I/O mapped 8250 or 16450 serial port
;
S16450  equ     0       ;base of 16450 UART
RXR     equ     0       ;Receiver buffer register
TXR     equ     0       ;Transmitter buffer register
IER     equ     1       ;Interrupt enable register
LCR     equ     3       ;Line control register
MCR     equ     4       ;Modem control register
LSR     equ     5       ;Line status register
;
; Define monitor serial port
;
SER_STATUS equ S16450+LSR
SER_DATA   equ S16450+RXR
SER_RXRDY  equ 1    ;Mask for rx buffer full
SER_TXRDY  equ 32   ;Mask for tx buffer empty

; ATA Drive Definitions

ATA_CS1			equ $40			;ATA Drive CS1 address
ATA_CS2			equ	$80			;ATA Drive CS2 address
ATA_DATA		equ	ATA_CS1		;ATA Drive Data Register
ATA_ERROR		equ	ATA_CS1+1	;ATA Drive Error Register (Read Only)
ATA_FEATURES	equ	ATA_CS1+1	;ATA Drive Feature Register (Write Only)
ATA_SEC_COUNT	equ	ATA_CS1+2	;ATA Drive Sector Count Register
ATA_SEC			equ	ATA_CS1+3	;ATA Drive Sector Number Register
ATA_CYL_LOW		equ	ATA_CS1+4	;ATA Drive Cylinder LSB Register
ATA_CYL_HI		equ	ATA_CS1+5	;ATA Drive Cylinder MSB Register
ATA_DRV_HEAD	equ	ATA_CS1+6	;ATA Drive Device/Head Register
ATA_STATUS		equ	ATA_CS1+7	;ATA Drive Status Register (Read Only)
ATA_COMMAND		equ	ATA_CS1+7	;ATA Drive Command Register (Write Only)

ATA_LBA_BYTE1	equ	ATA_CS1+3	;ATA Bytes 0 through 7 in LBA mode
ATA_LBA_BYTE2	equ	ATA_CS1+4	;ATA Bytes 8 through 15 in LBA mode
ATA_LBA_BYTE3	equ	ATA_CS1+5	;ATA Bytes 16 through 23 in LBA mode
ATA_LBA_BYTE4	equ	ATA_CS1+6	;ATA Bytes 24 through 27 in LBA mode

ATA_ALT_STATUS	equ	ATA_CS2+6	;ATA Drive Alternate Status Register
ATA_DEV_CTRL	equ	ATA_CS2+6	;ATA Drive Control Register
ATA_DEV_ADDRESS	equ	ATA_CS2+7	;ATA Drive Address Register

ATA_LBA0_7		equ	ATA_CS1+3	;ATA Drive LBA Bits 0-7
ATA_LBA8_15		equ	ATA_CS1+4	;ATA Drive LBA Bits 8-15
ATA_LBA16_23	equ	ATA_CS1+5	;ATA Drive LBA Bits 16-23
ATA_LBA24_27	equ	ATA_CS1+6	;ATA Drive LBA Bits 24-27

; ATA Read/Write Commands

CMD_READ_SECTOR_RETRY		equ	$20	;Read sectors in sector count register - retry enabled
CMD_READ_SECTOR_NORETRY		equ	$21	;Read sectors in sector count register - retry disabled
CMD_READ_LONG_RETRY			equ	$22	;Reads one sector with ECC bytes, retry enabled
CMD_READ_LONG_NORETRY		equ	$23	;Reads one sector with ECC bytes, retry disabled
CMD_WRITE_SECTOR_RETRY		equ	$30	;Write sectors in sector count register - retry enabled
CMD_WRITE_SECTOR_NORETRY	equ	$31	;Write sectors in sector count register - retry disabled
CMD_WRITE_LONG_RETRY		equ	$32	;Writes one sector with ECC bytes, retry enabled
CMD_WRITE_LONG_NORETRY		equ	$33	;Writes one sector with ECC bytes, retry disabled
CMD_WRITE_VERIFY			equ	$3C	;Write sectors in sector count register - verify after writing
CMD_VERIFY_SECTOR_RETRY		equ	$40	;Verify sectors in sector count register - retry enabled, no data 
CMD_VERIFY_SECTOR_NORETRY	equ	$41	;Verify sectors in sector count register - retry disabled, no data
CMD_READ_MULTIPLE           equ $C4 ;Read multiple sectors - interrupt when finished 
CMD_WRITE_MULTIPLE          equ $C5 ;Write multiple sectors - interrupt when finished 
CMD_READ_BUFFER				equ	$E4	;Read contents of sector buffer
CMD_WRITE_BUFFER			equ	$E8	;Write to sector buffer

;ATA Control Commands

CMD_REQUEST_SENSE		equ	$03	;Gets extended error for previous command
CMD_RECALIBRATE         equ $10 ;Reinitialize ATA controller (does nothing with CF)
CMD_SEEK                equ $70 ;Moves head to track specified (does nothing with CF)
CMD_DIAGNOSTIC			equ	$90	;Performs self diagnostics on the card
CMD_INITIALIZE			equ	$91	;Allows change to sectors per track and heads per cylinder
CMD_STANDBY_IMMEDIATE	equ	$94	;Puts card in standby mode without idle timer
CMD_STANDBY				equ	$96	;Puts card in standby mode after idle until timer expired
CMD_CHECK_POWER			equ	$98	;Returns power status of the card
CMD_SLEEP				equ	$99	;Puts card in sleep mode until hardware/software reset
CMD_MULTIPLE            equ $C6 ;Enable read/write multiple operations
CMD_IDENTIFY			equ	$EC	;Gets drive information into the sector buffer
CMD_SET_FEATURES		equ	$EF	;Set card features (aka 8-bit mode)

;ATA Control Settings

DEV_CONTROL_SRST	equ	$04	;Device Control Register Software Reset 
DEV_CONTROL_NIEN	equ	$02	;Device Control Register Interrupt Enable

DHR_SLAVE_SELECT	equ	$10	;DHR Register select slave (default is master)
DHR_LBA_MODE		equ	$40	;DHR Register enable LBA mode (default is CHS)

;ATA Error and Status Flags

ERROR_BBK		equ	$80	;Bad Block Mark (Always 0, not supported on CF)
ERROR_UNC		equ	$40	;Uncorrectable Data Error
ERROR_MC		equ	$20	;Media Changed (Always 0)
ERROR_IDNF		equ	$10	;Sector ID Field Not Found
ERROR_MCR		equ	$08	;Media Change Requested (Always 0)
ERROR_ABRT		equ	$04	;Status error or Invalid Command Aborted
ERROR_TKN0NF	equ	$02	;Track 0 Not Found (Not supported on CF)
ERROR_AMNF		equ	$01	;Address Mark Not Found (Not supported on CF)

STATUS_BSY		equ	$80	;Device Busy
STATUS_DRDY		equ	$40	;Device Ready
STATUS_DWF		equ	$20	;Device Write Fault
STATUS_DSC		equ	$10	;Device Seek Complete
STATUS_DRQ		equ	$08	;Data Request
STATUS_CORR		equ	$04	;Corrected Data
STATUS_IDX		equ	$02	;Index (Not used on CF, Always 0)
STATUS_ERR		equ	$01	;Command Error

;ATA Feature Codes

FEATURE_8BIT_ON			equ	$01	;Enable 8-bit transfers in LBA mode
FEATURE_LOOKAHEAD_OFF	equ	$55	;Disable read lookahead
FEATURE_DEFAULTS_OFF	equ	$66	;Disable power-on defaults
FEATURE_8BIT_OFF		equ	$81	;Disable 8-bit transfers in LBA mode
FEATURE_ECC_ON			equ	$BB	;Enable 4 bytes ECC on read/write long
FEATURE_DEFAULTS_ON		equ	$CC	;Enable power-on defaults

;Boot loader memory location

secbuf     equ $1000

;Monitor routines

monstart   equ $0100  ;monitor start/restart vector

	ORG 	$2000

; Set up the drive for 8-bit I/O                                

start:
   lxi d, msg_start        ;Point to start message
   call bios_print              ;Print it
   in ATA_DRV_HEAD         ;Get current DHR bits
   ori DHR_LBA_MODE        ;Set the LBA mode bit
   out ATA_DRV_HEAD        ;Write bits to the DHR register
   mvi a, FEATURE_8BIT_ON  ;Get feature code for 8-bit mode
   out ATA_FEATURES		   ;Write 8-bit feature code to features register
   mvi a, CMD_SET_FEATURES ;Get ready for Features command
   out ATA_COMMAND         ;Request Set Features command
   call DRIVEBUSY          ;Wait for command completion

st001:
   lxi d, msg_read         ;Point to read message
   call bios_print         ;Print it
   mvi a, 01h              ;Read one sector
   out ATA_SEC_COUNT
   mvi a, 00h              ;Set up for LBA 0 (first sector)
   out ATA_LBA0_7
   out ATA_LBA8_15
   out ATA_LBA16_23
   in ATA_LBA24_27         ;Get LBA and Drive Control bits
   ani 0f0h                ;Mask off LBA bits
   out ATA_LBA24_27        ;Save Drive Control bits
   mvi a, CMD_READ_SECTOR_RETRY  ;Set up for sector read with retry
   out ATA_COMMAND         ;Execute write sector with retry
   call drivebusy          ;Wait for command completion
   call dataready          ;Wait for data buffer ready
   lxi b, secbuf           ;Point to sector buffer
st002:
   in ATA_DATA             ;Get disk byte
   stax b                  ;Store it in the buffer
   inx b                   ;Point to next sector buffer byte
   in ATA_STATUS           ;Check card status
   ani STATUS_DRQ          ;Get data ready status bit
   jnz st002               ;Put another byte to the card
st003:
   lxi d, msg_done         ;Point to end message
   call bios_print              ;Print it
   jmp monstart            ;Return to monitor

drivebusy:                 ;Waits for drive to complete a command
   push psw
db001:
   in ATA_STATUS
   ani STATUS_BSY
   jnz db001
   pop psw
   ret

dataready:                 ;Waits for data to be written to disk
   push	psw
dr001:
   in ATA_STATUS
   ani STATUS_DRDY
   jz dr001
   pop psw
   ret
;
;
; JWD - Miscellaneous support routines
;
;
conout:	;console character output from register c
	in SER_STATUS  ;read device status
	ani SER_TXRDY  ;tx ready?
	jz conout      ;repeat if no
	mov	a, c       ;get character to accumulator
	out SER_DATA   ;send it
	ret

bios_print: ;Print a message on the console
	ldax d           ;Get char pointed to by D into A
	cpi 0            ;End of string?
	jz bios_pend     ;Exit if end of string
	mov c, a         ;Move shar into C for console out
	call conout      ;Print it
	inx d            ;Point to next character
	jmp bios_print   ;Repeat
bios_pend:
	ret              ;And exit
	
msg_start   dw      0d0ah
            db      'JWD 8080 Boot Loader Retriever v1.0'
			dw      0d0ah
			db      'Setting ATA controller'
			dw      0d0ah
			db      0
msg_read    db      'Reading boot loader from physical sector 0'
            dw      0d0ah
			db      0
msg_done    db      'Read complete'
            dw      0d0ah
			db      'Returning to monitor'
			dw      0d0ah
			db      0

end
