Category : Files from Magazines
Archive   : VOL10N19.ZIP
Filename : PATCH.ASM

 
Output of file : PATCH.ASM contained in archive : VOL10N19.ZIP
;--------------------------------------------------------------------;
; PATCH * Michael J. Mefford ;
; ;
; Program customizer ;
;--------------------------------------------------------------------;

_TEXT SEGMENT PUBLIC 'CODE'
ASSUME CS:_TEXT,DS:_TEXT,ES:_TEXT,SS:_TEXT

ORG 100H
START: JMP MAIN

; DATA AREA
; ---------
COLOR_ATTRIBS STRUC
B DB 71H ;Blue on lt. gray ;Menu
W DB 17H ;Lt. gray on blue ;Inactive window
C DB 31H ;Blue on cyan ;Active bar
I DB 1FH ;White on blue ;Active window
A DB 07H ;Lt. gray on black ;Inactive bar
S DB 10H ;Black on blue ;Unavailable entry
T DB 01H ;Blue on black ;Unavailable bar
COLOR_ATTRIBS ENDS

COLOR COLOR_ATTRIBS <>

COLOR_ATTR COLOR_ATTRIBS <>
MONO_ATTR COLOR_ATTRIBS <70H, 07H, 70H, 0FH, 70H, 00H, 00H>

BORDER_FLAG DB 0 ; =1 to disable.
XMODEM_FLAG DB FALSE ; =1 to disable file size check.

COPYRIGHT DB "PATCH 1.0 Copyright (c) 1991 Michael J. Mefford",CR,LF
FIRST_RIGHTS DB "First Published in PC Magazine, November 12, 1991",CR,LF,LF

SYNTAX LABEL BYTE
DB "Syntax: PATCH [.ZIF directory] [program directory]",CR,LF,LF

DB "$"

TAB EQU 9
CR EQU 13
LF EQU 10
CTRL_Z EQU 26
SPACE EQU 32
BOX EQU 254
FF EQU 12
SINGLE_QUOTES EQU 44
DOUBLE_QUOTES EQU 34
SHIFT_KEYS EQU 00000011B
RIGHT_SHIFT EQU 00000001B
LEFT_SHIFT EQU 00000010B
CTRL_KEY EQU 00000100B
ALT_KEY EQU 00001000B
ESC_SCAN EQU 01H
Y_SCAN EQU 15H
N_SCAN EQU 31H
F1_SCAN EQU 3BH
F2_SCAN EQU 3CH
F3_SCAN EQU 3DH
F4_SCAN EQU 3EH
F5_SCAN EQU 3FH
F6_SCAN EQU 40H
F7_SCAN EQU 41H
F8_SCAN EQU 42H
F9_SCAN EQU 43H
F10_SCAN EQU 44H
KB_FLAG EQU 17H
UP_SCAN EQU 48H
DOWN_SCAN EQU 50H
LEFT_SCAN EQU 4BH
RIGHT_SCAN EQU 4DH
PGUP_SCAN EQU 49H
PGDN_SCAN EQU 51H
BS_SCAN EQU 0EH
DEL_SCAN EQU 53H
HOME_SCAN EQU 47H
END_SCAN EQU 4FH
ENTER_SCAN EQU 1CH
TAB_SCAN EQU 0FH
CTRL_HOME_SCAN EQU 77H
CTRL_END_SCAN EQU 75H
PLUS1_SCAN EQU 0DH
PLUS2_SCAN EQU 4EH
MINUS1_SCAN EQU 0CH
MINUS2_SCAN EQU 4AH
SHIFT_F4 EQU 57H
SHIFT_F5 EQU 58H
SHIFT_F6 EQU 59H
SHIFT_F9 EQU 5CH
SHIFT_F10 EQU 5DH
CTRL_F2 EQU 5FH
ALT_F1 EQU 68H
SPACE_SCAN EQU 39H

LEFT_ARROW EQU 27
RIGHT_ARROW EQU 26
UP_ARROW EQU 24
DOWN_ARROW EQU 25
COMMA EQU ","
DECIMAL_POINT EQU "."
PLUS_SIGN EQU "+"
MINUS_SIGN EQU "-"
NOTE EQU 1046 ; C

PORT_A EQU 60H

TRUE EQU 1
FALSE EQU 0
FILE_CURRENT DB FALSE
LISTING_TYPE EQU 0
FILE_TYPE EQU 1
CUSTOM_TYPE EQU 2
SELECT_TYPE EQU 3
STATE DB LISTING_TYPE

DOS_VERSION DW ?
SCREEN_COLOR DB ?
BREAK DB ?

DEFAULT_DRIVE DB ?
CURRENT_DIR DB 66 DUP (?)
ZIF_DRIVE DB ?
ZIF_DIR DB 66 DUP (?)
SPEC_SIZE EQU $ - ZIF_DRIVE
PGM_DRIVE DB ?
PGM_DIR DB 66 DUP (?)

MATCHING STRUC
RESERVED DB 21 DUP (?)
ATTRIBUTE DB ?
FILE_TIME DW ?
FILE_DATE DW ?
SIZE_LOW DW ?
SIZE_HIGH DW ?
FILE_NAME DB 13 DUP (?)
MATCHING ENDS

FILE_RECORD STRUC
LIST_NAME DB 12 DUP (?)
FILE_RECORD ENDS

KILOBYTES = 1024
PARAGRAPH = 16
FILENAME_SIZE = 64 ;64K
ZIF_BUFF_SIZE = 64 ;64K
PGM_BUFF_SIZE = 64 ;64K
BAC_BUFF_SIZE = 64 ;64K
FILENAME_SEG DW ?
ZIF_BUFF_SEG DW ?
PGM_BUFF_SEG DW ?
BAC_BUFF_SEG DW ?

BUFFER_SIZES EQU (FILENAME_SIZE+ZIF_BUFF_SIZE+PGM_BUFF_SIZE+BAC_BUFF_SIZE) * (KILOBYTES/PARAGRAPH)

TEMP_RECORD EQU FILENAME_SIZE * KILOBYTES - SIZE FILE_RECORD

DTA DB SIZE MATCHING DUP (?)
ASCIIZ_NAME DB 13 DUP (?) ;ASCIIZ name.
BACKUP_NAME DB 13 DUP (?)

FAIL_FLAG DB FALSE ;True if open failed.
ROW DW 0

CRT_MODE EQU 49H
CRT_COLS EQU 4AH
CRT_ROWS EQU 84H
COLUMNS DW ?
CRT_WIDTH DW ?
CRT_START DW ?
STATUS_REG DW 3BAH
VIDEO_SEG DW 0B000H
ROWS DB 24

LISTING_TOP EQU 5
MENU_LINES EQU 6
CUSTOM_START EQU (SIZE FILE_RECORD + 1) * 2
COLOR_TOP EQU LISTING_TOP
SELECT_START EQU CUSTOM_START + ((CAT_NUL - CAT_TEXT + 2) * 2)
HOT_TOP EQU LISTING_TOP + 1
COM_TOP EQU LISTING_TOP + 2
LPT_TOP EQU LISTING_TOP + 3
TEXT_TOP EQU LISTING_TOP + 4
NUMBER_TOP EQU LISTING_TOP + 5
PICK_TOP EQU LISTING_TOP + 6
WINDOW_TOP EQU 24 / 2

LISTING_LEN DW ?
LISTING_ADDR DW 0
BAR_ADDR DW 0
LAST_ADDR DW ?

FILENAME DW ? ;Address of filespec to parse.
ZIF_BYTES DW ?
PGM_BYTES DW ?
FILE_CNT DW 0

MODIFY_FLAG DB FALSE

LISTING_MENU LABEL BYTE
DB "Select Utility then press Enter to select custom category. Esc to Exit",0

CUSTOM_MENU LABEL BYTE
DB "Select custom category then press Enter to customize. Esc to Exit",0

STAR_DOT_ZIF DB "*"
ZIF_EXT DB ".ZIF",0
ZIF_LEN EQU $ - ZIF_EXT
NONE_FOUND DB "No .ZIF files were found",CR,LF,LF,"$"
NOT_ENOUGH DB "Not enough memory",CR,LF,LF,"$"
CRLFLF DB CR,LF,LF,"$"

; CODE AREA
; ---------
KBD_STATUS DB ?

INT_9 PROC NEAR

PUSH AX
IN AL,PORT_A
CMP AL,0E0H
JZ INT_9_END
CMP AL,0E1H
JZ INT_9_END
MOV CS:KBD_STATUS,AL

INT_9_END: POP AX
JMP DWORD PTR CS:OLD9

INT_9 ENDP

;----------------------------------------------;
INT_24 PROC NEAR

STI
PUSHF
MOV CS:FAIL_FLAG,TRUE
MOV AL,3
CMP CS:DOS_VERSION,300H
JAE INT_24_END
XOR AL,AL
INT_24_END: POPF
IRET

INT_24 ENDP

;----------------------------------------------;

MAIN PROC NEAR
CLD

MOV AH,30H
INT 21H
XCHG AL,AH
MOV DOS_VERSION,AX

CALL INSTALL_9
CALL INSTALL_24

XOR BH,BH
MOV AH,8
INT 10H
MOV SCREEN_COLOR,AH

MOV AX,3300H ;Get Ctrl-Break state.
INT 21H
MOV BREAK,DL

XOR DL,DL
MOV AX,3301H
INT 21H

MOV AH,19H ;Get default drive so can
INT 21H ; restore after we change it.
MOV DEFAULT_DRIVE,AL

MOV BX,OFFSET STACK_POINTER + 15
MOV CL,4
SHR BX,CL
ADD BX,BUFFER_SIZES
MOV AH,4AH ;Allocate memory.
INT 21H
JNC SETUP_STACK
MOV DX,OFFSET NOT_ENOUGH
JMP ERROR_EXIT ;If not enough, exit.

SETUP_STACK: MOV AX,OFFSET STACK_POINTER
MOV SP,AX ;Set up stack.
ADD AX,15
MOV CL,4
SHR AX,CL
MOV CX,DS
ADD AX,CX
MOV FILENAME_SEG,AX ;And data segments.
ADD AX,FILENAME_SIZE * (KILOBYTES / PARAGRAPH)
MOV ZIF_BUFF_SEG,AX
ADD AX,ZIF_BUFF_SIZE * (KILOBYTES / PARAGRAPH)
MOV PGM_BUFF_SEG,AX
ADD AX,BAC_BUFF_SIZE * (KILOBYTES / PARAGRAPH)
MOV BAC_BUFF_SEG,AX

MOV DX,OFFSET DTA
MOV AH,1AH
INT 21H

CALL PARSE
CALL FIND_FILES
MOV DX,OFFSET NONE_FOUND
CMP FILE_CNT,0
JZ ERROR_EXIT
CALL VIDEO_SETUP
CALL HIDE_CURSOR
CALL DISP_DIR
MOV SI,OFFSET LISTING_MENU
CALL DISPLAY_MENU
CALL DISP_LISTING
CALL DISP_FILE
JMP SHORT NEXT_KEY

;************* Main Loop *************;

NEXT_KEY: CALL GETKEY
JC GOOD_EXIT
CMP AL,RIGHT_SCAN
JZ ENTER1
CMP AH,CR
JNZ CK_SPACE

ENTER1: CALL FILE
MOV STATE,LISTING_TYPE
CALL DISP_LISTING
CALL DISP_FILE
MOV SI,OFFSET LISTING_MENU
CALL DISPLAY_MENU
JMP NEXT_KEY

CK_SPACE: CMP AH,SPACE
JBE MAIN_DISPATCH
CALL SEARCH
JMP NEXT_KEY

MAIN_DISPATCH: MOV DI,OFFSET L_DISPATCH
MOV CX,L_LEN
CALL DISPATCH
CALL DISP_LISTING
JMP NEXT_KEY

;----------------------------------------------;

ERROR_EXIT: CALL PRINT_STRING
MOV AL,1
JMP SHORT EXIT

GOOD_EXIT: CALL CLOSE_SCREEN
XOR AL,AL ;EL=0.

EXIT: PUSH AX
CALL UNINSTALL_9
CALL RESTORE_DIR


MOV DL,BREAK
MOV AX,3301H
INT 21H

TERMINATE: MOV DX,OFFSET COPYRIGHT
CALL PRINT_STRING
POP AX
MOV AH,4CH
INT 21H

MAIN ENDP


;**************
; SUBROUTINES *
;**************
BACKUP_FLAG DB ? ;=TRUE if backup made.

FILE: MOV BACKUP_FLAG,FALSE
CMP FILE_CURRENT,TRUE
JZ DO_FILE2
JMP FILE_END

DO_FILE2: CALL SET_PGM
CALL MAKE_FILENAME

DO_FILE3: MOV DX,OFFSET ASCIIZ_NAME
XOR CX,CX
MOV AH,4EH
INT 21H
JNC READ_FILE

CALL NEW_PGM
JNC DO_FILE3
JMP FILE_END

READ_FILE: CALL READ_PGM
JNC CK_SIZE
JMP FILE_ERR

CK_SIZE: MOV AX,DTA.SIZE_LOW
MOV CX,DTA.SIZE_HIGH
MOV BX,FILE_SIZE[0]
CMP AX,BX
JNZ CK_MODEM
CMP CX,FILE_SIZE[2]
JZ DO_FILE

CK_MODEM: CMP XMODEM_FLAG,TRUE
JZ DO_FILE
ADD BX,7FH ;128-1
AND BX,NOT 7FH ;Round up.
CMP AX,BX
JNZ WRONG_SIZE

MOV SI,FILE_SIZE
PUSH DS
MOV DS,PGM_BUFF_SEG
CMP BYTE PTR [SI],0
POP DS
JNZ WRONG_SIZE

XMODEM: CALL DO_XMODEM
JC FILE_END

DO_FILE: MOV STATE,FILE_TYPE
CALL DISP_LISTING
NEXT_FILE: MOV SI,OFFSET CUSTOM_MENU
CALL DISPLAY_MENU
CALL DISP_FILE

NEXT_FILE2: CALL GETKEY
JC FILE_END
CMP AL,LEFT_SCAN
JZ FILE_END
MOV BX,CUSTOM_ADDR
CMP AH,CR
JZ DO_MENU2
CMP AL,RIGHT_SCAN
JZ DO_MENU2

CMP AL,UP_SCAN
JNZ CK_DOWN
CMP BX,OFFSET CATEGORY + SIZE CAT
JZ NEXT_FILE2
SUB CUSTOM_ADDR,SIZE CAT
JMP NEXT_FILE

CK_DOWN: CMP AL,DOWN_SCAN
JNZ NEXT_FILE2
ADD BX,SIZE CAT
CMP BX,OFFSET CAT_END
JAE NEXT_FILE2
MOV CUSTOM_ADDR,BX
JMP NEXT_FILE

DO_MENU2: CMP [BX.CAT_STATUS],INACTIVE
JZ NEXT_FILE2
MOV STATE,CUSTOM_TYPE
PUSH BX
CALL DISP_FILE
POP BX
CALL [BX.CAT_MENU]
JMP DO_FILE

WRONG_SIZE: CALL DO_WRONG
JMP FILE_END

FILE_ERR: CALL BEEP

FILE_END: CALL SET_ZIF
RET

;----------------------------------------------;
CANT_FIND LABEL BYTE
DB "ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»",0
PATH_EDIT_LEN EQU $ - CANT_FIND - 5
DB "º Can't find ",0
CANT_FIND2 LABEL BYTE
DB " Enter new path º",0
DB "º º",0
DB "º Esc to Cancel º",0
DB "ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ",0

NEW_PGM: CALL BEEP
CALL RESTORE_DIR
CALL CALC_WINDOW
ADD BP,SELECT_START
MOV DI,BP
MOV SI,OFFSET CANT_FIND
MOV BH,COLOR.W
CALL WRITE_STRING
ADD BP,CRT_WIDTH
MOV DI,BP
CALL WRITE_STRING
CALL WRITE_NAME
MOV SI,OFFSET CANT_FIND2
CALL WRITE_STRING

ADD BP,CRT_WIDTH
MOV DI,BP
MOV ADDR_SCREEN,DI
CALL WRITE_STRING
ADD BP,CRT_WIDTH
MOV DI,BP
CALL WRITE_STRING
ADD BP,CRT_WIDTH
MOV DI,BP
CALL WRITE_STRING

MOV CX,PATH_EDIT_LEN - 2
MOV SI,OFFSET PGM_DRIVE
MOV DI,OFFSET DTA
LODSB
ADD AL,"A"
STOSB
MOV AL,":"
STOSB
NEXT_PATH: LODSB
OR AL,AL
JZ NEW_PATH2
STOSB
LOOP NEXT_PATH
NEW_PATH2: JCXZ NEW_PATH3
MOV AL,SPACE
REP STOSB
NEW_PATH3: XOR AL,AL
STOSB

MOV LINE_START,OFFSET DTA
MOV LINE_END,OFFSET DTA + PATH_EDIT_LEN
MOV AX,ADDR_SCREEN
ADD AX,2 * 2
MOV SCREEN_START,AX
MOV LEGAL_CHARS,OFFSET ALL_CHARS
CALL EDITOR
JC NEW_PGM_END

MOV SI,OFFSET DTA
MOV DI,OFFSET PGM_DRIVE
CALL PARSE_IT
CALL DISP_DIR
CLC

NEW_PGM_END: RET

;----------------------------------------------;

SIZE_MSG LABEL BYTE
DB "ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»",0
DB "º ",0
SIZE_MSG2 LABEL BYTE
DB " size does not match the SIZE in the .ZIF file. º",0
XMODEM_MSG LABEL BYTE
DB "º º",0
DB "º However, ",0
XMODEM_MSG2 LABEL BYTE
DB " appears to have been downloaded via the º",0
DB "º XMODEM protocol which rounds a file up to a 128 byte multiple. º",0
DB "º º",0
DB "º If you have backed-up ",0
XMODEM_MSG3 LABEL BYTE
DB " it's Okay to continue. º",0
DB "º Continue Y/N? º",0
DB "ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ",0


WRONG_MSG LABEL BYTE
DB "º It's suggest that you download from ZiffNet the latest version º",0
DB "º of ",0
WRONG_MSG2 LABEL BYTE
DB " Press any key º",0
DB "ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ",0

DO_XMODEM: CALL BEEP
CALL DISP_SIZE
MOV SI,OFFSET XMODEM_MSG
CALL WRITE_STRING
ADD BP,CRT_WIDTH
MOV DI,BP
CALL WRITE_STRING
CALL WRITE_NAME
MOV SI,OFFSET XMODEM_MSG2
CALL WRITE_STRING

ADD BP,CRT_WIDTH
MOV DI,BP
CALL WRITE_STRING
ADD BP,CRT_WIDTH
MOV DI,BP
CALL WRITE_STRING
ADD BP,CRT_WIDTH
MOV DI,BP
CALL WRITE_STRING
CALL WRITE_NAME
MOV SI,OFFSET XMODEM_MSG3
CALL WRITE_STRING

ADD BP,CRT_WIDTH
MOV DI,BP
CALL WRITE_STRING
ADD BP,CRT_WIDTH
MOV DI,BP
CALL WRITE_STRING

CALL GETKEY
CMP AL,Y_SCAN
CLC
JZ XMODEM_END
STC
XMODEM_END: RET

;----------------------------------------------;
DO_WRONG: CALL BEEP
CALL DISP_SIZE
MOV SI,OFFSET WRONG_MSG
CALL WRITE_STRING
ADD BP,CRT_WIDTH
MOV DI,BP
CALL WRITE_STRING
CALL WRITE_NAME
MOV SI,OFFSET WRONG_MSG2
CALL WRITE_STRING
ADD BP,CRT_WIDTH
MOV DI,BP
CALL WRITE_STRING
CALL GETKEY
RET

;----------------------------------------------;

DISP_SIZE: CALL CALC_WINDOW
ADD BP,CUSTOM_START
MOV DI,BP
MOV SI,OFFSET SIZE_MSG
MOV BH,COLOR.W
CALL WRITE_STRING
ADD BP,CRT_WIDTH
MOV DI,BP
CALL WRITE_STRING
CALL WRITE_NAME
MOV SI,OFFSET SIZE_MSG2
CALL WRITE_STRING
ADD BP,CRT_WIDTH
MOV DI,BP
RET

;----------------------------------------------;
WRITE_NAME: MOV CX,12
MOV SI,OFFSET ASCIIZ_NAME
CALL WRITE_LINE
CALL PAD_LINE
RET

;----------------------------------------------;

CALC_WINDOW: MOV AX,WINDOW_TOP
CALL CALC_ADDR
MOV BP,DI
RET
;----------------------------------------------;

SEARCH: PUSH DS
CMP AH,"a"
JB DO_SEARCH
CMP AH,"z"
JA DO_SEARCH
AND AH,5FH

DO_SEARCH: MOV SI,BAR_ADDR
MOV DS,FILENAME_SEG
CMP [SI],AH
JZ NEXT_SEARCH
XOR SI,SI
JMP SHORT NEXT_SEARCH2

NEXT_SEARCH: ADD SI,SIZE FILE_RECORD
NEXT_SEARCH2: CMP BYTE PTR[SI],-1
JZ NO_MATCH
CMP [SI],AH
JNZ NEXT_SEARCH

POP DS
MOV BAR_ADDR,SI
MOV AX,LISTING_LEN
SHR AX,1
MOV BX,SIZE FILE_RECORD
MUL BX
SUB SI,AX
JNC STORE_MATCH
XOR SI,SI
STORE_MATCH: MOV LISTING_ADDR,SI
MOV FILE_CURRENT,FALSE
JMP SHORT SEARCH_END

NO_MATCH: POP DS
CALL BEEP

SEARCH_END: CALL DISP_LISTING
CALL DISP_FILE
RET

;----------------------------------------------;

L_DISPATCH DB UP_SCAN, DOWN_SCAN, PGUP_SCAN, PGDN_SCAN
DB HOME_SCAN, END_SCAN

L_LEN EQU $ - L_DISPATCH

DW LUP, LDOWN, LPGUP, LPGDN
DW LHOME, LEND


;----------------------------------------------;
; INPUT: AX=Listing page; BP=BAR_ADDR; DX=LISTING_ADDR; CX=LAST_ADDR.
; OUTPUT: LISTING_FLAG=TRUE if listing changed.

LUP: SUB BP,SIZE FILE_RECORD ;Move bar up a line.
JL LPAGE_END ;If < 0, ignore.
CMP BP,DX ;If bar below top, OK.
JAE LPAGE_UPDATE
SUB DX,SIZE FILE_RECORD ;Else, move listing up a line.
JMP SHORT LPAGE_UPDATE

LDOWN: ADD BP,SIZE FILE_RECORD ;Move bar down a line.
CMP BP,CX ;If > last line, then ignore.
JA LPAGE_END
ADD AX,DX ;Listing top + page length =
CMP BP,AX ; listing bottom; If bar below
JB LPAGE_UPDATE ; listing bottom, OK.
ADD DX,SIZE FILE_RECORD ;Else move listing down a line.
JMP SHORT LPAGE_UPDATE

LPGUP: SUB BP,AX ;Move bar up a page.
SUB DX,AX ;Move listing up a page.
JC LHOME ;If listing < top, then home.
JMP SHORT LPAGE_UPDATE ;Else, OK.

LPGDN: ADD DX,AX ;Move listing down a page.
CMP DX,CX ;If <= last line, do bar.
JBE DO_BAR
SUB DX,AX ;Else, back to where we were
MOV BP,CX ; and move bar to last line
JMP SHORT LPAGE_UPDATE ; and update.
DO_BAR: ADD BP,AX ;Move bar down a page.
CMP BP,CX ;If bar <= last line, OK.
JBE LPAGE_UPDATE
MOV BP,CX ;Else, move bar to last line.
JMP SHORT LPAGE_UPDATE

LHOME: XOR BP,BP ;Move bar to top.
XOR DX,DX ;Move listing to top.
JMP SHORT LPAGE_UPDATE

LEND: MOV BP,CX ;Move bar to last line.
ADD CX,SIZE FILE_RECORD ;Last line + 1 - page length =
SUB CX,AX ; top of last page.
CMP DX,CX ;If less than a full page,
JG LPAGE_UPDATE ; then already at last page.
MOV DX,CX ;Else, move listing to last page.

LPAGE_UPDATE: MOV BAR_ADDR,BP ;Store the new bar
MOV LISTING_ADDR,DX ; and listing line start.
MOV FILE_CURRENT,FALSE
MOV FAIL_FLAG,FALSE

LPAGE_END: RET

;----------------------------------------------;

FIND_FILES: CALL SET_ZIF
XOR BP,BP ;ES:BP -> filename storage.

MOV DX,OFFSET STAR_DOT_ZIF
XOR CX,CX
MOV AH,4EH ;Find first.
INT 21H
JC FILES_DONE ;If no, assumed right.

PUSH ES
MOV ES,FILENAME_SEG
XOR DI,DI
MOV AX,SPACE SHL 8 + SPACE
MOV CX,FILENAME_SIZE / 2 * KILOBYTES
REP STOSW ;Initialize with spaces.
POP ES

FIND_NEXT: MOV DX,OFFSET DTA.FILE_NAME
CALL READ_ZIF
JC FILES_DONE
CALL STORE_NAME ;Store a filename.
CMP BP,TEMP_RECORD - SIZE FILE_RECORD
JA FILES_DONE
MOV AH,4FH ;Find Next Matching.
INT 21H
JNC FIND_NEXT ;If successful store filename.

FILES_DONE: PUSH ES
MOV ES,FILENAME_SEG
MOV AX,-1 ;Else, mark the end with -1.
MOV DI,BP
STOSW
POP ES
SUB DI,SIZE FILE_RECORD + 2
JNC STORE_LAST
XOR DI,DI
STORE_LAST: MOV LAST_ADDR,DI
CALL SORT ;Sort 'em.

FILES_END: RET

;----------------------------------------------;
; INPUT: DX-> filename
; OUTPUT: CF = 1 if failed.

READ_ZIF: MOV AX,3D00H
INT 21H
JC OPEN_END
CMP FAIL_FLAG,TRUE
STC
JZ OPEN_FAIL

MOV BX,AX
CALL READZ
JNC OPEN_END

OPEN_FAIL: CALL BEEP
MOV FAIL_FLAG,FALSE
STC

OPEN_END: RET

;----------------------------------------------;
; INPUT: BX = filehandle.
; OUTPUT: CF=1 if read failed.

READZ: PUSH DS
MOV DS,ZIF_BUFF_SEG
XOR DX,DX
MOV CX,ZIF_BUFF_SIZE * KILOBYTES - 1
MOV AH,3FH
INT 21H

MOV CS:ZIF_BYTES,AX
MOV SI,AX
MOV BYTE PTR [SI],CTRL_Z
JC CLOSE
CMP CS:FAIL_FLAG,TRUE
STC
JZ CLOSE

CAPITALIZE: XOR SI,SI
MOV CX,AX
JCXZ CLOSE1
XOR AH,AH ;Quotes flag.
NEXT_CAP: LODSB
CMP AL,CTRL_Z
JZ CAP_END
CMP AL,AH
JZ CLOSE_QUOTES
CMP AH,DOUBLE_QUOTES
JZ NEXT_CAP

CMP AL,DOUBLE_QUOTES
JZ OPEN_QUOTES
CMP AL,"a"
JB NEXT_CAP
CMP AL,"z"
JA NEXT_CAP
AND BYTE PTR [SI - 1],5FH
JMP NEXT_CAP

OPEN_QUOTES: MOV AH,AL
JMP NEXT_CAP

CLOSE_QUOTES: XOR AH,AH
JMP NEXT_CAP
CAP_END:

CLOSE1: CLC

CLOSE: PUSHF
MOV AH,3EH
INT 21H
POPF

READ_END: POP DS
MOV FAIL_FLAG,FALSE
RET

;----------------------------------------------;
; INPUT: DX-> filename
; OUTPUT: CF = 1 if failed.

READ_PGM: MOV AX,3D00H
INT 21H
JC OPEN_END2
CMP FAIL_FLAG,TRUE
STC
JZ OPEN_FAIL2

MOV BX,AX
CALL READP
JNC OPEN_END

OPEN_FAIL2: CALL BEEP
MOV FAIL_FLAG,FALSE
STC

OPEN_END2: RET

;----------------------------------------------;
; INPUT: BX = filehandle.
; OUTPUT: CF=1 if read failed.

READP: PUSH DS
MOV DS,PGM_BUFF_SEG
XOR DX,DX
MOV CX,PGM_BUFF_SIZE * KILOBYTES - 1
MOV AH,3FH
INT 21H
JC CLOSE2
CMP CS:FAIL_FLAG,TRUE
STC
JZ CLOSE2
MOV CS:PGM_BYTES,AX

CLC

CLOSE2: PUSHF
MOV AH,3EH
INT 21H
POPF

READP_END: POP DS
MOV FAIL_FLAG,FALSE
RET

;----------------------------------------------;
;INPUT: BP->filename storage.
;OUTPUT: BP->next storage.

FILE_STR DB "[FILE]"
FILE_STR_LEN EQU $ - FILE_STR
FILENAME_STR DB "FILENAME"
FILENAME_STR_LEN EQU $ - FILENAME_STR

STORE_NAME: PUSH DS
MOV DS,ZIF_BUFF_SEG
XOR BX,BX

NEXT_FILE_STR: MOV DI,OFFSET FILE_STR
MOV SI,BX
CMP BYTE PTR [SI],CTRL_Z
JZ NAME_END1
INC BX
MOV CX,FILE_STR_LEN
REPZ CMPSB
JNZ NEXT_FILE_STR
ADD BX,FILE_STR_LEN - 1
MOV SI,BX
CALL PARSE_LINES
MOV BX,SI

NEXT_FILE_STR2:MOV DI,OFFSET FILENAME_STR
MOV SI,BX
CMP BYTE PTR [SI],CTRL_Z
JZ NAME_END1
CMP BYTE PTR [SI],"["
JZ NAME_END1
INC BX
MOV CX,FILENAME_STR_LEN
REPZ CMPSB
JNZ NEXT_FILE_STR2


CALL PARSE_LINE ;SI-> name start.
JC NAME_END1
PUSH ES ;Preserve extra segment.
MOV ES,CS:FILENAME_SEG
MOV DI,BP
MOV CX,SIZE LIST_NAME ;Store 12 bytes of filename.
JMP SHORT NEXT_STORE

EXTENSION: ADD DI,CX
MOV CX,3
SUB DI,CX

NEXT_STORE: LODSB ;Get a byte.
CMP AL,SPACE
JBE END_STORE ;If yes, finish with blanks.
CMP AL,"." ;Is it the period?
JZ EXTENSION
STOSB ;Store byte.
LOOP NEXT_STORE ;Get next byte.
END_STORE: ADD BP,SIZE LIST_NAME
INC CS:FILE_CNT
POP ES

NAME_END1: POP DS

NAME_END: RET ;Done here.

;----------------------------------------------;

DISP_FILE: CMP FILE_CURRENT,TRUE
JZ DISP_FILE2
CALL MAKE_ZIF_NAME
MOV DX,OFFSET ASCIIZ_NAME
CALL READ_ZIF
JC NO_FILE
MOV FILE_CURRENT,TRUE
CALL PARSE_FILE

DISP_FILE2: CALL DISP_CUSTOM
JMP SHORT DISP_FILE_END

NO_FILE: CALL CLEAR_FILE
CALL BEEP

DISP_FILE_END: RET

;----------------------------------------------;
ACTIVE EQU 0
INACTIVE EQU -1

CAT STRUC
CAT_STATUS DB INACTIVE
CAT_LEN DB ?
CAT_TEXT DB " "
CAT_NUL DB 0
CAT_MENU DW ?
CAT_PARSE DW ?
CAT ENDS

CATEGORY CAT <,5, "FILE]",, DUMMY_RET, FILE_PARSE>
CAT <,6, "COLOR]",, COLOR_MENU, COLOR_PARSE>
CAT <,7, "HOTKEY]",, HOTKEY_MENU, HOTKEY_PARSE>
CAT <,6, "MODEM]",, MODEM_MENU, MODEM_PARSE>
CAT <,8, "PRINTER]",, PRINTER_MENU, PRINTER_PARSE>
CAT <,5, "TEXT]",, TEXT_MENU, TEXT_PARSE>
CAT <,7, "NUMBER]",, NUMBER_MENU, NUMBER_PARSE>
CAT <,10,"PICK LIST]",, PICK_MENU, PICK_PARSE>

CAT_END EQU $
CATEGORY_LEN EQU (CAT_END - CATEGORY ) / SIZE CAT

CUSTOM_ADDR DW ? ;Index into custom menu.

VARS LABEL BYTE

FILE_SIZE DW ?,?
COLOR_MAX EQU 7
COLOR_MENUS DW COLOR_MAX DUP (?)

TEXT_MAX EQU 7
TEXT_MENUS DW TEXT_MAX DUP (?)

NUM_MAX EQU 7
NUMBER_MENUS DW NUM_MAX DUP (?)

PICK_MAX EQU 6
PICK_MENUS DW PICK_MAX DUP (?)
ENTRY_MENUS DW PICK_MAX DUP (PICK_MAX DUP (?))

SHIFT_ADDR DW ?
SHIFT_TEXT_ADDR DW ?
SCAN_ADDR DW ?
SCAN_TEXT_ADDR DW ?

VARS_LEN EQU $ - VARS

COLOR_ADDRS DW COLOR_MAX DUP (?)
COLOR_VALUES DW COLOR_MAX DUP (?)

TEXT_ADDRS DW TEXT_MAX DUP (?)
TEXT_LENGTH DW TEXT_MAX DUP (?)
STRING_MAX EQU 27
TEXT_EDIT DB TEXT_MAX DUP (STRING_MAX DUP (?))

NUMBER_ADDRS DW NUM_MAX DUP (?)
NUMBER_TYPE DW NUM_MAX DUP (?)
RANGE_LOW DW NUM_MAX DUP (?)
RANGE_HIGH DW NUM_MAX DUP (?)
INTEGER_MAX EQU 7
NUMBER_EDIT DB NUM_MAX DUP (INTEGER_MAX DUP (?))
NUMBER_RANGE DB INTEGER_MAX + 1 DUP (?)

PICK_ADDRS DW PICK_MAX DUP (?)
PICK_TYPES DW PICK_MAX DUP (?)
ENTRY_VALUES DW PICK_MAX DUP (PICK_MAX DUP (?))

;--------------;

INIT_VARS: MOV CUSTOM_ADDR,OFFSET CATEGORY + SIZE CAT

MOV BP,CATEGORY_LEN
MOV SI,OFFSET CATEGORY
MOV AX,INACTIVE
NEXT_INACTIVE: MOV [SI],AL
ADD SI,SIZE CAT
DEC BP
JNZ NEXT_INACTIVE

MOV CX,VARS_LEN
MOV DI,OFFSET VARS
REP STOSB
RET

;----------------------------------------------;

PARSE_FILE: PUSH DS
CALL INIT_VARS
MOV DS,ZIF_BUFF_SEG
XOR SI,SI
NEXT_BRACKET: LODSB
CMP AL,CTRL_Z
JZ PARSE_FILE_END
CMP AL,"["
JNZ NEXT_BRACKET

CMP BYTE PTR [SI],CTRL_Z
JZ PARSE_FILE_END
MOV BP,CATEGORY_LEN
MOV DX,SI
MOV BX,OFFSET CATEGORY
NEXT_CAT: MOV SI,DX
MOV DI,BX
ADD DI,CAT_TEXT
MOV CL,CS:[BX.CAT_LEN]
XOR CH,CH
REP CMPSB
JZ FOUND_CAT
ADD BX,SIZE CAT
DEC BP
JNZ NEXT_CAT
INC DX
MOV SI,DX
JMP NEXT_BRACKET

FOUND_CAT: CALL PARSE_LINES
JC NEXT_BRACKET
PUSH BX
CALL CS:[BX.CAT_PARSE]
POP BX
JC NEXT_BRACKET
MOV CS:[BX.CAT_STATUS],ACTIVE
JMP NEXT_BRACKET

PARSE_FILE_END:POP DS
RET

;----------------------------------------------;
;INPUT: DS:SI->ZIF_BUFF_SEG:OFFSET; BX->CATEGORY:OFFSET
;OUTPUT: DS:SI->end of category; CF=1 if no valid parameters found.

SIZE_STR DB "SIZE"
SIZE_STR_LEN EQU $ - SIZE_STR

;--------------;
FILE_PARSE: MOV BP,SIZE_STR_LEN
MOV BX,OFFSET SIZE_STR
CALL PARSE_KEY
JC FILE_PARSE_END
CALL GET_NUMBER
JC FILE_PARSE_END
MOV CS:FILE_SIZE[0],AX
MOV CS:FILE_SIZE[2],CX
FILE_PARSE_END:RET

;--------------;
ENTRY_INDEX DW ?
PICK_ADDR DW ?

PICK_PARSE: XOR BX,BX
MOV CS:ENTRY_INDEX,0
NEXT_PICK: MOV BP,SI
INC BP
CALL PARSE_STRING
JC PICK_END
CALL PARSE_LINE
JC PICK_END
CALL GET_NUMBER
JC PICK_END
SUB AX,100H
JC PICK_END
MOV CS:PICK_ADDR,AX
CALL PARSE_LINE
JC PICK_END

PUSH BX
PUSH BP
MOV BP,SIZE_STR_LEN
MOV BX,OFFSET SIZE_STR
CALL PARSE_KEY
POP BP
POP BX
JC PICK_END
CALL PARSE_LINE
JC PICK_END

LODSB
CMP AL,"B"
JZ STORE_PICK
CMP AL,"W"
STC
JNZ PICK_END

STORE_PICK: MOV CS:PICK_TYPES[BX],AX
CALL PARSE_END2
CALL PARSE_LINES
JC PICK_END
CALL PARSE_ENTRY
JC PICK_END
MOV AX,CS:PICK_ADDR
MOV CS:PICK_ADDRS[BX],AX
MOV CS:PICK_MENUS[BX],BP
ADD CS:ENTRY_INDEX,PICK_MAX * 2
INC BX
INC BX
CMP BX,PICK_MAX * 2
JAE PICK_END
CALL PARSE_LINES
JNC NEXT_PICK

CLC

PICK_END: RET

;--------------;
ENTRY_STR DB "ENTRY"
ENTRY_STR_LEN EQU $ - ENTRY_STR

VALUE_STR DB "VALUE"
VALUE_STR_LEN EQU $ - VALUE_STR

ENTRY_ADDR DW ?
ENTRY_MAX DW ?

PARSE_ENTRY: PUSH BX
PUSH BP
MOV BX,CS:ENTRY_INDEX
MOV AX,BX
ADD AX,PICK_MAX * 2
MOV CS:ENTRY_MAX,AX

NEXT_ENTRY: PUSH BX
MOV BP,ENTRY_STR_LEN
MOV BX,OFFSET ENTRY_STR
CALL PARSE_KEY
POP BX
JC PARSE_E_END
CALL PARSE_LINE
JC PARSE_E_END
MOV BP,SI
INC BP
CALL PARSE_STRING
JC PARSE_E_END
CALL PARSE_LINE
JC PARSE_E_END

PUSH BX
PUSH BP
MOV BP,VALUE_STR_LEN
MOV BX,OFFSET VALUE_STR
CALL PARSE_KEY
POP BP
POP BX
JC PARSE_E_END
CALL PARSE_LINE
JC PARSE_E_END
CALL GET_NUMBER
JC PARSE_E_END
MOV CS:ENTRY_VALUES[BX],AX
MOV CS:ENTRY_MENUS[BX],BP
INC BX
INC BX
CMP BX,CS:ENTRY_MAX
JAE PARSE_E_END
CALL PARSE_LINES
JC PARSE_E_DONE
CMP AL,DOUBLE_QUOTES
JNZ NEXT_ENTRY
PARSE_E_DONE: CLC

PARSE_E_END: POP BP
POP BX
RET

;--------------;
COLOR_PARSE: XOR BX,BX ;Index.
NEXT_COLOR: MOV BP,SI ;Save string start.
INC BP ;Bump past quotes.
CALL PARSE_STRING
JC COLOR_END
CALL PARSE_LINE
JC COLOR_END
CALL GET_NUMBER
JC COLOR_END
SUB AX,100H
JC COLOR_END
MOV CS:COLOR_MENUS[BX],BP ;String start.
MOV CS:COLOR_ADDRS[BX],AX ;Prog address.
INC BX
INC BX
CMP BX,COLOR_MAX * 2
JAE COLOR_END
CALL PARSE_LINES
JNC NEXT_COLOR

CLC

COLOR_END: RET

;--------------;
SHIFT_STR DB "SHIFT"
SHIFT_LEN EQU $ - SHIFT_STR
TEXT_STR DB "TEXT"
TEXT_LEN EQU $ - TEXT_STR
SCAN_STR DB "SCAN"
SCAN_LEN EQU $ - SCAN_STR

GOOD_PARA DB FALSE ;TRUE if valid parameters

HOTKEY_PARSE: MOV CS:GOOD_PARA,FALSE
PUSH SI
NEXT_SHIFT: MOV BP,SHIFT_LEN
MOV BX,OFFSET SHIFT_STR
CALL PARSE_KEY
JNC GOT_SHIFT
CALL PARSE_LINES
JNC NEXT_SHIFT
JMP SHORT CK_SCAN

GOT_SHIFT: CALL PARSE_LINE
JC CK_SCAN
CALL GET_NUMBER
JC CK_SCAN
SUB AX,100H
JC CK_SCAN
MOV CS:SHIFT_ADDR,AX
MOV CS:GOOD_PARA,TRUE
CALL PARSE_LINE
JC CK_SCAN

MOV BP,TEXT_LEN
MOV BX,OFFSET TEXT_STR
CALL PARSE_KEY
JC CK_SCAN
CALL PARSE_LINE
JC CK_SCAN
CALL GET_NUMBER
JC CK_SCAN
SUB AX,100H
JC CK_SCAN
MOV CS:SHIFT_TEXT_ADDR,AX

CK_SCAN: POP SI
NEXT_SCAN: MOV BP,SCAN_LEN
MOV BX,OFFSET SCAN_STR
CALL PARSE_KEY
JNC GOT_SCAN
CALL PARSE_LINES
JNC NEXT_SCAN
JMP SHORT HOT_END1

GOT_SCAN: CALL PARSE_LINE
JC HOT_END1
CALL GET_NUMBER
JC HOT_END1
SUB AX,100H
JC HOT_END1
MOV CS:SCAN_ADDR,AX
CALL PARSE_LINE
JC HOT_END1

MOV BP,TEXT_LEN
MOV BX,OFFSET TEXT_STR
CALL PARSE_KEY
JC HOT_END1
CALL PARSE_LINE
JC HOT_END1
CALL GET_NUMBER
JC HOT_END1
SUB AX,100H
JC HOT_END1
MOV CS:SCAN_TEXT_ADDR,AX

HOT_END1: CMP CS:GOOD_PARA,TRUE
CLC
JZ HOT_END
STC

HOT_END: RET

;--------------;
COM_STR DB "COM"
COM_LEN EQU $ - COM_STR
COM_ADDR DW ?

MODEM_PARSE: MOV BP,COM_LEN
MOV BX,OFFSET COM_STR
CALL PARSE_KEY
JC MODEM_END
CALL PARSE_LINE
JC MODEM_END
CALL GET_NUMBER
JC MODEM_END
SUB AX,100H
JC MODEM_END
MOV CS:COM_ADDR,AX
MODEM_END: RET

;--------------;
LPT_STR DB "LPT"
LPT_LEN EQU $ - LPT_STR
LPT_ADDR DW ?

PRINTER_PARSE: MOV BP,LPT_LEN
MOV BX,OFFSET LPT_STR
CALL PARSE_KEY
JC LPT_END
CALL PARSE_LINE
JC LPT_END
CALL GET_NUMBER
JC LPT_END
SUB AX,100H
JC LPT_END
MOV CS:LPT_ADDR,AX
LPT_END: RET

RET

;--------------;
LENGTH_STR DB "LENGTH"
LENGTH_STR_LEN EQU $ - LENGTH_STR


TEXT_PARSE: XOR BX,BX ;Index.
NEXT_TEXT: MOV BP,SI ;Save string start.
INC BP ;Bump past quotes.
CALL PARSE_STRING
JC TEXT_END
CALL PARSE_LINE
JC TEXT_END
CALL GET_NUMBER
JC TEXT_END
SUB AX,100H
JC TEXT_END
MOV CS:TEXT_ADDRS[BX],AX ;Prog address.
CALL PARSE_LINE
JC TEXT_END

PUSH BX
PUSH BP
MOV BP,LENGTH_STR_LEN
MOV BX,OFFSET LENGTH_STR
CALL PARSE_KEY
POP BP
POP BX
JC TEXT_END
CALL PARSE_LINE
JC TEXT_END
CALL GET_NUMBER
JC TEXT_END
MOV CS:TEXT_LENGTH[BX],AX
MOV CS:TEXT_MENUS[BX],BP ;String start.
INC BX
INC BX
CMP BX,TEXT_MAX * 2
JAE TEXT_END
CALL PARSE_LINES
JNC NEXT_TEXT

CLC

TEXT_END: RET

;--------------;
RANGE_STR DB "RANGE"
RANGE_STR_LEN EQU $ - RANGE_STR

NUMBER_PARSE: XOR BX,BX ;Index.
NEXT_NUM: MOV BP,SI ;Save string start.
INC BP ;Bump past quotes.
CALL PARSE_STRING
JC LILLY_N_END
CALL PARSE_LINE
JC LILLY_N_END
CALL GET_NUMBER
JC LILLY_N_END
SUB AX,100H
JC LILLY_N_END
MOV CS:NUMBER_ADDRS[BX],AX
CALL PARSE_LINE
JC LILLY_N_END

PUSH BX
PUSH BP
MOV BP,SIZE_STR_LEN
MOV BX,OFFSET SIZE_STR
CALL PARSE_KEY
POP BP
POP BX
JC LILLY_N_END
CALL PARSE_LINE
JC LILLY_N_END
LODSB
CMP AL,"B"
JZ STORE_NUMBER
CMP AL,"W"
JZ STORE_NUMBER
LILLY_N_END: STC
JMP SHORT N_END

STORE_NUMBER: MOV CS:NUMBER_TYPE[BX],AX
MOV CS:NUMBER_MENUS[BX],BP ;String start.
MOV CS:RANGE_LOW[BX],0
MOV CX,-1
CMP AL,"W"
JZ STORE_RANGE
MOV CX,255
STORE_RANGE: MOV CS:RANGE_HIGH[BX],CX

MOV CX,4
NEXT_SIZE2: LODSB
CMP AL,SPACE
JBE DO_RANGE
LOOP NEXT_SIZE2
DO_RANGE: DEC SI
CALL PARSE_LINE
JC NEXT_NUM2
PUSH BX
PUSH BP
MOV BP,RANGE_STR_LEN
MOV BX,OFFSET RANGE_STR
CALL PARSE_KEY
POP BP
POP BX
JC NEXT_NUM2

CALL PARSE_LINE
JC NEXT_NUM2
CMP BYTE PTR [SI],"-"
JNZ GET_LOW
INC SI
JMP SHORT GET_HIGH
GET_LOW: CALL PARSE_LINE
JC NEXT_NUM2
CALL GET_NUMBER
JC NEXT_NUM2
MOV CS:RANGE_LOW[BX],AX

GET_HIGH: CALL PARSE_LINE
JC NEXT_NUM2
CALL GET_NUMBER
JC NEXT_NUM2
MOV CS:RANGE_HIGH[BX],AX

NEXT_NUM2: INC BX
INC BX
CMP BX,NUM_MAX * 2
JAE N_END
CALL PARSE_END2
CALL PARSE_LINES
JC N_END2
JMP NEXT_NUM

N_END2: CLC

N_END: RET

;----------------------------------------------;
; INPUT: DS:SI-> String OUTPUT: CF=1 if no string.

PARSE_STRING: MOV AH,DOUBLE_QUOTES
CMP BYTE PTR [SI],AH
JNZ NO_STRING
INC SI
NEXT_STRING: LODSB
CMP AL,CTRL_Z
JZ NO_STRING1
CMP AL,CR
JZ NO_STRING
CMP AL,AH
JNZ NEXT_STRING
MOV BYTE PTR [SI-1],0 ;ASCIIZ string
CLC
JMP SHORT STRING_END

NO_STRING1: DEC SI
NO_STRING: STC
STRING_END: RET

;----------------------------------------------;
; INPUT: ES:BX->string to match; BP=string len; DS:SI-> FILEBUFF.
; OUTPUT: CF=1 if no match; CF=0 if match; SI-> next string.

PARSE_KEY: MOV AL,[SI]
CMP AL,CTRL_Z
JZ NO_KEY
CMP AL,"["
JZ NO_KEY

MOV CX,BP
MOV DI,BX
MOV AX,SI
REP CMPSB
JZ FOUND_KEY
MOV SI,AX
PUSH BX
CALL PARSE_END2
CALL PARSE_LINES
POP BX
JMP PARSE_KEY

FOUND_KEY: CALL PARSE_LINE
RET

NO_KEY: STC
RET

;----------------------------------------------;
PARSE_END2: LODSB
CMP AL,LF
JZ PARSE_END_END
CMP AL,CTRL_Z
JNZ PARSE_END2
DEC SI
PARSE_END_END: RET

;--------------;
PICK_M1 LABEL BYTE
DB "Select pick description then press Enter to pick. Esc to Exit",0

PICK_M DB "Pick descriptions",0
DB "~~~~~~~~~~~~~~~~~",0

PICK_INDEX DW ?


PICK_MENU: CMP [PICK_MENUS],INACTIVE
JZ P_MENU_END1
MOV PICK_INDEX,0

NEXT_P_MENU: MOV STATE,CUSTOM_TYPE
MOV SI,OFFSET PICK_M1
CALL DISPLAY_MENU

NEXT_P_MENU2: CALL DISP_PICK
CALL GETKEY
JC P_MENU_END
CMP AL,LEFT_SCAN
JZ P_MENU_END

CMP AH,CR
JZ DO_PICK
CMP AL,RIGHT_SCAN
JZ DO_PICK

CK_P_UP: MOV BX,PICK_INDEX
CMP AL,UP_SCAN
JNZ CK_P_DN
SUB BX,2
JC NEXT_P_MENU2
JMP SHORT PICK_UPDATE

CK_P_DN: CMP AL,DOWN_SCAN
JNZ NEXT_P_MENU2
INC BX
INC BX
CMP BX,PICK_MAX * 2
JAE NEXT_P_MENU2
CMP PICK_MENUS[BX],INACTIVE
JZ NEXT_P_MENU2
PICK_UPDATE: MOV PICK_INDEX,BX
JMP NEXT_P_MENU2

DO_PICK: CALL PICK_SELECT
JMP NEXT_P_MENU

P_MENU_END1: CALL BEEP
P_MENU_END: RET

;----------------------------------------------;
DISP_PICK: MOV AX,PICK_TOP
CALL CALC_ADDR
ADD DI,SELECT_START
MOV BH,COLOR.W

PUSH DI
MOV SI,OFFSET PICK_M
CALL WRITE_STRING
POP DI
ADD DI,CRT_WIDTH

PUSH DI
CALL WRITE_STRING
POP DI

;--------------;
XOR BP,BP
NEXT_PICK_DES: MOV SI,PICK_MENUS[BP]
CMP SI,INACTIVE
JZ PICK_DES_END
ADD DI,CRT_WIDTH
PUSH DI

MOV CX,STRING_MAX
MOV BH,COLOR.W
CMP PICK_INDEX,BP
JNZ WRITE_DESC4
MOV BH,COLOR.C
CMP STATE,CUSTOM_TYPE
JZ WRITE_DESC4
MOV BH,COLOR.A

WRITE_DESC4: PUSH DS
MOV DS,ZIF_BUFF_SEG
CALL WRITE_LINE
CALL PAD_LINE
POP DS
POP DI
INC BP
INC BP
CMP BP,PICK_MAX * 2
JB NEXT_PICK_DES

PICK_DES_END: RET

;----------------------------------------------;
PICK_M2 LABEL BYTE
DB "Pick entry then press Enter to select. Esc to Cancel",0

ENTRY_SELECT_HOME DW ?
ENTRY_SELECT_INDEX DW ?

PICK_SELECT: MOV STATE,SELECT_TYPE
MOV SI,OFFSET PICK_M2
CALL DISPLAY_MENU
CALL DISP_PICK

MOV AX,PICK_MAX
MOV BX,PICK_INDEX ;BX=Pick index.
MUL BX
MOV BP,AX ;BP=Entry index.
MOV ENTRY_SELECT_HOME,BP

MOV SI,PICK_ADDRS[BX]
PUSH DS
MOV DS,PGM_BUFF_SEG
MOV AX,[SI] ;Current value.
POP DS

NEXT_P_DEF: CMP CS:ENTRY_MENUS[BP],INACTIVE
JZ FOUND_DEF1
CMP BYTE PTR PICK_TYPES[BX],"W"
JZ CK_WORD
CMP BYTE PTR CS:ENTRY_VALUES[BP],AL
JZ FOUND_DEF
JMP SHORT LOOP_DEF
CK_WORD: CMP CS:ENTRY_VALUES[BP],AX
JZ FOUND_DEF
LOOP_DEF: INC SI
INC SI
INC BP
INC BP
JMP NEXT_P_DEF

FOUND_DEF1: DEC BP
DEC BP

FOUND_DEF: MOV ENTRY_SELECT_INDEX,BP

NEXT_P_SEL: CALL DISP_SELECT
CALL GETKEY
JC PICK_S_END
CMP AL,LEFT_SCAN
JZ PICK_S_END

CMP AH,CR
JZ DO_S_PICK

CK_S_UP: MOV BX,ENTRY_SELECT_INDEX
CMP AL,UP_SCAN
JNZ CK_S_DN
SUB BX,2
JC NEXT_P_SEL
CMP BX,ENTRY_SELECT_HOME
JB NEXT_P_SEL
JMP SHORT ENTRY_UPDATE

CK_S_DN: CMP AL,DOWN_SCAN
JNZ NEXT_P_SEL
INC BX
INC BX
CMP ENTRY_MENUS[BX],INACTIVE
JZ NEXT_P_SEL
ENTRY_UPDATE: MOV ENTRY_SELECT_INDEX,BX
JMP NEXT_P_SEL

DO_S_PICK: MOV MODIFY_FLAG,TRUE

PICK_S_END: CALL CK_MODIFIED
JC P_S_END
CALL COPY_PICK
CALL SAVE
P_S_END: CALL CLEAR_PICK
RET

;----------------------;
DISP_SELECT: MOV AX,PICK_INDEX
SHR AX,1
ADD AX,PICK_TOP + 2
CALL CALC_ADDR
ADD DI,SELECT_START + (STRING_MAX * 2)

MOV BP,ENTRY_SELECT_HOME
NEXT_SEL_DES: MOV SI,ENTRY_MENUS[BP]
CMP SI,INACTIVE
JZ SELECT_DES_END
PUSH DI

MOV CX,STRING_MAX
MOV BH,COLOR.W
CMP ENTRY_SELECT_INDEX,BP
JNZ WRITE_DESC6
MOV BH,COLOR.C

WRITE_DESC6: PUSH DS
MOV DS,ZIF_BUFF_SEG
CALL WRITE_LINE
CALL PAD_LINE
POP DS
POP DI
ADD DI,CRT_WIDTH
INC BP
INC BP
JMP NEXT_SEL_DES

SELECT_DES_END:RET

;----------------------------------------------;
COPY_PICK: PUSH ES
MOV ES,PGM_BUFF_SEG
MOV SI,PICK_INDEX
MOV DI,PICK_ADDRS[SI]
CMP BYTE PTR PICK_TYPES[SI],"W"
MOV SI,ENTRY_SELECT_INDEX
MOV AX,ENTRY_VALUES[SI]
JZ COPY_WORD2
STOSB
JMP SHORT COPY_P_END
COPY_WORD2: STOSW
COPY_P_END: POP ES
RET

;----------------------------------------------;
CLEAR_PICK: MOV AX,PICK_INDEX
SHR AX,1
ADD AX,PICK_TOP + 2
CALL CALC_ADDR
ADD DI,SELECT_START + (STRING_MAX * 2)

PUSH ES
MOV DX,STATUS_REG ;Retrieve status register.
MOV ES,VIDEO_SEG ;Point to screen segment.
MOV SI,COLOR_INDEX
MOV BH,COLOR.W
MOV AL,SPACE

MOV BP,PICK_MAX
NEXT_CLEAR5: PUSH DI
MOV CX,STRING_MAX
NEXT_CLEAR6: CALL WRITE_CHAR
LOOP NEXT_CLEAR6
POP DI
ADD DI,CRT_WIDTH
DEC BP
JNZ NEXT_CLEAR5
POP ES
RET

;----------------------------------------------;
COLOR_M1 LABEL BYTE
DB "Select color description then press Enter to change color. Esc to Exit",0

COLOR_M DB "Color Descriptions",0
DB "~~~~~ ~~~~~~~~~~~~",0

COLOR_INDEX DW 0


COLOR_MENU: CMP [COLOR_MENUS],INACTIVE
JZ C_MENU_END1
CALL COLOR_DEFAULT
MOV COLOR_INDEX,0

NEXT_C_MENU: MOV STATE,CUSTOM_TYPE
MOV SI,OFFSET COLOR_M1
CALL DISPLAY_MENU

NEXT_C_MENU2: CALL DISP_COLOR
CALL GETKEY
JC C_MENU_END
CMP AL,LEFT_SCAN
JZ C_MENU_END

CMP AH,CR
JZ DO_COLOR
CMP AL,RIGHT_SCAN
JZ DO_COLOR

CK_C_UP: MOV BX,COLOR_INDEX
CMP AL,UP_SCAN
JNZ CK_C_DN
SUB BX,2
JC NEXT_C_MENU2
JMP SHORT COLOR_UPDATE

CK_C_DN: CMP AL,DOWN_SCAN
JNZ NEXT_C_MENU2
INC BX
INC BX
CMP BX,COLOR_MAX * 2
JAE NEXT_C_MENU2
CMP COLOR_MENUS[BX],INACTIVE
JZ NEXT_C_MENU2
COLOR_UPDATE: MOV COLOR_INDEX,BX
JMP NEXT_C_MENU2

DO_COLOR: CALL COLOR_SELECT
JMP NEXT_C_MENU

C_MENU_END1: CALL BEEP
C_MENU_END: CALL CK_MODIFIED
JC C_M_END
CALL COPY_COLORS
CALL SAVE
C_M_END: RET

;--------------;

COLOR_DEFAULT: PUSH ES
MOV ES,PGM_BUFF_SEG
XOR BX,BX
NEXT_C_DEFAULT:CMP [COLOR_MENUS.BX],INACTIVE
JZ C_DEFAULT_END
MOV SI,[COLOR_ADDRS.BX]
MOV AX,ES:[SI]
MOV [COLOR_VALUES.BX],AX
INC BX
INC BX
CMP BX,COLOR_MAX * 2
JB NEXT_C_DEFAULT
C_DEFAULT_END: POP ES
RET

;--------------;

DISP_COLOR: PUSH DS

MOV AX,COLOR_TOP
CALL CALC_ADDR
ADD DI,SELECT_START
MOV BH,COLOR.W

PUSH DI
MOV SI,OFFSET COLOR_M
CALL WRITE_STRING
POP DI
ADD DI,CRT_WIDTH

PUSH DI
CALL WRITE_STRING
POP DI

;--------------;
XOR BP,BP
MOV DS,ZIF_BUFF_SEG

NEXT_COLOR_DES:MOV SI,CS:COLOR_MENUS[BP]
CMP SI,INACTIVE
JZ COLOR_DES_END
ADD DI,CS:CRT_WIDTH
PUSH DI

MOV BX,CS:COLOR_VALUES[BP]
XCHG BH,BL
MOV AL,SPACE
CALL WRITE_SCREEN
MOV AL,"["
CALL WRITE_SCREEN
MOV AX,BP
SHR AX,1
ADD AL,"A"
CALL WRITE_SCREEN
MOV AL,"]"
CALL WRITE_SCREEN
MOV AL,SPACE
CALL WRITE_SCREEN
MOV BH,CS:COLOR.W
CALL WRITE_SCREEN

CMP CS:COLOR_INDEX,BP
JNZ WRITE_DESC
MOV BH,CS:COLOR.C
CMP CS:STATE,CUSTOM_TYPE
JZ WRITE_DESC
MOV BH,CS:COLOR.A
WRITE_DESC: CALL WRITE_STRING

POP DI
INC BP
INC BP
CMP BP,COLOR_MAX * 2
JB NEXT_COLOR_DES

COLOR_DES_END: POP DS
RET

;----------------------------------------------;
COLOR_M2 LABEL BYTE
DB "Select color then press Enter. Esc to Cancel",0

COLOR_SELECT: MOV SI,COLOR_INDEX
PUSH COLOR_VALUES[SI]

MOV STATE,SELECT_TYPE
MOV SI,OFFSET COLOR_M2
CALL DISPLAY_MENU
NEXT_C_BARS: CALL DISP_COLOR

MOV AX,COLOR_TOP + COLOR_MAX + 2
CALL CALC_ADDR
ADD DI,SELECT_START

PUSH ES
MOV DX,STATUS_REG ;Retrieve status register.
MOV ES,VIDEO_SEG ;Point to screen segment.
MOV SI,COLOR_INDEX
XOR BH,BH

N_BACKGROUND: PUSH DI
N_FORGROUND: MOV AL,SPACE
CALL WRITE_CHAR
MOV AL,"*"
CMP BH,BYTE PTR COLOR_VALUES[SI]
JNZ DO_A_COLOR
MOV AX,COLOR_INDEX
SHR AL,1
ADD AL,"A"
DO_A_COLOR: CALL WRITE_CHAR
MOV AL,SPACE
CALL WRITE_CHAR
INC BH
TEST BH,1111B
JNZ N_FORGROUND
POP DI
ADD DI,CRT_WIDTH
TEST BH,10000000B
JZ N_BACKGROUND
POP ES

NEXT_C_KEY: CALL GETKEY
JC COLOR_S_END1
CMP AH,CR
JZ SELECT_ENTER

MOV CX,COLOR_VALUES[SI]
MOV BH,CL
MOV BL,CL
AND BH,11110000B ;Background
AND BL,00001111B ;Foreground
CMP AL,UP_SCAN
JNZ CK_COLOR_DN
SUB BH,00010000B
JC NEXT_C_KEY
JMP SHORT UPDATE_C

CK_COLOR_DN: CMP AL,DOWN_SCAN
JNZ CK_COLOR_RT
ADD BH,00010000B
TEST BH,10000000B
JNZ NEXT_C_KEY
JMP SHORT UPDATE_C

CK_COLOR_RT: CMP AL,RIGHT_SCAN
JNZ CK_COLOR_LT
ADD BL,1
TEST BL,00010000B
JNZ NEXT_C_KEY
JMP SHORT UPDATE_C

CK_COLOR_LT: CMP AL,LEFT_SCAN
JNZ NEXT_C_KEY
SUB BL,1
JC NEXT_C_KEY

UPDATE_C: OR BL,BH
MOV CL,BL
MOV COLOR_VALUES[SI],CX
JMP NEXT_C_BARS

SELECT_ENTER: POP AX
MOV MODIFY_FLAG,TRUE
JMP SHORT COLOR_S_END

COLOR_S_END1: MOV SI,COLOR_INDEX
POP COLOR_VALUES[SI]
COLOR_S_END: CALL CLEAR_BARS
RET

;----------------------------------------------;

CLEAR_BARS: MOV AX,COLOR_TOP + COLOR_MAX + 2
CALL CALC_ADDR
ADD DI,SELECT_START

PUSH ES
MOV DX,STATUS_REG ;Retrieve status register.
MOV ES,VIDEO_SEG ;Point to screen segment.
MOV SI,COLOR_INDEX
MOV BH,COLOR.W
MOV AL,SPACE

MOV BP,8
NEXT_CLEAR3: PUSH DI
MOV CX,16 * 3
NEXT_CLEAR4: CALL WRITE_CHAR
LOOP NEXT_CLEAR4
POP DI
ADD DI,CRT_WIDTH
DEC BP
JNZ NEXT_CLEAR3
POP ES
RET

;----------------------------------------------;
COPY_COLORS: PUSH ES
MOV ES,PGM_BUFF_SEG
XOR BX,BX
NEXT_C_COPY: CMP COLOR_MENUS[BX],INACTIVE
JZ COPY_C_END
MOV DI,COLOR_ADDRS[BX]
MOV AX,COLOR_VALUES[BX]
STOSB
INC BX
INC BX
JMP NEXT_C_COPY
COPY_C_END: POP ES
RET

;----------------------------------------------;
SCAN_CODES LABEL BYTE

DB "1",2,"2",3,"3",4,"4",5,"5",6,"6",7,"7",8,"8",9,"9",0AH,"0",0BH,"-",0CH
DB "=",0DH,"Q",10H,"W",11H,"E",12H,"R",13H,"T",14H,"Y",15H,"U",16H,"I",17H
DB "O",18H,"P",19H,"[",1AH,"]",1BH,"A",1EH,"S",1FH,"D",20H,"F",21H,"G",22H
DB "H",23H,"J",24H,"K",25H,"L",26H,";",27H,39,28H,96,29H,"\",2BH,"Z",2CH
DB "X",2DH,"C",2EH,"V",2FH,"B",30H,"N",31H,"M",32H,",",33H,".",34H,"/",35H

SCAN_COUNT EQU ($ - SCAN_CODES) / 2


;--------------;
HOT_MENU LABEL BYTE
DB "Current hotkey is ",0
HOT_MENU2 LABEL BYTE
DB "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~",0

HOT_MENU3 LABEL BYTE
DB "Select HotKey and then press Enter. Esc to cancel",0

HOT_MENU4 LABEL BYTE
DB "Select Shift key and then press Enter. Esc to cancel",0

HOT_MENU5 LABEL BYTE
DB "Press Alpha or Numeric key. Esc to cancel",0

SHIFT_TEXT DB "CtRs", "CtLs", "AlRs", "AlLs", "RsLs", "CtAl"
CTRL DB "Ctrl ",0
ALT DB "Alt ",0
RT_SHIFT DB "Right Shift ",0
LT_SHIFT DB "Left Shift ",0


SHIFT_COMBOS LABEL BYTE
DB CTRL_KEY + RIGHT_SHIFT, CTRL_KEY + LEFT_SHIFT, ALT_KEY + RIGHT_SHIFT
DB ALT_KEY + LEFT_SHIFT, RIGHT_SHIFT + LEFT_SHIFT, CTRL_KEY + ALT_KEY
COMBOS_LEN EQU $ - SHIFT_COMBOS

SHIFT_KEY DB ?
SHIFT_INDEX DW ?
HOTKEY LABEL WORD
SCAN_TEXT DB ?
SCAN_KEY DB ?

HOTKEY_MENU: MOV SI,OFFSET HOT_MENU3
CMP SCAN_ADDR,INACTIVE
JZ DISP_HOT_M
MOV SI,OFFSET HOT_MENU4
DISP_HOT_M: CALL DISPLAY_MENU

MOV AX,HOT_TOP
CALL CALC_ADDR
ADD DI,SELECT_START
MOV BP,DI
MOV BH,COLOR.W

MOV SI,OFFSET HOT_MENU
CALL WRITE_STRING

MOV SI,SHIFT_ADDR
PUSH DS
MOV DS,PGM_BUFF_SEG
MOV AL,[SI] ;Shift key
MOV SI,CS:SCAN_ADDR
MOV AH,[SI] ;AlphaNumeric key
POP DS
MOV SHIFT_KEY,AL
CMP SI,INACTIVE
JNZ SHIFT_ALPHA

SHIFT_ONLY: CALL DISP_COMBOS
ADD BP,CRT_WIDTH
MOV DI,BP
MOV SI,OFFSET HOT_MENU2
CALL WRITE_STRING
ADD BP,CRT_WIDTH

MOV AL,SHIFT_KEY
MOV DI,OFFSET SHIFT_COMBOS
MOV CX,COMBOS_LEN
REPNZ SCASB
NEG CX
ADD CX,COMBOS_LEN - 1
MOV SHIFT_INDEX,CX

;--------------;
NEXT_SH_ONLY: MOV DI,BP

XOR SI,SI
NEXT_SH_ONLY2: MOV AL,SHIFT_COMBOS[SI]
MOV BH,COLOR.C
CMP AL,SHIFT_KEY
JZ DISP_SHIFT
MOV BH,COLOR.W
DISP_SHIFT: PUSH SI
PUSH DI
CALL DISP_COMBOS
POP DI
POP SI
ADD DI,CRT_WIDTH
INC SI
CMP SI,COMBOS_LEN
JB NEXT_SH_ONLY2

CALL SHIFT_HOT
JNC NEXT_SH_ONLY
JMP HOT_DONE1

;--------------;
SHIFT_ALPHA: CALL DISP_COMBOS
XCHG AH,AL
CALL SCAN_LOOKUP
CALL WRITE_SCREEN
ADD BP,CRT_WIDTH
MOV DI,BP
MOV SI,OFFSET HOT_MENU2
CALL WRITE_STRING
ADD BP,CRT_WIDTH

NEXT_S_ALPHA: MOV DI,BP
MOV SI,OFFSET CTRL
MOV BH,COLOR.C
CMP SHIFT_KEY,CTRL_KEY
JZ DISP_CTRL
MOV BH,COLOR.W
DISP_CTRL: CALL WRITE_STRING
MOV DI,BP
ADD DI,CRT_WIDTH

MOV SI,OFFSET ALT
MOV BH,COLOR.C
CMP SHIFT_KEY,ALT_KEY
JZ DISP_ALT
MOV BH,COLOR.W
DISP_ALT: CALL WRITE_STRING

NEXT_S_ALPHA2: CALL GETKEY
JC HOT_DONE1
CMP AL,LEFT_SCAN
JZ HOT_DONE1
CMP AH,CR
JZ HOT_ALPHA
CMP AL,UP_SCAN
JNZ CK_ALPHA_DN
MOV SHIFT_KEY,CTRL_KEY
JMP NEXT_S_ALPHA
CK_ALPHA_DN: CMP AL,DOWN_SCAN
JNZ NEXT_S_ALPHA2
MOV SHIFT_KEY,ALT_KEY
JMP NEXT_S_ALPHA

;--------------;
HOT_ALPHA: MOV SI,OFFSET HOT_MENU5
CALL DISPLAY_MENU
MOV DH,HOT_TOP + 4
MOV DL,SELECT_START SHR 1
CALL SET_CURSOR
ADD BP,CRT_WIDTH
ADD BP,CRT_WIDTH

NEXT_ALPHA: CALL GETKEY
JC HOT_DONE1
CMP AL,LEFT_SCAN
JZ HOT_DONE1
CALL SCAN_LOOKUP
JCXZ NEXT_ALPHA
MOV HOTKEY,AX
MOV DI,BP
MOV BH,COLOR.C
CALL WRITE_SCREEN
MOV MODIFY_FLAG,1

HOT_DONE1: CALL HIDE_CURSOR
CALL CK_MODIFIED
JC HOT_DONE
CALL COPY_HOT
CALL SAVE

HOT_DONE: RET

;--------------;
SCAN_LOOKUP: PUSH ES
PUSH DI
MOV CX,CS
MOV ES,CX
MOV DI,OFFSET SCAN_CODES + 1
MOV CX,SCAN_COUNT ;38 possible Alt key combos.
NEXT_SCAN2: SCASB ;Do we have a match?
JZ GOT_HOT
INC DI ;If no, bump pointer to next byte.
LOOP NEXT_SCAN2
GOT_HOT: MOV AH,AL
MOV AL,[DI - 2]
POP DI
POP ES
RET

;----------------------------------------------;
;INPUT: AL=shift state; DI->screen; BH=color

DISP_COMBOS: PUSH AX
MOV CX,AX
MOV SI,OFFSET CTRL
TEST AL,CTRL_KEY
JZ CK_ALT
CALL WRITE_STRING
CK_ALT: MOV SI,OFFSET ALT
TEST CL,ALT_KEY
JZ CK_RT_SHIFT
CALL WRITE_STRING
CK_RT_SHIFT: MOV SI,OFFSET RT_SHIFT
TEST CL,RIGHT_SHIFT
JZ CK_LF_SHIFT
CALL WRITE_STRING
CK_LF_SHIFT: MOV SI,OFFSET LT_SHIFT
TEST CL,LEFT_SHIFT
JZ SHIFT_DONE
CALL WRITE_STRING
SHIFT_DONE: POP AX
RET

;----------------------------------------------;
SHIFT_HOT: CALL GETKEY
JC SHIFT_HOT_END
CMP AH,CR
JZ SHIFT_ENTER
MOV BL,SHIFT_KEY
MOV SI,SHIFT_INDEX

CMP AL,UP_SCAN
JNZ CK_SHIFT_DN
SUB SI,1
JC SHIFT_HOT
JMP SHORT SHIFT_UPDATE

CK_SHIFT_DN: CMP AL,DOWN_SCAN
JNZ SHIFT_HOT
ADD SI,1
CMP SI,COMBOS_LEN
JZ SHIFT_HOT
SHIFT_UPDATE: MOV AL,SHIFT_COMBOS[SI]
MOV SHIFT_KEY,AL
MOV SHIFT_INDEX,SI
CLC
JMP SHORT SHIFT_HOT_END

SHIFT_ENTER: MOV MODIFY_FLAG,1
STC
SHIFT_HOT_END: RET

;----------------------------------------------;
COPY_HOT: PUSH ES

MOV AL,SHIFT_KEY
MOV DI,OFFSET SHIFT_COMBOS
MOV CX,COMBOS_LEN
REPNZ SCASB
NEG CX
ADD CX,COMBOS_LEN - 1
SHL CX,1
SHL CX,1
ADD CX,OFFSET SHIFT_TEXT
MOV BP,CX

MOV ES,PGM_BUFF_SEG
MOV DI,SHIFT_ADDR
STOSB
MOV DI,SHIFT_TEXT_ADDR
CMP DI,INACTIVE
JZ COPY_SCAN

MOV SI,BP
CMP SCAN_ADDR,INACTIVE
JZ COPY_S_TEXT
MOV SI,OFFSET CTRL
CMP AL,CTRL_KEY
JZ COPY_S_TEXT
MOV SI,OFFSET ALT
COPY_S_TEXT: MOV CX,4
REP MOVSB

COPY_SCAN: MOV DI,SCAN_ADDR
CMP DI,INACTIVE
JZ COPY_H_END
MOV AL,SCAN_KEY
STOSB
MOV DI,SCAN_TEXT_ADDR
CMP DI,INACTIVE
JZ COPY_H_END
MOV AL,SCAN_TEXT
STOSB

COPY_H_END: POP ES
RET

;--------------;
COM_MENU LABEL BYTE
DB "Enter 1, 2 or 3 for COM port. Esc to cancel",0

COM_MENU2 LABEL BYTE
DB "COM port: ",0

COM_MENU3 LABEL BYTE
DB " COM address: ",0

COM_MENU4 LABEL BYTE
DB "Enter desired COM hexadecimal port address from your modem manual. Esc to cancel",0

COM_PORT DW ?
PORT_SCREEN DW ? ;Screen address to write port #
ADDR_SCREEN DW ? ;Screen address to write port addr.

COM_EDIT DB 4 DUP (SPACE)
COM_EDIT_LEN EQU $ - COM_EDIT
DB 0

MODEM_MENU: MOV SI,OFFSET COM_MENU
CALL DISPLAY_MENU

MOV SI,COM_ADDR
PUSH DS
MOV DS,PGM_BUFF_SEG
MOV CX,[SI]
POP DS
MOV COM_PORT,CX

MOV AX,COM_TOP
CALL CALC_ADDR
ADD DI,SELECT_START
MOV BH,COLOR.W
MOV SI,OFFSET COM_MENU2
CALL WRITE_STRING
MOV PORT_SCREEN,DI
MOV AL,"1"
CMP CX,3F8H
JZ DISP_PORT
INC AL
CMP CX,2F8H
JZ DISP_PORT
INC AL
DISP_PORT: MOV BH,COLOR.C
CALL WRITE_SCREEN

MOV BH,COLOR.W
MOV SI,OFFSET COM_MENU3
CALL WRITE_STRING
MOV ADDR_SCREEN,DI
MOV BP,CX
MOV BH,COLOR.C
CALL HEX_ASCII

MOV DH,COM_TOP
MOV DL,SELECT_START SHR 1 +10
CALL SET_CURSOR

NEXT_MODEM: CALL GETKEY
JC M_END1
CMP AH,CR
JZ M_END1
CMP AL,LEFT_SCAN
JZ M_END1
CMP AH,"1"
JB NEXT_MODEM
CMP AH,"3"
JA NEXT_MODEM
MOV AL,AH
MOV CL,AL
MOV DI,PORT_SCREEN
MOV BH,COLOR.C
CALL WRITE_SCREEN

MOV AX,3F8H
CMP CL,"1"
JZ GOT_ADDR
MOV AX,2F8H
CMP CL,"2"
JZ GOT_ADDR

MOV SI,OFFSET COM_MENU4
CALL DISPLAY_MENU
MOV AX,ADDR_SCREEN
MOV SCREEN_START,AX
MOV LINE_START,OFFSET COM_EDIT
MOV LINE_END,OFFSET COM_EDIT + COM_EDIT_LEN
MOV BH,COLOR.C
MOV LEGAL_CHARS,OFFSET NUMBER_CHARS
CALL EDITOR
JC M_END1
CALL CK_HEX

MOV SI,OFFSET COM_EDIT
CALL PARSE_LINE
CALL GET_NUMBER
JC MODEM_ERR

GOT_ADDR: MOV COM_PORT,AX
MOV BP,AX
MOV DI,ADDR_SCREEN
MOV BH,COLOR.C
CALL HEX_ASCII

MOV MODIFY_FLAG,TRUE

M_END1: CALL HIDE_CURSOR
CALL CK_MODIFIED
JC M_END
PUSH ES
MOV ES,PGM_BUFF_SEG
MOV DI,COM_ADDR
MOV AX,COM_PORT
STOSW
POP ES
CALL SAVE


M_END: RET

MODEM_ERR: MOV SI,OFFSET NUMBER_ERR
CALL BACKUP_ERR2
JMP M_END

;--------------;
CK_HEX: MOV SI,OFFSET COM_EDIT
MOV CX,COM_EDIT_LEN
NEXT_CK_HEX: LODSB
CMP AL,SPACE
JBE FOUND_HEX
LOOP NEXT_CK_HEX
DEC SI
MOV DI,SI
MOV AH,[DI]
JMP SHORT FOUND_HEX2

FOUND_HEX: DEC SI
CMP SI,OFFSET COM_EDIT
JBE CK_HEX_END
MOV DI,SI

MOV AH,[DI-1]
FOUND_HEX2: AND AH,5FH
MOV AL,"H"
CMP AH,AL
JZ CK_HEX_END
STOSB
CK_HEX_END: RET

;-----------------------
;INPUT: BP=number; BH=color.

HEX_ASCII: MOV CH,SPACE
MOV AX,BP
MOV AX,BP
XCHG AH,AL
AND AL,0FH
CALL DISP_HEX

MOV AX,BP
MOV CL,4
SHR AL,CL
CALL DISP_HEX
MOV AX,BP
AND AL,0FH
CALL DISP_HEX
MOV AL,"H"
CALL WRITE_SCREEN
RET

;--------------;
DISP_HEX: ADD AL,90H
DAA
ADC AL,40H
DAA
CMP AL,"0"
JZ ADJUST_HEX
MOV CH,"0"
JMP SHORT PRINT_HEX
ADJUST_HEX: MOV AL,CH
PRINT_HEX: CALL WRITE_SCREEN
RET

;--------------;
LPT_MENU LABEL BYTE
DB " printer addresses found. Select printer 1"
LPT_LENGTH EQU $ - LPT_MENU + 1
DB " Esc to cancel",0

PRINTER DB "LPT: ",0
PRINTER_LEN EQU $ - PRINTER
PRINTER_CNT DB ?

PRINTER_MENU: CALL COUNT_PRINTERS

CALL MENU_OFFSET
MOV BH,COLOR.B
PUSH DI
MOV AL,PRINTER_CNT
CALL WRITE_SCREEN
MOV SI,OFFSET LPT_MENU
CALL WRITE_STRING
POP DI

CMP PRINTER_CNT,"1"
JBE GO_PRINTER
ADD DI,LPT_LENGTH * 2
MOV AL,"-"
CALL WRITE_SCREEN
MOV AL,PRINTER_CNT
CALL WRITE_SCREEN

GO_PRINTER: MOV AX,LPT_TOP
CALL CALC_ADDR
ADD DI,SELECT_START
MOV SI,OFFSET PRINTER
MOV BH,COLOR.W
CALL WRITE_STRING
MOV CX,DI

MOV SI,LPT_ADDR
PUSH DS
MOV DS,PGM_BUFF_SEG
MOV AL,[SI]
POP DS
ADD AL,"1"

MOV BH,COLOR.C
CALL WRITE_SCREEN

MOV DH,LPT_TOP
MOV DL,SELECT_START SHR 1 + PRINTER_LEN - 1
CALL SET_CURSOR

NEXT_PRINTER: CALL GETKEY
JC P_END1
XCHG AH,AL
CMP AL,CR
JZ P_END1
CMP AH,LEFT_SCAN
JZ P_END1
CMP AL,"1"
JB NEXT_PRINTER
CMP AL,PRINTER_CNT
JA NEXT_PRINTER

GOT_PRINTER: MOV MODIFY_FLAG,1
MOV DI,CX
MOV BP,AX
CALL WRITE_SCREEN
P_END1: CALL HIDE_CURSOR
CALL CK_MODIFIED
JC P_END
MOV AX,BP
SUB AL,"1"

PUSH ES
MOV ES,PGM_BUFF_SEG
MOV DI,LPT_ADDR
STOSB
POP ES
CALL SAVE

P_END: RET

;--------------;
PRINTER_BASE EQU 8

COUNT_PRINTERS:PUSH DS
MOV AX,40H
MOV DS,AX
MOV AL,"0"
MOV SI,PRINTER_BASE
MOV AH,3

NEXT_PRINTERS: CMP WORD PTR [SI],0
JZ PRINTERS_END
INC SI
INC SI
INC AL
DEC AH
JNZ NEXT_PRINTERS

PRINTERS_END: POP DS
MOV PRINTER_CNT,AL
RET

;--------------;
T_MENU LABEL BYTE
DB "Select text description then press Enter to change text. Esc to Exit",0

TEXT_M DB "Text descriptions",0
DB "~~~~~~~~~~~~~~~~~",0

TEXT_INDEX DW 0

TEXT_MENU: CMP [TEXT_MENUS],INACTIVE
JZ T_MENU_END1
CALL TEXT_DEFAULT
MOV TEXT_INDEX,0

NEXT_T_MENU: MOV STATE,CUSTOM_TYPE
MOV SI,OFFSET T_MENU
CALL DISPLAY_MENU

NEXT_T_MENU2: CALL DISP_TEXT
CALL GETKEY
JC T_MENU_END
CMP AL,LEFT_SCAN
JZ T_MENU_END

CMP AH,CR
JZ DO_TEXT
CMP AL,RIGHT_SCAN
JZ DO_TEXT

CK_T_UP: MOV BX,TEXT_INDEX
CMP AL,UP_SCAN
JNZ CK_T_DN
SUB BX,2
JC NEXT_T_MENU2
JMP SHORT TEXT_UPDATE

CK_T_DN: CMP AL,DOWN_SCAN
JNZ NEXT_T_MENU2
INC BX
INC BX
CMP BX,TEXT_MAX * 2
JAE NEXT_T_MENU2
CMP TEXT_MENUS[BX],INACTIVE
JZ NEXT_T_MENU2
TEXT_UPDATE: MOV TEXT_INDEX,BX
JMP NEXT_T_MENU2

DO_TEXT: CALL TEXT_SELECT
JMP NEXT_T_MENU

T_MENU_END1: CALL BEEP
T_MENU_END: CALL CK_MODIFIED
JC T_M_END
CALL COPY_TEXT
CALL SAVE
T_M_END: RET

;--------------;
TEXT_DEFAULT: PUSH DS
MOV DS,PGM_BUFF_SEG
XOR BX,BX
XOR BP,BP
NEXT_T_DEFAULT:CMP CS:[TEXT_MENUS.BX],INACTIVE
JZ T_DEFAULT_END
MOV SI,CS:[TEXT_ADDRS.BX]
MOV DI,OFFSET TEXT_EDIT
ADD DI,BP
MOV CX,CS:[TEXT_LENGTH.BX]
CMP CX,STRING_MAX
JB GET_DEF
MOV CX,STRING_MAX - 1
GET_DEF: REP MOVSB
XOR AL,AL
STOSB
ADD BP,STRING_MAX
INC BX
INC BX
CMP BX,TEXT_MAX * 2
JB NEXT_T_DEFAULT
T_DEFAULT_END: POP DS
RET

;--------------;
T_EDIT_ADDR DW ?

DISP_TEXT: MOV AX,TEXT_TOP
CALL CALC_ADDR
ADD DI,SELECT_START
MOV BH,COLOR.W

PUSH DI
MOV SI,OFFSET TEXT_M
CALL WRITE_STRING
POP DI
ADD DI,CRT_WIDTH

PUSH DI
CALL WRITE_STRING
POP DI

;--------------;
XOR BP,BP
MOV T_EDIT_ADDR,OFFSET TEXT_EDIT
NEXT_TEXT_DES: MOV SI,TEXT_MENUS[BP]
CMP SI,INACTIVE
JZ TEXT_DES_END
ADD DI,CRT_WIDTH
PUSH DI

MOV CX,STRING_MAX
MOV BH,COLOR.W
CMP TEXT_INDEX,BP
JNZ WRITE_DESC2
MOV BH,COLOR.C
CMP STATE,CUSTOM_TYPE
JZ WRITE_DESC2
MOV BH,COLOR.A

WRITE_DESC2: PUSH DS
MOV DS,ZIF_BUFF_SEG
CALL WRITE_LINE
CALL PAD_LINE
POP DS

MOV BH,COLOR.W
MOV SI,T_EDIT_ADDR
MOV CX,TEXT_LENGTH[BP]
CALL WRITE_LINE
POP DI
ADD T_EDIT_ADDR,STRING_MAX
INC BP
INC BP
CMP BP,TEXT_MAX * 2
JB NEXT_TEXT_DES

TEXT_DES_END: RET

;----------------------------------------------;
TEXT_M2 LABEL BYTE
DB "Edit text then press Enter. Esc to cancel",0

TEXT_SELECT: MOV STATE,SELECT_TYPE
CALL DISP_TEXT
MOV SI,OFFSET TEXT_M2
CALL DISPLAY_MENU

MOV AX,TEXT_INDEX
MOV BX,AX
MOV CX,TEXT_LENGTH[BX]
SHR AX,1
ADD AX,TEXT_TOP + 2
MUL CRT_WIDTH
ADD AX,SELECT_START + (STRING_MAX * 2)
MOV SCREEN_START,AX

MOV AX,STRING_MAX
SHR BX,1
MUL BX
ADD AX,OFFSET TEXT_EDIT
MOV LINE_START,AX
ADD AX,CX
MOV LINE_END,AX
MOV BH,COLOR.C
MOV LEGAL_CHARS,OFFSET ALL_CHARS
CALL EDITOR
JC TEXT_S_END
MOV MODIFY_FLAG,TRUE
TEXT_S_END: RET

;----------------------------------------------;
COPY_TEXT: PUSH ES
MOV ES,PGM_BUFF_SEG
XOR BX,BX
MOV BP,OFFSET TEXT_EDIT
NEXT_T_COPY: CMP TEXT_MENUS[BX],INACTIVE
JZ COPY_T_END
MOV SI,BP
MOV DI,TEXT_ADDRS[BX]
MOV CX,TEXT_LENGTH[BX]
REP MOVSB
INC BX
INC BX
ADD BP,STRING_MAX
JMP NEXT_T_COPY

COPY_T_END: POP ES
RET

;--------------;
N_MENU LABEL BYTE
DB "Select number description then press Enter to change number. Esc to Exit",0

NUMBER_M DB "Number descriptions",0
DB "~~~~~~~~~~~~~~~~~~~",0

NUMBER_INDEX DW 0

NUMBER_MENU: CMP [NUMBER_MENUS],INACTIVE
JZ N_MENU_END1
CALL NUM_DEFAULT
MOV NUMBER_INDEX,0

NEXT_N_MENU: MOV STATE,CUSTOM_TYPE
MOV SI,OFFSET N_MENU
CALL DISPLAY_MENU

NEXT_N_MENU2: CALL DISP_NUMBERS
CALL GETKEY
JC N_MENU_END
CMP AL,LEFT_SCAN
JZ N_MENU_END

CMP AH,CR
JZ DO_NUM
CMP AL,RIGHT_SCAN
JZ DO_NUM

CK_N_UP: MOV BX,NUMBER_INDEX
CMP AL,UP_SCAN
JNZ CK_N_DN
SUB BX,2
JC NEXT_N_MENU2
JMP SHORT NUM_UPDATE

CK_N_DN: CMP AL,DOWN_SCAN
JNZ NEXT_N_MENU2
INC BX
INC BX
CMP BX,NUM_MAX * 2
JAE NEXT_N_MENU2
CMP NUMBER_MENUS[BX],INACTIVE
JZ NEXT_N_MENU2
NUM_UPDATE: MOV NUMBER_INDEX,BX
JMP NEXT_N_MENU2

DO_NUM: CALL NUMBER_SELECT
JMP NEXT_N_MENU

N_MENU_END1: CALL BEEP
N_MENU_END: CALL CK_MODIFIED
JC N_M_END
CALL COPY_NUMBERS
CALL SAVE
N_M_END: RET

;----------------------------------------------;
NUM_DEFAULT: PUSH DS
MOV DS,PGM_BUFF_SEG
XOR BX,BX
MOV BP,OFFSET NUMBER_EDIT
NEXT_N_DEFAULT:CMP CS:[NUMBER_MENUS.BX],INACTIVE
JZ N_DEFAULT_END
MOV SI,CS:[NUMBER_ADDRS.BX]
MOV AX,[SI]
CMP BYTE PTR CS:[NUMBER_TYPE.BX],"W"
JZ GOT_NUM_VAL
XOR AH,AH

GOT_NUM_VAL: MOV DI,BP
CALL DECIMAL_ASCII
ADD BP,INTEGER_MAX
INC BX
INC BX
CMP BX,NUM_MAX * 2
JB NEXT_N_DEFAULT
N_DEFAULT_END: POP DS
RET

;----------------------------------------------;
DECIMAL_ASCII: PUSH BX
MOV BX,10 ;Divisor of ten.
XOR CX,CX ;Zero in counter.
NEXT_COUNT: XOR DX,DX ;Zero in high half.
DIV BX ;Divide by ten.
ADD DL,"0" ;Convert to ASCII.
PUSH DX ;Save results.
INC CX ;Also increment count.
OR AX,AX ;Are we done?
JNZ NEXT_COUNT ;Continue until zero.
MOV SI,INTEGER_MAX - 1
SUB SI,CX
NEXT_DEC_STORE:POP AX ;Retrieve numbers.
STOSB
LOOP NEXT_DEC_STORE
MOV CX,SI
JCXZ DEC_END
MOV AL,SPACE
REP STOSB
DEC_END: XOR AL,AL
STOSB
POP BX
RET

;----------------------------------------------;
N_EDIT_ADDR DW ?

DISP_NUMBERS: MOV AX,NUMBER_TOP
CALL CALC_ADDR
ADD DI,SELECT_START
MOV BH,COLOR.W

PUSH DI
MOV SI,OFFSET NUMBER_M
CALL WRITE_STRING
POP DI
ADD DI,CRT_WIDTH

PUSH DI
CALL WRITE_STRING
POP DI

;--------------;
XOR BP,BP
MOV N_EDIT_ADDR,OFFSET NUMBER_EDIT
NEXT_NUM_DES: MOV SI,NUMBER_MENUS[BP]
CMP SI,INACTIVE
JZ NUM_DES_END
ADD DI,CRT_WIDTH
PUSH DI

MOV BH,COLOR.W
CMP NUMBER_INDEX,BP
JNZ WRITE_DESC3
MOV BH,COLOR.C
CMP STATE,CUSTOM_TYPE
JZ WRITE_DESC3
MOV BH,COLOR.A

WRITE_DESC3: PUSH DS
MOV DS,ZIF_BUFF_SEG
MOV CX,STRING_MAX
CALL WRITE_LINE
CALL PAD_LINE
POP DS

MOV BH,COLOR.W
MOV SI,N_EDIT_ADDR
CALL WRITE_STRING
POP DI
ADD N_EDIT_ADDR,INTEGER_MAX
INC BP
INC BP
CMP BP,NUM_MAX * 2
JB NEXT_NUM_DES

NUM_DES_END: RET

;----------------------------------------------;
NUMBER_M2 LABEL BYTE
DB "Enter number between "
RANGE_L EQU $ - NUMBER_M2
DB " Esc to cancel",0

NUMBER_SELECT: MOV STATE,SELECT_TYPE
CALL DISP_NUMBERS
CALL MENU_OFFSET
MOV BH,COLOR.B
PUSH DI
MOV SI,OFFSET NUMBER_M2
CALL WRITE_STRING

MOV BP,NUMBER_INDEX
MOV AX,RANGE_LOW[BP]
MOV DI,OFFSET NUMBER_RANGE
CALL DECIMAL_ASCII
POP DI
ADD DI,RANGE_L * 2
MOV SI,OFFSET NUMBER_RANGE
NEXT_RANGE: LODSB
CMP AL,SPACE
JA NEXT_RANGE
MOV BYTE PTR [SI-1],0
MOV SI,OFFSET NUMBER_RANGE
CALL WRITE_STRING
MOV AL,"-"
CALL WRITE_SCREEN
PUSH DI
MOV AX,RANGE_HIGH[BP]
MOV DI,OFFSET NUMBER_RANGE
CALL DECIMAL_ASCII
POP DI
MOV SI,OFFSET NUMBER_RANGE
CALL WRITE_STRING

MOV AX,NUMBER_INDEX
MOV BX,AX
SHR AX,1
ADD AX,NUMBER_TOP + 2
MUL CRT_WIDTH
ADD AX,SELECT_START + (STRING_MAX * 2)
MOV SCREEN_START,AX

MOV AX,INTEGER_MAX
SHR BX,1
MUL BX
ADD AX,OFFSET NUMBER_EDIT
MOV LINE_START,AX
ADD AX,INTEGER_MAX - 1
MOV LINE_END,AX
MOV BH,COLOR.C
MOV LEGAL_CHARS,OFFSET NUMBER_CHARS
CALL EDITOR
JC NUM_S_END

MOV SI,LINE_START
CALL PARSE_LINE
JC NUM_S_END2
CALL GET_NUMBER
JC NUMBER_ERROR
MOV BP,NUMBER_INDEX
CMP AX,RANGE_LOW[BP]
JB NUMBER_ERROR
CMP AX,RANGE_HIGH[BP]
JA NUMBER_ERROR

NUM_S_END2: MOV MODIFY_FLAG,TRUE
NUM_S_END: RET

NUMBER_ERROR: MOV SI,OFFSET NUMBER_ERR
CALL BACKUP_ERR2
PUSH DS
MOV BX,NUMBER_INDEX
MOV SI,DS:[NUMBER_ADDRS.BX]
MOV DS,PGM_BUFF_SEG
MOV AX,[SI]
POP DS
CMP BYTE PTR [NUMBER_TYPE.BX],"W"
JZ RESTORE_NUM
XOR AH,AH
RESTORE_NUM: MOV DI,LINE_START
CALL DECIMAL_ASCII
JMP NUM_S_END

;----------------------------------------------;
COPY_NUMBERS: PUSH ES
MOV ES,PGM_BUFF_SEG
XOR BX,BX
MOV BP,OFFSET NUMBER_EDIT
NEXT_N_COPY: CMP NUMBER_MENUS[BX],INACTIVE
JZ COPY_N_END
MOV SI,BP
CALL PARSE_LINE
JC LOOP_N_COPY
CALL GET_NUMBER
JC LOOP_N_COPY
MOV DI,NUMBER_ADDRS[BX]
CMP BYTE PTR NUMBER_TYPE[BX],"W"
JZ COPY_WORD
STOSB
JMP SHORT LOOP_N_COPY
COPY_WORD: STOSW
LOOP_N_COPY: INC BX
INC BX
ADD BP,INTEGER_MAX
JMP NEXT_N_COPY

COPY_N_END: POP ES
RET

;----------------------------------------------;
DISP_CUSTOM: MOV AX,LISTING_TOP
CALL CALC_ADDR
ADD DI,CUSTOM_START
MOV SI,OFFSET CATEGORY + SIZE CAT
MOV BP,LISTING_LEN

NEXT_CUSTOM: PUSH SI
PUSH DI
MOV CX,CRT_WIDTH
SUB CX,CUSTOM_START
SHR CX,1

CMP SI,OFFSET CAT_END
JAE PAD_CUSTOM
CMP [SI.CAT_STATUS],INACTIVE
JZ GET_INACTIVE

CMP SI,CUSTOM_ADDR
JNZ CK_CUST_COLOR
MOV BH,COLOR.C
CMP STATE,FILE_TYPE
JZ DISPLAY_CUST
MOV BH,COLOR.A
JMP SHORT DISPLAY_CUST

CK_CUST_COLOR: MOV BH,COLOR.I
CMP STATE,LISTING_TYPE
JNZ DISPLAY_CUST
MOV BH,COLOR.W
JMP SHORT DISPLAY_CUST

GET_INACTIVE: MOV BH,COLOR.T
CMP SI,CUSTOM_ADDR
JZ DISPLAY_CUST
MOV BH,COLOR.S

DISPLAY_CUST: ADD SI,SIZE CAT_STATUS + SIZE CAT_LEN
MOV AL,"["
CALL WRITE_CHAR
DEC CX
CALL WRITE_LINE
MOV BH,COLOR.W
CALL PAD_LINE

CK_LAST_CUST: POP DI
POP SI
ADD DI,CRT_WIDTH
CMP SI,OFFSET CAT_END
JAE LOOP_CUST
ADD SI,SIZE CAT

LOOP_CUST: DEC BP
JNZ NEXT_CUSTOM
RET

PAD_CUSTOM: MOV BH,COLOR.W
CALL PAD_LINE
JMP SHORT CK_LAST_CUST

;----------------------------------------------;
TOP_WINDOW LABEL BYTE
DB "ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»",0
WINDOW_LEN EQU $ - TOP_WINDOW
BOTTOM_WINDOW LABEL BYTE
DB "ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ",0

MODIFY_MENU LABEL BYTE
DB "º Do you wish to save your changes? Y/N º",0

BACKUP_MENU LABEL BYTE
DB "º Backup up program first? Y/N º",0

OVERWRITE_MENU LABEL BYTE
DB "º Backup file exists. Overwrite? Y/N º",0

DISK_FULL_MENU LABEL BYTE
DB "º Not enough disk space. Press any key. º",0

BACKUP_FAIL LABEL BYTE
DB "º Backup failed. Press any key. º",0

SAVE_FAIL LABEL BYTE
DB "º Save failed. Press any key. º",0

NUMBER_ERR LABEL BYTE
DB "º Number out of range. Press any key. º",0

CK_MODIFIED: CMP MODIFY_FLAG,TRUE
JNZ NO_MODIFY
MOV MODIFY_FLAG,FALSE

NEXT_MODIFY: MOV SI,OFFSET MODIFY_MENU
CALL DISP_WINDOW

CALL CLEAR_KEY
CALL GETKEY
CMP AL,ESC_SCAN
JZ NO_MODIFY
CMP AL,N_SCAN
JZ NO_MODIFY
CMP AL,Y_SCAN
JNZ NEXT_MODIFY

CK_BACKUP: CMP BACKUP_FLAG,TRUE
JZ YES_MODIFY
MOV SI,OFFSET BACKUP_MENU
CALL DISP_WINDOW
CALL CLEAR_KEY
GET_MODIFY: CALL GETKEY
CMP AL,ESC_SCAN
JZ NO_MODIFY
CMP AL,N_SCAN
JZ YES_MODIFY
CMP AL,Y_SCAN
JNZ GET_MODIFY

CALL DO_BACKUP
JC NEXT_MODIFY

YES_MODIFY: CALL CLEAR_MODIFIED
CLC
JMP SHORT MODIFIED_END

NO_MODIFY: CALL CLEAR_MODIFIED
STC

MODIFIED_END: RET

;----------------------------------------------;
; OUTPUT: CF=1 if failed.

READ_HANDLE DW ?
WRITE_HANDLE DW ?

DO_BACKUP: CALL MAKE_BACKUP
MOV DX,OFFSET BACKUP_NAME

XOR CX,CX
MOV AH,4EH
INT 21H
JNC OVERWRITE

CALL GET_FREE
JNC FREE_SPACE2
JMP BACKUP_END

OVERWRITE: MOV SI,OFFSET OVERWRITE_MENU
CALL DISP_WINDOW
CALL CLEAR_KEY
CALL GETKEY
CMP AL,Y_SCAN
JZ GET_DISK
CMP AL,ESC_SCAN
JNZ CK_OVER2
STC
JMP BACKUP_END2
CK_OVER2: CMP AL,N_SCAN
JNZ OVERWRITE
JMP BACKUP_END

GET_DISK: CALL GET_FREE
JNC FREE_SPACE
JMP BACKUP_ERR
FREE_SPACE: ADD AX,DTA.SIZE_LOW
ADC DX,DTA.SIZE_HIGH
FREE_SPACE2: SUB AX,FILE_SIZE[0]
SBB DX,FILE_SIZE[2]
JNC CREATE
MOV SI,OFFSET DISK_FULL_MENU
JMP SHORT BACKUP_ERR2

CREATE: MOV DX,OFFSET BACKUP_NAME
XOR CX,CX
MOV AH,3CH
INT 21H
JNC DO_BACKUP2
JMP SHORT BACKUP_ERR

DO_BACKUP2: MOV WRITE_HANDLE,AX
MOV DX,OFFSET ASCIIZ_NAME
MOV AX,3D00H
INT 21H
JC CLOSE_READ
MOV READ_HANDLE,AX

XOR DX,DX
NEXT_BACKUP: MOV BX,READ_HANDLE
MOV CX,BAC_BUFF_SIZE * KILOBYTES - 1
PUSH DS
MOV DS,BAC_BUFF_SEG
MOV AH,3FH
INT 21H
POP DS
JC CLOSE_READ
OR AX,AX
JZ CLOSE_READ

MOV CX,AX
MOV BX,WRITE_HANDLE
PUSH DS
MOV DS,BAC_BUFF_SEG
MOV AH,40H
INT 21H
POP DS
JC CLOSE_READ
CMP CX,AX
STC
JNZ CLOSE_READ
CMP CX,BAC_BUFF_SIZE * KILOBYTES - 1
JZ NEXT_BACKUP
CLC

CLOSE_READ: PUSHF
MOV BX,READ_HANDLE
MOV AH,3EH
INT 21H
POPF

CLOSE_WRITE: PUSHF
MOV BX,WRITE_HANDLE
MOV AH,3EH
INT 21H
POPF

JC BACKUP_ERR

BACKUP_END: MOV BACKUP_FLAG,TRUE
CALL CLEAR_MODIFIED
MOV FAIL_FLAG,FALSE
CLC
BACKUP_END2: RET

BACKUP_ERR: MOV SI,OFFSET BACKUP_FAIL
BACKUP_ERR2: CALL DISP_WINDOW
CALL BEEP
CALL GETKEY
CALL CLEAR_MODIFIED
MOV FAIL_FLAG,FALSE
STC
RET

;----------------------------------------------;
GET_FREE: MOV DL,PGM_DRIVE
INC DL
MOV AH,36H ;Disk free space.
INT 21H
CMP AX,-1
STC
JZ DISK_FREE_END
MUL BX ;Sectors per cluster * clusters
PUSH DX
MUL CX ;Result times bytes per cluster
MOV BX,AX
POP AX
PUSH DX
MUL CX
POP DX
ADD DX,AX
MOV AX,BX
CLC
DISK_FREE_END: RET ; = available bytes.

;----------------------------------------------;
BAK DB ".BAK",0

MAKE_BACKUP: MOV SI,OFFSET ASCIIZ_NAME
MOV DI,OFFSET BACKUP_NAME
MOV CX,8
NEXT_BACK: LODSB
CMP AL,"."
JZ BAK_EXT
OR AL,AL
JZ BAK_EXT
STOSB
LOOP NEXT_BACK
BAK_EXT: MOV SI,OFFSET BAK
MOV CX,5
REP MOVSB
RET

;----------------------------------------------;
; INPUT: SI-> window.

DISP_WINDOW: CALL CALC_WIN
MOV BH,COLOR.W
PUSH DI
PUSH SI
MOV SI,OFFSET TOP_WINDOW
CALL WRITE_STRING
POP SI
POP DI
ADD DI,CRT_WIDTH
PUSH DI
CALL WRITE_STRING
POP DI
ADD DI,CRT_WIDTH
MOV SI,OFFSET BOTTOM_WINDOW
CALL WRITE_STRING
RET

;----------------------------------------------;
CLEAR_MODIFIED:CALL CALC_WIN
MOV BH,COLOR.W
MOV AL,SPACE
MOV SI,3
NEXT_CLEAR_M: PUSH DI
MOV CX,WINDOW_LEN
CALL REPEAT_CHAR
POP DI
ADD DI,CRT_WIDTH
DEC SI
JNZ NEXT_CLEAR_M
RET

;----------------------------------------------;
CALC_WIN: MOV AL,ROWS
DEC AL
DEC AL
DEC AL
XOR AH,AH
CALL CALC_ADDR
ADD DI,SELECT_START
RET

;----------------------------------------------;
SAVE: CALL MAKE_FILENAME
MOV DX,OFFSET ASCIIZ_NAME
MOV AX,3D01H
INT 21H
JC SAVE_ERR
CMP FAIL_FLAG,TRUE
JZ SAVE_ERR

MOV BX,AX
MOV CX,PGM_BYTES
XOR DX,DX
PUSH DS
MOV DS,PGM_BUFF_SEG
MOV AH,40H
INT 21H
POP DS

PUSHF
MOV AH,3EH
INT 21H
POPF
JNC SAVE_END

SAVE_ERR: MOV SI,OFFSET SAVE_FAIL
CALL DISP_WINDOW
CALL BEEP
CALL GETKEY
CALL CLEAR_MODIFIED
STC

SAVE_END: MOV FAIL_FLAG,FALSE
RET

;----------------------------------------------;
CLEAR_FILE: PUSH ES
PUSH DI

MOV AX,LISTING_TOP
CALL CALC_ADDR
ADD DI,CUSTOM_START
MOV BP,LISTING_LEN
MOV DX,STATUS_REG ;Retrieve status register.
MOV ES,VIDEO_SEG
MOV BH,COLOR.W
MOV AL,SPACE

NEXT_CLEAR: PUSH DI
MOV CX,CRT_WIDTH
SUB CX,CUSTOM_START
SHR CX,1
NEXT_CLEAR2: CALL WRITE_CHAR
LOOP NEXT_CLEAR2
POP DI
ADD DI,CRT_WIDTH
DEC BP
JNZ NEXT_CLEAR

POP DI
POP ES
RET

;--------------------------------------------------------------;
; OUTPUT: ASCIIZ_NAME = filename

MAKE_ZIF_NAME: PUSH DS
MOV SI,BAR_ADDR
MOV DS,FILENAME_SEG ;DS:SI -> Source name.
MOV DI,OFFSET ASCIIZ_NAME ;Name storage.
MOV CX,8 ;8 characters of name.
NEXT_NAME2: LODSB
CMP AL,SPACE ;End of name?
JZ EXTENSION3 ;If yes, do extension.
STOSB
LOOP NEXT_NAME2

EXTENSION3: POP DS
MOV SI,OFFSET ZIF_EXT
MOV CX,ZIF_LEN
REP MOVSB
RET

;----------------------------------------------;

MAKE_FILENAME: PUSH DS
MOV SI,BAR_ADDR
MOV DS,FILENAME_SEG ;DS:SI -> Source name.
MOV BP,SI
MOV DI,OFFSET ASCIIZ_NAME ;Name storage.
MOV CX,8 ;8 characters of name.
NEXT_NAME: LODSB
CMP AL,SPACE ;End of name?
JZ EXTENSION2 ;If yes, do extension.
STOSB
LOOP NEXT_NAME
EXTENSION2: MOV SI,BP ;Retrieve name start.
ADD SI,9 ;Move to extension field.
CMP BYTE PTR [SI],SPACE ;Is there any extension?
JZ NAME_DONE ;If no, done.
MOV AL,"." ;Else, add delimiting dot.
STOSB
MOV CX,3 ;3 characters for extension.
NEXT_EXT: LODSB
CMP AL,SPACE
JZ NAME_DONE
STOSB
LOOP NEXT_EXT
NAME_DONE: XOR AL,AL ;ASCIIZ.
STOSB
POP DS
RET

;----------------------------------------------;
DISP_LISTING: PUSH DS
PUSH ES

MOV AX,LISTING_TOP
CALL CALC_ADDR
MOV SI,LISTING_ADDR
MOV BP,LISTING_LEN
MOV DX,STATUS_REG ;Retrieve status register.
MOV ES,VIDEO_SEG ;Point to screen segment.
MOV DS,FILENAME_SEG

NEXT_LISTING: PUSH DI
MOV CX,SIZE FILE_RECORD

DISPLAY_LINE: MOV BH,CS:COLOR.I
CMP BYTE PTR [SI],-1
JZ PAD_LISTING
CMP SI,CS:BAR_ADDR
JZ CK_BAR_COLOR
CMP CS:STATE,FILE_TYPE
JNZ DISPLAY_LINE2
MOV BH,CS:COLOR.W
JMP SHORT DISPLAY_LINE2

CK_BAR_COLOR: MOV BH,CS:COLOR.C
CMP CS:STATE,FILE_TYPE
JNZ DISPLAY_LINE2
MOV BH,CS:COLOR.A

DISPLAY_LINE2: LODSB
CALL WRITE_CHAR
LOOP DISPLAY_LINE2

CK_LAST_LINE: POP DI
ADD DI,CS:CRT_WIDTH
DEC BP
JNZ NEXT_LISTING
POP ES
POP DS
RET

PAD_LISTING: CALL PAD_LINE
JMP SHORT CK_LAST_LINE


;--------------;
PAD_LINE: JCXZ PAD_LINE_END
MOV AL,SPACE
PAD_LINE2: CALL WRITE_CHAR
LOOP PAD_LINE2
PAD_LINE_END: RET

;----------------------------------------------;
; Keeps track of a variable length field in CX while writing string.
WRITE_LINE: LODSB
OR AL,AL
JZ WRITE_LINE_END
CALL WRITE_CHAR
LOOP WRITE_LINE
WRITE_LINE_END:RET

;--------------;
DUMMY_RET: RET

;----------------------------------------------;
ZIF_DIR_MENU DB " Zif Directory is ",0
PGM_DIR_MENU DB " Utility Directory is ",0
HEADING DB "Utilities ",0,"Custom Categories",0
DB "~~~~~~~~~ ",0,"~~~~~~~~~~~~~~~~~",0

DISP_DIR: MOV BH,COLOR.B
MOV DI,CRT_WIDTH
MOV CX,DI
SHR CX,1

PUSH DI
MOV SI,OFFSET ZIF_DIR_MENU
CALL WRITE_LINE
MOV AL,ZIF_DRIVE
ADD AL,"A"
CALL WRITE_CHAR
DEC CX
MOV AL,":"
CALL WRITE_CHAR
DEC CX
MOV SI,OFFSET ZIF_DIR
CALL WRITE_LINE
CALL PAD_LINE

POP DI
MOV CX,CRT_WIDTH
ADD DI,CX
PUSH DI
SHR CX,1
MOV SI,OFFSET PGM_DIR_MENU
CALL WRITE_LINE
MOV AL,PGM_DRIVE
ADD AL,"A"
CALL WRITE_CHAR
DEC CX
MOV AL,":"
CALL WRITE_CHAR
DEC CX
MOV SI,OFFSET PGM_DIR
CALL WRITE_LINE
CALL PAD_LINE
POP DI

MOV CX,CRT_WIDTH
ADD DI,CX
SHR CX,1
PUSH DI
MOV BH,COLOR.W
MOV SI,OFFSET HEADING
CALL WRITE_LINE
INC DI
INC DI
DEC CX
CALL WRITE_LINE
CALL PAD_LINE
POP DI

MOV CX,CRT_WIDTH
ADD DI,CX
SHR CX,1
CALL WRITE_LINE
INC DI
INC DI
DEC CX
CALL WRITE_LINE
CALL PAD_LINE

RET

;---------------------------------------------------;
; Insertion sort; Doesn't destroy secondary sorts. ;
;---------------------------------------------------;
SORT: MOV BP,LAST_ADDR
XOR SI,SI ;SI = First record.
MOV DI,SIZE FILE_RECORD ;DI = Second record.
PUSH DS
PUSH ES
MOV ES,FILENAME_SEG
MOV DS,FILENAME_SEG

CMP BYTE PTR [SI],-1
JZ SORT_END
CMP BYTE PTR [DI],-1
JZ SORT_END

NEXT_SORT: CMP DI,BP ;Sort is done when last record.
JA SORT_END

PUSH SI ;Save record pointers.
PUSH DI
MOV AX,SI ;Carry source pointer in AX.
MOV DX,DI ;Carry destination pointer in DX.

NEXT_RECORD: MOV CX,SIZE FILE_RECORD ;Field length.
COMPARE: REPZ CMPSB ;Compare the fields.
JBE NO_SWITCH ;If below or equal, no switch.

SWAP: CMP DX,TEMP_RECORD ;Else, if DX points to temporary
JZ DO_SWAP ; storage, safe to move.
MOV SI,DX ;Else, move destination record
MOV DX,TEMP_RECORD ; to temporary storage.
MOV DI,DX
MOV CX,SIZE FILE_RECORD / 2
REP MOVSW

DO_SWAP: MOV SI,AX ;Restore source pointer
MOV DI,SI ;Destination = source +
ADD DI,SIZE FILE_RECORD ; field size.
MOV CX,SIZE FILE_RECORD / 2
REP MOVSW ;Move the record.

MOV DI,DX ;Prepare for next compare.
SUB AX,SIZE FILE_RECORD ;Destination = temporary.
MOV SI,AX ;Move one record towards start.
JNC NEXT_RECORD

NO_SWITCH: MOV SI,TEMP_RECORD ;If DX doesn't point to temporary
CMP DX,SI ; storage, no move was made; next
JNZ NEXT_INSERT ; compare.
MOV DI,AX ;Else, move temporary record
ADD DI,SIZE FILE_RECORD ; into last move location.
MOV CX,SIZE FILE_RECORD / 2
REP MOVSW

NEXT_INSERT: POP DI ;Restore outside loop pointers.
POP SI
ADD SI,SIZE FILE_RECORD ;Move both source and destination
ADD DI,SIZE FILE_RECORD ; to the next record.
JMP NEXT_SORT

SORT_END: POP ES
POP DS
MOV FILE_CURRENT,FALSE
RET

;----------------------------------------------;
; OUTPUT: CF=1 if illegal parameter; DX-> illegal parameter.

PARSE: MOV SI,81H
MOV DI,OFFSET ZIF_DRIVE
CALL PARSE_IT
CALL RESTORE_DIR
CALL PARSE_DELIMIT
CMP BYTE PTR [SI],SPACE
JA PARSE_PGM
COPY_ZIF: MOV SI,OFFSET ZIF_DRIVE
MOV DI,OFFSET PGM_DRIVE
MOV CX,SPEC_SIZE
REP MOVSB
CLC
JMP SHORT PARSE_END

PARSE_PGM: CMP BYTE PTR [SI],";"
JNZ PARSE_PGM2
INC SI
PARSE_PGM2: MOV DI,OFFSET PGM_DRIVE
CALL PARSE_IT
JC COPY_ZIF
PARSE_END: RET

;----------------------------------------------;
; INPUT: SI-> parameter; DI-> storage.
; OUTPUT: CF=1 if illegal parameter; DX-> illegal parameter; SI-> parameter end.

PARSE_IT: CALL PARSE_DELIMIT
MOV BX,SI ;Filespec start.
NEXT_PARSE: LODSB
CMP AL,";"
JZ PARSE_DRIVE
CMP AL,","
JZ PARSE_DRIVE
CMP AL,"/"
JZ PARSE_DRIVE
CMP AL,SPACE
JBE PARSE_DRIVE
CMP AL,":"
JNZ NEXT_PARSE
MOV DL,[SI-2]
AND DL,5FH ;Capitalize.
SUB DL,"A" ;Convert to DOS format.
CALL SELECT_DISK
MOV BX,SI ;Save as filename start.
JMP NEXT_PARSE ;Continue parsing.

PARSE_DRIVE: MOV AH,19H
INT 21H
STOSB
PUSH SI
MOV SI,OFFSET CURRENT_DIR
CALL GET_DIR
POP SI

DEC SI
CMP SI,BX
JZ PARSE_DIR
PUSH [SI]
MOV BYTE PTR [SI],0
MOV DX,BX
CALL CHANGE_DIR
POP [SI]

PARSE_DIR: PUSHF
PUSH SI
MOV SI,DI
CALL GET_DIR
POP SI
POPF

PARSE_DIR_END: MOV DX,BX
RET

;-----------------------------------------------------------------;
; INPUT: SI -> string; OUTPUT SI -> first non-white space. ;
;-----------------------------------------------------------------;
PARSE_DELIMIT:
NEXT_DELIMIT: LODSB ;Get a byte.
OR AL,AL
JZ LEADING_END2
CMP AL,CR
JZ LEADING_END2
CMP AL,SPACE ;Is it a space char or below?
JBE NEXT_DELIMIT
LEADING_END2: DEC SI ;Else, adjust pointer to
RET ; string start.

;----------------------------------------------;
PARSE_LINES: CALL PARSE_LINE
JNC PARSE_LINE_END
CMP AL,LF
JZ PARSE_LINES
JMP SHORT NO_PARA2

PARSE_LINE: LODSB
CMP AL,CTRL_Z
JZ NO_PARA
CMP AL,"["
JZ NO_PARA
CMP AL,LF
JZ NO_PARA2
CMP AL,SPACE
JBE PARSE_LINE
CMP AL,"="
JZ PARSE_LINE
CMP AL,";"
JZ COMMENTS

CLC
DEC SI
PARSE_LINE_END:RET

COMMENTS: LODSB
CMP AL,CTRL_Z
JZ NO_PARA
CMP AL,LF
JZ NO_PARA2
JMP COMMENTS

NO_PARA: DEC SI
NO_PARA2: STC
RET

;----------------------------------------------;
; INPUT: SI -> ASCII number.
; OUTPUT: CF=1 if no number. CX:AX = number.

NUM_FLAG DB ?

GET_NUMBER: PUSH SI
NEXT_CAP2: LODSB
CMP AL,SPACE
JBE GET_TYPE
CMP AL,"a"
JB NEXT_CAP2
CMP AL,"z"
JA NEXT_CAP2
AND BYTE PTR [SI-1],5FH
JMP NEXT_CAP2

GET_TYPE: POP SI
PUSH SI
NEXT_TYPE: LODSB
CMP AL,SPACE
JBE A_NUMBER
CMP AL,"A"
JB NEXT_TYPE
CMP AL,"H"
JA NEXT_TYPE
POP SI
CALL DO_HEX
JMP SHORT NUMBER_END

A_NUMBER: POP SI
CALL DO_NUMBER

NUMBER_END: RET

;--------------;
DO_HEX: PUSH BX
XOR BX,BX ;Start with zero.
XOR CX,CX
MOV CS:NUM_FLAG,0 ;Number exists flag.

NEXT_HEX: LODSB ;Get a byte.
CMP AL,CTRL_Z
JZ HEX_END1
CMP AL,"H"
JZ HEX_END2
CMP AL,COMMA
JZ NEXT_HEX
CMP AL,"-"
JZ HEX_END2
CMP AL,SPACE
JBE HEX_END2
SUB AL,"0" ;ASCII to binary.
JC HEX_ERR ;If not 0 to 9, skip.
CMP AL,9 ;Is it A - F ?
JLE NOT_ALPHA ;If no, OK.
SUB AL,7 ;Else, adjust for alpha.
CMP AL,10 ;Is it punctuation?
JB HEX_END2 ;If yes, skip.
CMP AL,15 ;Is it valid?
JA HEX_ERR ;If no, skip.
NOT_ALPHA: MOV AH,4
NEXT_SHIFT2: SHL BX,1
RCL CX,1
DEC AH
JNZ NEXT_SHIFT2
OR BL,AL ;Add to number.
MOV CS:NUM_FLAG,1
JMP NEXT_HEX
HEX_END1: DEC SI
HEX_END2: MOV AX,BX
CMP CS:NUM_FLAG,0
JZ HEX_ERR
CLC
HEX_END: POP BX
RET

HEX_ERR: STC
POP BX
RET

;--------------;
DO_NUMBER: PUSH BX
PUSH BP
XOR BX,BX ;Start with zero.
XOR CX,CX
MOV CS:NUM_FLAG,0 ;Number exists flag.
MOV BP,10
NEXT_NUMBER: LODSB
XOR AH,AH ;Zero in high half.
CMP AL,CTRL_Z
JZ NUM_END1
CMP AL,COMMA
JZ NEXT_NUMBER
CMP AL,"-"
JZ NUM_END2
CMP AL,SPACE ;Is byte a carriage return?
JBE NUM_END2 ;If yes, done.
CMP AL,COMMA
JZ NEXT_NUMBER
CMP AL,"0" ;Is byte a number?
JB NUM_ERR
CMP AL,"9"
JA NUM_ERR ;If no, done.
SUB AL,"0" ;Else, convert to hex.
XCHG AX,BX ;Store in BX.
MUL BP ;Multiply accumulated by 10.
PUSH DX
XCHG AX,CX
MUL BP
ADD BX,CX
POP CX
ADC CX,0
ADD CX,AX
MOV CS:NUM_FLAG,1 ;Flag number found.
JMP NEXT_NUMBER

NUM_END1: DEC SI
NUM_END2: MOV AX,BX
CMP CS:NUM_FLAG,0
JZ NUM_ERR
CLC
NUM_END: POP BP
POP BX
RET

NUM_ERR: STC
POP BP
POP BX
RET

;----------------------------------------------;
VIDEO_SETUP: PUSH ES
MOV AX,500H ;Make sure active page is zero.
INT 10H
MOV AX,40H ;Point to the ROM BIOS data area
MOV ES,AX
MOV AX,ES:CRT_COLS
MOV COLUMNS,AX
SHL AX,1
MOV CRT_WIDTH,AX
MOV AX,ES:[4EH]
MOV CRT_START,AX

MOV AX,ES:[63H]
ADD AX,6
MOV CX,0B000H
CMP AX,3BAH
JZ STORE_SEG
ADD CX,800H
STORE_SEG: MOV STATUS_REG,AX
MOV VIDEO_SEG,CX

MOV SI,OFFSET MONO_ATTR
MOV AL,ES:CRT_MODE ;Retrieve current video mode.
CMP AL,7 ;Is it mono mode?
JZ GET_ROWS ;If yes, continue.
CMP AL,2 ;Is it BW80?
JZ GET_ROWS ;If yes, continue.
MOV SI,OFFSET COLOR_ATTR
CMP AL,3 ;Is it mode CO80?
JZ GET_ROWS ;If yes, continue.
MOV AX,3 ;Else, change video mode to CO80.
INT 10H

GET_ROWS: XOR BH,BH
MOV DL,24
MOV AX,1130H
INT 10H
MOV ROWS,DL ;Store rows.
SUB DL,MENU_LINES - 1
XOR DH,DH
MOV LISTING_LEN,DX

POP ES
MOV DI,OFFSET COLOR
MOV CX,SIZE COLOR_ATTRIBS
REP MOVSB

;----------------------------------------------;

DISPLAY_SETUP: CALL CLS ;Clear screen.

CMP BORDER_FLAG,1
JZ DO_COPYRIGHT
MOV BL,COLOR.W ;Turn on border.
AND BL,7
XOR BH,BH
MOV AH,0BH
INT 10H

DO_COPYRIGHT: XOR AX,AX
CALL CALC_ADDR
INC DI
INC DI
MOV SI,OFFSET COPYRIGHT ;Point to copyright message.
MOV BH,COLOR.B ;Use header attribute.
CALL WRITE_STRING ;And display it.
RET

;----------------------------------------------;
; INPUT: SI-> menu.

DISPLAY_MENU: CALL MENU_OFFSET
MOV BH,COLOR.B
CALL WRITE_STRING
RET

;*************LINE EDITOR**********************;

LINE_START DW ?
LINE_END DW ?
SCREEN_START DW ?

;INPUT: BH=color; LINE_START=buffer position; LINE_END; SCREEN_START.
;OUTPUT: CY=1 if Esc pressed.

EDITOR: MOV DI,LINE_START
CALL EDIT_CURSOR
CALL DISP_EDIT
EDITOR2: CALL CK_KEY
JZ EDITOR2
OR AL,AL
JZ NEXT_EDIT
CMP AL,CR
JZ NEXT_EDIT
CMP AH,ESC_SCAN
JZ NEXT_EDIT

MOV CX,LINE_END
SUB CX,LINE_START
PUSH DI
MOV AL,SPACE
REP STOSB
POP DI

NEXT_EDIT: CALL EDIT_CURSOR
CALL DISP_EDIT
CALL GETKEY
XCHG AL,AH
NEXT_EDIT2: JNC DO_EDIT
JMP EDITOR_END

DO_EDIT: MOV DX,LINE_START
MOV CX,LINE_END

CMP AH,ENTER_SCAN
JNZ CK_RIGHT
JMP EDITOR_DONE

CK_RIGHT: CMP AX,RIGHT_SCAN SHL 8
JNZ CK_LEFT
CMP DI,CX
JZ NEXT_EDIT
INC DI

CK_LEFT: CMP AX,LEFT_SCAN SHL 8
JNZ CK_BS
CMP DI,DX
JZ NEXT_EDIT
DEC DI
JMP NEXT_EDIT

CK_BS: CMP AH,BS_SCAN
JNZ CK_DEL
CMP DI,DX
JZ NEXT_EDIT
DEC DI
CALL CK_INSERT
JNZ DO_DEL1
MOV BYTE PTR [DI],SPACE
JMP NEXT_EDIT

CK_DEL: CMP AX,DEL_SCAN SHL 8
JNZ CK_HOME
DO_DEL1: CALL DO_DEL
JMP NEXT_EDIT

CK_HOME: CMP AX,HOME_SCAN SHL 8
JNZ CK_END2
MOV DI,DX

CK_END2: CMP AX,END_SCAN SHL 8
JNZ CK_ASCII
MOV DI,CX
NEXT_CK_END2: CMP DI,DX
JZ NEXT_EDIT
CMP BYTE PTR [DI-1],SPACE
JNZ NEXT_EDIT
DEC DI
JMP NEXT_CK_END2

CK_ASCII: CALL CK_LEGAL
JC NEXT_EDIT
CMP DI,CX
JAE NEXT_EDIT
CALL CK_INSERT
JNZ DO_INSERT
STOSB
JMP NEXT_EDIT

DO_INSERT: MOV DX,DI
DEC CX
MOV DI,CX
SUB CX,DX
JZ DO_INSERT2
MOV SI,DI
DEC SI
STD
REP MOVSB
CLD
DO_INSERT2: MOV DI,DX
STOSB
JMP NEXT_EDIT

EDITOR_DONE: CLC
EDITOR_END: PUSHF
CALL HIDE_CURSOR
POPF
RET

;--------------;
LEGAL_CHARS DW ?

NUMBERS DB "1234567890abcdefhABCDEFH"
NUMBERS_LEN EQU $ - NUMBERS

CK_LEGAL: CMP AL,SPACE
JB ILLEGAL
CMP AL,127
JA ILLEGAL
JMP [LEGAL_CHARS]
NUMBER_CHARS: PUSH DI
PUSH CX
MOV DI,OFFSET NUMBERS
MOV CX,NUMBERS_LEN
REPNZ SCASB
POP CX
POP DI
JNZ ILLEGAL

ALL_CHARS: CLC
RET

ILLEGAL: STC
RET

;--------------------------
DO_DEL: PUSH DI
MOV SI,DI
INC SI
DEC CX
SUB CX,DI
JS DEL_END
REP MOVSB
MOV BYTE PTR [DI],SPACE
DEL_END: POP DI
RET

;--------------------------

CK_INSERT: PUSH DS
MOV DX,40H
MOV DS,DX
MOV DL,DS:[17H]
TEST DL,80H ;Insert
POP DS
RET

;--------------------------
EDIT_CURSOR: MOV AX,SCREEN_START
XOR DX,DX
DIV CRT_WIDTH
SHR DL,1
MOV DH,AL
MOV AX,DI
SUB AX,LINE_START
ADD DL,AL
CALL SET_CURSOR
RET

;--------------------------
DISP_EDIT: PUSH DI
MOV DI,SCREEN_START
MOV SI,LINE_START
CALL WRITE_STRING
POP DI
RET

;----------------------------------------------;
;INPUT: CX=char count; AX=character
REPEAT_CHAR: PUSH AX
CALL WRITE_SCREEN
POP AX
LOOP REPEAT_CHAR
RET

;----------------------------------------------;
; INPUT: AX = Starting line; OUTPUT: DI = Video address.

CALC_ADDR: MUL CRT_WIDTH
ADD AX,CRT_START
MOV DI,AX
RET

;----------------------------------------------;
; INPUT: AL = character to write; BH = attribute.

WRITE_SCREEN: PUSH ES
MOV ES,CS:VIDEO_SEG ;Point to screen segment.
MOV DX,CS:STATUS_REG ;Retrieve status register.
MOV BL,AL ;Store character in BL.

HORZ_RET: IN AL,DX ;Get status.
RCR AL,1 ;Is it low?
JC HORZ_RET ;If not, wait until it is.
CLI ;No more interrupts.

HWAIT: IN AL,DX ;Get status.
RCR AL,1 ;Is it high?
JNC HWAIT ;If no, wait until it is.

MOV AX,BX ;Retrieve character; now it's OK
STOSW ; to write to screen buffer.
STI ;Interrupts back on.
POP ES
RET ;Return

;----------------------------------------------;
; INPUT: AL=character to write; BH=attribute.

WRITE_CHAR: PUSH ES
MOV DX,CS:STATUS_REG
MOV ES,CS:VIDEO_SEG
MOV BL,AL ;Store character in BL.

HORZ_RET2: IN AL,DX ;Get status.
RCR AL,1 ;Is it low?
JC HORZ_RET2 ;If not, wait until it is.
CLI ;No more interrupts.

HWAIT2: IN AL,DX ;Get status.
RCR AL,1 ;Is it high?
JNC HWAIT2 ;If no, wait until it is.

MOV AX,BX ;Retrieve character; now it's OK
STOSW ; to write to screen buffer.
STI ;Interrupts back on.
POP ES
RET ;Return

;----------------------------------------------;
; INPUT: SI -> to string to display; DI -> where to display it.
; Entry point is WRITE_STRING.

WRITE_IT: CALL WRITE_SCREEN ;Write a character.
WRITE_STRING: LODSB ;Retrieve a character.
CMP AL,CR ;Keep writing until a carriage
JA WRITE_IT ; return or zero encountered.
RET

;----------------------------------------------;
CLEAR_MENU: CALL MENU_OFFSET ;Calculate menu screen offset.
PUSH DI
MOV BH,COLOR.B ;Menu attribute.
MOV CX,CRT_WIDTH ;Blank out the two lines of menu.
SHR CX,1
NEXT_MENU: MOV AL,SPACE
CALL WRITE_SCREEN
LOOP NEXT_MENU
POP DI
RET

;----------------------------------------;
; OUTPUT: DI -> Screen offset for menu. ;
;----------------------------------------;
MENU_OFFSET: MOV AL,ROWS
XOR AH,AH
CALL CALC_ADDR
RET

;----------------------------------------------;
CLS: MOV BH,COLOR.B ;Normal attribute.
CLS2: XOR CX,CX ;Top left corner.
MOV DL,BYTE PTR COLUMNS
DEC DL
MOV DH,ROWS
MOV AX,600H ;Scroll active page.
PUSH BP
INT 10H
POP BP
RET

;----------------------------------------------;
CLOSE_SCREEN: PUSH AX
MOV BH,SCREEN_COLOR
CALL CLS2

CMP BORDER_FLAG,1
JZ PLACE_CURSOR
XOR BX,BX
MOV AH,0BH
INT 10H

PLACE_CURSOR: XOR DX,DX
CALL SET_CURSOR
POP AX
RET

;----------------------------------------------;
BEEP: MOV BX,NOTE ;Tone frequency divisor.
MOV DX,12H
XOR AX,AX
DIV BX
MOV BX,AX ;8253 countdown.

CALL DELAY ;Wait till clock rolls over.

MOV AL,0B6H ;Channel 2 speaker functions.
OUT 43H,AL ;8253 Mode Control.
JMP $+2 ;IO delay.
MOV AX,BX ;Retrieve countdown.
OUT 42H,AL ;Channel 2 LSB.
JMP $+2
MOV AL,AH ;Channel 2 MSB.
OUT 42H,AL
IN AL,61H ;Port B.
OR AL,3 ;Turn on speaker.
JMP $+2
OUT 61H,AL

CALL DELAY ;Delay one second.
IN AL,61H ;Get Port B again.
AND AL,NOT 3 ;Turn speaker off.
JMP $+2
OUT 61H,AL
RET ;Done.

;-----------------------------;
DELAY: PUSH DS ;Preserve data segment.
MOV AX,40H ;Point to BIOS data segment.
MOV DS,AX
MOV AX,DS:[6CH] ;Retrieve timer low.
NEXT_BEEP: MOV DX,DS:[6CH] ;Retrieve timer low.
CMP DX,AX ;Have we timed out?
JZ NEXT_BEEP ;If not, wait until second up.
POP DS ;Restore data segment.
RET

;----------------------------------------------;
; INPUT: SI-> DIR storage.

GET_DIR: MOV BYTE PTR [SI],"\" ;DOS doesn't preface directory
INC SI ; with slash so we must.
XOR DL,DL
MOV AH,47H ;Get current directory.
INT 21H
RET

;----------------------------------------------;
HIDE_CURSOR: MOV DH,ROWS ;Retrieve CRT rows.
INC DH ;Move one line below off screen.
XOR DL,DL ;Column zero.

SET_CURSOR: PUSH BX
XOR BH,BH ;Page zero.
MOV AH,2 ;Set cursor position.
INT 10H
POP BX
RET

;----------------------------------------------;
RESTORE_DIR: MOV DX,OFFSET CURRENT_DIR
CALL CHANGE_DIR
MOV DL,DEFAULT_DRIVE
CALL SELECT_DISK
RET

;----------------------------------------------;
SELECT_DISK: MOV AH,0EH
INT 21H
RET

;----------------------------------------------;
CHANGE_DIR: MOV AH,3BH ;Change current directory.
INT 21H
RET

;----------------------------------------------;
SET_ZIF: CALL RESTORE_DIR
MOV DL,ZIF_DRIVE
CALL SELECT_DISK
MOV SI,OFFSET CURRENT_DIR
CALL GET_DIR
MOV DX,OFFSET ZIF_DIR
CALL CHANGE_DIR
RET

;----------------------------------------------;
SET_PGM: CALL RESTORE_DIR
MOV DL,PGM_DRIVE
CALL SELECT_DISK
MOV SI,OFFSET CURRENT_DIR
CALL GET_DIR
MOV DX,OFFSET PGM_DIR
CALL CHANGE_DIR
RET

;----------------------------------------------;
; INPUT: AL=Scan code; AH=Char; DI -> Valid scan codes table; CX = Table length
; OUTPUT: AL=Scan code; AH=Char.

DISPATCH: PUSH AX
MOV BX,CX
MOV DX,DI
ADD DX,CX
REPNZ SCASB
JNZ DISPATCH_END

GOT_DISPATCH: SUB BX,CX
DEC BX
SHL BX,1
ADD BX,DX
CALL GET_PARAMS
CALL [BX] ;Process the command.
DISPATCH_END: POP AX
RET

;----------------------------------------------;

GET_PARAMS: MOV AX,SIZE FILE_RECORD
MUL LISTING_LEN
MOV BP,BAR_ADDR
MOV DX,LISTING_ADDR
MOV CX,LAST_ADDR
PARAMS_END: RET

;----------------------------------------------;

GETKEY: TEST KBD_STATUS,80H
JNZ KEY_CLEAR
CALL CK_KEY
JNZ GETKEY2
JMP GETKEY

KEY_CLEAR: CALL CK_KEY
JNZ GETKEY2
MOV KBD_STATUS,0
CMP FILE_CURRENT,TRUE
JZ GETKEY
CALL DISP_FILE
JMP GETKEY

GETKEY2: XOR AH,AH ;Wait for next keyboard input.
INT 16H
XCHG AH,AL
CMP AL,ESC_SCAN
STC
JZ GETKEY_END
CLC
GETKEY_END: RET

CK_KEY: MOV AH,1 ;Is there a keystroke available.
INT 16H
RET

CLEAR_IT: XOR AH,AH
INT 16H ;Read keystrokes until buffer
CLEAR_KEY: CALL CK_KEY ; empty.
JNZ CLEAR_IT
RET

;----------------------------------------------;

WRITE_TTY: MOV AH,0EH
INT 10H
RET

;----------------------------------------------;
PRINT_STRING: MOV AH,9 ;Print string via DOS.
INT 21H
RET

;----------------------------------------------;
OLD9 DW ?,?

INSTALL_9: PUSH ES
MOV AX,3509H ;INT 9
INT 21H
MOV OLD9[0],BX
MOV OLD9[2],ES
MOV DX,OFFSET INT_9 ;Install new interrupt.
MOV AX,2509H
INT 21H
POP ES
RET

;----------------------------------------------;
UNINSTALL_9: PUSH DS
MOV DX,OLD9[0] ;Restore old INT 9.
MOV DS,OLD9[2]
MOV AX,2509H
INT 21H
POP DS
RET

;----------------------------------------------;
INSTALL_24: MOV DX,OFFSET INT_24 ;Install new interrupt.
MOV AX,2524H
INT 21H
RET

EVEN
STACK_POINTER = $ + 256


_TEXT ENDS
END START


  3 Responses to “Category : Files from Magazines
Archive   : VOL10N19.ZIP
Filename : PATCH.ASM

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/