;/*
; *                      Microsoft Confidential
; *                      Copyright (C) Microsoft Corporation 1981 - 1991
; *                      All Rights Reserved.
; */
	PAGE	90,132			;A2
	TITLE	GRTAB - DEFINE ROM GRAPHIC CHARACTERS FOR 128-255

LISTPARM =	1			;0=SUPPRESS LIST; 1=ALLOW LIST

;****************** START OF SPECIFICATIONS *****************************
; MODULE NAME: GRTAB
;	       (MAIN ROUTINE FOR UTILITY "GRAFTABL")

; DESCRIPTIVE NAME: Makes resident a 1K block of data defining the
;		    graphic character set when in graphics mode.

;FUNCTION:  When in normal text mode (BASIC SCREEN 0), the CGA will properly
;	    handle all 255 characters in the screen display.
;	    However, when in graphics mode (BASIC SCREEN 1), then only the
;	    first 128 characters are properly shown on the screen.
;	    The upper 128 characters are defined in graphics mode by
;	    the 8 by 128 byte table pointed to by interrupt 1FH
;	    vector, which is normally set to 0:0, thus producing
;	    garbage on the screen for the upper set of characters.

;	    The purpose of this routine is to define an 8 by 128 byte
;	    table that defines each of these graphic characters in
;	    the upper 128 byte range, and also to put the vector
;	    pointer to this table into the interrupt 1FH.  Once
;	    loaded, this table remains resident.

;	    An interrupt handler is hooked into the INT 2FH vector that
;	    screens INT 2FH calls for the MULTIPLEX ID value of hex B0,
;	    as defined by an equate, MY_MULTIPLEX_NUMBER, in GRATABHAN.
;	    The response indicates that GRAFTABL has been loaded, thus
;	    permitting the redefinition of the original 1K table with
;	    some new specification.

; ENTRY POINT: Entry_Point, JMP from GRTABHAN.SAL at his ORG 100H.
;    LINKAGE: [d:][path] GRAFTABL [437 | 850 | 860 | 863 | 865 | 852 | /STATUS]

; INPUT: (DOS COMMAND LINE PARAMETERS)
;	'/STATUS'- (OR /STA) Identify the table already loaded, if any.
;	'?'	- Display the list of operands supported by GRAFTABL.
;	437  - Install the USA Graphic Character Set
;	(BLANK) - Means same as '437', the 'USA' definition table
;	850  - Install the Multi-lingual Graphic Character Set
;	852  - Install the Latin II Graphic Character Set
;	860  - Install the Portuguese Graphic Character Set
;	863  - Install the Canadian French Graphic Character Set
;	865  - Install the Nordic Graphic Character Set

; EXIT-NORMAL:	ERRORLEVEL 0 - Leaving the 1K block of newly defined
;			       characters resident.  There was no
;			       previously defined character table.
;			       Available RAM reduced by hex 1360 bytes.

;		ERRORLEVEL 1 - No change in size of available RAM storage.
;			       A previously loaded character table exists.
;			       If a new table was requested, it has replaced
;			       the previous table at its original location.

;		ERRORLEVEL 2 - No change in size of available RAM storage.
;			       No previously loaded character table exists.
;
;				(The request for status, with parm="/sta[tus]",
;				or the request for HELP, with parm="?",
;				will result in either code 1 or 2.)

; EXIT-ERROR: ERRORLEVEL 3 - Incorrect parameter.
;			     No action taken, assumes "?" requested HELP.
;			     No change in size of available RAM storage.

;	      ERRORLEVEL 4 - Incorrect version of DOS, DOS 4.0 required

; EFFECTS: Either the desired table is hooked into Interrupt 1FH,
;	   or the identity of the previously loaded table is
;	   displayed.

; INTERNAL REFERENCES:
;    ROUTINES:
;	FINDTYPE - Scan existing table to see who is already loaded
;	MOVTAB - Moves the specified table to overlay a portion of the PSP
;	BADPARMS - Displays error message, and sets error ERRORLEVEL code
;	SENDMSG - passes parms to regs and invokes the system message routine.

;    DATA AREAS:
;	PSP - Contains the DOS command line parameters.  Partly overlayed
;	      with the installation of the character table.
;	WORKAREA - Temporary storage

; EXTERNAL REFERENCES:
;    ROUTINES:
;	GRTABHAN (ENTRY is "HANDLER") - hooked into INT 2FH to identify
;		 that GRAFTABL is loaded and resident.
;	SYSDISPMSG - Uses the MSG parm lists to construct the messages
;		 on STDOUT.
;	SYSLOADMSG - Loads messages, makes them accessable.
;	SYSPARSE - Processes the DOS Command line, finds parms.
;	PARSE - Interfaces with the system Parser.

;    DATA AREAS:
;	GRTABUS - 1k binary table of the USA graphic character set
;	GRTABML - 1k binary table of the Multi-Lingual graphic
;		  character set
;	GRTABPO - 1k binary table of the Portuguese graphic
;		  character set
;	GRTABCF - 1k binary table of the Canada French graphic
;		  character set
;	GRTABNO - 1k binary table of the Nordic graphic character
;		  set
;	GRTABSM - Definition of messages, text and MSG parm lists,
;		  and COPYRIGHT notice
;	GRTABPAR - Control blocks to define the DOS command line parms.
;	MPEXNUM - Byte containing the value of the multiplex number
;		(For ease of patching, if such should ever become needed,
;		this byte is immediately just before the interrupt 2FH
;		handler's entry point, after that handler is loaded.  To
;		change this value here in these source modules, that value
;		is defined in GRTABHAN as the equate: MY_MULTIPLEX_NUMBER.
;		For purposes of patching the GRAFTABL.EXE file, this byte
;		is at offset 5FH into the module.)

; NOTES:
;	 This module should be processed with the SALUT preprocessor
;	 with the re-alignment not requested, as:

;		SALUT GRTAB,NUL

;	 To assemble these modules, the alphabetical or sequential
;	 ordering of segments may be used.

;	 Sample LINK command:

; LINK @GRTABLK.ARF

; Where the GRTABLK.ARF is defined as:
;	 GRTABHAN+    (must be first in this list)
;	 GRTABUS+     (these languages must be linked in this order)
;	 GRTABML+     (Multi-Lingual)
;	 GRTABPO+     (Portuguese)
;	 GRTABCF+     (Canadian French)
;	 GRTABNO+     (Nordic)
;	 GRTABSM+     (contains COPYRIGHT, SYSDISPMSG, SYSLOADMSG code)
;	 GRTABPAR+    (contains PARSE control blocks, code to call SYSPARSE)
;	 GRTABP+      (contains SYSPARSE to decode COMMAND line parms)
;	 GRTAB	      (MAIN ROUTINE)
;	 GRAFTABL;

;	 These modules must be linked in this order.  The load module is
;	 a COM file, to be converted to COM with EXE2BIN.

; REVISION HISTORY:
;	     A000 Version 4.00: add PARSER, System Message Handler,
;	     A001 PTM 382 display "ACTIVE" OR "PREVIOUS" CP.
;	     A002 PTM 474 Avoid duplicate switches
;	     A003 PTM 538 Display parm in error
;	     A004 PTM1821 INCLUDE COPYRIGH.INC moved to msg_service macro.
;	     A005 PTM3262 specify BASESW EQU 1 before PARSE.ASM
;	     A006 PTM3512 PATHGEN START/END LABELS
;	     A007 PTM3621 break up large help msg into smaller messages
;	     A008 PTM3899 Free Environment, close all STDxxx handles
;			when leaving a resident portion
;

;****************** END OF SPECIFICATIONS *****************************
	HEADER	<MACRO DEFINITIONS, LOCAL EQUATES>
	INCLUDE PATHMAC.INC
;				   $SALUT (0,36,41,52)
DOSCALL 			   MACRO FN,SF	   ;;
				   IFNB <FN>	   ;;ARE THERE ANY PARMS AT ALL?
				   IFNB <SF>	   ;;
				   MOV	AX,(FN SHL 8)+SF ;;AH=FN;AH=SF
				   ELSE 	   ;;SINCE THERE IS NO SUB FUNC
				   MOV	AH,FN	   ;;
				   ENDIF	   ;;
				   ENDIF	   ;;
				   INT	21H	   ;;
				   ENDM 	   ;;
; =  =	=  =  =  =  =  =  =  =	=  =
;		  $SALUT (4,19,23,36)
FIXLIST 	  MACRO LP,DOIT    ;;
IF		  LP		   ;;
DOIT				   ;;
ENDIF				   ;;
		  ENDM		   ;;
; =  =	=  =  =  =  =  =  =  =	=  =
HEADER		  MACRO TEXT	   ;;
		  FIXLIST LISTPARM,.XLIST ;;
		  SUBTTL TEXT	   ;;
		  FIXLIST LISTPARM,.LIST ;;
		  PAGE		   ;;
		  ENDM		   ;;
; =  =	=  =  =  =  =  =  =  =	=  =
;		DEFINITION OF FUNCTIONS TO BE REQUESTED OF INT 2FH
GET_INST_STATE	  EQU 0 	   ;FUNCTION = "GET INSTALLED STATE"
WHERE_R_U	  EQU 1 	   ;FUNCTION = "WHERE ARE YOU?"
				   ; REQUESTS VECTOR OF LOADED TABLE BE
				   ; PUT IN VECTOR POINTED TO BY DS:[BX]

;		DOS FUNCTIONS:
SHOW_MSG	  EQU 09H	   ;"SEND MESSAGE TO CONSOLE" DOS FUNCTION
SET_VECT	  EQU 25H	   ;"SET VECTOR" DOS FUNCTION
GET_VERSION	  EQU 30H	   ;"GET DOS VERSION"
RET_RES 	  EQU 31H	   ;"RETURN TO DOS" REMAIN RESIDENT
GET_VECT	  EQU 35H	   ;"GET VECTOR" DOS FUNCTION
CLOSE		  EQU 3EH	   ; CLOSE A FILE HANDLE
FREE_AL_MEM	  EQU 49H	   ; FREE ALLOCATED MEM
RET_FN		  EQU 4CH	   ;"RETURN TO DOS" NOT REMAIN RESIDENT

;		VECTOR TABLE ENTRIES USED
VEC_GRAF_CHAR	  EQU 1FH	   ;VECTOR THAT POINTS TO GRAPHIC CHAR TABLE
VEC_MULTIPLX	  EQU 2FH	   ;VECTOR OF MULTIPLEX HANDLER
VEC_DOS 	  EQU 21H	   ;VECTOR POINTS TO DOS FUNCTIONS
VEC_RET 	  EQU 20H	   ;RETURN TO DOS, WITHOUT RET CODE

;		DOS COMMAND LINE PARAMETERS
RETCODE_QUESTION  EQU -1	   ;VALUE IN BX, IF PARM=?
RETCODE_PARSE_ERROR EQU 1	   ;Value in BX if parse error occurred
ZERO		  EQU 0 	   ;VALUE IN BX IF PARM=/STATUS OR /STA
		  FIXLIST LISTPARM,.XLIST ;
;SLASH		EQU  "/"	    ;REQUEST FOR STATUS
;PARM?		EQU  "?"	    ;REQUEST FOR HELP
;				THE PARM THAT ASKS FOR A PARTICULAR LANGUAGE
;				IS THE TWO CHAR PAIR, DEFINED AS "LANID"
;				IN THE STRUC, "LANGUAGE"
		  FIXLIST LISTPARM,.LIST ;

;		STANDARD FILE HANDLES
STDIN		  EQU 0 	   ;STANDARD INPUT DEVICE
STDOUT		  EQU 1 	   ;STANDARD OUTPUT DEVICE
STDERR		  EQU 2 	   ;STANDARD ERROR OUTPUT DEVICE
STDAUX		  EQU 3 	   ;STANDARD AUX DEVICE
STDPRN		  EQU 4 	   ;STANDARD PRINTER DEVICE

;		OTHER EQUATES
BLANK		  EQU " "	   ;BLANK CHARACTER
NIBBLE_SIZE	  EQU 4 	   ;SHIFT COUNT TO MOVE ONE NIBBLE		;
		  FIXLIST LISTPARM,.XLIST ;					;
;CASEBIT	EQU  0FFH-20H	    ;TURN OFF THIS BIT TO MAKE UPPER CASE
;LOWERA 	EQU  'a'	    ;LOWER CASE CHARACTER 'a'
;LOWERZ 	EQU  "z"	    ;LOWER CASE CHARACTER 'z'
		  FIXLIST LISTPARM,.LIST ;					;

		  EXTRN HANDLER_SIZE:ABS ;THE BYTE SIZE REQUIREMENTS
		  EXTRN CPID_L:ABS ;NO. BYTES IN EACH CPID ENTRY

VECTOR		  STRUC 	   ;DEFINITION OF ANY DOUBLE WORD VECTOR POINTER
VECOFF		  DW  ? 	   ;OFFSET PORTION OF VECTOR POINTER
VECSEG		  DW  ? 	   ;SEGMENT PORTION OF VECTOR POINTER
VECTOR		  ENDS
; = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
LANGUAGE	  STRUC 	   ;DEFINITION OF EACH LANGUAGE TABLE
LANCHAR 	  DB  1024 DUP(?)  ;8 BYTES PER EACH OF 128 CHARACTERS
LANID		  DW  ? 	   ;TWO BYTE CODEPAGE ID, TO MATCH
				   ; GRAFTABL CMD LINE PARM
LANNAME 	  DB  14 DUP(?)    ;ASCIIZ STRING NAME OF LANGUAGE
LANGUAGE	  ENDS

TABSIZE 	  EQU SIZE LANCHAR + SIZE LANID + SIZE LANNAME ;SIZE OF
				   ; EACH GRAPHIC CHAR TABLE + ID + NAME
; = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
;		VALUES FOR THE MSG_DESC CONTROL BLOCK
ONE_SUBS	  EQU 1 	   ;ONE VARIABLE FIELD IN MESSAGE		;
TWO_SUBS	  EQU 2 	   ;TWO VARIABLE FIELDS IN MESSAGE		;
CLASS_1 	  EQU 0100H	   ;CLASS 1 (DOS EXTENDED ERRORS)		;
CLASS_2 	  EQU 0200H	   ;CLASS 2 (PARSE ERRORS)			;
CLASS_A 	  EQU 0A00H	   ;CLASS A TYPE MESSAGE			;

;THIS MESSAGE DESCRIPTOR CONTROL BLOCK IS GENERATED, ONE PER MESSAGE,
;TO DEFINE THE SEVERAL PARAMETERS THAT ARE EXPECTED TO BE PASSED IN
;CERTAIN REGISTERS WHEN THE SYSDISPMSG FUNCTION IS TO BE INVOKED.

MSG_DESC	  STRUC 	   ;						;
MSG_NUM 	  DW  0 	   ;MESSAGE NUMBER (TO AX)			;
MSG_HANDLE	  DW  STDOUT	   ;HANDLE OF OUTPUT DEVICE (TO BX)		;
MSG_SUBLIST	  DW  0 	   ;POINTER TO SUBLIST (TO SI)			;
MSG_COUNT	  DW  0 	   ;SUBSTITUTION COUNT (TO CX)			;
MSG_CLASS	  DW  CLASS_A	   ;MESSAGE CLASS (IN HIGH BYTE, TO DH) 	;
				   ; LOW BYTE HAS 0 (FUNCTION "NO INPUT", TO DL)
MSG_DESC	  ENDS		   ;						;
; = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
SUBLIST 	  STRUC 	   ;						;
SUB_SIZE	  DB  SIZE SUBLIST ;SUBLIST SIZE (POINTER TO NEXT SUBLIST)	;
SUB_RES 	  DB  0 	   ;RESERVED					;
				   ;NEXT FIELD IS TO BE USED AS A DOUBLE WORD
SUB_VALUE	  DW  0 	   ;TIME, DATE, OR PTR TO DATA ITEM		;
SUB_VALUE_SEG	  DW  0 	   ;SEG ID OF PTR				;
				   ;(ABOVE FIELD MUST BE FILLED AT EXECUTION TIME
				   ; IF THIS IS A .COM FILE)
SUB_ID		  DB  0 	   ;N OF %N					;
SUB_FLAGS	  DB  0 	   ;DATA TYPE FLAGS				;
SUB_MAX_WIDTH	  DB  0 	   ;MAXIMUM FIELD WIDTH (0=UNLIMITED)		;
SUB_MIN_WIDTH	  DB  0 	   ;MINIMUM FIELD WIDTH 			;
SUB_PAD_CHAR	  DB  " "	   ;CHARACTER FOR PAD FIELD			;
SUBLIST 	  ENDS		   ;						;
; = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
DEFPSP		  STRUC
		  DB  2CH DUP(?)
ENVIRON_SEG	  DW  ? 	   ; SEG ID OF ENVIRONMENT
		  DB  32H DUP(?)   ; SET WHERE
MOV_DEST	  DB  20H DUP(?)   ; INT HANDLER IS TO BE MOVED TO
				   ;AT MOV_DEST+HANDLER_SIZE WILL GO THE
				   ; TABLE ITSELF
PARMCNT 	  DB  ? 	   ;COUNT OF CHARS IN DOS COMMAND LINE PARM LIST
PARMDOS 	  DB  127 DUP(?)   ;DOS COMMAND LINE PARM TEXT
DEFPSP		  ENDS

PSPSIZE 	  EQU SIZE DEFPSP  ;SIZE OF THE PSP ITSELF
SHIFT_SIZE	  EQU PSPSIZE - MOV_DEST ; HOW FAR HANDLER/TABLE MOVED
				   ;WHEN THEY BECAME RESIDENT
SHIFT_SIZE_SEG	  EQU SHIFT_SIZE SHR NIBBLE_SIZE ;DIVIDE BY 16			;
				   ; TO CONVERT BYTES TO SEGS
; = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
.LIST
		  HEADER <EXTERNALS AND WORKAREA>
;    $SALUT (4,6,12,28)

CSEG SEGMENT WORD PUBLIC
     ASSUME CS:CSEG	   ;AS A .COM FILE, THE DOS
     ASSUME DS:CSEG	   ;  LOADER HAS ALREADY
     ASSUME ES:CSEG	   ;  INITIALIZED ALL THESE
     ASSUME SS:CSEG	   ;  SEGMENT REGISTERS

GRTAB_BASE EQU $	   ;FOR EASE IN DEBUGGING				;
     PUBLIC GRTAB_BASE	   ; TELL LINK MAP WHERE THIS IS			;

     EXTRN END_PSP:BYTE    ;MOVE RESIDENT CODE FROM HERE TO "MOV_DEST"
     EXTRN MPEXNUM:BYTE    ;MULTIPLEXOR ID VALUE
     EXTRN HANDLER:NEAR    ;ENTRY TO INT2FH HANDLER
     EXTRN PREV_OWN:DWORD  ;VECTOR TO SAVE PREVIOUS INT2FH OWNER
     EXTRN PATCH_OFF:WORD  ;THESE TWO PATCH LOCATION IN GRTABHAN.SAL
     EXTRN PATCH_SEG:WORD  ; ARE TO RECEIVE LOCATION WHERE TABLE IS MOVED TO
     EXTRN TABLEUS:BYTE    ;START OF THE USA CHAR TABLE
     EXTRN COPYRIGHT:BYTE  ;USED TO MARK THE END OF THE ARRAY OF CHAR TABLES
			   ; THE COPYRIGHT TEXT IS ITSELF UN-REFERENCED
     FIXLIST LISTPARM,.XLIST ;							;
;    EXTRN MSG1:WORD	   ;NON-STD VER OF GRAPHIC CHAR SET TABLE IS ALREADY LOADED
;    EXTRN MSG2:WORD	   ;XXX VER OF GRAPHIC CHAR SET TABLE IS ALREADY LOADED
;    EXTRN MSG3:WORD	   ;XXX VER OF GRAPHIC CHAR SET HAS JUST BEEN LOADED
;    EXTRN MSG4:WORD	   ;INCORRECT PARAMETER
;    EXTRN MSG5:WORD	   ;NO VER OF GRAPHIC CHAR SET TABLE IS ALREADY LOADED
;    EXTRN MSG6:WORD	   ;INCORRECT DOS VERSION
;    EXTRN MSG7:WORD	   ;DOS COMMAND LINE PARAMETERS SUPPORTED:
;    EXTRN MSG8:WORD	   ;ID - LANGUAGE NAME
;    EXTRN LANGUAGE_NAME:BYTE ;14 BYTE AREA TO RECEIVE ASCIIZ OF LANGUAGE NAME
     FIXLIST LISTPARM,.LIST ;
     EXTRN MSGNUM_EXTERR:WORD ;ALL DOS EXTENDED ERRORS
     EXTRN MSGNUM_PARSE:WORD ;ALL PARSING ERRORS
     EXTRN SUBLIST_PARSE:WORD ;DEFINES THE BAD PARM AS %0
     EXTRN MSGNUM_VER:WORD ;"Incorrect DOS Version"
     EXTRN MSGNUM_ACTIVE:WORD ;"Active Code Page: %1"
     EXTRN MSGNUM_PREVIOUS:WORD ;"Previous Code Page: %1"
     EXTRN CPID:BYTE	   ;4 BYTE STRING OF CODE PAGE ID
     EXTRN SLIST_23:WORD   ;SET VECTOR TO POINT TO "NONE" OR "NON-STANDARD",
			   ; OR TO ONE OF THE "CPID" ENTRIES
     EXTRN MSGNUM_NONE:WORD ;"None"
     EXTRN MSGNUM_NSTD:WORD ;"Non-Standard"
     EXTRN MSGNUM_HELP_OPTIONS	: WORD	; MSG_DESC for all options messages
     EXTRN MSG_OPTIONS_FIRST	: ABS	; number of first message
     EXTRN MSG_OPTIONS_LAST	: ABS	;  and last
     EXTRN SYSDISPMSG:NEAR ;DISPLAY MESSAGE SUBROUTINE
     EXTRN SYSLOADMSG:NEAR ;LOAD MESSAGE SUBROUTINE
     EXTRN SYSGETMSG:NEAR  ;GET MESSAGE SUBROUTINE
     EXTRN PARSER:NEAR	   ;INVOKES SYSTEM DOS COMMAND LINE PARSER
;		$SALUT (4,17,22,32)
WHERE		DD   0	       ;VECTOR POINTING TO PREVIOUS GRAFTABL TABLE
			       ; VECTOR AS SET BY "GRTABHAN" MULTIPLEX HANDLER.
			       ;THIS IS NOT NECESSARILY WHAT INT 1FH HAS.
ADPSP		DW   0	       ;SEG ID OF PSP.
			       ; CHANGED TO SHOW ITS SHIFTED LOCATION
		FIXLIST LISTPARM,.XLIST ;
;ACC		 DW   0 	;ACCUMULATES VALUE OF CODEPAGE ID
;TEN		 DW   10	;CONSTANT TO CONVERT ASCII DECIMAL TO BINARY
;TOKEN		 DB   "    "	;RECEIVES THE TEXT OF DOS PARMS, AND OVERFLOW AREA
;TOKEN_SIZE	 EQU  $-TOKEN
		FIXLIST LISTPARM,.LIST ;
RESSIZE_PARA	DW   0	       ;SIZE OF RESIDENT PORTION, HANDLER+TABLE, IN PARAGRAPHS
RESSIZE_BYTE	DW   0	       ;SIZE OF RESIDENT PORTION, HANDLER+TABLE, IN BYTES
SAVESI		DW   0	       ;REMEMBER WHERE CURRENT TABLE STARTS
CPIDPOINTER	DW   CPID      ;POINTER TO ENTRY IN CPID TABLE OF PREVIOUS ENTRY
ACTIVECPID	DW   CPID      ;POINTER TO ENTRY IN CPID TABLE OF ACTIVE ENTRY
		PUBLIC ACTIVECPID
IDXXX		DW   0	       ;ID OF PREVIOUSLY LOADED TABLE
INSTALLED	DB   0	       ;OUTPUT OF INT 2FH, "FF"=GRAFTABL ALREADY INSTALLED
			       ; AND 'WHERE' WILL POINT TO PREVIOUS TABLE
			       ;"00"=GRAFTABL NOT INSTALLED

;		DEFINITIONS OF VALUES OF CONTENTS OF "INSTALLED".
LOADED		EQU  0FFH      ;GRAFTABLE IS ALREADY IN VECTOR


;		DEFINITIONS OF VALUES OF CONTENTS OF 'EXITFL'.
;		THESE VALUES ARE THE RETURN CODES CHECKED BY ERRORLEVEL.
EXVER		EQU  4	       ;RETURN TO DOS, INVALID DOS VERSION
EXPAR		EQU  3	       ;RETURN TO DOS, INVALID DOS COMMAND LINE PARMS
			       ; OR SYSTEM MESSAGE HANDLER ERROR
EXNONE		EQU  2	       ;RETURN TO DOS, NO TABLE WAS LOADED
EXDOS		EQU  1	       ;RETURN TO DOS, CHAR TABLE PREVIOUSLY LOADED
EXRES		EQU  0	       ;RETURN TO DOS, LEAVING THE 1K BLOCK OF CHAR RESIDENT

EXITFL		DB   EXDOS     ;ERRORLEVEL RETURN CODE, INITIALLY SET TO "EXDOS"
		PUBLIC EXITFL
BAD1F		DB   0	       ;FLAG, GET SET TO "RESET" IF GRAFTABL FONT IS
RESET		EQU  1	       ; LOADED, BUT 1FH DOES NOT POINT TO IT
; = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
		PATHLABL GRTAB
;  $SALUT (3,4,9,32)
   HEADER <EXECUTABLE CODE, INITIALIZATION>
MAIN PROC NEAR
ENTRY_POINT LABEL NEAR	       ;JUMPED TO FROM ENTRY POINT IN GRTABHAN
   PUBLIC ENTRY_POINT
;VERSION CHECKING AND SYSTEM MESSAGE INITIALIZATION

   CALL SYSLOADMSG	       ; INIT SYSMSG HANDLER

;  $IF	C		       ; IF THERE WAS A PROBLEM
   JNC $$IF1
      CALL SYSDISPMSG	       ; LET HIM SAY WHY HE HAD A PROBLEM

      MOV  EXITFL,EXVER        ; TELL ERRORLEVEL BAD DOS VERSION
      MOV  AH,RET_FN	       ;RETURN TO DOS, DO NOT STAY RESIDENT
;  $ELSE		       ; SINCE SYSDISPMSG IS HAPPY
   JMP SHORT $$EN1
$$IF1:
      CLD		       ;CLEAR DIRECTION TO AUTO-INCREMENT
      CALL MAIN_GRTAB	       ;PERFORM REST OF GRAFTABL FUNCTIONS
			       ;AH=DOS FUNCTION OF TYPE OF RETURN TO DOS
;  $ENDIF		       ;LOAD OF SYS MSGS OK?
$$EN1:
   MOV	AL,EXITFL	       ;PASS BACK ERRORLEVEL RET CODE
   INT	VEC_DOS 	       ;(21H) RETURN TO DOS WITH RET CODE

   INT	VEC_RET 	       ;(20H) IF ABOVE NOT WORK, EXIT ANYWAY
MAIN ENDP
; = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
   HEADER <MAIN_GRTAB - PERFORM FUNCTIONS INDICATED BY PARMS>
MAIN_GRTAB PROC NEAR
   PUBLIC MAIN_GRTAB
;INPUT: SYSTEM MESSAGE HANDLER IS LOADED, DOS VERSION IS CORRECT.
;OUTPUT: AH=FUNCTION NUMBER FOR PROPER TYPE OF RETURN TO DOS.

   MOV	SLIST_23.SUB_VALUE_SEG,CS ;DO MY OWN SEGMENT FIXUPS
   MOV	SUBLIST_PARSE.SUB_VALUE_SEG,CS ;

   MOV	ADPSP,ES	       ;SAVE SEG ID OF PSP
   SUB	ADPSP,SHIFT_SIZE_SEG   ;WHERE THE SEG BASE OF MOVED INT HANDLER WILL BE
   PUSH ES		       ;SAVE PSP SEGID

   MOV	AX,HANDLER_SIZE        ;GET SIZE OF MULTIPLEX HANDLER (NOT PSP)
   ADD	AX,TABSIZE	       ;ADD THE SIZE OF THE CHAR TABLE
   MOV	RESSIZE_BYTE,AX        ;SAVE BYTE LENGTH OF HANDLER + CHAR TABLE
			       ; THIS IS THE AMOUNT TO BE SHIFTED
   ADD	AX,OFFSET MOV_DEST     ;ADD ON PART OF PSP NOT OVERLAID BY CODE MOVE
   MOV	CL,NIBBLE_SIZE	       ;DIVIDE
   SHR	AX,CL		       ; BYTE SIZE BY 16
   MOV	RESSIZE_PARA,AX        ;SAVE PARA LENGTH OF HANDLER + CHAR TABLE
			       ; AND FIRST PART OF PSP
			       ;THIS IS THE AMOUNT TO STAY RESIDENT

			       ;PATCH INT2FH HANDLER IN CASE LATER
			       ; IT BECOMES RESIDENT AND IS ASKED
			       ; TO TELL WHERE ITS CHAR TABLE WENT
   MOV	AX,ADPSP	       ;TELL INT2FH HANDLER WHERE TO SAY HIS TABLE IS
   MOV	PATCH_SEG,AX
   MOV	PATCH_OFF,OFFSET TABLEUS ;TELL INT2FH HANDLER
			       ; WHERE TO SAY HIS TABLE IS
   MOV	AH,GET_VECT	       ;REQUEST "GET VECTOR" FUNCTION
   MOV	AL,VEC_MULTIPLX        ; FOR INTERRUPT 2FH
   INT	VEC_DOS 	       ;OUTPUT: ES:BX = VECTOR OF CONTENTS

   ASSUME ES:NOTHING	       ;SINCE ORIG PSP POINTER IS LOST
			       ;PATCH INT2FH HANDLER TO TELL HIM WHERE
			       ; THE CURRENT INT2FH HANDLER IS IN CASE
			       ; THIS NEW HANDLER REPLACES THE OLD ONE
   MOV	PREV_OWN.VECOFF,BX     ;REMEMBER WHERE THE PREVIOUS
   MOV	PREV_OWN.VECSEG,ES     ; OWNER OF INT 2FH WAS

   POP	ES		       ;RESTORE THE PSP SEGID BAS
   ASSUME ES:CSEG	       ;REG IS RESET BACK LIKE IT WAS
   CALL FINDTYPE	       ;SET 'IDXXX' TO SHOW
			       ; WHAT IS IN THE LOW RAM TABLE NOW

   FIXLIST LISTPARM,.XLIST     ;
;  CALL PROCESS 	       ;RESPOND TO REQUEST DEFINED BY PARMS
;			       ; SETTING 'EXITFL' TO INDICATE RESULTS
   FIXLIST LISTPARM,.LIST      ;
   CALL PARSER		       ;LOOK AT DOS COMMAND LINE PARMS

			       ;IF A CODEPAGE NUMBER WAS SPECIFIED
			       ;   BX = OFFSET TO LANGUAGE TABLE TO BE LOADED
			       ;   DX = INTEGER VALUE OF CODEPAGE SPECIFIED
			       ;IF /STATUS WAS SPECIFIED
			       ;   BX = 0
			       ;IF /? WAS SPECIFIED
			       ;   BX = -1
			       ; If parse error occurred
			       ;   BX = 1

   CALL DOFUNCTION	       ; RESPOND TO PARMS
			       ; SETTING 'EXITFL' TO INDICATE RESULTS


   FIXLIST LISTPARM,.XLIST     ;
;  $ELSE		       ;SINCE THE DOS VERSION IS NOT ACCEPTABLE,
;				THE LOCATION, "MSG6", CONTAINS OFFSET TO TEXT OF
;     MOV  DX,MSG6	       ;"INCORRECT DOS VERSION" (THIS MSG ENDS WITH "$")
;			       ;GET POINTER TO MESSAGE TEXT TO DS:DX
;     MOV  AH,SHOW_MSG	       ;'WRITE TO CONSOLE' DOS FUNCTION
;     INT  VEC_DOS
;
;     MOV  EXITFL,EXVER        ;SET ERROR CODE TO ERROR TERMINATION, BAD VERSION
;  $ENDIF		       ;CHECK DOS VERSION NUMBER
   FIXLIST LISTPARM,.LIST      ;

   CMP	EXITFL,EXRES	       ;CHECK THE EXIT TYPE FLAG
;  $IF	E		       ;IF NEW TABLE HAS JUST BEEN LOADED
   JNE $$IF4
;THE INT2FH HANDLER AND THE CHAR TABLE HAVE ALREADY BEEN MOVED INTO THE PSP.
;CHANGE THE TWO VECTORS TO POINT TO WHERE THEN WENT.
;THE TWO VECTORS ARE:
;      2FH  - MULTIPLEX INTERRUPT HANDLER
;      1FH  - 128 CHAR DEFINITION TABLE

      PUSH DS

      MOV  AX,ADPSP
      MOV  DS,AX	       ;FIND SEG WHERE TABLE WENT
      ASSUME DS:NOTHING

;SET INTERRUPT VECTOR 1FH TO POINT TO WHERE THE CHAR TABLE IS
      LEA  DX,TABLEUS	       ;WHERE CHAR TABLE IS
			       ; DS:DX = VECTOR POINTING TO CHAR TABLE AFTER MOVE
      MOV  AH,SET_VECT	       ;FUNCTION TO MOVE DS:DX INTO VECTOR FOR INT 1FH
      MOV  AL,VEC_GRAF_CHAR    ;INTERRUPT NUMBER TO RECEIVE NEW CONTENTS
      INT  VEC_DOS	       ;CHANGE THE VECTOR

;SET INTERRUPT VECTOR 2FH (MULTIPLEXOR) TO WHERE THE INTERRUPT HANDLER IS
      LEA  DX,HANDLER	       ;SET DS:DX = INT 2FH INTERRUPT HANDLER
      MOV  AH,SET_VECT	       ;FUNCTION TO MOVE DS:DX INTO VECTOR FOR INT 2FH
      MOV  AL,VEC_MULTIPLX     ;CHANGE THE CONTENTS OF THE MULTIPLEX INTERRUPT
      INT  VEC_DOS	       ;HOOK IN THE INTERRUPT HANDLER

      POP  DS		       ;RESTORE SEG REG
      ASSUME DS:CSEG

;FREE THE SEGMENT CONTAINING THE ENVIRONMENT
      PUSH ES		       ; SAVE NORMAL ES
      MOV  AX,DS:ENVIRON_SEG   ; FROM PSP GET SEG OF ENVIRONMENT
      MOV  ES,AX	       ; SET SEGREG TO POINT TO ENVIRONMENT SEG
      ASSUME ES:NOTHING        ; ES NOW HAS SEG OF ENVIRONMENT

      MOV  AH,FREE_AL_MEM      ; (49H) FREE MEM ALLOC TO ENVIRON
			       ;ES = SEGID OF BLOCK BEING RETURNED
      INT  VEC_DOS	       ; FREE ENVIRONMENT
			       ;IF CARRY IS SET, THERE WAS A PROBLEM
			       ;THIS IS NOT CHECKED, IGNORING ANY ERROR

      POP  ES		       ; RESTORE ES TO NORMAL VALUE
      ASSUME ES:CSEG	       ;

;LOOP THRU ALL THE STANDARD EXTERNALLY OPENED HANDLES, 0-4,
; AND CLOSE EACH ONE BEFORE RETURNING TO DOS, STAYING RESIDENT.
      MOV  BX,STDIN	       ; START WITH FIRST STANDARD HANDLE
;     $DO  COMPLEX
      JMP SHORT $$SD5
$$DO5:
	 INC  BX	       ; SELECT NEXT HANDLE
;     $STRTDO
$$SD5:
	 MOV  AH,CLOSE	       ; (3EH) CLOSE A FILE HANDLE
			       ; BX=FILE HANDLE
	 INT  VEC_DOS	       ; CLOSE THE HANDLE
			       ;CARRY IS SET IF A PROBLEM WITH CLOSE
			       ;IN THIS CASE, ERRORS ARE IGNORED.

	 CMP  BX,STDPRN        ; WAS THIS THE LAST ONE?
;     $ENDDO E		       ; QUIT IF LAST ONE
      JNE $$DO5

;SET UP TO LEAVE A PORTION OF GRAFTABL RESIDENT:
; THE MULTIPLEXOR INTERRUPT HANDLER, AND
; THE FIRST 1K PIXEL TABLE.

      MOV  DX,RESSIZE_PARA     ;MARK END OF AREA TO REMAIN RESIDENT
      MOV  AH,RET_RES	       ;FUNCTION TO RETURN TO DOS, BUT STAY RESIDENT
;  $ELSE		       ;SINCE NEW TABLE NOT LOADED
   JMP SHORT $$EN4
$$IF4:
			       ; MAYBE BECAUSE TABLE ALREADY LOADED
			       ; OR MAYBE BECAUSE BAD DOS COMMAND LINE PARMS
      MOV  AH,RET_FN	       ;RETURN TO DOS, DO NOT STAY RESIDENT
;  $ENDIF
$$EN4:
   RET			       ;RETURN TO CALLER
MAIN_GRTAB ENDP
; = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
   HEADER <LOCAL SUBROUTINES, SCAN TO SEE WHO IS ALREADY LOADED>
FINDTYPE PROC NEAR
   PUBLIC FINDTYPE
;OUTPUT =  IDXXX HAS 2 CHAR ID OF WHATEVER IS ALREADY LOADED
;	     OR IS JUST 0 IF 1FH DOES NOT POINT TO A GRAFTABL DEFINED FONT.
;	   SUBLIST_23 IS SET TO CONTAIN THE ORIGINAL CP PREVIOUSLY LOADED
;	   OR "NONE".  THIS SUBLIST IS USED BY BOTH MESSAGES 2 AND 3
;	   WHICH STATE THE "ACTIVE CODEPAGE" AND "PREVIOUS CODEPAGE".
;	   THE MESSAGE ACTUALLY TO USE THIS SUBLIST IS SELECTED IN "DOFUNCTION"
;	   AFTER INTERROGATING THE PARMS, SO THAT "/STATUS" & "?" WILL REFER TO
;	   THE "ACTIVE CODEPAGE", AND OTHER REQUESTS WILL SAY "PREVIOUS CP".

   ASSUME ES:NOTHING
   MOV	AH,MPEXNUM	       ;REQUEST "GET INSTALLED STATE"
   MOV	AL,GET_INST_STATE      ;IDENTIFY THAT IT IS ME I AM LOOKING FOR
			       ;AH=MULTIPLEXOR NUMBER, AL=FUNCTION REQUEST
   INT	VEC_MULTIPLX	       ;SEE IF I AM ALREADY LOADED

   MOV	INSTALLED,AL	       ;SAVE THE "INSTALLED?" INDICATOR
   CMP	AL,LOADED
;  $IF	E		       ;IF I AM ALREADY INSTALLED
   JNE $$IF10
;		GRAFTABL HAS BEEN LOADED.  SEVERAL CONDITIONS MAY EXIST:
;		  INT 1FH STILL POINTS TO THE TABLE GRAFTABL LOADED
;			AND THAT TABLE HAS NOT BEEN ALTERED.
;		  INT 1FH STILL POINTS TO THE TABLE GRAFTABL LOADED
;			BUT THE CONTENTS OF THAT TABLE HAS BEEN ALTERED.
;		  INT 1FH NO LONGER POINTS TO THE TABLE GRAFTABL LOADED,
;			BUT POINTS TO SOME OTHER CHARACTER TABLE.

;		NOW WE ASK THE MULTIPLEXOR HANDLER OF GRAFTABLE, SINCE WE
;		KNOW HE HAS BEEN LOADED, TO TELL US WHERE HIS GRAFTABL
;		CHARACTER FONT TABLE IS IN RESIDENT RAM, BY SETTING THE
;		DWORD AT "WHERE" TO POINT TO THE OLD GRAFTABL FONT TABLE.

			       ;AH STILL SHOULD HAVE "MY_MULTIPLEX_NUMBER"
      MOV  AL,WHERE_R_U        ;ASK "WHERE ARE YOU"
      LEA  BX,WHERE	       ;PASS POINTER TO VECTOR TO RECEIVE ANSWER
			       ;AH=MULTIPLEXOR NUMBER, AL=FUNCTION REQUEST
			       ;DS:BX POINTS TO "WHERE", TO RECEIVE RESPONSE
      INT  VEC_MULTIPLX        ;SET VECTOR "WHERE" TO POINT TO HIS TABLE

;		COMPARE THE CONTENTS OF VECTOR 1FH WITH THE VECTOR AT "WHERE"
;		WHICH POINTS TO THE CURRENTLY LOADED TABLE OF GRAFTABL.
;		THE USER MAY HAVE ALTERED THE CONTENTS OF VECTOR 1FH AFTER
;		HAVING PREVIOUSLY LOADED A GRAFTABL CHARACTER FONT.
      XOR  AX,AX	       ;ZERO A REG
      MOV  ES,AX	       ;LOOK AT THE VECTOR TABLE
			       ;ES IS STILL ASSUMED "NOTHING"
      MOV  AX,ES:[VEC_GRAF_CHAR * DWORD]
      CMP  AX,WHERE.VECOFF
;     $IF  E,AND
      JNE $$IF11
      MOV  AX,ES:[VEC_GRAF_CHAR * 4]+2
      CMP  AX,WHERE.VECSEG
;     $IF  E		       ;IF THE 1FH VECTOR IS POINTING TO A GRAFTABLE
      JNE $$IF11
			       ; DEFINED CHARACTER TABLE.
			       ;NOW TO FIND OUT WHICH TABLE HE IS POINT TO.
	 LEA  SI,TABLEUS       ;GET WHERE NEW TABLE IS IN THIS LOAD MODULE
			       ; DS:SI POINT TO NEW TABLE
;		IN THIS SEARCH, WE ARE GOING TO STEP THRU THE ARRAY OF
;		TABLES WE HAVE DEFINED, COMPARING EACH ONE WITH WHATEVER "WHERE"
;		POINTS TO, TO SEE IF WHAT WAS PREVIOUSLY LOADED IS LIKE ONE
;		OF THE TABLES WE HAVE DEFINED HERE.  A MATCH LETS US IDENTIFY
;		THE ONE PREVIOUSLY LOADED; A NON-MATCH MEANS THAT WHATEVER
;		THAT WAS PREVIOUSLY LOADED HAS APPARENTLY BEEN ALTERED,
;		SO WHATEVER IT WAS, WE CANNOT SAY WHICH TABLE IT IS, AND SO WE
;		IDENTIFY IT AS SIMPLY A NON-STANDARD TABLE.  THIS SEARCH WILL
;		SELECT WHICH OF TWO MESSAGES TO BE DISPLAYED.
	 MOV  SAVESI,SI        ;SAVE WHERE TABLE STARTS
;	 $SEARCH COMPLEX
	 JMP SHORT $$SS12
$$DO12:
	    ADD  CPIDPOINTER,CPID_L ;BUMP TO NEXT ENTRY IN CPID TABLE.
	    MOV  SI,SAVESI     ;GO BACK TO START OF TABLE
	    ADD  SI,TABSIZE    ;BUMP TO NEXT TABLE
	    MOV  SAVESI,SI     ;SAVE WHERE TABLE STARTS
;	 $STRTSRCH
$$SS12:
;		   THIS CHANGES ES TO PREVIOUSLY LOADED TABLE

	    LES  DI,WHERE      ;GET THE CONTENTS OF THE VECTOR
			       ;ES:DI POINTS TO THE OLD TABLE OF CHARACTERS
			       ;ES IS STILL ASSUMED "NOTHING"
	    MOV  CX,TABSIZE    ;GET SIZE OF TABLE
	    REP  CMPSB	       ;COMPARE ES:[DI] WITH DS:[SI]
;	 $EXITIF E	       ;IF THIS TABLE ALREADY LOADED
	 JNE $$IF12
	    LES  DI,WHERE      ;RESTORE THE POINTERS TO RESIDENT TABLE
	    MOV  AX,ES:[DI].LANID ;PICK UP 2 CHAR ID OF PREVIOUS TABLE
	    MOV  IDXXX,AX      ;REMEMBER WHAT THAT WAS PREVIOUSLY LOADED

	    PUSH DS	       ;SET UP THE ES REG TO
	    POP  ES	       ;  BE THE SAME AS DS, THE COMMON SEGMENT
	    ASSUME ES:CSEG     ;

	    MOV  AX,CPIDPOINTER ;GET OFFSET TO CURRENT CPID ENTRY
	    MOV  SLIST_23.SUB_VALUE,AX ;PUT CP NAME IN MSG
			       ;SO MSG IS "PREVIOUS CODE PAGE: XXX"
	    FIXLIST LISTPARM,.XLIST ;
;	     LEA  DI,LANGUAGE_NAME ;TO THE MESSAGES' VARIABLE NAME OF LANGUAGE FIELD
;	     MOV  SI,SAVESI	;FROM THE NAME OF THE LANGUAGE
;	     ADD  SI,OFFSET LANNAME ; IN THE NEW TABLE
;	     MOV  CX,SIZE LANNAME ;PASS NAME OF LANGUAGE
;	     REP  MOVSB 	;  TO THE MESSAGES
;				; AND THEN TELL THE OPERATOR WHAT WAS FOUND
;	     LEA  AX,MSG2	;XXX VER OF GRAPHIC CHAR SET TABLE IS ALREADY LOADED
	    FIXLIST LISTPARM,.LIST ;						;
;	 $ORELSE	       ;SINCE THIS VERSION IS NOT ALREADY LOADED
	 JMP SHORT $$SR12
$$IF12:
	    ASSUME ES:NOTHING  ;SINCE BASE REG NOT RESET YET,
	    LEA  AX,COPYRIGHT  ;WHICH IS IMMEDIATELY FOLLOWING THE LAST TABLE
	    CMP  AX,SI	       ;HAVE WE INCREMENTED THE INDEX TO BE BEYOND
			       ; THE LAST TABLE TO BE CHECKED
;	 $ENDLOOP BE	       ;QUIT IF NO NEXT TABLE TO BE LOOKED AT
	 JNBE $$DO12
	    PUSH DS	       ;SET UP THE ES REG TO				;
	    POP  ES	       ;  BE THE SAME AS DS, THE COMMON SEGMENT 	;
	    ASSUME ES:CSEG     ;TELL THE ASSEMBLER WHAT I JUST DID		;

	    FIXLIST LISTPARM,.XLIST ;						;
;	    LEA  AX,MSG1       ;NON-STD VER OF GRAPHIC CHAR SET TABLE IS ALREADY LOADED
	    FIXLIST LISTPARM,.LIST ;						;
	    CALL PREV_NONSTD   ;SET MSG TO SAY "NON STANDARD"			;
			       ; SO MSG IS "PREVIOUS CODE PAGE: NON-STANDARD"
;	 $ENDSRCH
$$SR12:
;     $ELSE		       ;SINCE THE 1FH VECTOR DOES NOT POINT TO
      JMP SHORT $$EN11
$$IF11:
			       ; THE SAME PLACE AS "WHERE"
			       ; THEN THE USER MUST HAVE USED GRAFTABL TO
			       ; LOAD A CHAR FONT, THEN LOADED SOME OTHER
			       ; TABLE AND CHANGED VECTOR 1FH TO POINT TO THE NEW ONE.
	 PUSH DS	       ;SET UP THE ES REG TO				;
	 POP  ES	       ;  BE THE SAME AS DS, THE COMMON SEGMENT 	;
	 ASSUME ES:CSEG        ;TELL THE ASSEMBLER WHAT I JUST DID		;

	 CALL PREV_NONSTD      ;SET MSG TO SAY "NON STANDARD"			;

	 FIXLIST LISTPARM,.XLIST ;						;
;	  LEA  AX,MSG1		;NON-STD VER OF GRAPHIC CHAR SET TABLE IS ALREADY LOADED
	 FIXLIST LISTPARM,.LIST ;						;
	 MOV  BAD1F,RESET      ;INDICATE THAT THE 1FH VECTOR DOES NOT POINT
			       ; TO THE GRAFTABL FONT
;     $ENDIF		       ;HAS 1Fh BEEN CHANGED?
$$EN11:
;  $ELSE		       ;SINCE GRAFTABLE IS NOT LOADED,
   JMP SHORT $$EN10
$$IF10:
      PUSH DS		       ;SET UP THE ES REG TO				;
      POP  ES		       ;  BE THE SAME AS DS, THE COMMON SEGMENT 	;
      ASSUME ES:CSEG	       ;TELL THE ASSEMBLER WHAT I JUST DID		;

      CALL PREV_NONE	       ;SET MSG TO SAY "NONE"				;

      FIXLIST LISTPARM,.XLIST  ;						;
;      LEA  AX,MSG5		;NO VER OF CHAR TABLE IS ALREADY LOADED
      FIXLIST LISTPARM,.LIST   ;						;
;  $ENDIF		       ;ALREADY INSTALLED?
$$EN10:
   RET
FINDTYPE ENDP
; = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
   HEADER <PREV_NONSTD - SET UP MESSAGE TO SAY 'NON-STANDARD'> ;		;
PREV_NONSTD PROC NEAR	       ;						;
   PUBLIC PREV_NONSTD	       ;						;
;SET UP SUBLIST FOR MSGS 2-3 TO HAVE THE VARIABLE TEXT: "NON-STANDARD".
   ASSUME ES:CSEG	       ;						;

   MOV	AX,MSGNUM_NSTD.MSG_NUM ;PASS MESSAGE NUMBER OF "NON-STANDARD"		;
   MOV	DH,BYTE PTR MSGNUM_NSTD.MSG_CLASS+BYTE ;PASS MSG'S CLASS                ;
   CALL SYSGETMSG	       ;FIND WHERE THE TEXT OF "NON-STANDARD" IS	;
			       ;ES:SI=VECTOR TO MESSAGE TEXT
   MOV	SLIST_23.SUB_VALUE,SI  ;PUT OFFSET INTO SUBLIST 			;
   RET			       ;RETURN TO CALLER				;
PREV_NONSTD ENDP	       ;						;
; = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
   HEADER <PREV_NONE - SET UP MESSAGE TO SAY 'NONE'> ;				;
PREV_NONE PROC NEAR	       ;						;
   PUBLIC PREV_NONE	       ;						;
;SET UP SUBLIST FOR MSGS 2-3 TO HAVE THE VARIABLE TEXT: "NONE".
   ASSUME ES:CSEG	       ;						;

   MOV	AX,MSGNUM_NONE.MSG_NUM ;PASS MESSAGE NUMBER OF "NONE"			;
   MOV	DH,BYTE PTR MSGNUM_NONE.MSG_CLASS+BYTE ;PASS MSG'S CLASS                ;
   CALL SYSGETMSG	       ;FIND WHERE THE TEXT OF "NONE" IS		;
			       ;ES:SI=VECTOR TO MESSAGE TEXT
   MOV	SLIST_23.SUB_VALUE,SI  ;PUT OFFSET INTO SUBLIST 			;
   RET			       ;RETURN TO CALLER				;
PREV_NONE ENDP		       ;						;
; = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
   FIXLIST LISTPARM,.XLIST     ;						    ;
;   HEADER <PARSE THE COMMAND LINE PARMS>
;PROCESS PROC NEAR		;LOOK AT RESPONSE TO DOS COMMAND LINE PARMS
;   PUBLIC PROCESS
;INPUT: DS, ES - PSP SEG ID
;OUTPUT:EXITFL IS SET TO REFLECT EXISTING CONDITIONS

;   CALL PARSER 		;LOOK AT DOS COMMAND LINE PARMS
;
;				;IF A CODEPAGE NUMBER WAS SPECIFIED
;				;   BX = OFFSET TO LANGUAGE TABLE TO BE LOADED
;				;   DX = INTEGER VALUE OF CODEPAGE SPECIFIED
;				;IF /STATUS WAS SPECIFIED
;				;   BX = 0
;				;IF QUESTION MARK WAS SPECIFIED
;				;   BX=-1
;				; if parse error occurred
;				;   bx = 1
;
;   CALL DOFUNCTION		; RESPOND TO PARMS
;				; SETTING 'EXITFL' TO INDICATE RESULTS
;
;   ASSUME ES:CSEG
;   PUSH ES			;SAVE BASE REG
;   MOV  SI,OFFSET PARMDOS	;GET WHERE DOS PARMS START
;   SUB  DI,DI			;INIT INDEX TO ZERO, WHERE TO PUT PARMS IN TOKEN
;   MOV  CL,DS:PARMCNT		;GET HOW MANY CHARS ARE IN THE DOS COMMAND LINE
;   XOR  CH,CH			;CLEAR COUNTER OF OUTPUT CHARS
;   CALL UPCASE 		;CONVERT ANY LOWER CASE CHARS TO UPPERCASE
;
;   $SEARCH
;      CMP  CL,0		;ANY DOS PARMS?
;   $LEAVE BE			;QUIT IF NOT PARMS
;      MOV  DL,BYTE PTR [SI]	;SET DL TO A PARM CHAR FROM PARMDOS IN PSP
;      CMP  DL,BLANK		;IGNORE THE SPECIAL CHARS:
;      $IF  A			;  BLANK AND LOWER NUMBERED CONTROL CHARS
;	  CMP  DL,"?"		;IS THE SPECIFIED PARAMETER
;	  $IF  E		; A QUESTION MARK?
;	     MOV  BYTE PTR TOKEN[DI],DL ;PUT DL CONVERTED TO TOKEN
;	     INC  DI		;BUMP TO NEXT AVAIL SPOT IN TOKEN
;	     ADD  CH,3		;COUNT THIS AS AN OUTPUT CHAR,WITH NO ROOM FOR MORE
;	  $ELSE 		;SINCE NOT "?"
;	     CMP  DL,'0'	;IF CHAR IN RANGE OF NUMERICS
;	     $IF  AE,AND	;UNSIGNED TEST
;	     CMP  DL,'9'
;	     $IF  BE
;		PUSH DX
;		MOV  AX,ACC
;		IMUL TEN	;CLOBBERS DX
;		POP  DX 	;FIX IT BACK
;		SUB  DL,'0'	;CONVERT FROM NUMERIC CHAR TO BINARY
;		ADD  AL,DL	;ADD NEW NUMBER FROM PARM
;		ADC  AH,0	;PROPOGATE CARRY
;		MOV  ACC,AX	;SAVE RESULTS SO FAR
;		INC  CH 	;COUNT THIS AS AN OUTPUT CHAR
;	     $ELSE		;SINCE NOT A NUMERIC CHARACTER
;		MOV  CH,TOKEN_SIZE ;FORCE PARM ERROR, MAY BE CHANGED TO OK
;		CMP  CL,4
;		$IF  AE 	;IF AT LEAST 4 CHARS ARE PRESENT
;		   CMP	WORD PTR [SI],"S/" ;FIRST TWO CHARS OF "/STA"
;		   $IF	E,AND
;		   CMP	WORD PTR [SI]+WORD,"AT" ;NEXT TWO CHARS OF "/STA"
;		   $IF	E
;		      MOV  BYTE PTR TOKEN[DI],'/' ;PUT "/" CONVERTED TO TOKEN
;		      INC  DI	;BUMP TO NEXT AVAIL SPOT IN TOKEN
;		      MOV  CH,3 ;COUNT THIS AS AN OUTPUT CHAR,WITH NO ROOM FOR MORE
;		      MOV  CL,1 ;PRETEND THAT IS ALL OF THE COMMAND LINE THERE IS
;		   $ENDIF	;"/STA"?
;		$ENDIF		;LENGTH 4?
;	     $ENDIF		;IS CHAR IN NUMERIC RANGE?
;	  $ENDIF		;IS PARM A QUESTION MARK?
;      $ENDIF			;BLANK OR LOWER VALUED CONTROL CHARS?
;      CMP  CH,TOKEN_SIZE	;IS OUTPUT AREA OVERLY FULL?
;   $EXITIF GE
;      CALL BADPARMS		;FUSS ABOUT INVALID PARMS
;   $ORELSE
;      INC  SI			;BUMP INDEX TO NEXT PARM CHAR IN PSP
;      DEC  CL			;DEC COUNT OF INPUT CHARS FROM PARMS
;   $ENDLOOP
;				;SINCE CL COUNT WENT TO ZERO,
;      CMP  CH,0		;ANY DOS PARMS LEFT AFTER CLEANUP?
;      $IF  E			;IF NONE,
;	  MOV  WORD PTR TOKEN,437 ;MAKE IT LOOK LIKE "US" WAS SPECIFIED
;      $ELSE			;SOME PARM WAS SPECIFIED
;	  CMP  BYTE PTR TOKEN,PARM?
;	  $IF  NE,AND		;IF NOT A QUESTION MARK, AND
;	  CMP  BYTE PTR TOKEN,SLASH
;	  $IF  NE		;IF NOT A SLASH (FOR /STATUS)
;	     MOV  AX,ACC	;GET ACCUMULATOR
;	     MOV  WORD PTR TOKEN,AX ; TO TOKEN
;	  $ENDIF		;QUESTION MARK?
;      $ENDIF			;ANY DOS PARMS?
;      CALL DOFUNCTION		;RESPOND TO PARAMETER IN "TOKEN"
;
;   $ENDSRCH			;SCANNING DOS COMMAND LINE FOR PARMS
;
;   POP  ES			;RESTORE BASE REG
;   ASSUME ES:CSEG
;
;   RET
;PROCESS ENDP
; = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
;UPCASE PROC NEAR
;;CONVERT CHARACTERS IN PARMLIST TO UPPER CASE
;;INPUT: SI=OFFSET IN DS: TO PARM TEXT
;;	 CX=NUMBER OF CHARS OF PARMS
;
;   OR	 CX,CX			;IF THERE ARE CHARS IN PARMLIST
;   $IF  NZ			; THEN GO LOOK AT EACH CHAR
;      PUSH SI			;SAVE POINTER TO PARMS
;      PUSH CX			;SAVE COUNTER OF PARMS
;      $DO			;STEP THRU ALL CHARS OF PARM LIST, CONVERTING TO UPPER CASE
;	  MOV  AL,[SI]		;GET CHAR FROM PARMLIST TO AL
;	  CMP  AL,LOWERA	;COMPARE WITH LOWER CASE "a"
;	  $IF  AE,AND		;UNSIGNED TEST
;	  CMP  AL,LOWERZ	;COMPARE WITH LOWER CASE "z"
;	  $IF  BE		;IF CHAR IS LOWER CASE
;	     AND  AL,CASEBIT	;CONVERT TO UPPER CASE BY DELETING THE 20H BIT
;	     MOV  [SI],AL	;RESTORE CHAR TO PARMLIST
;	  $ENDIF
;	  INC  SI		;BUMP INDEX TO LOOK AT NEXT CHAR
;      $ENDDO LOOP
;      POP  CX			;RESTORE COUNTER OF PARMS
;      POP  SI			;RESTORE POINTER TO PARMS
;   $ENDIF			;ANY CHARS IN PARMLIST?
;
;   RET
;UPCASE ENDP
; = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
   FIXLIST LISTPARM,.LIST      ;						;
   HEADER <RESPOND TO PARAMETER> ;AC000;
DOFUNCTION PROC NEAR	       ;RESPOND TO PARM 				;AC000;
   PUBLIC DOFUNCTION
;INPUT: IF A CODEPAGE NUMBER WAS SPECIFIED
;	    BX = OFFSET TO LANGUAGE TABLE TO BE LOADED
;	    DX = INTEGER VALUE OF CODEPAGE SPECIFIED
;	 IF /STATUS WAS SPECIFIED
;	    BX = 0
;	 IF QUESTION MARK WAS SPECIFIED
;	    BX=-1
;	 If a parse error occurred,
;	    BX = 1

   ASSUME ES:CSEG

   ; Check for error.  Then check for /? option found by parser.
   ; If so, display the options message and exit.

   CMP	BX, RETCODE_PARSE_ERROR
   JE	$$EN22		       ; exit directly on error
   CMP	BX, RETCODE_QUESTION   ;IS QUESTION MARK SPECFIED?
   JNE $$IF22
   CALL HELP		       ;DISPLAY RESULTS OF "?" SPECIFICATION
   JMP SHORT $$EN22
$$IF22:

      CMP  BX,ZERO	       ;IS /STATUS SPECIFIED?
;     $IF  NE		       ;IF NOT STATUS, MUST BE REQUEST TO LOAD A TABLE
      JE $$IF24
	 LEA  DI,MSGNUM_PREVIOUS ;"Previous Code Page: xxx"
	 CALL SENDMSG	       ;SEND THE MSG TO STDOUT

	 MOV  DX,[BX].LANID    ;FETCH THE TWO CHAR ID FROM TABLE
	 CMP  DX,IDXXX	       ;IS THIS ONE ALREADY LOADED?
;	 $IF  NE	       ;IF NOT, LOAD IT
	 JE $$IF25

	    FIXLIST LISTPARM,.XLIST ;						;
;	       MOV  CX,LENGTH LANNAME ;GET LENGTH OF NAME FIELD
;	       LEA  SI,[BX].LANNAME ;GET WHERE NAME FIELD IS IN THE NEW TABLE
;	       LEA  DI,LANGUAGE_NAME ;WHERE WHERE TO PUT NAME IN VARIABLE MESSAGE FIELD
;	       REP  MOVSB      ;MOVE LANGUAGE NAME TO PRINTF VARIABLE FIELD
	    FIXLIST LISTPARM,.LIST ;						;

	    MOV  SI,BX	       ;PASS START OF CHAR TABLE
	    CALL MOVTAB        ;PUT THIS TABLE INTO THE USA POSITION

	    FIXLIST LISTPARM,.XLIST ;						;
;	       LEA  AX,MSG3    ;XXXXX VER OF GRAPHIC CHAR SET HAS JUST BEEN LOADED
	    FIXLIST LISTPARM,.LIST ;						;
	    LEA  DI,MSGNUM_ACTIVE ;"ACTIVE CODE PAGE: XXX"			;
	    MOV  AX,ACTIVECPID ;GET POINTER TO CP JUST ACTIVATED		;
	    MOV  SLIST_23.SUB_VALUE,AX ;INTO SUBLIST				;
	    CALL SENDMSG       ;SHOW "ACTIVE CODE PAGE: XXX"			;

	    CMP  BAD1F,RESET   ;THIS FLAG SET TO '1' MEANS INT 1FH POINTS TO
;	    $IF  E	       ; A TABLE OTHER THAN ONE LOADED BY GRAFTABL
	    JNE $$IF26
	       PUSH DS
	       ASSUME DS:NOTHING
	       LDS  DX,WHERE   ;SET DS:DX POINT TO PREVIOUSLY LOADED GRAFTABL FONT
	       MOV  AH,SET_VECT ;FUNCTION TO MOVE DS:DX INTO VECTOR FOR INT 1FH
	       MOV  AL,VEC_GRAF_CHAR ;INTERRUPT NUMBER TO RECEIVE NEW CONTENTS
	       INT  VEC_DOS    ;CHANGE THE VECTOR

	       POP  DS
	       ASSUME DS:CSEG
;	    $ENDIF	       ;BAD 1FH?
$$IF26:
;	 $ENDIF 	       ;THIS TABLE ALREADY LOADED?
$$IF25:
	 FIXLIST LISTPARM,.XLIST ;						;
;	    ADD  BX,SIZE LANGUAGE ;STEP BASE TO NEXT TABLE
;	    LEA  AX,COPYRIGHT  ;AT START OF MSGS, BEYOND LAST TABLE
;	    CMP  BX,AX
;	 $ENDLOOP AE	       ;GO TRY THE NEXT TABLE, IF ANY
;			       ;NOT ANY NEXT TABLE
;	    CALL BADPARMS      ;FUSS ABOUT INVALID PARMS
;
;	 $ENDSRCH	       ;COMPARING TOKEN WITH ID IN TABLES
	 FIXLIST LISTPARM,.LIST ;						;
;     $ELSE		       ;SINCE /STATUS SPECIFIED,
      JMP SHORT $$EN24
$$IF24:
	 LEA  DI,MSGNUM_ACTIVE ;POINT TO "ACTIVE CODE PAGE: XXX"
	 CALL SENDMSG	       ; VARIABLE FILLED BY "FINDTYPE"

	 CMP  IDXXX,ZERO       ;SEE WHO IS LOADED, IF ANYBODY
;	 $IF  E
	 JNE $$IF30
	    MOV  EXITFL,EXNONE ;SET EXIT CODE TO SAY NO TABLE LOADED AT ALL
;	 $ELSE		       ;SINCE SOMEONE IS LOADED,
	 JMP SHORT $$EN30
$$IF30:
	    MOV  EXITFL,EXDOS  ;SAY A TABLE IS LOADED
;	 $ENDIF 	       ;ANY TABLE PREVIOUSLY LOADED?
$$EN30:

;     $ENDIF		       ;/STATUS?
$$EN24:
;  $ENDIF		       ;QUESTION MARK?
$$EN22:
   RET
DOFUNCTION ENDP
; = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
   HEADER <LOCAL SUBROUTINE, INSTALL A PARTICULAR TABLE>
MOVTAB PROC NEAR
   PUBLIC MOVTAB
;INPUT: SI = OFFSET OF SOURCE TABLE
;	ES = PSP
   ASSUME ES:CSEG
;OUTPUT: THE SOURCE TABLE WILL BE MOVED TO ONE OF TWO PLACES,
;	EITHER
;	(IF THIS IS THE FIRST TIME FOR GRAFTABL)
;	TO THE "TABLEUS" IN THE PSP (IF USA NOT SPECIFIED),
;	AND THEN BOTH INT2FH AND TABLEUS MOVED TO MOV_DEST IN THE PSP,
;	OR
;	ON TOP OF THE PREVIOUS TABLE LEFT BY A PREVIOUS GRAFTABL CALL,
;	AS POINTED TO BY THE VECTOR, "WHERE".

   PUSH ES		       ;SAVE THE PSP
   CMP	INSTALLED,LOADED       ;IF A PREVIOUS COPY OF GRAFTABL
;  $IF	E		       ; HAS ALREADY BEEN LOADED
   JNE $$IF35

      LES  DI,WHERE	       ;REMEMBER WHERE PREVIOUSLY TABLE WENT
      ASSUME ES:NOTHING        ;ES IS BASE OF OLD PREVIOUSLY LOADED TABLE
      MOV  CX,TABSIZE	       ;THE TABLE,2 CHAR ID,& LANGUAGE NAME ARE TO BE MOVED
      REP  MOVSB	       ;MOVE NEW TABLE ON TOP OF OLD TABLE
			       ;WHEN EXITING, LEAVE NOTHING RESIDENT
;  $ELSE		       ;SINCE NO PREVIOUS LOAD OF GRAFTABL EXISTS,
   JMP SHORT $$EN35
$$IF35:
      ASSUME ES:CSEG	       ;SINCE ES WAS NOT CHANGED,
      MOV  EXITFL,EXRES        ;WHEN EXITING, LEAVE TABLE RESIDENT
			       ; ALONG WITH THE INT2FH HANDLER

			       ;SINCE IT IS THE USA TABLE THAT IS
			       ; PHYSICALLY NEXT TO THE INTERRUPT HANDLER,
			       ; THAT PAIR OF AREAS IS WHAT WILL STAY
			       ; RESIDENT.  IF THE USA TABLE IS NOT THE
			       ; DESIRED VERSION, THEN FIRST MOVE THE
			       ; VERSION DESIRED ON TOP OF THE USA VERSION,
			       ; THEN MOVE THE PAIR DOWN ON TOP OF THE PSP
			       ; WHERE THEY WILL STAY RESIDENT.

      LEA  DI,TABLEUS	       ;FIND WHERE FIRST TABLE NOW IS
      CMP  SI,DI
;     $IF  NE		       ;IF NOT THE USA TABLE,
      JE $$IF37
	 MOV  CX,TABSIZE       ;SIZE OF THE TABLE
	 REP  MOVSB	       ;MOVE THE XX TABLE ONTO THE USA TABLE
;     $ENDIF
$$IF37:
      MOV  DI,OFFSET MOV_DEST  ;WHERE TO MOVE EVERYTHING TO
      LEA  SI,END_PSP	       ;START WITH THE INT2FH HANDLER, ON THRU THE TABLE
			       ;ES:DI POINT TO DESTINATION, DS:SI POINT TO SOURCE
      MOV  CX,RESSIZE_BYTE     ;GET SIZE OF TABLE + RESIDENT CODE INT HANDLER
      REP  MOVSB	       ;MOVE THE TABLE ONTO THE PSP OF THIS LOAD
;  $ENDIF
$$EN35:
   POP	ES		       ;RESTORE THE PSP POINTER TO ES
   RET
MOVTAB ENDP

; = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
   HEADER <HELP - GENERATE DISPLAY RESPONDING TO STATUS REQUEST>
HELP PROC NEAR
   PUBLIC HELP

;INPUT: DS, ES = POINTS TO PSP
;	IDXXX = CP OF WHO IS ALREADY LOADED
;	EXITFL = ERROR LEVEL RET CODE
;		NORMAL VALUE SAYS SOME TABLE HAS BEEN LOADED, BUT
;		INVALID PARMS COULD HAVE ALREADY ALTERED THIS VALUE.
;OUTPUT: EXITFL MAY BE CHANGED TO "EXNONE", IF NO TABLE LOADED
;
;NOTES:
;  04/24/90 c-PaulB: Modified this from handling a "?" on the command
;  line, to handling the standard "/?".  This function now displays
;  the help message and sets EXITFL for a fast way out, so the user
;  can try the command again.

   ASSUME ES:CSEG

   ; Set the exit flag so we can bail out
   ; after displaying the options help messages.

;   CMP	EXITFL,EXDOS	       ;IS RET CODE UNTOUCHED SO FAR?
;   JNE $$IF40
;      CMP  IDXXX,ZERO	       ;SEE WHO IS LOADED, IF ANYBODY
;      JNE $$IF41

   MOV  EXITFL,EXNONE    ;SET EXIT CODE TO SAY NO TABLE LOADED AT ALL

;$$IF41:
;$$IF40:

   FIXLIST LISTPARM,.XLIST     ;						;
   FIXLIST LISTPARM,.LIST      ;						;

   ; Display the options help message.
   ; Lines of the message are displayed starting
   ; with MSG_OPTIONS_FIRST through and including
   ; MSG_OPTIONS_LAST.  The numbers are defined
   ; in GRTABMS.INC, and the messages themselves
   ; are defined in GRAFTABL.SKL and the message files.

   lea	di, MSGNUM_HELP_OPTIONS		; point to options help msg struct
HelpMessageLoop:
   call	sendmsg				; display that message
   cmp	MSGNUM_HELP_OPTIONS.MSG_NUM, MSG_OPTIONS_LAST	; last msg to send?
   je	HelpMessageDone			;  done if so
   inc	MSGNUM_HELP_OPTIONS.MSG_NUM	; bump the message number
   jmp	short HelpMessageLoop		;  and go do the next message
HelpMessageDone:

   FIXLIST LISTPARM,.XLIST     ;						;
   FIXLIST LISTPARM,.LIST      ;					       ;
   RET
HELP ENDP


; = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
   FIXLIST LISTPARM,.XLIST     ;					       ;
;BADPARMS PROC NEAR
;   LEA  AX,MSG4		;"INCORRECT PARAMETER"
;   CALL SENDMSG
;
;   CALL HELP			;DISPLAY RESULTS OF "?" SPECIFICATION
;
;   MOV  EXITFL,EXPAR		;ERRORLEVEL CODE TO "PARM ERROR"
;   RET
;BADPARMS ENDP
; = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
   FIXLIST LISTPARM,.LIST

   HEADER <SENDMSG - PASS IN REGS DATA FROM MSG DESCRIPTOR TO DISP MSG> ;
SENDMSG PROC NEAR	       ;						;
   PUBLIC SENDMSG	       ;						;
;INPUT - DI=POINTER TO MSG_DESC STRUC FOR THIS MESSAGE
;OUTPUT - IF CARRY SET, EXTENDED ERROR MSG ATTEMPTED DISPLAYED
;	   IF CARRY CLEAR, ALL OK
;	   IN EITHER CASE, DI AND AX ALTERED, OTHERS OK
;  =  =  =  =  =  =  =	=  =  =  =  =

   PUSH BX		       ; SAVE CALLER'S REGS                             ;
   PUSH CX		       ;						;
   PUSH DX		       ;						;
   PUSH SI		       ;						;

;		 PASS PARMS TO MESSAGE HANDLER IN
;		 THE APPROPRIATE REGISTERS IT NEEDS.
   MOV	AX,[DI].MSG_NUM        ;MESSAGE NUMBER					;
   MOV	BX,[DI].MSG_HANDLE     ;HANDLE TO DISPLAY TO				;
   MOV	SI,[DI].MSG_SUBLIST    ;OFFSET IN ES: OF SUBLIST, OR 0 IF NONE		;
   MOV	CX,[DI].MSG_COUNT      ;NUMBER OF %PARMS, 0 IF NONE			;
   MOV	DX,[DI].MSG_CLASS      ;CLASS IN HIGH BYTE, INPUT FUNCTION IN LOW	;
   CALL SYSDISPMSG	       ;DISPLAY THE MESSAGE				;

;  $IF	C		       ;IF THERE IS A PROBLEM				;
   JNC $$IF44
			       ;AX=EXTENDED ERROR NUMBER			;
      LEA  DI,MSGNUM_EXTERR    ;GET REST OF ERROR DESCRIPTOR			;
      MOV  BX,[DI].MSG_HANDLE  ;HANDLE TO DISPLAY TO				;
      MOV  SI,[DI].MSG_SUBLIST ;OFFSET IN ES: OF SUBLIST, OR 0 IF NONE		;
      MOV  CX,[DI].MSG_COUNT   ;NUMBER OF %PARMS, 0 IF NONE			;
      MOV  DX,[DI].MSG_CLASS   ;CLASS IN HIGH BYTE, INPUT FUNCTION IN LOW	;
      CALL SYSDISPMSG	       ;TRY TO SAY WHAT HAPPENED			;

      STC		       ;REPORT PROBLEM					;
;  $ENDIF		       ;PROBLEM WITH DISPLAY?				;
$$IF44:

   POP	SI		       ;RESTORE CALLER'S REGISTERS                      ;
   POP	DX		       ;						;
   POP	CX		       ;						;
   POP	BX		       ;						;
   FIXLIST LISTPARM,.XLIST     ;						;
;;INPUT: AX = OFFSET OF MSG PARM LIST
;  PUSH AX
;  PUSH CS		       ;MATCH REQUIREMENTS OF PRINTF, WHICH IS "FAR"
;  CALL PRINTF		       ;USING MSG PARM LIST, BUILD MSG, DISPLAY MSG
;			       ;PRINTF WILL POP OFF THE PARM, PUSHED FROM AX
;
   FIXLIST LISTPARM,.LIST      ;						;

   RET			       ;						;
SENDMSG ENDP		       ;						;
   PATHLABL GRTAB	       ;
CSEG ENDS
   END
