home *** CD-ROM | disk | FTP | other *** search
- ; DEBUG EQU TRUE
-
- V286 EQU TRUE ; No XMS on 8088 PC's!
-
- ; Nifty James' Famous Expanded Memory Disk Drive
- ; (C) Copyright 1987 by Mike Blaszczak. All Rights Reserved
-
- ; Version 1.01 of 24 May 1987
- ; Version 1.10 of 25 May 1987
- ; Version 1.15 of 31 May 1987
- ; Version 1.20 of 16 Oct 1987
-
- ; Modified Terje Mathisen 20 March 90
- ; Version 1.02 of XMS-compatible NJRAMDX 14 Sep 1990
- ; Version 1.40 of 23 Jun 1991 by Mike Blaszczak
-
- ; Shareware $15 Please contribute!
-
- ; Assemble with
- ; TASM NJRAMDX
- ; (Comment out V286 EQU TRU for 8088 version.)
- ; TLINK /t NJRAMDX, NJRAMDX.SYS
-
- ; --> DEVICE DRIVER FORMAT FILE <--
-
- ; ---------------------------------------------------------------------------
-
- ; ASCII Characters
-
- bell equ 7 ; bell character
- tab equ 9 ; tab character
- lf equ 10 ; linefeed
- cr equ 13 ; carriage return
- space equ 32 ; space
- eos equ '$' ; end of DOS string
-
- ; ---------------------------------------------------------------------------
- ; I/O Ports
-
- Speak equ 061h ; speaker port
- SpeakMask equ 011111110b ; mask for speaker set bit
- SpeakToggle equ 000000010b ; toggle bit for the speaker
-
- ; ---------------------------------------------------------------------------
- ; DOS Calls
-
- ; These are DOS functions used by the driver.
-
- DisplayOut equ 002h ; call to print a single character
- PrintString equ 009h ; call to print a '$' string
- GetDOSVersion equ 030h ; call to get the DOS version #
-
- ; ---------------------------------------------------------------------------
- ; XMS Routines
-
- ; These are the XMS functions that we use. (These are specific functions
- ; of the XMS call.)
-
- X_Counts equ 08h ; determine free/total mem
- X_Alloc equ 09h ; open, allocate, obtain handle ID
- X_Version equ 00h ; get the XMS version number
- X_Move equ 0Bh ; move an XMS block
-
- ; ---------------------------------------------------------------------------
- ; Driver Equates
-
- ; This is the media descriptor byte. Since our RAM drive is not 2 sided,
- ; does not have 8 sectors per track, and is not removable, we use 0F8h.
- ; At least, that's what the IBM DTR manual says.
-
- MediaD equ 0F8h
-
- ; These are equates used by the driver. They are all status and
- ; error flags, as defined in the DOS Technical Reference Manual.
-
- ; FEDCBA9876543210 <- BIT NUMBERS
- errorflag equ 01000000000000000b ; error bit flag
- busystat equ 00000001000000000b ; busy status bit flag
- donestat equ 00000000100000000b ; done status bit flag
-
- err_writeprot equ 0 ; write protect violation
- err_badunit equ 1 ; unknown unit number
- err_notready equ 2 ; device not ready
- err_unknown equ 3 ; unknown command
- err_CRC equ 4 ; error CRC command
- err_reqlen equ 5 ; bad request length
- err_seek equ 6 ; seek failure
- err_badmedia equ 7 ; bad media
- err_badsector equ 8 ; sector not found
- err_badwrite equ 10 ; write fault
- err_badread equ 11 ; read fault
- err_general equ 12 ; general failure
-
- ; ---------------------------------------------------------------------------
- ; Structure Definitions
-
- ; The structures defined here are used to find information in the
- ; various request header formats. Of course, being structures, they
- ; don't take up space... they are used to define offsets for the
- ; addressing of the request header.
-
- rq equ es:bx ; base address used in routines
-
- ; -- Request Header (General Format)
-
- rhead struc
- rlen db ? ; length of the structure
- unitn db ? ; unit number
- command db ? ; command code
- status dw ? ; status code (returned by us)
- db 8 dup(?); reserved bytes
- rhead ends
-
-
- ; -- Request Header (INIT Command)
-
- inithead struc
- db (type rhead) dup (?)
- units db ? ; number of units
- ndadro dw ? ; ending address offset
- ndadrs dw ? ; ending address segment
- bpboff dw ? ; BPB offset pointer
- bpbseg dw ? ; BPB segment pointer
- taglet db ? ; drive tag letter
- inithead ends
-
- ; -- Request Header (Media Check)
-
- mediahead struc
- db (type rhead) dup (?)
- media db ? ; our meida descriptor byte
- change db ? ; changed media flag
- mediahead ends
-
- ; -- Request Header (Build BPB)
-
- bbpbhead struc
- db (type rhead) dup (?)
- db ? ; media descriptor byte
- baoff dw ? ; transferr buffer address offset
- baseg dw ? ; transferr buffer address segment
- dw ? ; BIOS parameter block pointer
- dw ? ; BIOS parameter block pointer
- bbpbhead ends
-
- ; -- Request Header (Read and Write)
-
- rwhead struc
- db (type rhead) dup (?)
- db ? ; media descriptor byte
- tbaoff dw ? ; transferr buffer address offset
- tbaseg dw ? ; transferr buffer address segment
- count dw ? ; sector count
- strtsec dw ? ; starting sector number
- rwhead ends
-
-
- ; With these headers defined as they are, access to the request header
- ; and command info fields is greatly simplified. By setting ES:BX to
- ; point to the request header, the information can be easily referenced
- ; by using constructs such as
-
- ; mov [rq.count],ax
- ; or
- ; mov al,[rq.command]
-
- ; Note that any part of the program can easily reference any particular
- ; command's structure, since the line
-
- ; db (type rhead) dup (?)
-
- ; makes all the command-specific structures "equivalent".
-
- ; XMS Extended Memory Move structure
-
- ExtendedMemoryMove STRUC
- NrOfBytes dw 0,0
- SourceHandle dw 0
- SourceOffset dw 0,0
- DestHandle dw 0
- DestOffset dw 0,0
- ExtendedMemoryMove ENDS
-
- ; Check to see if this is the 286 version
-
- ifdef V286
- .286
- ; if1
- %OUT Enhanced processor version
- ; endif
- ifdef PCL
- ; if1
- %OUT for the PC's Limited 286/386
- ; endif
- endif
- else
- ; if1
- %OUT Standard Version
- ; endif
- endif
-
- ; This macro is used during debugging. It prints a single character
- ; via the BIOS screen interface, and leaves the registers unchanged.
-
- ifdef DEBUG
-
- ; if1
- %OUT DEBUG Version
- ; endif
- PrintChar macro Char
- ifdef PCL
-
- push ax
- mov al,Char
- out 095h,al ; put it digit 3 of smartvu
- pop ax
-
- else
-
- push ax ; save the regs
- push bx
- push dx
- mov ah,15
- int 010h ; get the current page
- mov al,Char
- mov ah,14 ; print the character
- int 010h
-
- xor dx,dx
- mov ah,0 ; also to printer
- mov al,Char
- ; int 017h
-
- pop dx
- pop bx ;restore the regs
- pop ax
-
- endif
- endm
-
- else
- PrintChar macro Char ; if not debugging, blow it off
- endm
- endif
-
- ; ---------------------------------------------------------------------------
- ; Public declarations for SYMDEB
-
- ; These are public declarations included to allow SYMDEB to know where
- ; various lables and addresses are. They are only needed for debugging,
- ; and serve no other useful purpose.
-
- IF 1
-
- PUBLIC NextPlace
- PUBLIC Attrib, JumpTable, TopCommand, RBPoint, RBPointOff, RBPointSeg, SaveSS
- PUBLIC SaveSP, StackTop, STRATPROC, Strategy
- PUBLIC INTPROC, Interrupt, FreakOut, IOCTLInput, ReadNoWait
- PUBLIC InputStatus, InputFlush, badcommand, BigLog, MC, MediaCheck
- PUBLIC BBPB, BuildBPB, BPBArray, OurBoot, OurBPB, SecSize, SecPerCluster
- PUBLIC RDirLen, DiskSize, SecPerFAT, BootCode
- PUBLIC RSEC, Read
- PUBLIC WSEC, Write
- PUBLIC TestValues, RangeError, SPEAKERCLICK, MakeClick
- PUBLIC SpeakerFlag, LastResident
- PUBLIC EatingWhite, GotOption
- PUBLIC NoBump, NotSilence, PagesLoop, LastDigit, NotPages, NotUseAll
- PUBLIC Unrecognized, EndOfLine, BigBust, ReTry
- PUBLIC GoodCombo, WipeOut
- PUBLIC CalcDiskFree, ClickOkay, MsgOkay, InitFail, GenFail, HowMuch
- PUBLIC RqdPages, MajorVersion, OurVolume, Banner, General
- PUBLIC NoEMMThere, EMMError, Init, NoMem, TooBig, BadOption, NoClicking
- PUBLIC Installed, DriveName, InstalledB, Installed2, UsedSpace, Bin2Dec
- PUBLIC Bin2DecLoop, Bin2DecDigit, WorkAreaL, WorkAreaH
-
- ENDIF
-
- ; ---------------------------------------------------------------------------
-
- driver segment para public
- assume cs:driver,ds:driver,es:driver,ss:driver
-
- org 0 ; drivers begin at zero
- firstplace equ this byte ; this is the first byte
-
- ; ---------------------------------------------------------------------------
- ; Device Header
-
- ; This area contains the header information. It is used by DOS when loading
- ; the device driver, and it contains information used to describe the
- ; driver to the DOS environment.
-
- NextPlace dw -1,-1 ; pointer to next driver
- Attrib dw 00010000000000000b ; attribute word
- ;FEDCBA9876543210
-
- ; device is non-ibm and block mode
- ; doesn't support IOCTL, is not
- ; a network device
-
- dw offset Strategy ; the strategy entry
- dw offset Interrupt ; the interrupt entry
- db 1,'NJTMDSK' ; Nifty James/TM' Disk!
-
- ; ---------------------------------------------------------------------------
-
- ALIGN 2
-
- JumpTable label word
-
- ; This area is a "Jump Table" that is used to dispatch the code.
- ; Only the functions marked with a "*" in their comment field
- ; are actually implemented. (Since this is a block device, only
- ; some of the areas are actually used.)
-
- dw offset Init ; 0 * initialize
- dw offset MediaCheck ; 1 * media check
- dw offset BuildBPB ; 2 * build BIOS parameter block
- dw offset IOCTLInput ; 3 I/O Control (Input)
- dw offset Read ; 4 * read from device
- dw offset ReadNoWait ; 5 read from device (nondest,
- ; no wait, char only)
- dw offset InputStatus ; 6 input status
- dw offset InputFlush ; 7 flush pending input
- dw offset Write ; 8 * Write data
- dw offset Write ; 9 * Write data with Verify
- ;
- ; dw offset OutputStat ; 10 Output status
- ; dw offset OutputFlush ; 11 flush pending output
- ; dw offset IOCTLOutput ; 12 I/O Control (Output)
- ; dw offset DeviceOpen ; 13 Open Device
- ; dw offset DeviceClose ; 14 Close Device
- ; dw offset Removeable ; 15 Removable media check
- ;
- ; (The commands above 9 are all not implemented -- we don't
- ; make entries for them to optimize for space (and speed).
- ; The equate TopCommand must be set to the last used
- ; command code.)
-
- TopCommand equ 9 ; highest valid command
-
- RBPoint label dword ; Pointer to request buffer
- RBPointOff dw 0 ; offset part
- RBPointSeg dw 0 ; segment part
-
- SaveSS dw 0 ; save place for the SS register
- SaveSP dw 0 ; save place for the SP register
-
- R_EMM ExtendedMemoryMove <> ; EMM block for read requests
- W_EMM ExtendedMemoryMove <> ; EMM block for write
-
- EMM_Vector dd ?
-
- ; ---------------------------------------------------------------------------
- ; The local stack
-
- even ; make the stack a word-aligned area
- dw 128 dup ('TM') ; HIMEM.SYS may need 256 byte stack!
- StackTop:
-
- ; ---------------------------------------------------------------------------
- ; Strategy Entry Point For the Device Driver
-
- ; This routine simply stores the pointer to the request header
- ; so that request header has it. That's all it does. Really.
-
- STRATPROC proc far
-
- Strategy:
- mov [cs:RBPointOff],bx
- mov [cs:RBPointSeg],es ; just store the pointer
- ret ; and get outta here!
- ; (isn't it ironic that the shortest routine is called "Strategy"?)
- STRATPROC endp
-
- ; ---------------------------------------------------------------------------
- ; Interrupt Entry Point For the Device Driver
-
- ; This routine executes the command contained in the passed request header.
- ; DOS has called STRATEGY, and that routine stored a pointer to the request
- ; header for our use. We will construct our own stack area because the
- ; XMS driver uses a great deal of stack space. (Up to 256 bytes!)
-
- INTPROC proc far
- Interrupt:
- PrintChar 'D'
- push ax ; TMA
-
- mov [CS:SaveSS],ss ; save the SS register
- mov [CS:SaveSP],sp ; save the SP register
-
- mov ax, cs ;TMA mod
- cli
- mov ss, ax
- mov sp, offset StackTop ; initialize our stack
- sti
-
- ifdef V286
- pusha
- else
- push bx ; save the other regs
- push cx
- push dx
- push bp
- push si
- push di
- endif
-
- pushf ; and the flags
- cld ; set the string direction up
- push es
- push ds
-
- mov ds,ax ; setup the data segment register
-
- ; Note that during calls we use DS to point to our local data
- ; and ES to point to the request header.
-
- les bx,[RBPoint] ; get the request buffer
- mov al,[rq.command] ; get the command
- cbw ; word for jump table
-
- ; be sure that the command is in our range
-
- cmp al,TopCommand ; fifteen is the highest for us
- ja badcommand ; too high! # UNSIGNED! TMA
-
- shl ax,1 ; (one word offset = 2 bytes)
- mov si,ax
-
- ; fake a "short call" by setting the return address to the exit routine
-
- ifdef V286
- push offset BigLog
- else
- mov ax,offset BigLog
- push ax
- endif
- mov ax,donestat ; assume OK
- jmp JumpTable[si] ; and hop to it!
-
- ; ---------------------------------------------------------------------------
- ; We come here if we run into an XMS error. We'll set the "General
- ; Failure" flag, and return to MS-DOS
-
- FreakOut: mov ax,(errorflag+err_general+donestat)
- ; general failure
- ; and error settings
- jmp short BigLog
-
- ; ---------------------------------------------------------------------------
- ; Ran into an unsupported command - set the flag in the status word.
-
- BadInit: ; A second INIT call is a NO-NO
- IOCTLInput:
- ReadNoWait: ; those table entries are invalid commands
- InputStatus:
- InputFlush:
- pop ax ; (forget about the short call)
- badcommand:
- mov ax,(err_unknown+errorflag+donestat) ; an unknown command err
-
- ; ---------------------------------------------------------------------------
- ; This is the mass exit; everone splits through this point! When we
- ; arrive here, the AX reg will contain the word to be put into the
- ; status word. We'll do that:
-
- BigLog:
- PrintChar 'X'
-
- mov [rq.status],ax
-
- ; Now, we just undo the registers.
-
- pop ds ; the seg regs
- pop es
-
- popf ; the flags
- ifdef V286
- popa
- else
- pop di
- pop si ; and the data regs
- pop bp
- pop dx
- pop cx
- pop bx
- endif
-
- PrintChar 'd'
- cli
- mov ss,[CS:SaveSS]
- mov sp,[CS:SaveSP] ; restore the calling stack
- sti
-
- pop ax ; TMA
- ret
-
- INTPROC endp
-
- ; ---------------------------------------------------------------------------
- ; MEDIA CHECK
-
- ; This command checks to see if the media has been removed and replaced.
- ; Since a RAM drive is non-removable media, this command will always
- ; return a "false".
-
- MC proc near
- PrintChar 'M'
- MediaCheck:
- mov [rq.change],1 ; media has not been changed
- PrintChar 'm'
- ret ; return to leave
- MC endp
-
- ; ---------------------------------------------------------------------------
- ; BUILD BIOS PARAMETER BLOCK
-
- ; This command simply "builds" a BPB by telling DOS where it is located.
-
- BBPB proc near
- BuildBPB:
- PrintChar 'P'
- mov [rq.bpboff],OFFSET OurBPB ; the offset
- mov [rq.bpbseg],cs ; in our CS
- PrintChar 'p'
- ret
-
- BPBArray dw offset OurBPB
-
- OurBoot: db 0,0,0
- db 'TerjeXMS' ; whodat?
-
- OurBPB:
- SecSize dw 512 ; standard DOS sector size
- SecPerCluster db 1 ; sectors per allocation unit
- dw 1 ; number of reserved sectors
- db 1 ; number of copies of the FAT
- RDirLen dw 32 ; number of root directory sectors TMA
-
- DiskSize dw 1024 ; number of sectors on the disk
- db MediaD ; (media descriptor)
- SecPerFAT dw 1 ; number of sectors per FAT
-
- dw 8 ; sectors per track
- dw 1 ; number of heads
- dw 0 ; number of hidden sectors
- BootCode:
-
- OurBootLen equ this byte - OurBoot
-
- BBPB endp
-
-
- ; ---------------------------------------------------------------------------
- ; WRITE
-
- ; This command writes the specified number of sectors starting at the
- ; given sector. It returns the number of sectors actually written.
- ; Errors are returned if the sector is out of range, or if the number
- ; of sectors
- ; is past the end of the disk. (The error checking is done in the
- ; TestValues procedure.) This procedure doesn't do much itself. It's
- ; body is mostly an XMS move call.
-
- WSEC proc near
- Write:
- ifdef PCL
- mov al,'N'
- out 097h,al
- mov al,'J'
- out 096h,al ; display "NJ-W" on Smart-Vu
- mov al,'-'
- out 095h,al
- mov al,'W'
- out 094h,al
- endif
-
- PrintChar 'W'
-
- mov si, offset W_EMM
-
- call TestValues
-
- mov [si.DestOffset+1],ax
-
- mov [si.SourceOffset],cx
- mov [si.SourceOffset+2],dx
-
- jmp Do_XMS_Transfer
-
- WSEC endp
-
- ; ---------------------------------------------------------------------------
- ; READ
-
- ; This command reads the specified number of sectors starting at the
- ; given sector. It returns the number of sectors actually read. Errors
- ; are returned if the sector is out of range, or if the number of sectors
- ; is past the end of the disk. (The error checking is done in the
- ; TestValues procedure.) This procedure doesn't do much itself. It's
- ; body is mostly a call for an XMS move instruction.
-
- RSEC proc near
- Read:
-
- ifdef PCL
- mov al,'N'
- out 097h,al
- mov al,'J'
- out 096h,al ; display "NJ-R" on Smart-Vu
- mov al,'-'
- out 095h,al
- mov al,'R'
- out 094h,al
- endif
-
- PrintChar 'R'
-
- mov si, offset R_EMM
-
- call TestValues ; AX = Sec# Shl 1, DX:CX = TBA
-
- mov [si.SourceOffset+1],ax
-
- mov [si.DestOffset],cx
- mov [si.DestOffset+2],dx
-
- Do_XMS_Transfer: ; Common code for Read & Write
- mov ah, X_Move
- call [EMM_Vector]
- or ax,ax
- jz XMS_Transfer_Error
-
- mov ax,donestat ; OK!
-
- ReadClick label byte
- jmp MakeClick ; finish clicking
-
- XMS_Transfer_Error:
- mov ax,(err_general+errorflag+donestat)
- mov [rq.Count],0 ; Tell DOS nothing done!
-
- WriteClick label byte
- jmp MakeClick ; finish clicking
-
- RSEC endp
-
-
- RangeError: pop ax ; forget our return address
- mov ax,err_badsector + errorflag + donestat ; TMA
- mov [rq.count],0 ; no sectors were read, you know
- ret ; return back to the dispatcher
-
- TestValues proc near
-
- mov ax,[rq.strtsec]
- mov dx,ax
- mov cx,[rq.count]
- add dx,cx
- jc RangeError
- cmp dx,DiskSize
- ja RangeError
-
- shl ax,1 ; 512 byte/sector = 256 Shl 1
- shl cx,1 ; NrOfSectors Shl 9
- mov [si.NrOfBytes+1],cx
- mov cx,[rq.tbaoff]
- mov dx,[rq.tbaseg]
-
- TestValues endp
-
- ; ---------------------------------------------------------------------------
- ; This is a local procedure that clicks the speaker transparently. It
- ; is executed at the end of the TestValues procedure, which is excuted
- ; at the very beginning of the "READ" and "WRITE" functions. It is
- ; also JMP'd to at the end of the READ and WRITE routines, and the RET
- ; at the end of this procedure will return to the caller of the READ
- ; and WRITE functions. (Saves 2 bytes and a bunch of clocks, hey.)
-
- ; From here modified by TMA: Uses self-modifying code to decrease the size
-
- ; If no clicks, the init call will modify the PUSHF to a RETN, and save
- ; the rest of the proc from staying resident. The JMPs from READ & WRITE
- ; to this proc will also be patched into RETNs. This way, we don't have to
- ; test the SpeakerFlag at each call.
-
- SPEAKERCLICK proc near
-
- MakeClick: pushf
-
- push ax ; yes, save the accumulator
- in al,Speak
- and al,SpeakMask ; mask out the bit we don't need
- xor al,SpeakToggle ; toggle the control bit
- jmp $+2 ; For fast PC's
- out Speak,al ; and re-output it
- pop ax ; retrieve the accumulator
-
- popf
- NearRet label byte ; TMA Use this to get RETN opcode
- ret ; return to the caller
-
- SPEAKERCLICK endp
-
- ; ---------------------------------------------------------------------------
- ; This label marks the last byte of the device driver that actually
- ; remains resident. This driver takes less than 800 bytes, guaranteed.
-
- LastResident dw $ ; Current End Of Program
-
- SpeakerFlag db 1 ; one if we should be ticking
-
- JUMPS ; Non-critical code
-
- ; ---------------------------------------------------------------------------
- ; INITIALIZE
-
- ; This command sets up the internal data used by NJRAMD. The procedure
- ; sets the XMS to get the number of kB that the user requests. (The
- ; information following the specification in the CONFIG.SYS file is
- ; parsed to find the user parameters. See the NJFRAMD.DOC file to find
- ; the format of the CONFIG information.) The procedure requests memory
- ; from the XMS driver
-
- Init:
- PrintChar 'I'
- mov dx,offset Banner
- mov ah,PrintString ; show our copyright!
- int 21h
-
- mov ah,GetDOSVersion ; get the DOS version
- int 21h
- mov [MajorVersion],al
-
- mov ax,4300h
- int 2Fh ; Is an XMS driver loaded?
- cmp al,80h
- mov dx,offset NoEMMThere ; point to our error
- jne InitFail ; No XMS driver!
-
- PrintChar '1'
- ; the XMS is present. It's okay! Get EMM_Vector to call
-
- mov ax,4310h
- int 2Fh
- mov word ptr [EMM_Vector],bx
- mov word ptr [EMM_Vector+2],es
-
- mov ah,X_Counts ; get count of available
- call [EMM_Vector] ; memory
- ; QEMM 5.1 or bl,bl
- ; jl GenFail ; general failure?
-
- PrintChar '2'
- cmp ax,16 ; At least 16 kB?
- mov dx,offset NoMem ; print error
- jb InitFail
-
- PrintChar '3'
- mov [HowMuch],ax ; remember how much is left
-
- ; We will now attempt to parse the line of the CONFIG.SYS
- ; file to see if any of our options are on it.
-
- les bx,[RBPoint] ; get pointer to header
- les si,es:[bx+18] ; get pointer to commands
-
- EatingWhite: lods byte ptr es:[si] ; get the next byte
- cmp al,cr ; is it a carriage return?
- je EndOfLine
- cmp al,'0'
- jb NotPages
- cmp al,'9'
- ja NotPages
-
- ; We will handle the pages option by reading the command line until
- ; a non-numeric character. The resulting number will be the number
- ; of kB that the user requested.
-
- xor dx,dx ; zero the result
- sub al,'0'
- mov dl,al
-
- PagesLoop: lods byte ptr [es:si]; get the character
- cmp al,'0' ; is it a number?
- jb LastDigit ; nope!
- cmp al,'9' ; is it a number?
- ja LastDigit ; note!
-
- push ax ; save the digit temporarily
- mov ax,10
- mul dx ; multiply it out
- pop dx ; pop the digit into dx
-
- and dx,0Fh ; make a decimal digit of it
- add dx,ax ; add it into the sum
- jmp short PagesLoop
-
- LastDigit: mov [RqdPages],dx ; save requested number of pages
- cmp dx,16 ; is the requested # of KB < 16
- jb BadPages ; yeah! can't have that
- cmp al,cr ; was that last char a CR?
- je EndOfLine ; yes! end of the parse
- jne EatingWhite ; no, go back for more parsing
-
- BadPages: mov dx,offset TooSmall
- BadPages2: jmp InitFail
-
- NotPages:
- cmp al,'-' ; is it an option marker?
- je GotOption ; yeah! go process it!
- cmp al,'/'
- jne EatingWhite ; no... go back for more
-
- ; We are now pointing at the text of an option. We will
- ; get the option into the al to see exactly what it is, and we
- ; will then act accordningly.
-
- GotOption: lods byte ptr es:[si] ; get the option
- cmp al,'a' ; bump it to upper case?
- jl NoBump ; no need to
- cmp al,'z'
- jg NoBump ; no need to
-
- sub al,('a' - 'A') ; make it lower case
-
- NoBump: cmp al,'S' ; is it a silence option?
- jne NotSilence ; no...
- mov SpeakerFlag,0 ; yes, it is. Reset the option!
- jmp EatingWhite ; and eat up until end of
- ; this option
-
- NotSilence:
- cmp al,'A' ; is it use all memory?
- jne NotUseAll
-
- mov ax,[HowMuch]
- mov [RqdPages],ax ; request them all (KB)
- jmp EatingWhite
-
- NotUseAll:
- Unrecognized: mov dx,offset BadOption ; don't install
- jmp InitFail
-
-
- EndOfLine: ; The parsing is done! We will now check to see if the
- ; requested size is bigger than the available memory.
-
- PrintChar 'e'
- mov ax,[RqdPages] ; is the reqested amount
- cmp ax,[HowMuch] ; greater than available?
- mov dx,offset TooBig ; error msg
- ja InitFail ; yes, abort
-
- ; Now, we'll try to allocate that many kB. If the user
- ; didn't specify a number of kB, the default is 512
-
- PrintChar '4'
- mov dx,ax
- mov ah,X_Alloc ; open a new handle of (DX) kB
- call [EMM_Vector]
- or ax,ax
- jz GenFail
-
- PrintChar '5'
- mov [R_EMM.SourceHandle],dx ; save the handle for later
- mov [W_EMM.DestHandle],dx
-
- ; We will now setup the information in the BPB to reflect the
- ; status of the RAM drive. First, we'll store the DiskSize.
-
- mov ax,[RqdPages] ; get number of pages
- shl ax,1 ; 2 sectors in one KB
- ; add ax,7
- ; and ax, NOT 7 ; Should be divisible by 8!
- mov [DiskSize],ax ; store it in BPB
-
- ; Now, we'll figure out how many entries there will be in the
- ; root directory. We will allow 1 root directory entry for
- ; each 2k of storage that the disk has. We won't allow more
- ; than 512 root dir entries, though.
-
- ifdef V286
- shr ax,2
- else
- shr ax,1 ; figure out length of
- shr ax,1 ; root directory
- endif
- cmp ax,512 ; 1 entry per 2k of storage
- jb BigBust ; up to 512
-
- mov ax,512
-
- BigBust: add ax,31 ; make sure it's a multiple
- and ax,not 31 ; of 32 (round it up)
- mov [RDirLen],ax
-
- ; Since we use a 12-bit FAT, we must have 4087 clusters or less.
- ; We will start with a 1024-byte cluster, and double the cluster
- ; size until we have enough FAT space. A user must
- ; configure about 3.75 megabytes of memory as a RAM drive to
- ; cause the program to use 2048-byte clusters... otherwise, the
- ; drive will have 1024-byte clusters.
-
- mov cx,2 ; Two clusters per sector
- ; for starters.
-
- ReTry: mov ax,[DiskSize] ; get the disk size
- xor dx,dx
- div cx ; AX = (DiskSize/SPC)
- cmp ax,4087 ; is it less than 4087?
- jb GoodCombo ; yeah!
- shl cx,1 ; no. double the SPC and
- jnc ReTry ; try it again
- jmp GenFail ; 64kB cluster is an error!
-
- GoodCombo: mov [SecPerCluster],cl ; save SPC number
-
- PrintChar '6'
-
- ; AX still is set to the number of clusters on the disk. Very
- ; useful number, you know. We will find now the amount of FAT
- ; space that is needed.
-
- mov bx,ax ; ax = clustsers
- add ax,ax ; ax = 2*(clusters)
- add ax,bx ; ax = 3*(clusetrs)
-
- inc ax ; In case of odd # of clusters TMA
-
- shr ax,1 ; ax = 1.5*(clusters)
-
- add ax,511 ; To round up TMA
-
- xor dx,dx ; (FAT Length)
- mov cx,512 ; AX = ----------------
- div cx ; (BytesPerSector)
-
- mov [SecPerFAT],ax ; store it in the BPB
-
- ; The BPB is now set up properly. We will now "format" the
- ; RAM disk. First, we will have to set all the RAM area to
- ; zero. (Even on extremely large "drives", this doesn't take
- ; very long.)
-
- mov di, OFFSET InitBuffer ; Make a block of ZERO's
- push cs
- pop es
- mov cx, 512
- xor ax,ax
- rep stosw
-
- mov cx,[DiskSize] ; get number of sectors on disk
- shr cx,1 ; CX = # of KB
- mov si, OFFSET W_EMM; Use W_EMM for all writing
-
- ; mov [si.DestOffset+1],0 ; Init to 0
- mov [si.NrOfBytes],1024
- mov [si.SourceOffset], OFFSET InitBuffer
- mov [si.SourceOffset+2], CS
-
- WipeOut:
- mov ah, X_Move
- call [EMM_Vector]
- or ax,ax
- jz GenFail
-
- add [si.DestOffset+1],4 ; 4 * 256 = 1024 = 1 kB
- loop WipeOut ; if more, go back
-
- PrintChar '7'
-
- ; Now that everything is zeroed, we will copy the pseudo-boot
- ; sector that we have. DOS uses some of this information while
- ; reading and writing the disk, so we set it up there.
-
- mov [si.DestOffset+1],0 ; Init to 0
- mov [si.NrOfBytes], (OurBootLen + 1) AND 0FFFEh
- mov [si.SourceOffset], OFFSET OurBoot
-
- mov ah,X_Move
- call [EMM_Vector]
- or ax,ax
- jz GenFail
-
- PrintChar '8'
-
- ; The boot sector has been written in. We will now set up
- ; the FAT. This task is rather simplified, since we only
- ; have one copy of the FAT.
-
- mov byte ptr [InitBuffer],MediaD
- mov word ptr [InitBuffer+1],0FFFFh
-
- mov [si.DestOffset+1],2 ; 2 * 256 = 1 sector
- mov [si.NrOfBytes], 4
- mov [si.SourceOffset], OFFSET InitBuffer
-
- mov ah,X_Move
- call [EMM_Vector]
- or ax,ax
- jz GenFail
-
- PrintChar '9'
-
- ; Now, we will figure out where the first directory sector is.
- ; *WARNING* - This code assumes that there is only one copy of
- ; the FAT, and that there is one reserved sector. If ya change
- ; the drive to have 2 copies of the FAT, or modify it to have
- ; reserved sectors (for whatever reason you'd wanna do that),
- ; you'll have to change this code fragment!
-
- mov ax,[SecPerFAT]
- inc ax ; AX = first dir sector
- shl ax,1 ; * 2 for 256 byte blocks
- mov [si.DestOffset+1],ax
- mov [si.NrOfBytes], (OurVolumeLen + 1) AND 0FFFEh
- mov [si.SourceOffset], OFFSET OurVolume
-
- mov ah,X_Move
- call [EMM_Vector]
- or ax,ax
- jz GenFail
-
- ; Initialize W_EMM.NrOfBytes back to Zero
-
- mov [si.NrOfBytes],0
- PrintChar '!'
-
- ; Phew! Now the whole thing is done! We will show the user
- ; what has been done. First, we will figure out what device
- ; tag that we have. We will tell the user about it. DOS versions
- ; earlier than 3.00 don't let us know what our device tag is,
- ; so we can't tell the user.
-
- ; TMA: First, check if not clicking, if so disable that part:
-
- cmp SpeakerFlag, 0 ; is there clicking?
- jne UsingClick
-
- mov al, [NearRet]
- mov byte ptr [MakeClick], al ; RETN opcode TMA
- mov [ReadClick], al
- mov [WriteClick], al
-
- mov [LastResident], offset MakeClick + 1 ; Reduce RAM TMA
-
- UsingClick:
- les bx,[RBPoint] ; point to the header, again
- mov al,[rq.taglet] ; get the tag letter
- add al,'A' ; change it to a capital drive letter.
- mov [DriveName],al
-
- mov bx,[LastResident] ; calculate used size
- xor ax,ax
- mov si,offset UsedSpace
- call Bin2Dec ; store it in the messgae
-
- mov ah,X_Counts ; find amount of space left
- call [EMM_Vector]
- or bl,bl
- jl GenFail
-
- PrintChar '"'
- mov cl,6 ; AX:BX = (Space left SHL 16) SHR 6 = KB
- xor bx,bx ; Zero low word
- @@1:
- shr ax,1
- rcr bx,1
- loop @@1
-
- mov si,offset Installed2
- call Bin2Dec ; put it into the message!
-
- mov bx,[DiskSize] ; get sectors of disk space
- sub bx,[SecPerFAT] ; subtract space used by the FAT
-
- mov ax,[RDirLen] ; get the entries in the root dir
- ifdef V286
- shr ax,4
- else
- mov cl,4
- shr ax,cl ; divide by # of entries per sec
- endif
- sub bx,ax ; subtract some more
- dec bx ; and adjust down for boot sector
-
- mov cx,9 ; multiply the answer by 512
- xor ax,ax ; zero the high side
- CalcDiskFree: shl bx,1 ; shift low side up
- rcl ax,1 ; shift high side over, with carry
- loop CalcDiskFree
-
- mov si,offset Installed
- call Bin2Dec ; store it in the message
-
- cmp [SpeakerFlag], 0 ; is there clicking?
- jne ClickOkay
-
- mov ah,PrintString
- mov dx,offset NoClicking ; tell the user that it's
- int 21h ; been disabled.
-
- ClickOkay:
- cmp [MajorVersion],3 ; is it version three+?
- jae MsgOkay
-
- mov [DriveName],eos ; TMA Ver 2.x, so no drive letter
-
- MsgOkay: mov dx,offset Installed ;print part one of
- mov ah,PrintString ; installed! message
- int 21h
-
- mov dx,offset InstalledB ;print part two
- int 21h
-
- les bx,[RBPoint] ; get that pesky pointer
-
- mov ax, [LastResident] ; show DOS where we end TMA
- mov [rq.ndadro],ax ; offset
- ; show DOS were we end
- mov [rq.ndadrs],cs ; segment
- mov [rq.bpbseg],cs ; show DOS the BPB array
-
- mov [rq.units],1 ; we installed one unit
- mov [rq.bpboff],OFFSET BPBArray ; BPB array offset
-
- mov [JumpTable], OFFSET BadInit ; In case second INIT call
-
- xor ax,ax ; no return value
- ret
-
- ; ---------------------------------------------------------------------------
- ; Init failure
-
- ; We will come here if there is a failure during the initialization
- ; of the driver. We print a message letting the user know why we can't
- ; install, and we then zero ourselves out so that DOS doesn't waste any
- ; memory on us.
-
- InitFail: push dx ; save the specific error
- mov dx,offset General
- mov ah,PrintString
- int 21h
-
- pop dx ; now print specific error
- mov ah,PrintString
- int 21h
-
- les bx,[RBPoint] ; point to the request header
- mov ax,cs
-
- mov [rq.ndadrs],ax ; ending address is zero
-
- xor ax,ax ; because no memory is taken
- mov [rq.ndadro],ax ; since we failed
- mov [rq.units],al ; no units, either
- PrintChar 'i'
- ret
-
- ; ---------------------------------------------------------------------------
- ; General Failure
-
- ; There was an EMM Failure during the installation. If such is the case,
- ; we will terminate with an error message, and then go to the regular
- ; fail routine.
-
- GenFail: mov dx,offset EMMError
- jmp short InitFail
-
-
- ; ---------------------------------------------------------------------------
- ; Transient Data Area
-
- ; The TDA contains the variables used by the Initialization segment of
- ; the device driver. It doesn't stay resident.
-
- HowMuch dw ? ; amount of free EMS, in pages
- RqdPages dw 512 ; amount of pages requested
- ; (512k is the default)
- MajorVersion db 3 ; the DOS major version number
-
- MTIME MACRO h,m,s
- dw (h SHL 11) + (m SHL 5) + (s SHR 1)
- ENDM
-
- MDATE MACRO y,m,d
- dw ((y-80) SHL 9) + (m SHL 5) + d
- ENDM
-
- OurVolume db 'Terjes_Disk' ; 11-byte volume name
- db 000001000b ; volume label attribute
- db 10 dup (0) ; reserved space
- ;FEDCBA9876543210b
- ; File Time & Date
- MTIME 23 08 40 ; 23:08:40
- MDATE 90 09 30 ; 90-09-30
- db 6 dup (0) ; more reserved space
-
- OurVolumeLen equ $ - OFFSET OurVolume
-
- ; ---------------------------------------------------------------------------
- ; Messages
-
- ; These are messages that are used by the initialization section of the
- ; driver.
-
- Banner label byte
- db cr,lf
- db 'NJ/TMa Ram Disk (C) 1987 by Mike Blaszczak',cr,lf
- ifdef V286
- ifdef PCL
- db "PC's Limited "
- else
- db '286 '
- endif
- endif
- db 'XMS Version 1.02 (C) Terje Mathisen ',??time,' ',??date,cr,lf
-
- db cr,lf,eos
-
- General db 'Device not installed.',cr,lf,eos
-
- NoEMMThere db 'The XMS driver is not installed.',cr,lf,lf,eos
-
- EMMError db 'XMS failure during installation.',cr,lf,lf,eos
-
- NoMem db 'No free XMS Memory.',cr,lf,lf,eos
-
- TooBig db 'Requested size too big to fit.',cr,lf,lf,eos
-
- TooSmall db "Can't have disk size < 16kB.",cr,lf,lf,eos
-
- BadOption db 'Unrecognized option encountered.',cr,lf,lf,eos
-
- NoClicking db 'Clicking suppressed.',cr,lf,eos
-
- ; "Installed" marks the beginning of the information that is printed
- ; if the device is successfully installed. The beginning of each
- ; line has eight spaces, which are filled with the information by the
- ; BIN2DEC procedure. There is then one more space, so that the end
- ; of the number doesn't bump the first word... thus, a total of nine
- ; spaces begin the Installed, Installed2, and UsedSpace labels.
-
- Installed db ' bytes available on RAM drive '
- DriveName db '_:.',eos
-
- InstalledB db cr,lf
- Installed2 db ' bytes left in XMS storage.',cr,lf
- UsedSpace db ' bytes of standard DOS memory were'
- db ' taken.',cr,lf,lf
- db eos
-
- ; ---------------------------------------------------------------------------
- ; Init Subroutines
-
- ; The following area contains subroutines used by the INIT procedure of
- ; the device driver. They aren't kept in memory after the device has been
- ; installed.
-
- ; ---------------------------------------------------------------------------
- ; BIN2DEC
-
- ; This routine converts a binary number, in AX:BX, to decimal notation.
- ; It will convert up to 8 digits, and will supress leading zeros. The
- ; routine should be called with DS:SI set to point to the area to store
- ; the converted number.
-
- Bin2Dec proc near
-
- push es ; save the registers
- push ds
- push di
- push si
-
- mov WorkAreaL,bx
- mov WorkAreaH,ax ; put the number on our scratchpad
-
- mov ax,ds ; point to the answer with ES:DI
- mov es,ax
- mov di,si
- add di,7
-
- mov si,offset WorkAreaL ; point at scratchpad
-
- Bin2DecLoop: push si
-
- xor bx,bx ; done flag
- mov cx,2 ; 2 words in our number
- mov dx,bx ; clear remainder
- add si,2 ; point to the high end
-
- Bin2DecDigit: push cx ; save word count
- mov ax,[si] ; get the digit
- mov cx,10
- div cx ; convert it
- mov [si],ax ; store it back
- or bx,ax ; set the done flag appropriately
- sub si,2 ; point to next lower
- pop cx
- loop Bin2DecDigit
-
- or dl,'0' ; make it into a decimal digit
- mov [di],dl ; and store it
- dec di ; adjust pointer
-
- pop si ; get the pointer back
- and bx,bx ; is the result zero?
- jne Bin2DecLoop ; nope! Do more!
-
- pop si ; retrieve the used registers
- pop di
- pop ds
- pop es
- ret
-
- WorkAreaL dw 0 ; low end of the work area
- WorkAreaH dw 0 ; high side of the work area
-
- Bin2Dec endp
-
- InitBuffer label byte ; Use 1kB from here for init
-
- driver ends
- end
-
- ; That's a wrap.
- ; Special thanks to Bob Brody and Dr File Finder
-
-