;----------------------------------------------------------------------
;  Fontedit - Loads the current screen font and lets you modify it.
;  Saves font in a COM file with integral loader. Requires EGA or VGA.
;  Syntax:  FONTEDIT [filespec]
;  PC Magazine September 13, 1988
;----------------------------------------------------------------------
_TEXT          SEGMENT PUBLIC 'CODE'
               ASSUME  CS:_TEXT,DS:_TEXT
               ASSUME  ES:_TEXT,SS:_TEXT

               ORG     100H
START:         JMP     MAIN

;              DATA AREA
;              ---------
               DB      CR,SPACE,SPACE,SPACE,CR,LF

COPYRIGHT      DB      "FONTEDIT 1.0 (C) 1988 Ziff Communications Co. ",BOX
PROGRAMMER1    DB      " Akira Electronic Magazine ",BOX," MuadDib & Supreme Being",0,CTRL_Z

CR             EQU     13
LF             EQU     10
CTRL_Z         EQU     26
SPACE          EQU     32
BOX            EQU     254

ESC_SCAN       EQU     1
ENTER_SCAN     EQU     1CH
UP_ARROW       EQU     48H
DN_ARROW       EQU     50H
LEFT_ARROW     EQU     4BH
RIGHT_ARROW    EQU     4DH
Y_SCAN         EQU     15H
BS_SCAN        EQU     0EH
TAB_CHAR       EQU     9

MAX_POINTS     EQU     16
PIXEL_OFF      EQU     177
PIXEL_ON       EQU     219
EDIT_COL       EQU     32
EDIT_ROW       EQU     8
EDIT_TOP       EQU     EDIT_ROW SHL 8 + EDIT_COL
TEMPLATE_TOP   EQU     EDIT_TOP - 28
CHAR_TOP       EQU     EDIT_TOP + 28
BOX_TOP        EQU     EDIT_TOP + 1 - 400H
INTENSITY      EQU     1000B
MICKEY         EQU     20

BUTTONS        LABEL   WORD
LEFT_BUTTON    DB      0
RIGHT_BUTTON   DB      0
SHIFT_KEYS     EQU     3
SHIFT_STATE    DB      ?

HORIZONTAL     DW      0
VERTICAL       DW      0

NORMAL         EQU     07H
ATTRIBUTE      DB      07H
INVERSE        DB      70H
ROWS           DB      ?
MAX_LINES      DW      16
MOUSE_FLAG     DB      0
MODIFY_FLAG    DB      0
BLANKS         DB      0,32,255
FILE_FLAG      DB      0
FILE_HANDLE    DW      ?
FILENAME       DW      ?
LAST_PIXEL     DB      ?

;format: reg,value    SEQUENCER REGISTERS     GRAPHICS CONTROLLER REGISTERS
;                     MAP MASK   MEMORY MODE  MODE REG    MISC  READ MAP SELECT
ACCESS_A000H   DB     2,4,        4,7,        5,0,        6,4,        4,2
PROTECT_A000H  DB     2,3,        4,3,        5,10H,      6,0AH,      4,0

MENU           LABEL   BYTE
DB "F1 Del row    F2 Ins row    F3 Dup row    F4 Save $"
MENU1          LABEL   BYTE
DB "F5 Copy template char.    Tab = select edit/char box"
DB "   Use: arrow keys or mouse",CR,LF
DB "Hold Shift key or mouse button to drag.    Esc to exit"
DB "     Rows displayed = ",CR,LF
DB "Left button  = pixel on",CR,LF
DB "Right button = pixel off",CR,LF
DB "Space bar = toggle pixel$"
MENU2          LABEL   BYTE
DB "Enter = select char.",0
DB "Button = select char.",0
DB "PgUp/PgDn prev./next char.",0

CAPTIONS       DW      TEMPLATE_TOP - 2
               DB      "Template Char",0
               DW      EDIT_TOP - 2
               DB      "  Edit Char",0
               DW      CHAR_TOP - 2
               DB      "Character Set",0

CURRENT_BOX    DB      218, 5 DUP (196),   194, 5 DUP (196),   191
               DB      179, 5 DUP (SPACE), 179, 5 DUP (SPACE), 179
               DB      192, 5 DUP (196),   193, 5 DUP (196),   217

NOT_ENOUGH     DB      "Not enough memory$"
NOT_SUPPORTED  DB      "Font too tall$"
NOT_EGA_VGA    DB      "Ega/Vga not found$"
SAVE_MSG       DB      CR,LF,"Save ",0
FILE_MSG       DB      "file",0
CREATE_MSG     DB      CR,LF,"Create ",0
EXIST_MSG      DB      CR,LF,"Write over existing ",0
YES_NO         DB      "?  Y/N",0
FAILED_MSG     DB      CR,LF,"Failed$"
FILENAME_MSG   DB      CR,LF,"Enter filename",CR,LF,"$"
NOT_FONT_MSG   DB      " not font$"
COM            DB      ".COM",0
WARNING_MSG    DB      LF,"Warning!  The cursor row will be deleted in"
               DB      " EVERY character in this font.",CR,LF,"Continue$"

DISPATCH_KEY   DB      1,        4BH,      4DH,    48H,     50H,     49H
               DB      51H,      0FH,      39H,    1CH,     3BH,     53H
               DB      3CH,      52H,      3DH,    3EH,     3FH
DISPATCH_CNT   EQU     $ - DISPATCH_KEY

DISPATCH_TABLE DW      EXIT,    LEFT,    RIGHT,     UP,    DOWN,    PGUP
               DW      PGDN,    TAB,     SPACE_BAR, ENTER, DEL_ROW, DEL_ROW
               DW      INS_ROW, INS_ROW, DUP_ROW,   SAVE,  COPY_TEMP
DISPATCH_END   EQU     $ - 2

;              CODE AREA
;              ---------
MAIN           PROC    NEAR

               CLD                             ;All string operations forward.

               MOV     BX,1024                 ;Allocate 1024 paragraphs; 16K.
               MOV     AH,4AH
               INT     21H
               MOV     DX,OFFSET NOT_ENOUGH    ;Exit with message if not enough.
               JC      ERROR_MSG

               MOV     AX,500H                 ;Make sure zero video page.
               INT     10H

               MOV     AX,40H                  ;Point to BIOS data area.
               MOV     ES,AX

               MOV     AX,1A00H                ;Get display info.
               INT     10H
               CMP     AL,1AH                  ;Function supported?
               JNZ     CK_EGA                  ;If no, not VGA; check EGA.
               CMP     BL,7                    ;Else, monochrome VGA?
               JZ      GET_CRT_MODE            ;If yes, OK.
               CMP     BL,8                    ;Else, color VGA?
               JZ      GET_CRT_MODE            ;If yes, OK.

CK_EGA:        MOV     MAX_LINES,14            ;Else, use 14 max lines for EGA.
               MOV     BL,10H                  ;Get EGA information.
               MOV     AH,12H
               INT     10H
               MOV     DX,OFFSET NOT_EGA_VGA
               CMP     BL,10H                  ;Is there an EGA?
               JZ      ERROR_MSG               ;If no, exit with message.
               TEST    ES:BYTE PTR [87H],8     ;Is EGA active?
               JNZ     ERROR_MSG               ;If no, exit with message.

GET_CRT_MODE:  MOV     BL,ES:[49H]             ;Retrieve CRT_MODE.
               CALL    INFORMATION             ;Get font information.
               MOV     DX,OFFSET NOT_SUPPORTED
               CMP     CX,MAX_POINTS           ;Font greater than 16 points?
               JBE     CK_MODE                 ;If no, OK.

ERROR_MSG:     CALL    PRINT_STRING            ;Print error message.
ERROR_EXIT:    MOV     AL,1                    ;ERRORLEVEL = 1
               JMP     TERMINATE               ;Exit.

CK_MODE:       CMP     BL,7                            ;CRT_MODE mono?
               JZ      SAVE_MODE                       ;If yes, skip.
               MOV     BYTE PTR PROTECT_A000H + 7,0EH  ;Else, change parameter.
               CMP     BL,2                            ;Is mode BW80?
               JZ      SAVE_MODE               ;If yes, defaults.
               MOV     ATTRIBUTE,17H           ;Else, use color attributes.       
               MOV     INVERSE,71H
               CMP     BL,3                    ;Are we in CO80?
               JZ      SAVE_MODE               ;If yes, done here.
               MOV     AX,3                    ;Else, change to CO80.
               INT     10H
               MOV     BL,3

SAVE_MODE:     MOV     CRT_MODE,BL             ;Save CRT_MODE in loader.
               CALL    SETUP                   ;Setup the display.

;************************** MAIN LOOP **************************;
; User input dispatcher.  AH = ASCII character; AL = Scan Code. ;
;***************************************************************;

INPUT:         CALL    HIDE_CURSOR             ;Park cursor off screen.
               CALL    GET_INPUT               ;Get some input from user.
               CMP     BUTTONS,0               ;Was a mouse button pressed?
               JZ      CK_ASCII                ;If no, check keyboard input.
               CALL    BUTTON_PRESS            ;Else, process button press.
               JMP     SHORT INPUT             ;Next input.

CK_ASCII:      OR      AL,AL                   ;Scan code zero?
               JZ      ALT_INPUT               ;If yes, ALT keypad entry.
               MOV     DI,OFFSET DISPATCH_KEY  ;Else, check dispatch table.
               MOV     CX,DISPATCH_CNT
               REPNZ   SCASB
               JNZ     ALT_INPUT               ;If no match, keyboard char.
               SHL     CX,1                    ;Else, look up subroutine
               MOV     DI,OFFSET DISPATCH_END
               SUB     DI,CX
               CALL    [DI]                    ; and process command.
               JMP     SHORT INPUT             ;Next input.

ALT_INPUT:     OR      AH,AH                   ;ASCII zero?
               JZ      INPUT                   ;If yes, skip.
               MOV     EDIT_CHAR,AH            ;Else, store as new character.
               CALL    SETUP_END               ;Display new edit character.
               JMP     SHORT INPUT             ;Next input.

;---------------------------------------------------;
; Exit.  If font was modified, prompt user to save. ;
;---------------------------------------------------;

EXIT:          CALL    CLS                     ;Clear the screen.
               CMP     MODIFY_FLAG,1           ;Font modified?
               JNZ     GOOD_EXIT               ;If no, return to DOS.
               MOV     SI,OFFSET FILE_MSG
               CMP     FILE_FLAG,1             ;If there a filename?
               JZ      DO_FILE                 ;If yes, display it.
               MOV     FILENAME,SI             ;Else, display "file".
DO_FILE:       MOV     SI,OFFSET SAVE_MSG
               CALL    PROMPT                  ;Prompt user to save.
               JNZ     GOOD_EXIT               ;If "Y"es not pressed, exit.
               CMP     FILE_FLAG,1             ;Else, is there a filename?
               JZ      DO_SAVE                 ;If yes, save it.
               CALL    GET_NAME                ;Else, get a filename.
               JC      GOOD_EXIT               ;If aborted, exit.

DO_SAVE:       CALL    SAVE_FILE               ;Save the font COM file.

GOOD_EXIT:     CALL    CLS                     ;Clear the screen.
               XOR     AL,AL                   ;ERRORLEVEL zero.
TERMINATE:     MOV     AH,4CH                  ;Return to DOS.
               INT     21H

MAIN           ENDP

;            ***************
;            * SUBROUTINES *
;            ***************

;-------------------------------------------------------------;
; What follows is the user input command processing routines. ;
;-------------------------------------------------------------;

BUTTON_PRESS:  CALL    GET_CURSOR              ;Is cursor in character box?
               JNZ     DO_ENTER                ;If yes, process as if Enter.
               CMP     LEFT_BUTTON,0           ;Else, left button press
               JZ      TURN_OFF                ; will turn on pixel.
               CALL    GET_PIXEL               ;Get the pixel.
               OR      [DI],AH                 ;Turn it on.
               JMP     SHORT BUTTON_END

TURN_OFF:      CALL    GET_PIXEL               ;Right button will turn off pixel
               XOR     AH,0FFH                 ;Invert bit mask.
               AND     [DI],AH                 ;Turn it off.

BUTTON_END:    CALL    UPDATE_CURSOR           ;Update the cursor.
               CALL    LOAD_CHAR               ;Load the character.
               RET

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

ENTER:         CALL    GET_CURSOR              ;Is cursor in character box?
               JZ      ENTER_END               ;If no, ignore.
DO_ENTER:      CALL    GET_CHAR                ;Else, get the highlighted char.
               MOV     EDIT_CHAR,AL            ;Store it as new edit char.
               CALL    NEW_CHAR                ;Display the edit character.
ENTER_END:     RET

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

LEFT:          MOV     BP,0FFH                 ;Movement = 0 rows; -1 cols.
               JMP     SHORT ARROWS

RIGHT:         MOV     BP,1                    ;Movement = 0 rows; +1 cols.
               JMP     SHORT ARROWS

UP:            MOV     BP,0FF00H               ;Movement = -1 rows; 0 cols.
               JMP     SHORT ARROWS

DOWN:          MOV     BP,100H                 ;Movement = +1 rows; 0 cols.

ARROWS:        CALL    RESTORE                 ;Restore current cursor position.
               CALL    GET_CURSOR              ;Cursor in edit box?
               MOV     CX,BP
               JNZ     CHAR_ARROW              ;If no, do character movement.
               ADD     CL,CL                   ;Else, double up col. movement.
               CALL    CK_BOUNDS               ;Move cursor; check the boundary.
               SUB     AX,EDIT_TOP             ;AX has position; make relative.
               MOV     EDIT_CURSOR,AX          ;Store as new edit cursor.
               MOV     BH,LAST_PIXEL           ;Retrieve the last pixel.
               CALL    GET_PIXEL               ;Get pixel in new position.
               CMP     SHIFT_STATE,0           ;Button or Shift key depressed?
               JZ      UPDATE_PIXEL2           ;If no, update new cursor pos.

               OR      BH,BH                   ;Else, was last pixel on?
               JNZ     BIT_ON                  ;If yes, drag to new position.
               XOR     AH,0FFH                 ;Else, invert mask
               AND     BYTE PTR [DI],AH;       ; and drag pixel off.
               JMP     SHORT UPDATE_PIXEL1

BIT_ON:        OR      BYTE PTR [DI],AH        ;Turn the pixel on.

UPDATE_PIXEL1: CALL    LOAD_CHAR               ;Load the character.

UPDATE_PIXEL2: CALL    UPDATE_CURSOR           ;Update the cursor display.
               RET

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

CHAR_ARROW:    CALL    CK_BOUNDS               ;Move cursor; check the boundary.
               SUB     AX,CHAR_TOP             ;Convert to relative position.
               MOV     CHAR_CURSOR,AX          ;Store new character box pos.
               CMP     SHIFT_STATE,0           ;Button or Shift key depressed?
               JZ      CHAR_END                ;If no, done here.
NEW_CHAR:      CALL    GET_CHAR                ;Else, get the character
               MOV     EDIT_CHAR,AL            ; and use as new edit character.
               CALL    DISPLAY_FONT            ;Display it.

CHAR_END:      CALL    UPDATE_CURSOR           ;Update the cursor display.
               RET

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

PGUP:          DEC     EDIT_CHAR               ;Next lower edit character.
               JMP     SHORT PAGE_END

PGDN:          INC     EDIT_CHAR               ;Next higher edit character.

PAGE_END:      CALL    SETUP_END               ;Display it.
               RET

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

TAB:           CALL    RESTORE                 ;Restore current cursor position.
               XOR     EDIT_FLAG,1             ;Toggle Edit/char active box.
               CALL    UPDATE_CURSOR           ;Display cursor in new box.
               RET

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

SPACE_BAR:     CALL    GET_CURSOR              ;Is cursor in character box?
               JNZ     SPACE_END               ;If yes, ignore.
               CALL    GET_PIXEL               ;Else, get the pixel.
               XOR     [DI],AH                 ;Toggle the pixel.
               CALL    UPDATE_PIXEL1           ;Update character and cursor.
SPACE_END:     RET

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

DEL_ROW:       CALL    GET_CURSOR              ;Is cursor in character box?
               JNZ     DELETE_RETURN           ;If yes, ignore.
               MOV     BP,POINTS               ;Else, retrieve scan line points.
               CMP     BP,1                    ;Is there only one scan line?
               JZ      DELETE_RETURN           ;If yes, ignore.
               MOV     AL,AH                   ;Else, delete position equals
               XOR     AH,AH                   ; POINTS - relative ROW.
               SUB     BP,AX

               CALL    CLEAR_MENU              ;Clear part of the menu and
               MOV     DX,OFFSET WARNING_MSG   ; display warning message.
               CALL    PRINT_STRING
               CALL    QUERY                   ;Should we delete?
               JNZ     DELETE_END              ;If no, done here.

               MOV     BX,POINTS               ;Else, retrieve bytes/char.
               MOV     SI,OFFSET EDIT_FONT     ;Delete edit font.
               CALL    DELETE
               MOV     SI,OFFSET TEMPLATE_FONT ;Do same to template font.
               CALL    DELETE

               DEC     BX                             ;One less byte/char.
               MOV     BH,BL
               CMP     BYTE PTR EDIT_CURSOR + 1,BL    ;Was last row deleted?
               JNZ     LOAD_IT                        ;If no, OK.
               DEC     BL                             ;Else, move cursor up one
               MOV     BYTE PTR EDIT_CURSOR + 1,BL    ; row so it's on new char.

LOAD_IT:       MOV     BP,OFFSET EDIT_FONT
               CALL    USER_LOAD               ;Load the new font.
               CALL    INFORMATION             ;Get font information.
               MOV     MODIFY_FLAG,1           ;Note that font's been modified.
               CALL    CLS                     ;Clear the old display.
DELETE_END:    XOR     DX,DX
               CALL    SET_CURSOR
               CALL    DISPLAY_COPY            ;Display new font.
DELETE_RETURN: RET

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

DELETE:        MOV     DI,SI                   ;Destination starts at source.
               MOV     CX,256                  ;256 characters to do.
NEXT_DELETE:   PUSH    CX                      ;Save character count.
               MOV     CX,BX                   ;BX has bytes/character.
CK_SKIP:       CMP     CX,BP                   ;Is this the row to delete?
               JZ      SKIP_ROW                ;If yes, skip it.
               MOVSB                           ;Else, move it down.
               JMP     SHORT LOOP_DELETE
SKIP_ROW:      INC     SI                      ;Skip deletion row.
LOOP_DELETE:   LOOP    CK_SKIP                 ;Do all character rows.
               POP     CX
               LOOP    NEXT_DELETE             ;Do all 256 characters.
               RET

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

INS_ROW:       XOR     BL,BL                   ;Insert a zero byte.
               JMP     SHORT INS_EDIT

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

DUP_ROW:       MOV     BL,-1                   ;Insert a duplicate byte.
INS_EDIT:      CALL    GET_CURSOR              ;Is cursor in character box?
               JNZ     INSERT_END              ;If yes, ignore.
               MOV     BH,AH                   ;Row to be inserted.
               INC     BH                      ;Adjust.
               MOV     BP,POINTS               ;Retrieve bytes/char.
               CMP     BP,MAX_LINES            ;Character maxed out?
               JZ      INSERT_END              ;If yes, done here.
               STD                             ;Else, backward moves.
               MOV     SI,OFFSET EDIT_FONT     ;Insert a row.
               CALL    INSERT
               MOV     SI,OFFSET TEMPLATE_FONT ;Do same to template font.
               CALL    INSERT

               CLD                             ;String operation back forward.
               MOV     BX,BP                   ;Increment bytes/character.
               MOV     BH,BL
               INC     BH
               MOV     BP,OFFSET EDIT_FONT     ;Load the new font.
               CALL    USER_LOAD
               CALL    INFORMATION             ;Get font information.
               MOV     MODIFY_FLAG,1           ;Note that font's been modified.
               CALL    SETUP_END               ;Display new font.
INSERT_END:    RET

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

INSERT:        MOV     AX,BP                   ;Go to end of font
               MOV     CX,256                  ; (256 * points) - 1
               MUL     CX
               DEC     AX
               ADD     SI,AX
               MOV     DI,SI
               ADD     DI,CX                   ;New font = old font + 256.

NEXT_INSERT:   PUSH    CX                      ;Save character count.
               MOV     CX,BP                   ;Retrieve bytes/char.
MOVE_BYTE:     MOVSB                           ;Move a byte.
               CMP     CL,BH                   ;Is there an insert row?
               JNZ     LOOP_INSERT
               MOV     AL,BL                   ;If yes, assume zero insert.
               OR      BL,BL                   ;Is zero to be inserted?
               JZ      INSERT_IT               ;If yes, guessed right.
               MOV     AL,[SI+1]               ;Else, duplicate with byte below.
INSERT_IT:     STOSB                           ;Insert it.
LOOP_INSERT:   LOOP    MOVE_BYTE               ;Do all bytes/char.
               POP     CX
               LOOP    NEXT_INSERT             ;Do all 256 characters.
               RET

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

COPY_TEMP:     CALL    GET_PIXEL               ;Get index to current char.
               MOV     DI,SI                   ;Destination = Source+(16 * 256).
               ADD     SI,MAX_POINTS * 256
               MOV     CX,POINTS               ;Bytes/character to copy.
               REP     MOVSB                   ;Copy them.
               CALL    SETUP_END               ;Update the display.
               CALL    LOAD_CHAR               ;Load the new character.
               RET

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

SAVE:          CMP     FILE_FLAG,1             ;Is there a filename?
               JZ      SAVE_IT                 ;If yes, save it.
               CALL    CLS                     ;Else, clear screen.
               CALL    GET_NAME                ;Get a filename.
               PUSHF                           ;Save results.
               CALL    DISPLAY_HEAD            ;Redisplay menu.
               POPF                            ;Retrieve results.
               JC      SAVE_END                ;If user aborted, skip save.
SAVE_IT:       CALL    SAVE_FILE               ;Save the file.
SAVE_END:      RET

;*********** END OF COMMAND PROCESSING ROUTINES ***********;

;---------------------------;
; OUTPUT                    ;
;   If Edit cursor, ZF = 1  ;
;   If Char cursor, ZF = 0  ;
;   DX = cursor position.   ;
;   AX = relative position. ;
;---------------------------;

GET_CURSOR:    MOV     AX,EDIT_CURSOR          ;Assume edit cursor; retrieve it.
               MOV     DX,AX                   ;Cursor position = relative
               ADD     DX,EDIT_TOP             ; position + top left of edit box
               CMP     EDIT_FLAG,1             ;Are we in edit box?
               JZ      CURSOR_END              ;If yes, guessed right.
               MOV     AX,CHAR_CURSOR          ;Else, retrieve char. cursor.
               MOV     DX,AX                   ;Calculate cursor position.
               ADD     DX,CHAR_TOP

CURSOR_END:    RET

;---------------------------------------------------;
; Return highlighted cursor position to background. ;
;---------------------------------------------------;

RESTORE:       MOV     BL,ATTRIBUTE            ;Background attribute.
               CALL    GET_CURSOR              ;Is cursor in character box?
               JNZ     CHAR_RESTORE            ;If yes, restore char box.
               CALL    GET_PIXEL               ;Else, get pixel and write.
               CALL    WRITE_PIXEL
               RET

CHAR_RESTORE:  CALL    GET_CHAR                ;Get character and write.
               CALL    WRITE_CHAR
               RET

;--------------------------------;
; Highlight new cursor position. ;
;--------------------------------;

UPDATE_CURSOR: MOV     BL,ATTRIBUTE            ;Retrieve background attribute.
               OR      BL,INTENSITY            ;Turn on intensity bit.
               CALL    GET_CURSOR              ;Is cursor in character box?
               JNZ     DO_CHAR                 ;If yes, do character cursor.

FONT_CURSOR:   CALL    GET_PIXEL               ;Else, get pixel and write it.
               CALL    WRITE_PIXEL
               RET

DO_CHAR:       CALL    GET_CHAR                ;Retrieve character.
               MOV     DI,OFFSET BLANKS        ;Use inverse video for invisible
               MOV     CX,3                    ; characters 0, 32 and 255.
               REPNZ   SCASB
               JNZ     DO_CURSOR
               MOV     BL,INVERSE

DO_CURSOR:     CALL    WRITE_CHAR              ;Update the character cursor.
               RET

;-----------------------------------;
; INPUT                             ;
;   AX = Relative cursor position.  ;
;   DX = Actual cursor position.    ;
;   CX = Direction.                 ;
;                                   ;
; OUTPUT                            ;
;   DX = New cursor position.       ;
;   AX = New cursor position.       ;
;   BX preserved.                   ;
;-----------------------------------;

CK_BOUNDS:     ADD     DH,CH                   ;Add row direction
               ADD     DL,CL                   ; and column direction.
               PUSH    BX                      ;Save BX.
               MOV     BL,16                   ;Use 16 as bounds for char.
               CMP     EDIT_FLAG,1             ; box bottom.
               JNZ     CK_LEFT                 ;Use bytes/char bounds for edit
               MOV     BX,POINTS               ; box bottom.

CK_LEFT:       ADD     AL,CL                   ;Add column to relative pos.
               JGE     CK_RIGHT
               ADD     DL,16                   ;If too far left, 
               JMP     SHORT BOUNDS_END        ; wrap to right.

CK_RIGHT:      CMP     AL,16                   ;If too far right,
               JB      CK_UP
               SUB     DL,16                   ; wrap to left.

CK_UP:         ADD     AH,CH                   ;Add row to relative position.
               JGE     CK_DOWN
               ADD     DH,BL                   ;If too far up,
               JMP     SHORT BOUNDS_END        ; wrap to bottom

CK_DOWN:       CMP     AH,BL                   ;If too far down,
               JB      BOUNDS_END              ; wrap to top.
               MOV     DH,EDIT_ROW

BOUNDS_END:    MOV     AX,DX                   ;Return copy of cursor position.
               POP     BX                      ;Restore BX.
               RET

;----------------------------------;
; INPUT                            ;
;   AX = Relative cursor position. ;
;                                  ;
; OUTPUT                           ;
;   AL = character.                ;
;----------------------------------;

GET_CHAR:      MOV     CL,4                    ;Character = row * 16 + column.
               SHL     AH,CL
               ADD     AL,AH
               RET

;-------------------;
; Font information. ;
;-------------------;

INFORMATION:   MOV     BH,2                    ;Get information.
               MOV     AX,1130H
               INT     10H
               PUSH    CS                      ;Restore extra segment.
               POP     ES
               MOV     POINTS,CX               ;Store bytes/character.
               MOV     ROWS,DL                 ;Store rows on screen.
               RET

;----------------------------------------------------------------;
; Filename is parsed of white space and COM extension tacked on. ;
;----------------------------------------------------------------;

PARSE_FILE:    MOV     SI,81H                  ;Point to parameter.
NEXT_PARSE:    LODSB                           ;Get a byte.
               CMP     AL,SPACE                ;Is it leading space?
               JZ      NEXT_PARSE              ;If yes, ignore.
               CMP     AL,TAB_CHAR             ;Is it leading tab?
               JZ      NEXT_PARSE              ;If yes, ignore.
               DEC     SI                      ;Adjust pointer.
               MOV     FILENAME,SI             ;Store start of filename.

FIND_END:      LODSB                           ;Get a byte.
               CMP     AL,SPACE                ;Is it space or below?
               JBE     PARSE_END               ;If yes, end of filename.
               CMP     AL,"a"                  ;Capitalize.
               JB      CK_DOT
               CMP     AL,"z"
               JA      CK_DOT
               AND     BYTE PTR [SI-1],5FH
CK_DOT:        CMP     AL,"."                  ;Is it a dot?
               JNZ     FIND_END                ;If no, continue.
PARSE_END:     MOV     DI,SI                   ;Else, if dot or end of filename
               DEC     DI                      ; tack on ".COM".
               MOV     SI,OFFSET COM
               MOV     CX,5
               REP     MOVSB
               RET

;-----------------------------;
; OUTPUT                      ;
;   If file exists,    CY = 0 ;
;   If file not found, CY = 1 ;
;-----------------------------;

OPEN_FILE:     MOV     DX,FILENAME
               MOV     AX,3D02H                ;Open file for reading, writing.
               INT     21H
SAVE_HANDLE:   MOV     FILE_HANDLE,AX          ;Save filehandle.
               MOV     BX,AX
               RET

;------------------------;
; OUTPUT                 ;
;   If successful CY = 0 ;
;   If failed     CY = 1 ;
;------------------------;

CREATE_FILE:   MOV     DX,FILENAME
               XOR     CX,CX                   ;Create normal file.
               MOV     AH,3CH
               INT     21H
               JC      CREATE_END
               CALL    SAVE_HANDLE             ;If successful, save filehandle
               CALL    SAVE_FILE               ; and save font file.
CREATE_END:    RET

;-----------------------------------------------------------------------; 
; Read the parsed file.  Check if legitimate font file.  Load the font. ;
;-----------------------------------------------------------------------;

READ_FILE:     MOV     BX,FILE_HANDLE          ;Retrieve filehandle.
               MOV     DX,OFFSET LOADER        ;Point to loader.
               MOV     CX,LOADER_LENGTH        ;Bytes to read.
               MOV     AH,3FH                  ;Read from disk.
               INT     21H
               MOV     SI,OFFSET PROGRAMMER1   ;Use name as legitimate font
               MOV     DI,OFFSET PROGRAMMER2   ; file signature.
               MOV     CX,SIGNATURE_LEN / 2
               REPZ    CMPSW
               JZ      READ_END
               CALL    DISP_FILENAME
               MOV     DX,OFFSET NOT_FONT_MSG  ;If not font file, exit
               JMP     ERROR_MSG               ; with message.

READ_END:      MOV     FILE_FLAG,1             ;Else, note that filename found.
               PUSH    BX                      ;Save filehandle.
               MOV     BP,OFFSET EDIT_FONT     ;Point to font.
               MOV     BH,BYTE PTR POINTS      ;Bytes/character.
               CALL    USER_LOAD               ;Load the font.
               CALL    INFORMATION             ;Get font information.
               POP     BX                      ;Retrieve filehandle.

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

CLOSE_FILE:    MOV     AH,3EH
               INT     21H
               RET

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

SAVE_FILE:     CALL    OPEN_FILE               ;Open the file and write
               MOV     DX,OFFSET LOADER        ; font image and loader to disk.
               MOV     CX,LOADER_LENGTH
               MOV     AH,40H
               INT     21H
               MOV     MODIFY_FLAG,0           ;Reset modify flag.
               MOV     FILE_FLAG,1             ;Note that have a filename.
               JMP     SHORT CLOSE_FILE

;-------------------------------; 
; INPUT                         ;
;   SI = first string to write. ;
;                               ;
; OUTPUT                        ;
;   If "Y"es pressed, ZF = 1    ;
;   Else,             ZF = 0    ;
;-------------------------------;

PROMPT:        CALL    TTY_STRING              ;Write preface string.
               CALL    DISP_FILENAME           ;Write filename.
QUERY:         MOV     SI,OFFSET YES_NO        ;Write query string.
               CALL    TTY_STRING
               CALL    GET_KEY                 ;Get a response.
               CMP     AH,Y_SCAN               ;Check if "Y" pressed.
               RET

;-------------------------------; 
; OUTPUT                        ;
;   If name valid,       CY = 0 ;
;   If invalid or abort, CY = 1 ;
;-------------------------------;

GET_NAME:      MOV     DX,OFFSET FILENAME_MSG  ;Ask for filename.
               CALL    PRINT_STRING
               MOV     DI,81H                  ;Use PSP's DTA for input.
NEXT_NAME:     CALL    GET_KEY                 ;Get a keystroke.
               CMP     AH,ESC_SCAN             ;Esc?
               STC
               JZ      NAME_END                ;If yes, abort with CY = 1.
               CMP     AH,LEFT_ARROW           ;Backspace with left arrow
               JZ      DO_BS                   ; or backspace key.
               CMP     AH,BS_SCAN
               JZ      DO_BS
               CMP     AH,ENTER_SCAN           ;If Enter key, done here.
               JZ      STORE_BYTE
               CMP     AL,SPACE                ;Ignore space and below.
               JBE     NEXT_NAME
               JMP     SHORT STORE_BYTE

DO_BS:         DEC     DI                      ;TTY Backspace = the characters
               MOV     AL,8                    ; 8, space and 8.
               PUSH    AX
               CALL    WRITE_TTY
               MOV     AL,SPACE
               CALL    WRITE_TTY
               POP     AX
               JMP     SHORT DISPLAY_BYTE

STORE_BYTE:    STOSB
               CMP     AH,ENTER_SCAN           ;Done if Enter.
               JZ      PARSE_IT
DISPLAY_BYTE:  CALL    WRITE_TTY               ;Echo input to screen.
               JMP     SHORT NEXT_NAME

PARSE_IT:      CALL    PARSE_FILE              ;Parse the filename.
               CALL    OPEN_FILE               ;See if it exists.
               JC      CREATE_IT               ;If no, create it.
               MOV     SI,OFFSET EXIST_MSG     ;Else, ask if should write
               CALL    PROMPT                  ; over existing file.
               JNZ     GET_NAME

CREATE_IT:     CALL    CREATE_FILE             ;Create the file.
               JNC     NAME_END
               MOV     DX,OFFSET FAILED_MSG    ;If failed, inform user
               CALL    PRINT_STRING            ; and ask for new filename.
               JMP     SHORT GET_NAME
NAME_END:      RET

;--------------------------------------;
; OUTPUT                               ;
;   AH = Bit mask                      ;
;   AL = PIXEL_ON or PIXEL_OFF         ;
;   SI = Pointer to start of Character ;
;   DI = Pointer to start of Scan Line ;
;   PIXEL = 0 or -1                    ;
;--------------------------------------;

GET_PIXEL:     MOV     SI,OFFSET EDIT_FONT     ;Point to start of edit font.
               CALL    CHAR_START              ;Index to current character.
               MOV     DI,SI                   ;Also into DI.
               MOV     CX,EDIT_CURSOR          ;Retrieve edit cursor.
               SHR     CL,1                    ;Two col/bit so divide by two.
               MOV     AH,10000000B            ;Bit starts in most significant.
               SHR     AH,CL                   ;Shift bit to column position.
               MOV     CL,CH                   ;Row in CL.
               XOR     CH,CH                   ;Zero in high half.
               ADD     DI,CX                   ;Add to character start.
               MOV     CL,[DI]                 ;Retrieve the current byte.
               MOV     AL,PIXEL_OFF            ;Assume it is off.
               MOV     LAST_PIXEL,0
               AND     CL,AH                   ;AND with bit mask.
               JZ      END_PIXEL               ;If off, guessed right.
               MOV     AL,PIXEL_ON             ;Else, pixel is on.
               MOV     LAST_PIXEL,-1
END_PIXEL:     RET

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

WRITE_PIXEL:   CALL    WRITE_CHAR              ;Two characters/pixel.
               INC     DL
               CALL    WRITE_CHAR
               INC     DL
               RET

;----------------------------;
; INPUT                      ;
;   SI = Font start.         ;
;                            ;
; OUTPUT                     ;
;   AX = Edit character.     ;
;   SI = Start of character. ;
;----------------------------;

CHAR_START:    MOV     CX,POINTS               ;Retrieve bytes/character.
               MOV     AL,EDIT_CHAR            ;Retrieve edit character.
               XOR     AH,AH                   ;Zero in high half.
               PUSH    AX                      ;Preserve character.
               MUL     CL                      ;Char start = bytes/char * char.
               ADD     SI,AX                   ;Add to index.
               POP     AX                      ;Retrieve character.
               RET

;--------------------------------------------;
; OUTPUT                                     ;
;   AH = ASCII character.                    ;
;   AL = Scan code.                          ;
;   BUTTONS = button pressed.                ;
;   SHIFT_STATE = Shift or button depressed. ;
;--------------------------------------------;

GET_INPUT:     XOR     BP,BP                   ;Store input in BP; start with 0.
               MOV     SHIFT_STATE,0           ;Zero in Shift state.
               MOV     BUTTONS,0               ;Zero in Buttons also.
               CMP     MOUSE_FLAG,1            ;Is the mouse active?
               JNZ     CK_KEYBOARD             ;If no, skip mouse poll.

               XOR     BX,BX                   ;Left button.
               MOV     AX,5                    ;Button press information.
               INT     33H
               OR      SHIFT_STATE,AL          ;Store button depressed info.
               OR      LEFT_BUTTON,BL          ;Store button press info.
               MOV     BX,1                    ;Do same for right button.
               MOV     AX,5
               INT     33H
               OR      RIGHT_BUTTON,BL         ;Store button pressed info.
               CMP     BUTTONS,0               ;Any button pressed?
               JNZ     INPUT_END               ;If yes, done here.

MOUSE_MOTION:  MOV     AX,0BH                  ;Read mouse motion.
               INT     33H
               ADD     CX,HORIZONTAL           ;Add in last horizontal motion.
               ADD     DX,VERTICAL             ; and last vertical motion.
               MOV     AX,MICKEY               ;Retrieve mouse unit of motion.

               MOV     SI,RIGHT_ARROW          ;Assume right movement.
               CMP     CX,AX                   ;Is horizontal > mickey?
               JG      HORIZ                   ;If yes, guessed right.
               MOV     SI,DN_ARROW             ;Assume down movement.
               CMP     DX,AX                   ;Is vertical > mickey?
               JG      VERT                    ;if yes, guessed right.

               NEG     AX                      ;Else, negate mickey.
               MOV     SI,LEFT_ARROW           ;Assume left movement.
               CMP     CX,AX                   ;Is horizontal < mickey?
               JL      HORIZ                   ;If yes, guessed right.
               MOV     SI,UP_ARROW             ;Assume up movement.
               CMP     DX,AX                   ;Is vertical < mickey?
               JGE     STORE_MOTION            ;If yes, guessed right.

VERT:          SUB     DX,AX                   ;Subtract vertical mickey.
               JMP     SHORT STORE_SCAN        ;Update vertical.

HORIZ:         SUB     CX,AX                   ;Subtract horizontal mickey.

STORE_SCAN:    MOV     BP,SI                   ;Store scan code in BP.
STORE_MOTION:  MOV     HORIZONTAL,CX           ;Update movements.
               MOV     VERTICAL,DX

CK_KEYBOARD:   MOV     AH,2                    ;Keyboard Shift state.
               INT     16H
               AND     AL,SHIFT_KEYS           ;Mask off all but Shift keys.
               OR      SHIFT_STATE,AL          ;Store Shift state.

               MOV     AH,1                    ;Keystroke status.
               INT     16H
               JZ      STORE_INPUT             ;If none available, done here.
               CALL    GET_KEY                 ;Else, get keystroke
               XCHG    AL,AH                   ;Exchange scan/ASCII code.
               JMP     SHORT INPUT_END

STORE_INPUT:   MOV     AX,BP                   ;Return input in AX
               OR      AX,AX                   ;Is there input?
               JNZ     INPUT_END               ;If yes, done here.
               CMP     BUTTONS,0               ;Is there button pressed?
               JNZ     INPUT_END               ;If yes, done here.
               JMP     GET_INPUT               ;Else, wait until input.
INPUT_END:     RET

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

DISPLAY_FONT:  MOV     AL,ROWS                 ;Retrieve rows on screen.
               INC     AL                      ;Zero based; adjust.
               MOV     DX,34EH                 ;Display at Row 2; Col. 77.
               MOV     CX,3                    ;Three bytes to write.
               MOV     BL,ATTRIBUTE            ;Use background attribute.
               CALL    DIVIDE                  ;Display the number.

               MOV     DX,BOX_TOP + 103H       ;Point to inside of info box.
               MOV     AL,EDIT_CHAR            ;Retrieve character.
               CALL    WRITE_CHAR              ;Display it.

               ADD     DL,7                    ;Move to end of number col.
               MOV     CX,3                    ;Three bytes to write.
               CALL    DIVIDE                  ;Display the number.

               MOV     BP,TEMPLATE_TOP         ;Display template character.
               MOV     SI,OFFSET TEMPLATE_FONT
               CALL    UPDATE_FONT

               MOV     BP,EDIT_TOP             ;Display edit character.
               MOV     SI,OFFSET EDIT_FONT

UPDATE_FONT:   CALL    CHAR_START              ;Retrieve index to character.

NEXT_LINE:     LODSB                           ;Get a byte.
               MOV     AH,AL                   ;Store in AH.
               MOV     DI,AX                   ;Store in DI.
               PUSH    CX                      ;Preserve bytes/char.
               MOV     CX,8                    ;Eight bits/byte.
               MOV     DX,BP                   ;Top left of font display.

NEXT_PIXEL:    RCL     DI,1                    ;Get a bit.
               MOV     AL,PIXEL_ON             ;Assume it's on.
               JC      DISPLAY_IT              ;Did bit end up in carry flag?
               MOV     AL,PIXEL_OFF            ;If no, guessed wrong; pixel off.

DISPLAY_IT:    CALL    WRITE_PIXEL             ;Display the pixel.
               LOOP    NEXT_PIXEL              ;Do all 8 pixels.

               ADD     BP,100H                 ;Next display row.
               POP     CX                      ;Retrieve bytes/char.
               LOOP    NEXT_LINE               ;Do all rows.
               RET

;---------------------------;
; INPUT                     ;
;   Entry point = DIVIDE.   ;
;   AL = Number to display. ;
;   BL = Attribute.         ;
;   CX = Places to display. ;
;   DX = Cursor position.   ;
;---------------------------;

NEXT_COUNT:    MOV     AH,SPACE                ;Assume zero.
               OR      AL,AL                   ;Is it a zero?
               JZ      ASCII                   ;If yes, display space instead.
DIVIDE:        MOV     BH,10                   ;Divisor of ten.
               XOR     AH,AH                   ;Zero in high half.
               DIV     BH                      ;Divide by ten.
               ADD     AH,"0"                  ;Convert to ASCII.
ASCII:         XCHG    AL,AH                   ;Remainder in AL.
               CALL    WRITE_CHAR              ;Display it.
               XCHG    AL,AH                   ;Back to AH.
               DEC     DL                      ;Move back one column.
               LOOP    NEXT_COUNT              ;Display all three bytes.
               RET

;---------------------;
; INPUT               ;
;   AL = Character    ;
;   BL = Attribute    ;
;   AX, CX preserved. ;
;---------------------;

WRITE_CHAR:    PUSH    AX
               PUSH    CX
               CALL    SET_CURSOR
               MOV     CX,1
               MOV     AH,9                    ;Write attribute/character.
               INT     10H
               POP     CX
               POP     AX
               RET

;------------------------------------------------------------------------------;
; The Ega/Vga registers are programmed to access segment A000h where the       ;
; fonts are stored.  The font is retrieved and registers reset back to normal. ;
;------------------------------------------------------------------------------;

RETRIEVE_FONT: MOV     SI,OFFSET ACCESS_A000H  ;Point to access parameters.
               CALL    SET_REGISTERS           ;Set the registers.

               MOV     BX,POINTS               ;Retrieve bytes/character.
               MOV     AX,0A000H               ;Point to font segment.
               MOV     DS,AX
               MOV     DI,OFFSET EDIT_FONT     ;Point to destination.
               MOV     BP,256                  ;256 characters.
               XOR     DX,DX                   ;Source starting offset of zero.

NEXT_CHAR:     MOV     SI,DX                   ;Point to source.
               MOV     CX,BX                   ;Bytes/character.
               REP     MOVSB                   ;Retrieve the bytes.
               ADD     DX,20H                  ;Next character two paragraphs.
               DEC     BP                      ;Do all 256 characters.
               JNZ     NEXT_CHAR

               PUSH    CS                      ;Restore data segment.
               POP     DS

               MOV     SI,OFFSET EDIT_FONT     ;Copy the edit font to template.
               MOV     DI,OFFSET TEMPLATE_FONT
               MOV     CX,MAX_POINTS * 256 / 2
               REP     MOVSW

               MOV     SI,OFFSET PROTECT_A000H ;Point to normal parameters.

SET_REGISTERS: MOV     CX,2                    ;Two sequencer registers.
               MOV     DX,3C4H                 ;Indexing register.
               CALL    NEXT_REGISTER

               MOV     CX,3                    ;Three graphics controller regs.
               MOV     DL,0CEH                 ;Indexing registers.
NEXT_REGISTER: LODSB                           ;Get index.
               OUT     DX,AL
               INC     DX
               LODSB                           ;Get value.
               OUT     DX,AL
               DEC     DX
               LOOP    NEXT_REGISTER
               RET

;-----------------------------------------------------;
; Similar to RETRIEVE_FONT procedure except character ;
; is uploaded instead of entire font down loaded.     ;
;-----------------------------------------------------;

LOAD_CHAR:     MOV     SI,OFFSET ACCESS_A000H
               CLI
               CALL    SET_REGISTERS
               MOV     SI,OFFSET EDIT_FONT     ;Point to character
               CALL    CHAR_START              ; to upload.
               PUSH    CX                      ;Preserve bytes/char.
               MOV     CL,5                    ;32 bytes record for A000h font.
               SHL     AX,CL                   ;Index to appropriate character.
               MOV     DI,AX
               POP     CX
               MOV     AX,0A000H               ;Point to font segment.
               MOV     ES,AX
               REP     MOVSB                   ;Upload the bytes.
               PUSH    CS                      ;Restore extra segment.
               POP     ES
               MOV     SI,OFFSET PROTECT_A000H
               CALL    SET_REGISTERS
               STI
               MOV     MODIFY_FLAG,1           ;Note that font modified.
               RET

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

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

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

HIDE_CURSOR:   MOV     DH,ROWS
               INC     DH
               XOR     DL,DL                   ;Hide cursor one row below
               CALL    SET_CURSOR              ; displayable rows.
               RET

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

CLEAR_MENU:    MOV     CX,100H                 ;Row 1; column zero.
               MOV     DX,34FH                 ;Row 3; column 79.
               JMP     SHORT SCROLL

CLS:           XOR     CX,CX                   ;Row zero; column zero.
               MOV     DH,ROWS                 ;Rows.
               MOV     DL,79                   ;Column 79.
SCROLL:        MOV     BH,7                    ;Attribute.
               MOV     AX,600H                 ;Scroll window of active page.
               INT     10H

               XOR     DX,DX
               CALL    SET_CURSOR
               RET

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

GET_KEY:       XOR     AH,AH
               INT     16H
               RET

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

PRINT_STRING:  MOV     AH,9
               INT     21H
               RET

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

DISP_FILENAME: MOV     SI,FILENAME
               JMP     SHORT TTY_STRING

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

DO_WRITE:      CALL    WRITE_TTY
TTY_STRING:    LODSB
               OR      AL,AL
               JNZ     DO_WRITE
               RET

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

WRITE_TTY:     MOV     AH,0EH
               INT     10H
               RET

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

DO_CHAR_ATTR:  CALL    WRITE_CHAR
               INC     DL
CHAR_ATTRIB:   LODSB
               OR      AL,AL
               JNZ     DO_CHAR_ATTR
               RET

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

SETUP:         CLI                             ;No interrupts.
               CALL    RETRIEVE_FONT           ;Retrieve font.
               STI                             ;Interrupts back on.
               CMP     BYTE PTR DS:[80H],0     ;Command line parameter?
               JZ      CK_MOUSE                ;If no, skip to mouse.
               CALL    PARSE_FILE              ;Else, parse the parameter.
               CALL    OPEN_FILE               ;Try to open the file.
               JC      CREATE_PROMPT           ;If not found, ask to create.
               CALL    READ_FILE               ;Else, read the file.
               JMP     SHORT CK_MOUSE          ;Done here.

CREATE_PROMPT: MOV     SI,OFFSET CREATE_MSG    ;Display create query.
               CALL    PROMPT
               JZ      CREATE                  ;If got thumbs up, create it.
               JMP     ERROR_EXIT              ;Else, exit.

CREATE:        CALL    CREATE_FILE             ;Create the file.
               MOV     DX,OFFSET FAILED_MSG    ;If failed, exit with message.
               JNC     CK_MOUSE
               JMP     ERROR_MSG

CK_MOUSE:      XOR     AX,AX                   ;Mouse reset and status.
               INT     33H
               OR      AX,AX                   ;Is mouse active?
               JZ      DISPLAY_HEAD            ;If no, skip.
               MOV     MOUSE_FLAG,1            ;Else, flag.

DISPLAY_HEAD:  CALL    CLS                     ;Clear the screen.
DISPLAY_COPY:  MOV     SI,OFFSET COPYRIGHT     ;Display copyright in
               MOV     BL,INVERSE              ; inverse video.
               CALL    CHAR_ATTRIB

               MOV     DX,100H                 ;Row 1; column 0.
               CALL    SET_CURSOR
               MOV     DX,OFFSET MENU          ;Display menu.
               CALL    PRINT_STRING
               CMP     FILE_FLAG,1             ;If filename, display it.
               JNZ     DISPLAY_MENU
               CALL    DISP_FILENAME

DISPLAY_MENU:  MOV     DX,200H                 ;Row 2; column 0.
               CALL    SET_CURSOR
               MOV     DX,OFFSET MENU1         ;Display more menu.
               CALL    PRINT_STRING

               MOV     SI,OFFSET MENU2
               MOV     BL,NORMAL
               MOV     DX,436H
               CALL    CHAR_ATTRIB
               MOV     DX,536H
               CALL    CHAR_ATTRIB
               MOV     DX,636H
               CALL    CHAR_ATTRIB

               MOV     SI,OFFSET CAPTIONS      ;Display three captions.
               MOV     CX,3

NEXT_CAPTION:  LODSW
               MOV     DX,AX                   ;Caption starting cursor pos.
NEXT_CAP:      LODSB
               CMP     AL,0                    ;End of string?
               JZ      END_CAPTION
               CALL    WRITE_CHAR              ;Write the caption.
               INC     DH                      ;Next row.
               JMP     SHORT NEXT_CAP
END_CAPTION:   LOOP    NEXT_CAPTION            ;Do all three captions.

               MOV     BL,ATTRIBUTE            ;Background attribute.
               MOV     SI,OFFSET CURRENT_BOX   ;Display char/ASCII box.
               MOV     DX,BOX_TOP              ;Starting position.
               MOV     BP,3                    ;Three rows.
NEXT_BOX:      MOV     CX,13                   ;13 characters/row.
NEXT_BOX_CHAR: LODSB
               CALL    WRITE_CHAR
               INC     DL                      ;Next column.
               LOOP    NEXT_BOX_CHAR
               MOV     DL,LOW BOX_TOP          ;Starting column.
               INC     DH                      ;Next row.
               DEC     BP
               JNZ     NEXT_BOX

               MOV     DX,CHAR_TOP             ;Display character set.
               XOR     AL,AL                   ;Start with ASCII zero.
NEXT_SET:      MOV     CX,16                   ;16 bytes/row.
NEXT_BYTE:     CALL    WRITE_CHAR
               INC     AL                      ;Next character.
               JZ      SETUP_END
               INC     DL                      ;Next row.
               LOOP    NEXT_BYTE
               MOV     DL,LOW CHAR_TOP         ;Starting column.
               INC     DH
               JMP     NEXT_SET

SETUP_END:     CALL    DISPLAY_FONT            ;Display the edit and template.
               CALL    UPDATE_CURSOR           ;Display the cursor.
               RET

;**************** FONTLOADER ****************;
; This is the code to load the font and      ;
; is followed by the edit and template font  ;
;********************************************;

LOADER         LABEL   BYTE
LOADER_OFFSET  EQU     100H - LOADER

               JMP     BEGINNING

;              DATA AREA
;              ---------
               DB      CR,SPACE,SPACE,SPACE,CR,LF

PROGRAMMER2    DB      " PC Magazine ",BOX," Michael J. Mefford",0,CTRL_Z
SIGNATURE_LEN  EQU     $ - PROGRAMMER2

CRT_MODE       DB      ?
EDIT_CURSOR    DW      102H
CHAR_CURSOR    DW      401H
EDIT_CHAR      DB      65
POINTS         DW      ?
EDIT_FLAG      DB      1

;              CODE AREA
;              ---------

BEGINNING:     MOV     AX,500H                 ;Active page zero
               INT     10H

               MOV     AH,0FH                  ;Current video state.
               INT     10H
               MOV     DI,OFFSET CRT_MODE + LOADER_OFFSET ;Font CRT_MODE.
               MOV     AH,[DI]
               CMP     AL,AH                   ;Same mode?
               JZ      LOAD_FONT               ;If yes, skip.
               MOV     AL,AH                   ;Else, change video mode.
               XOR     AH,AH
               INT     10H

LOAD_FONT:     MOV     BP,OFFSET EDIT_FONT + LOADER_OFFSET
               MOV     DI,OFFSET POINTS + LOADER_OFFSET
               MOV     BH,[DI]
USER_LOAD:     MOV     CX,256
               XOR     DX,DX
               XOR     BL,BL
               MOV     AX,1110H                ;User alpha load.
               INT     10H
               RET                             ;Terminate.

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

EVEN
EDIT_FONT      LABEL   BYTE
TEMPLATE_FONT  EQU     EDIT_FONT + MAX_POINTS * 256
LOADER_END     EQU     TEMPLATE_FONT + MAX_POINTS * 256
LOADER_LENGTH  EQU     LOADER_END - LOADER

_TEXT          ENDS
               END     START
