home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS 1992 December / simtel1292_SIMTEL_1292_Walnut_Creek.iso / msdos / pcmag / vol6n18.arc / SNIPPER.ASM next >
Assembly Source File  |  1987-09-01  |  26KB  |  779 lines

  1. page 60,132
  2. ; SNIPPER is a resident utility which allows cutting out a portion
  3. ; of the screen.  The selected portion may be printed, written
  4. ; to disk or entered in the keyboard buffer.  Activate SNIPPER by
  5. ; pressing ALT-W, then position the cursor in the upper left corner of
  6. ; the window using the arrow keys.  Press CR to fix the first corner,
  7. ; then expand the window with arrow keys.  Finally, type "P" to print,
  8. ; "F" for disk file, "G" to retrieve or CR for a help menu.  Press ESC
  9. ; any time to exit SNIPPER.  When installing SNIPPER, use the optional
  10. ; parameters to expand it's internal buffer for displays (such as the
  11. ; EGA) containing more than the standard 25 rows and 80 columns.
  12. ;   SNIPPER  [rows,columns]
  13.  
  14. ;------------------------------------;
  15. ; BIOS_SEG IS THE ROM-BIOS DATA AREA ;
  16. ;------------------------------------;
  17. BIOS_SEG    SEGMENT    AT 0040H
  18.         ORG    004AH
  19. CRT_COLS    DB    ?        ;CURRENT NUMBER OF COLUMNS
  20.         ORG    0050H
  21. CURSOR_POSN    DW    8 DUP(?)    ;CURRENT CURSOR LOCATION
  22.         ORG    0062H
  23. ACTIVE_PAGE    DB    ?        ;ACTIVE PAGE FOR CGA AND EGA
  24.         ORG    0084H
  25. ROWS        DB    ?        ;LAST ROW NUMBER FOR EGA
  26. BIOS_SEG    ENDS
  27.  
  28. CSEG        SEGMENT
  29.         ASSUME    CS:CSEG,DS:NOTHING
  30.         ORG    0100H        ;BEGINNING FOR .COM PROGRAMS
  31. START:        JMP    INITIALIZE    ;INITIALIZATION CODE IS AT END
  32.  
  33. ;--------------------------------;
  34. ; DATA AREA USED BY THIS PROGRAM ;
  35. ;--------------------------------;
  36. HOTKEY        EQU    11H        ;SCAN CODE FOR "W" KEY
  37. SHIFT_MASK    EQU    00001000B    ;MASK FOR ALT KEY
  38. ;
  39. COPYRIGHT    DB    "SNIPPER 1.0 (c) 1987 Ziff Communications Co."
  40.         DB    13,10,"Hotkey is ALT-W",13,10,"$",1AH
  41. PROGRAMMER    DB    "Tom Kihlken"
  42. INSTALLED_MSG    DB    "Already Installed",13,10,"$"
  43. BAD_DOS_MSG    DB    "Requires DOS 2.0+",13,10,"$"
  44. OLDINT09    DD    ?    ;OLD KEYBOARD BREAK INTERRUPT VECTOR
  45. OLDINT13    DD    ?    ;OLD BIOS DISK IO INTERRUPT VECTOR
  46. OLDINT16    DD    ?    ;OLD KEYBOARD INTERRUPT VECTOR
  47. OLDINT21    DD    ?    ;OLD DOS FUNCTION INTERRUPT VECTOR
  48. ERR_STAT    DB    ?    ;ERROR STATUS DURING FILE OUTPUT
  49. FILE_PROMPT    DB    "Enter Filename: "
  50. FILENAME    DB    "SCREEN.CUT"    ;THE DEFAULT FILENAME
  51.         DB    15 DUP (0)    ;LEAVE ROOM FOR DRIVE AND PATH
  52. BUFF_NEXT    DW      BUFF_START    ;POINTER TO NEXT KEY IN BUFFER
  53. BUFF_LAST    DW      BUFF_START    ;POINTER TO LAST KEY IN BUFFER
  54. BUFF_START    EQU    OFFSET INITIALIZE
  55. BUFF_SIZE    EQU    25*(80+2)    ;ROOM FOR 25 ROWS OF 80 COLUMNS
  56. BUFF_END    DW    BUFF_START+BUFF_SIZE
  57. TOP_LEFT    LABEL    WORD        ;FIRST CORNER OF WINDOW
  58. LEFT_SIDE    DB    0        ;COLUMN NUMBER OF LEFT SIDE
  59. TOP_ROW        DB    0        ;ROW NUMBER OF TOP SIDE
  60. BOT_RIGHT    LABEL    WORD        ;SECOND CORNER OF WINDOW
  61. RIGHT_SIDE    DB    ?        ;COLUMN NUMBER OR RIGHT SIDE
  62. BOT_ROW        DB    ?        ;ROW NUMBER OF BOTTOM
  63. SEND_CHAR    DW    ?        ;POINTER TO CHARACTER HANDLER
  64. SEND_KEYS    DB    0        ;IF=1, USE KEYSTROKES FROM BUFFER
  65. WRIT_FILE    DB    0        ;IF=1, NEED TO WRITE TO DISK
  66. BUSY_FLAGS    DB    0        ;BIT MASKED AS FOLLOWS:
  67.                     ; 1 - DOS IS ACTIVE
  68.                     ; 2 - BIOS IO IS ACTIVE
  69.                     ; 4 - SNIPPER IS ACTIVE
  70. DOS_STAT    DB    0        ;CURRENT DOS FUNCTION
  71.  
  72. HELP_MENU    DB    201,10 DUP(205),187
  73.         DB    186," F - File ",186
  74.         DB    186," P - Print",186
  75.         DB    186," S - Save ",186
  76.         DB    186," G - Get  ",186
  77.         DB    186,"Esc- Quit ",186
  78.         DB    200,10 DUP(205),188
  79.  
  80. ;------------------------------------------------------------------;
  81. ; SNIPPER BUILDS THE WINDOW AND ACCEPTS COMMANDS FROM THE KEYBOARD ;
  82. ;------------------------------------------------------------------;
  83. SNIPPER        PROC    NEAR
  84.         ASSUME    DS:CSEG, ES:BIOS_SEG
  85.         XOR    BX,BX        ;BX IS INCREMENT FOR ROW/COLUMN
  86. GET_KB_KEY1:
  87.         MOV    DX,TOP_LEFT    ;GET LOCATION OF FIRST CORNER
  88.         ADD    DH,BH        ;ADD IN THE ROW INCREMENT
  89.         ADD    DL,BL        ;ADD IN THE COLUMN INCREMENT
  90.         CMP    DL,0        ;AT LEFT EDGE OF SCREEN?
  91.         JGE    NOT_LEFT_EDGE
  92.         MOV    DL,CRT_COLS    ;JUMP TO THE RIGHT EDGE
  93.         DEC    DL
  94. NOT_LEFT_EDGE:
  95.         CMP    DL,CRT_COLS    ;AT RIGHT EDGE OF SCREEN YET?
  96.         JB    NOT_RIGHT_EDGE    ;IF NOT, KEEP MOVING RIGHT
  97.         XOR    DL,DL        ;IF YES, WRAP TO LEFT EDGE
  98. NOT_RIGHT_EDGE:
  99.         CMP    DH,0        ;AT TOP OF SCREEN YET?
  100.         JGE    NOT_AT_TOP
  101.         MOV    DH,ROWS        ;JUMP DOWN TO THE BOTTOM
  102. NOT_AT_TOP:
  103.         CMP    DH,ROWS        ;AT BOTTOM OF SCREEN?
  104.         JLE    NOT_AT_BOTTOM
  105.         XOR    DH,DH        ;JUMP BACK TO THE TOP
  106. NOT_AT_BOTTOM:
  107.         MOV    TOP_LEFT,DX    ;SAVE NEW CORNER LOCATION
  108.         CALL    REV_VIDEO    ;CHANGE IT TO REVERSE VIDEO
  109.         XOR    AH,AH        ;BIOS KEYBOARD INPUT
  110.         INT    16H        ;GET A KEYSTROKE
  111.         PUSH    AX
  112.         CALL    REV_VIDEO    ;PUT ATTRIBUTE BACK TO NORMAL
  113.         POP    AX
  114.         CMP    AH,1        ;IS IT ESCAPE?
  115.         JNE    NOT_ESC
  116.         RET            ;JUST RETURN TO EXIT
  117. NOT_ESC:
  118.         MOV    BX,0FF00H    ;INCREMENT TO SUBTRACT ONE ROW
  119.         CMP    AH,48H        ;IS IT UP ARROW?
  120.         JE    GET_KB_KEY1
  121.         MOV    BX,0100H    ;INCREMENT TO ADD ONE ROW
  122.         CMP    AH,50H        ;IS IT DOWN ARROW?
  123.         JE    GET_KB_KEY1
  124.         MOV    BX,0001H    ;INCREMENT TO ADD ONE COLUMN
  125.         CMP    AH,4DH        ;IS IT RIGHT ARROW?
  126.         JE    GET_KB_KEY1
  127.         MOV    BX,00FFH    ;INCREMENT TO SUBTRACT ONE COLUMN
  128.         CMP    AH,4BH        ;IS IT LEFT ARROW?
  129.         JE    GET_KB_KEY1
  130.         XOR    BX,BX
  131.         CMP    AL,13        ;IS IT A CARRIAGE RETURN?
  132.         JNE    NOT_CR
  133.         MOV    DX,TOP_LEFT    ;A CARRIAGE RETURN WAS PRESSED
  134.         MOV    BOT_RIGHT,DX    ;INITIALIZE THE SECOND CORNER
  135.         CALL    REV_VIDEO    ;CHANGE IT BACK TO REVERSE VIDEO
  136.         JMP    SHORT GET_KB_KEY2
  137. NOT_CR:
  138.         CMP    AH,22H        ;IS IT THE "G" KEY
  139.         JE    TYPE_BUFF    ;IF YES, THAN GET THE WINDOW
  140.         JMP    GET_KB_KEY1    ;JUST GET ANOTHER KEY
  141. TYPE_BUFF:
  142.         MOV    SEND_KEYS,1    ;SIGNAL TO SEND THE KEYS
  143.         RET
  144. GET_KB_KEY2:
  145.         XOR    AH,AH
  146.         INT    16H        ;GET A KEYSTROKE
  147. GOT_KEY2:    MOV    DX,BOT_RIGHT
  148.         CMP    AH,48H        ;IS IT UP ARROW?
  149.         JE    SUB_ROW        ;SUBTRACT A ROW FROM WINDOW
  150.         CMP    AH,50H        ;IS IT DOWN ARROW?
  151.         JE    ADD_ROW        ;ADD A ROW TO THE WINDOW
  152.         CMP    AH,4DH        ;IS IT RIGHT ARROW?
  153.         JE    ADD_COL        ;ADD A COLUMN TO THE WINDOW
  154.         CMP    AH,4BH        ;IS IT LEFT ARROW?
  155.         JE    SUB_COL        ;SUBTRACT A COLUMN FROM WINDOW
  156.         JMP    NOT_ARROW_KEY
  157. SUB_COL:
  158.         DEC    DL        ;SUBTRACT A COLUMN
  159.         CMP    DL,LEFT_SIDE    ;DONT ERASE IT COMPLETELY
  160.         JL    GET_KB_KEY2
  161.         MOV    RIGHT_SIDE,DL    ;SAVE NEW RIGHT SIDE COLUMN
  162.         INC    DL
  163.         JMP    SHORT COL_LOOP
  164. ADD_COL:
  165.         INC    DL        ;ADD A COLUMN
  166.         CMP    DL,CRT_COLS    ;AT RIGHT EDGE OF SCREEN?
  167.         JAE    GET_KB_KEY2    ;STOP WHEN SCREEN IS FILLED
  168.         MOV    RIGHT_SIDE,DL    ;SAVE NEW RIGHT SIDE COLUMN
  169. COL_LOOP:
  170.         CALL    REV_VIDEO    ;REVERSE THIS CHARACTER
  171.         DEC    DH        ;MOVE TO NEXT ROW
  172.         CMP    DH,TOP_ROW    ;AT TOP ROW YET?
  173.         JGE    COL_LOOP    ;LOOP UNTIL AT TOP ROW
  174.         JMP    GET_KB_KEY2
  175. SUB_ROW:
  176.         DEC    DH
  177.         CMP    DH,TOP_ROW    ;AT TOP OF WINDOW?
  178.         JL    GET_KB_KEY2    ;DONT ERASE IT COMPLETELY
  179.         MOV    BOT_ROW,DH
  180.         INC    DH
  181.         JMP    SHORT ROW_LOOP
  182. ADD_ROW:
  183.         INC    DH
  184.         CMP    DH,ROWS        ;AT BOTTOM OF SCREEN?
  185.         JG    GET_KB_KEY2    ;STOP WHEN SCREEN IS FILLED
  186.         MOV    BOT_ROW,DH
  187. ROW_LOOP:
  188.         CALL    REV_VIDEO    ;REVERSE THIS CHARACTER
  189.         DEC    DL        ;MOVE TO NEXT COLUMN
  190.         CMP    DL,LEFT_SIDE    ;AT LEFT EDGE YET?
  191.         JGE    ROW_LOOP    ;CONTINUE UNTIL AT LEFT EDGE
  192.         JMP    GET_KB_KEY2
  193. NOT_ARROW_KEY:
  194.         CMP    AH,19H        ;WAS IT THE "P" KEY?
  195.         JNE    NOT_P
  196.         MOV    SEND_CHAR,OFFSET PRINT_CHAR
  197.         JMP    READ_WINDOW
  198. NOT_P:
  199.         MOV    BUFF_NEXT,BUFF_START
  200.         MOV    BUFF_LAST,BUFF_START
  201.         MOV    SEND_CHAR,OFFSET BUFF_CHAR
  202.  
  203.         CMP    AH,1FH        ;WAS IT THE "S" KEY?
  204.         JNE    NOT_S
  205.         MOV    SEND_CHAR,OFFSET BUFF_CHAR
  206.         JMP    READ_WINDOW
  207. NOT_S:
  208.         CMP    AH,22H        ;IS IT THE "G" KEY
  209.         JNE    NOT_G
  210.         MOV    SEND_KEYS,1
  211.         JMP    READ_WINDOW
  212. NOT_G:
  213.         CMP    AH,21H        ;IS IT THE "F" KEY
  214.         JNE    NOT_F
  215.         MOV    WRIT_FILE,0
  216.         CALL    GET_FILENAME
  217.         CMP    WRIT_FILE,-1    ;WAS ESCAPE REQUESTED?
  218.         JE    ERASE_BOX
  219.         CALL    READ_WINDOW
  220.         MOV    WRIT_FILE,1
  221.         TEST    BUSY_FLAGS,00000011B     ;IS INT21 OR INT13 BUSY?
  222.         JNZ    RETURN        ;IF YES, WAIT TILL LATER
  223.         CALL    WRITE_TO_FILE    ;IF NOT, DO IT NOW
  224. RETURN:
  225.         RET
  226. NOT_F:
  227.         CMP    AH,1        ;IS IT ESCAPE?
  228.         JE    ERASE_BOX ;IF YES, ERASE BOX AND EXIT
  229.         CMP    AL,13        ;IS IT A CARRIAGE RETURN?
  230.         JE    DISPLAY_HELP    ;IF YES, DISPLAY HELP
  231.         JMP    GET_KB_KEY2    ;OTHERWISE JUST GET ANOTHER KEY
  232. ERASE_BOX:
  233.         MOV    SEND_CHAR,OFFSET RETURN
  234.         JMP    READ_WINDOW
  235. DISPLAY_HELP:
  236.         CALL    EXCHANGE_HELP    ;PUT UP THE HELP MENU
  237.         XOR    AH,AH
  238.         INT    16H        ;GET ANOTHER KEYSTROKE
  239.         PUSH    AX        ;SAVE THE KEYSTROKE
  240.         CALL    EXCHANGE_HELP    ;PULL DOWN THE HELP MENU
  241.         POP    AX        ;GET BACK THE KEYSTROKE
  242.         JMP    GOT_KEY2
  243. ;*********************************************************************
  244. REV_VIDEO:
  245.         CALL    READ_CHAR    ;READ CHARACTER AND ATTRIBUTE
  246.         MOV    BL,AH        ;SAVE ATTRIBUTE IN BL
  247.         AND    BL,10001000B    ;GET BLINK AND INTENSITY BITS
  248.         AND    AH,01110111B    ;NOW LOOK ONLY AT COLOR BITS
  249.         MOV    CL,4        ;ROTATE FOUR COUNTS
  250.         ROR    AH,CL        ;ROTATE FOREGROUND AND BACKGROUND
  251.         OR    BL,AH        ;PUT BACK BLINK AND INTENSITY BITS
  252.         CALL    DISPLAY_CHAR    ;WRITE CHARACTER AND ATTRIBUTE
  253.         RET
  254. ;*********************************************************************
  255. READ_WINDOW:
  256.         MOV    DX,TOP_LEFT    ;GET LOCATION OF FIRST CORNER
  257. READ_LOOP:
  258.         CALL    REV_VIDEO    ;PUT ATTRIBUTE BACK TO NORMAL
  259.         CALL    READ_CHAR    ;READ THE CHARACTER
  260.         CALL    SEND_CHAR    ;CALL TO THE POINTER
  261.         INC    DL        ;NEXT CHAR IN ROW
  262.         CMP    DL,RIGHT_SIDE    ;AT THE RIGHT BORDER YET?
  263.         JLE    READ_LOOP    ;DO ALL CHARACTERS IN THIS ROW
  264.         CALL    CR_LF        ;SEND CR-LF AFTER EACH ROW
  265.         INC    DH        ;MOVE TO NEXT ROW
  266.         MOV    DL,LEFT_SIDE    ;BACK TO LEFT EDGE
  267.         CMP    DH,BOT_ROW    ;AT THE BOTTOM BORDER YET?
  268.         JLE    READ_LOOP    ;READ ENTIRE WINDOW
  269.         RET
  270. ;*********************************************************************
  271. CR_LF:
  272.         MOV    AL,13
  273.         CALL    SEND_CHAR    ;SEND A CARRIAGE RETURN
  274.         MOV    AL,10
  275.         CALL    SEND_CHAR    ;SEND A LINE FEED
  276.         RET
  277. ;*********************************************************************
  278. DISPLAY_CHAR:
  279.         PUSH    BX        ;SAVE THE ATTRIBUTE
  280.         CALL    GET_CURS_ADDR    ;GET ADDRESS OF BIOS CURSOR
  281.         MOV    ES:[BX],DX    ;TELL BIOS WHERE THE CURSOR IS
  282.         POP    BX        ;GET BACK THE ATTRIBUTE
  283.         MOV    BH,ACTIVE_PAGE    ;GET ACTIVE PAGE
  284.         PUSH    CX        ;SAVE THE LOOP COUNT
  285.         MOV    CX,1        ;WRITE 1 CHARACTER
  286.         MOV    AH,9        ;WRITE CHARACTER AND ATTRIBUTE
  287.         INT    10H
  288.         POP    CX        ;RECOVER LOOP COUNT
  289.         RET            ;DONE WRITING THE CHARACTER
  290. ;*********************************************************************
  291. READ_CHAR:
  292.         CALL    GET_CURS_ADDR    ;GET ADDRESS OF BIOS CURSOR
  293.         MOV    ES:[BX],DX    ;TELL BIOS WHERE THE CURSOR IS
  294.         MOV    BH,ACTIVE_PAGE    ;GET ACTIVE PAGE
  295.         MOV    AH,8        ;BIOS FUNCTION TO READ CHARACTER
  296.         INT    10H        ;READ THE CHARACTER/ATTRIBUTE
  297.         RET
  298. ;*********************************************************************
  299. PRINT_CHAR:
  300.         PUSH    DX
  301.         XOR    AH,AH        ;USE FUNCTION 0
  302.         XOR    DX,DX        ;PRINTER NUMBER 0
  303.         INT    17H        ;BIOS PRINT CHARACTER FUNCTION
  304.         ROR    AH,1        ;LOOK AT BIT ZERO
  305.         JNC    PRINT_OK    ;DID A TIMEOUT OCCUR?
  306.         MOV    SEND_CHAR,OFFSET RETURN
  307. PRINT_OK:
  308.         POP    DX
  309.         RET            ;DONE PRINTING CHARACTER
  310. ;*********************************************************************
  311. BUFF_CHAR:
  312.         MOV    BX,BUFF_LAST    ;GET LOCATION OF LAST CHARACTER
  313.         MOV    [BX],AL        ;PUT THE CHARACTER IN BUFFER
  314.         INC    BX        ;ADVANCE THE POINTER
  315.         MOV    BUFF_LAST,BX    ;CHECK FOR BUFFER FULL
  316.         CMP    BX,BUFF_END    ;IS THE BUFFER FULL YET?
  317.         JNE    BUFF_OK        ;IF NOT, KEEP GOING
  318.         MOV    SEND_CHAR,OFFSET RETURN
  319. BUFF_OK:
  320.         RET            ;NOW ITS IN THE BUFFER
  321. ;*********************************************************************
  322. GET_CURS_ADDR:
  323.         MOV    BL,ACTIVE_PAGE    ;GET THE CURRENT PAGE NUMBER
  324.         XOR    BH,BH        ;CONVERT TO A WORD OFFSET
  325.         SHL    BX,1        ;TIMES TWO FOR A WORD
  326.         ADD    BX,OFFSET CURSOR_POSN ;ADD IN BASE ADDRESS
  327.         RET
  328. ;*********************************************************************
  329. EXCHANGE_HELP:
  330.         XOR    DX,DX        ;START AT TOP LEFT CORNER
  331.         LEA    SI,HELP_MENU
  332. EXCHANGE_LOOP:
  333.         CMP    DL,12        ;AT LAST COLUMN IN THIS ROW YET?
  334.         JL    SWAP_CHAR
  335.         XOR    DL,DL        ;BACK TO FIRST COLUMN
  336.         INC    DH        ;DO THE NEXT ROW
  337.         CMP    DH,7        ;AT LAST ROW YET?
  338.         JL    SWAP_CHAR    ;QUIT WHEN LAST ROW IS DONE
  339.         RET
  340. SWAP_CHAR:
  341.         CALL    READ_CHAR    ;READ CHARACTER AT THIS POSITION
  342.         XCHG    AL,CS:[SI]    ;SWAP WITH THE HELP TEXT
  343.         MOV    BL,AH        ;ATTRIBUTE IS THE SAME
  344.         CALL    DISPLAY_CHAR    ;PUT NEW CHARACTER ON SCREEN
  345.         INC    DL        ;POINT TO NEXT POSITION
  346.         INC    SI
  347.         JMP EXCHANGE_LOOP
  348. ;*********************************************************************
  349. GET_FILENAME:
  350.         LEA    SI,FILE_PROMPT    ;POINT TO THE PROMPT FOR SOURCE
  351.         XOR    DI,DI        ;USE THE PSP FOR BUFFER
  352.         XOR    DX,DX        ;PUT PROMPT AT TOP LEFT CORNER
  353.         MOV    CX,40        ;USE MAX OF 40 CHARACTERS
  354. DISPLAY_PROMPT:
  355.         PUSH    CX        ;SAVE LOOP COUNT
  356.         CALL    READ_CHAR    ;GET CHARACTER ON THIS LINE
  357.         MOV    CS:[DI],AX    ;STORE IT IN THE PSP
  358.         INC    DI        ;ADD TWO FOR NEXT CHARACTER
  359.         INC    DI
  360.         MOV    AL,CS:[SI]    ;GET NEXT PROMPT CHARACTER
  361.         INC    SI        ;NEXT CHARACTER IN PROMPT
  362.         MOV    BL,47H        ;ATTRIBUTE FOR PROMPT
  363.         CALL    DISPLAY_CHAR    ;PUT UP THE PROMPT CHARACTER
  364.         INC    DL        ;POINT TO NEXT COLUMN
  365.         POP    CX        ;GET BACK LOOP COUNT
  366.         LOOP    DISPLAY_PROMPT    ;ENTIRE PROMPT AND FILENAME
  367. FIND_LAST_LETTER:
  368.         DEC    SI        ;BACKUP TO LAST LETTER
  369.         DEC    DL        ;BACKUP TO LAST COLUMN
  370.         CMP    BYTE PTR [SI],0    ;IS THIS A LETTER?
  371.         JE    FIND_LAST_LETTER;BACKUP UNTILL A LETTER IS FOUND
  372.         INC    DL        ;PUT BLINKING BOX AT LAST LETTER
  373. READ_KB:
  374.         MOV    AL,219        ;ASCII FOR BOX CHARACTER
  375.         MOV    BL,47H+80H    ;MAKE IT A BLINKING BOX CHARACTER
  376.         CALL    DISPLAY_CHAR    ;WRITE THE BLINKING BOX
  377. ;
  378.         XOR    AH,AH        ;FUNCTIO 0 TO GET NEXT KEY
  379.         INT    16H        ;BIOS KEYBOARD INPUT
  380.         CMP    AL,13        ;IS IT A CARRIAGE RETURN?
  381.         JE    ERASE_PROMPT
  382.         CMP    AL,8        ;IS IT A BACKSPACE?
  383.         JE    BACK_SPACE
  384.         CMP    AH,1        ;IS IT ESCAPE?
  385.         JE    ESC_RET
  386.         CMP    AL,"."        ;IS IT A VALID LETTER?
  387.         JL    READ_KB
  388.         CMP    AL,"z"        ;IS IT A VALID LETTER?
  389.         JG    READ_KB
  390.         CMP    DL,39        ;ONLY ALLOW 40 CHARACTERS
  391.         JGE    READ_KB
  392. TTY_KEY:
  393.         MOV    BL,47H        ;ATTRIBUTE FOR FILENAME
  394.         CALL    DISPLAY_CHAR    ;WRITE THE LETTER
  395.         INC    DL        ;MOVE TO NEXT COLUMN
  396.         JMP    READ_KB        ;GET ANOTHER KEYSTROKE
  397. BACK_SPACE:
  398.         CMP    DL,16        ;AT BEGINNING OF LINE?
  399.         JLE    READ_KB        ;IF YES, CAN'T BACKUP FROM HERE
  400.         MOV    AL,0        ;WRITE A NORMAL BLANK (ASCII 0)
  401.         MOV    BL,47H        ;ATTRIBUTE FOR FILENAME
  402.         CALL    DISPLAY_CHAR    ;WRITE THE LETTER
  403.         DEC    DL        ;BACKUP THE CURSOR
  404.         JMP    READ_KB        ;THEN GET THE NEXT KEY
  405. ESC_RET:
  406.         MOV    WRIT_FILE,-1    ;INDICATE ESCAPE IS REQUESTED
  407. ERASE_PROMPT:
  408.         XOR    AL,AL        ;GET RID OF THE CURSOR
  409.         CALL    DISPLAY_CHAR    ;WRITE THE LETTER
  410.         LEA    DI,FILE_PROMPT    ;COPY TO FILENAME
  411.         XOR    SI,SI        ;COPY FROM PSP
  412.         XOR    DX,DX        ;PROMPT IS AT ROW ZERO
  413.         MOV    CX,40        ;COPY ALL 40 CHARACTERS
  414. ERASE_LOOP:
  415.         CALL    READ_CHAR    ;GET CHARACTER ON THIS LINE
  416.         MOV    CS:[DI],AL    ;PUT IN BACK IN MEMORY
  417.         INC    DI
  418.         MOV    AX,CS:[SI]    ;GET THE ORIGINAL CHARACTER BACK
  419.         MOV    BL,AH        ;PUT ATTRIBUTE INTO BL
  420.         INC    SI
  421.         INC    SI
  422.         CALL    DISPLAY_CHAR    ;WRITE ORIGINAL CHARACTER
  423.         INC    DL        ;MOVE TO NEXT COLUMN
  424.         LOOP    ERASE_LOOP    ;ERASE THE ENTIRE PROMPT
  425.         RET
  426. SNIPPER        ENDP
  427.  
  428. ;---------------------------------------------------------------------;
  429. ; THIS COPIES THE BUFFER CONTENTS TO A FILE. IT SHOULD ONLY BE CALLED ;
  430. ; WHEN DOS IS IN A STABLE AND REENTRANT CONDITION.                    ;
  431. ;---------------------------------------------------------------------;
  432. WRITE_TO_FILE    PROC    NEAR
  433.         ASSUME    DS:NOTHING, ES:NOTHING
  434.  
  435.         MOV    WRIT_FILE,0    ;TURN OFF REQUEST FLAG
  436.         PUSH    AX        ;MUST PRESERVE ALL REGISTERS
  437.         PUSH    BX
  438.         PUSH    CX
  439.         PUSH    DX
  440.         PUSH    DS
  441.         PUSH    ES
  442.         PUSH    CS
  443.         POP    DS
  444.         ASSUME    DS:CSEG        ;DS POINTS TO OUR CODE SEGMENT
  445.              MOV    AX,3524H    ;GET DOS CRITICAL ERROR VECTOR
  446.         INT    21H        ;DOS FUNCTION TO GET VECTOR
  447.         PUSH    BX        ;SAVE OLD VECTOR ON STACK
  448.         PUSH    ES
  449.  
  450. ; REPLACE THE DOS SEVERE ERROR INTERRUPT WITH OUR OWN ROUTINE.
  451.  
  452.         MOV    DX,OFFSET NEWINT24
  453.         MOV    AX,2524H    ;SETUP TO CHANGE INT 24h VECTOR
  454.         INT    21H        ;CHANGE DOS SEVERE ERROR VECTOR
  455.         MOV    DX,OFFSET FILENAME ;POINT TO FILENAME
  456.  
  457. ; FIRST TRY TO OPEN THE FILE.  IF DOS RETURNS WITH THE CARRY FLAG SET,
  458. ; THE FILE DIDN'T EXIST AND WE MUST CREATE IT.  ONCE THE FILE IS OPENED,
  459. ; ADVANCE THE FILE POINTER TO THE END OF FILE TO APPEND.
  460.  
  461.         MOV    AX,3D02H    ;DOS FUNCTION TO OPEN FILE
  462.         INT    21H        ;DOS WILL RETURN WITH CARRY FLAG
  463.         JC    FILE_NOT_FOUND    ;SET IF FILE DOESN'T EXIST.
  464.         MOV    BX,AX        ;KEEP HANDLE IN BX ALSO
  465.         XOR    CX,CX        ;MOVE DOS FILE POINTER TO THE
  466.         XOR    DX,DX        ;END OF THE FILE. THIS LETS US
  467.         MOV    AX,4202H    ;APPEND THIS TO AN EXISTING FILE
  468.         INT    21H        ;DOS FUNCTION TO MOVE POINTER
  469.         JNC    WRITE_FILE    ;IF NO ERROR, CONTINUE TO WRITE
  470. DOS_ERROR:
  471.         CMP    ERR_STAT,0    ;DID A SEVERE ERROR OCCUR?
  472.         JNE    REP_VECTOR    ;IF SEVERE ERROR, JUST QUIT
  473.         JMP    SHORT CLOSE_FILE;JUST CLOSE THE FILE
  474.  
  475. FILE_NOT_FOUND:    CMP    ERR_STAT,0    ;DID A SEVERE ERROR OCCUR?
  476.         JNE    REP_VECTOR    ;IF SEVERE ERROR, JUST QUIT
  477.         MOV    CX,0020H    ;ATTRIBUTE FOR NEW FILE
  478.         MOV    AH,3CH        ;CREATE FILE FOR WRITING
  479.         INT    21H        ;DOS FUNCTION TO CREATE FILE
  480.         JC    DOS_ERROR       ;ON ANY ERROR, TAKE JUMP
  481.         MOV    BX,AX        ;SAVE HANDLE IN BX
  482. WRITE_FILE:     MOV    DX,BUFF_START    ;POINT TO BUFFER
  483.         MOV    CX,BUFF_LAST    ;GET BUFFER POINTER
  484.         SUB    CX,DX        ;NUMBER OF CHARS IN BUFFER
  485.         MOV    AH,40H        ;DOS WRITE TO A DEVICE FUNCTION
  486.         INT    21H        ;WRITE TO THE FILE
  487. CLOSE_FILE:
  488.         MOV    AH,3EH        ;DOS FUNCTION TO CLOSE THE FILE
  489.         INT    21H
  490. REP_VECTOR:
  491.         POP    DS        ;GET INT 24H VECTOR FROM STACK
  492.         POP    DX
  493.         MOV    AX,2524H    ;RESTORE CRITICAL ERROR VECTOR
  494.         INT    21H        ;DOS FUNCTION TO CHANGE VECTOR
  495.         POP    ES        ;FINALLY RESTORE ALL REGISTERS
  496.         POP    DS
  497.         POP    DX
  498.         POP    CX
  499.         POP    BX
  500.         POP    AX
  501.         RET            ;FINISHED WRITING TO DISK
  502. WRITE_TO_FILE    ENDP
  503. ;---------------------------------------------------------------------;
  504. ; INTERRUPT 09 ROUTINE.  WATCH FOR TRIGGER KEY TO POP UP.
  505. ;---------------------------------------------------------------------;
  506. NEWINT09    PROC    FAR
  507.         ASSUME    DS:NOTHING, ES:NOTHING
  508.         STI            ;ALLOW OTHER INTERRUPTS
  509.         PUSH    AX        ;MUST SAVE PROCESSOR STATE
  510.         IN    AL,60H        ;GET THE SCAN CODE
  511.         CMP    AL,HOTKEY    ;IS IT THE HOT KEY?
  512.         JE    TRIGGER        ;IF YES, CHECK THE MASK
  513. INT09_EXIT:    POP    AX        ;RESTORE THE PROCESSOR STATE
  514.         JMP    OLDINT09    ;CONTINUE WITH ROM ROUTINE
  515.  
  516. TRIGGER:    MOV    AH,2        ;GET KEYBOARD STATUS
  517.         INT    16H        ;BIOS KEYBOARD SERVICE
  518.  
  519.         AND    AL,0FH        ;Take lo er four bits
  520.         CMP    AL,SHIFT_MASK    ;IS ALT KEY DOWN?
  521.         JNZ    INT09_EXIT    ;IF NOT, IGNORE IT
  522.         TEST    BUSY_FLAGS,00000100B ;IS SNIPPER ALREADY ACTIVE?
  523.         JNZ    INT09_EXIT    ;IF ACTIVE, THEN EXIT
  524.         OR    BUSY_FLAGS,00000100B ;ITS ACTIVE NOW
  525.         PUSHF
  526.         CALL    OLDINT09    ;LET ROM PROCESS THE KEY
  527.         PUSH    BX        ;MUST PRESERVE ALL REGISTERS
  528.         PUSH    CX
  529.         PUSH    DX
  530.         PUSH    BP
  531.         PUSH    SI
  532.         PUSH    DI
  533.         PUSH    DS
  534.         PUSH    ES
  535.         PUSH    CS
  536.         POP    DS        ;SET DS TO CSEG
  537.         MOV    AX,BIOS_SEG    ;ES POINTS TO BIOS DATA AREA
  538.         MOV    ES,AX
  539.         ASSUME    DS:CSEG, ES:BIOS_SEG
  540.         CALL    GET_CURS_ADDR    ;CURSOR ADDRESS FOR THIS PAGE
  541.         PUSH    ES:[BX]        ;SAVE THE CURSOR LOCATION
  542.         CALL    SNIPPER        ;DO THE WINDOW
  543.         CALL    GET_CURS_ADDR    ;CURS0R ADDRESS FOR THIS PAGE
  544.         POP    ES:[BX]        ;GET BACK CURSOR  POSITION
  545.         AND    BUSY_FLAGS,11111011B  ;SNIPPER IS NOT ACTIVE
  546.         POP    ES        ;RESTORE ALL REGISTERS
  547.         POP    DS
  548.         POP    DI
  549.         POP    SI
  550.         POP    BP
  551.         POP    DX
  552.         POP    CX
  553.         POP    BX
  554.         POP    AX
  555.         IRET            ;NOW WERE ALL DONE
  556. NEWINT09    ENDP
  557. ;---------------------------------------------------------------------;
  558. ; INTERRUPT 13 ROUTINE. SET BIOS BUST BIT                             ;
  559. ;---------------------------------------------------------------------;
  560. NEWINT13    PROC    FAR
  561.         ASSUME    DS:NOTHING, ES:NOTHING
  562.         OR    BUSY_FLAGS,00000010B    ;SET BIOS BUSY BIT
  563.         PUSHF
  564.         CALL    OLDINT13    ;DO THE BIOS FUNCTION
  565.         PUSHF            ;SAVE RESULT FLAGS
  566.         AND    BUSY_FLAGS,11111101B    ;CLEAR BIOS BUSY BIT
  567.         POPF            ;GET BACK RESULT FLAGS
  568.         STI            ;MUST RETURN WITH INTERUPTS ON
  569.         RET    2        ;RETURN BIOS RESULT FLAGS
  570. NEWINT13    ENDP
  571. ;---------------------------------------------------------------------;
  572. ; INTERRUPT 16 ROUTINE. INSERT KEYSTROKES FROM BUFFER                 ;
  573. ;---------------------------------------------------------------------;
  574. NEWINT16    PROC    FAR
  575.         ASSUME    DS:NOTHING, ES:NOTHING
  576.         PUSH    BX
  577.         CMP    SEND_KEYS,1    ;SENDING KEYS FROM BUFFER?
  578.         JE    INSERT_KEY    ;IF YES, THEN GET NEXT ONE
  579.         CMP    WRIT_FILE,1    ;ANYTHING TO WRITE TO DISK?
  580.         JE    CHECK_DOS_STAT    ;IF YES, THIS IS THE TIME
  581. BIOS_KB:
  582.         POP    BX
  583.         JMP    OLDINT16    ;JUST DO NORMAL KB ROUTINE
  584. CHECK_DOS_STAT:
  585.         CMP    DOS_STAT,0AH    ;DOING READ STRING?
  586.         JE    BEGIN_NOW    ;IF YES, ITS SAFE TO BEGIN
  587.         CMP    DOS_STAT,8    ;DOING KEYBOARD INPUT?
  588.         JNE    BIOS_KB        ;IF YES, ITS SAFE TO BEGIN
  589. BEGIN_NOW:
  590.         STI            ;GET INTERRUPTS BACK ON
  591.         CALL    WRITE_TO_FILE    ;EMPTY THE BUFFER
  592.         JMP    BIOS_KB        ;CONTINUE WITH BIOS ROUTINE
  593. INSERT_KEY:
  594.         STI            ;INTERRUPTS BACK ON
  595.         MOV    BX,BUFF_NEXT    ;GET ADDRESS OF NEXT BYTE
  596.         CMP    BX,BUFF_LAST    ;AT END OF BUFFER YET?
  597.         JL    GET_A_KEY    ;IF NOT, GET THE NEXT ONE
  598.         MOV    SEND_KEYS,0    ;WHEN DONE, TURN OFF SEND SWITCH
  599. GET_A_KEY:
  600.         MOV    AL,CS:[BX]    ;GET THE NEXT KEY CODE
  601.         CMP    AL,10        ;IS IT A LINE FEED?
  602.         JNE    NOT_LF        ;DONT RETURN THE LINE FEEDS
  603.         INC    BUFF_NEXT    ;SKIP TO NEXT KEY
  604.         JMP    INSERT_KEY
  605. NOT_LF:
  606.         CMP    AH,1        ;REQUEST FOR STATUS ONLY?
  607.         JE    RETURN_STATUS    ;IF YES, RETURN STATUS ONLY
  608.         CMP    AH,0        ;REQUEST TO GET THE NEXT KEY
  609.         JNE    BIOS_KB        ;IF NOT, IGNORE THIS FUNCTION
  610.         INC    BX        ;REMOVE THIS KEY FROM OUR BUFFER
  611.         MOV    BUFF_NEXT,BX    ;SAVE THE POINTER TO NEXT KEY
  612. RETURN_STATUS:
  613.         OR    BL,1        ;CLEAR ZERO FLAG TO INDICATE A
  614.         POP    BX        ;KEY IS AVAILIABLE
  615.         RET    2        ;RETURN WITH THESE FLAGS
  616. NEWINT16    ENDP
  617. ;---------------------------------------------------------------------;
  618. ; INTERRUPT 21 ROUTINE.  THIS ROUTINE IS USED TO MONITOR DOS FUNCTION ;
  619. ; CALLS. IF THE BUFFER NEEDS TO BE FLUSHED, IT WIL BE DONE HERE.      ;
  620. ;---------------------------------------------------------------------;
  621. NEWINT21    PROC    FAR
  622.         ASSUME    DS:NOTHING, ES:NOTHING
  623.         STI
  624.         OR    AH,AH        ;DOING FUNCTION ZERO?
  625.         JNE    NOT_ZERO
  626.         MOV    AH,4CH        ;IF YES, CHANGE IT TO A 4CH
  627. NOT_ZERO:
  628.         OR    BUSY_FLAGS,00000001B    ;SET DOS BUSY BIT
  629.         MOV    DOS_STAT,AH
  630.         PUSHF            ;SIMULATE AN INTERRUPT
  631.         CALL    OLDINT21    ;DO THE DOS FUNCTION
  632.         PUSHF            ;SAVE THE RESULT FLAGS
  633.         AND    BUSY_FLAGS,11111110B    ;CLEAR DOS BUSY BIT
  634.         CMP    WRIT_FILE,1    ;ANYTHING TO WRITE TO DISK?
  635.         JNE    NO_WRITE    ;IF NOT JUST RETURN
  636.         CALL    WRITE_TO_FILE    ;SAFE TO ACCESS DISK NOW
  637. NO_WRITE:
  638.         POPF            ;RECOVER DOS RESULT FLAGS
  639.         RET    2        ;RETURN WITH DOS RESULT FLAGS
  640. NEWINT21    ENDP
  641.  
  642. ;---------------------------------------------------------------------;
  643. ; NEW INTERRUPT 24H (CRITICAL DOS ERROR).  THIS INTERRUPT IS ONLY IN  ;
  644. ; EFFECT ONLY DURING A WRITE SCREEN.  IT IS REQUIRED TO SUPPRESS THE  ;
  645. ; 'ABORT, RETRY, IGNORE' MESSAGE.  ALL FATAL DISK ERRORS ARE IGNORED. ;
  646. ;---------------------------------------------------------------------;
  647. NEWINT24    PROC    FAR
  648.         ASSUME    CS:CSEG, DS:NOTHING, ES:NOTHING
  649.         STI            ;TURN INTERRUPTS BACK ON
  650.         INC     ERR_STAT    ;SET THE ERROR FLAG
  651.          XOR    AL,AL        ;TELLS DOS TO IGNORE THE ERROR
  652.         IRET            ;THATS ALL WE DO HERE
  653. NEWINT24    ENDP
  654.  
  655. ;--------------------------------------------------------------------;
  656. ; HERE IS THE CODE USED TO INITIALIZE SNIPPER.                       ;
  657. ;--------------------------------------------------------------------;
  658.         ASSUME    CS:CSEG, DS:CSEG, ES:CSEG
  659. INITIALIZE:
  660.         LEA    DX,COPYRIGHT
  661.         MOV    AH,9        ;DOS DISPLAY STRING SERVICE
  662.         INT    21H        ;DISPLAY TITLE MESSAGE
  663. ; SEARCH FOR AN PREVIOUSLY INSTALLED COPY OF SNIPPER
  664.         NOT    BYTE PTR START    ;MODIFY TO AVOID FASLE MATCH
  665.         XOR    BX,BX        ;START SEARCH AT SEGMENT ZERO
  666.         MOV    AX,CS        ;COMPARE TO THIS CODE SEGMENT
  667. NEXT_SEGMENT:
  668.         INC    BX        ;LOOK AT NEXT SEGMENT
  669.         CMP    AX,BX        ;UNTIL REACHING THIS CODE SEG
  670.         MOV    ES,BX
  671.         JE    NOT_INSTALLED
  672.         LEA    SI,START    ;SETUP TO COMPARE STRINGS
  673.         MOV    DI,SI
  674.         MOV    CX,16        ;16 BYTES MUST MATCH
  675.         REP    CMPSB        ;COMPARE DS:SI TO ES:DI
  676.         OR    CX,CX        ;DID THE STRINGS MATCH?
  677.         JNZ    NEXT_SEGMENT    ;IF NO MATCH, TRY NEXT SEGMENT
  678.         LEA    DX,INSTALLED_MSG
  679.         JMP    SHORT ERR_EXIT
  680. NOT_INSTALLED:
  681.         MOV    AH,30H
  682.         INT    21H        ;GET DOS VERSION NUMBER
  683.         CMP    AL,2        ;IS IT HIGHER THAN 2.0?
  684.         JAE    VER_OK        ;IF YES, PROCEED
  685.         LEA    DX,BAD_DOS_MSG
  686. ERR_EXIT:    MOV    AH,9        ;DOS DISPLAY STRING SERVICE
  687.         INT    21H        ;DISPLAY ERRER MESSAGE
  688.         RET            ;RETURN TO DOS
  689. VER_OK:
  690.         INC    SI        ;POINT TO FIRST PARAMETER
  691.         MOV    SI,81H        ;POINT TO PARAMETER AREA
  692.         CALL    GET_PARAM    ;GET FIRST PARAMETER (ROWS)
  693.         PUSH    AX        ;SAVE THE ROW COUNT
  694.         CALL    GET_PARAM    ;GET SECOND PARAMETER (COLUMNS)
  695.         ADD    AX,2        ;ADD SPACE FOR CR AND LF
  696.         POP    BX        ;GET BACK FIRST PARAMETER
  697.         MUL    BX        ;PRODUCT OF ROWS AND COLUMNS
  698.         OR    AX,AX        ;WAS ANYTHING ENTERED?
  699.         JZ    NO_PARAMS    ;IF NOT, USE DEFAULT VALUE
  700.         CMP    AX,10000    ;MAXIMUM BUFFER IS 10000 BYTES
  701.         JLE    SIZE_IS_OK
  702.         MOV    AX,10000
  703. SIZE_IS_OK:
  704.         ADD    AX,BUFF_START
  705.         MOV    BUFF_END,AX    ;SET THE NEW BUFFER SIZE
  706. NO_PARAMS:
  707.         MOV    AX,BIOS_SEG    ;LOOK AT BIOS DATA AREA
  708.         MOV    ES,AX
  709.         ASSUME    ES:BIOS_SEG
  710.         CMP    ROWS,0        ;IS NUMBER OF ROWS ENTERED HERE
  711.         JNE    MUST_BE_EGA    ;IF YES, AN EGA MAY BE PRESENT
  712.         MOV    ROWS,24        ;IF NOT EGA, MUST BE 24 ROWS
  713. MUST_BE_EGA:
  714.         ASSUME    ES:NOTHING
  715.                MOV    AX,3509H    ;GET KEYBOARD BREAK VECTOR
  716.         INT    21H
  717.         MOV    WORD PTR [OLDINT09],  BX  ;SAVE SEGMENT
  718.         MOV    WORD PTR [OLDINT09+2],ES  ;SAVE OFFSET
  719.         MOV    DX, OFFSET NEWINT09
  720.         MOV    AX, 2509H
  721.         INT    21H        ;DOS FUNCTION TO CHANGE VECTOR
  722.  
  723.                MOV    AX,3513H    ;GET BIOS DISK INTERRUPT VECTOR
  724.         INT    21H
  725.         MOV    WORD PTR [OLDINT13],  BX  ;SAVE SEGMENT
  726.         MOV    WORD PTR [OLDINT13+2],ES  ;SAVE OFFSET
  727.         MOV    DX, OFFSET NEWINT13
  728.         MOV    AX, 2513H
  729.         INT    21H        ;DOS FUNCTION TO CHANGE VECTOR
  730.  
  731.                MOV    AX,3516H    ;GET KEYBOARD INPUT VECTOR
  732.         INT    21H
  733.         MOV    WORD PTR [OLDINT16],  BX  ;SAVE SEGMENT
  734.         MOV    WORD PTR [OLDINT16+2],ES  ;SAVE OFFSET
  735.         MOV    DX, OFFSET NEWINT16
  736.         MOV    AX, 2516H
  737.         INT    21H        ;DOS FUNCTION TO CHANGE VECTOR
  738.  
  739.                MOV    AX,3521H    ;GET DOS FUNCTION VECTOR
  740.         INT    21H
  741.         MOV    WORD PTR [OLDINT21],  BX
  742.         MOV    WORD PTR [OLDINT21+2],ES
  743.         MOV    DX, OFFSET NEWINT21
  744.         MOV    AX, 2521H
  745.         INT    21H        ;DOS FUNCTION TO CHANGE VECTOR
  746.  
  747. ;--------------------------------------------------------------------;
  748. ; DEALLOCATE OUR COPY OF THE ENVIORNMENT.                            ;
  749. ; EXIT USING INT 27H. LEAVE CODE AND SPACE FOR BUFFER RESIDENT.      ;
  750. ;--------------------------------------------------------------------;
  751.  
  752.         MOV    AX,DS:[002CH]    ;GET SEGMENT OF ENVIORNMENT
  753.         MOV    ES,AX        ;PUT IT INTO ES
  754.         MOV    AH,49H        ;RELEASE ALLOCATED MEMORY
  755.         INT    21H
  756.         MOV    DX,BUFF_END    ;LEAVE THIS MUCH RESIDENT
  757.         INT    27H        ;TEMINATE AND STAY RESIDENT
  758. ;---------------------------------------------------------;
  759. ; GET_PARAM RETRIEVES AN INTEGER FROM THE COMMAND LINE.   ;
  760. ;---------------------------------------------------------;
  761. GET_PARAM:    XOR    AX,AX        ;CLEAR AX FOR TOTAL
  762. GET_DIGIT:    MOV    BL,[SI]        ;GET CHARACTER INTO BL
  763.         CMP    BL,0DH        ;IS IT THE LAST ONE?
  764.         JE    DONE
  765.         INC    SI        ;POINT TO NEXT CHARACTER
  766.         CMP    BL,","        ;IS IT THE DELIMITER?
  767.         JE    DONE
  768.         SUB    BL,30H        ;CONVERT ASCII TO INTEGER
  769.         JC    GET_DIGIT    ;IS IT A VALID DIGIT
  770.         CMP    BL,9
  771.         JA    GET_DIGIT    ;IF NOT VALID, JUST SKIP IT
  772.         MOV    BH,10        ;TIMES 10 FOR NEXT DIGIT
  773.         MUL    BH        ;MULTIPLY SUM AND ADD THIS DIGIT
  774.         ADD    AL,BL        ;ADD DIGIT TO SUM
  775.         JMP    GET_DIGIT    ;READ ALL CHARACTERS ON LINE
  776. DONE:        RET
  777. CSEG        ENDS
  778.         END    START
  779.