Farba Research x86 ASM Example

;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

Farba Products WORK