📄 atoms.asm
字号:
;----------------------------------------------------------------------------;
; ATOMS.ASM: A simple installable character device driver
;----------------------------------------------------------------------------;
; based on skeleton in The MS-DOS Encyclopedia, (c) 1988 Microsoft Press
;----------------------------------------------------------------------------;
include dos.inc ; macros to @ShowStr, etc...
.model tiny, c
.386
;----------------------------------------------------------------------------;
; Typedefs & Prototypes
;----------------------------------------------------------------------------;
NPBYTE TYPEDEF NEAR PTR BYTE ; near pointer to a byte
FPTR TYPEDEF FAR PTR ; far pointer
NPTR TYPEDEF NEAR PTR ; near pointer
SEGPTR TYPEDEF WORD ; segment selector
DEVLINK TYPEDEF SDWORD ; link to other devices
ATRIBUT TYPEDEF WORD ; device attribute word
st_len PROTO NEAR, :NPBYTE, :SEGPTR ; gets the length of string into ax
int2hex PROTO NEAR, :WORD, :NPBYTE ; converts integers to hexadecimal
scan PROTO NEAR, :NPBYTE, :SEGPTR ; scans the command line
;----------------------------------------------------------------------------;
; Flags & Numeric Equates
;----------------------------------------------------------------------------;
MaxCmd EQU 24t ; max command code for DOS 3.2+
cr EQU 0Dh ; carriage return
lf EQU 0Ah ; linefeed
eom EQU '$' ; end-of-message signal
space EQU 20h ; ascii space
tab EQU 09h ; ascii tab
; definition of a Request Header structure so that we can access its elements.
; this structure is not exhaustive at all: block device use some addresses for
; different purposes, and have data beyond the cmd_seg word.
REQ_HEADER STRUCT 1t
lengt BYTE ? ; request header length
unit BYTE ? ; unit number (for Block Devs. only)
code BYTE ? ; command code
stat WORD ? ; status word
reserv QWORD ? ; reserved for DOS us
media BYTE ? ; Media ID (for Block Devs. only)
xfer NPTR ? ; offset of data buffer
xseg SEGPTR ? ; segment of data buffer
UNION
xcount WORD ? ; count of bytes in request, or
cmd_off NPTR ? ; offset of CONFIG.SYS line
ENDS
cmd_seg SEGPTR ? ; segment of CONFIG.SYS line
REQ_HEADER ENDS
; values for the different 'magic numbers' used with Device Drivers
fNEXTLINK EQU -1 ; word to tell DOS to substitute by address of
; next device
fCHARDEVICE EQU 8000h ; bit to define device as a character device
fOPENCLOSE EQU 0040h ; bit to indicate that device supports open/close
fDONE EQU 0100h ; Status Done bit meaning device is done
fERROR EQU 8000h ; Status Error bit meaning error on operation
fWRITE_E EQU 000Ah ; Write Fault Error bit
fUNKNOWN_E EQU 0003h ; Unknown Command Error bit
; values for the search flags used by Write Routine
fDELETE EQU 2
fINSERT EQU 4
fSEARCH EQU 8
; value for buffer (atom memory) limits
BUFMINIMUM EQU 00400h ; the minimum will be 1024 bytes
BUFMAXIMUM EQU 07FFFh ; the maximum will be 32K. It can be less than that,
; but not greater, because of the signed index for
; scasb operations.
;----------------------------------------------------------------------------;
; Code Segment
;----------------------------------------------------------------------------;
.code
org 0 ; drivers should start at address 0000h
; this will cause a linker warning - ignore it.
Header: ; device driver header
DEVLINK fNEXTLINK ; link to next device driver
ATRIBUT fCHARDEVICE+fOPENCLOSE ; device attribute word:
; char.device+open/close
NPTR Strat ; 'Strat' entry point
NPTR Intr ; 'Intr' entry point
BYTE 'ATMS ' ; logical device name (needs to be 8 chars)
;----------------------------------------------------------------------------;
; data variables
;----------------------------------------------------------------------------;
null BYTE 0 ; dummy to do a quick erase of pAtomValue
pAtomVal NPBYTE null ; pointer to value of result of atom search
MemEnd NPBYTE AtomList ; end of used memory: initially AtomList
MaxMem NPBYTE AtomList ; end of available memory: initially AtomList
lpHeader FPTR 0 ; far pointer to request header
;----------------------------------------------------------------------------;
; Dispatch table for the interrupt routine command codes
;----------------------------------------------------------------------------;
Dispatch:
NPTR Init ; 0 = init driver
NPTR Error ; 1 = Media Check (block devices only)
NPTR Error ; 2 = build BPB (block devices only)
NPTR Error ; 3 = I/O control read (not supported)
NPTR Read ; 4 = read (input) from device (int 21h, 3Fh)
NPTR Error ; 5 = nondestructive read (not supported)
NPTR Success ; 6 = ret input status (int 21h, 4406h)
NPTR Error ; 7 = flush device input buffer (not supportd)
NPTR Write ; 8 = write (output) to device (int 21h, 40h)
NPTR Write ; 9 = write with verify (== write) (21h, 40h)
NPTR OutStat ; 10 = ret output status (int 21h, 4407h)
NPTR Error ; 11 = flush output buffer (not supported)
NPTR Error ; 12 = I/O control write (not supported)
NPTR Success ; 13 = device open (int 21h, 3Dh)
NPTR Success ; 14 = device close (int 21h, 3Eh)
NPTR Error ; 15 = removable media (block devices only)
NPTR Error ; 16 = Output until Busy (mostly for spooler)
NPTR Error ; 17 = not used
NPTR Error ; 18 = not used
NPTR Error ; 19 = generic IOCTL (not supported)
NPTR Error ; 20 = not used
NPTR Error ; 21 = not used
NPTR Error ; 22 = not used
NPTR Error ; 23 = get logical device (block devices only)
NPTR Error ; 24 = set logical device (block devices only)
;----------------------------------------------------------------------------;
; Strategy Routine
;----------------------------------------------------------------------------;
; device driver Strategy routine, called by MS-DOS kernel with
; ES:BX = address of request header
;----------------------------------------------------------------------------;
Strat PROC FAR
mov word ptr cs:[lpHeader], bx ; save the address of the
mov word ptr cs:[lpHeader+2], es ; request into 'lpHeader', and
ret ; back to MS-DOS kernel
Strat ENDP
;----------------------------------------------------------------------------;
; Intr
;----------------------------------------------------------------------------;
; Device driver interrupt routine, called by MS-DOS kernel after call to
; Strategy routine
; This routine basically calls the appropiate driver routine to handle the
; requested function.
; Routines called by Intr expect:
; ES:DI will have the address of the request header
; DS will be set to cs
; These routines should only affect ax, saving es,di,ds at least
;
; Input: NONE Output: NONE -- data is transferred through request header
;
;----------------------------------------------------------------------------;
Intr PROC FAR
pusha ; save registers
pushf ; save flags
cld ; direction flag: go from low to high address
mov si, cs ; make local data addressable
mov ds, si ; by setting ds = cs
les di, [lpHeader] ; ES:DI = address of req.header
ASSUME di:PTR REQ_HEADER ; to be able to use the REQ_HEADER offsets
xor ebx, ebx ; erase ebx
mov bl,es:[di].code ; get EBX = command code (from req.header)
.IF (bx > MaxCmd) ; check to make sure we have a valid
call Error ; command code
.ELSE ; else, call command-code routine,
call NPTR PTR Dispatch[2*ebx] ; indexed from Dispatch table
.ENDIF ; (Ebx used to allow scaling factors)
or ax, fDONE ; merge Done bit into status and
mov es:[di].stat,ax ; store status into request header
ASSUME di:NOTHING ; di will be something else now
popf ; restore registers
popa ; restore flags
ret ; return to MS-DOS kernel
Intr ENDP
; Command-code routines are called by the Interrupt routine via the dispatch
; table. Each routine should return AX = 0000H if function was completed
; successfully or AX = 8000h + error code if function failed.
;----------------------------------------------------------------------------;
; function 4 = read (input) from device
;----------------------------------------------------------------------------;
; what the function basically does is transfer from the string that pAtomVal
; points, to the buffer pointed to in the request header. the values have
; to be loaded into ES:DI and DS:SI for the movsb instruction, and other
; checks are made to account for differences in sizes ;
;----------------------------------------------------------------------------;
Read PROC NEAR USES ds es di
lds si, [lpHeader] ; put request header address in DS:SI
ASSUME si:PTR REQ_HEADER ; to use the REQ_HEADER offsets
mov cx, [si].xcount ; load cx with the size of buffer
mov es, [si].xseg ; load es with segment of buffer
mov di, [si].xfer ; load di with offset of buffer
ASSUME si:NOTHING ; si will be something else now
mov si, cs ; load ds with cs again
mov ds, si
mov si, pAtomVal ; load si with the string's address
INVOKE st_len, si, ds ; load length of pAtomVal into ax
.IF (cx > ax) ; if cx > ax, then the buffer has space
mov bx, ax ; to hold all the string. bx (used to
mov cx, ax ; return number of chars transferred)
rep movsb ; will hold the string length. We move
; into cx the length of the string,
; and copy the result string into the
; buffer, with the 'rep movsb' instruct
.ELSE
mov bx, cx ; otherwise, bx will be the size of the
dec cx ; the buffer. decrement cx so that we
rep movsb ; leave the last char of the buffer
xor ax,ax ; free to store a 0 to mark the end of
stosb ; the string.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -