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

1249 lines
33 KiB
Plaintext
Raw Permalink 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.

eject ; Dec 9, 1983
; DISK I/O
; --------
;****************************************************************
;* *
;* FLOPPY DISK DRIVER EQUATES *
;* *
;****************************************************************
; The following equates are set to the size of a double density,
; single sided 5 & 1/4" floppy.
bytes_per_sector equ 512
sectors_per_track equ 8 ;1 to 8
bytes_per_track equ sectors_per_track * bytes_per_sector
; The following equates are for the INTEL 8272 Floppy Disk
; Controller.
fdc_stat equ 03f4h ;status port for disk controller
fdc_data equ fdc_stat+1 ;data port for disk controller
fdc_port equ 03f2h ;all bits clear on channel reset
;7 6 5 4 3 2 1 0
;| | | | | | \_/
;| | | | | | |
;| | | | | | drive select 00=a,01=b,10=c,11=d
;| | | | | fdc reset*
;| | | | int & dmarq enable
;d c b a motor on
fdc_on equ 00001100b ;mask to keep the 8272 unreset
fdc_reset_bit equ 00000100b ;8272 reset when low
fdc_no_motor equ 11111100b ;mask for no motors
fdc_cmnd_read equ 01100110b ;mfm, skip deleted data, read
fdc_cmnd_write equ 01000101b ;mfm, write
fdc_cmnd_format equ 01001101b ;mfm, format
fdc_cmnd_seek equ 00001111b ;seek
fdc_cmnd_recal equ 00000111b ;home to track 0
fdc_cmnd_si equ 00001000b ;sense interupt status
fdc_cmnd_spec equ 00000011b ;specify
fdc_ready_write equ 10000000b ;mask for transfer ready
fdc_ready_read equ 10000000b ;mask for transfer ready
fdc_spec_1 equ 11001111b ;srt=0c, hd unload=0f 1st specify byte
fdc_spec_2 equ 00000010b ;hd load=1, mode=dma 2nd specify byte
f_bytes equ 2 ;magic number for 512 bytes per sector
f_sectors equ 8 ;sectors per track
f_gap equ 050h ;this is IBM's, not August's
f_filler equ 0e5h ;fill character
r_bytes equ 2 ;magic number for 512 bytes
r_sectors equ 8
r_gap equ 02ah
r_dtl equ 0ffh
; Equates for paramter passing for read and write from BDOS.
; At the disk read and write function entries,
; all disk I/O parameters are on the stack.
; The stack at these entries appears as follows:
; +-------+-------+
; +14 | DRV | MCNT | Drive and Multi sector count
; +-------+-------+
; +12 | TRACK | Track number
; +-------+-------+
; +10 | SECTOR | Physical sector number
; +-------+-------+
; +8 | DMA_SEG | DMA segment
; +-------+-------+
; +6 | DMA_OFF | DMA offset
; +-------+-------+
; +4 | RET_SEG | BDOS return segment
; +-------+-------+
; +2 | RET_OFF | BDOS return offset
; +-------+-------+
; SP+0 | RET_ADR | Return address to XIOS ENTRY routine
; +-------+-------+
;
; These parameters may be indexed and modifided
; directly on the stack by the XIOS read and write rotines
; They will be removed by the BDOS when the XIOS completes
; the read/write function and returns to the BDOS.
drive equ byte ptr 14[bp]
mcnt equ byte ptr 15[bp]
track equ word ptr 12[bp]
sector equ word ptr 10[bp]
dma_seg equ word ptr 8[bp]
dma_off equ word ptr 6[bp]
; Some equates in the Disk Parameter Header (DPH)
; and the Disk Parameter Block.
xlt equ 0 ;translation table offset in DPH
dpb equ 8 ;disk parameter block offset in DPH
spt equ 0 ;sectors per track offset in DPB
psh equ 15 ;physical shift factor offset in DPB
;****************************************************************
;* *
;* 8237 DIRECT MEMORY ACCESS CONTROLLER PORT AND COMMANDS *
;* *
;****************************************************************
dma_c2_address equ 004h ;8237 channel 2 address rw
dma_c2_count equ 005h ;8237 channel 2 transfer count rw
dma_stat_reg equ 008h ;8237 status register ro
dma_bmsk_reg equ dma_stat_reg+2 ;8237 binary channel mask wo
dma_mode_reg equ dma_stat_reg+3 ;8237 mode register wo
dma_cbpf equ dma_stat_reg+4 ;8237 clear byte ptr f/f wo
dma_page_fdc equ 081h ;a16 to a20 for channel 2
; The following labels define single mode, address increment
; auto-initialization disable, read or write using channel 2
dma_mode_write_fdc equ 01001010b
dma_mode_read_fdc equ 01000110b
dma_mode_verify_fdc equ 01000010b
dma_bmsk_fdc equ 00000010b ;binary channel mask for disk
; DMA channel assignments
;channel 0 dynamic memory refresh
;channel 1
;channel 2 floppy disk controller
;channel 3
;********************************************************
;* *
;* DISK DRIVER ROUTINES *
;* *
;********************************************************
reorg12 equ offset $
cseg
org reorg12
; Function 7: Select Disk
; entry: CL = disk to be selected
; DL = 00h if disk has not been previously selected
; = 01h if disk has been previously selected
; exit: AX = 0 if illegal disk
; = offset of DPH relative from
; XIOS Data Segment
io_seldsk:
mov bx,offset select_tbl
jmps disk_branch ; vector to select routine
; Function 11: Read sector
; Reads the sector on the current disk, track and
; sector into the current dma buffer.
; entry: parameters on stack
; exit: AL = 00 if no error occured
; AL = 01 if an error occured
io_read:
mov bx,offset read_tbl
jmps io_rw ; code shared with io_write
; Function 12: Write disk
; Write the sector in the current Dma buffer to the current disk
; on the current track in the current sector.
; exit: Al = 00H if no error occured
; = 01H if error occured
; = 02H if read only disk
io_write:
mov bx,offset write_tbl
io_rw:
mov bp,sp ; for stack parameters
mov cl,drive ; for branching out
mov cur_disk,cl ; save for port stuff
; jmps disk_branch
; Branch point for select, read, and write
; entry: bx -> routine vector base
; cl = disk number
disk_branch:
mov ch,0 ; clear msb
mov si,cx
shl si,1 ; si = disk number index
jmp word ptr [bx+si]
; Select Disk branch table
select_tbl dw select_flop,select_flop ; A,B
dw select_flop,select_flop ; C,D
dw select_hd,select_hd ; E,F
dw sel_fid,sel_fid,sel_fid ; G,H,I
dw sel_fid,sel_fid,sel_fid ; J,K,L
dw select_mdisk,sel_fid,sel_fid ; M,N,O
dw sel_fid ; P
; Read branch table
read_tbl dw read_flop,read_flop ; A,B
dw read_flop,read_flop ; C,D
dw read_hd,read_hd ; E,F
dw read_fid,read_fid,read_fid ; G,H,I
dw read_fid,read_fid,read_fid ; J,K,L
dw read_mdisk,read_fid,read_fid ; M,N,O
dw read_fid ; P
; Write branch table
write_tbl dw write_flop,write_flop ; A,B
dw write_flop,write_flop ; C,D
dw write_hd,write_hd ; E,F
dw write_fid,write_fid,write_fid ; G,H,I
dw write_fid,write_fid,write_fid ; J,K,L
dw write_mdisk,write_fid,write_fid ; M,N,O
dw write_fid ; P
; Function 12: Flush buffers
; This is a nop nowadays
io_flushbuf:
xor al,al ; zero for no error
ret
; Function 15: Format floppy disk
; Format the current track of the current drive
; exit: al = 00h if no error
; al > 00h if error occured
io_format:
mov bp,sp ; for stack parameters
jmp format_floppy ; down in the code
; *** Floppy Disk Subroutines ***
; Select floppy disk, check size if first time
; entry: cl = drive number
select_flop:
mov bx,dph_tbl[si] ; fetch dph pointer
cmp dl,0 ; if not first time selected
jnz sel_flop1 ; then skip
push cx
mov al,0FEh ; lsb off
shl al,cl ; move off bit to disk number
and recal_byte,al ; this drive not recal'd
pop cx
call floppy_size ; double or single sided
sel_flop1:
mov ax,bx ; return dph in ax
ret
; Read floppy track 0 sector 0 into the local buffer
; to decide whether it's single or double sided.
; entry: bx = dph pointer
; cl = drive code
; exit: bx = dph pointer or 00h if error reading size
floppy_size:
push bx ; save dph pointer
mov al,cl ; drive code
mov ah,1 ; multi-sector count
push ax ; stack drive,mcnt
sub ax,ax
push ax ; stack track
push ax ; stack sector
push ds ; stack dma segment
mov ax,offset local_buffer
push ax ; stack dma offset
sub sp,4 ; skip the far return
call io_read ; read the sector
add sp,14 ; clear the stack
pop bx ; restore dph pointer
test al,al ; if an error comes back
jnz flop_size_bad ; then return zero
mov ax,offset dpbs ; assume single sided
cmp floppy_type,01h ; if not double sided
jnz flop_size1 ; then skip
mov ax,offset dpbd ; set to double sided
flop_size1:
mov .dpb[bx],ax ; save dpb offset in dph
ret
flop_size_bad:
sub bx,bx ; return bx=00h
ret
; Floppy Disk sector Read entry
read_flop:
mov dma_mode_storage,dma_mode_read_fdc
mov fdc_command,fdc_cmnd_read ;set up the command words
jmp flp_io
; Floppy Disk sector Write entry
write_flop:
mov dma_mode_storage,dma_mode_write_fdc
mov fdc_command,fdc_cmnd_write
call flp_io
test al,al ; check return code
jnz write_flop_done ; if bad, give up
cmp verify_flag,0 ; if we aren't verifying
jz write_flop_done ; then we're also done
mov dma_mode_storage,dma_mode_verify_fdc
mov fdc_command,fdc_cmnd_read
call flp_io
write_flop_done:
ret ; with code in al
; Floppy Disk track Format entry
format_floppy:
mov dma_mode_storage,dma_mode_write_fdc
mov fdc_command,fdc_cmnd_format
mov ax,ds ; local dma buffer for c,h,r,n
mov bx,offset format_block
call comp_dma ; set up dma pointers
mov al,drive ; current drive (on stack)
mov cur_disk,al ; save for seek
mov ax,track ; also on stack
mov cur_trk,ax
call disk_init ; this does it
call check_disk_op ; did it work?
cmp al,'R' ; if retry requested
jz format_floppy ; then loop up
ret ; else return error code
; Common code for floppy read and write
flp_io:
;note: SECTOR, TRACK is 0 relative
;MCNT, SECTORS_PER_TRACK are relative to 1
mov ax,sectors_per_track;max sectors - starting sector =
sub ax,sector ;sectors left on track
xor bh,bh
mov bl,mcnt ;multi sector count is byte variable
cmp ax,bx ;sectors left on track, sectors requested
jbe flp_track ;transfer to end of track
mov al,mcnt ;transfer to before end of track
flp_track:
sub mcnt,al ;AL = # sectors to r/w on this iteration
;through FLP_IO,
mov track_mcnt,al ;sectors to r/w on current track
;check for 64K page overlap
mov ah,al ;shl al,8 (* 256)
xor al,al
shl ah,1 ;* 512
push ax ;how many bytes to r/w on cur trk
mov ax,dma_seg ;compute new 20 bit DMA addr
mov bx,dma_off
call comp_dma ;returns AX = low 16 bits of 20 bit adr
not ax ;how many bytes left in this 64K page
pop bx
cmp ax,bx ;does this transfer fit in 64K page
jae flp_pg_ok ;yes
call flp_deblock ;do a local read/write
jnz flp_ret
mov ax,dma_seg ;compute new 20 bit DMA addr
mov bx,dma_off
call comp_dma
flp_pg_ok: ;read will not cross 64K boundary
mov al,track_mcnt ;could be 0 if we just deblocked
test al,al ! jz nxt_track
mov num_sec,al ;read the rest from this track
call floppy_io ;DMA is already computed
jnz flp_ret ;return error if zero flag is not set
xor ax,ax
mov ah,num_sec ;shl num_sec,8 (* 256)
shl ah,1 ;* 512
add dma_off,ax
nxt_track:
xor al,al
cmp mcnt,al
jz flp_ret
inc track
mov sector,0
jmp flp_io
flp_ret: ; AL = 00 if sucessful
ret ; AL = error code if not
comp_dma: ;Compute 20 bit address from offset, segment
; Entry: AX = segment
; BX = offset
; Exit AX = low 16 bits
; CH = highest 4 bits of address, always less then 16 -
; no megabyte wrap around
;
; The XIOS variables DMA_low16 and DMA_high4 are
; set by this routine. These variables are transferred
; to the floppy disk controller by the routine DMA_SET_UP.
mov cl,4 ! rol ax,cl ;make paragraphs into bytes
mov ch,al ! and al,0f0h ;save high 4 bits, 0 low 4 bits
add ax,bx ;add byte offset
adc ch,0 ! and ch,0fh ;add in the carry, page is less than
mov dma_low16,ax ;16
mov dma_high4,ch
ret
; deblock transfers across 64k boundary by read/write locally
flp_deblock:
mov al,ah ;how many sectors fit in this page ?
xor ah,ah
shr ax,1 ;divided by 512
test ax,ax ;if no sectors, skip to local_rw
jz flp_local_rw
mov num_sec,al ;sectors to read
sub track_mcnt,al ;track_mcnt always > AL
call floppy_io ;read them, dma already computed
jnz flp_deblock_err
xor ax,ax
mov al,num_sec ;compute new DMA offset
add sector,ax ;still on the same track
xchg ah,al ;shl ax,8 (* 256)
shl ah,1 ;* bytes_per_sector (512)
add dma_off,ax ;64K wrap around is legal
flp_local_rw:
cmp fdc_command,fdc_cmnd_write
jnz flp_deblock_op ;skip if not writing
push ds ! push es
mov si,dma_off
mov di,offset local_buffer
push ds ! pop es ;local destination
mov ds,dma_seg
mov cx,bytes_per_sector/2
rep movsw ;move dma to local
pop es ! pop ds
flp_deblock_op:
mov bx,offset local_buffer ;deblock spanning sector
mov ax,ds ;compute 20 bit local DMA addr
call comp_dma
mov num_sec,1 ;read one sector
call floppy_io ;XIOS data cannot span 64K page
jnz flp_deblock_err ;return error
inc sector ! dec track_mcnt
cmp fdc_command,fdc_cmnd_read
jnz flp_deblock_done ;skip if not reading
push es
mov di,dma_off ;move the sector to user's area
mov si,offset local_buffer
mov es,dma_seg
mov cx,bytes_per_sector/2
rep movsw
pop es
flp_deblock_done:
add dma_off,bytes_per_sector
xor al,al ;set zero flag for success
ret
flp_deblock_err:
or al,al ;set not zero for error
ret
; FLOPPY_IO performs the physical read and write
; to the physical disk.
floppy_io:
mov ax,track
mov cur_trk,ax ; set track
mov ax,sector
inc ax ; disk sectors start at 1
mov cur_sec,ax ; set sector
mov d_a_number,r_bytes
mov d_a_eot,r_sectors
mov d_a_gpl,r_gap
mov d_a_dtl,r_dtl
call disk_init
call check_disk_op ;see if it was ok
cmp al,'R' ;if retry requested
jz floppy_io ; then go again
test al,al ;set zf if no error
ret ;and return with al
; The following routine does the read or write operation
; as selected by the global variables.
disk_init:
mov disk_error,0 ;clear the global error flag
mov disk_on_going,0FFh ;flag the motor off counter
mov cl,cur_disk
mov al,01h ; lsb only
shl al,cl ; move bit to disk number
test recal_byte,al ; if disk has been recal'd
jnz disk_routine ; then skip to operation
or recal_byte,al ; flag for next time
; First time through, or after controller reset, recal the drive
disk_recal:
call motor_on
call recal ;back to track zero
call wait_sense_get ;sense interrupt cycle
; Drive has been recal'd, continue with operation
disk_routine:
mov retry_counter,max_retry
disk_routine_1:
call motor_on ;make sure the drive is spun up
call seek ;and were on the right track
call wait_sense_get ;sense interrupt cycle
call dma_setup
call disk_operation
mov disk_time_out,60 ;now, watch for too much time
mov dx,fdc_flag ;if more than a second
call flagwait ; then we have a time out error
mov disk_time_out,0 ;shut off the counter
cmp disk_error,80h ;if it's a timeout error
jz disk_ctrl_reset ; then reset the controller
disk_routine_2:
call fdc_status_get
mov al,d_r_st0
and al,0c0h ;mask out for termination bits
jz disk_routine_ok ;if no errors then leap
dec retry_counter ;else see if more retries are allowed
jnz disk_routine_1 ;if so then try whole operation again
disk_routine_ok:
mov disk_on_going,0 ;allow the motor to shut off
ret
; Reset the disk controller following a time out error
disk_ctrl_reset:
cli
mov al,motor_flags ; get motor bit
and al,not fdc_reset_bit ; reset bit low
mov dx,fdc_port
out dx,al ; reset that hummer
mov recal_byte,0 ; recal all drives
mov disk_on_going,0 ; allow motors off
or al,fdc_reset_bit
out dx,al ; unreset it too
sti
call wait_sense_get ; sense interrupt cycle
mov al,d_r_st0 ; get status byte
cmp al,0C0h ; if perfect
jz d_c_respecify ; then redo the specify
mov disk_error,20h ; bad NEC error
ret
d_c_respecify:
jmp flop_specify ; respecify head step
; The following routine reads/writes as directed from the disk.
disk_operation:
mov disk_results,7 ;7 byte result transfer
mov al,fdc_command ;get the requested command
mov d_a_command,al ;drop it in to the command block
cmp al,fdc_cmnd_format ;if formating
jz disk_op_format ; then skip
mov disk_arguments,9 ;9 byte command string
jmp fdc_command_put ;fire off the disk controller
disk_op_format:
push es ;first set up the format block
push ds ! pop es ; for the current cylinder/head.
mov di,offset format_block ;this is done most easily
mov cx,sectors_per_track ; after "seek" sets cyl/head.
mov ax,word ptr d_a_cylinder ;al=cyl, ah=head
disk_op_format_loop:
stosw ;store cylinder/head
inc di ! inc di ;past sector/size
loop disk_op_format_loop ;do for each sector
pop es
mov d_a_cylinder,f_bytes ;now use the usual r/w command
mov d_a_head,f_sectors ; string for the format command.
mov d_a_record,f_gap ;the names don't match the
mov d_a_number,f_filler ; but we promise not to tell.
mov disk_arguments,6 ;format command is shorter
jmp fdc_command_put ;fire off the disk controller
; The following routine homes the head on the selected disk drive.
recal:
mov disk_arguments,2 ;specify number of arguments
mov d_a_command,fdc_cmnd_recal
mov disk_results,1 ;request 1 result read
mov al,cur_disk ;get current disk
mov d_a_drive,al ;set up the command block
call fdc_command_put ;go send the command block
ret
; The following routine seeks to the selected track on the
; selected drive
seek:
mov disk_results,1 ;only one status byte returned
mov disk_arguments,3 ;request 3 byte command transfer
mov d_a_command,fdc_cmnd_seek
mov bl,0 ;get a head 0
mov ax,cur_trk
cmp al,40 ;test for off side 1
jb side_ok ;if single sided then leap
mov bl,1 ;else get a head 1
neg al ;and wrap around the edge
add al,79 ;cyl = 79 - track
side_ok:
mov d_a_head,bl ;set up the head
add bl,bl ;multiply by 2
add bl,bl ;multiply by 4
mov d_a_cylinder,al ;set up the track number
or bl,cur_disk ;mash disk into head
mov d_a_drive,bl ;set up disk number
mov ax,cur_sec ;get starting sector
mov d_a_record,al ;and set it up
add al,num_sec ;compute new end of track ????
dec al
mov d_a_eot,al ;save it
call fdc_command_put ;send the command block
ret
; do a flag wait, sense interrupt and get fdc status
; used by recal, seek, and reset operations
wait_sense_get:
mov dx,fdc_flag
call flagwait
call sense_interrupt ;jolt the less than wonderful 8272
jmp fdc_status_get ;read the status
; The following routine does the sense interrupt command on
; the FDC. It is called after a recal or a seek to test for
; seek complete.
sense_interrupt:
mov disk_results,2 ;2 bytes are returned
mov disk_arguments,1 ;only one byte of command
mov d_a_command,fdc_cmnd_si ;drop in the sense interrupt commmand
call fdc_command_put ;send the command
ret
; floppy disk controller specify command sets head step speed
flop_specify:
mov disk_arguments,3 ; for a specify command
mov d_a_command,fdc_cmnd_spec
mov al,fdc_spec1_var ; pick up step speed
mov d_a_drive,al ; and store it
mov d_a_cylinder,fdc_spec_2 ; just the usual here
jmp fdc_command_put ; set the head step speed
; The following routine sends the command block in the
; DISK_ARGUMENTS table to the 8272 FDC.
; The number of commands to write to the FDC is the
; first item in the table.
fdc_command_put:
mov dx,fdc_stat ;point to the i/o port
mov si,offset disk_arguments ;point to the table of arguments
cld ;make sure we go forward
lodsb ;get the length of the arguments table
mov cl,al ;get it into the count register
sub ch,ch ;zero the high byte
fdc_command_loop:
in al,dx ;get the current control byte
test al,fdc_ready_write ;if not ok to send next byte
jz fdc_command_loop ;then loop waiting
inc dx ;point at the data port
lodsb ;else get the byte to send
out dx,al ;send it
dec dx ;point back at the status port
loop fdc_command_loop ;if not last byte then loop
ret ;else were all done
; The following routine gets the status information from the 8272
; FDC and places them in the table at DISK_RESULTS.
; The first byte in the table is the number of results
; to read from the FDC
fdc_status_get:
push es ;save UDA
mov dx,fdc_stat ;point at the status port
mov ax,ds ;get our data segment
mov es,ax ;into the extra segment
mov di,offset disk_results + 1 ;point to where to put the data
cld ;make sure we go forward
mov cl,disk_results ;fetch the number of expected results
sub ch,ch ;zero the high byte
fdc_status_loop:
in al,dx ;get the current control byte
test al,fdc_ready_read ;if not ok to read next byte
jz fdc_status_loop ;then loop waiting
inc dx ;point at the data port
in al,dx ;get the byte
stosb ;put it in the structure
dec dx ;point back at the status port
loop fdc_status_loop ;if not last then loop
pop es ;restore UDA
ret ;else return
; The following routine sets the DMA device up for a
; read/write operation. The current DMA command word must be
; at DMA_MODE_STORAGE. The read/write operation cannot cross
; a physical 64K boundary.
dma_setup:
out dma_cbpf,al ;reset the byte pointer
mov al,dma_mode_storage ;get the mode byte
out dma_mode_reg,al ;set the mode
mov ax,dma_low16 ;low 16 bits of 20 bit DMA address
out dma_c2_address,al ;send low 8 bits
mov al,ah
out dma_c2_address,al ;send next 8 bits
mov al,dma_high4 ;high 4 bits of 20 bit DMA address
out dma_page_fdc,al
xor ax,ax
mov ah,num_sec ;shl num_sec,8 (* 256)
shl ah,1 ;*512
dec ax ;0 relative
out dma_c2_count,al ;set up the low byte
mov al,ah ;get the low byte
out dma_c2_count,al ;and the high byte
mov al,dma_bmsk_fdc ;get the binary channel mask
out dma_bmsk_reg,al ;enable the disk channel
ret
; The following routine resets the motor_off_counter and tests
; the current motor_flags against the selected drive.
motor_on:
mov motor_off_counter,40h ;extend the motor off counter
mov al,010h ;pick up a bit for motor a
mov cl,cur_disk ;fetch the binary drive code
shl al,cl ;make it a bit for motor x
mov ah,motor_flags ;fetch the motor bits
test ah,al ;check to see if its on
jnz motor_on_done ;yes then leap
or al,fdc_on ;mask in the no reset,enable interupt
or al,cur_disk ;mask in the drive
mov motor_flags,al ;save for later
mov dx,fdc_port ;point to the port number
out dx,al ;select & motor on
mov dx,ticks_per_second/8 ;wait 1/8 of second for the
mov cl,p_delay ;motor to spin up on writes
call supif
motor_on_done:
ret
; The following routine checks to see if the last
; disk operation was OK. It returns with the zero flag
; set if it was OK or if the error is to be ignored.
; exit: al = FFh for accept error
; 00h for no error or ignore error
; 'R' for retry operation
check_disk_op:
mov al,d_r_st0 ; get the first result byte
and al,0C0h ; see if ok
jnz flop_error ; no then leap
ret ; al = 0 for no error
flop_error:
mov cl,cur_disk ; force a recal on next op
mov al,0FEh ; lsb off
shl al,cl ; move off bit to disk number
and recal_byte,al ; this drive not recal'd
cmp disk_error,0 ; if error code is there
jnz flop_error_go ; then skip the lookup
mov si,offset flop_er_tbl ; stat bits / error codes
mov cx,flop_er_size ; table length
flop_error_loop:
lodsw ; get stat bits
test d_r_st12,ax ; test received status
jnz flop_error_found ; if this is the one
inc si ; past error code
loop flop_error_loop
flop_error_found:
lodsb ; fetch the error code
mov disk_error,al ; and store for message
flop_error_go:
jmp do_disk_error ; return A=FFh, I=00h, R='R'
; *** Memory Disk Routines ***
; Select Memory Disk
select_mdisk:
mov ax,dph_tbl[si] ; fetch dph pointer
ret
; Memory Disk read
read_mdisk:
push ds ! push es
call mdisk_calc ;calculate byte address
les di,dword ptr dmaoff ;load destination DMA address
sub si,si ;setup source DMA address
mov ds,bx ;load pointer to sector in memory
jmps mdisk_common ;jump to shared code
; Memory Disk write
write_mdisk:
push ds ! push es
call mdisk_calc ;calculate byte address
mov es,bx ;setup destination DMA address
sub di,di
lds si,dword ptr dmaoff ;load source DMA address
mdisk_common:
rep movsw ;move between user and memory
pop es ! pop ds
xor ax,ax ;return no error
ret
; Calculate mdisk offset into memory
; output: BX = sector paragraph address
; CX = length in words to transfer
mdisk_calc:
mov bx,track ;pickup track number
mov cl,3 ;8 sectors per track
shl bx,cl
add bx,sector ;gives absolute sector number
mov cl,5 ;times 32 for paragraph of sector start
shl bx,cl
add bx,mdisk_start ;plus base address of disk in memory
mov ch,mcnt
mov cl,0 ;cx = 256 * mcnt = words to move
ret
; *** Disk Error Handling Routine ***
; This is called on any disk error from either
; the floppy or hard disk drivers.
; entry: disk_error = ROS error code
; exit: al = 00h for ignore error (treat op as sucessful)
; 01h for accept error
; 02h for accept write protect error
; 'R' for retry operation
uda_err_mode equ byte ptr .10h ; error mode byte in uda
err_mode_code equ 0FEh ; or 0FFh for quick return
wp_code equ 03h ; ROS definition
do_disk_error:
cmp es:uda_err_mode,err_mode_code
jae disk_error_accept ; skip if quick return
mov al,drive
add al,'A'
mov disk_er_drive,al ; save drive code
; save track number
push es
push ds ! pop es
mov di,offset disk_er_track
mov ax,track
call store_track ; into the error message
mov di,offset disk_er_codes
mov cx,disk_er_size
mov al,disk_error
repnz scasb ; look for our code
mov ax,di
sub ax,offset disk_er_codes + 1
mov cx,sub_msg_size
mul cl ; ax = sub message index
mov si,ax
add si,offset sub_msg_tbl
mov di,offset disk_er_sub
rep movsb ; store the sub message
pop es
mov si,offset disk_er_msg ; finished message
mov di,offset disk_er_resp ; allowable responses
call sl_error_out ; error message to stat line
cmp al,'R' ; if retry,
jz disk_error_done ; then return 'R'
cmp al,'I' ; if ignore
mov al,0 ; then return 00h
jz disk_error_done ; if accept
disk_error_accept:
mov al,1 ; then assume physical
cmp disk_error,wp_code ; but if write protect
jnz disk_error_done ; then return a 2
mov al,2
disk_error_done:
ret
; store the track number
; entry: ax = track number
; di -> track num destination
; es = ds
store_track:
mov word ptr .1[di],' '
mov dh,0 ; for lzb test
div er_hundred ; al = hund's, ah = rem
call store_digit ; print hundred's place
mov al,ah
cbw ; ready for divide
div er_ten
call store_digit ; print ten's place
mov al,ah
mov dh,0FFh ; no lzb on last digit
; jmps store_digit ; print one's place
store_digit:
push ax ; save the number
or dh,al ; if zero, then lead blank
jz store_lzb
or al,'0' ; convert to ascii
stosb ; and store to message
store_lzb:
pop ax ; restore number
ret
eject
;****************************************************************
;* *
;* FLOPPY DISK DRIVER DATA *
;* *
;****************************************************************
reorg13 equ (offset $ + 1) and -2
dseg
org reorg13
max_retry equ 4
; The following tables are used to issue commands to,
; and read results from the 8272 FDC. The first entry
; in each table is the number of bytes to send or receive
; from the device
disk_arguments db 0 ;number of arguments to send
d_a_command db 0 ;command read/write
d_a_drive db 0 ;drive select & head select
d_a_cylinder db 0 ;cylinder to read/write
d_a_head db 0 ;head
d_a_record db 0 ;sector
d_a_number db 2 ;magic number for 512 bytes/sector
d_a_eot db sectors_per_track ;end of track sector number
d_a_gpl db 02ah ;inter sector gap length
d_a_dtl db 0ffh ;data length
disk_results db 0 ;number of bytes to read
d_r_st0 db 0 ;status byte 0
d_r_st1 db 0 ;status byte 1
d_r_st2 db 0 ;status byte 2
d_r_st12 equ word ptr d_r_st1
d_r_cylinder db 0 ;cylinder we are on now
d_r_head db 0
d_r_record db 0
d_r_number db 0 ;number of sectors read
f_a_bytes db 0
f_a_sectors db 0
f_a_gap db 0
f_a_filler db 0
dma_mode_storage db dma_mode_read_fdc ;current dma mode
fdc_command db fdc_cmnd_read ;current fdc mode
cur_disk db 0ffh
cur_trk dw 0
cur_sec dw 0
num_sec db 1 ;num sectors to read/write
;in one call to controller
track_mcnt rb 1 ;multi sector count on
;current track
dma_low16 rw 1 ;20 bit address storage
dma_high4 rb 1
disk_on_going db 0 ;0FFh if disk op happening
retry_counter db 0 ;0 no more retrys left
motor_flags db 0 ;last state of the motor bits
motor_off_counter db 0
fdc_spec1_var db fdc_spec_1 ;head step default
recal_byte db 0 ;which disks have been recal'd
verify_flag db 0 ;verify after disk write?
disk_time_out db 0 ;count down for tick routine
; Format Block consists of eight copies of c,h,r,n data
; which the Floppy Disk Controller uses for formating.
format_block db 0,0,1,f_bytes
db 0,0,2,f_bytes
db 0,0,3,f_bytes
db 0,0,4,f_bytes
db 0,0,5,f_bytes
db 0,0,6,f_bytes
db 0,0,7,f_bytes
db 0,0,8,f_bytes
; Floppy now shares its local buffer with hard disk
; Stack switch area for disk interrupt
dw 0cccch,0cccch,0cccch,0cccch
dw 0cccch,0cccch,0cccch,0cccch
dw 0cccch,0cccch,0cccch,0cccch
dw 0cccch,0cccch,0cccch,0cccch
dw 0cccch,0cccch,0cccch,0cccch
dw 0cccch,0cccch,0cccch,0cccch
dw 0cccch,0cccch,0cccch,0cccch
dw 0cccch,0cccch,0cccch,0cccch
dw 0cccch,0cccch,0cccch,0cccch
dw 0cccch,0cccch,0cccch,0cccch
user_save_area rs 0
user_ss dw 0
user_sp dw 0
user_ax dw 0
mdisk_start dw 0D000h ;may be changed in init
; Floppy disk parameter headers
dph0 dw 0000h,0000h ;translate table
dw 0000h,0000h ;scratch area
dw dpbs ;dsk parm block
dw 0ffffh ;check
dw 0ffffh ;alloc vectors
dw 0ffffh ;dir buff cntrl blk
dw 0ffffh ;data buff cntrl blk
dw 0ffffh ;hash table segment
dph1 dw 0000h,0000h ;translate table
dw 0000h,0000h ;scratch area
dw dpbs ;dsk parm block
dw 0ffffh ;check
dw 0ffffh ;alloc vectors
dw 0ffffh ;dir buff cntrl blk
dw 0ffffh ;data buff cntrl blk
dw 0ffffh ;hash table segment
dph2 dw 0000h,0000h ;translate table
dw 0000h,0000h ;scratch area
dw dpbs ;dsk parm block
dw 0ffffh ;check
dw 0ffffh ;alloc vectors
dw 0ffffh ;dir buff cntrl blk
dw 0ffffh ;data buff cntrl blk
dw 0ffffh ;hash table segment
dph3 dw 0000h,0000h ;translate table
dw 0000h,0000h ;scratch area
dw dpbs ;dsk parm block
dw 0ffffh ;check
dw 0ffffh ;alloc vectors
dw 0ffffh ;dir buff cntrl blk
dw 0ffffh ;data buff cntrl blk
dw 0ffffh ;hash table segment
dph_md dw 0000h,0000h ;translate table
dw 0000h,0000h ;scratch area
dw dpb_md ;dsk parm block
dw 0000h ;no check
dw 0ffffh ;alloc vectors
dw 0ffffh ;dir buff cntrl blk
dw 0ffffh ;data buff cntrl blk
dw 0ffffh ;hash table segment
; single sided floppy parameter block
dpbs dw 8 ;sectors per track
db 3 ;block shift
db 7 ;block mask
db 0 ;extnt mask
dw 155 ;disk size in 1k blocks
;less offset track(s)
dw 63 ;directory max
db 11000000b ;alloc0
db 0 ;alloc1
dw 16 ;check size
dw 1 ;offset
db 2 ;phys sec shift
db 3 ;phys sec mask
; double sided floppy parameter block
dpbd dw 8 ;sectors per track
db 4 ;block shift
db 15 ;block mask
db 1 ;extnt mask
dw 157 ;disk size in 2k blocks
;less offset track(s)
dw 63 ;directory max
db 10000000b ;alloc0
db 0 ;alloc1
dw 16 ;check size
dw 1 ;offset
db 2 ;phys sec shift
db 3 ;phys sec mask
; memory disk parameter block
dpb_md dw 8 ;sectors per track
db 4 ;block shift
db 15 ;block mask
db 1 ;extnt mask
dsm_md dw 191 ;max disk size in 2k blocks
;recalculated in init
dw 63 ;directory max
db 10000000b ;alloc0
db 0 ;alloc1
dw 8000h ;;check size, changed to show permanent media
dw 0 ;offset
db 2 ;phys sec shift
db 3 ;phys sec mask
;************************************************
;* *
;* DISK ERROR MESSAGE DATA AREA *
;* *
;************************************************
disk_error rb 1 ; ROS error code store
; Table of floppy status bits versus ROS error codes
flop_er_tbl db 10h,01h,40h ; bad seek
db 20h,20h,10h ; bad crc
db 04h,00h,04h ; sector not found
db 02h,00h,03h ; write protect
db 01h,01h,02h ; bad addr mark
flop_er_size equ (offset $ - offset flop_er_tbl)/3
db 0BBh ; unknown error
disk_er_codes db 01h,02h,03h,04h,05h,07h
db 08h,09h,0Bh,10h,11h,20h
db 40h,80h,0AAh,0FFh,0BBh
disk_er_size equ offset $ - offset disk_er_codes
sub_msg_size equ 19
sub_msg_tbl db ' ROS Command Error ' ; 01h
db ' Bad Address Mark ' ; 02h
db ' Write-Protected ' ; 03h
db ' Record Not Found ' ; 04h
db ' Can''t Reset Disk ' ; 05h
db ' Drive Setup Error ' ; 07h
db ' DMA Chip Failure ' ; 08h
db ' DMA Address Error ' ; 09h
db 'Bad Track Flag Set ' ; 0Bh
db 'Uncorrectable Data ' ; 10h
db 'Data Was Corrected ' ; 11h
db 'Bad Disk Controller' ; 20h
db 'Head Position Error' ; 40h
db ' No Response ' ; 80h
db 'Read Doesn''t Match ' ; AAh
db 'Can''t Sense Status ' ; FFh
db ' Error Not Defined ' ; BBh and default
disk_er_msg db 'Disk Error Drive '
disk_er_drive db 'X: Track '
disk_er_track db 'XXX '
disk_er_sub db 'Some sort of detail '
db ' Retry, Ignore, Accept? '
disk_er_resp db 3,'RIA'
er_hundred db 100 ; for track divide
er_ten db 10