      PAGE 55,132
      TITLE NOTEPAD4
;
;NotePad IV -  NON BIOS VERSION
;      Permission is granted to copy and distribute freely copies of this
;      program with the following restrictions:
;
;      1) this program be passed in the public domain
;      2) a fee, beyond the cost of the disk, not be charged
;      3) improvements not be passed on but rather sent to me
;       to check for possible incompatibilities with other programs
;
;   (Stephen) Randy Davis and Larry Weiss
;   (214)454-2426
;
;       feel free to call with bugs or questions
;

;PARAMETERS -- MOST OF THESE MAY BE CHANGED TO ANY DESIRED VALUE

wndw_ht    equ 24             ;mark the size of the window to open
wndw_wd    equ 40
our_key    equ  07100H        ;scan code - ascii of key to open window
                              ;now scan codes of edit keys  (AltF10)
up_arrow   equ  4800H
down_arrow equ  5000H
left_arrow equ  4B00H
right_arrow equ 4D00H
del_left   equ  0008H         ;user rubout (cntrl H) key
delete     equ  5300H         ;use del (lower right)
insert     equ  5200H
cr         equ  000DH
home       equ  4700H
escape     equ  001BH
tab        equ  7400H         ; ^right arrow
rev_tab    equ  7300H         ; ^left arrow
line_ins   equ  5100H         ;pg down
line_del   equ  4900H         ;pg up
mark_char  equ  4F00H         ;end key
print_char equ  372AH         ;PrtSc* = print note on printer
file_change equ 4A2DH         ;key to update file and get a new one

blank      equ  07020H       ;define the character which is a blank
attrib     equ  070H         ;and our default attribute (should agree with blank)
video      equ  10H          ;video BIOS call
inv_attrib equ  007H         ;inverse of 'attrib'
    PAGE
CSEG  SEGMENT PARA PUBLIC 'CODE'
      ASSUME CS:CSEG,DS:CSEG,SS:CSEG,ES:NOTHING   ;STANDARD DECL FOR .COM FILE

      ORG  100H

Start:
      JMP INSTALL

;PLACE DATA AREA HERE FOR READIBILITY

COPYRITE    DB 'Copyright Stephen R. Davis, Larry Weiss 1985  All Rights Reserved'
            DB '(214)454-2426'
SCREENSAVE: DW (WNDW_HT * (WNDW_WD+1)) DUP (BLANK);RESERVE SPACE TO SAVE OPENED WINDOW
LINESAVE:	DW WNDW_WD DUP (BLANK) ;BUFFER FOR LINE DELETE

CURSOR_POS  DW 0
OLD_CUR_POS DW 0
SAVESTACK   DW 0,0

REQUEST     DB 0              ;REQUEST TYPE
KEY_RQST_HANDLER DD 0         ;ADDRESS OF THE ORIGINAL KEYBOARD REQUEST HANDLER

LEFT_MARG  DB   00            ;define the confines of the window
RIGHT_MARG DB   00
TOP_MARG   DB   00
BOT_MARG   DB   00
UPPER_LEFT DW   00

MARK       DW   00            ;FLAGS USED IN CHARACTER FEED
FEED_START DW   00            ;ADDRESS IN BUFFER OF BEGIN...
FEED_END   DW   00            ;...AND END OF FEED

DISPLAY_SEG DW	00		;DISPLAY SEGMENT (B000 -> MONOCHROME,
				;                 B800 -> GRAPHIX)

FILE_NM_LOC DW  00            ;ADDRESS OF FILE NAME (0 -> NO FILE)
PAGE
BEGIN PROC FAR                ;FAR LABEL SINCE ITS ENTERED BY INTERRUPT
MAINLOOP:
      MOV  CS:REQUEST,AH      ;SAVE HIS AX REGISTER

      MOV  AX,CS:FEED_START   ;ARE WE IN THE MIDDLE OF FEEDING CHARS...
      CMP  AX,CS:FEED_END     ;...TO THE APPLICATION?
      JZ   NOFEED             ;NO
      JMP  FEED               ;YES
NOFEED:
      MOV  AH,CS:REQUEST      ;RESTORE USERS AX
      PUSHF                   ;CALL OLD INTERRUPT HANDLER (EMULATE INT INSTRUCTION)
      CALL DWORD PTR [CS:KEY_RQST_HANDLER] ;GO AHEAD AND GET THE CHARACTER
      PUSHF                   ;SAVE THE FLAG RETURNED (FOR AH = 1 REQUESTS)

      CMP CS:REQUEST,00H      ;WE ARE ONLY INTERESTED IN CHAR. REQUESTS
      JZ  CNTINUE
      JMP FORGET_IT

CNTINUE:                      ;YES -- CHECK CHARACTER FOR 'OURS'
      POPF                    ;FOR NON-TYPE 1 REQUESTS DONT NEED TO SAVE FLAGS
      CMP AX,OUR_KEY
      JZ  CNT_AGIN
      JMP RETURN_CHAR

CNT_AGIN:                     ;WE GOT IT! GO INTO EDITOR MODE
      MOV CS:SAVESTACK,SP
      MOV CS:SAVESTACK+2,SS
      MOV  AX,CS
      MOV  SS,AX
      MOV  SP,100H

      STI                     ;ENABLE INTERRUPTS WHILE PROCESSING CHARACTERS

      PUSH BP                 ;SET UP A STACK FRAME
      MOV  BP,SP
      SUB  SP,0EH
      CALL SAVEREG
      MOV  DS,AX

      CALL CALC_WINDOW        ;CALCULATE WINDOW EXTREMETIES

      CALL READ_CURSOR
      MOV OLD_CUR_POS,DX      ;SAVE THE CURSOR FOR LATER RESTORING

      CALL WINDOW_SWAP        ;SWING IN THE EDIT WINDOW (WITH NOTES?)

      CALL INSERT_CR          ;PUT THE CARRIAGE RETURNS ON UNPRINTABLE COL.S

      MOV DX,CURSOR_POS       ;RESTORE CURSOR IN EDIT WINDOW
      CALL PLACE_CURSOR

      CALL EDITOR             ;LET HIM EDIT IN THE WINDOW

      CALL READ_CURSOR
      MOV CURSOR_POS,DX       ;SAVE CURSOR IN EDIT WINDOW FOR NEXT EDIT

      CMP  MARK,00H          ;CLEAR THE MARK POSITION (IF IT'S SET)
      JZ   NOT_SET
      MOV  DX,MARK
      CALL READ_CHAR          ;GET THE CHARACTER...
      MOV  AH,ATTRIB          ;...PUT ITS ATTRIBUTE BACK TO EVERYONE ELSE'S...
      CALL WRITE_CHAR         ;...AND PUT IT BACK
      MOV  MARK,00H
NOT_SET:

      CALL WINDOW_SWAP        ;PUT BACK WHATEVER WAS ORIGINALLY THERE

      CALL UPDATE_FILE        ;NOW ATTEMPT TO UPDATE THE DISK FILE

      MOV DX,OLD_CUR_POS      ;RESTORE THE CURSOR POSITION
      CALL PLACE_CURSOR

      CALL RESTREG
      ADD  SP,0EH
      POP  BP

      MOV SS,CS:SAVESTACK+2
      MOV SP,CS:SAVESTACK

      MOV AH,00              ;RESTORE REQUEST TO SOMETHING DECENT
      JMP MAINLOOP           ;GO GET ANOTHER CHARACTER TO RETURN HIM

RETURN_CHAR:
      IRET                   ;RETURN WITH CHARACTER

;HANDLE REQUEST TYPE 1'S BY FAR RETURNING 2 AND TYPE 2'S BY JUST RETURNING

FORGET_IT:
      CMP  CS:REQUEST,1      ;was it a "is char present" request?
      JZ   FI100

      POPF                   ;no -- just return whatever BIOS returned
      IRET

FI100:
      POPF                   ;yes -- make funny return
      RET 02
      PAGE
;HERE WE ARE IN THE PROCESS OF FEEDING CHARACTERS TO THE APPLICATION
;FROM THE WINDOW BUFFER (SCREENSAVE)

FEED:
      CMP  CS:REQUEST,1       ;WAS THIS A CHAR TYPE REQUEST?
      JA   KSTAT
                              ;YES -- RETURN HIM CHAR
      PUSH BX                 ;GET THE NEXT CHARACTER FROM THE BUFFER
      CALL SKIP_SPACES        ;STRIP LEADING SPACES

      MOV  BX,CS:FEED_START   ;OK - GET THE NEXT CHARACTER WE NEED
      MOV  AX,CS:[BX]
      POP  BX
      XOR  AH,AH              ;WE NO LONGER HAVE THE SCAN CODE, BUT ALL
                              ;ARE ASCII ANYWAY

      CMP  CS:REQUEST,0       ;IS THIS A 'GET KEYBOARD CHAR' RQST?
      JNZ  FEED_KEY_STAT

      ADD  CS:FEED_START,2    ;YES -- MOVE UP THE POINTER BY 1
      IRET

FEED_KEY_STAT:
      STI                     ;NO -- ENABLE INTERRUPTS AND FEED HIM THE
      RET  02                 ;THE Z FLAG CLEAR WITH HIS CHAR

KSTAT:
      MOV  AH,CS:REQUEST      ;RESTORE REQUEST TO AH
      CALL DWORD PTR [CS:KEY_RQST_HANDLER] ;PERFORM BIOS CALL -- WE DON'T KNOW
                              ;WHAT HE'S DOING AND WE SHOULDN'T GET IN THE WAY
      IRET                    ;RETURN THE RESULTS TO HIM (WHATEVER THEY ARE)
BEGIN ENDP
      PAGE
BODY  PROC NEAR               ;MAKE SHORT CALLS ONLY TO THE MAIN ROUTINES

SKIP_SPACES:
;TRY TO AVOID FEEDING SPACES WITH NOTHING AFTER THEM ON A LINE --
;JUST SKIP OVER TO THE RETURN AND FEED THAT ALONE

      MOV  BX,CS:FEED_START   ;START WITH THE NEXT FEED CHARACTER
LOOK_AHEAD:
      MOV  AX,CS:[BX]
      CMP  AL,CR              ;IS THIS CHAR A RETURN
      JZ   STRIP_SPACES
      CMP  AL,' '             ;IS THIS CHARACTER ANOTHER SPACE?
      JNZ  GO_ON
      ADD  BX,2               ;YES -- KEEP LOOKING AHEAD
      CMP  BX,CS:FEED_END     ;IS THAT IT?
      JNZ  LOOK_AHEAD
                              ;YES--
      SUB  BX,2               ;RAN OFF THE END -- BACK UP ONE CHAR AT LEAST!
STRIP_SPACES:
      MOV  CS:FEED_START,BX   ;HERE WE FOUND NOTHING BUT SPACES BEFORE THE
                              ;CARRIAGE RETURN -- SKIP THAT!

GO_ON:
      RET
      PAGE
CALC_WINDOW:
      MOV AH,0FH              ;FIND OUT VIDEO MODE FOR CALCULATING WINDOW SIZE
      INT VIDEO               ;GO AHEAD AND GET THE CHARACTER

      MOV CX,0B000H           ;IS IT MONOCHROME?
      CMP AL,7                ;WELL LOOK FOR MODE 7
      JZ  CW100
      MOV CX,0B800H           ;NO -- ITS GRAPHIX
CW100:
      MOV DISPLAY_SEG,CX      ;SAVE THIS OFF

      DEC AH                  ;THIS IS THE NUMBER OF COLS ON SCREEN
      MOV RIGHT_MARG,AH       ;SET UP RIGHT AND LEFT MARGINS
      SUB AH,(WNDW_WD - 1)
      MOV LEFT_MARG,AH

      MOV TOP_MARG,0          ;WE HAVE NO INFORMATION ON NUMBER OF ROWS
      MOV BOT_MARG,WNDW_HT-1  ;SO WE MUST ASSUME SOMETHING NORMAL (IT ISNT
                              ;AS CRITICAL ANYWAY)

      MOV AL,AH               ;NOW ADD UPPER_LEFT HAND CORNER VALUE
      XOR AH,AH
      MOV UPPER_LEFT,AX


      CMP CURSOR_POS,0        ;IF THIS IS THE FIRST TIME WEVE DONE THIS...
      JNZ CW200
      MOV CURSOR_POS,AX       ;...PLACE THE CURSOR IN THE UPPER LEFT HAND CORNER

CW200:
      RET
      PAGE
INSERT_CR:                      ;INSERT CARRIAGE RETURNS 1 PAST RIGHT MARGIN
      MOV CX,WNDW_HT
      MOV SI,OFFSET SCREENSAVE
      MOV BX,WNDW_WD
      ADD BX,BX                 ;EVERY LOCATION TAKES A WORD, NOT A BYTE
ICR_LOOP:
      ADD SI,BX                 ;GO 1 BEYOND RIGHT MARGIN
      MOV BYTE PTR [SI],CR
      ADD SI,2                  ;THAT CR TAKES A SPACE
      LOOP ICR_LOOP
      RET

WRITE_POS DW 0

WINDOW_SWAP:
	MOV CX,WNDW_HT		;GET THE NUMBER OF ROWS IN WINDOW AREA
      MOV SI,OFFSET SCREENSAVE;START AT BEGINNING OF BUFFER
      MOV ES,DISPLAY_SEG      ;LOAD UP THE VIDEO SEGMENT
      MOV DI,00
      XOR BX,BX
WS050:
      MOV BL,LEFT_MARG        ;START ON THIS LINE AT THE LEFT MARGIN
WS100:
      SHL  BX,1               ;CHANGE THE COLUMN NUMBER TO BYTE POINTER
      MOV  AX,ES:[BX][DI]     ;GET THE NEXT CHARACTER FROM SCREEN
      XCHG AX,[SI]            ;STORE IT AWAY AND WRITE THE SAVED CHARACTER
      ADD  SI,2               ;AND MOVE SAVE POINTER OVER A WORD
      MOV  ES:[BX][DI],AX     ;NOW RESTORE THAT CHARACTER TO THE SCREEN
      SHR  BX,1               ;PUT THE BYTE OFFSET BACK TO COLUMN NUMBER
      INC  BX
      CMP  BL,RIGHT_MARG      ;ARE WE BEYOND THE END OF THE LINE?
      JNA  WS100
                              ;YES -- SKIP DOWN TO NEXT LINE
      ADD  SI,2               ;SKIP BEYOND CARRIAGE RETURN
      SHL  BX,1
      ADD  DI,BX
      LOOP WS050
      RET
      PAGE
EDITOR:

;first check for "special" keys -- print screen and close file
;if not one of these, check for edit keys (such as insert, delete, etc.)
;if not one of those, assume its ascii and just insert it in the notepad
;return when AltF10 detected.

ED100:
      CALL GET_CHAR         ;read a character from the keyboard
      CMP  AX,PRINT_CHAR    ;check for print note first
      JNZ  ED102

      CALL PRINTNOTE        ;we need to print the note -- not change it
      MOV  BX,CURSOR_POS     ;in many cases we need the cursor position
      JMP  ED500

ED102:
      CMP  AX,FILE_CHANGE   ;is he wanting to close and open a new file
      JNZ  ED103
      CALL CHANGE_FILE
      MOV  BX,CURSOR_POS
      JMP  ED500

ED103:
      OR   AL,AL            ;if this is ascii then dont retain scan code
      JZ   ED105
      XOR  AH,AH

ED105:
      MOV  BX,CURSOR_POS     ;in many cases we need the cursor position
      CMP  AX,OUR_KEY        ;check for exit
      JNZ  ED110
      JMP  ED800

ED110:
      CMP  AX,CR             ;check for each special character individually
      JNZ  ED120
      MOV  BL,LEFT_MARG
      INC  BH
      JMP  ED500
ED120:
      CMP  AX,LEFT_ARROW
      JNZ  ED140
      DEC  BL
      JMP  ED500

ED140:
      CMP  AX,RIGHT_ARROW
      JNZ  ED160
      INC  BL
      JMP  ED500

ED160:
      CMP   AX,UP_ARROW
      JNZ   ED180
      DEC   BH
      JMP   ED500

ED180:
      CMP  AX,DOWN_ARROW
      JNZ  ED200
      INC  BH
      JMP  ED500

ED200:
      CMP  AX,HOME
      JNZ  ED205
      MOV  BX,UPPER_LEFT
      JMP  ED500

ED205:                       ;tab and back tab functions
      CMP  AX,TAB
      JNZ  ED210
      MOV  CL,1              ;go forward
      JMP  ED213

ED210:
      CMP  AX,REV_TAB
      JNZ  ED220

      MOV  CL,0FFH           ;go backwards one tab slot
      ADD  DL,CL             ;start one char to left initially

ED213:
      CALL READ_CHAR         ;get char at current position
      MOV  CH,AL             ;save it for comparison
ED215:
      ADD  DL,CL             ;move over one character
      CMP  DL,RIGHT_MARG     ;stop at left and right margins (or beyond)
      JGE  ED218
      CMP  DL,LEFT_MARG
      JLE  ED218

      CALL READ_CHAR         ;read current character
      CMP  CH,' '            ;if original was a space...
      JNZ  ED216
      CMP  AL,' '            ;...then go until not space
      JZ  ED215
      JMP ED217
ED216:
      CMP AL,' '             ;...else, go until space
      JNZ ED215
ED217:
      CMP  CL,0FFH           ;if we were going backwards (towards left)...
      JNZ  ED218
      ADD  DL,1              ;...then scoot back to the right by 1
ED218:
      MOV  BX,DX
      JMP  ED500

ED220:
      CMP  AX,ESCAPE         ;wipe out remainder of line
      JNZ  ED240
      MOV  DX,BX
      CALL ERASE_LINE
      JMP  ED500

ED240:
      CMP  AX,DEL_LEFT       ;move cursor left one char and then do normal del
      JNZ  ED260
      DEC  BL
      CMP  BL,LEFT_MARG
      JNB  ED250
      MOV  BL,RIGHT_MARG
      CMP  BH,00
      JZ   ED250
      DEC  BH
ED250:
      JMP  ED270

ED260:
      CMP  AX,DELETE         ;in delete char we...
      JNZ  ED280
ED270:
      MOV  CL,RIGHT_MARG
      SUB  CL,BL             ;calculate how many chars to right margin
      XOR  CH,CH
      MOV  DX,BX             ;...start at current cursor position...
      JCXZ ED275
ED272:
      INC  DL                ;...get character just to the right...
      CALL READ_CHAR
      DEC  DL                ;...move left one position...
      CALL WRITE_CHAR        ;...and write it there...
      INC  DL                ;...now do it again for the char to the right...
      LOOP ED272             ;...for the distance from cursor to right margin;...
ED275:
      MOV  AX,BLANK
      CALL WRITE_CHAR
      JMP  ED500

ED280:
      CMP  AX,INSERT         ;in the case of insert we do reverse of delete
      JNZ  ED300
      MOV  DH,BH             ;start at the right margin
      MOV  DL,RIGHT_MARG
      MOV  CL,DL             ;caculate number of spaces to right
      SUB  CL,BL
      XOR  CH,CH
      JCXZ ED290
ED285:
      DEC  DL
      CALL READ_CHAR
      INC  DL
      CALL WRITE_CHAR
      DEC  DL
      LOOP ED285
ED290:
      MOV  AX,BLANK
      CALL WRITE_CHAR
      JMP  ED500

ED300:
      CMP  AX,LINE_INS       ;check for insert line
      JNZ  ED320
      MOV  DH,BOT_MARG       ;we're going to need that
ED305:
      MOV  DL,LEFT_MARG      ;always start at the far left
      CMP  DH,BH             ;are we on our current line?
      JZ   ED315
                             ;no -- then move it down
      MOV  CX,WNDW_WD
ED310:
      DEC  DH
      CALL READ_CHAR         ;get the character
      INC  DH
      CALL WRITE_CHAR        ;and put it back one line higher
      INC  DL                ;move right one character
      LOOP ED310

      DEC  DH                ;now move up a line and do it again
      JMP  ED305
ED315:
      MOV  BL,LEFT_MARG      ;move us over the far left marg
      CALL ERASE_LINE        ;and wipe out the line we are on
      JMP  ED500

ED320:
      CMP  AX,LINE_DEL       ;and check for line delete
      JNZ  ED340

ED325:
      MOV  DL,LEFT_MARG      ;always start at the far left
      CMP  DH,BOT_MARG       ;are we on the last line?
      JZ   ED335
                             ;no -- then move it up
      MOV  CX,WNDW_WD
ED330:
      INC  DH
      CALL READ_CHAR         ;get the character
      DEC  DH
      CALL WRITE_CHAR        ;and put it back one line lower
      INC  DL                ;move right one character
      LOOP ED330

      INC  DH                ;now move down a line and do it again
      JMP  ED325
ED335:
      MOV  BL,LEFT_MARG      ;move us over the far left marg of our line
      CALL ERASE_LINE        ;and wipe out the bottom line
      JMP  ED500

ED340:
      CMP  AX,MARK_CHAR      ;check for mark
      JNZ  ED400

      MOV  DX,BX
      CMP  MARK,00H          ;if mark isnt set...
      JNZ  ED350
      MOV  MARK,BX           ;...just set it and switch its attrib
      CALL READ_CHAR
      MOV  AH,INV_ATTRIB
      CALL WRITE_CHAR
      JMP  ED500

ED350:                       ;else, store off feed start and end addresses
      CALL CONVERT_LOC
      MOV  FEED_START,AX

      MOV  DX,MARK
      CALL CONVERT_LOC
      MOV  FEED_END,AX

      MOV  CX,FEED_START
      CMP  CX,AX
      JNA  ED355
      MOV  FEED_START,AX
      MOV  FEED_END,CX
ED355:
      JMP  ED800             ;note that this char exits the note pad

ED400:                       ;wasnt an edit character -- must be ascii
                             ;just write the character at current position
      MOV  AH,ATTRIB
      CALL W_CHAR
      INC  BL                ;move over one position

ED500:                       ;adjust resulting cursor position
      CMP  BL,RIGHT_MARG
      JNA  ED550
      MOV  BL,RIGHT_MARG
ED550:
      CMP  BH,BOT_MARG
      JNA  ED600
      MOV  BH,BOT_MARG
ED600:
      CMP  BL,LEFT_MARG
      JNB  ED650
      MOV  BL,LEFT_MARG
ED650:
      CMP  BH,0FFH           ;special case for a top margin of 0
      JNZ  ED700
      MOV  BH,BOT_MARG
ED700:
      MOV  CURSOR_POS,BX
      MOV  DX,BX             ;be sure and move the cursor to the new
      CALL PLACE_CURSOR      ;position
      JMP  ED100

ED800:                       ;exit
      RET
      PAGE
PRINTNOTE:
      CALL P_SPLAT_ROW       ;put a row of splats across the top
                             ;to delineate the note on the printer page

      MOV  DX,UPPER_LEFT
PN100:
      CMP   DL,LEFT_MARG     ;put a splat on left hand side also
      JNZ   PN150
      MOV   AL,2AH
      CALL  PRINT_C
      JNZ   PN400            ;don't proceede on timeout of printer

PN150:
      CALL  READ_CHAR        ;get the character @ DX
      CALL  PRINT_C          ;and print it
      JNZ   PN400            ;quit immediatly on i/o timeout

      CMP   DL,RIGHT_MARG
      JNZ   PN200

      MOV   AL,2AH           ;print a right margin delineator
      CALL  PRINT_C
      MOV   AL,0AH          ;print carriage return-line feed after every line
      CALL  PRINT_C
      MOV   AL,0DH
      CALL  PRINT_C

      CMP  DH,BOT_MARG       ;are we finished?
      JZ   PN300
                             ;no -- drop down a line
      INC  DH
      MOV  DL,LEFT_MARG
      DEC  DL
PN200:
      INC  DL                ;skip over to next character in note
      JMP  PN100

PN300:
      CALL P_SPLAT_ROW

PN400:
      RET


P_SPLAT_ROW:
      MOV  CX,WNDW_WD+2      ;put a row of splats across the page
PS100:
      MOV  AL,2AH
      CALL PRINT_C
      JNZ  PS200             ;return immediately on time out
      LOOP PS100

      MOV  AL,0AH
      CALL PRINT_C
      MOV  AL,0DH
      CALL PRINT_C
PS200:
      RET
      PAGE
;
; THE FOLLOWING ROUTINE CLOSES THE CURRENT FILE AND ASKS FOR A NEW
; FILE; IT ATTEMPTS TO OPEN THIS FILE UP AND READ IT AS A NOTE PAD
;
CHANGE_FILE:
      CALL WINDOW_SWAP       ;PUT THE NOTE PAD INTO THE SAVE BUFFER FOR WRITING

      CALL UPDATE_FILE       ;IF CURSOR HOMED, WRITE THE NOTEPAD OUT TO THE CURRENT FILE

      MOV  FILE_NM_LOC,0     ;THAT FILE IS NOW CLOSED PERMANENTLY

      MOV  DX,UPPER_LEFT     ;ERASE THE TOP LINE AND POSE FILE NAME QUESTION
      MOV  BX,DX
      CALL ERASE_LINE

      MOV  DX,BX
      CALL PLACE_CURSOR      ;PUT CURSOR ON LEFT AND ASK FOR FILE NAME
      MOV  BX,OFFSET CF500
CF100:
      MOV  AL,BYTE PTR [BX]
      INC  BX
      CMP  AL,0              ;0 TERMINATES AN ASCIIZ STRING
      JZ   CF200
      MOV  AH,ATTRIB
      CALL W_CHAR
      INC  DL
      CALL PLACE_CURSOR
      JMP  CF100

CF200:
      MOV  BX,80H            ;STORE NAME IN PSP

CF300:
      CALL GET_CHAR         ;GET FILE NAME UNTIL RETURN
      CMP  AL,0DH            ;CHECK FOR RETURN
      JZ   CF400
      CMP  AL,08H            ;WE DO NEED TO HANDLE DEL CHAR SEPERATLY
      JNZ  CF350

      DEC  BX                ;HE'S TRYING TO DELETE THE LAST CHAR
      DEC  DL                ;MOVE THE POINTER BACK IN THE FILE AND DEL CHAR
      MOV  AL,20H            ;ON SCREEN W/ A SPACE
      MOV  AH,ATTRIB
      CALL WRITE_CHAR
      JMP  CF300

CF350:
      INC  BX                ;SAVE OFF NEXT CHARACTER OF NAME FIELD
      MOV  BYTE PTR [BX],AL

      MOV  AH,ATTRIB
      CALL W_CHAR            ;ECHO CHAR SO HE CAN SEE WHAT HE IS TYPING
      INC  DL
      CALL PLACE_CURSOR
      JMP  CF300

CF400:
      SUB  BX,80H            ;CALCULATE CHARACTER COUNT
      MOV  BYTE PTR [DS:80H],BL

      MOV  DL,LEFT_MARG      ;NOW ERASE NAME OF FILE AND RESTORE WHAT WAS THERE
      MOV  BX,DX
      CALL RESTORE_LINE

      CALL READ_FILE         ;READ UP THE INDICATED FILE INTO SAVE BUFFER
      CALL WINDOW_SWAP       ;NOW PUT NOTE PAD DISPLAY BACK UP
      RET

CF500: DB 'ENTER FILE NAME:',0

READ_FILE:                   ;READS FILE NAME IN 81 WITH LENGTH COUNT IN 80
                             ;ON SUCCESSFUL READS, RETURNS CARRY CLEARED

      MOV  BX,80H            ;FIND BEGINNING OF NAME FIELD
      XOR  CH,CH
      MOV  CL,BYTE PTR [BX]         ;GO GET THE ARGUMENT COUNT FROM THE PSP
      JCXZ  RF300
RF100:                       ;FIND THE BEGINNING OF THE FILE NAME
      INC  BX
      CMP  BYTE PTR [BX],20H
      JNZ  RF150
      LOOP RF100

RF150:
      MOV  DI,BX             ;NOW TERMINATE IT WITH A 0
	JCXZ RF300		;A 0 LENGTH FILE NAME INDICATES NO FILE TO READ
      MOV  FILE_NM_LOC,BX    ;SAVE THE LOCATION OF THE NAME
RF200:
      INC  DI
      LOOP RF200
      MOV  BYTE PTR [DI],0

      MOV  AH,3DH            ;OPEN FILE
      MOV  AL,0              ;FILE FOR READ ONLY
      MOV  DX,BX             ;PUT FILE NAME IN GOOD REGISTER
      INT  21H
      JC   RF400             ;CARRY SET INDICATES OPEN ERROR

      MOV  BX,AX             ;READ THE FILE INTO THE NOTE PAD AREA
      MOV  CX,(WNDW_HT*(WNDW_WD+1)*2)
      MOV  DX,OFFSET SCREENSAVE
      MOV  AH,3FH
      INT  21H

      MOV  AH,3EH            ;CLOSE THE FILE
      INT  21H

RF300:
      CLC                    ;MAKE SURE CARRY CLEAR FOR NO ERROR RETURN
      RET

RF400:
      RET                    ;LEAVE CARRY SET ON ERROR RETURN


UPDATE_FILE:
      CMP  FILE_NM_LOC,0     ;IF THERE IS NOT FILE...
      JZ   UP100             ;...THEN JUST SKIP THIS WHOLE THING

      MOV  AX,CURSOR_POS     ;UPDATE IF CURSOR HOMED
      CMP  AX,UPPER_LEFT
      JNZ  UP100

      MOV  AL,1H             ;OPEN THE FILE FOR WRITING
      MOV  AH,3DH
      MOV  DX,FILE_NM_LOC    ;GET THE FILENAME IN DX
      INT  21H
      JC   UP100             ;IF ERROR ON OPEN, JUST GIVE IT UP

      MOV  BX,AX
      MOV  AH,40H            ;WRITE THE FILE
      MOV  CX,(WNDW_HT*(WNDW_WD+1)*2)
      MOV  DX,OFFSET SCREENSAVE
      INT  21H

      MOV  AH,3EH
      INT  21H               ;NOW CLOSE THE FILE
UP100:
      RET

      PAGE
;
; HERE WE PLACE SOME SMALL GENERAL PURPOSE ROUTINES
;

PRINT_C:                     ;print the char in AL on LPT1 (parrallel)
                             ;returns printer status in AH
      PUSH DX
      XOR  DX,DX             ;select LPT1
      XOR  AH,AH             ;select "print"
      INT  17H
      POP  DX
      TEST AH,1              ;set the zero flag if not error,
                             ;clear zero flag if char not printed
      RET

PLACE_CURSOR:                ;place cursor at location in dx
      MOV AH,2H
      PUSH BX
      XOR BX,BX
      INT VIDEO
      POP  BX
      RET

READ_CURSOR:                  ;read cursor location into dx
      MOV AH,3H
      PUSH BX
      XOR BX,BX
      INT VIDEO
      POP  BX
      RET

READ_CHAR:                    ;read screen character at location in dx
      CALL CALC_VID_LOC       ;covert the row and column into location
      MOV  AX,ES:[DI]         ;get the character at that location
      RET

R_CHAR:                       ;read screen char at current cursor location
      MOV AH,8H
      PUSH BX
      XOR BH,BH
      INT VIDEO
      POP  BX
      RET

WRITE_CHAR:                   ;write attrib/char in ax at location in dx
      PUSH AX                 ;save the attrib from destruction
      CALL CALC_VID_LOC       ;convert the row and column into location
      POP  AX
      MOV ES:[DI],AX
      RET

W_CHAR:                       ;write character at current cursor location
      PUSH BX                 ;save bx register
      MOV  BL,AH
      XOR  BH,BH
      PUSH CX                 ;retain cx register also
      MOV  CX,1
      MOV  AH,9H
      INT VIDEO
      POP  CX
      POP  BX
      RET

CALC_VID_LOC:                ;convert the row and column in dx into an
                             ;offset into the video display buffer
      XOR  AX,AX
      XOR  DI,DI
      PUSH CX                ;save cx -- some of those who call us need it
      XOR  CX,CX

      MOV  AL,RIGHT_MARG     ;get the width of a row
      INC  AX
      MOV  CL,DH             ;put the number of rows into cx
      JCXZ CVL200
CVL100:
      ADD  DI,AX             ;for every row add in another 80/40 columns
      LOOP CVL100
CVL200:
      MOV  AL,DL             ;now move over to the current column
      ADD  DI,AX

      POP  CX                ;restore cx

      SHL  DI,1              ;now convert this into byte offset
      MOV  ES,DISPLAY_SEG
      RET


GET_CHAR:                     ;get character from keyboard into ax
      MOV AH,0H
      PUSHF                   ;simulate call to keyboard handler
      CALL DWORD PTR [KEY_RQST_HANDLER]
      RET

ERASE_LINE:                   ;erase the current line from dx to right margin
      MOV  CL,RIGHT_MARG
      SUB  CL,BL
      INC  CL
      XOR  CH,CH
      MOV  SI,OFFSET LINESAVE

ER100:
      CALL READ_CHAR         ;get the character @ current location
      MOV  [SI],AX
      ADD  SI,2

      MOV  AX,BLANK
      CALL WRITE_CHAR
      INC  DL
      LOOP ER100
      RET

RESTORE_LINE:                ;restore the current line from dx to right margin
      MOV  CL,RIGHT_MARG
      SUB  CL,BL
      INC  CL
      XOR  CH,CH
      MOV  SI,OFFSET LINESAVE

RL100:
      MOV  AX,[SI]           ;get the saved char
      CALL WRITE_CHAR         ;put the saved char where indicated
      ADD  SI,2
      INC  DL
      LOOP RL100
      RET

CONVERT_LOC:                 ;convert a location on the screen (in dx) into location
                             ;in the screen save buffer
      MOV  CX,DX
      MOV  AX,OFFSET SCREENSAVE
CL100:
      CMP  CH,TOP_MARG
      JZ   CL200
      ADD  AX,(WNDW_WD+1)*2
      DEC  CH
      JMP  CL100

CL200:
      CMP  CL,LEFT_MARG
      JZ   CL300
      ADD  AX,2
      DEC  CL
      JMP  CL200

CL300:
      RET

SAVEREG:
      MOV  -2[BP],BX          ;save reggies on stack frame
      MOV  -4[BP],CX
      MOV  -6[BP],DX
      MOV  -8[BP],SI
      MOV  -0AH[BP],DI
      MOV  -0CH[BP],DS
      MOV  -0EH[BP],ES
      RET
RESTREG:
      MOV  BX,-2[BP]          ;NOW PUT THE REGGIES BACK AND PULL DOWN
      MOV  CX,-4[BP]          ;THE STACK FRAME
      MOV  DX,-6[BP]
      MOV  SI,-8[BP]
      MOV  DI,-0AH[BP]
      MOV  DS,-0CH[BP]
      MOV  ES,-0EH[BP]
      RET
BODY  ENDP
      PAGE
INSTALL:
;THIS PROGRAM INSTALLS THE REST OF THE CODE FOR NOTE PAD

;TRY TO READ THE INITIAL FILE UP INTO THE NOTE PAD AREA

      CALL READ_FILE
      JNC  GO_AHEAD          ;ON ERROR READING FILE...
                             ;...OUTPUT ERROR MESSAGE AND QUIT
      MOV  DX,OFFSET ERRMSG
      MOV  AH,9H
      INT  21H
      MOV  AH,4CH
      MOV  AL,1              ;SET THE ERROR LEVEL
      INT  21H

GO_AHEAD:
      MOV DX,OFFSET MESSAGE  ;OUTPUT 'OK' MESSAGE
      MOV AH,9H
      INT 21H

      MOV AX,3516H            ;GET INTERRUPT 16 VECTOR
      INT 21H
      MOV WORD PTR KEY_RQST_HANDLER,BX
      MOV WORD PTR KEY_RQST_HANDLER+2,ES

      MOV AX,2516H
      MOV DX,OFFSET BEGIN     ;NOW PUT OUR ROUTINE THERE
      INT 21H

      MOV DX,OFFSET INSTALL   ;TERMINATE AND STAY RESIDENT
      ADD DX,100H
      MOV CL,4
      SHR DX,CL
      MOV AH,31H
      INT 21H

MESSAGE DB 10,13,'NotePad4 installed',10,13,'Alt-F10 to enter and exit',10,13,'$'

ERRMSG DB 10,13,'NotePad4 not installed',10,13,'Error on file read',10,13,'$'

CSEG ENDS
     END START
