;	CBIOS for my 8080 microcomputer
;	JWD - v3.0   03/05/2026   
;
;   Changes in v2.0
;
;   Changed 'nsects' variable to only load 800h bytes (CPP) on warm boot.
;   Moved drive initialization to a subroutine.
;   Added storage for most recent drive error.
;   Added read of ATA error register in the ataerror routine to avoid lockups with ataready.
;
;   Changes in v3.0
;
;   Added drivers for second serial port and one parallel printer port
;   Changed cpp start to DE00 to allow more room for the serial and parallel drivers
;
;
	CPU 8080        ; Specify architecture
	PAGE 0
;
;
;Physical CF disk LBA to DTS partition layout
;
;Drive n       |DSK|| TRK   ||SEC |
;        xxxxxx00 00000000 00000000 - drive d, track t, sector 0
;        xxxxxx00 00000000 00111111 - drive d, track t, sector 63
;		
;Drive A (0)   |DSK|| TRK   ||SEC |
;        xxxxxx00 0000000 00000000 - drive  0, track   0, sector  0
;        xxxxxx00 00111111 11111111 - drive  0, track 255, sector 63
;
;Drive B (1)   |DSK|| TRK   ||SEC |
;        xxxxxx00 01000000 00000000 - drive  1, track   0, sector  0
;        xxxxxx00 01111111 11111111 - drive  1, track 255, sector 63
;
;Drive C (2)   |DSK|| TRK   ||SEC |
;        xxxxxx00 10000000 00000000 - drive  2, track   0, sector  0
;        xxxxxx00 10111111 11111111 - drive  2, track 255, sector 63
;
;Drive D (3)   |DSK|| TRK   ||SEC |
;        xxxxxx00 11000000 00000000 - drive  3, track   0, sector  0
;        xxxxxx00 11111111 11111111 - drive  3, track 255, sector 63
;
;Drive E (4)   |DSK|| TRK   ||SEC |
;        xxxxxx01 00000000 00000000 - drive  4, track   0, sector  0
;        xxxxxx01 00111111 11111111 - drive  4, track 255, sector 63
;
;Drive F (5)   |DSK|| TRK   ||SEC |
;        xxxxxx01 01000000 00000000 - drive  5, track   0, sector  0
;        xxxxxx01 01111111 11111111 - drive  5, track 255, sector 63
;
;Drive G (6)   |DSK|| TRK   ||SEC |
;        xxxxxx01 10000000 00000000 - drive  6, track   0, sector  0
;        xxxxxx01 10111111 11111111 - drive  6, track 255, sector 63
;
;Drive H (7)   |DSK|| TRK   ||SEC |
;        xxxxxx01 11000000 00000000 - drive  7, track   0, sector  0
;        xxxxxx01 11111111 11111111 - drive  7, track 255, sector 63
;
;Drive I (8)   |DSK|| TRK   ||SEC |
;        xxxxxx10 00000000 00000000 - drive  8, track   0, sector  0
;        xxxxxx10 00111111 11111111 - drive  8, track 255, sector 63
;
;Drive J (9)   |DSK|| TRK   ||SEC |
;        xxxxxx10 01000000 00000000 - drive  9, track   0, sector  0
;        xxxxxx10 01111111 11111111 - drive  9, track 255, sector 63
;
;Drive K (10)  |DSK|| TRK   ||SEC |
;        xxxxxx10 10000000 00000000 - drive 10, track   0, sector  0
;        xxxxxx10 10111111 11111111 - drive 10, track 255, sector 63
;
;Drive L (11)  |DSK|| TRK   ||SEC |
;        xxxxxx10 11000000 00000000 - drive 10, track   0, sector  0
;        xxxxxx10 11111111 11111111 - drive 10, track 255, sector 63
;
;Drive M (12)  |DSK|| TRK   ||SEC |
;        xxxxxx11 00000000 00000000 - drive 10, track   0, sector  0
;        xxxxxx11 00111111 11111111 - drive 10, track 255, sector 63
;
;Drive N (13)  |DSK|| TRK   ||SEC |
;        xxxxxx11 01000000 00000000 - drive 10, track   0, sector  0
;        xxxxxx11 01111111 11111111 - drive 10, track 255, sector 63
;
;Drive O (14)  |DSK|| TRK   ||SEC |
;        xxxxxx11 10000000 00000000 - drive 10, track   0, sector  0
;        xxxxxx11 10111111 11111111 - drive 10, track 255, sector 63
;
;
;CF physical partition geometry
;
;Disks              15
;Tracks            256
;Sectors/Track      64
;Bytes/Sector      512
;Total disk    8.388MB
;
;CF logical partition geometry
;
;Disks              15
;Tracks            256
;Sectors/Track     256
;Bytes/Sector      128
;Total disk    8.388MB
;
; Initial stack
;
STACK	equ 0FFFFh	;Initial stack at top of RAM
;
; BIOS parameters
;
DRIVES	equ 4		;Number of logical drives in the system
SECSIZE	equ 80h		;CP/M sector size
MONITOR	equ 100h	;System monitor start address
;
; Equates for I/O mapped 8250 serial port
;
SERIAL0 equ 0		;base I/O address of first 8250 UART
SERIAL1 equ 8       ;base I/O address of second 8250 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
;
SER0_STATUS	equ SERIAL0+LSR
SER0_DATA	equ SERIAL0+RXR
SER1_STATUS	equ SERIAL1+LSR
SER1_DATA	equ SERIAL1+RXR
SER_RXRDY	equ 1	;Mask for rx buffer full
SER_TXRDY	equ 32	;Mask for tx buffer empty
;
; Equates for I/O mapped 8211 parallel port
;
LPT0        equ 10h     ;base I/O address of the parallel port
LPT0_DATA   equ LPT0    ;TX register
LPT0_STAT   equ LPT0+1  ;Printer status register
LPT0_CTRL   equ LPT0+2  ;Printer control register
LPTS_BUSY   equ 80h     ;Mask for printer busy from printer
LPTS_ACK    equ 40h     ;Mask for printer acknowledge from printer
LPTS_PE     equ 20h     ;Mask for paper error from printer
LPTS_SLCT   equ 10h     ;Mask for select from printer
LPTS_ERR    equ 08h     ;Mask for printer error from printer
LPTC_IRQ    equ 10h     ;Mask for interrupt enable
LPTC_SLCT   equ 08h     ;Mask for select to printer
LPTC_INIT   equ 04h     ;Mask for reset to printer
LPTC_AUTO   equ 02h     ;Mask for autofeed to printer
LPTC_STRB   equ 01h     ;Mask for strobe to printer
;
; ATA Drive Definitions
;
ATA_CS1			equ 40h			;ATA Drive CS1 address
ATA_CS2			equ 80h			;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 Command Options
;
CMD_RETRY		equ 0
CMD_NO_RETRY	equ 1
;
; ATA Read/Write Commands
;
CMD_READ_SECTOR_RETRY		equ 20h		;Read sectors in sector count register - retry enabled
CMD_READ_SECTOR_NORETRY		equ 21h		;Read sectors in sector count register - retry disabled
CMD_READ_LONG_RETRY			equ 22h		;Reads one sector with ECC bytes, retry enabled
CMD_READ_LONG_NORETRY		equ 23h		;Reads one sector with ECC bytes, retry disabled
CMD_WRITE_SECTOR_RETRY		equ 30h		;Write sectors in sector count register - retry enabled
CMD_WRITE_SECTOR_NORETRY	equ 31h		;Write sectors in sector count register - retry disabled
CMD_WRITE_LONG_RETRY		equ 32h		;Writes one sector with ECC bytes, retry enabled
CMD_WRITE_LONG_NORETRY		equ 33h		;Writes one sector with ECC bytes, retry disabled
CMD_WRITE_VERIFY			equ 3Ch		;Write sectors in sector count register - verify after writing
CMD_VERIFY_SECTOR_RETRY		equ 40h		;Verify sectors in sector count register - retry enabled, no data 
CMD_VERIFY_SECTOR_NORETRY	equ 41h		;Verify sectors in sector count register - retry disabled, no data
CMD_READ_MULTIPLE           equ 0C4h	;Read multiple sectors - interrupt when finished 
CMD_WRITE_MULTIPLE          equ 0C5h	;Write multiple sectors - interrupt when finished 
CMD_READ_BUFFER				equ 0E4h	;Read contents of sector buffer
CMD_WRITE_BUFFER			equ 0E8h	;Write to sector buffer
;
;ATA Control Commands
;
CMD_REQUEST_SENSE		equ 3		;Gets extended error for previous command
CMD_RECALIBRATE			equ 10h		;Reinitialize ATA controller (does nothing with CF)
CMD_SEEK				equ 70h		;Moves head to track specified (does nothing with CF)
CMD_DIAGNOSTIC			equ	90h		;Check power mode of the card
CMD_INITIALIZE			equ	91h		;Allows change to sectors per track and heads per cylinder
CMD_STANDBY_IMMEDIATE	equ	94h		;Puts card in standby mode without idle timer
CMD_STANDBY				equ	96h		;Puts card in standby mode after idle until timer expired
CMD_CHECK_POWER			equ	98h		;Returns power status of the card
CMD_SLEEP				equ	99h		;Puts card in sleep mode until hardware/software reset
CMD_MULTIPLE			equ 0C6h	;Enable read/write multiple operations
CMD_IDENTIFY			equ	0ECh	;Performs self diagnostics on the card
CMD_SET_FEATURES		equ	0EFh	;Set card features (aka 8-bit mode)
;
;ATA Control Settings
;
DEV_CONTROL_SRST	equ 04h		;Device Control Register Software Reset 
DEV_CONTROL_NIEN	equ 02h		;Device Control Register Interrupt Enable
;
DHR_SLAVE_SELECT	equ 10h		;DHR Register select slave (default is master)
DHR_LBA_MODE		equ 40h		;DHR Register enable LBA mode (default is CHS)
;
;ATA Error Register Flags
;
ERROR_BBK		equ 80h		;Bad Block Mark (Always 0, not supported on CF)
ERROR_UNC		equ 40h		;Uncorrectable Data Error
ERROR_MC		equ 20h		;Media Changed (Always 0)
ERROR_IDNF		equ 10h		;Sector ID Field Not Found
ERROR_MCR		equ 08h		;Media Change Requested (Always 0)
ERROR_ABRT		equ 04h		;Status error or Invalid Command Aborted
ERROR_TKN0NF	equ 02h		;Track 0 Not Found (Not supported on CF)
ERROR_AMNF		equ 01h		;Address Mark Not Found (Not supported on CF)
;
;ATA Status Register Flage
;
STATUS_BSY		equ 80h		;Device Busy
STATUS_DRDY		equ 40h		;Device Ready
STATUS_DWF		equ 20h		;Device Write Fault
STATUS_DSC		equ 10h		;Device Seek Complete
STATUS_DRQ		equ 08h		;Data Request
STATUS_CORR		equ 04h		;Corrected Data
STATUS_IDX		equ 02h		;Index (Not used on CF, Always 0)
STATUS_ERR		equ 01h		;Command Error
;
;ATA Feature Codes
;
FEATURE_8BIT_ON			equ	01h		;Enable 8-bit transfers in LBA mode
FEATURE_LOOKAHEAD_OFF	equ	55h		;Disable read lookahead
FEATURE_DEFAULTS_OFF	equ	66h		;Disable power-on defaults
FEATURE_8BIT_OFF		equ	81h		;Disable 8-bit transfers in LBA mode
FEATURE_ECC_ON			equ	0BBh	;Enable 4 bytes ECC on read/write long
FEATURE_DEFAULTS_ON		equ	0CCh	;Enable power-on defaults
;
;
ccp		equ 0de00h		;base of ccp
bdos	equ ccp+800h	;base of bdos
bios	equ ccp+1600h	;base of bios
cdisk	equ 0004h		;current disk number 0=a,... l5=p
iobyte	equ 0003h		;intel i/o byte
;
	org	bios	;origin of this program
;
nsects	equ (800h/SECSIZE)+1	;warm start sector count (loads CPP only)
;
;
;*****************************************************
;*                                                   *
;*         CP/M to host disk constants               *
;*                                                   *
;*****************************************************
;
blksiz	equ 4096			;CP/M allocation size
hstsiz	equ 512				;host disk sector size
hstspt	equ 64				;host disk sectors/trk
hstblk	equ hstsiz/SECSIZE	;CP/M sects/host buff
;cpmspt	equ hstblk * hstspt	;CP/M sectors/track (not used due to SPT=256.  Changed code.)
secmsk	equ hstblk-1		;sector mask
secshf	equ 2               ;log2(hstblk) = 2
;
;
;*****************************************************
;*                                                   *
;*        BDOS constants on entry to write           *
;*                                                   *
;*****************************************************
;
wrall	equ 0		;write to allocated
wrdir	equ 1		;write to directory
wrual	equ 2		;write to unallocated
;
;	jump vector for individual subroutines
;
	jmp boot	;cold start
wboote:
	jmp wboot	;warm start
	jmp const	;console status
	jmp conin	;console character in
	jmp conout	;console character out
	jmp list	;list character out
	jmp punch	;punch character out
	jmp reader	;reader character in
	jmp home	;move head to home position
	jmp seldsk	;select disk
	jmp settrk	;set track number
	jmp setsec	;set sector number
	jmp setdma	;set dma address
	jmp read	;read disk
	jmp write	;write disk
	jmp listst	;return list status
	jmp sectran	;sector translate
;
;	fixed data tables for four-drive standard
;	128MB Compact Flash Card
;
dpbase:
;	disk Parameter header for disk logical 00 (A)
	dw	0000h, 0000h
	dw	0000h, 0000h
	dw	dirbf, dpblk
	dw	enddat, all00
;	disk parameter header for disk logical 01 (B)
	dw	0000h, 0000h
	dw	0000h, 0000h
	dw	dirbf, dpblk
	dw	enddat, all01
;	disk parameter header for disk logical 02 (C)
	dw	0000h, 0000h
	dw	0000h, 0000h
	dw	dirbf, dpblk
	dw	enddat, all02
;	disk parameter header for disk logical 03 (D)
	dw	0000h, 0000h
	dw	0000h, 0000h
	dw	dirbf, dpblk
	dw	enddat, all03
;
;
dpblk:	;disk parameter block, common to all disks
	dw	256		;SPT - logical sectors per track
	db	5		;BSH - block shift factor
	db	31		;BLM - block mask
	db	1		;EXM - extent mask
	dw	2039	;DSM - logical disk size-1 in blocks
	dw	1023	;DRM - directory max entries-1
	db	0ffh	;AL0 - alloc 0 Reserved directory blocks
	db	0		;AL1 - alloc 1 Reserved directory blocks
	dw	0		;CKS - check size
	dw	1		;OFF - track offset
;
;	end of fixed tables
;
;	individual subroutines to perform each function
;
boot:               ;initialize all system hardware
	lxi sp, STACK	;set stack pointer
	xra	a			;zero in the accum
	sta	iobyte		;clear the iobyte
	sta	cdisk		;select disk zero
	sta	hstact		;host buffer inactive
	sta	unacnt		;clear unalloc count
    sta disk00      ;default physical to logical disk map
	inr a
	sta disk01
	inr a
	sta disk02
	inr a
	sta disk03
;
;  Initialize 8250 serial ports
;
;  access baud generator, no parity, 1 stop bit, 8 data bits
;
	mvi a, 83h
	out SERIAL0+LCR
	out SERIAL1+LCR
;
;  fixed baud rate of 19200:  crystal is 1.843200 Mhz.
;  Divisor is 1843200/(16*19200) = 6
;
	mvi a, 6			;fix at 19.2 kbaud
	out SERIAL0+RXR		;lsb
	out SERIAL1+RXR     ;lsb
	xra a
	out SERIAL0+RXR+1	;msb=0
	out SERIAL1+RXR+1	;msb=0
;
;  access data registers, no parity, 1 stop bits, 8 data bits
;
	mvi a, 3
	out SERIAL0+LCR
	out SERIAL1+LCR
;
;  no loopback, OUT2 on, OUT1 on, RTS on, DTR (LED) on
;
	mvi a, 0fH
	out SERIAL0+MCR
	out SERIAL1+MCR
;
;  disable all interrupts: modem, receive error, transmit, and receive
;
	xra a
	out SERIAL0+IER
	out SERIAL1+IER
;
;  print bios boot message
;
    lxi d, bios_boot  ;point to boot message
	call bios_print  ;print it
;
;
;  Check that ATA controller is presenrt
;
    mvi a, 0aah         ;get test signature
	out ATA_SEC_COUNT   ;store in sector count register
	xra a
	in ATA_SEC_COUNT    ;retrieve test signature
	cpi 0aah            ;is it correct?
	jz init_drive       ;move along if it is
	lxi d, bios_nodrv   ;otherwise print error
	call bios_print
	jmp MONITOR         ;Exit to monitor
;
init_drive:
;
;  Initialize the ATA controller
;
	call atainit
;
;  Set drive to sector zero and seek
;
	xra a				;zero accumulator
	out ATA_LBA0_7		;save in LBA
	out ATA_LBA8_15
	out ATA_LBA16_23
	in ATA_LBA24_27		;get mode control and final lba
	ani 0f0h			;mask off lba bits
	out ATA_LBA24_27	;save everything but lba
	mvi a, 1			;set for one sector
	out ATA_SEC_COUNT
	mvi a, CMD_SEEK
	out ATA_COMMAND		;Request Seek command
	call atabusy		;Wait for command completion
;
;  Check that ATA init completed without errors
;
	call ataerror   	;Check ATA error status
	jz boot_initok  	;Boot is good?
	lxi d, bios_err 	;Point to init error message
	call bios_print 	;Print it
	jmp MONITOR     	;Exit to monitor
;
boot_initok:
	lxi d, bios_msg 	;Point to boot start message
	call bios_print 	;Print it
	jmp	gocpm			;initialize and go to cp/m
;
wboot:	;reload CP/M CPP from disk
	lxi	sp, 80h			;use space below buffer for stack
	xra	a				;zero in the accum
	sta	hstact			;host buffer inactive
	sta	unacnt			;clear unalloc count
;
;  Reinitialize the ATA controller
;
	call atainit
;
; Reload CP/M from disk
;
	mvi c, 0  		;Select drive 0
	call seldsk
	mvi c, 0  		;Select track 0
	call settrk
	mvi c, 4  		;Select sector 4
	call setsec
	lxi h, ccp		;Get dma position
	shld dmaadr		;Save dma position
	mvi a, nsects	;Sector counter
	sta seccnt		;Store it in memory
load01:
	call read		;read the first block
    lda seksec		;Get current sector
	inr a			;Point to next sector
    mov c, a		;Transfer to c register
	call setsec		;Set next sector
;
	lhld dmaadr		;Get DMA address
	lxi b, SECSIZE	;Get sector size
	dad b			;Add byte increment
    shld dmaadr		;Save DMA address
;    
    lda seccnt		;Get sector counter
	dcr a			;Decrement the sector count
	sta seccnt		;Save sector counter
	jnz load01		;If not zero, do next sector
;
;	end of	load operation, set parameters and go to cp/m
;
gocpm:
	mvi	a, 0c3h		;c3 is a jmp instruction
	sta	0			;for jmp to wboot
	lxi	h, wboote	;wboot entry point
	shld 1			;set address field for jmp at 0
;
	sta	5			;for jmp to bdos
	lxi	h, bdos+6	;bdos entry point
	shld 6			;address field of Jump at 5 to bdos
;
	lxi	b, 80h		;default dma address is 80h
	call setdma
;
	lda	cdisk		;get current disk number
	mov	c, a		;send to the ccp
	jmp	ccp			;go to cp/m for further processing
;
;
;   BIOS messages
;
bios_boot:	db 'JWD 8080 Micro System BIOS Boot v1.0'
    dw 0d0ah
	db 0
bios_nodrv: db 'No ATA Controller Detected'
    dw 0d0ah
	db 0
bios_err:	db 'ATA Controller Error'
    dw 0d0ah
	db 0
bios_msg:	db 'Booting CP/M 2.2'
    dw 0d0ah
	db 0
;
;
; I/O Handlers
;
const:	;console status, return 0ffh if character ready, 00h if not
	in SER0_STATUS ;read device status
	ani SER_RXRDY ;rx ready ?
	jz conno       ;exit if not
	mvi a, 0ffh    ;raise signal if ready
conno:
	ret
;
conin:	;console character into register a
	in SER0_STATUS ;read device status
	ani SER_RXRDY ;rx ready?
	jz conin       ;repeat if no
	in SER0_DATA   ;get character if ready
	ani	7fh        ;strip parity bit
	ret
;
conout:	;console character output from register c
	in SER0_STATUS ;read device status
	ani SER_TXRDY ;tx ready?
	jz conout      ;repeat if no
	mov	a, c       ;get character to accumulator
	out SER0_DATA  ;send it
	ret
;
list:	;list character from register c
    call listst      ;Check printer ready
	ani 01h          ;Not ready?
	jz list          ;Check again
	mov	a, c         ;character to register a
	out LPT0_DATA    ;Put the character on the printer bus
	mvi a,LPTC_STRB  ;Get strobe bit
	out LPT0_CTRL    ;Set the control bits
	xri LPTC_STRB    ;Clear strobe bit
	out LPT0_CTRL    ;Activate strobe signal
listw0:
	in LPT0_STAT     ;Get current status
	ani LPTS_BUSY    ;Check busy line
	jnz listw0       ;Wait until active
listw1:
    in LPT0_STAT     ;Get current status
	ani LPTS_BUSY    ;Check busy line
    jz listw1        ;Wait until inactive
	ret
;
listst:	;return list status (0 if not ready, 1 if ready)
    in LPT0_STAT   ;Get printer status
	ani LPTS_BUSY + LPTS_ACK + LPTS_PE + LPTS_ERR  ;Check status flags
	cpi 0c8h       ;Status ready?
	jnz liststex   ;Exit with status code 0
    mvi a, 01h     ;Signal ready
    ret
liststex:
	xra	a	 	   ;0 is always ok to return
	ret
;
punch:	;punch	character from	register C
	in SER1_STATUS ;read device status
	ani SER_TXRDY  ;tx ready?
	jz punch       ;repeat if no
	mov	a, c       ;get character to accumulator
	out SER1_DATA  ;send it
	ret
;
;
reader:	;reader character into register a from reader device
	in SER1_STATUS ;read device status
	ani SER_RXRDY  ;rx ready?
	jz reader      ;repeat if no
	in SER1_DATA   ;get character if ready
	ani	7fh        ;strip parity bit
	ret
;
;
;	Disk I/O Operations
;
home:	;home the disk
	lda	hstwrt	   ;check for pending write
	ora	a
	jnz	homed
	sta	hstact	   ;clear host active flag
homed:
	ret
;
seldsk:	;select disk
	lxi	h, 0000h   ;Error return code
	mov	a,c		   ;selected disk number
	cpi	DRIVES     ;Must be between 0 and DRIVES-1
    rnc            ;Return if carry clear (invalid drive)
	sta	sekdsk	   ;seek disk number
	mov	l,a		   ;disk number to HL
	mvi	h,0
	rept	4	   ;multiply by 16
	dad	h
	endm
	lxi	d,dpbase   ;base of parm block
	dad	d		   ;hl=.dpb(curdsk)
	ret
;
settrk:	;set track given by registers BC
	mov	h,b
	mov	l,c
	shld sektrk    ;track to seek
	ret
;
setsec:	;set sector given by register c 
	mov	a,c
	sta	seksec     ;sector to seek
	ret
;
setdma:	;set dma address given by BC
	mov	h,b
	mov	l,c
	shld	dmaadr
	ret
;
sectran:	;translate the sector given by bc using the
			;translate table given by de
			;JWD - no translation
	xchg		;hl=.trans
	dad	b		;hl=.trans (sector)
	ret			;with value in hl
;
;
;*****************************************************
;*                                                   *
;*	The READ entry point takes the place of          *
;*	the previous BIOS defintion for READ.            *
;*                                                   *
;*****************************************************
;
read:	;read the selected CP/M sector
	xra	a
	sta	unacnt
	mvi	a,1
	sta	readop		;read operation
	sta	rsflag		;must read data
	mvi	a,wrual
	sta	wrtype		;treat as unalloc
	jmp	rwoper		;to perform the read
;
;*****************************************************
;*                                                   *
;*	The WRITE entry point takes the place of         *
;*	the previous BIOS defintion for WRITE.           *
;*                                                   *
;*****************************************************
;
write:	;write the selected CP/M sector
	xra	a			;0 to accumulator
	sta	readop		;not a read operation
	mov	a,c			;write type in c
	sta	wrtype
	cpi	wrual		;write unallocated?
	jnz	chkuna		;check for unalloc
;
;	write to unallocated, set parameters
;
	mvi	a,blksiz/128	;next unalloc recs
	sta	unacnt
	lda	sekdsk			;disk to seek
	sta	unadsk			;unadsk = sekdsk
	lhld sektrk
	shld unatrk			;unatrk = sectrk
	lda	seksec
	sta	unasec			;unasec = seksec
;
chkuna:	;check for write to unallocated sector
	lda	unacnt		;any unalloc remain?
	ora	a
	jz	alloc		;skip if not
;
;	more unallocated records remain
;
	dcr	a			;unacnt = unacnt-1
	sta	unacnt
	lda	sekdsk		;same disk?
	lxi	h,unadsk
	cmp	m			;sekdsk = unadsk?
	jnz	alloc		;skip if not
;
;	disks are the same
;
	lxi	h,unatrk
	call sektrkcmp	;sektrk = unatrk?
	jnz	alloc		;skip if not
;
;	tracks are the same
;
	lda	seksec		;same sector?
	lxi	h,unasec
	cmp	m			;seksec = unasec?
	jnz	alloc		;skip if not
;
;	match, move to next sector for future ref
;
	inr	m			;unasec = unasec+1
	mov	a,m			;end of track?
    cpi 0           ;check CP/M sectors
    jnz noovf       ;skip if no overflow
;
;	overflow to next track
;
	mvi	m,0			;unasec = 0
	lhld unatrk
	inx	h
	shld unatrk		;unatrk = unatrk+1
;
noovf:	;match found, mark as unnecessary read
	xra	a			;0 to accumulator
	sta	rsflag		;rsflag = 0
	jmp	rwoper		;to perform the write
;
alloc:	;not an unallocated record, requires pre-read
	xra	a			;0 to accum
	sta	unacnt		;unacnt = 0
	inr	a			;1 to accum
	sta	rsflag		;rsflag = 1
;
;*****************************************************
;*                                                   *
;*	Common code for READ and WRITE follows           *
;*                                                   *
;*****************************************************
;
rwoper:	;enter here to perform the read/write
	xra	a			;zero to accum
	sta	erflag		;no errors (yet)
	lda	seksec		;compute host sector
	rept secshf
	ora	a			;carry = 0
	rar				;shift right
	endm
	sta	sekhst		;host sector to seek
;
;	active host sector?
;
	lxi	h,hstact	;host active flag
	mov	a,m
	mvi	m,1			;always becomes 1
	ora	a			;was it already?
	jz filhst		;fill host if not
;
;	host buffer active, same as seek buffer?
;
	lda	sekdsk
	lxi	h,hstdsk	;same disk?
	cmp	m			;sekdsk = hstdsk?
	jnz	nomatch
;
;	same disk, same track?
;
	lxi	h,hsttrk
	call sektrkcmp	;sektrk = hsttrk?
	jnz	nomatch
;
;	same disk, same track, same buffer?
;
	lda	sekhst
	lxi	h,hstsec	;sekhst = hstsec?
	cmp	m
	jz	match		;skip if match
;
nomatch:	;proper disk, but not correct sector
	lda	hstwrt		;host written?
	ora	a
	cnz	writehst	;clear host buff
;
filhst:	;may have to fill the host buffer
	lda	sekdsk
	sta	hstdsk
	lhld sektrk
	shld hsttrk
	lda	sekhst
	sta	hstsec
	lda	rsflag		;need to read?
	ora	a
	cnz	readhst		;yes, if 1
	xra	a			;0 to accum
	sta	hstwrt		;no pending write
;
match:	;copy data to or from buffer
	lda	seksec		;mask buffer number
	ani	secmsk		;least signif bits
	mov	l,a			;ready to shift
	mvi	h,0			;double count
	rept 7			;shift left 7
	dad	h
	endm
;
;	hl has relative host buffer address
;
	lxi	d,hstbuf
	dad	d			;hl = host address
	xchg			;now in DE
	lhld dmaadr		;get/put CP/M data
	mvi	c,128		;length of move
	lda	readop		;which way?
	ora	a
	jnz	rwmove		;skip if read
;
;	write operation, mark and switch direction
;
	mvi	a,1
	sta	hstwrt		;hstwrt = 1
	xchg			;source/dest swap
;
rwmove:	;C initially 128, DE is source, HL is dest
	ldax d			;source character
	inx	d
	mov	m,a			;to dest
	inx	h
	dcr	c			;loop 128 times
	jnz	rwmove
;
;	data has been moved to/from host buffer
;
	lda	wrtype		;write type
	cpi	wrdir		;to directory?
	lda	erflag		;in case of errors
	rnz				;no further processing
;
;	clear host buffer for directory write
;
	ora	a			;errors?
	rnz				;skip if so
	xra	a			;0 to accum
	sta	hstwrt		;buffer written
	call writehst
	lda	erflag
	ret
;
;*****************************************************
;*                                                   *
;*	Utility subroutine for 16-bit compare            *
;*                                                   *
;*****************************************************
;
sektrkcmp:	;HL = .unatrk or .hsttrk, compare with sektrk
	xchg
	lxi	h,sektrk
	ldax d			;low byte compare
	cmp	m			;same?
	rnz				;return if not
;
;	low bytes equal, test high 1s
;
	inx	d
	inx	h
	ldax d
	cmp	m			;sets flags
	ret
;
;*****************************************************
;*                                                   *
;*	WRITEHST performs the physical write to          *
;*	the host disk, READHST reads the physical        *
;*	disk.					                         *
;*                                                   *
;*****************************************************
;
;
;hstdsk = host disk #, hsttrk = host track #,
;hstsec = host sect #. read "hstsiz" bytes
;into hstbuf and return error flag in erflag.
;
readhst:	;perform read operation
	mvi a, CMD_READ_SECTOR_RETRY  ;Set up for sector read
    call doatacmd    ;Execute the read command
	jz rdhst01       ;Signal read error
	mvi	a, 1		 ;error condition
	sta erflag       ;flag an error
	ret
;
rdhst01:
	lxi b, hstbuf    ;Point to sector buffer
rdhst02:
	in ATA_DATA      ;Get data byte
	stax b           ;Store data in sector buffer
	inx b            ;Point to next buffer byte
	in ATA_STATUS    ;Check card status
	ani STATUS_DRQ   ;Get data ready status bit
	jnz rdhst02      ;Get another byte from the card
	xra a            ;Signal no error
	sta erflag       ;save it
    ret
;
;hstdsk = host disk #, hsttrk = host track #,
;hstsec = host sect #. write "hstsiz" bytes
;from hstbuf and return error flag in erflag.
;return erflag non-zero if error
;
writehst:	;perform a write operation
	mvi a, CMD_WRITE_SECTOR_RETRY ;Set up for sector write
    call doatacmd                 ;Execute the read command
	jz wrhst01                    ;Signal read error
	mvi	a, 1		              ;error condition
	sta erflag                    ;flag an error
	ret
;
wrhst01:
	lxi b, hstbuf   ;Point to sector buffer
wrhst02:
    ldax b          ;Get data byte from sector buffer
	out ATA_DATA    ;Send data byte to ATA drive
	inx b           ;Point to next buffer byte
	in ATA_STATUS   ;Check drive status
	ani STATUS_DRQ  ;Get data ready status bit
	jnz wrhst02     ;Get another byte from the card
    xra a           ;Signal no error
	sta erflag      ;Save it
    ret
;
;
doatacmd: ;Execute ATA READ/Write Command
    mov b, a          ;Save A temporarially
	call atabusy      ;Wait for command completion
;
;Set disk number MSB in LBA
;
    lda hstdsk        ;Get current host disk number
	lxi h, disk00     ;Point to physical/logical disk map
doata01:
	dcr a             ;Decrement host disk number
	jm doata02        ;Last drive?
	inx h             ;Point to next map
	jmp doata01       ;Do again
doata02:
    mov a, m          ;Get actual drive from map
    ora a             ;Clear carry
    rar               ;Remove lsb of disk number
	ora a             ;Clear carry
	rar               ;Remove lsb of disk number
    out ATA_LBA16_23  ;Save it in ATA_LBA16-23
	mov a, m          ;Get actual drive from map
    ani 3             ;Get two lsb of disk number
	rrc               ;Put them in msb
	rrc
	mov c, a          ;Save it in C
;
;Set disk number LSB and track MSB in LBA
;
    lda hsttrk        ;Get current host track number
    ani 0fch          ;Remove two lsb bits
	rrc               ;Move over two bits
	rrc
    ora c             ;Add in drive lsb bits
    out ATA_LBA8_15   ;Set it in ATA_LBA8_15
    lda hsttrk        ;Get current host track number
	ani 3             ;Get two lsb bits of track
	rrc               ;Put them in msb
	rrc
;
;Set track number LSB and sector in LBA
;
	mov c, a          ;Save it in C
	lda hstsec        ;Get current host sector number
	ani 3fh           ;Remove msb bits just in case
	ora c             ;Add in sector bits
	out ATA_LBA0_7    ;Set it in ATA_LBA0_7
;
;Set sector count to 1
;
	mvi a, 01         ;Read/Write one sector
	out ATA_SEC_COUNT ;Set it in ATA drive
	in ATA_LBA24_27   ;Make sure all other LBA bits are zero
	ani 0f0h
	out	ATA_LBA24_27  ;set it in ATA_LBA24_27
;
;Set Read/Write command code
;
    mov a, b          ;restore A
	out ATA_COMMAND   ;Read sector with retry
	call atabusy      ;Wait for command completion
	call ataready     ;Wait for data buffer ready
	call ataerror     ;Check for error condition
    ret
;
;
atainit: ;Initialize the ATA adapter
    call ataerror                           ;Check for errors
	call ataready	 						;Wait for drive ready
	call atabusy                            ;Make sure drive isn't in a busy state
	in ATA_DEV_CTRL							;Get the ATA device control bits
	ani DEV_CONTROL_SRST+DEV_CONTROL_NIEN	;Enable software reset, disable interrupts
	out ATA_DEV_CTRL						;Store in device control register
	in ATA_DEV_CTRL							;Get the ATA device control bits
	ani DEV_CONTROL_NIEN					;Turn off software reset
	out ATA_DEV_CTRL						;Store in device control register
	call atabusy							;Wait for drive to be ready
;
;  Set drive for LBA mode
;
	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
;
;  Enable 8-bit data transfers
;
	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 atabusy				;Wait for command completion
    ret
	
atabusy: ;Wait for the ATA adapter to complete a command
	in ATA_STATUS    ;Get ATA status
	ani STATUS_BSY   ;Mask on busy bit
	jnz atabusy      ;If busy check again
	ret              ;And exit
;
;
ataready: ;Wait for ATA to become ready to execute commands
	in ATA_STATUS    ;Get ATA status
	ani	STATUS_DRDY  ;Mask on data ready bit
	jz ataready      ;Check again if not ready
	ret              ;And exit
;
;
ataerror: ;Check for ATA error status (not zero on error)
    in ATA_ERROR     ;Read the error register
    sta drverr       ;Save error bits
	in ATA_STATUS    ;Get ATA status
	ani STATUS_ERR   ;Mask on command error bit (sets flags)
	ret              ;And exit
;
;
; JWD - Miscellaneous support routines
;
;
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
;
;
;*****************************************************
;*                                                   *
;*	Unitialized RAM data areas		                 *
;*                                                   *
;*****************************************************
;
begdat	equ	$	 	;beginning of data area
;
sekdsk:	ds	1		;seek disk number
sektrk:	ds	2		;seek track number
seksec:	ds	1		;seek sector number
;
hstdsk:	ds	1		;host disk number
hsttrk:	ds	2		;host track number
hstsec:	ds	1		;host sector number
;
sekhst:	ds	1		;seek shr secshf
hstact:	ds	1		;host active flag
hstwrt:	ds	1		;host written flag
;
unacnt:	ds	1		;unalloc rec cnt
unadsk:	ds	1		;last unalloc disk
unatrk:	ds	2		;last unalloc track
unasec:	ds	1		;last unalloc sector
;
erflag:	ds	1		;error reporting
rsflag:	ds	1		;read sector flag
readop:	ds	1		;1 if read operation
wrtype:	ds	1		;write operation type
dmaadr:	ds	2		;last dma address
;
seccnt: ds  1       ;sector counter for CP/M loader
;
drverr: ds  1		;last drive error
;
;
;	scratch ram area for bdos use
;
dirbf:	ds	128	 	;scratch directory area
all00:	ds	256	 	;allocation vector 0
all01:	ds	256	 	;allocation vector 1
all02:	ds	256	 	;allocation vector 2
all03:	ds	256	 	;allocation vector 3
;
;   Sector buffer
;
hstbuf:	ds	hstsiz	;host buffer
;
;   Virtual disk map
;
disk00: ds  1       ;physical disk to virtual disk translation
disk01: ds  1       ;physical disk to virtual disk translation
disk02: ds  1       ;physical disk to virtual disk translation
disk03: ds  1       ;physical disk to virtual disk translation
;
enddat	equ	$	 	;end of data area
datsiz	equ	$-begdat;	;size of data area
;
;
	end