Files
oldlinux-files/distributions/TAMU/dos_utils/rawfd.asm
2024-02-19 00:23:20 -05:00

880 lines
32 KiB
NASM

;*********************************************************************;
;* rawfd.asm - a raw floppy device driver for MS-LOSS *;
;* *;
;* written by david safford (dave.safford@net.tamu.edu) *;
;* portions of this code were derived from: *;
;* condrv.asm by Michael Tischer (overall skeleton) *;
;* drvload by Rick Knoblaugh (exe based loading) *;
;* tapedriver by Brian Antoine (raw device open trap) *;
;* *;
;* Assembly: TASM rawfd *;
;* LINK rawfd *;
;* *;
;* Usage: add "device=rawfd.exe" to config.sys and reboot, *;
;* or load the driver directly with "rawfd". *;
;* This will create devices "rawfda" and "rawfdb" for *;
;* drives a and b respectively. The driver gets initial *;
;* drive type information from bios. All data sent to *;
;* the raw drives is simply streamed out to floppy one *;
;* track at a time. If less than a track is written, *;
;* it is held indefinitely until more data is sent, *;
;* until the device is closed, or until the flush *;
;* ioctl is sent (by rerunning rawfd.exe). *;
;* If the driver is already loaded, subsequent invocations *;
;* will send any command line options to the driver *;
;* via ioctl. Command switches include 'f' (format), 'n' *;
;* (no format), and 'r' (reset). *;
;* *;
;*********************************************************************;
code segment
assume cs:code,ds:code
org 0
;=====================================================================
;Driver section
;---------------------------------------------------------------------;
; header rawfda
hdr_a dw offset hdr_b ;well documented (:-) way to link to
dw 0 ;hdr_b
dw 1110100000000000b ;Driver attribute
dw offset strata ;Pointer to strategy routine
dw offset intr ;Pointer to interrupt routine
db "RAWFDA " ;Raw Floppy driver
;---------------------------------------------------------------------;
; header rawfdb
hdr_b dw -1,-1 ;end of driver chain
dw 1110100000000000b ;Driver attribute
dw offset stratb ;Pointer to strategy routine
dw offset intr ;Pointer to interrupt routine
db "RAWFDB " ;Raw Floppy driver
;---------------------------------------------------------------------;
; driver function pointer table
fct_tab dw offset init ;Function 0: Initialization
dw offset dummy ;Function 1: Media check
dw offset dummy ;Function 2: Create BPB
dw offset no_sup ;Function 3: I/O control read
dw offset dummy ;Function 4: Read
dw offset dummy ;Function 5: Non-destructive read
dw offset dummy ;Function 6: Input status
dw offset dummy ;Function 7: Delete input buffer
dw offset write ;Function 8: Write
dw offset write ;Function 9: Write & verify
dw offset dummy ;Function 10: Output status
dw offset flush ;Function 11: Delete output buffer
dw offset ioc_wr ;Function 12: I/O control write
dw offset open ;Function 13: Open (Ver. 3.0 and up)
dw offset close ;Function 14: Close
dw offset dummy ;Function 15: Changeable medium
dw offset write ;Function 16: Output until busy
;---------------------------------------------------------------------;
; driver equates and data
cmd_fld equ 2 ;Offset command in req header
status equ 3 ;Offset status in req header
end_adr equ 14 ;Offset driver end adr in req header
num_db equ 18 ;Offset number in req header
b_adr equ 14 ;Offset buf address in req header
NUM_SEC_4 equ 18 ;type 4 diskette (1.44M)
NUM_SEC_2 equ 15 ;type 2 diskette (1.2M)
BUF_SZ_4 equ 512*18 ;Size of track buf for type 4 (1.44)
BUF_SZ_2 equ 512*15 ;size of track buf for type 2 (1.2)
num_cmd equ 16 ;Subfunctions 0-16 are supported
db_ptr dw (?),(?) ;Address of req header
buf_start dw (?) ;offset to active track buffer
buf_end dw (?) ;offset to end of buffer
fmt db 18*4 dup (?) ;format track data buffer
num_cyl db 80
num_head db 2
num_sec_a db NUM_SEC_4
buf_sz_a dw BUF_SZ_4
num_sec_b db NUM_SEC_2
buf_sz_b dw BUF_SZ_2
do_format db 1
cur_count dw 0
cur_drive db 0
cur_num_sec db NUM_SEC_4
cur_buf_sz dw BUF_SZ_4
cur_cyl db 0
cur_head db 0
tries db 0
req_num dw 0
req_dec dw 0
req_off dw 0
req_seg dw 0
init_done db 0
;---------------------------------------------------------------------;
; driver a strategy
strata proc far ;Strategy routine
mov cs:db_ptr,bx ;Store address of req header in the
mov cs:db_ptr+2,es ;Variable DB_PTR
mov cs:cur_drive,0 ;let writer know logical device
mov ah,cs:num_sec_a ;update cur parameters
mov cs:cur_num_sec,ah
mov ax,cs:buf_sz_a
mov cs:cur_buf_sz,ax
ret ;Return to caller
strata endp
;---------------------------------------------------------------------;
;driver b strategy
stratb proc far ;Strategy routine
mov cs:db_ptr,bx ;Store address of req header in
mov cs:db_ptr+2,es ;the Variable DB_PTR
mov cs:cur_drive,1 ;let writer know logical device
mov ah,cs:num_sec_b ;update cur drive parameters
mov cs:cur_num_sec,ah
mov ax,cs:buf_sz_b
mov cs:cur_buf_sz,ax
ret ;Return to caller
stratb endp
;---------------------------------------------------------------------;
; driver interrupt code
intr proc far ;Interrupt routine
push ax ;Push registers onto the stack
push bx
push cx
push dx
push di
push si
push bp
push ds
push es
pushf ;Push flag register onto the stack
push cs ;Set data segment register
pop ds ;Code and data are identical
les di,dword ptr db_ptr ;Address of data req header to ES:DI
mov bl,es:[di+cmd_fld] ;Get command code
cmp bl,num_cmd ;is command code permitted?
jle bc_ok ;YES --> bc_ok
mov ax,8003h ;Code for "Unknown command"
jmp short intr_end ;Return to caller
bc_ok: shl bl,1 ;Calculate pointer in jump table
xor bh,bh ;Clear BH
call [fct_tab+bx] ;Call function
les di,dword ptr cs:db_ptr ;req header address to ES:DI
intr_end label near
or ax,0100h ;Set ready bit
mov es:[di+status],ax ;Store everything in the status field
popf ;Restore flag register
pop es ;Restore other registers
pop ds
pop bp
pop si
pop di
pop dx
pop cx
pop bx
pop ax
ret ;Return to caller
intr endp
;----------------------------------------------------------------------
; dummy (no error) routine
dummy proc near ;This routine does nothing
xor ax,ax ;Clear busy bit
ret ;Return to caller
dummy endp
;----------------------------------------------------------------------
; dummy (returns error) routine
no_sup proc near ;for all functions
;which should not be called
mov ax,8003h ;Error: Command not recognized
ret ;Return to caller
no_sup endp
;----------------------------------------------------------------------
;write: simply buffer chars until have a full track.
write proc near
;this is a pain in asm -- have to handle case of
;request larger, smaller, or equal to that available in buffer.
;move original request info to working variables
mov cx, es:[di+num_db] ;Number of source characters
mov cs:req_num, cx ;working pointer to source data
mov ax, es:[di+b_adr]
mov cs:req_off, ax
mov ax, es:[di+b_adr+2]
mov cs:req_seg, ax
write_s: ; set up copy of data to buffer: want ds:si -> es:di
; for cx count, LIMITED by buf length and any current contents
;check that we have something to put in buffer
mov cx, cs:req_num
jcxz write_e
;calculate di (destination offset)
mov ax, cs:buf_start ;get start of buf
add ax, cs:cur_count ;add offset for already queued data
mov di,ax
;calculate cx (count for this loop)
mov ax, cs:cur_buf_sz ;how big is buf
sub ax, cs:cur_count ;minus queued data -> available
cmp ax, cx ;num left to write
jnb write_a ;fits within buf
mov cx,ax ;data too big, so limit to buf space
;cx now has num to write this time
write_a: mov cs:req_dec,cx ;save this decrement count
mov ds,cs:req_seg ;point ds to source data
mov si,cs:req_off
push cs
pop es ;point es to dest buf
cld ;Increment on movsb
rep movsb ;do the move (ds:si -> es:di for cx)
; copy done, update cur_count, req_off, req_num
mov ax,cs:cur_count
add ax,cs:req_dec
mov cs:cur_count,ax
mov ax,cs:req_num
sub ax,cs:req_dec
mov cs:req_num,ax
mov ax,cs:req_off
add ax,cs:req_dec
mov cs:req_off,ax
;if buf full, write it and check for more
mov ax,cs:cur_count
cmp ax,cs:cur_buf_sz
jb write_e
call flush ;write the track
cmp ax,1
jz write_err
jmp write_s ;check for more source data
write_e: xor ax,ax ;Everything O.K.
ret ;Return to caller
write_err:
mov ax,0ah ;send write error
ret
write endp
;----------------------------------------------------------------------
; flush a track to diskette, with optional formatting
flush proc near
mov cs:tries,0 ;reset error count
flush_s: mov ah,cs:do_format ;do we want to format track first?
cmp ah,1
jnz flush_w
; format the track first
; first set the media descriptor table (ddtp)
mov ah, 18h ;set media function
mov ch, cs:num_cyl
dec ch ;need max cyl number (79)
mov cl, cs:cur_num_sec
mov dl, cs:cur_drive
int 13h ;do set media
jc ferror
mov ax,0
mov ds,ax
mov bx,1eh*4 ;bios ptr to ddtp
mov ds:[bx],di
mov ds:[bx+2],es
; set up fmt buffer
mov ch,1 ;sec number (1..18)
mov bx, offset cs:fmt
f_fmt: mov ah,cs:cur_cyl
mov cs:[bx],ah ;cylinder
inc bx
mov ah,cs:cur_head
mov cs:[bx],ah ;head
inc bx
mov cs:[bx],ch ;sector
inc bx
mov ah,2
mov cs:[bx],ah ;sector size 2 (512 bytes)
inc bx
inc ch
mov ah,ch
cmp ah,cs:cur_num_sec
jna f_fmt
;set up for format call
push cs
pop es
mov bx, offset cs:fmt ;es:bx -> format buffer
mov dh,cs:cur_head ;head
mov dl,cs:cur_drive ;drive
mov ch,cs:cur_cyl ;cylinder
mov al,cs:cur_num_sec ;num sectors
mov ah,5 ;format command
int 13h ;do the format
jc ferror
flush_w: ;write the track
mov ah,3 ;write command
mov al,cs:cur_num_sec
mov ch,cs:cur_cyl
mov cl,1 ;start sector
mov dh,cs:cur_head
mov dl,cs:cur_drive
push cs
pop es
mov bx, cs:buf_start
int 13h ;bios write track
jnc flush_c
ferror: ;got error
mov ah,cs:tries
inc ah
cmp ah,3
jz flush_f ;3 errors, give up
mov cs:tries,ah
mov ah,0 ;reset drive
mov dl,cs:cur_drive
int 13h
jmp flush_s ;try again
flush_c: ;write complete, update pointers
xor ax,ax
mov cs:cur_count,ax ;zero cur_count
mov ah,cs:cur_head
xor ah,1 ;bump head
mov cs:cur_head,ah
cmp ah,1 ;head was 0, so don't bump cyl
jz flush_e
mov ah,cs:cur_cyl ;head was 1 so bump cyl
inc ah
cmp ah,80
jb flush_d
xor ax,ax ;end of disk, zero cyl
flush_d: mov cs:cur_cyl,ah
flush_e:
xor ax,ax ;Everything O.K.
ret ;Return to caller
flush_f: mov ax,1 ;return error to write function
ret
flush endp
;----------------------------------------------------------------------
; driver open function
open proc near
mov cs:opened_flag,1
mov ax,cs:cur_count
cmp ax,0
jz open_a
call flush
open_a:
call reset
xor ax,ax
ret
open endp
;----------------------------------------------------------------------
; driver close function
close proc near
mov cs:opened_flag,0
mov ax,cs:cur_count
cmp ax,0
jz close_a
call flush
close_a:
call reset
xor ax,ax
ret
close endp
;----------------------------------------------------------------------
; ioctl write driver function
ioc_wr proc near
lds si, es:[di+b_adr] ;get ioc msg in ds:[si]
mov ah,ds:[si] ;get first data char
cmp ah,'f' ;is 'f'ormat?
jnz ioc_wr_a
mov cs:do_format,1
jmp ioc_wr_e
ioc_wr_a:
cmp ah,'n' ;is 'n'o format ?
jnz ioc_wr_b
mov cs:do_format,0
jmp ioc_wr_e
ioc_wr_b:
cmp ah,'r'
jnz ioc_wr_e
mov ax,cs:cur_count
cmp ax,0
jz ioc_wr_c
call flush
ioc_wr_c:
call reset
ioc_wr_e:
xor ax,ax
ret
ioc_wr endp
;----------------------------------------------------------------------
; reset current write parameters
reset proc near
mov cs:cur_count,0
mov cs:cur_drive,0
mov cs:cur_cyl,0
mov cs:cur_head,0
ret
reset endp
;----------------------------------------------------------------------
; int 21 handler to make all our opens in binary mode
orig_int_21 dd ? ;Original INT 21 Vector
opened_flag db 0
our_int_21 proc far
pushf ;Save entry flags
cmp ah,3Dh ;Is it an open request?
jnz not_open_req
popf ;Restore entry flags
sti ;Allow interrupts
pushf ;After the iret
cli ;Shut interrupts off
call cs:orig_int_21 ;While we Pass the request on
pushf
cli
cmp cs:opened_flag,0 ;Was it an open for us?
jz not_our_open
mov cs:opened_flag,0 ;Clear for next time
push ax
mov bx,ax ;Save the Handle
mov ax,4400h ;Get Device Information
pushf
call cs:orig_int_21
mov dh,0 ;Setup
or dl,20h ;for RAW Mode
mov ax,4401h ;Set Device Information
pushf
call cs:orig_int_21
pop ax
not_our_open: popf ;The Original Flags to return
ret 2 ;Return and discard flags
not_open_req: popf ;Pop the saved flags
jmp cs:orig_int_21 ;Continue with original code
our_int_21 endp
;======================================================================
; driver initialization section
;----------------------------------------------------------------------
; init - if loaded by config.sys
init proc near
;check if this is second call
mov ah,cs:init_done
cmp ah,0
jnz init_e
;on entry, es:di -> request header
lds si, es:[di+18] ;get ds:si -> command line after '='
mov al,0 ;counter to limit search
init_a: mov ah,ds:[si] ;look for first switch character
cmp ah, ' '
jz init_b ;found a space?
cmp ah, 0 ;found a null?
jmp init_f ;if so, nothing to do
inc si
inc al
cmp al,128 ;we will look for 128 chars in tail
jnz init_a ;keep on looking
jmp init_f ;give up
init_b: inc si ;got a space
mov ah,ds:[si] ;this is first arg
cmp ah,'N' ;dos makes the line all uppercase
jnz init_f ;if not 'N' then nothing to do
mov cs:do_format,0 ;got 'N', so reset do_format
init_f:
call do_init ;method independent initialization
call patch_us_in ;raw open checker
init_e:
mov cs:init_done,1
;set end address of driver -- keep init, as it is called twice
les di,dword ptr cs:db_ptr ;req header address to ES:DI
mov ax,cs:buf_end
mov es:[di+end_adr],ax
mov es:[di+end_adr+2],cs
xor ax,ax
ret
init endp
;-------------------------------------------------------------------------
; init code common to driver or exe loading
do_init proc near
;get drive params
;default is drive a: 1.44, drive b: 1.2M
mov ah,8 ;get drive type
mov dl,0 ;drive a:
int 13h
cmp bl,4 ;is drive 0 type 4 (1.44M)?
jz do_init_a
mov cs:num_sec_a,NUM_SEC_2 ;not type 4 - then assume type 2
mov cs:buf_sz_a,BUF_SZ_2
do_init_a:
mov ah,8 ;get drive type
mov dl,1 ;drive b:
int 13h
cmp bl,2 ;is drive b: type 2 (1.44M)?
jz do_init_b
mov cs:num_sec_a,NUM_SEC_4 ;not type 2 - then assume type 4
mov cs:buf_sz_a,BUF_SZ_4
do_init_b:
;setup track buf start and end offsets
mov ax, offset cs:patch_end
mov cs:buf_start, ax
add ax,BUF_SZ_4
mov cs:buf_end,ax
;check if it crosses 64K DMA boundary
mov cl,4
push cs
pop dx
mov ax, cs:buf_start ;compute buf_start's top nibble
shr ax,cl
add ax,dx
and ax,0f000h
mov bx,ax ;save buf_start's in bx
mov ax, cs:buf_end ;compute buf_end's top nibble
shr ax,cl
add ax,dx
and ax,0f000h
cmp ax,bx ;compare them
jz do_init_e ;same, so buf OK
mov ax,cs:buf_end
mov cs:buf_start,ax ;first buf bad, so move to its end
add ax,BUF_SZ_4
mov cs:buf_end,ax
do_init_e:
;add psp and good luck
mov ax,cs:buf_end
add ax,200h
mov cs:buf_end,ax
ret
do_init endp
;---------------------------------------------------------------------
; initialization code to patch in our int 21 handler
vect_int_21 equ word ptr 4 * 21h
patch_us_in proc near
cli
mov ax,0 ;Patch Ourselves into
mov es,ax ;the INT 21 Vector
mov ax,es:[vect_int_21] ;Offset
mov word ptr cs:orig_int_21,ax
mov ax, offset cs:our_int_21
mov es:[vect_int_21],ax
mov ax,es:[vect_int_21+2] ;Segment
mov word ptr cs:orig_int_21+2,ax
mov ax,cs
mov es:[vect_int_21+2],ax
sti
ret
patch_end:
patch_us_in endp
;======================================================================
; exe section: load driver or send ioctl
; Equates
DOS_GET_VER equ 30h
DOS_RELEASE_MEM equ 49h
DOS_TSR_FUNC equ 31h
DOS_LIST_LISTS equ 52h ;undocumented call to get "list of lists"
; structs:
;
doub_word struc
d_offset dw ?
d_segment dw ?
doub_word ends
list_lists30 struc ;list of lists info (DOS 3.0)
dpb_ptr30 dd ? ;ptr 1st DOS DPB
file_tab30 dd ? ;ptr DOS file tables
clock_ptr30 dd ? ;ptr to CLOCK$ device
con_ptr30 dd ? ;ptr to CON device
num_blk30 db ? ;number block devices
max_byte30 dw ? ;max bytes/block
dsk_buf30 dd ? ;ptr 1st disk buffer
cds_ptr30 dd ? ;ptr to current disk struc
last_drv30 db ? ;LASTDRIVE value
strg_wrk30 dd ? ;STRING workspace area
srg_size30 dw ? ;size of STRING area
fcb_tab30 dd ? ;ptr to FCB table
fcb_y_30 dw ? ;y in FCBs=x,y
nul_dev30 db 18 dup(?) ;NUL device header
list_lists30 ends
list_lists31 struc ;list of lists info (DOS 3.1)
dpb_ptr31 dd ? ;ptr 1st DOS DPB
file_tab31 dd ? ;ptr DOS file tables
clock_ptr31 dd ? ;ptr to CLOCK$ device
con_ptr31 dd ? ;ptr to CON device
max_byte31 dw ? ;max bytes/block
dsk_buf31 dd ? ;ptr 1st disk buffer
cds_ptr31 dd ? ;ptr to current disk struc
fcb_tab31 dd ? ;ptr to FCB table
fcb_y_31 dw ? ;y in FCBs=x,y
num_blk31 db ? ;number block devices
last_drv31 db ? ;LASTDRIVE value
nul_dev31 db 18 dup(?) ;NUL device header
num_join31 db ? ;number of JOINed drives
list_lists31 ends
dev_header struc
dev_chain dd ?
dev_attrib dw ?
dev_stratr dw ?
dev_intr dw ?
dev_num_units db ? ;first byte of char name
dev_char_name db 7 dup(?)
dev_header ends
ver_spec_off struc ;version specific offsets
vcds_ptr dw ? ;offset of CDS ptr
vdpb_ptr dw ? ;offset of DPB ptr
vnul_dev_ptr dw ? ;offset of NUL device header
vlast_drive dw ? ;offset of LASTDRIVE
ver_spec_off ends
; data:
;
dos_ver dw 0
nul_dev_ptr dd ? ;pointer to NUL device
dos30_ver_off ver_spec_off <cds_ptr30,dpb_ptr30,nul_dev30,last_drv30>
dos31_ver_off ver_spec_off <cds_ptr31,dpb_ptr31,nul_dev31,last_drv31>
;----------------------------------------------------------------------
; entry point if run as exe
exe_start proc near
call do_args ;get first option char
call do_ioc ;exits if drv already loaded
call do_init ;init driver
call patch_us_in
call get_list_ptr ;find nul dev header
call add_to_chain ;add rawfda and rawfdb to chain
call go_tsr ;leave driver resident
exe_start endp
;--------------------------------------------------------------
;set do_format and ioc_msg based on args
do_args proc near
;the exe program is just starting, so ds is still PSP
;set defaults
mov cs:do_format,1 ;default is format
mov cs:ioc_msg, byte ptr 'r'
mov si,82h
mov ah,ds:[si]
cmp ah,'n'
jnz do_args_a
mov cs:ioc_msg, byte ptr 'n'
mov cs:do_format,0
ret
do_args_a:
cmp ah,'f'
jnz do_args_b
mov cs:ioc_msg, byte ptr 'f'
mov cs:do_format,1
ret
do_args_b:
cmp ah,'r'
jnz do_args_e
mov cs:ioc_msg, byte ptr 'r'
do_args_e:
ret
do_args endp
;--------------------------------------------------------------
; see if driver is loaded, and if so, send command switch via ioctl
drv_name db 'RAWFDA',0h
ioc_msg db 'n'
do_ioc proc near
;try to open rawfda using file handle call
push cs
pop ds
mov al,2 ;read/write access
mov ah,3dh ;open file handle
lea dx,drv_name ;filename
int 21h ;do it
jc do_ioc_e ;failed, ret to load driver
;do ioctl write ;succeeded, so just send ioctl
push cs
pop ds
mov bx,ax ;handle ->bx
mov ah,44h ;ioctl
mov al,3 ;write
mov cx,1 ;length of write
lea dx,ioc_msg ;msg buf
int 21h ;doit
;simply exit now
mov ax,4c00h
int 21h
do_ioc_e:
ret
do_ioc endp
;--------------------------------------------------------------
;get_list_ptr - Use Undocumented DOS function 52h to
; retrieve pointer to NUL driver header.
get_list_ptr proc near
mov ax,3000h
int 21h ; get dos version
mov cs:dos_ver,ax
mov ah, DOS_LIST_LISTS
int 21h ;get list of lists
mov di, offset cs:dos31_ver_off ;default to 3.1 >
cmp byte ptr cs:dos_ver, 4 ;maj ver 4 >?
jae get_list100
cmp dos_ver, 300h ;is it 3.0?
jne get_list100
mov di, offset cs:dos30_ver_off ;offsets for 3.0
get_list100:
mov si, cs:[di].vnul_dev_ptr ;offset to NUL ptr
mov ax, es ;get seg NUL dev
mov cs:nul_dev_ptr.d_segment, ax
add si, bx
mov cs:nul_dev_ptr.d_offset, si ;offset of NUL dev
get_list999:
ret
get_list_ptr endp
;--------------------------------------------------------------
;add_to_chain - Add driver to driver chain.
;
add_to_chain proc near
lds si, cs:nul_dev_ptr ;point to NUL header
mov ax, [si].dev_chain.d_offset ;ptr to next drvr
mov dx, [si].dev_chain.d_segment
cli
; point NUL -> hdr_a (es:bx)
mov bx, offset cs:hdr_a
mov [si].dev_chain.d_offset, bx ;put ours in list
push cs
pop es
mov [si].dev_chain.d_segment, es
; point hdr_a -> hdr_b (offset is already set)
mov es:[bx+2],es
; point hdr_b -> orig next dev (dx:ax)
mov bx, offset cs:hdr_b
mov es:[bx].dev_chain.d_offset, ax ;link to
mov es:[bx].dev_chain.d_segment, dx ;old 1st drvr
sti
ret
add_to_chain endp
;--------------------------------------------------------------
;go_tsr - Free our environment and then terminate and stay resident
go_tsr proc near
mov bx, cs:buf_end
mov cl,4
shr bx,cl
mov ah, DOS_RELEASE_MEM
push bx
int 21h
pop dx
mov ax, (DOS_TSR_FUNC SHL 8) ;exit code 0
int 21h ;Go TSR
tail: ret
go_tsr endp
;======================================================================
code ends
end exe_start