;DUMMY DEVICE-DRIVER XMSFORCE.ASM
; This is a dummy device-driver that is used to force an extended-memory size
;that will override the amount of actual extended-memory found by the POST. The
;main use of this is to reduce the amount of extended-memory so that demented
;programs that bomb in machines with more than 16 megabytes of total memory can
;be run. (The amount of extended-memory could also be forced higher than the
;amount that actually exists, but no useful purpose for this is yet known.)
; This dummy device driver is loaded early in CONFIG.SYS (before HIMEM.SYS),
;via a line that looks like:
;
; DEVICE=C:\path\XMSFORCE.SYS nnnnn
;
;where "nnnnn" is the forced amount of *extended* memory, in kilobytes. For
;example, if you want to limit *total* memory to 16 megabytes, you would be
;limiting *extended* memory to 15 megabytes, and you would use the value 15360
;for "nnnnn". (Note that 15360 is 15*1024.)
; The value of "nnnnn" that you specify gets loaded into CMOS locations 30h
;and 31h (which is, of course, where the POST put the actual count). This is
;where other drivers (or programs) find the extended-memory size via INT 15h
;Function 88h.
; After loading the CMOS locations with the forced extended-memory size, this
;driver returns a units-count of zero when it exits its driver "initialization"
;routine. Thus, NO part of this driver stays loaded, and thus it consumes ZERO
;memory.
;Written by E. Nicholas Cupery 27 Feb 96
PUBLIC MAIN ;SHOW GLOBAL LABEL ON LINK MAP
PUBLIC BREAK ;SHOW GLOBAL LABEL ON LINK MAP
PUBLIC LAST ;SHOW GLOBAL LABEL ON LINK MAP
EXTRN BLNKTAB:NEAR ;CONVERT ANY TAB TO A SINGLE SPACE
EXTRN LSTRIP:NEAR ;STRIPS LEADING BLANKS
EXTRN TSTRIP:NEAR ;STRIPS TRAILING BLANKS
EXTRN CVTUPR:NEAR ;CONVERTS LINE TO UPPER-CASE
EXTRN NXTOKN:NEAR ;GETS NEXT TOKEN FROM COMMAND-LINE
EXTRN NASC2W:NEAR ;CONVERTS NUMERIC ASCII TO WORD
EXTRN W2HASC:NEAR ;CONVERTS WORD TO HEXADECIMAL ASCII
EXTRN L2DASC:NEAR ;CONVERTS LONGWORD TO DECIMAL ASCII
EXTRN DSPLIN:NEAR ;DISPLAYS ONE LINE ON VIDEO SCREEN
EXTRN KBDWAIT:NEAR ;PROMPTS AND WAITS FOR A KEYPRESS
EXTRN KBPAUSE:NEAR ;PAUSES UNTIL NO CTRL, ALT, OR SHIFT
EXTRN REGDMP:NEAR ;DUMPS ALL REGISTERS
CODSEG SEGMENT PARA PUBLIC 'CODE' ;DEFINE CODE-SEGMENT
ASSUME CS:CODSEG,SS:CODSEG,DS:CODSEG,ES:CODSEG ;SET SEGMENT ASSUMPTIONS
ORG 0h ;ORIGIN FOR DEVICE-DRIVERS
MAIN: ;GLOBAL LABEL FOR LINK MAP
DRVHDR DD -1 ;LINK TO NEXT DEVICE-DRIVER
DW 4000h ;DEVICE ATTRIBUTE WORD
DW STRAT ;OFFSET TO "STRATEGY" ROUTINE
DW INTR ;OFFSET TO "INTERRUPT" ROUTINE
DB 0 ;NUMBER OF UNITS SUPPORTED (*NOT USED!*)
DB 0,0,0,0,0,0,0 ;NOT USED FOR BLOCK DEVICES
;BEGIN GENERAL DATA STORAGE AREA
;
ALIGN 4 ;MAKE SURE ARE ON LONGWORD BOUNDARY
AXSAVE DW 0 ;SAVE CELL FOR ENTRY AX REGISTER
BXSAVE DW 0 ;SAVE CELL FOR ENTRY BX REGISTER
CXSAVE DW 0 ;SAVE CELL FOR ENTRY CX REGISTER
DXSAVE DW 0 ;SAVE CELL FOR ENTRY DX REGISTER
SISAVE DW 0 ;SAVE CELL FOR ENTRY SI REGISTER
DISAVE DW 0 ;SAVE CELL FOR ENTRY DI REGISTER
BPSAVE DW 0 ;SAVE CELL FOR ENTRY BP REGISTER
DSSAVE DW 0 ;SAVE CELL FOR ENTRY DS REGISTER
ESSAVE DW 0 ;SAVE CELL FOR ENTRY ES REGISTER
FSSAVE DW 0 ;SAVE CELL FOR ENTRY FS REGISTER
GSSAVE DW 0 ;SAVE CELL FOR ENTRY GS REGISTER
;
RHPTR DD 0 ;SAVE CELL FOR POINTER TO REQUEST-HEADER
;
COMLINE DB 128 DUP (0) ;STORAGE FOR COMMAND-LINE
;
KBCOUNT DW 0 ;# OF KB OF XMS MEMORY TO FORCE
;
TMTMSG DB 7,'XMSFORCE -- Failure - Too many tokens specified'
DB 7,13,10,0
BTKNMSG DB 7,'XMSFORCE -- Failure - Bad XMS-memory-size specifier token'
DB 7,13,10,0
NCLMSG DB 7,'XMSFORCE -- Failure - No XMS-memory-size specified'
DB 7,13,10,0
AOKMSG DB 'XMSFORCE v1.0 -- XMS SIZE successfully forced',13,10,0
;
;END OF GENERAL DATA STORAGE AREA
;BEGIN DRIVER DISPATCH TABLE
;
ALIGN 4 ;MAKE SURE ARE ON LONGWORD BOUNDARY
DTABLE DW INIT ; 0 -- INITIALIZE DRIVER
DW BADCMD ; 1 -- MEDIA CHECK
DW BADCMD ; 2 -- BUILD BPB
DW BADCMD ; 3 -- IOCTL READ
DW BADCMD ; 4 -- "PLAIN" READ
DW BADCMD ; 5 -- "NONDESTRUCTIVE" READ (NOT USED)
DW BADCMD ; 6 -- INPUT STATUS (NOT USED)
DW BADCMD ; 7 -- FLUSH INPUT BUFFERS (NOT USED)
DW BADCMD ; 8 -- "PLAIN" WRITE
DW BADCMD ; 9 -- "WRITE WITH VERIFY"(USES "WRITE")
DW BADCMD ;10 -- OUTPUT STATUS (NOT USED)
DW BADCMD ;11 -- FLUSH OUTPUT BUFFERS (NOT USED)
DW BADCMD ;12 -- IOCTL WRITE
;;; DW BADCMD ;13 -- DEVICE OPEN (NOT USED)
;;; DW BADCMD ;14 -- DEVICE CLOSE (NOT USED)
;;; DW BADCMD ;15 -- REMOVABLE MEDIA? (NOT USED)
;;; DW BADCMD ;16 -- OUTPUT UNTIL BUSY (NOT USED)
;;; DW BADCMD ;17 -- UNUSED COMMAND (ERROR)
;;; DW BADCMD ;18 -- UNUSED COMMAND (ERROR)
;;; DW BADCMD ;19 -- GENERIC IOCTL (NOT USED)
;;; DW BADCMD ;20 -- UNUSED COMMAND (ERROR)
;;; DW BADCMD ;21 -- UNUSED COMMAND (ERROR)
;;; DW BADCMD ;22 -- UNUSED COMMAND (ERROR)
;;; DW BADCMD ;23 -- GET LOGICAL DEVICE (NOT USED)
;;; DW BADCMD ;24 -- SET LOGICAL DEVICE (NOT USED)
MAXCMD = (($ - DTABLE) / 2) - 1 ;CALCULATE MAXIMUM LEGAL COMMAND-CODE
;
;END OF DRIVER DISPATCH TABLE
;BEGIN DRIVER "STRATEGY" ROUTINE
;
STRAT PROC FAR ;STRATEGY ROUTINE MUST BE FAR PROCEDURE
MOV WORD PTR CS:[RHPTR],BX ;SAVE OFFSET OF NEW REQUEST HEADER
MOV WORD PTR CS:[RHPTR+2],ES ;SAVE SEGMENT OF NEW REQUEST HEADER
RET ;RETURN TO MS-DOS KERNEL
STRAT ENDP ;END OF STRATEGY ROUTINE PROCEDURE
;
;END OF DRIVER "STRATEGY" ROUTINE
;BEGIN DRIVER "INTERRUPT" ROUTINE
;
INTR PROC FAR ;INTERRUPT ROUTINE MUST BE FAR PROCEDURE
PUSHF ;SAVE FLAGS
MOV CS:DSSAVE,DS ;SAVE DS
MOV CS:AXSAVE,AX ;SAVE AX
MOV AX,CS ;GET A COPY OF CS
MOV DS,AX ;POINT DATA-SEGMENT TO CODE-SEGMENT
MOV BXSAVE,BX ;SAVE BX
MOV CXSAVE,CX ;SAVE CX
MOV DXSAVE,DX ;SAVE DX
MOV SISAVE,SI ;SAVE SI
MOV DISAVE,DI ;SAVE DI
MOV BPSAVE,BP ;SAVE BP
MOV ESSAVE,ES ;SAVE ES
CLD ;MAKE SURE STRING ADDRESSES INCREMENT
LES DI,[RHPTR] ;POINT ES:DI TO THE REQUEST HEADER
MOV BL,ES:[DI+2] ;FETCH THE COMMAND-CODE BYTE
MOV BH,0 ;MAKE COMMAND-CODE A FULL-WORD
CMP BX,MAXCMD ;IS THE COMMAND-CODE OUT-OF-RANGE?
JLE OKAY ;BR IF NO (OKAY)
MOV AX,8003h ;"ERROR" + "UNKNOWN COMMAND"
JMP INTRDON ;TAKE COMMON EXIT
OKAY: SHL BX,1 ;CALCULATE BYTE-INDEX IN DISPATCH TABLE
CALL WORD PTR [BX+DTABLE] ;CALL PROPER COMMAND-CODE ROUTINE
INTRDON:LES DI,CS:[RHPTR] ;RESTORE POINTER TO REQUEST-HEADER
OR AX,0100h ;SET "DONE" BIT IN RETURN STATUS
MOV ES:[DI+3],AX ;STORE RETURN STATUS FOR CALLER
MOV AX,AXSAVE ;RESTORE AX
MOV BX,BXSAVE ;RESTORE BX
MOV CX,CXSAVE ;RESTORE CX
MOV DX,DXSAVE ;RESTORE DX
MOV SI,SISAVE ;RESTORE SI
MOV DI,DISAVE ;RESTORE DI
MOV BP,BPSAVE ;RESTORE BP
MOV ES,ESSAVE ;RESTORE ES
MOV DS,DSSAVE ;RESTORE DS (DO THIS LAST !!!)
POPF ;RESTORE FLAGS
RET ;RETURN TO MS-DOS KERNEL
INTR ENDP ;END OF INTERRUPT ROUTINE PROCEDURE
;
;END OF DRIVER "INTERRUPT" ROUTINE
;BEGIN ROUTINE TO HANDLE BAD COMMAND-CODES
;
BADCMD PROC NEAR
MOV AX,8003h ;"ERROR" + "UNKNOWN COMMAND"
RET ;RETURN TO (LOCAL) CALLER
BADCMD ENDP
;
;END OF ROUTINE TO HANDLE BAD COMMAND-CODES
BREAK:
;BEGIN "INITIALIZATION" ROUTINE
;
INIT PROC NEAR ;INITIALIZATION ROUTINE
MOV AX,DS ;GET A COPY OF DS
PUSH DS ;SAVE DS
LDS SI,ES:[DI+18] ;POINT TO THE COMMAND-LINE
MOV ES,AX ;POINT ES TO MY LOCAL DATA
MOV DI,OFFSET COMLINE ;POINT TO MY COMMAND-LINE BUFFER
MOV CH,79 ;MAX CHARS TO COPY
MOV CL,0 ;COMMAND-LINE CHAR-COUNT
;WILL FIRST STRIP OFF THE NAME OF THE DRIVER ITSELF
NAMLOOP:LODSB ;FETCH NEXT CHAR
CMP AL,' ' ;FOUND THE 1ST BLANK YET?
JNE NAMLOOP ;BR IF NO
CLOOP: LODSB ;FETCH NEXT COMMAND-LINE CHAR
CMP AL,13 ;IS IT A CARRIAGE-RETURN?
JE GOTCMD ;BR IF YES (HAVE WHOLE COMMAND)
CMP AL,10 ;IS IT A LINE-FEED?
JE GOTCMD ;BR IF YES (HAVE WHOLE COMMAND)
STOSB ;PUT COMMAND-LINE CHAR LOCALLY
INC CL ;COUNT THIS COMMAND-LINE CHAR
DEC CH ;COUNT THIS ONE MOVED
JNE CLOOP ;BR IF MORE COMMAND-LINE CHARS
GOTCMD: POP AX ;LOCAL DS SEGMENT VALUE
MOV DS,AX ;POINT DS TO LOCAL DATA
MOV ES,AX ;POINT ES TO LOCAL DATA
CMP CL,0 ;IS THERE ANY COMMAND-LINE?
JNE GOTCMDL ;BR IF YES
;IF GOT HERE, WAS AN EMPTY-COMMAND LINE
NOCMDL: MOV SI,OFFSET NCLMSG ;"NO COMMAND LINE"
CALL DSPLIN ;DISPLAY THE MESSAGE
CALL KBPAUSE ;PAUSE IF ANY CTRL, ALT, OR SHIFT
JMP INITDON ;TAKE COMMON EXIT NOW
;WILL NOW LOOK FOR ONE TOKEN, THAT IS THE # OF KB OF XMS TO FORCE
GOTCMDL:MOV AL,CL ;COPY STRING-LENGTH FOR OTHERS
MOV SI,OFFSET COMLINE ;POINT TO COPIED COMMAND-LINE
CALL BLNKTAB ;CONVERT TAB TO SINGLE SPACE
CALL LSTRIP ;STRIP LEADING BLANKS
CALL TSTRIP ;STRIP TRAILING BLANKS
CALL CVTUPR ;CONVERT LINE TO ALL UPPER-CASE
MOV DI,SI ;POINTER TO CLEAN COMMAND-LINE
MOV CL,AL ;LENGTH OF CLEAN COMMAND-LINE
CMP CL,0 ;IS COMMAND-LINE ZERO-LENGTH?
JE NOCMDL ;BR IF YES (NO TOKEN, AFTER ALL)
CALL NXTOKN ;POINT TO THE TOKEN
CMP CL,0 ;ARE THERE ANY OTHER TOKENS?
JE JUST1 ;BR IF NO (OKAY)
MOV SI,OFFSET TMTMSG ;"TOO MANY TOKENS"
CALL DSPLIN ;DISPLAY THE MESSAGE
CALL KBDWAIT ;WAIT FOR ANY KEYBOARD KEY
JMP INITDON ;TAKE COMMON EXIT NOW
JUST1: CALL NASC2W ;CONVERT NUMERIC ASCII # OF KBYTES
JNC CONVOK ;BR IF NO ERROR FOUND
BADKB: MOV SI,OFFSET BTKNMSG ;"BAD XMS MEMORY SIZE SPECIFIER TOKEN"
CALL DSPLIN ;DISPLAY THE MESSAGE
CALL KBDWAIT ;WAIT FOR ANY KEYBOARD KEY
JMP INITDON ;TAKE COMMON EXIT NOW
CONVOK: CMP BX,1 ;IS IT LESS THAN 1 KB TO LEAVE?
JL BADKB ;BR IF YES (ERROR)
MOV KBCOUNT,BX ;STORE THE SIZE-TO-LEAVE IN KBYTES
MOV AL,30h ;CMOS ADDRESS OF LOW-ORDER XMS SIZE
OUT 70h,AL ;ADDRESS LOW-ORDER CMOS STORAGE CELL
MOV AL,BL ;LOW-ORDER XMS SIZE
OUT 71h,AL ;STORE LOW-ORDER XMS-SIZE IN CMOS
MOV AL,31h ;CMOS ADDRESS OF HIGH-ORDER XMS SIZE
OUT 70h,AL ;ADDRESS HIGH-ORDER CMOS STORAGE CELL
MOV AL,BH ;HIGH-ORDER XMS SIZE
OUT 71h,AL ;STORE HIGH-ORDER XMS-SIZE IN CMOS
;WILL NOW INFORM OPERATOR OF SUCCESS
MOV SI,OFFSET AOKMSG ;"XMS SIZE FORCED SUCCESSFULLY"
CALL DSPLIN ;DISPLAY THE MESSAGE
CALL KBPAUSE ;PAUSE IF ANY CTRL, ALT, OR SHIFT
JMP INITDON ;TAKE COMMON EXIT NOW
INITDON:LES DI,[RHPTR] ;POINT ES:DI TO REQUEST HEADER
MOV BYTE PTR ES:[DI+13],0 ;SET NUMBER OF UNITS TO ZERO
MOV WORD PTR ES:[DI+14],0 ;SET BREAK ADDRESS OFFSET TO 0
MOV WORD PTR ES:[DI+16],CS ;SET BREAK ADDRESS SEGMENT TO 0
XOR AX,AX ;RETURN SUCCESS STATUS
RET ;RETURN TO (LOCAL) CALLER
INIT ENDP ;END OF INITIALIZATION ROUTINE
;
;END OF INITIALIZATION ROUTINE
LAST: ;GLOBAL LABEL FOR LINK MAP
CODSEG ENDS ;END OF SEGMENT NAMED "CODSEG"
END ;END OF WHOLE SOURCE MODULE