home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 September
/
Simtel20_Sept92.cdr
/
msdos
/
ramdisk
/
adjram41.arc
/
AMDISK.ASM
< prev
next >
Wrap
Assembly Source File
|
1988-05-15
|
33KB
|
1,165 lines
; ==== Adjustable Memory Disk Device Driver ====
;
; (c) Copyright 1986,1987,1988 by Gary R. Cramblitt
;
; v2.2 1 Jul 86 Initial version
; v2.3 24 Aug 86 Bug. FAT media byte not updated properly
; v2.4 29 Aug 86 Sync version # with ADJRAM.C No other changes.
; v2.5 30 Aug 86 Add /E (LOTUS/INTEL,Microsoft Expanded Memory) support;
; Increase root directory to 128 entries;
; Permit maximum size of 2043K.
; Get rid of second FAT.
; v3.0 5 Sep 86 Sync version # with ADJRAM.C. No other changes.
; v3.1 2 Oct 86 Sync version # with ADJRAM.C. No other changes.
; v3.2 18 Oct 86 Add SIZE= option
; v4.0 3 Jan 87 Chg SIZE= to MINSIZE=
; Add support for reserved conventional memory.
; Add CLUSTER= option.
; Allow for expanded memory blocks larger than 64K.
; Use legal FAT ID. CHKDSK now happy with memory disk, and
; occasional DOS "FAT error" prevented.
; Add volume label to memory disk.
; v4.1 15 May 88 Restrict reserved memory to A segment or above.
; Initialize reserved memory.
;
; ==== Constant Definition ===================
;
; ---- Customizable Definitions -----
; If these are changed, the corresponding symbols in adjram.c
; must be altered.
def_size_K equ 64 ; default 64K ram disk
max_isize_K equ 512 ; maximum 512K initial ram disk size
cnv_sec_per_blk equ 64 ; 32K increments in conventional mem
max_clusters equ 4086 ; max clusters per disk
; (12-bit FAT entries)
bytes_per_sec equ 512 ; bytes per sector
def_sec_per_cl equ 1 ; default sectors per cluster
par_per_sec_lg2 equ 5 ; 16-byte paragraphs per sector
; log 2. Must match bytes_per_sec
fats_per_disk equ 1 ; number of FATS
; ---- Derived quantities ----
max_mem_blks equ (max_clusters/cnv_sec_per_blk)
; Maximum number of memory blocks
; assuming cluster size of 1
sec_per_K equ 1024/bytes_per_sec ; sectors per 1024 bytes
sec_per_K_lg2 equ 1 ; must match above parameter
par_per_sec equ bytes_per_sec/16 ; 16-byte paragraphs per sector
def_size_sec equ def_size_K*sec_per_K ; default size in sectors
bytes_per_fat equ max_clusters*3/2
sec_per_fat equ (bytes_per_fat+bytes_per_sec-1)/bytes_per_sec
sec_per_em_pag equ 16*sec_per_K ; sectors per expanded mem page
sec_per_em_pag_lg2 equ 5 ; sectors per em page log 2, must
; match above parameter
sec_per_em_pag_msk equ sec_per_em_pag-1
; ---- Static Request Header Structure Definitions ----
srh_length equ 0 ; (byte) length field
srh_unit equ 1 + srh_length ; (byte) unit field
srh_command equ 1 + srh_unit ; (byte) command code
srh_status equ 1 + srh_command ; (word) status field
srh_reserved equ 2 + srh_status ; (8 bytes)
srh_size equ 8 + srh_reserved ; request header size
; ---- Request Header Status values ----
s_done equ 0100h ; done, no errors
s_busy equ 0200h ; busy, no errors
e_err equ 8000h
e_protect equ e_err+0h ; error: write protect
e_unknown_unit equ e_err+1h ; error: unknown unit
e_not_ready equ e_err+2h ; error: not ready
e_command equ e_err+3h ; error: unknown command
e_crc equ e_err+4h ; error: bad CRC
e_bad_length equ e_err+5h ; error: bad structure length
e_seek equ e_err+6h ; error: bad seek
e_media equ e_err+7h ; error: unknown media
e_not_found equ e_err+8h ; error: sector not found
e_paper equ e_err+9h ; error: out of paper
e_write equ e_err+ah ; error: write fault
e_read equ e_err+bh ; error: read fault
e_general equ e_err+ch ; error: general error not listed above
; ---- Non-destructive read parameter block ----
rh_read_data equ srh_size ; (byte) non-destructive data
; ---- Input/output parameter block ----
rh_media equ 0 + srh_size ; (byte) media descriptor
rh_buf_offset equ 1 + rh_media ; (word) transfer buffer offset
rh_buf_segment equ 2 + rh_buf_offset ; (word) transfer buffer segment
rh_buf_size equ 2 + rh_buf_segment ; (word) transfer buffer size
rh_start equ 2 + rh_buf_size ; (word) transfer starting sector
m_fixed equ 0f8h ; media: fixed disk
m_ss9 equ 0fch ; media: single sided, 8 sectors/track
m_ds9 equ 0fdh
m_ss8 equ 0feh
m_ds8 equ 0ffh
; ---- Build BPB parameter block ----
; preceeded by media descriptor
rh_bpb equ 1 + rh_media ; (dword) bpb buffer address
rh_tbl_offset equ 4 + rh_bpb ; (dword) bpb table offset/segment
rh_tbl_segment equ 2 + rh_tbl_offset
; ---- Media Check parameter block ----
rh_check equ 1 + rh_media ; (byte) media check result
mc_changed equ -1 ; media has changed
mc_maybe equ 0 ; media may have been changed
mc_same equ 1 ; media has not changed
; ---- Initialize parameter block ----
rh_units equ 0 + srh_size ; (byte) number of units supported
rh_end_offset equ 1 + rh_units ; (word) end address of driver
rh_end_segment equ 2 + rh_end_offset
rh_bpb_offset equ 2 + rh_end_segment ; (word) BPB array address
rh_bpb_segment equ 2 + rh_bpb_offset
rh_cmd_offset equ rh_bpb_offset ; (word) "device=" cmd line
rh_cmd_segment equ rh_bpb_segment
rh_cmd equ rh_bpb_offset
; ---- DOS interrupts ----
dosi_dosf equ 21h ; DOS function dispatcher
; ---- User Interrupts ----
usri_emm equ 67h ; Expanded Memory Manager
; ---- DOS interrupt 21 functions ----
dosf_outstr equ 9 ; display string
dosf_seldisk equ 0eh ; select disk
dosf_getdisk equ 19h ; get current disk
; ---- LOTUS/INTEL/Microsoft Expanded Memory Manager functions ----
emm_status equ 40h ; get manager status
emm_get_PFseg equ 41h ; get page frame segment
emm_get_pages equ 42h ; get number of pages
emm_get_handle equ 43h ; get handle and allocate memory
emm_map_memory equ 44h ; map memory
emm_fre_handle equ 45h ; free handle and memory
emm_get_ver equ 46h ; get EMM version
emm_sav_map equ 47h ; save mapping context
emm_res_map equ 48h ; restore mapping context
emm_num_handles equ 4bh ; get number of EMM handles
emm_hdl_pages equ 4ch ; get pages owned by handle
emm_all_pages equ 4dh ; get pages for all handles
emm_pag_map equ 4eh ; get or set page map
; ---- Device Driver Header Attribute Definitions ----
a_input equ 0001h ; standard input device
a_output equ 0002h ; standard output device
a_nul equ 0004h ; NUL device
a_clock equ 0008h ; CLOCK$ device
a_ibm equ 0 ; IBM block device (bit 13)
a_not_ibm equ 2000h ; non-IBM block device (bit 13)
a_ioctl equ 4000h ; IOCTL functions supported
a_block equ 0 ; block device (bit 15)
a_character equ 8000h ; character device (bit 15)
; ---- FAT IDs ----
fid_fix equ 0f8h ; fixed disk
fid_ds15 equ 0f9h ; double-sided, 15 sector
fid_ss9 equ 0fch ; single-sided, 9 sector
fid_ds9 equ 0fdh ; double-sided, 9 sector
fid_ss8 equ 0feh ; single-sided, 8 sector
fid_ds8 equ 0ffh ; double-sided, 8 sector
fid_ss8sd equ 0feh ; single-sided, 8 inch, single dens
fid_ss8sd_alt equ 0fdh ; alternate sssd 8 inch
fid_ds8dd equ 0feh ; double-side, 8 inch, double-dens
; ---- Character Codes ----
cc_ht equ 9 ; TAB
cc_cr equ 13
cc_lf equ 10
cc_sp equ ' '
cc_bel equ 7
; ==== Device Driver Header Definition ====
cseg segment para public 'CODE'
driver proc far
assume cs:cseg,ds:cseg,es:cseg
dd -1 ; last driver in chain
dw a_block + a_not_ibm ; driver attribute
dw dev_strategy ; offset to strategy routine
dw dev_interrupt ; offset to interrupt routine
db 1 ; number of devices or device name
db 7 dup ( ? ) ; filler for block device
; ==== Device Driver Tables ====
;
; ---- BIOS Parameter Block Table and Entries ----
bpb_table dw bpb ; one entry for each unit
; ---- Request Header Address set by dev_strategy ----
rh_address dd 1 dup ( ? ) ; request header base address
rh_offset equ word ptr rh_address
rh_segment equ word ptr rh_address + 2
; ---- Request Header Command Dispatch Table ----
cmd_table dw initialize ; initialize driver
dw media_check ; media check
dw build_bpb ; build BPB
dw ioctl_read ; IOCTL read
dw read ; normal read
dw check_input ; non-destructive read/status
dw input_status ; input status
dw input_flush ; flush input buffers
dw write ; normal write
dw write_verify ; normal write with read verify
dw output_status ; output status
dw output_flush ; flush output buffer
dw ioctl_write ; IOCTL write
; ==== Common Device Driver Routines ====
;
; ---- Device Driver Strategy Routine ----
;
; es:bs == request header address
dev_strategy proc far
mov cs:rh_offset,bx
mov cs:rh_segment,es
ret
dev_strategy endp
; ---- Device Driver Interrupt Routine ----
dev_interrupt proc far
push ax ; save registers used
push bx
push cx
push dx
push di
push si
push ds
push es
cld ; clear direction flag
push cs ; setup small memory model
pop ds ; ds := program segment
les bx,rh_address ; es:bx := request header index
mov si,es:srh_command[bx] ; si := request command (byte)
and si,0ffh ; si := request command
add si,si ; si := word table offset
call word ptr cmd_table[si] ; ax := command result
lds bx,cs:rh_address ; ds:bx := request header index
mov srh_status[bx],ax ; update request status
pop es ; restore register
pop ds
pop si
pop di
pop dx
pop cx
pop bx
pop ax
ret
dev_interrupt endp
; **** END OF DEVICE INDEPENDENT PORTION OF DRIVER ****
; ---- Memory Block Table that follows BPB in boot sector ----
mem_blk_table_entry struc
typ db ? ; 0 = normal 1 = EM 2 = extended
par dw ? ; paragraph address of block
siz dw ? ; number of sectors in the memory block
hdl dw ? ; EM handle for the block
mem_blk_table_entry ends
nor_flg equ 0 ; Memory Block is in normal memory
em_flg equ 1 ; Memory Block is in expanded memory
rm_flg equ 2 ; Memory Block is in reserved memory
; ---- Flag indicating whether expanded memory mapping context has been
; saved yet or not. If nonzero, it indicates that the context has
; been saved.
em_context_flg db 0 ; 0 = not saved; 1 = context saved
em_context_hdl dw ? ; EM handle underwhich context saved
; ==== Memory Disk Device Driver Code ====
;
; ---- Driver support functions (near functions) ----
;
; es:bx == request header
; ds == cs
; All other registers are usable.
;
; ax := result status
;
; ---- Initialize driver ----
initialize proc near ; initialize driver
lea ax,word ptr mdisk_data ; ax := end of driver
mov cl,4 ; cl := paragraph size log2
shr ax,cl ; ax := paragraphs in driver
mov dx,cs ; dx := driver segment
add ax,dx ; ax := memory disk segment
mov mem_blk_table.par,ax ; update for subsequent transfers
call parse_size ; Parse MINSIZE= clause
call parse_cluster ; Parse CLUSTER= clause
mov ax,cs
mov es,ax
call enter_vol_label ; Put volume label in directory
lea di,word ptr fat1 ; es:di := first FAT
mov cx,sec_per_fat*bytes_per_sec ; cx = size
push di ; save first FAT address
push es
mov al,fid_ds9 ; say "dbl-sided, 9 sector"
mov [di],al ; update FAT media byte
mov word ptr 1[di],0ffffh ; allocate initial sectors
add di,3 ; adjust FAT index
sub cx,3 ; adjust FAT size
xor al,al ; al := 0
rep stosb ; clear rest of FAT
pop ds
pop si ; ds:si := first FAT index
if fats_per_disk - 1 ; assemble if 2 FATS
lea di,word ptr fat2 ; es:di := second FAT
mov cx,sec_per_fat*bytes_per_sec ; cx := size
rep movsb ; copy first FAT to second FAT
endif
add di,32 ; skip over volume label
mov ax,cs:bpb_root ; ax := number of directory entries
dec ax ; less volume label
mov cl,5 ; cl := size of entry log2 (32 bytes)
shl ax,cl ; ax := directory size
mov cx,ax ; cx := directory size
xor al,al ; al := 0
rep stosb ; zero directory
lds bx,cs:rh_address ; ds:bx := request header address
mov byte ptr rh_units[bx],1 ; return number of units
mov word ptr rh_end_offset[bx],offset mdisk_data
mov rh_end_segment[bx],cs ; Calc end address of disk
mov ax,cs:mem_blk_table.siz
mov cl,par_per_sec_lg2
shl ax,cl
add rh_end_segment[bx],ax ; return ending address of driver
mov word ptr rh_bpb_offset[bx],offset bpb_table
mov rh_bpb_segment[bx],cs ; return BPB table address
push cs
pop ds
mov ah,dosf_getdisk ; get current default disk
int dosi_dosf
mov dl,al ; select it
mov ah,dosf_seldisk
int dosi_dosf ; DL := number of drives
add al,'A' ; convert to letter
mov drive,al ; store into message
mov dx,offset initok
mov ah,dosf_outstr
int dosi_dosf
mov ax,s_done ; ax := done, no errors
ret
initok db cc_cr,cc_lf,'AMDISK v4.1 '
db '(c) Copyright 1986, 1987, 1988 by Gary Cramblitt'
db cc_cr,cc_lf
db ' -- Initialized as disk '
drive db ' '
db ':',cc_cr,cc_lf,'$'
initialize endp
; ---- Media check ----
;
; Memory disk is non-removable media, however, the size of the media
; can be changed. We check the number of memory blocks allocated,
; which is located in the boot record. If it has changed, then the media
; has changed.
media_check proc near ; media check
lds bx,cs:rh_address ; ds:bx := request header address
mov al,cs:bpb_media ; Get our media byte
cmp al,rh_media[bx] ; Changed from what DOS has?
jz media_same ; -- Yes
mov byte ptr rh_check[bx],mc_changed
mov ax,s_done
ret
media_same:
mov byte ptr rh_check[bx],mc_same
mov ax,s_done ; ax := function done, no errors
ret
media_check endp
; ---- Build BPB ----
;
; Read boot sector and copy into BPB buffer
; Adjust request header values
build_bpb proc near ; build BPB
lds bx,cs:rh_address ; ds:bx := request header address
mov word ptr rh_tbl_offset[bx],offset bpb
mov word ptr rh_tbl_segment[bx],cs
mov ax,s_done ; ax := function done, no errors
ret
build_bpb endp
; ---- Read from device ----
read proc near ; normal read
push es
pop ds ; ds := request header segment
mov di,rh_buf_offset[bx]
mov ax,di
and di,000fh ; di := buffer offset within paragraph
mov cl,4
shr ax,cl
add ax,rh_buf_segment[bx] ; ax := paragraph of buffer
mov es,ax ; es:di := normalized buffer address
mov dx,rh_buf_size[bx] ; dx := sectors to read
mov ax,rh_start[bx] ; ax := first sector to read
mov bx,-(size mem_blk_table_entry) ; find the memory block which..
or dx,dx ; All sectors transfered?
jnz find_mem_blk_2_rd ; -- No
jmp rd_finish_2 ; -- Yes, squirrelly request!
find_mem_blk_2_rd: ; contains the desired sector..
add bx,size mem_blk_table ; by subtracting the # of sectors..
sub ax,cs:mem_blk_table.siz[bx] ; in each block.
jnc find_mem_blk_2_rd
add ax,cs:mem_blk_table.siz[bx] ; bx := ptr to memory block table
; ax := sector within the block
cmp cs:mem_blk_table.typ[bx],em_flg; Expanded Memory?
jnz rd_1st_cnv_blk
call save_em_context ; save expanded mem context
call get_em_PF_seg ; get em page frame segment
push ax ; save sector within block
and ax,sec_per_em_pag_msk ; ax := sec within em page
push ax ; save sec within em page
mov cl,par_per_sec_lg2 ; paragraphs per sector log 2
shl ax,cl ; ax := paragraph within em page
add ax,cs:mem_blk_table.par[bx] ; ax := paragraph in phy mem
mov ds,ax
xor si,si ; ds:si := address of 1st sector
mov cx,sec_per_em_pag
pop ax ; get sector within em page
sub cx,ax ; cx := secs remaining in em page
pop ax ; get sector within block
push cx ; save secs remaining in em page
sub ax,cs:mem_blk_table.siz[bx]
neg ax ; ax := sectors remaining in block
dec ax ; relative to 0
mov cl,sec_per_em_pag_lg2 ; adjust to em pages
shr ax,cl
inc ax ; ax := em pages remaining in blk
pop cx ; get sectors remaining in em page
call map_em_pag ; map EM to log mem
jmp rd_one_pag
rd_1st_cnv_blk:
push ax ; save sector within block
mov cl,par_per_sec_lg2 ; paragraphs per sector log 2
shl ax,cl ; ax := paragraph within memory block
add ax,cs:mem_blk_table.par[bx] ; ax := paragraph in phy mem
mov ds,ax
xor si,si ; ds:si := address of sector
mov cx,cs:mem_blk_table.siz[bx] ; cx := end sector
pop ax ; ax := start sector
sub cx,ax ; cx := secs remaining in mem blk
mov ax,1 ; ax := pages remaining to be read
rd_one_pag:
push ax ; save pages remaining count
rd_one_sector:
push cx ; save sector count for this blk
mov cx,bytes_per_sec ; 512 bytes per sector
rep movsb ; copy sector to buffer
pop cx ; cx := secs remaining in this blk+1
mov ax,ds
add ax,par_per_sec ; 32 paragraphs per sector
mov ds,ax
xor si,si ; ds:si := address of next sector
mov ax,es ; advance es to next sector
add ax,par_per_sec
mov es,ax
and di,000fh ; es:di := normalized transfer address
dec dx ; decrement sector count
jz rd_finish ; all done?
loop rd_one_sector ; if not end of mem blk, do another
pop ax ; ax := number of em pages left
dec ax ; all pages read?
jnz rd_next_em_pag ; -- no, then read next em page
rd_next_mem_blk:
add bx,size mem_blk_table_entry
cmp cs:mem_blk_table.typ[bx],em_flg
jnz rd_next_cnv_mem_blk
call save_em_context
call get_em_PF_seg
mov ax,cs:mem_blk_table.siz[bx] ; ax := sectors in mem block
mov cl,sec_per_em_pag_lg2 ; adjust to em pages in mem block
shr ax,cl
rd_next_em_pag:
call map_em_pag ; Map EM to logical mem
mov ds,cs:mem_blk_table.par[bx]
mov cx,sec_per_em_pag
jmp rd_one_pag
rd_next_cnv_mem_blk:
call restore_em_context
mov ax,1 ; ax := pages remaining
mov ds,cs:mem_blk_table.par[bx] ; ds:si := start of new mem blk
mov cx,cs:mem_blk_table.siz[bx] ; cx := size of blk in sectors
jmp rd_one_pag
rd_finish:
pop ax ; Clear page remaining count
call restore_em_context ; If needed, restore em context
rd_finish_2:
mov ax,s_done ; ax := transfer done
ret
read endp
; ---- Write to device ----
write proc near ; normal write
push es
pop ds ; ds := request header segment
mov dx,rh_buf_size[bx] ; dx := sectors to write
mov si,rh_buf_offset[bx]
mov ax,si
and si,000fh ; si := buffer offset within paragraph
mov cl,4
shr ax,cl
add ax,rh_buf_segment[bx] ; ax := paragraph of buffer
mov cx,ax
mov ax,rh_start[bx] ; ax := first sector to write
mov ds,cx ; ds:si := normalized buffer address
mov bx,-(size mem_blk_table_entry)
or dx,dx ; transfer already done?
jnz find_mem_blk_2_wrt ; -- no, of course not
jmp wrt_finish_2 ; -- yes, squirrelly request!
; find the memory block which...
find_mem_blk_2_wrt: ; contains the desired sector..
add bx,size mem_blk_table_entry ; by subtracting the # of sectors..
sub ax,cs:mem_blk_table.siz[bx] ; in each block.
jnc find_mem_blk_2_wrt
add ax,cs:mem_blk_table.siz[bx]
; bx := ptr to memory block table
; ax := sector within the block
cmp cs:mem_blk_table.typ[bx],em_flg; Expanded Memory?
jnz wrt_1st_cnv_blk
call save_em_context ; save expanded mem context
call get_em_PF_seg ; get em page frame segment
push ax ; save sector within block
and ax,sec_per_em_pag_msk ; ax := sec within em page
push ax ; save sec within em page
mov cl,par_per_sec_lg2 ; paragraphs per sector log 2
shl ax,cl ; ax := paragraph within em page
add ax,cs:mem_blk_table.par[bx] ; ax := paragraph in phy mem
mov es,ax
xor di,di ; es:di := address of 1st sector
mov cx,sec_per_em_pag
pop ax ; get sector within em page
sub cx,ax ; cx := secs remaining in em page
pop ax ; get sector within block
push cx ; save secs remaining in em page
sub ax,cs:mem_blk_table.siz[bx]
neg ax ; ax := sectors remaining in block
dec ax
mov cl,sec_per_em_pag_lg2 ; adjust to em pages
shr ax,cl
inc ax ; ax := em pages remaining in blk
pop cx ; get sectors remaining in em page
call map_em_pag ; map EM to log mem
jmp wrt_one_pag
wrt_1st_cnv_blk:
push ax ; save sector within block
mov cl,par_per_sec_lg2 ; paragraphs per sector log 2
shl ax,cl ; ax := paragraph within memory block
add ax,cs:mem_blk_table.par[bx] ; ax := paragraph in phy mem
mov es,ax
xor di,di ; es:di := address of sector
mov cx,cs:mem_blk_table.siz[bx] ; cx := end sector
pop ax ; ax := start sector
sub cx,ax ; cx := secs remaining in mem blk
mov ax,1 ; ax := # of pages remaining
wrt_one_pag:
push ax ; save pages remaining count
wrt_one_sector:
push cx ; save sector count for this blk
mov cx,bytes_per_sec ; 512 bytes per sector
rep movsb ; copy buffer to sector
pop cx ; cx := secs remaining in this blk+1
mov ax,es
add ax,par_per_sec ; 32 paragraphs per sector
mov es,ax
xor di,di ; es:di := address of next sector
mov ax,ds ; advance ds to next sector
add ax,par_per_sec
mov ds,ax
and si,000fh ; ds:si := normalized transfer address
dec dx ; decrement sector count
jz wrt_finish ; all done?
loop wrt_one_sector ; if not end of mem blk, do another
pop ax ; ax := number of em pages left
dec ax ; all memory for this em handle gone?
jnz wrt_next_em_pag
wrt_next_mem_blk:
add bx,size mem_blk_table_entry
cmp cs:mem_blk_table.typ[bx],em_flg
jnz wrt_next_cnv_mem_blk
call save_em_context
call get_em_PF_seg
mov ax,cs:mem_blk_table.siz[bx] ; ax := sectors in mem block
mov cl,sec_per_em_pag_lg2 ; adjust to em pages in mem block
shr ax,cl
wrt_next_em_pag:
call map_em_pag ; Map EM to logical mem
mov es,cs:mem_blk_table.par[bx]
mov cx,sec_per_em_pag
jmp wrt_one_pag
wrt_next_cnv_mem_blk:
call restore_em_context
mov ax,1 ; ax := pages remaining
mov es,cs:mem_blk_table.par[bx] ; es:di := start of new mem blk
mov cx,cs:mem_blk_table.siz[bx] ; cx := size of blk in sectors
jmp wrt_one_pag
wrt_finish:
pop ax ; Clear page remaining count
call restore_em_context ; If needed, restore em context
wrt_finish_2:
mov ax,s_done ; ax := transfer done
ret
write endp
; ---- Write and verify ----
write_verify proc near ; normal write with read verify
jmp write ; assume no errors
write_verify endp
; ==== Support Functions ====
;
; ---- Map a handle of Expanded Memory to a 16K logical page
; ENTRY: BX = pointer to Memory Block Table entry (in CS)
; AX = logical page number to map to (first page is 1)
; EXIT: Memory is mapped and paragraph address updated in entry.
; USES: Flags
map_em_pag proc near
push ax
push bx
push dx
mov dx,cs:mem_blk_table.hdl[bx] ; get handle of mem blk
mov bx,ax ; logical page number
dec bx ; relative to 0
mov ah,emm_map_memory
mov al,0 ; physical page number
int usri_emm
pop dx
pop bx
pop ax
ret
map_em_pag endp
; ---- Get Expanded Memory Page Frame Segment ----
; ENTRY: BX = pointer to Memory Block Table entry into which
; PF segment is to be stored.
; USES: Flags
get_em_PF_seg proc near
push ax
push bx
mov ah,emm_get_PFseg ; get page frame segment
int usri_emm
mov ax,bx
pop bx
mov cs:mem_blk_table.par[bx],ax ; store page frame segment
pop ax
ret
get_em_PF_seg endp
; ---- Save Expanded Memory Context ----
; Saves the Expanded Memory context. Context is saved under given
; handle. Flag at "em_context_flg" is set accordingly.
; ENTRY: BX = pointer to Memory Block Table entry containing
; the handle underwhich to save context.
; USES: Flags
save_em_context proc near
cmp cs:em_context_flg,0 ; em context saved yet?
jnz save_em_context_xit ; -- yes, nothing to do
push ax
push dx
mov dx,cs:mem_blk_table.hdl[bx] ; get handle of mem blk
mov cs:em_context_hdl,dx ; store handle underwhich saved
mov ah,emm_sav_map
int usri_emm
mov cs:em_context_flg,1 ; flag as saved
pop dx
pop ax
save_em_context_xit:
ret
save_em_context endp
; ---- Restore expanded memory context, if needed. ---
; USES: Flags
restore_em_context proc near
cmp cs:em_context_flg,0 ; was context saved?
jz restore_em_context_xit ; - No, then just exit
push ax
push dx
mov ah,emm_res_map ; - Yes, then restore it..
mov dx,cs:em_context_hdl ; ..using this handle
int usri_emm
mov cs:em_context_flg,0 ; clear context flag
pop dx
pop ax
restore_em_context_xit:
ret
restore_em_context endp
; ---- Unimplemented functions ----
unimplemented proc near
mov ax,e_command
ret
unimplemented endp
ioctl_read equ unimplemented ; IOCTL read
check_input equ unimplemented ; non-destructive read/status
input_status equ unimplemented ; input status
input_flush equ unimplemented ; flush input buffers
output_status equ unimplemented ; output status
output_flush equ unimplemented ; flush output buffers
ioctl_write equ unimplemented ; IOCTL write
; ==== Memory Disk Data Area ====
;
; Align to paragraph boundary for easy computation of the sector address
if (( $ - driver) mod 16 )
org ( $ - driver) + (16 - (( $- driver) mod 16 ))
endif
mdisk_data equ $ ; memory disk starts here
; ---- Boot Sector ----
boot_record db 3 dup ( 0 ) ; non-bootable (no jump instruction )
db 'AMDISK4 ' ; identification
bpb: ; BIOS Parameter Block
bytes_in_sector dw bytes_per_sec ; bytes/sector
bpb_sec_per_cl db def_sec_per_cl ; sectors/cluster
bpb_reserved dw 1 ; reserved sectors
bpb_fats db fats_per_disk ; number of FAT's
bpb_root dw 128 ; directory entries in root
bpb_total dw def_size_sec ; total number of sectors
bpb_media db 1 ; media dexcriptor byte ...
mem_blk_cnt equ bpb_media ; ...is used for number of mem blocks
bpb_fat_size dw sec_per_fat ; sectors/FAT
dw 1 ; sectors/track
dw 1 ; number of heads
dw 0 ; hidden sectors
mem_blk_table mem_blk_table_entry <nor_flg,,def_size_sec,0> ; memory block table
end_mem_blk_table equ $+(size mem_blk_table)*(max_mem_blks)
; ---- NOTE: From here on down, code is overwritten during initialization
fat1 equ boot_record + bytes_per_sec
fat2 equ fat1 + (bytes_per_sec*sec_per_fat)
root_dir equ fat1+(fats_per_disk*sec_per_fat*bytes_per_sec)
first_data_sec equ root_dir + (128*32)
min_cluster_siz equ 1
max_cluster_siz equ 8
init_sectors equ (first_data_sec - boot_record)/bytes_per_sec
min_size_sec equ init_sectors + max_cluster_siz ; Must be 1 data cluster
min_size_K equ (min_size_sec + sec_per_K - 1)/sec_per_K
size_string db 7,'MINSIZE'
count dw ?
delims db cc_sp,cc_ht,cc_cr
num_delims = offset $ - offset delims
size_error db cc_cr,cc_lf,cc_bel
db 'Illegal MINSIZE= clause in CONFIG.SYS for '
db 'device AMDISK'
db cc_cr,cc_lf,'Defaulting to 64K',cc_cr,cc_lf,'$'
cluster_string db 7,'CLUSTER'
cluster_error db cc_cr,cc_lf,cc_bel
db 'Illegal CLUSTER= clause in CONGIG.SYS for '
db 'device AMDISK'
db cc_cr,cc_lf,'Defaulting to 1',cc_cr,cc_lf,'$'
; ---- Skip over white space in a string.
; ENTRY: ES:DI ==> string
; EXIT: ES:DI ==> first non-white character
skip_white proc near
cmp byte ptr es:[di],' '
jz skip_white_1
cmp byte ptr es:[di],cc_ht
jz skip_white_1
ret
skip_white_1:
inc di
jmp skip_white
skip_white endp
; ---- MATCH ----
; Scans a string to locate a second string
; ENTRY: DS:SI ==> string to locate. The first byte of this string
; is the length of the string
; ES:DI ==> string to search
; EXIT: zero flag set if string found
; ES:DI ==> char after match string
; zero flag unset if string not found
; ES:DI unchanged
; USES: none
match proc near
push ax
push es
push di
; Prepare for search
lodsb ; Get count and save it
sub ah,ah
mov count,ax
mov al,ds:[si] ; Get first char to match
; Try to match the first character in the search string
match1:
cmp byte ptr es:[di],cc_cr ; At end of string?
jz match4 ; -- Yes, exit
cmp al,es:[di] ; -- No, does first char match?
jz match2 ; -- Yes, try to match string
inc di ; -- No, try next character
jmp match1
; First character matches, try to match string
match2:
push si ; Save old pointers in case no match
push di
mov cx,count
repz cmpsb ; Check if strings match
jz match3 ; -- Yes, exit
pop di ; -- No, restore pointers
pop si
inc di ; Bump past match character
jmp match1 ; Try to match first character again
; String was matched, clean up and set 'Z' flag
match3:
add sp,8 ; Get rid of garbage on the stack
sub ax,ax ; Ensure Zero flag set
pop ax
ret
; String was not matched, restore registers and clear 'Z' flag
match4:
sub al,al
inc al
pop di
pop es
pop ax
ret
match endp
; GET_DECIMAL - Get a decimal number
;
; ENTRY: ES:DI pointer to string
;
; EXIT: AX - Number (errors return as 0)
;
get_decimal proc near
push cx
mov cx,10
gd0: push bx
push dx
sub bx,bx ; BX will hold number
; Get character and turn it into a digit
gd1:
mov al,es:[di] ; Get a character
call delim ; Check if delimiter
jz gd3 ; -- Yes, exit
sub al,'0' ; Make it a number
js gd2 ; Error if below 0
cmp al,cl ; Check if it is above the maximum digit
jae gd2 ; Error, invalid character
inc di ; Character good, advance pointer
; Update number
xchg ax,bx ; Prepare for shifting number
sub dx,dx
mul cx
sub bh,bh ; Add new digit to old number
add bx,ax
adc dx,0 ; Check for overflow
jz gd1 ; No, return for more digits
; Error, not a valid number. Return 0
gd2:
sub bx,bx ; Yes, return 0
gd3:
mov ax,bx ; Return value in AX
pop dx
pop bx
pop cx
ret
get_decimal endp
; DELIM - Check for delimiter character
;
; ENTRY: ES:DI points to character to check
;
; EXIT: 'Z' set, char was delimiter
; 'Z' clear, char was not a delimiter
;
delim proc near
push cx
push di
push es
push cs
pop es
mov di,offset delims
mov cx,num_delims
repnz scasb
pop es
pop di
pop cx
ret
delim endp
; ---- Look for and parse MINSIZE= option from DEVICE= line in CONFIG.SYS.
; The resulting size in sectors is placed at locations
; "mem_blk_table.siz" and at "bpb_total".
;
; ENTRY: ES:BX ==> DOS request packet
; DS = CS
; USES:
parse_size proc near
push es
push bx
cld
les di,es:[bx+rh_cmd]
call skip_white
mov si,offset size_string
call match
jnz parse_size_exit
call skip_white ; Skip white space
cmp byte ptr es:[di],'=' ; Make sure an equals sign is there
jnz ds2 ; No, error do no size
inc di ; Skip over =
call skip_white ; Skip white
call get_decimal ; Get a decimal number
; Ensure correct AMDISK size
cmp ax,min_size_K ; Is disk less than minimum
jb ds2 ; Yes, Notify user
ds1:
cmp ax,max_isize_K ; No, is disk greater than max.
jbe ds3 ; No, skip
; Invalid MDISK size, notify user.
ds2:
mov dx,offset size_error
mov ah,dosf_outstr
int dosi_dosf
jmp parse_size_exit
; Convert size to sectors and store it.
ds3:
mov cl,sec_per_K_lg2
shl ax,cl
mov mem_blk_table.siz,ax
mov bpb_total,ax
parse_size_exit:
pop bx
pop es
ret
parse_size endp
; ---- Look for and parse CLUSTER= option from DEVICE= line in CONFIG.SYS.
; The resulting sectors per cluster is placed at location
; bpb_sec_per_cl
;
; ENTRY: ES:BX ==> DOS request packet
; DS = CS
; USES:
parse_cluster proc near
push es
push bx
cld
les di,es:[bx+rh_cmd]
call skip_white
mov si,offset cluster_string
call match
jnz parse_cluster_exit
call skip_white ; Skip white space
cmp byte ptr es:[di],'=' ; Make sure an equals sign is there
jnz pc3 ; No, error do no cluster
inc di ; Skip over =
call skip_white ; Skip white
call get_decimal ; Get a decimal number
; Ensure correct AMDISK cluster size
cmp al,min_cluster_siz ; Is disk less than minimum
jb pc3 ; Yes, notify user
pc1:
cmp al,max_cluster_siz ; No, is disk greater than max.
ja pc3 ; Yes, notify user
mov bl,al
pc2:
sal bl,1 ; If power of 2, then...
jnc pc2 ; when a bit is shifted out..
and bl,bl ; the rest is all 0s
jz pc4
; Invalid MDISK cluster size, notify user.
pc3:
mov dx,offset cluster_error
mov ah,dosf_outstr
int dosi_dosf
jmp parse_cluster_exit
; Store cluster size in BPB.
pc4:
mov bpb_sec_per_cl,al
parse_cluster_exit:
pop bx
pop es
ret
parse_cluster endp
; ---- Store volume label into directory ----
;
; ENTRY: ES = CS
; USES: SI,DI,DS,CX
volume_label db 'ADJRAM v4.1'
db 00001000b ; attr = volume label
db 10 dup (0)
dw 0110000000000000b ; time = 12:00:00
; hhhhhmmmmmmsssss
dw 0001000010101111b ; date = 15May88
; yyyyyyymmmmddddd yr=yyyyyyy+1980
dw 0 ; start cluster
db 4 dup (0) ; size
enter_vol_label proc near
push es
pop ds
lea si,volume_label ; ds:si ==> volume label
lea di,root_dir ; es:di ==> directory
mov cx,32
rep movsb
ret
enter_vol_label endp
; ==== End of Memory Disk Device Driver ====
driver endp
cseg ends
end