home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS - Coast to Coast
/
simteldosarchivecoasttocoast.iso
/
pcmag
/
vol6n10.zip
/
MAKEBAR.ASM
< prev
next >
Wrap
Assembly Source File
|
1987-03-23
|
46KB
|
1,390 lines
;======================================================================
; MAKEBAR: read a bar-definition-file (.BDF) and produce a tokenized
; .BAR file to be read by the TSR interpreter.
;----------------------------------------------------------------------
CSEG SEGMENT PUBLIC PARA 'CODE'
ASSUME CS:CSEG, DS:CSEG, ES:CSEG, SS:CSEG
ORG 100H
ENTPT: JMP MAIN
COPYRIGHT DB "MAKEBAR 1.0 (c) 1987, Ziff-Davis Publishing Corp",CR,LF,'$'
AUTHOR DB 1AH,"Robert L. Hummel"
;======================================================================
; EQUATES
;----------------------------------------------------------------------
SPACE EQU 20H ;Some keys
CR EQU 0DH
LF EQU 0AH
QUOTE EQU 22H
TAB EQU 09H
INPUT_BUF_LEN EQU 4000 ;Size constants
OUTPUT_BUF_LEN EQU 46000
MENU_BUF_LEN EQU 4000
MENU_TBL_LEN EQU 4000
BUF_TOTAL = INPUT_BUF_LEN + OUTPUT_BUF_LEN + MENU_BUF_LEN + MENU_TBL_LEN
_SHIFT EQU 1 ;Internal shift flags
_CTRL EQU 2
_ALT EQU 4
;----------------------------------------------------------------------
; BUFFERS AND POINTERS
;----------------------------------------------------------------------
INPUT_HNDL DW 0 ;Handle of source file
INPUT_HNDL_PTR DW 0 ;Pointer to source file
INPUT_BUF_END DW 0FFFFH ;Used by GET_CHAR
OUTPUT_HNDL DW 0 ;Handle of output file
MENU_HEAD DW 0 ;Pointer to current menu
SHIFT_FLAGS DB 0 ;Hold current shift state
;----------------------------------------------------------------------
; COUNTERS
;----------------------------------------------------------------------
SOURCE_LINE DW 0 ;For error messages
NEW_REF DB -1 ;Two counters track menu
OLD_REF DB 0 ; references
;----------------------------------------------------------------------
; TABLES
;----------------------------------------------------------------------
CMD_TABLE LABEL BYTE ;All commands names
_ASK EQU 0 ; and their token values
A_CMD DB 'ASK',0
_CR EQU 1
C_CMD DB 'CR',0
_EXECUTE EQU 2
E_CMD DB 'EXECUTE',0
_INPUT EQU 3
I_CMD DB 'INPUT',0
_MENU EQU 4
M_CMD DB 'MENU',0
_OPTION EQU 5
O_CMD DB 'OPTION',0
_PROGRAM EQU 6
P_CMD DB 'PROGRAM',0
_TYPE EQU 7
_CMD DB 'TYPE',0
_MEND EQU 8
DB 'MEND',0
_END EQU 9
DB 'END',0
_SEND EQU 10
DB 0 ;End-of-table byte
;----------------------------------------------------------------------
KEY_NAME_TBL LABEL BYTE ;Special key names
DB "S",0,"C",0,"A",0
DB "F1",0,"F2",0,"F3",0,"F4",0,"F5",0
DB "F6",0,"F7",0,"F8",0,"F9",0,"F10",0
DB "ESC",0,"TAB",0,"ENTER",0,"BS",0
DB "HOME",0,"PGUP",0,"END",0,"PGDN",0,"INS",0
DB "DEL",0,"U",0,"D",0,"L",0,"R",0,0
;----------------------------------------------------------------------
; These keys may be combined with the CRTL and ALT keys
;----------------------------------------------------------------------
KEY_TBL_1 DB "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-=\[]",0
;----------------------------------------------------------------------
; CTRL combinations for 1234567890-= (use {c}1 for ; {c}3 for ")
;----------------------------------------------------------------------
KEY_TBL_2 DW 003BH,0FE03H,0022H,0,0,001EH,0,0,0,0,001FH,0
DW 001CH, 001BH, 001DH
;----------------------------------------------------------------------
; Scan codes for alpha keys a-z (used for ALT-alpha)
;----------------------------------------------------------------------
KEY_TBL_3 DB 01EH,30H,2EH,20H,12H,21H,22H,23H,17H,24H,25H,26H
DB 32H,31H,18H,19H,10H,13H,1FH,14H,16H,2FH,11H,2DH
DB 15H,2CH
;----------------------------------------------------------------------
; SHIFT,CTRL for enter,bs,home,pgup,end,pgdn,ins,del,up,down,left,right
;----------------------------------------------------------------------
KEY_TBL_4 DB 0DH,0FEH, 8,7FH, 47H,77H, 49H,84H, 4FH,75H
DB 51H,76H, 52H,0, 53H,0, 48H,0, 50H,0, 4BH,73H,4DH,74H
;----------------------------------------------------------------------
; FLAGS
;----------------------------------------------------------------------
MENU_FLAG DB 0 ;Non-zero when open menu blk
OPTION_FLAG DB 0 ;Non-zero when open option blk
PROGRAM_NAME_FLAG DB 0 ;Non-zero when PROGRAM cmd read
;----------------------------------------------------------------------
; MESSAGES
;----------------------------------------------------------------------
USAGE_MSG DB "Usage: MAKEBAR [path]input_file [path]output_file$"
BAD_FILE_MSG DB "Can't Open File$"
READ_MSG DB "Error Reading File$"
DUP_CMD_MSG DB "Duplicate Cmd$"
CMD_ORDER_MSG DB "Cmd Out Of Order$"
UNK_CMD_MSG DB "Unknown Cmd$"
NEST_MSG DB "MENU Without MEND$"
SYNTAX_MSG DB "Syntax Error$"
NO_NAME_MSG DB "Missing Name$"
REF_MSG DB "Bad MENU Reference$"
EOF_MSG DB "Missing END$"
DEAD_KEY_MSG DB "Dead-Key or Bad Key$"
LINE_MSG DB CR,LF,"Error At Line # $"
LINE_NUM_BUF EQU $-2
;======================================================================
; MENU FILE TOKENIZER - MAIN PROCEDURE
;----------------------------------------------------------------------
MAIN PROC NEAR
MOV AH,9 ;Display string fn
MOV DX,OFFSET COPYRIGHT ;Say who we are
INT 21H ; Thru DOS
CLD ;Strings moves forward
;----------------------------------------------------------------------
; Check the file specs given on the command line. Open the files and
; save the file handles.
;----------------------------------------------------------------------
CALL OPEN_FILES
;----------------------------------------------------------------------
; Read in the Menu-Definition-File and tokenize. Write the output to
; the new .BAR file for use with the resident interpreter.
;----------------------------------------------------------------------
CALL TOKENIZE
;----------------------------------------------------------------------
; Close and update any open files. Terminate gracefully.
;----------------------------------------------------------------------
CALL CLOSE_FILES
MOV AX,4C00H ;Terminate process
INT 21H ; Thru DOS
MAIN ENDP
;======================================================================
; OPEN_FILES - Open the file given on the command line and the working
; files needed during operation of the tokenizer.
;----------------------------------------------------------------------
OPEN_FILES PROC NEAR
MOV SI,81H ;Command line parameters
CALL OPEN_A_FILE ;Open source
MOV INPUT_HNDL,AX ;Save handle
CALL OPEN_A_FILE ;Open destination file
MOV OUTPUT_HNDL,AX ;Save handle
MOV INPUT_BUF_END,0 ;Make refresh occur
RET
OPEN_FILES ENDP
;======================================================================
; OPEN_A_FILE - Read a string from the command line and attempt to open
; it as a file.
;----------------------------------------------------------------------
OPEN_CNT DB 0 ;Switch for open function
OPEN_A_FILE PROC NEAR
CALL NON_WHITE ;Point SI to 1st nonwhite
CMP AL,CR ;If not carriage return
JNE HAVE_ARGS ; go parse arguments
NO_SPECS:
MOV DX,OFFSET USAGE_MSG ;Say how we are used
OPEN_ERR:
JMP ERROR_EXIT ;Exit thru error procedure
HAVE_ARGS:
PUSH SI ;Save start of string
CALL WHITE ;Point past end
MOV CX,SI ;End of string
POP SI ;Restore start
SUB CX,SI ;Get length of string
OR CX,CX ;If zero
JZ NO_SPECS ; no file names
MOV DI,OFFSET PATH_BUF ;Copy string here
REP MOVSB ; do it
XOR AL,AL ;Make ASCIIZ
STOSB ; put in buffer
;----------------------------------------------------------------------
; Attempt to open the file.
;----------------------------------------------------------------------
MOV AX,3D00H ;Open file for reading
CMP OPEN_CNT,0 ; if first file
JE FILE_OPEN
DEC AH ;Create file if second
FILE_OPEN:
MOV DX,OFFSET PATH_BUF ;Pathname of file
INT 21H ;Handle in AX
JNC SOURCE_OPEN ;No carry if open OK
MOV DX,OFFSET BAD_FILE_MSG
JMP OPEN_ERR
SOURCE_OPEN:
INC OPEN_CNT ;Change switch
RET
OPEN_A_FILE ENDP
;======================================================================
; Close the files to update the length.
;----------------------------------------------------------------------
CLOSE_FILES PROC NEAR
MOV AH,3EH ;Close file function
MOV BX,INPUT_HNDL ; first file
INT 21H ; Thru DOS
MOV AH,3EH ;Close file function
MOV BX,OUTPUT_HNDL ; second file
INT 21H ; Thru DOS
RET
CLOSE_FILES ENDP
;======================================================================
; TOKENIZE - Read and process the Bar Definition File (BDF).
;----------------------------------------------------------------------
TOKENIZE PROC NEAR
;----------------------------------------------------------------------
; Clear flags and reset source line counter.
;----------------------------------------------------------------------
XOR AX,AX ;Zero
MOV PROGRAM_NAME_FLAG,AL ;No program name
MOV MENU_FLAG,AL ;Not inside MENU block
MOV SOURCE_LINE,AX ;Source line counter
MOV DI,OFFSET MENU_NAME_TBL ;Fill the table with zeros
MOV CX,MENU_TBL_LEN ;Entire length
REP STOSB ; do it
CALL REFRESH_BUFFER ;Load some chars into buf
MOV DI,OFFSET OUTPUT_BUF ;Init output buffer pointer
;----------------------------------------------------------------------
; Parse the input file to find commands and arguments.
;----------------------------------------------------------------------
FIND_WORD:
CALL NON_WHITE ;Find non-white char
CMP AL,CR ;Ignore blank lines
JNE TKN_1
CALL NEXT_LINE ;Skip to next line
JMP FIND_WORD ;Continue search
TKN_1:
;----------------------------------------------------------------------
; SI points to word. Compare with known commands. Return token in AL
; or 0FFh if not recognized.
;----------------------------------------------------------------------
CALL MAKEZ ;Copy word at SI to buffer
; and make ASCIIZ
PUSH SI ;Preserve SI
MOV SI,OFFSET CMD_TABLE ;Look up word in this table
CALL TABLE_LOOKUP ;Compare cmd in buffer
POP SI ;AL = CMD # or FFh
CMP AL,0FFH ;Is error?
JNE TKN_1A ;No, valid CMD found
MOV DX,OFFSET UNK_CMD_MSG
JMP SHORT TKN_ERR
TKN_1A:
;----------------------------------------------------------------------
; If this is a PROGRAM statement, load the name of the program into the
; buffer. Must be the first command in the file.
;----------------------------------------------------------------------
CMP PROGRAM_NAME_FLAG,0 ;Test if already had PROGRAM
PUSHF ;Save result
CMP AL,_PROGRAM ;Is this PROGRAM?
JE TKN_2 ; yes, jump
POPF ;Did we have one?
JNZ TKN_4 ; yes, try next cmd
JMP TKN_7A ; no, error
TKN_2:
POPF ; yes, Did we have one?
JZ TKN_3 ; No, this is first
MOV DX,OFFSET DUP_CMD_MSG ; Yes, indicate error
JMP SHORT TKN_ERR
TKN_3:
;----------------------------------------------------------------------
; Copy the name of the program to the output buffer.
;----------------------------------------------------------------------
MOV BX,DI ;Point BX to start of buf
MOV AL,SPACE ;Fill name with spaces
MOV CX,10 ;10 chars
REP STOSB ;do it
CALL NEXT_WORD
CALL TKN_STRING ;Copy quoted string
INC PROGRAM_NAME_FLAG ;Make flag non-zero
JMP FIND_WORD ;Get next command
TKN_4:
;----------------------------------------------------------------------
; END command terminates processing.
;----------------------------------------------------------------------
CMP AL,_END ;If not END, jump
JNE TKN_4A
MOV AL,MENU_FLAG ;If no open menus
OR AL,OPTION_FLAG ; or options
JZ NO_OPENS ; then clean up
TKN_4AA:
MOV DX,OFFSET NEST_MSG ;Else, nesting error
JMP SHORT TKN_ERR
NO_OPENS:
MOV AL,NEW_REF ;If no unbalanced menu
OR AL,OLD_REF ; references
JZ REFS_OK ; write the results
MOV DX,OFFSET REF_MSG ;Else, error
TKN_ERR:
JMP ERROR_EXIT
REFS_OK:
;----------------------------------------------------------------------
; Flush the output buffer.
;----------------------------------------------------------------------
MOV DX,OFFSET OUTPUT_BUF ;Source buffer
MOV CX,DI ;End of buf
SUB CX,DX ; minus start = length
MOV AH,40H ;Write to file
MOV BX,OUTPUT_HNDL ; this handle
INT 21H ;Thru DOS
RET ;Return to top level
;----------------------------------------------------------------------
; Is this a command to start a MENU block?
;----------------------------------------------------------------------
TKN_4A:
CMP AL,_MENU
JNE TKN_6 ;Jump if not MENU
;----------------------------------------------------------------------
; If we are already inside an active menu block, either an MEND is
; missing or the menus are nested (both are errors).
;----------------------------------------------------------------------
CMP MENU_FLAG,0
JNE TKN_4AA ;Jump if in block
;----------------------------------------------------------------------
; Execute MENU block startup code. Point BX to a buffer where the
; tokenized output will be temporarily stored. When then menu block is
; closed, the addresses in the menu header will be adjusted by the length
; of the header.
;----------------------------------------------------------------------
TKN_5:
MOV BX,OFFSET MENU_BUF ;Buffer for menu tokens
MOV MENU_HEAD,DI ;Location of menu header
XOR AX,AX ;Number of OPTIONS = 0
STOSW ; placed in header (at DI)
INC MENU_FLAG ;Say we're inside menu
;----------------------------------------------------------------------
; Make sure the MENU command is followed by a name
;----------------------------------------------------------------------
CALL NEXT_WORD ;Point SI to next word
CMP AL,CR ;If CR, no name = error
JE TKN_8A
;----------------------------------------------------------------------
; If Menu name is in table, it was referenced by an earlier menu and
; the address must be put back into that command line. If it's not in
; the table, add it and point to it's address.
;----------------------------------------------------------------------
CALL MAKEZ ;Change menu name to ASCIIZ
MOV AX,MENU_HEAD ;Signal says posting
SUB AX,OFFSET OUTPUT_BUF ;Make into offset
CALL SEARCH_MENU_TABLE ;Enter in table or resolve
; reference or error
CALL NEXT_LINE ;Skip to next line
JMP FIND_WORD ;Continue parsing
;----------------------------------------------------------------------
; If not a MENU command, must be inside a block to be valid.
;----------------------------------------------------------------------
TKN_6:
CMP MENU_FLAG,0
JE TKN_7A ;Jump if outside block
TKN_7:
;----------------------------------------------------------------------
; Inside a MENU block must reside a series of OPTION blocks.
;----------------------------------------------------------------------
CMP OPTION_FLAG,0 ;Test if in option block
PUSHF ; save result
CMP AL,_OPTION
JE TKN_8 ;Jump if OPTION
POPF ;Inside option block?
JNE TKN_12 ;Jump if yes
TKN_7A:
MOV DX,OFFSET CMD_ORDER_MSG
JMP TKN_ERR
TKN_8:
POPF ;Inside option block?
JE NEW_OPTION_BLOCK ;Jump if not
;----------------------------------------------------------------------
; Close this option block by adding the SEND command.
;----------------------------------------------------------------------
MOV AL,_SEND ;Send token
CALL PUT_MENUBUF ; to buffer
;----------------------------------------------------------------------
; To start new option block, increase the option counter.
; Parse name and help line and point the pointers at them.
;----------------------------------------------------------------------
NEW_OPTION_BLOCK:
INC OPTION_FLAG ;Inside option block
MOV BP,MENU_HEAD ;Number options this MENU
INC WORD PTR [BP] ; increase by 1
;----------------------------------------------------------------------
; Save the option name
;----------------------------------------------------------------------
CALL NEXT_WORD ;Point to OPTION NAME
CMP AL,CR ;Option must have name
JNE TKN_9 ;Jump if name
TKN_8A:
MOV DX,OFFSET NO_NAME_MSG
JMP TKN_ERR
TKN_9:
CALL MAKEZ ;Make name at SI into ASCIIZ
MOV AX,BX ;Address where name is stored
SUB AX,OFFSET MENU_BUF ; from start of buffer
STOSW ; is saved in menu header
;----------------------------------------------------------------------
; Copy the option name from the asciiz buffer to the menu buffer
;----------------------------------------------------------------------
PUSH SI ;Save register
MOV SI,OFFSET PATH_BUF ;Source for copy
COPY_NAME:
LODSB ;Get char
CALL PUT_MENUBUF
OR AL,AL ;If not last byte
JNZ COPY_NAME ;continue copy
POP SI ;Restore register
CALL NEXT_WORD ;Look for help line
;----------------------------------------------------------------------
; HELP line is quoted string
;----------------------------------------------------------------------
TKN_11:
MOV AX,BX ;Offset from start of buf
SUB AX,OFFSET MENU_BUF ; is location to put
STOSW ; in header
CALL TKN_STRING
;----------------------------------------------------------------------
; Put the offset where the tokenized commands will start into the header.
;----------------------------------------------------------------------
MOV AX,BX ;Current location
SUB AX,OFFSET MENU_BUF ; minus start is offset
STOSW ;Put word
JMP FIND_WORD
;----------------------------------------------------------------------
; The MEND command requires some cleanup to be performed.
; The length of the header is calculated and added to the offsets in
; the header. Then the menu_buf is appended to the output_buf.
;----------------------------------------------------------------------
TKN_12:
CMP AL,_MEND
JNE TKN_12AA ;If not MEND, move on
;----------------------------------------------------------------------
; Close out the option block (if there was one).
;----------------------------------------------------------------------
MOV AL,_SEND ;Write send token
CALL PUT_MENUBUF
;----------------------------------------------------------------------
; Flush the buffers.
;----------------------------------------------------------------------
PUSH SI ;Save register
MOV SI,MENU_HEAD ;Address of start of menu
; in the output file
LODSW ;Number of entries in menu
MOV CX,AX ;#Words = # entries * 3
SHL CX,1 ; *2
ADD CX,AX ; +1 is same as *3
JCXZ APPEND_TOKENS ;NULL menu bailout
PUSH CX ;Save number words to update
MOV AX,CX ;Length of offset
INC AX ; plus one
SHL AX,1 ; times two
MOV CX,MENU_HEAD ;Offset of start of menu
SUB CX,OFFSET OUTPUT_BUF ; from start of buf
ADD AX,CX ; plus length of header
POP CX
UPDATE_POINTERS:
ADD WORD PTR [SI],AX ; makes pointers correct
INC SI ;Skip to next pointer
INC SI
LOOP UPDATE_POINTERS
;----------------------------------------------------------------------
; Append the tokenized menu.
;----------------------------------------------------------------------
APPEND_TOKENS:
MOV CX,BX ;End of menu buf
MOV SI,OFFSET MENU_BUF ;start of menu buf
SUB CX,SI ;# bytes to transfer
REP MOVSB ;do it
POP SI ;restore register
;----------------------------------------------------------------------
; Reset the flags.
;----------------------------------------------------------------------
MOV MENU_FLAG,0
MOV OPTION_FLAG,0
JMP FIND_WORD ;get next command
;----------------------------------------------------------------------
; Deal with commands other than PROGRAM, MENU, and OPTION
;----------------------------------------------------------------------
TKN_12AA:
CALL PUT_MENUBUF ;Put the token in the file
CMP AL,_EXECUTE ;If not execute
JNE TKN_13 ; move on
;----------------------------------------------------------------------
; EXECUTE command references another menu. Let the SEARCH_MENU_TABLE
; routine satisfy the reference, or leave a "wanted" message.
;----------------------------------------------------------------------
CALL NEXT_WORD ;Point to named menu
CMP AL,CR ;End of line?
JNE TKN_12A
JMP TKN_8A ;(out of range jump)
TKN_12A:
CALL MAKEZ ;Put name in buffer
MOV AX,0FFFFH ;= want address
CALL SEARCH_MENU_TABLE ;Try and resolve
JMP FIND_WORD
;----------------------------------------------------------------------
; Separate out commands that take arguments.
;----------------------------------------------------------------------
TKN_13:
CMP AL,_ASK ;Ask takes string
JE TKN_17
CMP AL,_TYPE ;So does type
JE TKN_17
JMP FIND_WORD ;Token already in, so go
;----------------------------------------------------------------------
; These commads require the quoted string following them to be included
; as an ASCIIZ string following the command byte.
;----------------------------------------------------------------------
TKN_17:
CALL NEXT_WORD ;Point to string
CALL TKN_STRING ;Copy it
JMP FIND_WORD
TOKENIZE ENDP
;======================================================================
; AX points to an ASCIIZ string. Look up that string in table pointed
; to by SI to see if we can find a match.
;----------------------------------------------------------------------
TABLE_LOOKUP PROC NEAR
PUSH DI ;Save registers
PUSH CX
PUSH DX
XOR CL,CL ;Command counter
CL_0:
CMP BYTE PTR [SI],0 ;End of table?
JNZ CL_2 ; jump if not
MOV CL,0FFH ;Signal error
CL_1:
MOV AL,CL ;Return cmd number
POP DX ;Restore registers
POP CX
POP DI
RET
CL_2:
MOV DI,AX ;Pointer to unknown command
CALL STR_CMP ;NC if matched
JNC CL_1
INC CL ;Point to next
JMP CL_0 ;Test for table end
TABLE_LOOKUP ENDP
;======================================================================
; Compare strings at DI,SI where SI is a table.
;----------------------------------------------------------------------
STR_CMP PROC NEAR
STR_1:
MOV DL,[DI] ;Get char from string
INC DI ;Point to next
MOV DH,[SI] ;Get char from table
INC SI ;Point to next
CMP DH,DL ;If equal
JE STR_3 ; jump
DEC SI ;Backup in table
STR_2:
MOV DH,[SI] ;Examine char in table
INC SI ;Goto next char
OR DH,DH ;Is it end of entry?
JNZ STR_2 ; no, continue scanning
STC ; return with failure
RET
STR_3:
OR DX,DX ;If both not 0
JNZ STR_1 ; continue compare
CLC ; else, they match
RET
STR_CMP ENDP
;======================================================================
; This procedure prints an error message and indicates the source line
; number on which it occured.
;----------------------------------------------------------------------
ERROR_EXIT PROC NEAR
MOV AH,9 ;Print the error message
INT 21H ; Thru DOS
MOV DI,OFFSET LINE_NUM_BUF ;Put line number here
MOV AX,SOURCE_LINE ;Contains line number
MOV BX,10 ;Base 10 numbers
LINE_LOOP:
XOR DX,DX ;Divide DX:AX by BX
DIV BX
ADD DL,30H ;Make number into ASCII
MOV [DI],DL ;Save it
DEC DI ;Move toward more significant
OR AX,AX ;If remainder not 0
JNZ LINE_LOOP ; continue
MOV DX,OFFSET LINE_MSG ;Write message
MOV AH,9 ;Display string fn
INT 21H ; Thru DOS
CALL CLOSE_FILES ;Close files and terminate
MOV AX,4CFFH ; with error=255
INT 21H ; Thru DOS
ERROR_EXIT ENDP
;======================================================================
; Position SI to the begining of the next word.
;----------------------------------------------------------------------
NEXT_WORD PROC NEAR
CALL IS_WHITE ;Is current char white?
JNC NW_1 ; yes, jump
CALL WHITE ; no, find first white
NW_1:
CALL NON_WHITE ;Scan for first non-white
RET
NEXT_WORD ENDP
;======================================================================
; Enter with SI pointing to string. Position so SI points at first
; delimiter character AFTER the present char. Return char in AL.
; AL changed, SI moved by file routines.
;----------------------------------------------------------------------
WHITE PROC NEAR
MOV AL,[SI] ;Check current char
CMP AL,CR ; for EOL
JE W_RET ; and leave
W_LOOP:
CALL GET_CHAR ;Get the next char
CMP AL,CR ;If EOL
JE W_RET ; return
CALL IS_WHITE ;Returns NC if white
JC W_LOOP
W_RET:
RET
WHITE ENDP
;======================================================================
; Enter with SI pointing to string. Position so SI points at first
; non-delimiter character AFTER the present char. Return char in AL.
; AL changed, SI moved by file routines.
;----------------------------------------------------------------------
NON_WHITE PROC NEAR
MOV AL,[SI] ;Check current char
CMP AL,CR ; for EOL
JE NW_RET ; and leave
NW_LOOP:
CALL GET_CHAR ;Get the next char
CMP AL,CR ;If EOL
JE NW_RET ; return
CALL IS_WHITE ;Returns NC if white
JNC NW_LOOP
NW_RET:
RET
NON_WHITE ENDP
;======================================================================
; Examine Char at SI and return CY if non-white, NC if delimiter
; Only flags changed.
;----------------------------------------------------------------------
DELIMS DB " ,:",LF,TAB ;White chars
IS_WHITE PROC NEAR
PUSH AX ;Save registers
PUSH CX
PUSH DI
MOV AL,[SI] ;Examine current char
MOV CX,5 ;Number delims to compare
MOV DI,OFFSET DELIMS ;Here they are
REPNE SCASB ;Compare them
CLC ;NC if delimiter
JZ IW_1 ;ZR indicates match found
STC ; else CY
IW_1:
POP DI ;Restore registers
POP CX
POP AX
RET
IS_WHITE ENDP
;======================================================================
; Make the character in AL UPPER case.
;----------------------------------------------------------------------
MAKE_UC PROC NEAR
CMP AL,'a' ;If between a and z
JB UC_1
CMP AL,'z'
JA UC_1
SUB AL,20H ;Make upper case
UC_1:
RET
MAKE_UC ENDP
;======================================================================
; Position SI to the next char after a CR. Start search with current
; char.
;----------------------------------------------------------------------
NEXT_LINE PROC NEAR
MOV AL,[SI] ;Examine current char
NL_1:
CMP AL,CR ;Is it CR?
JE NL_2 ; yes, jump
CALL GET_CHAR ; no, get next char
JMP NL_1 ; and try again
NL_2:
CALL GET_CHAR ;Go to first char after CR
RET
NEXT_LINE ENDP
;======================================================================
; This proc helps resolve references to other menus made with
; EXECUTE commands. If entered with AX=FFFF, the calling procedure has
; a reference it needs to satisfy. If an address has been entered in
; the table for that name, the address is returned and the reference is
; satisfied. If not, a "IOU" is inserted in the table.
; If entered with AX != FFFFh, the calling procedure is supplying the
; starting address of the MENU named at PATH_BUF, and asking that these
; be entered into the table to satisfy a reference. If the name is found
; in the table, left in a previous bookmark, the reference is satisfied
; and the entry is marked "USED". Any further use of that name generates
; an error.
; For a forward reference, the structure is:
; ASCIIZ string
; WORD - Address of the start of the menu that
; contains the reference (menu_head)
; WORD - Offset from the end of menu header that
; is the destination for the fix-up
; For a POST:
; ASCIIZ string
; WORD - Address in output file of menu
; WORD - 0FFFFh
; Possibilities:
; 1) Post a MENU not previously requested
; 2) Post a MENU previously requested
; 3) Request a MENU previously posted
; 4) Ask for a MENU not previously posted
;----------------------------------------------------------------------
SEARCH_MENU_TABLE PROC NEAR
PUSH SI ;Save used registers
PUSH DI
PUSH DX
PUSH CX
;----------------------------------------------------------------------
; AX=FFFF if request. AX != FFFF, then post the MENU address.
;----------------------------------------------------------------------
CMP AX,0FFFFH ;Switch = FFFF if request
JE WANT_ADR ; to satisfy reference
;----------------------------------------------------------------------
; AX contains the output file offset of a menu to post in this table.
; If the name is in the table, it was referenced earlier and we can go
; back and fix it up. If the name is NOT in the table, post it.
;----------------------------------------------------------------------
CALL MENU_LOOK ;Changes SI
JNC SMT_7 ;Match was found if NC
;----------------------------------------------------------------------
; 1) Post a menu not previously requested. No match found. Create a
; new entry for this menu. SI points to end of table.
; Copy the name from the buffer to the menu table.
;----------------------------------------------------------------------
INC NEW_REF ;Add a new reference
MOV CX,0FFFFH ;POST code
SMT_1:
MOV DI,OFFSET PATH_BUF ;Name of MENU is here
;----------------------------------------------------------------------
; Copy menu name to menu_name_table
;----------------------------------------------------------------------
SMT_5:
MOV DL,[DI] ;Get char in DL
INC DI ;Point to next
MOV [SI],DL ;Save this char in table
INC SI ;next destination
OR DL,DL ;Was that last byte?
JNZ SMT_5 ; no, get more
;----------------------------------------------------------------------
; Make AX into offset from buffer start. Save in menu table.
; CX contains either offset from menu_buf of reference or FFFF (POST).
;----------------------------------------------------------------------
MOV WORD PTR [SI],AX ;Menu at this address
MOV WORD PTR [SI][2],CX
JMP SHORT SMT_EXIT ;clean up
;----------------------------------------------------------------------
; 2) Post a MENU previously requested. SI points past the ASCIIZ name
; in the table to the location of the forward reference.
;----------------------------------------------------------------------
SMT_7:
INC OLD_REF ;Add an old reference
MOV DX,WORD PTR [SI][2] ;2nd word
CMP DX,0FFFFH ; if POST, two menus have
JE SMT_7B ; the same name (error)
CMP DX,0FFFEH ;Already referenced
JNE SMT_8 ; is also an error
SMT_7B:
MOV DX,OFFSET REF_MSG ;error
JMP ERROR_EXIT
SMT_8:
;----------------------------------------------------------------------
; SI points to address of menu as offset from start of buffer.
; SI+2 points to additional offset from end of header.
; Address of reference is [si] + 2 + 3 * 2 * [[si]] + [si+2] !
;----------------------------------------------------------------------
MOV DI,WORD PTR [SI] ;Offset into output_buf
ADD DI,OFFSET OUTPUT_BUF ;Absolute memory location
MOV CX,WORD PTR [DI] ;Number of entries in header
SHL CX,1 ; 2 bytes per word
ADD DI,CX
INC CX ; 2 for first word
SHL CX,1
ADD DI,CX
ADD DI,WORD PTR [SI][2]
MOV [DI],AX ; reference satisfied
MOV WORD PTR [SI][2],0FFFEH ;Mark this entry USED
JMP SHORT SMT_EXIT ;Clean up
;----------------------------------------------------------------------
; Try and satisfy a reference.
;----------------------------------------------------------------------
WANT_ADR:
CALL MENU_LOOK ;CY if no match
JC SMT_9
;----------------------------------------------------------------------
; 3) Request a MENU previously posted. Entry name was in table.
;----------------------------------------------------------------------
DEC NEW_REF
MOV DX,WORD PTR [SI][2] ;Second word
CMP DX,0FFFFH ; should contain POST code
JNE SMT_7B ;if not, ERROR!
MOV AX,[SI] ;Get address of menu
CALL PUT_MENUBUF ;Put lower byte
MOV AL,AH
CALL PUT_MENUBUF ;Put upper byte
MOV WORD PTR [SI][2],0FFFEH ;Mark entry used
JMP SHORT SMT_EXIT
;----------------------------------------------------------------------
; 4) Ask for a MENU not previously posted. Put an IOU in the table.
;----------------------------------------------------------------------
SMT_9:
DEC OLD_REF
MOV AX,MENU_HEAD ;First entry is offset of
SUB AX,OFFSET OUTPUT_BUF ; requesting menu
MOV CX,BX ;Second entry is offset past
SUB CX,OFFSET MENU_BUF ; the menu header
ADD BX,2 ;Leave room for address
JMP SMT_1 ;Go create table entry
SMT_EXIT:
POP CX ;Restore registers
POP DX
POP DI
POP SI
RET
SEARCH_MENU_TABLE ENDP
;======================================================================
; Look for a name in the menu. If carry is set, no match was found
; and SI points to the end of the table. If carry is clear, match was
; found and SI points to address word after entry.
; SI is changed.
;----------------------------------------------------------------------
MENU_LOOK PROC NEAR
PUSH DI ;Save registers
PUSH DX
MOV SI,OFFSET MENU_NAME_TBL ;Known names
ML_1:
CMP BYTE PTR [SI],0 ;If 0, end of table
JNE ML_3 ; else compare entry
STC ;signal error
ML_2:
POP DX ;Restore registers
POP DI
RET
ML_3:
MOV DI,OFFSET PATH_BUF ;Name to find
CALL STR_CMP ;CY if no match
JNC ML_2 ;Return result
ADD SI,4 ;Skip past addresses
JMP ML_1 ;Check next table entry
MENU_LOOK ENDP
;======================================================================
; Interpret the contents of the quoted string and write to output buffer.
; Entered with SI pointing to the beginning quote. An illegal character
; or combination produces an error and terminates processing.
;----------------------------------------------------------------------
TKN_STRING PROC NEAR
CMP BYTE PTR [SI],QUOTE ;Must point to quoted string
JNE TS_ERR ; or syntax error.
PUSH DI ;Save used register
TS_1:
MOV SHIFT_FLAGS,0 ;Clear shift flags
TS_1AA:
CALL GET_CHAR ;Get next char
CMP AL,CR ;If not CR
JNE TS_QUOTE ; go to next test
TS_ERR:
JMP SC_ERR ;Report a syntax error
TS_QUOTE:
CMP AL,QUOTE ;If quote
JNE TS_1A
TS_EXIT:
XOR AL,AL ; make string asciiz
CALL PUT_MENUBUF ; and write to buffer
CALL NEXT_LINE ;Position to next line
POP DI ;Restore register
RET ; and leave
TS_1A:
CMP AL,"{" ;Key name follows
JE SPEC_CHAR ;Go snif it out
CMP SHIFT_FLAGS,0 ;If any shift keys on
JNE DO_XLAT ; do translation
XOR AH,AH ;Else, ASCII output of AL
;----------------------------------------------------------------------
; If AH=0, output the code in AL; If AH != 0, output AH, then AL.
;----------------------------------------------------------------------
OUTPUT_CODE:
OR AH,AH ;If AH=0
JZ OC_1 ; output AL only
XCHG AH,AL ;Switch
CALL PUT_MENUBUF ;Write the high byte
XCHG AH,AL ;restore AL
OC_1:
CALL PUT_MENUBUF ;Put AL in buffer
JMP TS_1 ;Get another character
;----------------------------------------------------------------------
; These keys may be valid with CTRL and ALT, but not SHIFT
;----------------------------------------------------------------------
DO_XLAT:
CMP SHIFT_FLAGS,_SHIFT ;Is SHIFT on?
JNE TS_1B
DEAD_KEY_ERROR:
MOV DX,OFFSET DEAD_KEY_MSG ;Invalid key combo
JMP ERROR_EXIT
TS_1B:
;----------------------------------------------------------------------
; See if the key is in the table.
;----------------------------------------------------------------------
CALL MAKE_UC ;Make upper case
MOV AH,AL ;Save original char
MOV DI,OFFSET KEY_TBL_1 ;Source for compare
MOV CX,DI ;Save start of table
TS_2:
MOV AL,[DI] ;Get char from table
INC DI ;Point to next
OR AL,AL ;If 0, AL was illegal char
JE DEAD_KEY_ERROR
CMP AL,AH ;Do we have a match?
JNE TS_2 ;No, try again
DEC DI ;Backup pointer
MOV AX,DI ;Have a match if here
SUB AX,CX ;Offset into table
;----------------------------------------------------------------------
; Found a match. Tediously calculate output byte.
;----------------------------------------------------------------------
CMP AL,25 ;If A-Z
JBE IS_ALPHA ; process as alpha keys
SUB AL,26 ;Remove alpha bias
CMP SHIFT_FLAGS,_CTRL ;Go perform CTRL combos
JE TS_4
;These are ALT combos
CMP AL,11
JA DEAD_KEY_ERROR ;Too high
ADD AX,0FE78H ;Output 2 bytes
JMP OUTPUT_CODE
TS_4: ;CTRL combos
SHL AL,1 ;multiply by 2
MOV DI,OFFSET KEY_TBL_2
ADD DI,AX ;Find word in table
MOV AX,[DI] ;Load into AX
OR AX,AX ;If zero
JZ DEAD_KEY_ERROR ; this combo not allowed
JMP OUTPUT_CODE ; else write it
;----------------------------------------------------------------------
; CTRL/ALT alphabet combinations.
;----------------------------------------------------------------------
IS_ALPHA:
CMP SHIFT_FLAGS,_ALT
JE TS_5
INC AL ;ALT is simple, add 1
JMP OUTPUT_CODE
TS_5:
MOV DI,OFFSET KEY_TBL_3 ;CTRL keys combos
ADD DI,AX ;Add offset to
MOV AL,[DI] ; get char code
MOV AH,0FEH ;Signal extended ASCII
JMP OUTPUT_CODE ;Send it to buffer
;----------------------------------------------------------------------
; Here the {strings} are translated to their equivalent keystrokes.
; Move the key name into the ASCIIZ buffer.
;----------------------------------------------------------------------
SPEC_CHAR:
MOV DI,OFFSET PATH_BUF
SC_1:
CALL GET_CHAR ;Get char inside brace
CMP AL,'{' ;If doubled, use {
JNE SC_2 ;Jump for other chars
CMP DI,OFFSET PATH_BUF ;If = no chars have been
JE OC_1 ; processed yet. {{
SC_ERR:
MOV DX,OFFSET SYNTAX_MSG
JMP ERROR_EXIT
SC_2:
CMP AL,QUOTE ;If char is quote
JE SC_ERR
CMP AL,'}' ;Close brace?
JE SC_3 ; go search table
CALL MAKE_UC ;Make Upper case
STOSB ;Put char in buffer
JMP SC_1 ;Continue to copy key name
SC_3:
XOR AL,AL ;Make asciiz
STOSB
PUSH SI ;Save register
MOV SI,OFFSET KEY_NAME_TBL ;Search this table
MOV AX,OFFSET PATH_BUF ; for this entry
CALL TABLE_LOOKUP
POP SI ;Restore register
;----------------------------------------------------------------------
; Return # of entry in table or FFh if not found.
;----------------------------------------------------------------------
CMP AL,0FFH
JE SC_ERR
OR AL,AL ;0 = Turn SHIFT on
JNZ SC_4
MOV SHIFT_FLAGS,_SHIFT
JMP TS_1AA
SC_4:
DEC AL ;0 = Turn CTRL on
JNZ SC_5
MOV SHIFT_FLAGS,_CTRL
JMP TS_1AA
SC_5:
DEC AL ;0 = Turn ALT on
JNZ SC_6
MOV SHIFT_FLAGS,_ALT
JMP TS_1AA
;----------------------------------------------------------------------
; Test for the Function keys (40 combinations).
;----------------------------------------------------------------------
SC_6:
DEC AL ;Adjust key number
CMP AL,9 ;Keys 0-9
JA SC_10 ;Can't be function key
ADD AL,3BH ;Key code
MOV AH,SHIFT_FLAGS ;Test for shift states
CMP AH,_SHIFT ;For shift
JNE SC_7
ADD AL,19H ;Add 19h
SC_7:
CMP AH,_CTRL ;For CTRL
JNE SC_8
ADD AL,23H ;Add 23h
SC_8:
CMP AH,_ALT ;For ALT
JNE SC_9
ADD AL,2DH ;Add 2dh
SC_9:
MOV AH,0FEH ;Say this is extended ascii
JMP OUTPUT_CODE ;Dump the bytes
;----------------------------------------------------------------------
; ALT is not allowed for these keys.
;----------------------------------------------------------------------
SC_10:
CMP SHIFT_FLAGS,_ALT ;If Alt's not on
JNE SC_10B ; jump
SC_10A:
JMP DEAD_KEY_ERROR ; else, error
SC_10B:
SUB AL,10 ;Eliminate BIAS
JNZ SC_11
MOV AL,01BH ;ESC key
JMP OC_1
SC_11:
DEC AL ;TAB key
JNZ SC_13
CMP SHIFT_FLAGS,0 ;Any shift keys on?
JE SC_12 ; no, jump
CMP SHIFT_FLAGS,_SHIFT ;SHIFT only is allowed
JNE SC_10A ; else error
MOV AX,0FE0FH ;SHIFT-TAB
JMP OUTPUT_CODE ;to output
SC_12:
MOV AL,9 ;Normal TAB
SC_12A:
JMP OC_1
;----------------------------------------------------------------------
; These keys are the same alone or with SHIFT, but CTRL is different.
;----------------------------------------------------------------------
SC_13:
DEC AL ;Remove bias
XOR AH,AH ;Zero AH
MOV DI,OFFSET KEY_TBL_4 ;Table of SHIFT/CTRL values
SHL AX,1 ;Make into offset
ADD DI,AX ; from start of table
MOV DH,SHIFT_FLAGS ;If CTRL
CMP DH,_CTRL
JNE SC_14
INC DI ; move one byte further
SC_14:
MOV DL,[DI] ;Get char from table
OR DL,DL ; =0 if illegal
JZ SC_10A
XCHG AL,DL ;Put offset in DL
CMP DL,2 ;What key?
JE SC_12A ;If BS, all ascii
JA SC_15 ;above, all extended
CMP DH,_CTRL ;CTRL-ENTER is special
JNE SC_12A
SC_15:
MOV AH,0FEH ;Add extended code
JMP OUTPUT_CODE
TKN_STRING ENDP
;======================================================================
; Read from the BDF file into a buffer. If an EOF is found, perform
; the final cleanup and return to the main procedure.
; (must also keep track of file pointer and signal when EOF)
;----------------------------------------------------------------------
REFRESH_BUFFER PROC NEAR
PUSH BX
PUSH CX
PUSH DX
MOV AH,42H ;Move file pointer
XOR AL,AL ; offset from begining
MOV BX,INPUT_HNDL ;File handle
XOR CX,CX ;Offset CX:DX
MOV DX,INPUT_HNDL_PTR
INT 21H ;Thru DOS
JC READ_ERR
;----------------------------------------------------------------------
; Fill buffer.
;----------------------------------------------------------------------
MOV AH,3FH ;Read from handle fn
MOV BX,INPUT_HNDL
MOV CX,INPUT_BUF_LEN ;Number bytes to read
MOV SI,OFFSET INPUT_BUF
MOV DX,SI ;DS:DX destination
CMP [INPUT_HNDL_PTR],0 ;If start of file
JNE NOT_FIRST_READ
MOV BYTE PTR [SI],20H ;Make a current char
DEC CX ;Read one less byte
INC DX ;New destination
NOT_FIRST_READ:
INT 21H ;Thru DOS
JNC READ_OK
READ_ERR:
MOV DX,OFFSET READ_MSG
JMP ERROR_EXIT
READ_OK:
ADD INPUT_HNDL_PTR,AX ;Add bytes read
SUB DX,SI ;Calculate ending address
ADD AX,DX ; of our buffer
MOV INPUT_BUF_END,AX ; and save it
POP DX ;Restore registers
POP CX
POP BX
RET
REFRESH_BUFFER ENDP
;======================================================================
; Copy the word at SI into a buffer and make it asciiz
;----------------------------------------------------------------------
MAKEZ PROC NEAR
PUSH DI
MOV DI,OFFSET PATH_BUF ;Destination
PUSH DI
MZ_1:
CALL IS_WHITE ;Examine char at SI
JNC MZ_2
MOV AL,[SI] ;Get the char
CMP AL,CR ;Is it CR?
JE MZ_2 ; yes, terminate string
CALL MAKE_UC ; else, change to lower case
STOSB ; save at DI
CALL GET_CHAR ;Get next char
JMP MZ_1
MZ_2:
XOR AL,AL ;Make ASCIIZ
STOSB
POP AX ;Return buffer address
POP DI ;Restore pointer
RET
MAKEZ ENDP
;======================================================================
; Get a char from the input buffer. If past the end of the buffer,
; refresh the buffer. Check for EOF (SI = INPUT_BUF_END != BUF_LEN)
; If comments are detected, they are weeded out.
;----------------------------------------------------------------------
COMMENT_FLAG DB 0 ;=1 if inside comment
GET_CHAR PROC NEAR
;----------------------------------------------------------------------
; Check buffer for pointer past end.
;----------------------------------------------------------------------
PUSH AX ;Save register
INC SI ;Advance pointer
MOV AX,SI ;Current location
SUB AX,OFFSET INPUT_BUF ; minus start
CMP AX,INPUT_BUF_END ; longer than allowed
POP AX ;(restore pointer)
JB GC_1 ; no need to refresh
CALL REFRESH_BUFFER ;Attempt to load buffer
;----------------------------------------------------------------------
; AX contains the number of chars read. If AX=0, unexpected EOF.
;----------------------------------------------------------------------
OR AX,AX ;# chars read
JNZ GC_1 ;jump if not 0
MOV DX,OFFSET EOF_MSG
JMP ERROR_EXIT
;----------------------------------------------------------------------
; check for, and eliminate comments
;----------------------------------------------------------------------
GC_1:
MOV AL,[SI] ;Get new char
CMP AL,CR ;If new line
JNE GC_3
MOV COMMENT_FLAG,0 ; reset comment
INC SOURCE_LINE ; next line
GC_2:
CMP COMMENT_FLAG,0 ;If inside comment
JNE GET_CHAR ; read until CR
RET
GC_3:
CMP AL,';' ;If this isn't a comment
JNE GC_2 ; then continue
INC COMMENT_FLAG ;else read until CR
JMP GET_CHAR
GET_CHAR ENDP
;======================================================================
PUT_MENUBUF PROC NEAR
MOV [BX],AL ;Write byte to [bx]
INC BX ;Move pointer
RET
PUT_MENUBUF ENDP
;======================================================================
; Buffer Allocation area
;----------------------------------------------------------------------
PC = $
LAST_BYTE = PC
PATH_BUF = PC ;DB 64 DUP (?)
PC = PC + 64
INPUT_BUF = PC ;DB INPUT_BUF_LEN DUP(?)
PC = PC + INPUT_BUF_LEN
OUTPUT_BUF = PC ;DB OUTPUT_BUF_LEN DUP(?)
PC = PC + OUTPUT_BUF_LEN
MENU_BUF = PC ;DB MENU_BUF_LEN DUP(?)
PC = PC + MENU_BUF_LEN
MENU_NAME_TBL = PC ;DB MENU_TBL_ELN DUP(?)
PC = PC + MENU_TBL_LEN
;----------------------------------------------------------------------
CSEG ENDS
END ENTPT