home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 14 / CDACTUAL.iso / cdactual / demobin / share / program / asm / SNIPPER.ZIP / SNIPPER.ASM
Encoding:
Assembly Source File  |  1989-06-26  |  25.9 KB  |  791 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     INIT      ;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.1 (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 INIT
  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.  
  563.         OR    BUSY_FLAGS,00000010B    ;SET BIOS BUSY BIT
  564.         PUSHF
  565.         CALL    OLDINT13    ;DO THE BIOS FUNCTION
  566.         PUSHF            ;SAVE RESULT FLAGS
  567.         AND    BUSY_FLAGS,11111101B    ;CLEAR BIOS BUSY BIT
  568.         POPF            ;GET BACK RESULT FLAGS
  569.         STI            ;MUST RETURN WITH INTERUPTS ON
  570.         RET    2        ;RETURN BIOS RESULT FLAGS
  571.  
  572. NEWINT13    ENDP
  573. ;---------------------------------------------------------------------;
  574. ; INTERRUPT 16 ROUTINE. INSERT KEYSTROKES FROM BUFFER                 ;
  575. ;---------------------------------------------------------------------;
  576. NEWINT16    PROC    FAR
  577.         ASSUME    DS:NOTHING, ES:NOTHING
  578.         PUSH    BX
  579.         CMP    SEND_KEYS,1    ;SENDING KEYS FROM BUFFER?
  580.         JE    INSERT_KEY    ;IF YES, THEN GET NEXT ONE
  581.         CMP    WRIT_FILE,1    ;ANYTHING TO WRITE TO DISK?
  582.         JE    CHECK_DOS_STAT    ;IF YES, THIS IS THE TIME
  583. BIOS_KB:
  584.         POP    BX
  585.         JMP    OLDINT16    ;JUST DO NORMAL KB ROUTINE
  586. CHECK_DOS_STAT:
  587.         CMP    DOS_STAT,0AH    ;DOING READ STRING?
  588.         JE    BEGIN_NOW    ;IF YES, ITS SAFE TO BEGIN
  589.         CMP    DOS_STAT,8    ;DOING KEYBOARD INPUT?
  590.         JNE    BIOS_KB        ;IF YES, ITS SAFE TO BEGIN
  591. BEGIN_NOW:
  592.         STI            ;GET INTERRUPTS BACK ON
  593.         CALL    WRITE_TO_FILE    ;EMPTY THE BUFFER
  594.         JMP    BIOS_KB        ;CONTINUE WITH BIOS ROUTINE
  595. INSERT_KEY:
  596.         STI            ;INTERRUPTS BACK ON
  597.         MOV    BX,BUFF_NEXT    ;GET ADDRESS OF NEXT BYTE
  598.         CMP    BX,BUFF_LAST    ;AT END OF BUFFER YET?
  599.         JL    GET_A_KEY    ;IF NOT, GET THE NEXT ONE
  600.         MOV    SEND_KEYS,0    ;WHEN DONE, TURN OFF SEND SWITCH
  601. GET_A_KEY:
  602.         MOV    AL,CS:[BX]    ;GET THE NEXT KEY CODE
  603.         CMP    AL,10        ;IS IT A LINE FEED?
  604.         JNE    NOT_LF        ;DONT RETURN THE LINE FEEDS
  605.         INC    BUFF_NEXT    ;SKIP TO NEXT KEY
  606.         JMP    INSERT_KEY
  607. NOT_LF:
  608.         CMP    AH,1        ;REQUEST FOR STATUS ONLY?
  609.         JE    RETURN_STATUS    ;IF YES, RETURN STATUS ONLY
  610.         CMP    AH,0        ;REQUEST TO GET THE NEXT KEY
  611.         JNE    BIOS_KB        ;IF NOT, IGNORE THIS FUNCTION
  612.         INC    BX        ;REMOVE THIS KEY FROM OUR BUFFER
  613.         MOV    BUFF_NEXT,BX    ;SAVE THE POINTER TO NEXT KEY
  614. RETURN_STATUS:
  615.         OR    BL,1        ;CLEAR ZERO FLAG TO INDICATE A
  616.         POP    BX        ;KEY IS AVAILIABLE
  617.         RET    2        ;RETURN WITH THESE FLAGS
  618. NEWINT16    ENDP
  619. ;---------------------------------------------------------------------;
  620. ; INTERRUPT 21 ROUTINE.  THIS ROUTINE IS USED TO MONITOR DOS FUNCTION ;
  621. ; CALLS. IF THE BUFFER NEEDS TO BE FLUSHED, IT WIL BE DONE HERE.      ;
  622. ;---------------------------------------------------------------------;
  623. NEWINT21    PROC    FAR
  624.         ASSUME    DS:NOTHING, ES:NOTHING
  625.  
  626.         PUSHF            ;SAVE ENTRY FLAGS
  627.  
  628.         STI            ;ALLOW INTERRUPTS
  629.         CMP    AH,4BH        ;IF EXEC
  630.         JNE    NOT_EXEC
  631.         POPF
  632.         JMP    CS:OLDINT21    ;GO DIRECT
  633. NOT_EXEC:
  634.         OR    AH,AH        ;DOING FUNCTION ZERO?
  635.         JNE    NOT_ZERO
  636.         MOV    AH,4CH        ;IF YES, CHANGE IT TO A 4CH
  637. NOT_ZERO:
  638.         OR    BUSY_FLAGS,00000001B    ;SET DOS BUSY BIT
  639.         MOV    DOS_STAT,AH
  640.         POPF            ;RESTORE ORIGINAL FLAGS
  641.         PUSHF            ;SIMULATE AN INTERRUPT
  642.         CALL    CS:OLDINT21    ;DO THE DOS FUNCTION
  643.         PUSHF            ;SAVE THE RESULT FLAGS
  644.         AND    BUSY_FLAGS,11111110B    ;CLEAR DOS BUSY BIT
  645.         CMP    WRIT_FILE,1    ;ANYTHING TO WRITE TO DISK?
  646.         JNE    NO_WRITE    ;IF NOT JUST RETURN
  647.         CALL    WRITE_TO_FILE    ;SAFE TO ACCESS DISK NOW
  648. NO_WRITE:
  649.         POPF            ;RECOVER DOS RESULT FLAGS
  650.         STI
  651.         RET    2        ;RETURN WITH DOS RESULT FLAGS
  652. NEWINT21    ENDP
  653.  
  654. ;---------------------------------------------------------------------;
  655. ; NEW INTERRUPT 24H (CRITICAL DOS ERROR).  THIS INTERRUPT IS ONLY IN  ;
  656. ; EFFECT ONLY DURING A WRITE SCREEN.  IT IS REQUIRED TO SUPPRESS THE  ;
  657. ; 'ABORT, RETRY, IGNORE' MESSAGE.  ALL FATAL DISK ERRORS ARE IGNORED. ;
  658. ;---------------------------------------------------------------------;
  659. NEWINT24    PROC    FAR
  660.         ASSUME    CS:CSEG, DS:NOTHING, ES:NOTHING
  661.         STI            ;TURN INTERRUPTS BACK ON
  662.         INC     ERR_STAT    ;SET THE ERROR FLAG
  663.          XOR    AL,AL        ;TELLS DOS TO IGNORE THE ERROR
  664.         IRET            ;THATS ALL WE DO HERE
  665. NEWINT24    ENDP
  666.  
  667. ;--------------------------------------------------------------------;
  668. ; HERE IS THE CODE USED TO INITIALIZE SNIPPER.                       ;
  669. ;--------------------------------------------------------------------;
  670.         ASSUME    CS:CSEG, DS:CSEG, ES:CSEG
  671. INIT:
  672.         LEA    DX,COPYRIGHT
  673.         MOV    AH,9        ;DOS DISPLAY STRING SERVICE
  674.         INT    21H        ;DISPLAY TITLE MESSAGE
  675. ; SEARCH FOR AN PREVIOUSLY INSTALLED COPY OF SNIPPER
  676.         NOT    BYTE PTR START    ;MODIFY TO AVOID FASLE MATCH
  677.         XOR    BX,BX        ;START SEARCH AT SEGMENT ZERO
  678.         MOV    AX,CS        ;COMPARE TO THIS CODE SEGMENT
  679. NEXT_SEGMENT:
  680.         INC    BX        ;LOOK AT NEXT SEGMENT
  681.         CMP    AX,BX        ;UNTIL REACHING THIS CODE SEG
  682.         MOV    ES,BX
  683.         JE    NOT_INSTALLED
  684.         LEA    SI,START    ;SETUP TO COMPARE STRINGS
  685.         MOV    DI,SI
  686.         MOV    CX,16        ;16 BYTES MUST MATCH
  687.         REP    CMPSB        ;COMPARE DS:SI TO ES:DI
  688.         OR    CX,CX        ;DID THE STRINGS MATCH?
  689.         JNZ    NEXT_SEGMENT    ;IF NO MATCH, TRY NEXT SEGMENT
  690.         LEA    DX,INSTALLED_MSG
  691.         JMP    SHORT ERR_EXIT
  692. NOT_INSTALLED:
  693.         MOV    AH,30H
  694.         INT    21H        ;GET DOS VERSION NUMBER
  695.         CMP    AL,2        ;IS IT HIGHER THAN 2.0?
  696.         JAE    VER_OK        ;IF YES, PROCEED
  697.         LEA    DX,BAD_DOS_MSG
  698. ERR_EXIT:    MOV    AH,9        ;DOS DISPLAY STRING SERVICE
  699.         INT    21H        ;DISPLAY ERRER MESSAGE
  700.         RET            ;RETURN TO DOS
  701. VER_OK:
  702.         INC    SI        ;POINT TO FIRST PARAMETER
  703.         MOV    SI,81H        ;POINT TO PARAMETER AREA
  704.         CALL    GET_PARAM    ;GET FIRST PARAMETER (ROWS)
  705.         PUSH    AX        ;SAVE THE ROW COUNT
  706.         CALL    GET_PARAM    ;GET SECOND PARAMETER (COLUMNS)
  707.         ADD    AX,2        ;ADD SPACE FOR CR AND LF
  708.         POP    BX        ;GET BACK FIRST PARAMETER
  709.         MUL    BX        ;PRODUCT OF ROWS AND COLUMNS
  710.         OR    AX,AX        ;WAS ANYTHING ENTERED?
  711.         JZ    NO_PARAMS    ;IF NOT, USE DEFAULT VALUE
  712.         CMP    AX,10000    ;MAXIMUM BUFFER IS 10000 BYTES
  713.         JLE    SIZE_IS_OK
  714.         MOV    AX,10000
  715. SIZE_IS_OK:
  716.         ADD    AX,BUFF_START
  717.         MOV    BUFF_END,AX    ;SET THE NEW BUFFER SIZE
  718. NO_PARAMS:
  719.         MOV    AX,BIOS_SEG    ;LOOK AT BIOS DATA AREA
  720.         MOV    ES,AX
  721.         ASSUME    ES:BIOS_SEG
  722.         CMP    ROWS,0        ;IS NUMBER OF ROWS ENTERED HERE
  723.         JNE    MUST_BE_EGA    ;IF YES, AN EGA MAY BE PRESENT
  724.         MOV    ROWS,24        ;IF NOT EGA, MUST BE 24 ROWS
  725. MUST_BE_EGA:
  726.         ASSUME    ES:NOTHING
  727.                MOV    AX,3509H    ;GET KEYBOARD BREAK VECTOR
  728.         INT    21H
  729.         MOV    WORD PTR [OLDINT09],  BX  ;SAVE SEGMENT
  730.         MOV    WORD PTR [OLDINT09+2],ES  ;SAVE OFFSET
  731.         MOV    DX, OFFSET NEWINT09
  732.         MOV    AX, 2509H
  733.         INT    21H        ;DOS FUNCTION TO CHANGE VECTOR
  734.  
  735.                MOV    AX,3513H    ;GET BIOS DISK INTERRUPT VECTOR
  736.         INT    21H
  737.         MOV    WORD PTR [OLDINT13],  BX  ;SAVE SEGMENT
  738.         MOV    WORD PTR [OLDINT13+2],ES  ;SAVE OFFSET
  739.         MOV    DX, OFFSET NEWINT13
  740.         MOV    AX, 2513H
  741.         INT    21H        ;DOS FUNCTION TO CHANGE VECTOR
  742.  
  743.                MOV    AX,3516H    ;GET KEYBOARD INPUT VECTOR
  744.         INT    21H
  745.         MOV    WORD PTR [OLDINT16],  BX  ;SAVE SEGMENT
  746.         MOV    WORD PTR [OLDINT16+2],ES  ;SAVE OFFSET
  747.         MOV    DX, OFFSET NEWINT16
  748.         MOV    AX, 2516H
  749.         INT    21H        ;DOS FUNCTION TO CHANGE VECTOR
  750.  
  751.                MOV    AX,3521H    ;GET DOS FUNCTION VECTOR
  752.         INT    21H
  753.         MOV    WORD PTR [OLDINT21],  BX
  754.         MOV    WORD PTR [OLDINT21+2],ES
  755.         MOV    DX, OFFSET NEWINT21
  756.         MOV    AX, 2521H
  757.         INT    21H        ;DOS FUNCTION TO CHANGE VECTOR
  758.  
  759. ;--------------------------------------------------------------------;
  760. ; DEALLOCATE OUR COPY OF THE ENVIORNMENT.                            ;
  761. ; EXIT USING INT 27H. LEAVE CODE AND SPACE FOR BUFFER RESIDENT.      ;
  762. ;--------------------------------------------------------------------;
  763.  
  764.         MOV    AX,DS:[002CH]    ;GET SEGMENT OF ENVIORNMENT
  765.         MOV    ES,AX        ;PUT IT INTO ES
  766.         MOV    AH,49H        ;RELEASE ALLOCATED MEMORY
  767.         INT    21H
  768.         MOV    DX,BUFF_END    ;LEAVE THIS MUCH RESIDENT
  769.         INT    27H        ;TEMINATE AND STAY RESIDENT
  770. ;---------------------------------------------------------;
  771. ; GET_PARAM RETRIEVES AN INTEGER FROM THE COMMAND LINE.   ;
  772. ;---------------------------------------------------------;
  773. GET_PARAM:    XOR    AX,AX        ;CLEAR AX FOR TOTAL
  774. GET_DIGIT:    MOV    BL,[SI]        ;GET CHARACTER INTO BL
  775.         CMP    BL,0DH        ;IS IT THE LAST ONE?
  776.         JE    DONE
  777.         INC    SI        ;POINT TO NEXT CHARACTER
  778.         CMP    BL,","        ;IS IT THE DELIMITER?
  779.         JE    DONE
  780.         SUB    BL,30H        ;CONVERT ASCII TO INTEGER
  781.         JC    GET_DIGIT    ;IS IT A VALID DIGIT
  782.         CMP    BL,9
  783.         JA    GET_DIGIT    ;IF NOT VALID, JUST SKIP IT
  784.         MOV    BH,10        ;TIMES 10 FOR NEXT DIGIT
  785.         MUL    BH        ;MULTIPLY SUM AND ADD THIS DIGIT
  786.         ADD    AL,BL        ;ADD DIGIT TO SUM
  787.         JMP    GET_DIGIT    ;READ ALL CHARACTERS ON LINE
  788. DONE:        RET
  789. CSEG        ENDS
  790.         END    START
  791.