home *** CD-ROM | disk | FTP | other *** search
-
- ; 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
- ; Version 1.30 of 05 Dec 1989
- ; Version 1.40 of 23 Jun 1991
-
- ; Shareware $15 Please register!
-
- ; Assemble with
- ; MASM NJRAMD;
- ; (Use MASM /DV286 NJRAMD; to assemble 286 version.)
- ; LINK NJRAMD;
- ; EXE2BIN NJRAMD.EXE NJRAMD.SYS
- ; DEL NJRAMD.EXE
-
- ; --> DEVICE DRIVER FORMAT FILE <--
- ; --> REMEMBER TO USE EXE2BIN <--
-
- ; ---------------------------------------------------------------------------
-
- ; 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
-
- ; ---------------------------------------------------------------------------
-
- EMM equ 067h ; the E/EMS memory manager
-
- ; ---------------------------------------------------------------------------
- ; 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 #
-
- ; ---------------------------------------------------------------------------
- ; E/EMM Routines
-
- ; These are the E/EMM functions that we use. (These are specific functions
- ; of the EMM interrupt.)
-
- E_PageBase equ 041h ; determine the Page Fram Base Addr
- E_Counts equ 042h ; determine free/total mem
- E_Open equ 043h ; open, allocate, obtain handle ID
- E_MapPage equ 044h ; map a logical page into window
- E_Version equ 046h ; get the E/EMM version number
- E_Save equ 047h ; save mapping context
- E_Restore equ 048h ; restore mapping context
-
- ; ---------------------------------------------------------------------------
- ; 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".
-
- ; 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.
-
- PUBLIC NextPlace
- PUBLIC Attrib, JumpTable, TopCommand, RBPoint, RBPointOff, RBPointSeg, SaveSS
- PUBLIC SaveSP, SaveAX, EMMHandle, EMMBase, StackTop, STRATPROC, Strategy
- PUBLIC INTPROC, Interrupt, NoSaveM, FreakOut, IOCTLInput, ReadNoWait
- PUBLIC InputStatus, InputFlush, badcommand, BigLog, NoRestore, MC, MediaCheck
- PUBLIC BBPB, BuildBPB, BPBArray, OurBoot, OurBPB, SecSize, SecPerCluster
- PUBLIC RDirLen, DiskSize, SecPerFAT, BootCode, TAddr, TAddrOff, TAddrSeg
- PUBLIC TDone, TCount, TSector, RSEC, Read, ReadLoop, ReadDone
- PUBLIC ReadFinish, ReadError, WSEC, Write, WriteLoop, WriteDone, WriteFinish
- PUBLIC WriteError, CLIPPER, RangeError, InRange, SPEAKERCLICK, MakeClick
- PUBLIC NoClick, SpeakerFlag, LastTime, GS, GetSector, CantGet, LastResident
- PUBLIC EMMPresent, GenFailHook, EMMPresent2, MemForMe, EatingWhite, GotOption
- PUBLIC NoBump, NotSilence, PagesLoop, LastDigit, NotPages, NotUseAll
- PUBLIC Unrecognized, EndOfLine, GoodSize, GotPages, BigBust, ReTry
- PUBLIC GoodCombo, NoKludge, WipeOut, WipeOut2, FindFree, CalcEMMFree
- PUBLIC CalcDiskFree, ClickOkay, MsgOkay, InitFail, GenFail, HowMuch
- PUBLIC RqdPages, MajorVersion, OurVolume, Banner, EMMIDString, General
- PUBLIC NoEMMThere, EMMError, Init, NoMem, TooBig, BadOption, NoClicking
- PUBLIC Installed, DriveName, InstalledB, Installed2, UsedSpace, Bin2Dec
- PUBLIC Bin2DecLoop, Bin2DecDigit, WorkAreaL, WorkAreaH
-
- ; ---------------------------------------------------------------------------
-
- 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,'NJ_DISK' ; Nifty James' Disk!
-
- ; ---------------------------------------------------------------------------
-
- 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
- SaveAX dw 0 ; save place for the accumulator
-
- EMMHandle dw 0 ; our handle, as assigned by the EMM
- EMMBase dw 0 ; base of the EMM physical window
-
- ; ---------------------------------------------------------------------------
- ; TDATA
-
- ; This is a "temporary" data area that is used to hold the data used
- ; by the transfer routines.
-
- TAddr label dword
- TAddrOff dw 0 ; the transferr (read to, write from)
- TAddrSeg dw 0 ; address
-
- TDone dw 0 ; count of sectors done
- TCount dw 0 ; number of sectors to do
- TSector dw 0 ; the sector to be transfer
-
- ; ---------------------------------------------------------------------------
- ; The local stack
-
- even ; make the stack a word-aligned area
- dw 64 dup (0DEADh)
- 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
- ; EMM uses a great deal of stack space.
-
- INTPROC proc far
- Interrupt:
- PrintChar 'D'
- mov CS:SaveSS,ss ; save the SS register
- mov CS:SaveSP,sp ; save the SP register
- mov CS:SaveAX,ax
-
- cli
- mov ax,offset StackTop ; initialize our stack
- mov sp,ax
- mov ax,cs
- mov ss,ax
- 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
-
- mov ax,0FFFFh ; wipe out any memory of previous
- mov LastTime,ax ; page mappings
-
- ; 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
-
- ; be sure that the command is in our range
-
- cmp al,TopCommand ; fifteen is the highest for us
- jg badcommand ; too high! bad command
-
- ; it's a good command - be on the lookout for an unorthodox
- ; initialization call
-
- or al,al ; is it function zero?
- je NoSaveM ; yes, don't save the map context
-
- push ax ; no, save the AX register
- mov ah,E_Save ; save the mapping context
- mov dx,EMMHandle ; under our handle
- int EMM ; ask the manager to do it
- or ah,ah ; if there was an error,
- jnz FreakOut ; freak out!
- pop ax ; if not, get the AX back
-
- NoSaveM: shl ax,1 ; (one word offset = 2 bytes)
- mov si,ax
-
- ; fake a "short call" by setting the return address to the exit routine
-
- mov ax,offset BigLog
- push ax
- xor ax,ax ; clear our status
- jmp cs:JumpTable[si] ; and hop to it!
- jmp short BigLog
-
- ; ---------------------------------------------------------------------------
- ; We come here if we run into an EMM error. We'll set the "General
- ; Failure" flag, and return to MS-DOS
-
- FreakOut: mov rq.status,(errorflag+err_general)
- ; general failure
- ; and error settings
- jmp short BigLog
-
- ; ---------------------------------------------------------------------------
- ; Ran into an unsupported command - set the flag in the status word.
-
-
- IOCTLInput:
- ReadNoWait: ; those table entries are invalid commands
- InputStatus:
- InputFlush:
- pop ax ; (forget about the short call)
- badcommand:
- mov ax,(err_unknown+errorflag) ; 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'
- les bx,cs:RBPoint ; point to the request block
- push ax
- mov al,rq.command ; was it an init?
- and al,al ; yes! don't restore
- pop ax
- je NoRestore
-
- push ax
- mov ah,E_Restore ; restore the EMS
- mov dx,EMMHandle ; mapping context
- int EMM
- or ah,ah ; if there was an error
- pop ax
- jnz FreakOut
-
- NoRestore: or ax,donestat ; set the done status
- 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 ax,CS:SaveAX
- mov sp,CS:SaveSP ; restore the calling stack
- mov ss,CS:SaveSS
- sti
- 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 'NiftyEMS' ; 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 entries
-
- 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
-
-
- ; ---------------------------------------------------------------------------
- ; 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
- ; CLIPPER procedure.) This procedure doesn't do much itself. It's
- ; body is mostly a string move instruction. The starting address
- ; and ending address are set up by the CLIPPER procedure.
-
- 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'
- call CLIPPER ; do the clipping, if need be
-
- ReadLoop: mov ax,TCount ; are we done transferring yet?
- cmp TDone,ax
- je ReadDone ; yes! quit the loop
-
- mov ax,TSector ; no ... do some more!
- call GetSector
- jc ReadError
- PrintChar 'y'
-
- mov si,di ; setup the get from address
- les di,TAddr ; get the store to address
- mov cx,256 ; number of words to move
- mov ds,EMMBase
-
- rep movsw ; move it!
-
- mov ax,cs ; get addressing back
- mov ds,ax
-
- add TAddrOff,512 ; increment transferr address
- inc TDone
- inc TSector
- jmp short ReadLoop
-
- ReadDone: xor ax,ax ; clear error flags
-
- ReadFinish: les bx,[RBPoint] ; point to request header
- mov dx,TDone ; store actual transferred
- mov rq.Count,dx
- jmp MakeClick ; finish clicking
-
- ReadError: mov ax,(err_badread+errorflag) ; there was an error!
- jmp short ReadFinish
-
- RSEC 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 CLIPPER procedure.) The procedure does very little itself;
- ; it's body consists mostly of a string move instruction. The
- ; source, destination, and other counts are set up by the CLIPPER
- ; procedure.
-
- 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'
- call CLIPPER ; do the clipping, if need be
-
- WriteLoop: mov ax,TCount ; are we done transferring yet?
- cmp TDone,ax
- je WriteDone ; yep, we are, quit the loop
-
- mov ax,TSector ; no ... do some more!
- call GetSector
- jc WriteError
- PrintChar 'k'
- mov es,EMMBase
- lds si,cs:TAddr ; get the store to address
- mov cx,256 ; number of words to move
-
- rep movsw ; move it!
-
- mov ax,cs ; reset addressing back
- mov ds,ax
-
- add TAddrOff,512 ; increment transferr address
- inc TDone ; count of sectors
- inc TSector ; and current sector number
- jmp short WriteLoop
-
- WriteDone: xor ax,ax ; clear error flags
-
- WriteFinish: les bx,[RBPoint] ; point to request header
- mov dx,TDone ; store actual transferred
- mov rq.Count,dx
- jmp MakeClick ; finish clicking
-
- WriteError: mov ax,(err_badwrite+errorflag) ; there was an error!
- jmp short WriteFinish
-
-
- WSEC endp
-
- ; ---------------------------------------------------------------------------
- ; Clipper
-
- ; This local procedure checks the parameters passed to the READ and
- ; WRITE commands to be sure that they are valid. If they are indeed
- ; valid, it will call the speaker click procedure to take care of
- ; the "audible" options. It also saves the context of the EMM, and
- ; sets up the EMM to work with our process.
-
- CLIPPER proc near
-
- mov cx,rq.strtsec ; get the starting sector number
- mov TSector,cx
- cmp cx,DiskSize ; is it larger than the drive?
- jg RangeError ; yes! there's an error
-
- mov ax,rq.count
- mov TCount,ax ; save it for later
- add cx,ax ; add in the number of sec to read
- cmp cx,DiskSize ; is it larger than life?
- jle InRange ; no... it's okay
-
- RangeError: pop ax ; forget our our return address
- mov ax,err_badsector; that's a bad sector!
- or ax,errorflag ; (and that's an error in my book)
- mov rq.count,0 ; no sectors were read, you know
- ret ; return back to the dispatcher
-
- InRange: mov ax,rq.tbaoff ; get the transfer base addr
- mov TAddrOff,ax
- mov ax,rq.tbaseg
- mov TAddrSeg,ax
- xor ax,ax ; zero transferred count
- mov TDone,ax
-
- ; just flow through to MakeClick
-
- CLIPPER endp
-
- ; ---------------------------------------------------------------------------
- ; This is a local procedure that clicks the speaker transparently. It
- ; is executed at the end of the CLIPPER 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.)
-
- SPEAKERCLICK proc near
-
- MakeClick: pushf
- cmp SpeakerFlag,0 ; should we?
- je NoClick ; no, forget it happened
-
- 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
- out Speak,al ; and re-output it
- pop ax ; retrieve the accumulator
-
- NoClick: popf
- ret ; return to the caller
-
- SpeakerFlag db 1 ; one if we should be ticking
- ; (the default is ticking)
-
- SPEAKERCLICK endp
-
- ; ---------------------------------------------------------------------------
- ; GetSector
-
- ; This routine calls the EMM to map the page with the requested sector
- ; into the physical window. On entry, AX contains the requested sector.
- ; On exit, the EMS is setup so that the requested sector is in the window.
- ; [EMMBASE]:DI will point to it. This routine is rather funky; it does
- ; the mapping using the slippery shift functions, instead of using DIV
- ; or a lookup table. *SUPER FAST*!
-
- GS proc near
-
- GetSector:
- PrintChar 'G'
- cmp ax,DiskSize ; check the range!
- jg CantGet
-
- push ax ; save a copy of the number
- ifdef V286
- shr ax,5
- else
- mov cl,5
- shr ax,cl ; divide the sector by 32
- ; so that AX=EMM Page
- endif
-
- cmp ax,LastTime ; is it the same thing
- je Optimized ; we got last time?
-
- mov LastTime,ax ; remember it for later
-
- mov bx,ax ; nah, we'll have to get this one
- mov ah,E_MapPage
- mov al,0 ; map it into zero
- mov dx,EMMHandle
- int EMM
- or ah,ah ; was there an error?
- jne CantGet
-
- Optimized: pop ax ; retrieve the remainder
- and ax,01Fh ; mask out high bits of offset
- ifdef V286
- shl ax,9
- else
- mov cl,9
- shl ax,cl ; find the offset of the sector
- endif
- mov di,ax
- clc
- PrintChar 'g'
- jmp short MakeClick ; tick-tock on the way out
-
- CantGet: pop ax ; forget the remainder, since there was err
- stc ; set the error flag, if error
- ret
-
- ; An ingenious optimization, if I must say so myself. This variable holds
- ; the last EMS logical page that was fetched by this routine. This way,
- ; the program never gets the same page twice in a row. It's reset to
- ; 0FFFFh by the Interrupt routine so that we won't forget to get a page
- ; when one hasn't been attained. (0FFFFh is a unique code that will never
- ; correspond to an actual page.) Since DOS often does more than one
- ; sequential read or write in a single call to the driver, this small
- ; feature can save quite a bit of time.
-
- LastTime dw 0FFFFh
-
- GS endp
-
- ; ---------------------------------------------------------------------------
- ; This label marks the last byte of the device driver that actually
- ; remains resident. This driver takes less than 800 bytes, guaranteed.
-
- LastResident:
- ; ---------------------------------------------------------------------------
- ; INITIALIZE
-
- ; This command sets up the internal data used by NJRAMD. The procedure
- ; sets the EMM to get the number of pages 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 EMM
-
- 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
-
- xor ax,ax ; point to the 0000 segment
- mov es,ax
- mov bx,(EMM*4)+2 ; find the EMM interrupt
- mov ax,es:[bx]
- mov es,ax ; point to the EMM device
- mov di,10 ; header
-
- mov si,offset EMMIDString ; point to the EMM identifier
- mov cx,8
- repz cmpsb ; compare a bunch of bytes
- jz EMMPresent
-
- mov dx,offset NoEMMThere ; point to our error
- jmp InitFail ; the EMM isn't there!!
-
- EMMPresent: ; the Extended Memory Manager is present. It's okay!
- ; get the EMM Page base, and save it for future reference.
-
- mov ah,E_PageBase ; get the page base
- int EMM
- or ah,ah
- je EMMPresent2 ; general failure?
- GenFailHook: jmp GenFail ; (RELATIVE JMP OUT OF RANGE
-
- EMMPresent2: mov EMMBase,bx ; save it for later
-
- mov ah,E_Counts ; get count of available
- int EMM ; memory
- or ah,ah
- jne GenFailHook ; general failure?
- ; (RELATIVE JMP OUT OF RANGE)
-
- cmp bx,0 ; is there any left for me?
- jne MemForMe
-
- mov dx,offset NoMem ; print error
- jmp InitFail
-
- MemForMe: mov HowMuch,bx ; 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: mov al,es:[si] ; get the next byte
- inc si
- cmp al,cr ; is it a carriage return?
- je EndOfLine
- 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: mov al,es:[si] ; get the option
- inc si ; and increment the pointer
- 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,'P' ; is it the pages option?
- jne 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 pages that the user requested.
-
- xor dx,dx ; zero the result
-
- PagesLoop: mov al,es:[si] ; get the character
- inc si
- cmp al,'0' ; is it a number?
- jl LastDigit ; nope!
- cmp al,'9' ; is it a number?
- jg 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
- and dx,dx ; is the requested page number zero?
- je 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,'A' ; is it use all memory?
- jne NotUseAll
-
- mov ax,HowMuch
- mov RqdPages,ax ; request them all
- jmp EatingWhite
-
- NotUseAll:
- Unrecognized: mov dx,offset BadOption ; don't install
- jmp short BadPages2
-
-
-
- EndOfLine: ; The parsing is done! We will now check to see if the
- ; requested size is bigger than the available memory.
-
- mov ax,RqdPages ; is the reqested amount
- cmp HowMuch,ax ; greater than available?
- jge GoodSize ; no, size is good
-
- mov dx,offset TooBig ; yes, that's an error
- jmp InitFail
-
- GoodSize: ; Now, we'll try to allocate that many pages. If the user
- ; didn't specify a number of pages, the default is 32 pages,
- ; which is 512k of storage.
-
- mov bx,ax
- mov ah,E_Open ; open a new handle of (BX) pages
- int EMM
- or ah,ah
- je GotPages ; (RELATIVE JMP OUT OF RANGE)
- jmp GenFail
-
- GotPages: mov EMMHandle,dx ; save the handle for later
-
- ; 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
- ifdef V286
- shl ax,5
- else
- mov cl,5 ; thirty-two 512-byte sectors
- shl ax,cl ; in a 16384-byte page
- endif
- 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 moer
- ; 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
- jl 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)
- 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. The maximum amount of
- ; memory on a single EMS card is 2 megabytes. 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?
- jl GoodCombo ; yeah!
- shl cx,1 ; no. double the SPC and
- jnc ReTry ; try it again
-
- GoodCombo: mov SecPerCluster,cl ; save SPC number
-
- ; 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)
- shr ax,1 ; ax = 1.5*(clusters)
-
- xor dx,dx ; (FAT Length)
- mov cx,512 ; AX = ----------------
- div cx ; (BytesPerSector)
-
- or dx,dx ; is there a remainder?
- je NoKludge
- inc ax ; yes, add another sector
-
- NoKludge: 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. Especially if you have an IBM PS/2 System 80 --
- ; and then, all the chicks will dig ya!)
-
- mov cx,RqdPages ; get number of pages in disk
-
- WipeOut: mov bx,cx ; ask for this page
- dec bx
- mov dx,EMMHandle ; into physical page zero
- mov ah,E_MapPage
- mov al,0
- int EMM
-
- or ah,ah
- je WipeOut2 ; if there was an error, get out
- jmp GenFail ; (RELATIVE JMP OUT OF RANGE)
-
- WipeOut2: mov es,EMMBase ; get addressing to it
- xor ax,ax ; store a zero
- mov di,ax ; zero the destination
- push cx
- mov cx,8192 ; *words* in a page
-
- rep stosw
-
- pop cx
- loop WipeOut ; if more, go back
-
- ; 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.
-
- xor ax,ax ; get the 0 sector
- call GetSector
- mov es,EMMBase
- mov si,offset OurBoot
- mov cx,OurBootLen
- rep movsb ; move it in there
-
- ; 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 ax,1
- call GetSector ; get sector 1
- mov byte ptr es:[di],MediaD
- mov word ptr es:[di+1],0FFFFh
-
- ; 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
- call GetSector
- mov si,offset OurVolume
- mov cx,OurVolumeLen ; move words
- rep movsw
-
- ; 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.
-
- 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,offset LastResident ; calculate used size
- xor ax,ax
- mov si,offset UsedSpace
- call Bin2Dec ; store it in the messgae
-
- mov ah,E_Counts ; find amount of space left
- int EMM ; in the EMS memory
- or ah,ah
- je FindFree
- jmp GenFail
-
- FindFree: xor ax,ax ; zero high side (the EMM call put
- ; the # of free pages in BX.)
- mov cx,14 ; multiply the # of pages by 16k
- CalcEMMFree: rcl bx,1 ; shift it low side
- rcl ax,1 ; and carry though to high side
- loop CalcEMMFree
-
- mov si,offset Installed2
- call Bin2Dec ; put it into the message!
-
- mov ax,DiskSize ; get sectors of disk space
- sub ax,SecPerFAT ; subtract space used by the FAT
- mov bx,ax ; put that subtotal in bx
-
- 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
-
- mov cx,9 ; multiply the answer by 512
- xor ax,ax ; zero the high side
- CalcDiskFree: rcl 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
-
- mov al,SpeakerFlag ; is there clicking?
- or al,al
- jne ClickOkay
- mov ah,PrintString
- mov dx,offset NoClicking ; tell the user that it's
- int 21h ; been diasbled.
-
- ClickOkay: mov al,MajorVersion
- cmp al,3 ; is it version three?
- je MsgOkay
-
- mov al,eos ; its version two
- mov DriveName,al
-
- 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,offset LastResident ; show DOS where we end
- mov rq.ndadro,ax ; offset
- mov ax,cs ; show DOS were we end
- mov rq.ndadrs,ax ; segment
- mov rq.bpbseg,ax ; show DOS the BPB array
-
- mov rq.units,1 ; we installed one unit
- mov ax,offset BPBArray ; BPB array offset
- mov rq.bpboff,ax
-
- 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 32 ; amount of pages requested
- ; (512k is the default)
- MajorVersion db 3 ; the DOS major version number
-
- OurVolume db 'Niftys_Disk' ; 11-byte volume name
- db 000001000b ; volume label attribute
- db 10 dup (0) ; reserved space
- dw 953
- ;FEDCBA9876543210b
- ;YYYYYYYMMMMDDDDD
- dw 00000111101011111b ; DATE = Oct 31, 1987
- db 6 dup (0) ; more reserved space
-
- OurVolumeLen equ 16
-
- ; ---------------------------------------------------------------------------
- ; Messages
-
- ; These are messages that are used by the initialization section of the
- ; driver.
-
- Banner db cr,lf,"Nifty James Famous E/EMS RAMdisk Drive",cr,lf
- db 'Copyright 1987 by Mike Blaszczak',cr,lf
- db 'Version 1.40ASP of 23 June, 1991',cr,lf
- ifdef V286
- ifdef PCL
- db "(PC's Limited Version)",cr,lf
- else
- db '(Enhanced Processor Version)',cr,lf
- endif
- endif
- db lf,eos
-
- EMMIDString db 'EMMXXXX0'
-
- General db 'Device not installed.',cr,lf,eos
-
- NoEMMThere db 'The EMM is not installed.',cr,lf,lf,eos
-
- EMMError db 'EMM failure during installation.',cr,lf,lf,eos
-
- NoMem db 'No free EMM Memory.',cr,lf,lf,eos
-
- TooBig db 'Requested size too big to fit.',cr,lf,lf,eos
-
- TooSmall db "Can't have zero disk size.",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 EMS 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
-
- driver ends
- end
-
- ; That's a wrap.
- ; Special thanks to Bob Brody and Dr File Finder~~~~~~~~~~~~.
-
-