Files
Sepp J Morris 31738079c4 Upload
Digital Research
2020-11-06 18:50:37 +01:00

2232 lines
56 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

title 'Floppy system driver'
;******************************************
; FLOPPY DISK MODULE *
; Last changed : 2/13/84 *
; *
;******************************************
include exerror.equ
include system.lib
eject !
include diskhdr.equ
F_DELAY equ 141
P_PRIORITY equ 145
DELAY_16MS equ 1
; FLOPPY MODE BITS
two_sided equ 01b
d_density equ 10b
; READ WRITE CONTROL STRING EQUATES
; the field offsets into control string
; refer to 8272 spec(1982 intel data catalog)
; p. 9-146, or the Disk 1 Controller manual for the details
D equ 1 ; controller-relative drive number
C equ 2 ; 0-relative cylinder number
H equ 3 ; 0-relative head number ( side )
R equ 4 ; 1-relative sector number
N equ 5 ; the N factor ( magic number )
EOT equ 6 ; End Of Track record number
GPL equ 7 ; Inter-record gap Length
DTL equ 8 ; The DTL factor ( magic number )
; FLOPPY DISK STATUS FIELD EQUATES
stat0 equ 0 ; Offsets into Status string returned by FDC
stat1 equ 1 ; Every bit means something, so refer
stat2 equ 3 ; to the Intel documentation.
; BIT EQUATES FOR STATUS REGISTER 0
STR0_IC6 equ 40H ; Interrupt Code bit 6
STR0_IC7 equ 80H ; Interrupt Code bit 7
STR0_NR equ 08H ; not ready
STR0_SE equ 20H ; seek end
STR0_UNIT_MASK equ 03H ; unit mask
RLC equ 0C0H ; Ready Line Change
; BIT EQUATES FOR STATUS REGISTER 3
STR3_UNIT_SEL0 EQU 1H ; drive the FDC was working with
STR3_UNIT_SEL1 EQU 2H ; used by door open code
STR3_HEAD_ADDR EQU 4H ; Head FDC was working with
STR3_TRACK_0 EQU 10H ; Head over track 0
STR3_READY EQU 20H ; FDD's ready status
STR3_WRT_PROT EQU 40H ; FDD's write protect status
STR3_FAULT EQU 80H ; FDD's fault status
; FLOPPY CONTROLLER PORT ADDRESSES
fd_port equ 0c0h ; floppy base port
fdcs equ fd_port ; status register
fdcd equ fd_port + 1 ; data register
fdma equ fd_port + 2 ; dma addr when write
ints equ fd_port + 2 ; status register when read
ser equ fd_port + 3 ; serial port
; FLOPPY CONTROLLER FUNCTION DEFINITIONS
f_s_rtk equ 02 ; single density read track
f_d_rtk equ 42H ; double density read track
f_spec equ 03 ; specify
f_dsts equ 04 ; drive status
f_rdat equ 06 ; read sector fm
f_drdt equ 46H ; read sector mfm
f_wrat equ 05 ; write sector fm
f_wrdt equ 45H ; write sector mfm
f_reca equ 07 ; recalibrate
f_rsts equ 08 ; read status
f_seek equ 0fh ; seek
f_s_read_id equ 0ah ; single density read ID
f_d_read_id equ 04ah ; double density read ID
; FLOPPY DRIVE CONTROL EQUATES
spt1 equ 16 - 8 ; shugart 800s (8 ms)
; equ 16 - 3 ; shugart 850s (3 ms)
; equ 16 - 3 ; remex (3 ms)
; DPB EQUATES
dspt equ 0 ; physical sectors per track
bsh equ 2 ; block shift factor
blm equ 3 ; block mask
exm equ 4 ; extent mask
dsm equ 5 ; disk storage max
drm equ 7 ; directory mask
al0 equ 9 ; allocation vector 0
al1 equ 10 ; allocation vector 1
cks equ 11 ; check sum vector
off equ 13 ; physical track offset
dos equ 12 ; offset to match DOS DPB and DPB
fatadd equ 10 ; FAT address
; FOR COMMAND AND STATUS CIRCULAR BUFFER
RET_ADDR equ 4 ; bp and di
FLOPPY_INT_DRIVEN EQU TRUE
FLOPPY_POLL_DRIVEN EQU not FLOPPY_INT_DRIVEN
; THESE ARE FOR THE DISK ERRORS
DRV_NOT_READY EQU 0
DRV_FAULT EQU 1
DRV_RECAL EQU 2
ERROR_MESSAGE_LENGTH EQU 25
MAIN_CONSOLE_INPUT EQU 0
; FOR THE FLOPPY DISK CODE READABILITY
SUCCESS EQU 0H
FIRST_CALL_FLAG EQU 1
N_MASK EQU 00001100B
FLPY_CNT_MASK EQU 03H
TWO_SIDE_BIT EQU 08H
FDC_INT_CODE_MASK EQU 0C0H
FDC_SENSE_DRV_STAT_MSK EQU 07H
STATUS_N_FIELD EQU 6
N_FIELD_MASK EQU 03
START_NO_END EQU 01000000B
EOT_END EQU 80H
RECAL_MASK EQU 11111100B
RECAL_TEST EQU 20H
MAX_FLPY_TRKS EQU 76
NEW_CYLINDER EQU 2
DRIVE_MASK EQU 00FFH
MAX_DRIVES EQU 2
HEAD_BIT EQU 4
DRV_HD_OFFSET EQU 1
SEEK_MASK EQU 11111100B
VALID_SEEK EQU 00100000B
SIST_RESULT_LEN EQU 2
INT_WAIT_POLL_BIT EQU 80H
FDC_CMD_OUT_TEST EQU 080H
FDC_CMD_IN_TEST EQU 0C0H
MSTR_RQM EQU 80H
MSTR_IO_DIRECTION EQU 40H
RW_ERROR EQU 01H
RW_MEDIA_CHANGE EQU 0FFH
NO_MEDIA_ERROR EQU 0FFH
SELDSK_ERROR EQU 0
RETRY EQU 1
ACCEPT EQU 2
IGNORE EQU 3
NO_TRACK EQU 0FFH
ASCII_UPPER_CASE_MASK EQU 11011111B
FDC_READ EQU 1
FDC_WRITE EQU 2
TRACK_10 EQU 10
ALTOS_DD equ 02H
; Interrupt Control Equates
PRECAL equ 1
PSEEK equ 2
PRW equ 3
PRDID equ 4
; Interrupt Error Equates
CMD_ERR equ 0
STAT_ERR equ 1
SEEK_ERR equ 2
; number of times to decrement a counter
; to use up 12 us.
BURN equ 4 ; 23 clocks for a memory decrement,
; + 6 clocks to calculate its address,
; = @ 120 clocks
eject ! include dskcomn.equ
cgroup group code, data
eject ! include sysdat.lib
dseg
extrn current_reskew:word
extrn xlts:byte, xltd1:byte, xltd2:byte, xltd3:byte
extrn dpbs1:byte, dpbs2:byte, dpbd1:byte, dpbd2:byte, dpbd3:byte
extrn dpbd4:byte, dpbd5:byte, dpbd6:byte, dpbpc1:byte
extrn re_skew_26_2:byte, re_skew_15_2:byte, re_skew_8_2:byte
extrn log_phy:byte, phy_log:byte
extrn disk_modes:byte
extrn dph_tbl:word
extrn idrive:byte, itrack:word, isector:byte
extrn idmaoff:word
type_disk_error db 0 ; error log byte
fdc_op db 0 ; operation byte that read or write
; will pass to phys_rw
tburn dw 0 ; FDC needs 12 us between each read
; of the master status port.This is the
; the counter we use for it in process
; context.
iburn dw 0 ; The counter for use in interrupt
; context.
int_drv_flpy db TRUE ; turns on the interrupt drive for
; the floppies
cseg
public read, write, f_dsk_sel
public fdc_init, flint
extrn dispatch:word
extrn sysdat:word
extrn doorop:byte
extrn supif:near
extrn read_write:near, io_read:near
eject
;==========
flint: ; Floppy Disk Interrupt Entry
;==========
;
; ENTRY: interrupt entry
;
; EXIT: leap to the system dispatcher.
; NOTE: this interrupt handler expects an 8080 mode IO system
push ds ; set up a local stack
mov ds,sysdat
mov flpy_int_ssreg,ss
mov flpy_int_spreg,sp
mov ss,sysdat
mov sp,offset flpy_int_tos
push ax ; save the registers we need
push bx ; in interrupt context
call flpy_fork_r
pop bx ; restore the registers used
pop ax ; by the interrupt handler
mov ss, flpy_int_ssreg ; restore the pre-interrupt stack
mov sp, flpy_int_spreg
pop ds ; now the pre-interrupt data segment
jmpf cs:dword ptr dispatch ; go to the system dispatcher
flpy_fork_r:
;-----------
;
; This takes care of the FDC interrupt request
; It detects a door-open interrupt from the FDC
;
; ENTRY: from the flint
;
; EXIT: none
;
dseg
int_ctl db 0 ; Interrupt Control Byte. Routine that
; initiated the IO must set this .
fsb db 0 ; 1st status byte read from FDC's status
; stack when handling a R/W or Read ID
; when processing a read/write
; interrupt.
fdc_interrupt_stat db 0, 0 ; returned status from an interrupt
; ^ ^
; | +--- present cylinder number
; +------ fdc status byte 0
last_main_status db 0 ; last read from FDC's main status port
; when FDC had a request for the master
mstr_lc dw 0 ; Master Status Port Loop Counter
; Used to find out if we have to wait
; for FDC to become ready for command
; or Status transfers after it sends us
; an interrupt.
ffrc0 dw 0 ; Incremented if int handler is entered
; and FDC wants a Sense Int Status Cmd,
; and Int Code shows this interrupt is
; not from a change in a FDC ready line.
ffrc1 dw 0 ; loop counter for the FDC when
; FDC gets ready to give us status
ffrc2 dw 0 ; loop counter for the FDC when
; establishing the data direction
; from the FDC to the CPU.
cseg
ffr0:
push cx ! push dx ; Make sure FDC is ready for either a
push di ! push si ; a Command or Status Transfer
push bp ! push es
ffr1:
in al, FDCS ; Is FDC ready for data transfer ?
test al, MSTR_RQM
jnz ffr2 ; Yes
inc mstr_lc ; inc the master port loop count
jmps ffr1 ; query the port again.
ffr2: ; FDC ready for data transfer
mov last_main_status, al ; save the main status register image
test al, MSTR_IO_DIRECTION ; Does FDC want a command ?
jz ffr18 ; Yes
jmp ffr3 ; No, it wants status (or a truffle).
ffr18: ; Send Sense Interrupt Status command.
mov al, F_RSTS ; Removes the FDC's pending interrupt.
out FDCD, al
call pic_reset ; Reset the FDC's PIC IR line
push cx ; Read the FDC's interrupt status
mov cx, length fdc_interrupt_stat
mov bx, offset fdc_interrupt_stat
ffr9: ; Wait for FDC to be ready for status rd
call ibrn
in al, FDCS
test al, MSTR_RQM
jnz ffr14
inc ffrc1
jmps ffr9
ffr14:
test al, MSTR_IO_DIRECTION
jnz ffr15
call ibrn
in al, FDCS
inc ffrc2
jmps ffr14
ffr15:
in al, FDCD ; get the data byte
mov [bx], al ; save the data byte
inc bx ; point to next buffer position
loop ffr9 ; loop until the entire expected
; status is read
pop cx ; restore counter register
cmp int_ctl, PSEEK ; Expect a seek complete interrupt ?
jne ffr7 ; No
jmps ffr11
ffr7: ; Check for Recal complete interrrupt
cmp int_ctl, PRECAL
jne ffr8 ; Don't expect one.
ffr11: ; check Interrupt Code bits (6 & 7) of
; STR0 for change of ready line status
mov al, fdc_interrupt_stat
and al, STR0_IC6 or STR0_IC7 or STR0_SE
cmp al, RLC
jne ffr10 ; Not due to change of status.
call d_open ; Change of status, post door-open flag
jmps ffre
ffr10: ; Interrupt not due to change of ready
; line status and we expect a seek or
; or recal complete interrupt.
mov int_ctl,0 ; This implies the seek or recal has
call iset ; completed. Reset int_ctl & do flagset
jmps ffre
ffr8: ; Didn't expect a Seek or Recal complete
; interrupt. Check Int bits of STR0
mov al, fdc_interrupt_stat ; for change of ready line status.
and al, STR0_IC6 or STR0_IC7 or STR0_SE
cmp al, RLC
jne ffr13 ; Not due to change of status.
call d_open ; Change of status, post door-open flag
jmps ffre
ffr13: ; No change in ready line status & we're
; not expecting a seek or recal complete
; interrupt. Record event and leave.
inc ffrc0
jmps ffre
ffr3: ; FDC wants the CPU to read its status
; Expect to handle a Read ID or R/W int
cmp int_ctl, PRW ; Are we expecting a R/W complete int ?
jne ffr4 ; No
jmps ffr12
ffr4:
cmp int_ctl, PRDID ; Expecting a Read ID interrupt ?
jne ffr5 ; No
ffr12:
mov int_ctl, 0 ; reset int_ctl
in al, FDCD ; get the status byte
mov fsb, al ; save it for the process
call pic_reset ; Reset the FDC's PIC IR line
call iset ; wake up the process
jmps ffre
ffr5: ; We should never get here.
; FDC wants to send us Status, but we
; never issued a command that should leave
; the FDC in a state where it wants to send
; us status.
call stat_errf
ffre:
pop es ! pop bp ; restore the environment
pop si ! pop di
pop dx ! pop cx
ret
iset:
;----
; INTERRUPT (flag) SET
;
mov dl, fdc_flag ; set the systems floppy flag
mov cl, f_flagset
call supif
ret
pic_reset: ; Reset the floppy's PIC IR line
;---------
pushf
cli
mov al, FLPY_EOI
out MASTER_PIC_PORT, al
popf
ret
d_open: ; Door Open
;------
;
; Set the global door open flag in the XIOS header.
; Set the media flag in the DPH of the drive that
; issued the door open interrupt.
; Use the unit select code from status register 0 of the FDC
; to figure out which drive issued the door open interrupt.
;
; Also use the unit select code from STR0 to index into the DPH
; table for the DPH that needs it's media flag posted.
mov doorop, TRUE ; global door open interrupt flag
; post media flag in the DPH
xor bx, bx ; clear the index register
mov bl, fdc_interrupt_stat ; get the physical floppy code
and bl, STR0_UNIT_MASK ; mask off the irrelevant bits
mov bl, phy_log[bx] ; bias the index off of the first
; floppy in the system.
; this translates from the
; controller's drive number to the
; system drive number
shl bx, 1 ; it's a word value table
mov bx, dph_tbl[bx] ; bx = DPH of the drive that needs
; it's media flag set
mov MF[bx], TRUE ; set the MF
ret
stat_errf: ; Status Error Handler
;---------
; By the time we get here the FDC is expecting to have
; it's status read, but we never issued a command that
; should put the FDC is this state.
;
; The FDC has an outstanding interrupt pending to the PIC.
; We have to clear the pending interrupt from the FDC
; so we can reset the PIC.
;
; There is no code ready to handle this interrupt so
; just iret when we get back.
dseg
stat_errc dw 0 ; status error counter
stat_errb rb 10H ; status error handler buffer
bytes_read dw 0 ; the number of status bytes read
sefs db 0 ; status error first status byte
; read flag
cseg
inc stat_errc ; indicate we have been here
mov sefs, TRUE ; initialize the first status
; byte flag
mov bytes_read, 0 ; clear the number of bytes read
mov bx, 0 ; index into the buffer
se0:
call ibrn
in al, FDCS ; wait for the FDC to become
test al, MSTR_RQM ; ready to send us its status
jz se0
test al, MSTR_IO_DIRECTION ; Check data transfer direction
jz se1
in al, FDCD ; FDC still wants to send us status.
mov stat_errb[bx], al ; put it in the circular buffer
inc bx ; inc the buffer pointer
and bx, length ( stat_errb - 1 )
inc bytes_read ; inc the number of bytes we read
cmp sefs, TRUE ; Is this 1st status byte read ?
jne se2 ; No
call pic_reset
mov sefs, FALSE
se2:
jmps se0
se1:
ret
ibrn:
;----
; Time Use routine used by the interrupt code to wait for the FDC.
mov iburn, BURN
ibrn0:
dec iburn
jnz ibrn0
ret
eject
fdc_init: ; FDC initialization
;--------
; Sets up the values for FDC's 3 internal timers :
; HUT (head unload time) = (0fh * 16ms) [240ms]
; SRT (step rate) = (0fh - 0dh * 1ms increments) [3ms]
; HLT (head load time) = (31H * 2ms) [98ms]
;
; Set up FDC's DMA mode:
; ND ( non-dma bit ) = DMA mode [0]
; (See pgs.41,38 of DISK1 manual.
;
; This system uses a byte per floppy as a bit vector for floppy type.
; It also changes the DRM field of DOS DBPs.
;
; ENTRY: none
;
; EXIT: The FDC is initialized as specified above
dseg
spec_str db f_spec, 0dfh, 70
cseg
mov bx, offset spec_str ; intialize the FDC
mov cx,length spec_str
call send_cmd
sti ; Now init FMT to zeros.
push es ; save the extra segment
mov ax,ds ; set up for store string operation
mov es,ax
xor ax,ax
mov cx, length fmt
mov di, offset fmt
rep stosb ; clear the table to 0
pop es ; restore the extra segment
mov bx, offset dpbpc1 ; Run time fix of DRM,CKS and DSM
mov word ptr DRM+DOS[bx],67 ; field of PCMODE DPB
mov word ptr CKS+DOS[bx],801aH
mov word ptr DSM+DOS[bx],495
ret
;====
read:
;====
;
; ENTRY: bp = addr of IOPB
;
; EXIT: if track = 0 disks are treated as SS SD
; if track > 0 go to the read write deskewing routine
cmp TRACK, 0
jne rd1
mov fdc_op, FDC_READ
call trk0_rw
mov si, offset read1
call read_write
push ax
call trk0_rw1
pop ax
jmps rd2
rd1: ; Not track 0
mov si, offset read1
jmp read_write
rd2:
ret
read1:
;-----
;
mov fdc_op, FDC_READ
call phys_rw
ret
;=====
write:
;=====
;
; ENTRY: bp = addr of IOPB
;
; EXIT: if track = 0 disks are treated as SS SD
; if track > 0 go to the read write deskewing routine
cmp TRACK, 0
jne wrt0
mov fdc_op, FDC_WRITE
call trk0_rw
mov si, offset write1
call read_write
push ax
call trk0_rw1
pop ax
jmps wrt1
wrt0: ; Not track 0
mov si, offset write1
jmp read_write
wrt1:
ret
write1:
;------
;
mov fdc_op, FDC_WRITE ; copy current FMB
call phys_rw
ret
trk0_rw:
;-------
; This is the setup for track 0
;
; ENTRY: bp = addr of IOPB
;
dseg
save_fmb db 0 ; temp store for current fmb
saved_reskew dw 0 ; temp store for the current reskew
rw_save rb 9 ; temp store for the current rw_cs
cseg
mov f_call,FALSE ; used in f_dsk_sel
mov al, c_fmb ; save the current FMB
mov save_fmb, al
mov ax, current_reskew ; save the current reskew index
mov saved_reskew, ax
; save read write control string
pushf ; set up the flags and es
push es
mov ax, ds
mov es,ax
cld
mov cx, 9 ; set up the length, source, and dest
mov si, offset rw_cs
mov di, offset rw_save
rep movsb ; do the move
pop es ; restore the environment
popf
xor al,al ; set the floppy mode byte
mov c_fmb, al ; fmb code for a S.S. S.D. floppy
mov ax, offset re_skew_26_2 ; set the current reskew factor
mov current_reskew, ax
call ds0 ; entry point in disk select that
; has the c_fmb established. This
; call sets up the select
; specific bytes in the rw_cs
ret
trk0_rw1:
;-------
; This is the reset from track 0
;
; ENTRY: bp = addr of IOPB
;
mov al, save_fmb ; restore the current FMB
mov c_fmb, al
mov ax, saved_reskew ; restore the reskew factor
mov current_reskew, ax
; restore the pre-track 0 operation
; read write control string
pushf ; set up the flags and es
push es
mov ax, ds
mov es,ax
cld
mov cx, 9 ; set up the length, source, and dest
mov di, offset rw_cs
mov si, offset rw_save
rep movsb ; do the move
pop es ; restore the environment
popf
ret
phys_rw: ; Physical read/write
;------
; Does the actual read or write from the floppy.
;
; ENTRY: fdc_op = FDC_READ if a read / FDC_WRITE if a write
; idrive = the drive to read or write
; itrack = the track to read or write from
; isector = the sector to read or write from
; idmaoff = the dma offset to read or write to or from
; idmaseg = the dma segment to read or write to or from
;
; N, GTL, and DTL in rw_cs are to be set up by io_seldsk
;
; EXIT: al = SUCCESS if everthing went OK
; al = RW_MEDIA_CHANGE if the media was changed
; al = RW_ERROR if an error other than a detectable
; media change occured.
;
dseg
; D C H R N EOT GPL DTL
rw_cs db 0, 0, 0, 0, 0, 0, 0, 0, 0 ; read_write_control_string
; S S S ; r/w status return string
; T T T
; 0 1 2 C H R N
rw_ss db 0, 0, 0, 0, 0, 0, 0
last_fmb db 0
rw_rc db 0 ; read write recal counter
rw_rt db 0 ; read write retry counter, used
; by the sense drive status code
rw_rt1 db 0 ; read write retry counter, used
; by the main read/write loop
cseg
mov rw_rc, RW_RECAL_COUNT ; Pre-load retry counters.
mov rw_rt, RETRY_COUNT
mov rw_rt1, RETRY_COUNT
rw1: ; Is drive on line ?
mov al,phy_drive
call sense_drvst
cmp al, SUCCESS
je rw3
dec rw_rt ; Drive wasn't ready.
jz rw2
; We still have have some more retries.
; Wait a while and check the ready status
; After the second try, wait 16MS longer
; than the previous attempt.
mov dx, RETRY_COUNT ; if retries > 0 then
sub dl, rw_rt ; delay = Max retries - retries left
cmp dx, 0
jne rw12
mov dx, DELAY_16MS ; else delay = 1
rw12:
mov cl, F_DELAY
call supif
jmps rw1 ; check the ready status again
rw2: ; Ran out of retries while checking for
; the drive being ready. So, return
mov ah,ATTA_FAILED ; a hard error.
mov al, RW_ERROR
jmp rw_e
rw3: ; The drive is ready and on line. Set
; up the read write control string
call rw_preamble ; and do the seek.
cmp al, SUCCESS
je rw4
mov ah,SEEK_FAILED ; rw_preamble had trouble with the seek
mov al, RW_ERROR ; Return an error.
jmp rw_e
rw4:
mov rw_rt1, RETRY_COUNT ; reset the retry count
rw7: ; Top of the inner retry loop
mov bx, offset rw_cs ; send out the command string
mov cx, length rw_cs
call send_cmd
mov int_ctl, PRW ; set the interrupt action control
sti
call int_wait ; wait for the read or write
mov bx, offset rw_ss ; get the results of the operation
mov cx, length rw_ss
call stat_ret
mov al, rw_ss ; get the FDC's return code
and al, FDC_INT_CODE_MASK ; check for start but no end due to EOT
cmp al, START_NO_END
je rw5
jmps rw6 ; Error other than End of Track
rw5: ; error may have been due to EOT, check
; with Status register 1
cmp byte ptr (rw_ss + 1), EOT_END
jne rw6
mov al, SUCCESS ; Return code was EOT
jmps rw_e
rw6: ; error was not an EOT, so retry.
dec rw_rt1
jnz rw7 ; If we haven't exhausted our retry
; attempts go to top of the inner loop
; Else had 10 failures to read the disk
; If no recal attempts were made,check
; for a media change.
cmp rw_rc, RW_RECAL_COUNT
jne rw8
mov al, ( rw_cs + D ) ; sample the drive
call build_fmb
cmp al, c_fmb ; Compare it to what we thought we were
; reading.If not the same, it's a media
jne rw11 ; change.
rw8: ; At this point we've made 10 retries
; on this sector, and media is the same
; as the one expected. Do recal.
dec rw_rc ; decrement the recal counter
jz rw9
mov al, ( rw_cs + D ) ; get the drive
call recal ; do a recal
jmp rw3 ; goto the top of the recal loop
rw9: ; We've tried 5 recals and 50 retries
; give up on the disk and return
mov ah,ATTA_FAILED ; hard error to the BDOS.
mov al, RW_ERROR
jmps rw_e
rw11: ; Media was changed.
mov ah, NO_EXTD_ERR ; Return media change to the BDOS.
mov al, RW_MEDIA_CHANGE
rw_e: ; read write exit point
or al,al ; set the flags on the return code
ret
eject
rw_preamble:
;-----------
;
; Read/write pre-amble does the following funtions
;
; 1] sets up D, C, H, R, and EOT fields of rw_cs
; 2] sets up the 24 bit DMA address for the Disk1
; 3] positions the head over the cylinder the track is on
; that we want to read or write.
;
; ENTRY: fdc_op = read or write opertion
; idrive = drive to prepare for the read or write
; itrack = cylinder to seek to
; isector = sector to read/write
; idmaoff = where to get or put it
; idmaseg = " " " " " "
; c_fmb = the current floppy mode byte for this drive
; it is set up by a previous BDOS call to io_seldsk
;
; EXIT: al = ERROR if there was a seek error
; al = SUCCESS if the seek happened OK
;
; If the return code is SUCCESS the system exists in the
; following state:
;
; 1] the command byte, D, C, H, R, and EOT fields of
; 2] the R/W control string are set up
; 3] the dma address ( segment and offset ) is set up on the
; DISK 1 controler.
; 4] The Head is positioned over the proper cylinder.
;
; The following image of the read write control is here for
; reference, so we can see what bytes in the control string
; are set up.
;
; command byte
; |
; V D C H R N EOT GPL DTL
; rw_cs db 0, 0, 0, 0, 0, 0, 0, 0, 0
;
;
dseg
rwp_rt db 0 ; retry counter, used by seek
; code
rwp_cyl db 0 ; rw_preambles local copy of the
; cylinder to seek to.
cseg
; Set up the Read Write control string Record and End Of Track
; fields. The R and EOT fields are set to the same value
; so the FDC will error out at the end of a track. This is
; because the Terminal count pin on the FDC is tied to ground.
; The Bdos uses 0 relative sectors, but the FDC uses 1 relative
; sectors.
mov al, isector ; get the sector the de-skewing/re-skewing
; wants returned.
inc al ; translate from the BDOS's 0 relative
; sector reference to the FDC's 1 relative
; sector reference
mov ( rw_cs + R ),al ; stuff the read write control string's
; record field
mov ( rw_cs + EOT ),al ; and the read write control string's EOT
; field
; set up the read or write control byte
; use the fdc_op code to see if it's a
; read or write.Use density bit of FMB
; to see if it's single or dbl
cmp fdc_op, FDC_READ ; Is it a read ?
jne rwp2 ; No
test c_fmb, D_DENSITY ; Is it double ?
jz rwp1 ; No
mov rw_cs, F_DRDT
jmps rwp4
rwp1: ; it's a single density read
mov rw_cs, F_RDAT
jmps rwp4
rwp2: ; it's a write operation
test c_fmb, D_DENSITY
jz rwp3
mov rw_cs, F_WRDT
jmps rwp4
rwp3: ; it's a single density write
mov rw_cs, F_WRAT
rwp4: ; FDC op code is now set
lea bx, idmaoff ; set up the dma addr for DISK1
mov dx,2[bx]! mov ax,dx ; get the segment
mov cl,4! shl ax,cl ; (para to bytes) shift add to offset
mov cl,4! shr dh,cl ; save ms nibble
add ax,[bx] ; add the bytes
adc dh,0 ; carry into msn
mov dl,al ; send 24 bits to controller
mov al,dh! out F_DMA,al ; msb - lsb
mov al,ah! out F_DMA,al
mov al,dl! out F_DMA,al
; Now we have to seek to the proper cylinder
; for single sided disks the track requested is the
; cylinder we need to put the head over.
; For a single sided disk we always used head 0.
; for double sided disk we have to divide the track by two
; to derive the cylinder. The head is selected depending on the
; track, if were seeking to an ODD track it's head 1,
; if it's an even track we select head 0.
mov rwp_rt, RETRY_COUNT ; pre-load retry count
mov ax, itrack ; get CCP/M's logical track
test c_fmb,TWO_SIDED ; is it a two sided disk ?
jz rwp5 ; No
shr ax,1 ; Yes, div by 2 to get cylinder
rwp5: ; al-> cylinder we want to seek to
mov rwp_cyl, al
mov ( rw_cs + C ), al ; Set cylinder # for R/W control string
mov al,phy_drive ;Set up head/drive field of R/W ctl str
test c_fmb, TWO_SIDED ; is it a double sided disk
jz rwp6 ; No
test itrack, 1 ; Check lsb for odd track.
jz rwp6 ; Even track.
or al,HEAD_BIT ; Set head bit in drive field of R/W
; ctl string for head 1
mov byte ptr ( rw_cs + H ), 1 ; Now set head field for head 1
jmps rwp7
rwp6: ; it is head 0
mov byte ptr ( rw_cs + H ), 0
rwp7: ; AL -> drive code with head bit OR'd in
mov ( rw_cs + D ), al ; Copy into R/W control string.
rwp8:
mov ah, ( rw_cs + D ) ; set up the drive/head parameter
mov al, rwp_cyl ; set up the cylinder parameter
call p_seek ; do the seek
cmp al, SUCCESS
je rwp9
dec rwp_rt ; Seek failed, do the retry.
jz rwp10
mov dx, DELAY_16MS ; Wait, and try again.
mov cl, F_DELAY
call supif
jmps rwp8
rwp10: ; Out of retries, return hard error
mov ah,SEEK_FAILED
mov al, ERROR
rwp9:
or al,al
ret
;=========
f_dsk_sel: ; Disk Select Entry Point
;=========
;
; The floppy system supports 10 different disk formats
; They are:
;
; 1] SD 128 bytes/sector 26 sectors/track
; 2] DD 128 bytes/sector 26 sectors/track
; 3] DD 256 bytes/sector 26 sectors/track
; 4] DD 512 bytes/sector 26 sectors/track
; 5] DD 1024 bytes/sector 26 sectors/track
; 6-10] same as 1 - 5 with double sided disks.
;
; On a first time select as indicated by dl(0) = 0 , this :
;
; 1] determines what kind of floppy is in the drive
; 2] does a recal, homes the head
; 3] sets up the floppy mode table byte for this disk.
; 4] sets up the dph for this disk
;
; on all calls it
;
; 1] sets up the current floppy mode byte.
; 2] sets up the select specific bytes in the r/w control string
; N, GPL, and DTL
; 3] returns the addr of the DPH
;
; The following image of the read write control is here for
; reference, so we can see what bytes in the control string
; are set up when the disk select call is made.
;
; command byte
; |
; V D C H R N EOT GPL DTL
; rw_cs db 0, 0, 0, 0, 0, 0, 0, 0, 0
;
;
; ENTRY: cl = concurrents logical drive
; dl(0) = first select request
; if dl(0) = 1 this is not a first call request
; else if dl(0) = 0 this is a first call request
;
; EXIT: Ax, Bx = addr of the DPH if everything went OK
; = 0 if there was an error or if a non-supported
; drive was requested.
;
dseg
phy_drive db 0 ; current drive we're selecting
c_fmb db 0 ; the current floppy mode byte
save_xlt rw 1
f_call rw 1 ; first call select flag
edpb rw 1 ; correct DPB if a PC_MODE disk
fat_id rw 1 ; FAT id if PC_MODE disk
rb 126 ; make room for a 128 byte sector
; FLOPPY MODE BYTE AND TABLE
;
; 7 6 5 4 3 2 1 0
; \/ + ------- set = two sided, reset = single sided
; | +---------- set = double density, reset = single density
; +------------- 0 = 128 byte sectors
; 1 = 256 byte sectors
; 2 = 512 byte sectors
; 3 = 1024 byte sectors
fmt rb 16 ; floppy mode table
; Gap length table. Indexed by the N
; field read from disk sector header,
; Values correspond to those the FDC
; expects in the GPL byte of R/W ctl st
gpl_tbl db 07H ; SD 128 byte sectors
db 0EH ; DD 256 byte sectors
db 1BH ; DD 512 byte sectors
db 35H ; DD 1024 byte sectors
; the address of the re skewing tables
; It is indexed by the value in the N field of the Read Write control string
re_skew_tbl dw offset re_skew_26_2 ; S.D. 128 bytes/sec
dw offset re_skew_26_2 ; D.D. 256 bytes/sec
dw offset re_skew_15_2 ; D.D. 512 bytes/sec
dw offset re_skew_8_2 ; D.D. 1024 bytes/sec
cseg
xor bx,bx
mov bl,cl ; logical index
mov al,log_phy[bx] ; convert from concurrents logical
; drive to our controller drive reference
mov phy_drive,al ; stash the physical drive we are selecting
;*** mov bl,disk_modes[bx] ; get mode of drive for easy ref.
;*** mov c_mode,bl
mov bl,al ; put logical drive into physical floppy table
mov phy_log[bx],cl
; check for this being the first call for this floppy
test dl, FIRST_CALL_FLAG
jnz nfc
; this is the first call
; so, take care of it. al has physical floppy number
mov f_call,TRUE
call first_call
cmp al, ERROR ! jne ds4
; there was an error when we made the first call
; so, set up the error code for CCP/M and return the error
mov ax, SELDSK_ERROR
mov bx, ax
jmp ds3 ; leap to the exit point
ds4:
; we made it through the first call for this disk
; al contains the current FMB ( floppy mode byte )
; bx contains Extended DPB (used if this is a PC_MODE disk)
mov bx,pc_dpb_tbl[bx] ; store EDPB
mov edpb,bx
mov c_fmb, al ; set the current FMB
; put the current FMB in the FMT ( floppy mode table )
xor bx, bx ; clear the memory pointer register
mov bl, phy_drive ; set the index into the FMT
mov fmt[bx], al ; put the FMB into the FMT indexed
; by the drive we're working with.
jmps ds0 ; leap around getting the FMB
; from the FMT
nfc:
; we land here when this is not a first time select for
; this disk.
mov f_call,FALSE
xor bx, bx ; clear the memory pointer
mov bl, phy_drive ; index into the FMT as per the drive
mov al, fmt[bx] ; get the drive's FMB
mov c_fmb, al ; set our current FMB to
; this drives FMB
ds0: ;*** ENTRY POINT FOR THE TRK0_RW ROUTNE ***
;
; at this point the current FMB is set up for the disk
; that has been selected, and al contains an image of the current
; FMB.
; Set up the N field of the Read Write control string.
; at this point al contains an image of the current FMB
and al, N_MASK ; mask out the N field from the FMB
shr al,1 ! shr al,1 ; move the N field into position
mov ( rw_cs + N ), al ; for the read write control string
; Set up the DTL field of the Read Write control string.
; The DTL field is only used when the N field is zero.
; The DTL field must be set to 0FFH if it's not used.
; When the N field is 0 the DTL field value is the number of
; bytes to transfer for each sector. The FDC still reads the
; entire sector to calculate the check sum.
; The N field is only zero on a single density floppy.
; At this point al contains an image of the Read Write
; control string N field.
cmp al, 0
jne ds1
; this must be a single density floppy
mov byte ptr ( rw_cs + DTL), 128
jmps ds2
ds1:
; this must be double density floppy
mov byte ptr ( rw_cs + DTL ), 0ffh
ds2:
; Set up the GPL ( inter-record gap length ) field of the
; read write control string. There is a different inter-record
; depending on the size and density of the sector. This can
; be determined by the value of the N field.
; At this point al still contains the value of the N field.
xor bx, bx ; clear the memory pointer
mov bl, al ; get the table index
mov al, gpl_tbl[bx] ; get the GPL value
mov ( rw_cs + GPL ), al ; initialize the control string
; GPL field
; Set up the re_skew vector
; In order to minimize rotation latency of any of the floppy
; disk formats we have to re skew the order in which the sectors
; are read from the disk.
; There is a table of address of the re skewing tables that
; is indexed by the N field of the FMB.
xor bx, bx ; clear the memory pointer
mov bl, c_fmb ; get the FMB
shr bx, 1 ; shift the N field into place
and bx, 0FEH ; to be used as an word index
; ( multiplied by two )
mov ax, re_skew_tbl[bx] ; get the reskewing table address
mov current_reskew, ax ; into the current reskewing pointer
;*** ; return the DPH for concurrents logical disk
;***
;*** xor bx, bx ; clear the memory pointer
;*** mov bl, idrive ; get CCP/M's logical drive
;*** shl bx, 1 ; times 2 for word values
;*** mov ax, dph_tbl[bx] ; get the DPH into ax
;*** mov bx, ax ; and put it into BX also
;***
;***
; For PC_MODE disk support we need to check disk type
; and return the Extend DPB for PC_MODE disk
;
; On first time calls we will get the fat_id and if it
; matches PC_MODE disk types we will modify the DPH
; to reference the EDPB and set the XLT field to 0.
mov ax,f_call
cmp ax,FALSE
je not_a_fat ; not first time, so DPH is already set up
mov bx,edpb ; If Extended DPB is 0 then disk in
cmp bx,0 ; drive is not supported as a PC_MODE
jz cpm_disk ; disk, so assume CCP/M
; read the first FAT sector
mov al,idrive
mov ah,1
push ax ; drive | m_cnt
xor dx,dx
mov ax,FATADD[bx] ; bx has edpb in it
div word ptr DOS+SPT[bx]
push ax ; track
push dx ; sector
push ds ; dma seg
mov ax,offset fat_id
push ax ; dma off
sub sp,4 ; far return
call io_read
add sp,14 ; clear stack
cmp al,0
jz ok_read
mov ax,SELDSK_ERROR
mov bx,ax
ret ; report error
ok_read:
mov ax,fat_id
cmp ax,0fffcH
jb cpm_disk ; This is a CP/M disk
; change DPH to indicate PC_MODE disk
xor bx,bx
mov bl,idrive
mov disk_modes[bx],1 ; indicate PC_MODE disk
shl bx,1
mov bx,dph_tbl[bx] ; get present DPH
;*** mov XLT[bx],0
mov ax,edpb
mov DPB[bx],ax
jmps not_a_fat
cpm_disk:
xor bx,bx
mov bl,idrive
shl bx,1
mov bx,dph_tbl[bx]
mov ax,save_xlt
mov XLT[bx],ax
not_a_fat:
; at this point the present DPH is correct for the current disk
xor bx,bx
mov bl,idrive
shl bx,1
mov ax,dph_tbl[bx] ; get DPH
mov bx,ax
ds3:
ret
eject
first_call:
;----------
;
; This routine does the following
; 1] builds the FMB for the drive passed in al
; 2] puts this FMB in the FMT ( floppy mode byte table )
; for the drive specified.
; 3] sets up the DPH ( disk parameter header )
; 3.1] Puts in the address of the proper skew table
; 3.2] Puts in the address of the proper DPB ( disk parameter
; block )
;
; ENTRY: al = drive to perform the first call dance on
; the drives are 0 relative.
;
; EXIT: al = the FMB ( floppy mode byte ) that was built
; for the disk specified in al.
; al = ERROR if there was an error
dseg
fc_drv db 0 ; local drive code for first call
fc_fmb db 0 ; local version of the FMB
; This table contains the addresses of the skewing tables.
; The skewing table will change for each floppy.
; The value in the N field is used to select the proper skew table
; to plug into the skew field of the DPH for a given floppy.
xtable DW (Offset xlts) ;single 128
DW (Offset xltd1) ;double 256
DW (Offset xltd2) ;double 512
DW (Offset xltd3) ;double 1024
; This table is indexed by the FMB ( floppy mode byte ).
; Each entry corresponds to a uniquie bit combination of the
; FMB. At each entry is the address of the DPB that concurrent
; expects to use for the floppy with the FMB that indexed into the
; table. This DPB address is in turn put into the DPH that
; corresponds to the floppy selected.
dpb_tbl dw dpbs1 ; ss, sd, 128
dw dpbs2 ; ds, sd, 128
dw 0
dw 0
dw 0
dw 0
dw dpbd1 ; ss, dd, 256
dw dpbd2 ; ds, dd, 256
dw 0
dw 0
dw dpbd3 ; ss, dd, 512
dw dpbd4 ; ds, dd, 512
dw 0
dw 0
dw dpbd5 ; ss, dd, 1024
dw dpbd6 ; ds, dd, 1024
; same as above but for PC_MODE disks
pc_dpb_tbl dw dpbpc1 ; ss, sd, 128
dw 0 ; ds, sd, 128
dw 0
dw 0
dw 0
dw 0
dw 0 ; ss, dd, 256
dw 0 ; ds, dd, 256
dw 0
dw 0
dw 0 ; ss, dd, 512
dw 0 ; ds, dd, 512
dw 0
dw 0
dw 0 ; ss, dd, 1024
dw 0 ; ds, dd, 1024
cseg
; figure out what type of floppy is in the drive.
mov fc_drv, al
call build_fmb
cmp al, ERROR ! jne fc1
; there must have been an error
; when we attempted to build the FMB
;*** mov al, ERROR
jmps fc_exit
fc1:
; At this point al contains the FMB
; stash the FMB for use later in this routine
mov fc_fmb, al
; check for the disk being an ALTOS Double Density disk
; WE are not supporting this format
; so, we will return an ERROR at this time
cmp al, ALTOS_DD ! jne fc3
; the disk was an Altos DD
; so, return an error
mov ah,NO_EXTD_ERR
mov al, ERROR
jmps fc_exit
fc3:
; the disk was not an Altos DD so finish the First call dance
; set up the DPH ( disk parameter header )
; Scince we are supporting 10 different types of floppies
; we have to dynamicly change the DPH's to reflect the type of
; floppy in the drive at first select time.
; The fields of the DPH that have to be set up for each floppy
; are the Skew table address, and the DPB address.
; The DPH is determined by concurrents logical disk number
; Get the address of the DPH for the drive passed in
; into di
xor bx, bx ; clear the table index register
mov bl,idrive ; get the logical drive
mov disk_modes[bx],0 ; assume CCP/M until proven PC_MODE
shl bx, 1
mov di, dph_tbl[bx] ; get the dph's address
; AT this point di contains the addr of the DPH we're
; building, and al contains a copy of the FMB
; The FMB's bit(2) and bit(3) ( bits being 0 relative )
; contain the bytes per sector code. This code is used
; to index into the table of translate tables.
xor bh, bh ; clear the xlate table index
mov bl, al ; current FMB into index register
shr bx, 1 ; shift the bytes/sector field
; into position to be used as an
; index into xlate table
and bx, 0FFFEH ; mask out the bit that was used
; to hold the density attribute
mov ax, xtable[bx] ; get the translate table address
mov save_xlt,ax
mov XLT[di],0 ; No XLT needed for PC_MODE test
; The DPB table contains an entry for each possible
; combination of bits from the floppy mode byte
; This code uses the current FMB to index into the DPB table
; to select the proper DPB for the disk we found in the drive
xor bh, bh ; clear index into DPB table
mov bl, fc_fmb ; get the FMB
shl bx, 1 ; times 2 for a word table index
mov ax, dpb_tbl[bx] ; get the DPB's address (CCP/M)
;*** cmp c_mode,0 ; Is it a CCP/M disk?
;*** jz yes3
;*** mov ax,pc_dpb_tbl[bx] ; get the DOS DPB's address (extended)
;*** cmp ax,0 ; Is this disk type supported?
;*** jnz yes3
;*** mov ax,ERROR
;**** jmp fc_exit
;***yes3:
mov DPB[di], ax ; put the DPB's address into the
; DPH we're building
mov al, fc_fmb ; set this for our return code
fc_exit:
ret
build_fmb:
;---------
;
; This routine builds the floppy mode byte from the disk.
;
; ENTRY: al = disk the FMB should be built for ( 0 relative )
;
; EXIT: al = FMB if every thing went OK
; al = ERROR if something went wrong while we
; were building the FMB.
dseg
fmb db 0 ; this is the current floppy mode byte
; FLOPPY MODE BYTE BIT DEFINITIONS
;
; 7 6 5 4 3 2 1 0
; \/ + ------- set = two sided, reset = single sided
; | +---------- set = double density, reset = single density
; +------------- 0 = 128 byte sectors
; 1 = 256 byte sectors
; 2 = 512 byte sectors
; 3 = 1024 byte sectors
bfmb_rt db 0 ; retry counter, this is used when we're
; attempting to seek to track 10. To
; sample the media.
bfmb_rt1 db 0 ; retry counter 1, this is used when we're
; sampling the media with the read ID
; routine.
bfmb_dsk db 0 ; this is the disk we're building
; the FMB for. It is initialized upon
; entry to this routine.
; Density table.
; This table is indexed by the N factor from the read ID status
; string.
den_tbl db 0000b ; 128 bytes/sector
db 0100b ; 256 bytes/sector
db 1000b ; 512 bytes/sector
db 1100b ; 1024 bytes/sector
cseg
; pre-load both retry counters used in making the query to the disk
mov bfmb_rt, RETRY_COUNT
mov bfmb_rt1, RETRY_COUNT
mov bfmb_dsk, al ; save off the disk we're working
; for use later in this routine
bfmb0:
; recalibrate the drive, and FDC for this drive.
; This puts the head over track 0.
; The recal routine takes care of it's own retrying
; and checking for drive status functions.
call recal
cmp al, SUCCESS ! je bfmb1
; an error was returned when
; we tried to recal the drive
mov al, ERROR
jmp bfmb_e
bfmb1:
; The drive has been recalibrated.
; Put the head over track 10, to get past the system
; track(s).
mov ah, bfmb_dsk ; set the drive to seek on
mov al, TRACK_10 ; set the track to seek to
call p_seek ; do the seek
cmp al, SUCCESS ! je bfmb3
; an error was returned from the seek
; so, do the retrying movie
dec bfmb_rt ; decrement the retry count
jnz bfmb1 ; if it's not 0 try again
; the retry counter hit 0
; so, return a hard error
mov ah,SEEK_FAILED
mov al, ERROR ; set the error code
jmp bfmb_e ; leap to the routine exit point.
bfmb3:
; At this point the head is over track 10
; so we have to build the FMB
mov fmb, 0 ; initialize the FMB to 0
; this is equivilent to single sided,
; single density disk
; The previous calls for recalibrating the drive
; and seeking to track 10 both made queries to the FDC
; for the drive status. The most current drive status
; is now in the status register 3 table of the sense drive
; status subroutine. This value is used to determine
; the number of sides for the floppy in the current drive
; we're working with.
xor bx, bx ; index into the drive status table
mov bl, bfmb_dsk ; as per the disk we're working with
test stat_reg_3_tbl[bx], TWO_SIDE_BIT ; test for the number of sides
jz bfmb4
; it must be a double sided disk
; so, or this atribute into the FMB
or fmb, TWO_SIDED
bfmb4:
; The number of sides has been resolved
; so, resolve the density ( either SD or DD ).
; The way we resolve the density is by attempting
; to read the ID field of the first sector that goes by
; the head as a single density disk. If this read ID operation
; fails we try to read the first sector going by the head
; as a double density disk.
; If we get an error from the double density read ID we go into
; a retry movie.
; If the disk happens to be a double density disk we read the
; N field from the read ID status string to figure out how many
; bytes per sector this double density disk has.
; The density and bytes per sector information is put into
; the FMB.
mov ah, bfmb_dsk ; set up the disk to query
mov al, FS_READ_ID ; query for single density
call read_id ; query the disk
mov al, STAT0[bx] ; get the results
and al, FDC_INT_CODE_MASK ; mask for the interrupt code
cmp al, SUCCESS ! jne bfmb5 ; was the read ID a success
; yes, this completes building
; the FMB for a single density floppy.
; fmb contains the code for a single or
; double sided, single density disk.
; so leap to the exit point
jmps bfmb6
bfmb5:
; the single density read ID failed.
; so try to read the disk as a double density disk.
mov ah, bfmb_dsk ; set up disk to query
mov al, FD_READ_ID ; query for double density
call read_id ; query the disk
mov al, STAT0[bx] ; get the results
and al, FDC_INT_CODE_MASK ; mask for the interrupt code
cmp al, SUCCESS ! je bfmb7 ; was the read ID a success
; no, an error was returnd from the
; double density read ID attempt.
; so, do the retry movie.
dec bfmb_rt1 ; decrement the retry count
jnz bfmb4 ; and try again
; we have exhausted our retry
; limit so return a hard error
mov ah,ATTA_FAILED
mov al, ERROR
jmps bfmb6
bfmb7:
; At this point we know the disk is a double density disk
; so, or the double density attribute into the FMB
or fmb, D_DENSITY
; get the disk's N factor from the read ID status string.
; then use this N factor to index into the density table
; the density table contains the FMB's density code
; ( bytes per sector, assumed double density at this point )
mov bl, STATUS_N_FIELD[bx] ; get the N factor
xor bh,bh ; clear the indexes high byte
mov al, den_tbl[bx] ; get the density mask
or fmb, al ; or the density mask into the
; FMB.
bfmb6:
mov al, fmb ; make sure the FMB is in al
bfmb_e:
ret
read_id:
;-------
;
; read ID expects the control byte and the drive/head byte
; set up before it is called.
; It returns with bx pointing to the returned status.
;
; ENTRY: ah = drive and head code
; al = FDC's command code for the density to read
; the ID of.
;
; EXIT: bx = addr(rdid_ss) ( read ID status string )
; al = error return code
; SUCCESS if the read ID status string contains
; the results of an attempted read ID
; operation
; ERROR if the drive was not ready.
dseg
; READ ID CONTROL STRING
;
rdid_cs db 0, 0
; ^ ^
; | +--- head and drive
; +------ control byte
; READ ID STATUS STRING
;
; S S S
; T T T
; 0 1 2 C H R N
rdid_ss db 0, 0, 0, 0, 0, 0, 0
cseg
; set up the read ID control string
mov rdid_cs, al ; set up the control byte
mov ( rdid_cs + D ), ah ; set up the drive and head field
mov al, ah ; sense drive status requires the
; drive in al
and al, FLPY_CNT_MASK ; mask out the head bit
call sense_drvst ; is the drive ready
cmp al, SUCCESS ! je rdid1
; the drive was not ready
; so, return a hard error
mov al, ERROR
jmps rdid2
rdid1:
; the drive is on line and ready
; so, attempt to read the first header cruising by the head
mov bx, offset rdid_cs ; send the read id command
mov cx, length rdid_cs
call send_cmd
mov int_ctl, PRDID ; set the interrupt control
sti
call int_wait ; wait for the read
mov bx, offset rdid_ss ; get the status
mov cx, length rdid_ss
call stat_ret
mov al, SUCCESS ; we tried to read the ID
; so, set a kosher return code
mov bx, offset rdid_ss ; return a pointer to the status
; string
rdid2:
ret
p_seek:
;------
;
; Seek primitive. This builds the seek command string from the
; parameters passed in. It checks the drive status and tries to
; seek. After the seek it checks the return code from the FDC to see
; if the FDC and the program agree on the track the head is over.
;
; If the drive is not ready, off line, or if the track the FDC thinks the
; head is over is different from the track we wanted an error is returned.
; This routine does no retries. It is the responsibility of the caller
; for all error recovery.
;
; ENTRY: ah = drive to seek on ( the head bit must be set up )
; controler relative
; al = track to seek to
;
; EXIT: al = SUCCESS if everthing went OK
; al = ERROR if it could not find the right track
; or the drive was off line
dseg
seek_f_s db f_seek, 0, 0
; ^ ^
; | +--- new cylinder number
; +------ head and drive
;
; Refer to the fdc_interrupt_stat data structure at the beginning
; of this program for a picture of the status returned by the FDC
; from a seek operation.
cseg
; set up the control string for a seek from the drive and
; track passed in.
mov ( seek_f_s + D ), ah ; set up the drive to seek on
mov ( seek_f_s + C ), al ; set up the track to seek to
mov al, ah ; parameter setup for
; sense drive status
call sense_drvst ; now sense the drive's ready status
cmp al, SUCCESS! je ps0
; the drive is not ready
; so, return a hard error
jmps ps_e
ps0:
; the drive must be on line and ready
mov bx, offset seek_f_s ; send the seek command
mov cx, length seek_f_s
cli
call send_cmd
mov int_ctl, PSEEK ; set the interrupt code
sti
call int_wait ; wait for the seek
mov al, fdc_interrupt_stat ; get status word 0
and al, SEEK_MASK ; mask for the seek complete bit
cmp al, VALID_SEEK ! je ps1
; the seek did not complete so return an error
mov ah,SEEK_FAILED
mov al, ERROR
jmps ps_e
ps1:
; the seek completed with no error, so far
; check the FDC's idea of the track the head is over with the
; callers idea of the track the head is over.
mov al, ( seek_f_s + C ) ; compare the cylinder we're over
cmp al, ( fdc_interrupt_stat + 1) ; to the one we wanted
mov al, SUCCESS
je ps_e
; the track the FDC thinks we are over
; is different from the track the caller requested.
; so return an error.
mov ah,ADR_NOT_FND
mov al, ERROR
ps_e:
or al,al ; set the flags for a return code
ret
recal:
;-----
;
; this issues the recalibrate command to a drive
;
; ENTRY: al = drive to recal.
;
; EXIT: al = ERROR if there was an error
; al = SUCCESS if everything went OK
dseg
rccs db f_reca, 0 ; re-cal control string
rcl_drv db 0 ; the drive recal is working with
rcl_rt db 0 ; recal error count, used when
; sensing a drive's status.
rcl_rt1 db 0 ; recal error count 1, used when
; exacuting the recalibrate command
cseg
mov rcl_rt, RETRY_COUNT ; pre-load retry counter 0, used
; by the code checks for the drive
; being ready and on line.
mov rcl_rt1, RETRY_COUNT ; pre-load retry counter 1, used
; by code that sends the recal
; command to the FDC
mov rcl_drv, al ; stash the drive
rcl0:
and al, FLPY_CNT_MASK ; mask out the head bit (drive only)
mov ( rccs + D ), al ; stuff the control string
call sense_drvst ; is the drive ready ?
cmp al, SUCCESS ! je rcl1
; no, so do the retry movie
dec rcl_rt ; decrement the retry count
jz rcl5
; there are some more retries left
; so wait for a little while
mov dx, DELAY_16MS
mov cl, F_DELAY
call supif
mov al, rcl_drv ; restore the drive were waiting for
jmps rcl0 ; goto the top of the disk error loop
rcl5:
; we ran out of retries while we were waiting
; for the drive to become ready
; so, return a hard error
mov ah,ATTA_FAILED
mov al, ERROR
jmps rcl2
rcl1:
; At this point the drive must
; be ready and on line
mov bx, offset rccs ; send the recal command
mov cx, length rccs
cli
call send_cmd
mov int_ctl, PRECAL ; set the interrupt code
sti
call int_wait ; wait for the recal to happen
; the floppy interrupt routine reads
; the FDC's interrupt sense.
; fdc_interrupt_stat is the value read.
mov al, fdc_interrupt_stat ; check for a kosher recal
and al, RECAL_MASK
cmp al, RECAL_TEST ! je rcl3 ; exit if recal ok
; something went wrong with
; the recal, do the retry movie
dec rcl_rt1
jz rcl4
mov dx, DELAY_16MS ; wait for a system tick
mov cl, F_DELAY
call supif
mov al, rcl_drv ; restore the drive we're recaling
jmps rcl1 ; goto the top of the error loop
rcl4:
; we ran out of retries
; so, return a hard error
mov ah,ATTA_FAILED
mov al, ERROR
jmps rcl2
rcl3:
mov al, SUCCESS
rcl2:
or al,al
ret
sense_drvst:
;-----------
;
; Sense drive status
;
; This routine issues the sense drive status to the FDC for
; the drive passed in al. It also updates the status register
; three table for this drive. This is the closest point to the
; drive status query in the program. So by updating the drive status
; table in this routine the drive status table is as current as possible.
;
; ENTRY: al = the drive we want to query.
;
; EXIT: al = SUCCESS if the drive is on line
; ERROR if the drive is not on line
; stat_reg_3_tbl[drive] = status register 3
;
dseg
sdscs db f_dsts, 0 ; sense drive status control string
sdsst db 0 ; sense drive status returned status
; STATUS REGISTER 3 TABLE, these reflect the most current
; status of the floppy drives.
stat_reg_3_tbl db 0 ; floppy 0, ( A )
db 0 ; floppy 1, ( B )
db 0 ;
db 0 ;
rb 12 ; for more floppies latter...
; loop counters to keep track of this routine
sdsc0 dw 0
sdsc1 dw 0
sdsc2 dw 0
sdsc3 dw 0
cseg
; query the FDC for the FDD's current status
and al, FLPY_CNT_MASK ; mask out any head info
mov ( sdscs + D ), al ; set up the sense disk stat string
mov bx, offset sdscs ; use the send command routine
mov cx, length sdscs ; for history it will provide
sds3:
call tbrn
in al, FDCS ; wait for the FDC to become
test al, MSTR_RQM ; ready for data xfer
jnz sds4
inc sdsc0
jmps sds3
sds4:
test al, MSTR_IO_DIRECTION ; wait for the FDC to become
jz sds5 ; ready to receive a command
call tbrn
in al, FDCS
inc sdsc1
jmps sds4
sds5:
mov al, [bx] ; get the command byte
out FDCD, al ; send it to the FDC
inc bx ; point to next byte
loop sds3 ; loop until the complete command is
; sent.
; wait for the FDc to become ready
; to send us it's status
sds0:
call tbrn
in al, FDCS
test al, MSTR_RQM
jnz sds6
inc sdsc2
jmps sds0
sds6:
test al, MSTR_IO_DIRECTION
jnz sds7
call tbrn
in al, FDCS
inc sdsc3
jmps sds6
sds7:
; it's ready, so read the status
in al, FDCD
mov sdsst, al
; stash the byte in the status register 3 table
xor bh,bh ; clear the upper byte
mov bl, ( sdscs + D ) ; get the drive
mov al, sdsst ; get status register 3
mov stat_reg_3_tbl[bx], al ; put the status register 3 just
; read from the floppy into the
; table entry for this floppy
; set the return code for the drive
; being on line and ready or not.
test al, STR3_READY ! jnz sds1
; the drive is not ready
mov ah,ATTA_FAILED
mov al, ERROR
jmps sds2
sds1:
; the drive is on line and ready
mov al, SUCCESS
sds2:
ret
eject
send_cmd:
;--------
; Send Command
;
; ENTRY: bx = addr ( command string )
; cx = length ( command string )
;
; EXIT: the command is sent
;
; NOTE: just before the last byte of a command is sent
; to the FDC interrupts are turned off. This is so
; the caller can post the proper action for the interrupt
; handler before an interrupt can get in. The caller
; is responsible to enabling interrupts as soon as the
; handler has it's control byte set.
;
; The FDC requires 12 us between each query of the main
; status port. The time burn loops do this
dseg
scc0 dw 0 ; waiting for FDC ready
scc1 dw 0 ; waiting for FDC direction
cseg
; poll on the fdc to be ready
sc1:
call tbrn
in al, FDCS
test al, MSTR_RQM
jnz sc2
inc scc0
jmps sc1
sc2:
; wait for the FDC's direction
test al, MSTR_IO_DIRECTION
jz sc3
call tbrn
in al, FDCS
inc scc1
jmps sc2
sc3:
; send the command byte
mov al, [bx]
out fdcd, al
inc bx
; when cx = 1 it means the next byte through
; will be the last byte of the command being sent
; to the FDC.
cmp cx, 1 ! jne scs2 ; is the next byte the last ?
; yes, the next
; byte is the last
cli
scs2:
loop sc1
ret
stat_ret:
;--------
; STATUS RETURN
;
; input cx number of bytes to expect from the fdc
; bx addr of where to put the results
;
; output the location starting where bx was pointing
; for a length of cx will have the results from the
; fdc result phase
;
; NOTE: The FDC requires 12 us between each query of the main
; status port. The time burn loops do this
dseg
strc0 dw 0 ; waiting for FDC ready counter
strc1 dw 0 ; waiting for FDC direction counter
cseg
mov al, fsb ; get the first status byte, that
mov [bx], al ; was read by the interrupt handler
inc bx ; inc the status buffer pointer
dec cx ; inc the bytes left count
jcxz sre ; if it's a one byte worth of status
; skip the loop
str1:
; wait for the FDC to become ready to send it's status
call tbrn
in al, FDCS
test al, MSTR_RQM
jnz str2
inc strc0
jmps str1
str2:
test al, MSTR_IO_DIRECTION
jnz str3
call tbrn
in al, FDCS
inc strc1
jmps str2
str3:
in al, FDCD ; get the FDC's status byte
mov [bx], al ; stash the status byte
inc bx ; point to the next buffer position
loop str1 ; loop until all the status bytes are
; read.
sre:
ret
eject
tbrn:
;----
;
; Time burn routine used by the interrupt code to wait for
; the FDC
mov tburn, BURN
tbrn0:
dec tburn
jnz tbrn0
ret
int_wait:
;
; this routine does a flag wait on the fdc operation.
cmp int_drv_flpy, TRUE! je real_int
iw1:
in al, ints ; poll the interrupt status
test al, INT_WAIT_POLL_BIT
jz iw1
ret
real_int:
mov dl, fdc_flag
mov cl, f_flagwait
call supif
ret
rw 48
flpy_int_tos rw 0
flpy_int_ssreg rw 1
flpy_int_spreg rw 1
end