Category : Assembly Language Source Code
Archive   : ASMDRV.ZIP
Filename : RAMDISK.ASM
page 60,132
title A RAM Disk Device Driver
;****************************************************************
;* This is a Ram Disk Device Driver *
;****************************************************************
;summary:
; This ram disk device driver is built to DOS 3+ requirements.
; The command processing allows for 17 commands, numbered from
; 0 thru 16. This device driver runs under DOS versions 2 and 3.
; The specific extensions that are allowed in DOS 3 are not used
; where there would be a problem in using the device driver under
; DOS versions before 3.
;****************************************************************
;* INSTRUCTING THE ASSEMBLER *
;****************************************************************
cseg segment para public 'code' ;only one segment
ramdisk proc far
assume cs:cseg,es:cseg,ds:cseg
;structures
rh struc ;request header
rh_len db ? ;len of packet
rh_unit db ? ;unit code
;(block devices only)
rh_cmd db ? ;device driver command
rh_status dw ? ;returned by device driver
rh_res1 dd ? ;reserved
rh_res2 dd ? ;reserved
rh ends ;
rh0 struc ;Initialization (command 0)
rh0_rh db size rh dup (?) ;fixed portion
rh0_nunits db ? ;number of units
;(block devices only)
rh0_brk_ofs dw ? ;offset address for break
rh0_brk_seg dw ? ;segment address for break
rh0_bpb_tbo dw ? ;offset address of pointer
;to BPB array
rh0_bpb_tbs dw ? ;segment address of pointer
;to BPB array
rh0_drv_ltr db ? ;first available drive
;(DOS 3+) (block only)
rh0 ends ;
rh1 struc ;Media_Check (command 1)
rh1_rh db size rh dup (?) ;fixed portion
rh1_media db ? ;media descriptor from DPB
rh1_md_stat db ? ;media status returned by
;device driver
rh1 ends ;
rh2 struc ;Get_BPB (command 2)
rh2_rh db size rh dup(?) ;fixed portion
rh2_media db ? ;media descriptor from DPB
rh2_buf_ofs dw ? ;offset address of
;data transfer area
rh2_buf_seg dw ? ;segment address of
;data transfer area
rh2_pbpbo dw ? ;offset address of
;pointer to BPB
rh2_pbpbs dw ? ;segment address of
;pointer to BPB
rh2 ends ;
rh4 struc ;INPUT (command 4)
rh4_rh db size rh dup(?) ;fixed portion
rh4_media db ? ;media descriptor from DPB
rh4_buf_ofs dw ? ;offset address of
;data transfer area
rh4_buf_seg dw ? ;segment address of
;data transfer area
rh4_count dw ? ;transfer count
;(sectors for block)
;(bytes for character)
rh4_start dw ? ;start sector number
;(block only)
rh4 ends ;
rh8 struc ;OUTPUT (command 8)
rh8_rh db size rh dup(?) ;fixed portion
rh8_media db ? ;media descriptor from DPB
rh8_buf_ofs dw ? ;offset address of
;data transfer area
rh8_buf_seg dw ? ;segment address of
;data transfer area
rh8_count dw ? ;transfer count
;(sectors for block)
;(bytes for character)
rh8_start dw ? ;start sector number
;(block only)
rh8 ends ;
rh9 struc ;OUTPUT_VERIFY (command 9)
rh9_rh db size rh dup(?) ;fixed portion
rh9_media db ? ;media descriptor from DPB
rh9_buf_ofs dw ? ;offset address of
;data transfer area
rh9_buf_seg dw ? ;segment address of
;data transfer area
rh9_count dw ? ;transfer count
;(sectors for block)
;(bytes for character)
rh9_start dw ? ;start sector number (block only)
rh9 ends ;
rh15 struc ;Removeable (command 15)
rh15_len db ? ;len of packet
rh15_unit db ? ;unit code
;(block devices only)
rh15_cmd db ? ;device driver command
rh15_status dw ? ;returned by device driver
rh15_res1 dd ? ;reserved
rh15_res2 dd ? ;reserved
rh15 ends ;
;commands that do not have unique portions to the request header:
; INPUT_STATUS (command 6)
; INPUT_FLUSH (command 7)
; OUTPUT_STATUS (command 10)
; OUTPUT_FLUSH (command 11)
; OPEN (command 13)
; CLOSE (command 14)
; REMOVEABLE (command 15)
;
;****************************************************************
;* MAIN PROCEDURE CODE *
;****************************************************************
begin:
start_address equ $ ;starting address
;****************************************************************
;* DEVICE HEADER REQUIRED BY DOS *
;****************************************************************
next_dev dd -1 ;no device driver after this
attribute dw 2000h ;blk dev, non IBM format
strategy dw dev_strategy ;address of strategy routine
interrupt dw dev_interrupt ;address if interrupt routine
dev_name db 1 ;number of block devices
db 7 dup(?) ;7 byte filler
;****************************************************************
;* WORK SPACE FOR OUR DEVICE DRIVER *
;****************************************************************
rh_ofs dw ? ;offset address of request header
rh_seg dw ? ;segment address of request header
boot_rec equ $ ;dummy DOS boot record
db 3 dup(0) ;not a jump instruction
db 'TWG 1.0' ;vendor id
bpb equ $ ;standard values in ( )
bpb_ss dw 512 ;512 byte sector size
bpb_au db 1 ;cluster size is 1 sector
bpb_rs dw 1 ;1 (boot) reserved sector
bpb_nf db 1 ;1 FAT only *** (2)
bpb_ds dw 48 ;#files (64)
bpb_ts dw 205 ;sects=100kb + 5 overhead (360)
bpb_md db 0feh ;ss8 media descriptor (fc)
bpb_fs dw 1 ;FAT sectors (2)
bpb_ptr dw bpb ;bios parameter block pointer array (1 entry)
;current ram disc information
total dw ? ;transfer sector count
verify db 0 ;verify 1=yes , 0=no
start dw 0 ;start sector number
disk dw 0 ;RAM disk start address
buf_ofs dw ? ;data transfer offset address
buf_seg dw ? ;data transfer segment address
res_cnt dw 5 ;# reserved sectors
ram_par dw 6560 ;paragraphs of memory
bell db 1 ;1= bell on for RAM disk i/o
;****************************************************************
;* THE STRATEGY PROCEDURE *
;****************************************************************
dev_strategy: mov cs:rh_seg,es ;save the segment address
mov cs:rh_ofs,bx ;save the offset address
ret ;return to DOS
;****************************************************************
;* THE INTERRUPT PROCEDURE *
;****************************************************************
;device interrupt handler - 2nd call from DOS
dev_interrupt:
cld ;save machine state on entry
push ds
push es
push ax
push bx
push cx
push dx
push di
push si
mov ax,cs:rh_seg ;restore ES as saved by STRATEGY call
mov es,ax ;
mov bx,cs:rh_ofs ;restore BX as saved by STRATEGY call
;jump to appropriate routine to process command
mov al,es:[bx].rh_cmd ;get request header header command
rol al,1 ;times 2 for index into word table
lea di,cmdtab ;function (command) table address
mov ah,0 ;clear hi order
add di,ax ;add the index to start of table
jmp word ptr[di] ;jump indirect
;CMDTAB is the command table that contains the word address
;for each command. The request header will contain the
;command desired. The INTERRUPT routine will jump through an
;address corresponding to the requested command to get to
;the appropriate command processing routine.
CMDTAB label byte ;* = char devices only
dw INITIALIZATION ; initialization
dw MEDIA_CHECK ; media check (block only)
dw GET_BPB ; build bpb
dw IOCTL_INPUT ; ioctl in
dw INPUT ; input (read)
dw ND_INPUT ;*non destructive input no wait
dw INPUT_STATUS ;*input status
dw INPUT_FLUSH ;*input flush
dw OUTPUT ; output (write)
dw OUTPUT_VERIFY ; output (write) with verify
dw OUTPUT_STATUS ;*output status
dw OUTPUT_FLUSH ;*output flush
dw IOCTL_OUT ; ioctl output
dw OPEN ; device open
dw CLOSE ; device close
dw REMOVEABLE ; removeable media
dw OUTPUT_BUSY ; output til busy
;****************************************************************
;* YOUR LOCAL PROCEDURES *
;****************************************************************
save proc near ;saves data from Request Header
;
; called from INPUT, OUTPUT
;
mov ax,es:[bx].rh4_buf_seg ;save data transfer
mov cs:buf_seg,ax ; segment
mov ax,es:[bx].rh4_buf_ofs ;save data transfer
mov cs:buf_ofs,ax ; offset
mov ax,es:[bx].rh4_start ;get start sector number
mov cs:start,ax ; save it
mov ax,es:[bx].rh4_count ;# sectors to transfer
mov ah,0 ;clear hi order
mov cs:total,ax ; save in our area
ret ;return to caller
save endp
cvt2seg proc near ;calculates memory address
;
; requires cs:start starting sector
; cs:total total sector count
; cs:disk RAM disk start address
;
; returns ds segment address
; cx count of total bytes
; si = 0 for paragraph boundary
;
; uses ax
; cx
; si
; ds
;
mov ax,cs:start ;get starting sector number
mov cl,5 ;multiply by 32 paragraphs/sector
shl ax,cl ; by shifting left 5 places
mov cx,cs:disk ;get start segment of RAM disk
add cx,ax ;add to initial segment
mov ds,cx ; DS has start segment
mov si,0 ;make it on a paragraph boundary
mov ax,cs:total ;total number of sectors
mov cx,512 ;byte per sector
mul cx ;multiply to get xfer length
or ax,ax ;too large (carry set)?
jnz calc1 ;no (less than 64k)
mov ax,0ffffh ;yes - make it 64k
calc1: mov cx,ax ;move length to cx
ret ;return to caller
cvt2seg endp
bell1 proc near ;bell on if needed
cmp cs:byte ptr bell,0 ;bell required?
jz nobell1 ;no
mov al,0b6h ;magic #
out 43h,al ;timer2
mov ax,400h ;cycles
out 42h,al ;lsb
mov al,ah ;msb
out 42h,al
in al,61h ;spkr port
or al,3 ;spkr/timer on
out 61h,al ;
nobell1: ret ;return
bell1 endp
bell2 proc near ;bell off if needed
cmp cs:byte ptr bell,0 ;bell off needed?
jz nobell2 ;no
in al,61h ;get port
and al,0fch ;spkr/timer2 off
out 61h,al ;
nobell2: ret ;return
bell2 endp
;****************************************************************
;* DOS COMMAND PROCESSING *
;****************************************************************
;command 0 Initialization
Initialization:
call bell1 ;optional bell tone
call initial ;display console message
push cs ;move cs
pop dx ; to dx
;calculate end segment of RAM disk
lea ax,cs:start_disk ;start address of RAM disk
mov cl,1 ;hex digit shift count
ror ax,cl ;divide by 16 = paragraphs
add dx,ax ;add to current cs value
mov cs:disk,dx ;RAM disk start address
mov ax,ram_par ;add # RAM disk paragraphs
add dx,ax ;to start segment of RAM disk
;return the break address to DOS
mov es:[bx].rh0_brk_ofs,0 ;offset is 0
mov es:[bx].rh0_brk_seg,dx ;segment
;return number of units for a block device
mov es:[bx].rh0_nunits,1 ;only one ram disk
;return address of array of BIOS Parameter Blocks (1 only)
lea dx,bpb_ptr ;address of bpb pointer array
mov es:[bx].rh0_bpb_tbo,dx ;return offset
mov es:[bx].rh0_bpb_tbs,cs ;return segment
;initialize boot, FAT, Directory to zeroes
push ds ;cvt2seg destroys ds
mov cs:start,0 ;start sector = 0
mov ax,cs:res_cnt ;#reserved sectors
mov cs:total,ax ;#sectors
call cvt2seg ;address and count
mov al,0 ;fill value
push ds ;save
pop es ;move to es
mov di,si ;all offsets = 0
rep stosb ;clear reserved sectors
;move boot record to sector 0
pop ds ;restore ds -> RAM disk
mov es,cs:disk ;RAM disk start address
mov di,0 ;zero out di (boot record)
lea si,cs:boot_rec ;address of boot record
mov cx,24 ;
rep movsb ;copy 24 bytes of boot record
;build one and only one FAT
mov cs:start,1 ;logical sector 1
mov cs:total,1 ;doesn't matter
call cvt2seg ;get ds:si set quickly
mov ds:byte ptr [si],0feh ;set the first 2 FAT
mov ds:byte ptr 1[si],0ffh ; entries to describe
mov ds:byte ptr 2[si],0ffh ; disc
call bell2 ;optional bell off
;end of initialization - restore es:bx exit
mov ax,cs:rh_seg ;move request header
mov es,ax ; segment to es
mov bx,cs:rh_ofs ; offset to bx
jmp done ;set DONE bit & exit
;command 1 Media_Check
Media_Check: ;block device only
mov es:[bx].rh1_media,1 ;media is unchanged
jmp done ;set DONE bit & exit
;command 2 Get_BPB
Get_BPB: ;read Boot record
push es ;save request header segment
push bx ;save request header offset
mov cs:start,0 ;boot record = sector 0
mov cs:total,1 ;1 sector
call cvt2seg ;convert to RAM disk address
push cs ;set es to
pop es ; cs
lea di,cs:bpb ;address of bios param blk
add si,11 ;add 11 to si
mov cx,13 ;length of bpb
rep movsb ;move
pop bx ;restore request header offset
pop es ;restore request header segment
mov dx,cs:bpb_ptr ;pointer to BPB array
mov es:[bx].rh2_pbpbo,dx ; to Request Header
mov es:[bx].rh2_pbpbs,cs ;same for segment
lea dx,cs:bpb ;address of BPB =
mov es:[bx].rh2_buf_ofs,dx ; sector buffer offset
mov es:[bx].rh2_buf_seg,cs ;same for segment
jmp done ;set DONE bit & exit
;command 3 IOCTL_Input
IOCTL_Input:
jmp unknown ;set error bit/code & exit
;command 4 Input Read Ram disk and return data to DOS
Input:
call bell1 ;turn on bell if required
call save ;save Request Header data
call cvt2seg ;calc RAM disk start address
mov es,cs:buf_seg ;set destination seg & ofs
mov di,cs:buf_ofs ; to es:di
mov ax,di ;get offset
add ax,cx ;add transfer length
jnc input1 ;overflow?
mov ax,0ffffh ;yes - use max transfer
sub ax,di ;subtract offset from max
mov cx,ax ;new transfer count
input1: rep movsb ;Read RAM disk to data area
call bell2 ;turn off bell if required
mov ax,cs:rh_seg ;move request header
mov es,ax ; segment to es
mov bx,cs:rh_ofs ; offset to bx
jmp done ;set DONE bit & exit
;command 5 ND_Input
ND_Input:
jmp busy ;set BUSY bit & exit
;command 6 Input_Status
Input_Status:
jmp done ;set DONE bit & exit
;command 7 Input_Flush
Input_Flush:
jmp done ;set DONE bit & exit
;command 8 Output Write data to RAM disk
Output:
call bell1 ;turn bell on if needed
call save ;save Request Header data
call cvt2seg ;get start address in ram
push ds ;move to
pop es ; es
mov di,si ;same for di
mov ds,cs:buf_seg ;ds:si points to source
mov si,cs:buf_ofs ; data in DOS
rep movsb ;move ds:si to es:di
mov bx,cs:rh_ofs ;restore es:bx
mov es,cs:rh_seg ;for possible jmp to input
cmp cs:verify,0 ;do we verify write?
jz out1 ;no
mov cs:verify,0 ;reset verify indicator
jmp input ;read those sectors back in
out1: call bell2 ;turn bell off if required
mov ax,cs:rh_seg ;move request header
mov es,ax ; segment to es
mov bx,cs:rh_ofs ; offset to bx
jmp done ;set DONE bit & exit
;command 9 Output_Verify
Output_Verify: ;output (write) with verify
mov cs:verify,1 ;set the verify flag
jmp output ;go to output routine
;command 10 Output_Status
Output_Status:
jmp done ;set DONE bit & exit
;command 11 Output_Flush
Output_Flush:
jmp done ;set DONE bit & exit
;command 12 IOCTL_Output
IOCTL_Out:
jmp unknown ;set error bit/code & exit
;command 13 Open
Open:
jmp done ;set DONE bit & exit
;command 14 Close
Close:
jmp done ;set DONE bit & exit
;command 15 Removeable
Removeable:
mov es:[bx].rh_status,0200h ;set busy
jmp done ;set DONE bit & exit
;command 16 Output Til Busy
OUTPUT_BUSY:
jmp unknown ;set error bit/code & exit
;****************************************************************
;* ERROR EXIT *
;****************************************************************
unknown:
or es:[bx].rh_status,8003h ;set error bit and error code
jmp done ;set done and exit
;****************************************************************
;* COMMON EXIT *
;****************************************************************
load_status:
mov cx,cs:rh_seg ;restore request header
mov es,cx ; segment to es
mov cx,cs:rh_ofs ;restore offset also
xchg bx,cx ;switch them
mov es:[bx].rh_status,ax ;return status
mov es:[bx].rh8_count,cx ;return output count
jmp done ;set done bit and exit
busy: or es:[bx].rh_status,0200h ;set busy bit
done: or es:[bx].rh_status,0100h ;set done
pop si ;restore all registers
pop di
pop dx
pop cx
pop bx
pop ax
pop es
pop ds
ret ;return to DOS
;****************************************************************
;* END OF PROGRAM *
;****************************************************************
end_of_program:
;org to paragraph boundary for start of RAM disk
if ($-start_address) mod 16
org ($-start_address)+16-(($-start_address) mod 16)
endif
start_disk equ $
initial proc near ;
lea dx,msg1 ;initialization
mov ah,9 ; message
int 21h ;doscall
ret ;return
initial endp
msg1 db 'The Waite Group 100k Ram Disc',0dh,0ah,'$'
ramdisk endp
cseg ends
end begin
;that's all folks
Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!
This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.
But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/