mirror of
https://github.com/SEPPDROID/Digital-Research-Source-Code.git
synced 2025-10-25 17:34:06 +00:00
1249 lines
33 KiB
Plaintext
1249 lines
33 KiB
Plaintext
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
|
||
|