home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast.iso / screen / savscr.asm < prev    next >
Assembly Source File  |  1994-03-05  |  35KB  |  1,022 lines

  1.     PAGE    ,132
  2.     TITLE    SAVSCR
  3.  
  4. COMMENT    \
  5.     Usage: "SAVSCR  filename".  SAVSCR  makes itself resident and  handles
  6.                PrtSc interrupts (interrupt 5).   When the Shift-PrtSc keys are
  7.                depressed,  whatever  is on the text screen will be saved in  a
  8.                disk  file.   You  might do this in the middle of  any  program
  9.                where  you want to save a copy of what you see on  the  screen.
  10.                You can do this repeatedly, and each screen will be appended to
  11.                the file and separated by a form feed.  Later you can edit this
  12.                file and use it to document your programs.
  13.  
  14.            If  there  is a problem writing to the file,  you will  hear  a
  15.                "beep".
  16.  
  17.     Written  by  Ted  Shapin,  9/26/85 (Thanks to the  unknown  author  of
  18.         SAVSC.COM).
  19.  
  20.     Edit History:
  21.  
  22.     Edit ID     Who   Date    Description
  23.     -------- --- --------    -----------------------------------------------
  24.     [VAC001] VAC 12/31/85    (VAC=Vince Cuomo) - Added code to:
  25.  
  26.                      1) Properly  handle  EGA/ECD hardware  in
  27.                                         "43 line" mode;
  28.                      2) Provide extra pathname support in  the
  29.                                         filespec;
  30.                      3) Prevent  screen saves when in graphics
  31.                                         video  modes (modes 4 through  6,  and
  32.                                         greater than 7);
  33.                      4) Prevent  system  hangs  when  a   user
  34.                                         presses Shift-PrtSc while we're inside
  35.                                         this code;
  36.                      5) Support  screens  pages  1  through  7
  37.                                         (CGA/PGA/EGA adapters only);
  38.                      6) Initially  open  the  output  file  in
  39.                                         "append" mode (instead of creating the
  40.                                         output file from scratch), so that the
  41.                                         user  doesn't accidentally lose useful
  42.                                         screen images;
  43.                      7) Make  sure that we don't  do  anything
  44.                                         while  the INT 10H code in the BIOS is
  45.                                         executing (because SAVSCR needs to use
  46.                                         INT  10H  also,  and  the  BIOS  isn't
  47.                                         reentrant);  instead,  set  a flag  so
  48.                                         that our INT 10H "shell" will  perform
  49.                                         an  INT 5 if SAVSCR was invoked  while
  50.                                         INT 10H was executing;
  51.                      8) Put some of the references to absolute
  52.                                         memory  addresses in the ROM BIOS data
  53.                                         area  back in the code to  reduce  the
  54.                                         time it takes SAVSCR to execute and to
  55.                                         reduce  the chances of system  "hangs"
  56.                                         (absolute   memory  addresses   really
  57.                                         aren't  much of a problem since  we're
  58.                                         already  assuming that the  monochrome
  59.                                         and    CGA/PGA/EGA   adapter    buffer
  60.                                         addresses  are at B000 or  B800,  plus
  61.                                         there doesn't appear to be a clean way
  62.                                         to  get the video page address  offset
  63.                                         except  to  look directly at the  BIOS
  64.                                         data area in word 0000:044E);
  65.                      9) Set  the  IBM-PC BIOS's "performing  a
  66.                                         PrtSc  operation  now"  flag   (memory
  67.                                         location    0050:0000h)   to   1    as
  68.                                         documented  in  the BIOS code  and  in
  69.                                         Peter Norton's book.
  70.  
  71.                 Also  cleaned  up some of the SAVSCR  messages
  72.                                 and  .DOC  file,  and did  some  general  code
  73.                                 "cleanup"  (bet  ya' won't recognize the  code
  74.                                 anymore Ted...).
  75.  
  76.     \
  77.  
  78. ;    $PAGE -----------------------------------------------------------------
  79.  
  80. ;[VAC001]
  81. ;[VAC001] The  following equates are the absolute memory addresses that SAVSCR
  82. ;[VAC001] uses.   If  references to additional absolute memory  locations  are
  83. ;[VAC001] needed,  update  these equates so that the memory locations are  all
  84. ;[VAC001] defined in a single place in the SAVSCR code.
  85. ;[VAC001]
  86.  
  87. B$CVPAO    EQU    44EH        ;[VAC001] BIOS Current Video Page Address
  88.                 ;[VAC001] Offset
  89. B$PSFLG    EQU    50H        ;[VAC001] BIOS "Performing PrtSc" Flag Segment
  90.                 ;[VAC001] Address
  91. B$GABAO    EQU    0B800H        ;[VAC001] BIOS Graphics Adapter Buffer Address
  92.                 ;[VAC001] Offset
  93. B$MABAO    EQU    0B000H        ;[VAC001] BIOS Monochrome Adapter Buffer
  94.                 ;[VAC001] Address Offset
  95.  
  96. ;    $PAGE -----------------------------------------------------------------
  97.  
  98. BELL    EQU     7        ; ASCII code (decimal) for a bell
  99. CR    EQU    13        ; ASCII code (decimal) for a carriage-return
  100. LF    EQU    10        ; ASCII code (decimal) for a line-feed
  101. SPACE    EQU    32        ; ASCII code (decimal) for a space
  102. TAB    EQU     9        ; ASCII code (decimal) for a tab
  103.  
  104. MAXCHRS    EQU    43d*132d    ;[VAC001] The maximum number of    display    screen
  105.                 ;[VAC001] characters that we'll need to write
  106.                 ;[VAC001] out (25x80 isn't good enough because
  107.                 ;[VAC001] EGA/ECD hardware can be set to "43
  108.                 ;[VAC001] line" mode, and some of the newer
  109.                 ;[VAC001] video cards finally support 132
  110.                 ;[VAC001] columns)
  111.  
  112. ;    $PAGE -----------------------------------------------------------------
  113.  
  114. S00000    SEGMENT    BYTE PUBLIC 'CODE'
  115.     ASSUME    CS:S00000
  116.  
  117.     DB    100H DUP (0)    ; Space    for the    INT 5 stack
  118. I5STK    EQU    00FFH        ;[VAC001] Pointer to the top-of-INT 5 stack
  119.  
  120. SAVSCR    PROC    FAR
  121.  
  122. BEGIN:    ORG    100H        ; All PC/MS-DOS .COM programs start at 100H
  123.     JMP    START        ; Start    address when run from COMMAND.COM
  124.  
  125. ;    $PAGE -----------------------------------------------------------------
  126.  
  127. ;
  128. ; The following are storage allocations for various SAVSCR control variables.
  129. ;
  130. SIGNAT    DW    2234H        ; Signature of this routine
  131. OLD5V1    DW    ?        ; Storage for the old INT 5 (PrtSc) interrupt
  132. OLD5V2    DW    ?        ; vector
  133. ACTIVEF    DB    0        ; Active flag (go to DOS INT 5 handler if 0)
  134. FILEH    DW    ?        ; Storage for the output file handle
  135. CRLF    DB    CR,LF,'$'    ; DOS INT 21H function code 9 format CR/LF
  136. FORMFD    DB    0CH        ; ASCII form-feed character
  137. EXITSP    DW    ?        ;[VAC001] Storage for the SP value for IEXIT
  138. EGA    DB    0        ;[VAC001] EGA hardware flag
  139. I5LOCK    DB    0        ;[VAC001] "Ignore INT 5" flag
  140. I10LOCK    DB    0        ;[VAC001] "INT 10H executing" flag
  141. IN_I10    DB    0        ;[VAC001] A "second level" INT 10H flag
  142. DOINT5    DB    0        ;[VAC001] "Do INT 5 after INT 10H" flag
  143. OLD10V1    DW    ?        ;[VAC001] Storage for INT 10h vector
  144. OLD10V2    DW    ?        ;[VAC001]    "     "   "   "    "
  145. CURSMOD    DW    ?        ;[VAC001] Storage for the cursor mode at entry
  146. CURSLOC    DW    ?        ;[VAC001] Storage for the cursor location
  147. CURSCHR    DW    ?        ;[VAC001] Storage for the char at cursor loc.
  148. SCRROWS    DW    ?        ;[VAC001] Storage for the number of screen rows
  149. SCRCOLS    DW    ?        ;[VAC001] Storage for the number of screen cols
  150. VIDSEGM    DW    ?        ;[VAC001] Storage for the video buffer segment
  151. VIDEOM    DB    ?        ;[VAC001] Storage for the current video mode
  152. ACTIVEP    DB    ?        ;[VAC001] Storage for the active video page
  153. IN10_SS    DW    ?        ;[VAC001] Storage for the SS register
  154. IN10_SP    DW    ?        ;[VAC001] Storage for the SP register
  155. IN10_AX    DW    ?        ;[VAC001] Storage for the AX register
  156. INT5_SS    DW    ?        ;[VAC001] Storage for the SS register
  157. INT5_SP    DW    ?        ;[VAC001] Storage for the SP register
  158. INT5_AX    DW    ?        ;[VAC001] Storage for the AX register
  159.     DW    100 DUP (0)    ;[VAC001] INT 10H handler code stack
  160. INTSTK    DW    ?        ;[VAC001] Mark the start of the INT 10H stack
  161.  
  162. FILEN    DB    75D DUP    (0)    ;[VAC001] Filename (paths OK)
  163.  
  164. ;    $PAGE -----------------------------------------------------------------
  165.  
  166. ;[VAC001]+ (Start of edit VAC001 code block)
  167.  
  168.     ;
  169.     ; This  routine  will be called in place of INT 10H (the  normal  BIOS
  170.     ; interrupt code for video service).   If an interrupt is being issued
  171.     ; to set the screen to "43 line" mode (EGA/ECD hardware only), then we
  172.     ; need to "trap" the call so that we can reset the INT 5 vector to our
  173.     ; code  again  (for some reason,  the firmware in the EGA  resets  the
  174.     ; INT 5  vector,  which means that SAVSCR is permanently deactivated).
  175.     ; Also,  we  need  to  block our INT 5  handler when INT 10H  code  is
  176.     ; executing in order to help prevent system "hangs".
  177.     ;
  178. INTTENH:CLI            ; Disable interrupts for now...
  179.     PUSHF            ; Save the CPU flags
  180.     TEST    BYTE PTR CS:IN_I10,1
  181.                 ; Did INT 10H call itself (who knows...), or
  182.                 ; did INT 5 call INT 10H?
  183.     JZ    INT10OK        ; If not, then continue
  184.     POPF            ; If so, then restore the CPU flags
  185.     JMP    DWORD PTR CS:OLD10V1
  186.                 ; And go do a normal INT 10H call
  187.  
  188. INT10OK:POPF            ; Restore the CPU flags
  189.     MOV    BYTE PTR CS:IN_I10,1
  190.                 ; Set the "second level" lockout flag...
  191.     MOV    BYTE PTR CS:I10LOCK,1
  192.                 ; Set the "INT 10H executing" flag
  193.     MOV    CS:IN10_SS,SS    ; Save the SS register
  194.     MOV    CS:IN10_SP,SP    ; Save the SP register
  195.     MOV    CS:IN10_AX,AX    ; Save the AX register
  196.     PUSH    CS        ; Get our code segment address
  197.     POP    SS        ; Make the INT 10H stack be in the same    segment
  198.     MOV    SP,CS:INTSTK    ; Make SP point    to the beginning of the    stack
  199.     PUSHF            ; Store    the current CPU    flags on the stack
  200.     PUSH    CS        ; Store    our code segment on the    stack
  201.     PUSH    DS        ; Store the original DS value on the stack
  202.     PUSH    CS        ; Get our CS value
  203.     POP    DS        ; And set DS to be the same as CS
  204.     MOV    AX,OFFSET INTTENR
  205.                 ; Get the return address for INT 10H
  206.     POP    DS        ; Restore the original DS value
  207.     PUSH    AX        ; Save the return address in our INT 10H
  208.                 ; handler on the stack (now INT    10H will IRET
  209.                 ; back into our    code so    that we    can reset the
  210.                 ; INT 5    vector to us, if needed)
  211.     MOV    AX,CS:IN10_AX    ; Restore the original contents    of AX
  212.  
  213.     JMP    DWORD PTR CS:OLD10V1
  214.                 ; And go to the    BIOS's INT 10H code
  215.  
  216.     ;
  217.     ; Get here after the real INT 10H code in the ROM BIOS is finished
  218.     ;
  219. INTTENR:NOP            ; Make sure that INT 10H really IRETs to the
  220.     NOP            ; right place...
  221.     PUSH    DX        ; Save DX for now
  222.     PUSH    DS        ; Save the current DS register contents
  223.     PUSH    CS        ; Get the current CS register contents
  224.     POP    DS        ; Set DS to be the same    as CS for function 25H
  225.     MOV    DX,IN10_AX    ; Get the INT 10H function code
  226.     MOV    IN10_AX,AX    ; Save the AX value that INT 10H returned
  227.     TEST    BYTE PTR DS:EGA,1
  228.                 ; Is there an EGA card on this machine?
  229.     JZ    CHKINT5        ; If not, go see if we got an INT 5 request
  230.     CMP    DH,12H        ; If so, then is this the 12H function code?
  231.     JNE    CHKINT5        ; If not, go see if we got an INT 5 request
  232.     STI            ; Enable interrupts again...
  233.     MOV    AH,25H        ; And make sure that the interrupt handler
  234.     MOV    AL,5        ; for INT 5
  235.     MOV    DX,OFFSET INTVEC; is pointed at the code in SAVSCR
  236.     INT    21H        ; and is still active
  237.     CLI            ; Disable interrupts...
  238.  
  239. CHKINT5:TEST    BYTE PTR DS:DOINT5,1
  240.                 ; Did the user request a "SAVSCR" while INT 10H
  241.                 ; was executing?
  242.     JZ    I10EXIT        ; If not, then go exit from the INT 10H code
  243.     MOV    BYTE PTR DS:DOINT5,0
  244.                 ; Otherwise, clear the "Do an INT 5" flag
  245.     MOV    BYTE PTR DS:I10LOCK,0
  246.                 ; Reset the INT 10H lockout flag
  247.     STI            ; Enable interrupts again...
  248.     INT    5        ; And do an INT 5 to save the screen image
  249.     MOV    BYTE PTR DS:I10LOCK,1
  250.                 ; Set the INT 10H lockout flag again for a bit
  251.  
  252. I10EXIT:POP    DS        ; Restore the original DS register contents
  253.     POP    DX        ; Restore the original DX register contents
  254.     MOV    AX,CS:IN10_SS    ; Get the original SS contents
  255.     MOV    SS,AX        ; Restore the user's SS register
  256.     MOV    AX,CS:IN10_SP    ; Get the original SP contents
  257.     MOV    SP,AX        ; Restore the user's SP register
  258.     MOV    AX,CS:IN10_AX    ; Restore the AX register
  259.     MOV    BYTE PTR CS:I10LOCK,0
  260.                 ; Clear the INT 10H lockout flag
  261.     MOV    BYTE PTR CS:IN_I10,0
  262.                 ; Clear the "second level" lockout flag too...
  263.     IRET            ; And return to    the original caller's code
  264.  
  265. ;[VAC001]- (End    of edit    VAC001 code block)
  266.  
  267. ;    $PAGE -----------------------------------------------------------------
  268.  
  269. INTVEC:    ; The INT 5 interrupt handler code starts here...
  270.     CLI            ;[VAC001] Disable interrupts
  271.     TEST    BYTE PTR CS:I5LOCK,1
  272.                 ;[VAC001] Are we executing INT 5 right now?
  273.     JZ    INT5OK        ;[VAC001] If not, then continue
  274.     IRET            ;[VAC001] Otherwise, return right now...
  275.  
  276. INT5OK:    MOV    BYTE PTR CS:I5LOCK,1
  277.                 ;[VAC001] Lock out any other INT 5's for now...
  278.     TEST    BYTE PTR CS:I10LOCK,1
  279.                 ;[VAC001] Is the INT 10H code executing now?
  280.     JZ    I10CLEAR    ;[VAC001] If not, then go do the INT 5 code
  281.     MOV    BYTE PTR CS:DOINT5,1
  282.                 ;[VAC001] Otherwise, tell our INT 10H "shell"
  283.                 ;[VAC001] to activate the INT 5 code when thru
  284.     MOV    BYTE PTR CS:I5LOCK,0
  285.                 ;[VAC001] Reset the INT 5 lock out flag byte
  286.     IRET            ;[VAC001] And exit until called again...
  287.  
  288. I10CLEAR:            ;[VAC001]
  289.     MOV    CS:INT5_SS,SS    ;[VAC001] Save caller's SS value
  290.     MOV    CS:INT5_SP,SP    ;[VAC001] Save caller's SP value
  291.     PUSH    CS        ;[VAC001]
  292.     POP    SS        ;[VAC001] Initialize our stack segment address
  293.     MOV    SP,I5STK    ;[VAC001] Initialize our stack pointer
  294.     PUSH    DS        ; Save the caller's DS contents
  295.     PUSH    CS:INT5_AX    ;[VAC001] Save the caller's AX contents
  296.     TEST    BYTE PTR CS:ACTIVEF,1
  297.                 ; Are we active?
  298.     JNZ    DOIT        ; Yes, we are
  299.     POP    AX        ;[VAC001] So restore AX
  300.     POP    DS        ; Restore DS
  301.     MOV    SS,CS:INT5_SS    ;[VAC001] Restore the original SS contents
  302.     MOV    SP,CS:INT5_SP    ;[VAC001] Restore the original SP contents
  303.     MOV    BYTE PTR CS:I5LOCK,0
  304.                 ;[VAC001] Reset the INT 5 lock out flag byte
  305.     JMP    DWORD PTR CS:OLD5V1
  306.                 ; And go to the old INT 5 handler
  307.  
  308. DOIT:    PUSH    BX
  309.     PUSH    CX
  310.     PUSH    DX
  311.     PUSH    SI
  312.     PUSH    DI
  313.     PUSH    ES
  314.     PUSH    BP        ;[VAC001] (INT 10H can destroy the BP register)
  315.     MOV    CS:EXITSP,SP    ;[VAC001] Remember the current stack pointer
  316.     STI            ; Allow    interrupts
  317.     CALL    SCRINFO        ;[VAC001] Get needed display screen information
  318.     MOV    SI,B$PSFLG    ;[VAC001] Get the "performing PrtSc" flag's
  319.                 ;[VAC001] segment address
  320.     MOV    ES,SI        ;[VAC001] Set ES
  321.     XOR    SI,SI        ;[VAC001] Set SI to zero
  322.     MOV    BYTE PTR ES:[SI],1
  323.                 ;[VAC001] Set the "performing PrtSc" flag to 1
  324.                 ;[VAC001] (as documented in the BIOS code and
  325.                 ;[VAC001] in Peter Norton's book)
  326.     MOV    ES,SI        ;[VAC001] Point ES at the ROM BIOS data segment
  327.     MOV    AX,B$GABAO    ;[VAC001] Assume we're using a CGA/PGA/EGA card
  328.     CMP    BYTE PTR CS:VIDEOM,4
  329.                 ;[VAC001] Are we in modes 0-3 (text modes)?
  330.     JL    TXTMODE        ;[VAC001] If so, then continue
  331.     CMP    BYTE PTR CS:VIDEOM,7
  332.                 ;[VAC001] If not, are we in monochrome mode?
  333.     JNE    IEXITNC        ;[VAC001] If not, then go exit now (graphics
  334.                 ;[VAC001] mode screens look like junk when
  335.                 ;[VAC001] saved    because    the video fields are
  336.                 ;[VAC001] different)
  337.     MOV    AX,B$MABAO    ;[VAC001] Set the buffer address for mono cards
  338.  
  339. TXTMODE:MOV    CS:VIDSEGM,AX    ;[VAC001] Save the video RAM segment address
  340.     MOV    AH,1        ;[VAC001] "Set cursor type"
  341.     MOV    CX,2010H    ;[VAC001] Make the cursor invisible
  342.     INT    10H        ;[VAC001]
  343.     MOV    AH,8        ;[VAC001] "Read attr/char at curr. curs. loc."
  344.     MOV    BH,CS:ACTIVEP    ;[VAC001] Get the current active video page no.
  345.     INT    10H        ;[VAC001] Get the attibute/character
  346.     MOV    CS:CURSCHR,AX    ;[VAC001] Save them...
  347.     MOV    SI,B$CVPAO    ;[VAC001] Get the offset of the current video
  348.                 ;[VAC001] page address offset
  349.     MOV    SI,ES:[SI]    ;[VAC001] Get the current video page address
  350.                 ;[VAC001] offset (this provides support for
  351.                 ;[VAC001] screen pages 1 through 7 on
  352.                 ;[VAC001] CGA/PGA/EGA cards) for MLOOP below...
  353.     PUSH    CS        ;[VAC001] Get our CS value
  354.     POP    DS        ;[VAC001] And set DS to our code segment value
  355.     MOV    DI,OFFSET IEND    ; Our buffer is    at the end of the INT 5 service
  356.                 ; routine in our code segment
  357.     MOV    CX,CS:SCRCOLS    ;[VAC001] Get the number of columns to save
  358.     MOV    AX,CS:SCRROWS    ;[VAC001] Get the number of rows to save
  359.     MOV    DS,CS:VIDSEGM    ;[VAC001] Set DS to the current video RAM seg.
  360.     MUL    CX        ;[VAC001] Put number of chars to save in AX
  361.     XCHG    AX,CX        ;[VAC001] Put the number of chars in CX    for the
  362.                 ;[VAC001] LOOP instruction
  363.     CLD            ;[VAC001] We want SI to be incremented
  364.  
  365. MLOOP:    LODSW            ; Load a word from the video RAM segment
  366.     MOV    BYTE PTR CS:[DI],AL
  367.                 ;[VAC001] Put the byte in our buffer
  368.     INC    DI        ;[VAC001] Increment the buffer pointer
  369.     LOOP    MLOOP        ; Save display screen bytes until CX=0
  370.  
  371.     PUSH    CS        ; Get our CS contents
  372.     POP    DS        ; And put it in DS
  373.     CALL    OPENEND        ; Open the file    and position to    the end
  374.     MOV    SI,OFFSET IEND    ; Point    to our buffer
  375.     MOV    CX,CS:SCRCOLS    ;[VAC001] Put number of screen columns in CX
  376.     MOV    DI,CS:SCRROWS    ;[VAC001] Put number of screen rows in DI
  377.     CALL    WRITESC        ;[VAC001] Write    out the    display    screen lines
  378.     CALL    CLOSEF        ; and close the    output file
  379.  
  380. IEXIT:    MOV    AX,CS:CURSCHR    ;[VAC001] Get the char/attrib. to write
  381.     MOV    BL,AH        ;[VAC001] Put the attribute where needed
  382.     MOV    AH,9        ;[VAC001] "Write attr/char at cursor location"
  383.     MOV    BH,CS:ACTIVEP    ;[VAC001] Get the active video page
  384.     MOV    CX,1        ;[VAC001] Only write one character
  385.     INT    10H        ;[VAC001] Do it...
  386.     MOV    AH,1        ;[VAC001] "Set cursor type"
  387.     MOV    CX,CS:CURSMOD    ;[VAC001] Get the original cursor mode
  388.     INT    10H        ;[VAC001] Restore the cursor mode to its value
  389.                 ;[VAC001] at entry
  390.  
  391. IEXITNC:MOV    SI,B$PSFLG    ;[VAC001] Get the "performing PrtSc" flag's
  392.                 ;[VAC001] segment address
  393.     MOV    ES,SI        ;[VAC001] Set ES
  394.     XOR    SI,SI        ;[VAC001] Set SI to zero
  395.     MOV    BYTE PTR ES:[SI],0
  396.                 ;[VAC001] Set "performing PrtSc" flag back to 0
  397.     POP    BP        ;[VAC001]
  398.     POP    ES
  399.     POP    DI
  400.     POP    SI
  401.     POP    DX
  402.     POP    CX
  403.     POP    BX
  404.     POP    AX
  405.     POP    DS
  406.     MOV    SS,CS:INT5_SS    ;[VAC001]
  407.     MOV    SP,CS:INT5_SP    ;[VAC001]
  408.     MOV    BYTE PTR CS:I5LOCK,0
  409.                 ;[VAC001] Tell the world that we're inactive
  410.     IRET
  411.  
  412. ;    $PAGE -----------------------------------------------------------------
  413.  
  414. ;[VAC001]+ (Beginning of edit VAC001 code block)
  415.  
  416. SCRINFO    PROC NEAR
  417.     ;
  418.     ; Determine the following screen information:
  419.     ;
  420.     ;    EGA     (byte)    If 1, then we're running on an active EGA card
  421.     ;    VIDEOM  (byte)    The current video mode
  422.     ;    ACTIVEP (byte)    The current active video page
  423.     ;    SCRROWS (word)    The number of display screem rows
  424.     ;    SCRCOLS (word)    The number of display screen columns
  425.     ;    CURSMOD (word)    The cursor mode
  426.     ;    CURSLOC (word)    The cursor location
  427.     ;
  428.     MOV    AX,1130H    ; EGA function code, "information" subfunction
  429.                 ; code
  430.     PUSH    BP        ; Save the current BP contents
  431.     PUSH    ES        ; Save the current ES contents
  432.     XOR    BH,BH        ; The pointer value to be returned in ES:BP (we
  433.                 ; don't care about it...)
  434.     MOV    DX,-1        ; Put a    ridiculous value in DX as the number of
  435.                 ; screen rows (on PC's which don't have    the EGA
  436.                 ; installed, this value    won't be changed when
  437.                 ; INT 10h returns to us; otherwise, DL will
  438.                 ; contain the last row number on the display
  439.                 ; screen)
  440.     INT    10H        ; Do it...
  441.     POP    ES        ; Restore ES
  442.     POP    BP        ; Restore BP
  443.     MOV    BYTE PTR CS:EGA,0
  444.                 ; Assume that we don't have an active EGA card
  445.     XOR    DH,DH        ; Clear    DX's high byte
  446.     CMP    DL,23D        ; Are there at least 24    lines on the display
  447.                 ; screen?
  448.     JL    USE_25R        ; If not, then assume 25 rows
  449.     CMP    DL,68D        ; Are we set to    a believable number?
  450.     JG    USE_25R        ; If not, then assume that function 11h    isn't
  451.                 ; implemented in this machine's BIOS (i.e.,
  452.                 ; the EGA card is not installed    or is not
  453.                 ; active), and default to a 25 row display
  454.                 ; screen
  455.     MOV    BYTE PTR CS:EGA,1
  456.                 ; Otherwise, set "active EGA card" flag byte
  457.     JMP    GOT_NR        ; And continue
  458.  
  459. USE_25R:MOV    DL,24D        ; Assume row 24    is the last screen row...
  460.  
  461. GOT_NR:    INC    DL        ; Convert from offset zero to offset 1
  462.     MOV    CS:SCRROWS,DX    ; Remember the number of screen rows
  463.     MOV    AH,15D        ; "Current video state"
  464.     INT    10H        ; Get the current video    state (places the
  465.                 ; current display page number in BH, the number
  466.                 ; of display screen columns in AH, and the
  467.                 ; current video mode in AL)
  468.     MOV    BYTE PTR CS:VIDEOM,AL
  469.                 ; Remember the current video mode
  470.     MOV    BYTE PTR CS:ACTIVEP,BH
  471.                 ; Remember the current active display page no.
  472.     MOV    DL,AH        ; Put the last column number in    DL
  473.     XOR    DH,DH        ; Clear    DH
  474.     MOV    CS:SCRCOLS,DX    ; Remember the number of display screen columns
  475.     MOV    AH,3        ; "Read cursor position" (BH is set above...)
  476.     INT    10H        ; Get the current cursor mode and position
  477.     MOV    CS:CURSMOD,CX    ; Save the current cursor mode
  478.     MOV    CS:CURSLOC,DX    ; Save the current cursor location
  479.     RET            ; And return to    our caller
  480.  
  481. SCRINFO    ENDP
  482.  
  483. ;[VAC001]- (End    of edit    VAC001 code block)
  484.  
  485. ;    $PAGE -----------------------------------------------------------------
  486.  
  487. OPENEND    PROC    NEAR
  488.     MOV    DX,OFFSET FILEN
  489.     MOV    AL,1        ; Write    access
  490.     MOV    AH,3DH
  491.     INT    21H
  492.     JNC    TOEOF
  493.     CMP    AX,2
  494.     JE    OPEN2
  495.     CALL    BEEP        ; An open problem
  496.     MOV    SP,CS:EXITSP    ;[VAC001] Fix up the stack pointer
  497.     JMP    IEXIT        ; Go exit from SAVSCR
  498.  
  499. OPEN2:    CALL    CREATEF
  500.     JNC    TOEOF        ;[VAC001] If all is well, then position to EOF
  501.     CALL    BEEP        ; Some kind of OPEN problem
  502.     MOV    SP,CS:EXITSP    ;[VAC001] Fix up the stack pointer
  503.     JMP    IEXIT        ;[VAC001] Go exit from SAVSCR
  504.  
  505. TOEOF:    MOV    CS:FILEH,AX    ; Save file handle
  506.     MOV    BX,AX
  507.     MOV    AL,2        ; Move to end of file
  508.     XOR    CX,CX        ;[VAC001] (faster than MOV CX,0)
  509.     XOR    DX,DX
  510.     MOV    AH,42H
  511.     INT    21H
  512.     JC    BADEOF        ;[VAC001]
  513.     RET
  514.  
  515. BADEOF:    CALL    BEEP        ;[VAC001] Some kind of problem
  516.     MOV    SP,CS:EXITSP    ;[VAC001] Fix up the stack pointer
  517.     JMP    IEXIT        ;[VAC001] Go exit from SAVSCR
  518.  
  519. OPENEND    ENDP
  520.  
  521. ;    $PAGE -----------------------------------------------------------------
  522.  
  523. CREATEF    PROC    NEAR
  524.     XOR    CX,CX        ; Attribute bits zero
  525.     MOV    AH,3CH        ; Create a file    or if it exists, set the
  526.     INT    21H        ; length to zero
  527.     RET
  528.  
  529. CREATEF    ENDP
  530.  
  531. ;    $PAGE -----------------------------------------------------------------
  532.  
  533. WRITESC    PROC    NEAR        ;[VAC001] Enter    with DI    containing the number
  534.                 ;[VAC001] of display screen rows to be saved
  535.                 ;[VAC001] and CX containing the    number of
  536.                 ;[VAC001] display screen columns to be saved,
  537.                 ;[VAC001] SI pointing to the start of the file
  538.                 ;[VAC001] buffer's area.
  539.     PUSH    DS        ; Save the DS value at entry
  540.     PUSH    CS        ;[VAC001] Get our CS value
  541.     POP    DS        ;[VAC001] And set DS to be the same as CS
  542.  
  543. WRITELP:PUSH    CX        ;[VAC001] Save the number of screen columns
  544.     PUSH    DI        ;[VAC001] Save the number of screen rows
  545.     MOV    BX,CS:FILEH    ;[VAC001] Put the output file handle in BX
  546.  
  547.     ;[VAC001]
  548.     ;[VAC001] Now strip off    trailing spaces    from the line image that we're
  549.     ;[VAC001] going    to write out to    the output file.
  550.     ;[VAC001]
  551. WL2:    MOV    DI,CX        ;[VAC001] Get the current char count
  552.     ADD    DI,SI        ;[VAC001] Add it to the    line image's video
  553.                 ;[VAC001] buffer address
  554.     DEC    DI        ;[VAC001] Correct for offset by    1 (needs to be
  555.                 ;[VAC001] offset by 0)
  556.     MOV    DL,BYTE PTR CS:[DI]
  557.                 ;[VAC001] Get that byte
  558.     CMP    DL,SPACE    ;[VAC001] Is it    a SPACE?
  559.     JNE    WL3        ;[VAC001] If not, then we've stripped off any
  560.                 ;[VAC001] trailing spaces
  561.     LOOP    WL2        ;[VAC001] Otherwise, it's a SPACE, so backup
  562.                 ;[VAC001] another character
  563.     POP    DI        ;[VAC001] We can only get here when the    entire
  564.                 ;[VAC001] line is spaces, so fix up the    stack
  565.     JMP    WRITEOK        ;[VAC001] And just go write out    a CR/LF
  566.  
  567. WL3:    MOV    AH,40H
  568.     MOV    DX,SI
  569.     INT    21H        ; Do the write
  570.     POP    DI        ;[VAC001] Restore DI
  571.     JC    BADWRT        ;[VAC001] If there's a problem, then go "beep"
  572.  
  573. WRITEOK:MOV    AH,40H
  574.     MOV    CX,2
  575.     MOV    DX,OFFSET CRLF
  576.     INT    21H        ; Write    a CRLF at the end of the line
  577.     JNC    NXTADDR        ; If all is well, then continue
  578.  
  579. BADWRT:    POP    CX        ;[VAC001] Restore CX
  580.     POP    DS        ;[VAC001] Restore DS
  581.     JMP    BEEP        ;[VAC001] And go "beep" at the user
  582.  
  583. NXTADDR:POP    CX        ;[VAC001] Restore the number of screen columns
  584.     ADD    SI,CX        ; Advance to the next line
  585.     DEC    DI
  586.     JNE    WRITELP
  587.     MOV    DX,OFFSET FORMFD
  588.     MOV    CX,1
  589.     MOV    AH,40H
  590.     INT    21H        ; Write    a FORM FEED
  591.     POP    DS
  592.     JC    BEEP        ;[VAC001] If something is wrong, then go "beep"
  593.     RET
  594.  
  595. ;    $PAGE -----------------------------------------------------------------
  596.  
  597. CLOSEF    PROC    NEAR
  598.     MOV    BX,CS:FILEH
  599.     MOV    AH,3EH
  600.     INT    21H        ; Close    the output file
  601.     JNC    CLOSEOK        ;[VAC001]
  602.  
  603. BEEP:    MOV    DL,7        ; Some kind of file error occured so
  604.     MOV    AH,2        ; just send a "beep" and return
  605.     INT    21H
  606.  
  607. CLOSEOK:RET
  608.  
  609. CLOSEF    ENDP
  610.  
  611. WRITESC    ENDP            ;[VAC001]
  612.  
  613. ;    $PAGE -----------------------------------------------------------------
  614.  
  615. IEND:    ;
  616.     ; End of the resident code. File buffer    area overlays the following.
  617.     ;
  618.  
  619. ;    $PAGE -----------------------------------------------------------------
  620.  
  621. NOFILEM    DB    'You must specify an output filename such as "C:\PATH\SCREEN.OUT".',CR,LF,'$'
  622.  
  623. BADDOS    DB    'Sorry, SAVSCR must run under PC/MS-DOS V2.0 or greater.',CR,LF,'$'
  624.  
  625. PROBM    DB    'I am unable to create the output file "$'
  626.  
  627. FEXISTS    DB    CR,LF
  628.     DB    'Warning, that output file already exists.  Screen output will be',CR,LF
  629.     DB    "appended to the output file's current contents.",CR,LF,'$'
  630.  
  631. ALREADY    DB    CR,LF
  632.     DB    'SAVSCR is already resident in memory and is active.',CR,LF,LF
  633.     DB    'Would you like to disable SAVSCR and return to the original',CR,LF
  634.     DB    'DOS PrtSc interrupt handler (Y/N)? $'
  635.  
  636. INACTIV    DB    CR,LF
  637.     DB    'SAVSCR is already resident in memory but is inactive.',CR,LF,LF
  638.     DB    'Would you like to enable SAVSCR (Y/N)? $'
  639.  
  640. OKINACM    DB    CR,LF
  641.     DB    'OK, SAVSCR is now inactive.  To re-activate SAVSCR, just',CR,LF
  642.     DB    'type "SAVSCR" and answer the questions.',CR,LF,'$'
  643.  
  644. OKACTM    DB    CR,LF
  645.     DB    'OK, SAVSCR is now active again.  Press Shift-PrtSc to save',CR,LF
  646.     DB    'the display screen to the output file.',CR,LF,'$'
  647.  
  648. INITMSG    DB    CR,LF
  649.     DB    'The SAVSCR V1.1 (save display screen) program is now resident',CR,LF
  650.     DB    'in memory.  Press Shift-PrtSc to save the display screen in',CR,LF
  651.     DB    'the output file "$'
  652.  
  653. QUOTEND    DB    '".',CR,LF,'$'
  654.  
  655. WOULDM    DB    'Would you like instructions (Y/N)? $'
  656.  
  657. OPNMSG    DB    'Attempting to create screen image output file $'
  658.  
  659. DOCM    DB    CR,LF
  660.     DB    '        Usage:  "SAVSCR [[d:]\path\]filename.ext"',CR,LF,LF
  661.     DB    '        SAVSCR  makes itself resident and handles PrtSc interrupts (BIOS',CR,LF
  662.     DB    '        interrupt 5).  When the Shift and PrtSc keys are pressed  AT THE',CR,LF
  663.     DB    '        SAME TIME, whatever is  on the display screen will be written to',CR,LF
  664.     DB    '        a disk  file.  You might do this in the middle of a program when',CR,LF
  665.     DB    '        you  want to save a copy of what you see on the screen.  You may',CR,LF
  666.     DB    '        do this  repeatedly, and each screen will be appended at the end',CR,LF
  667.     DB    '        of the file and will be separated by a form feed.  Later you can',CR,LF
  668.     DB    '        edit or print this file and use it to document your programs  or',CR,LF
  669.     DB    '        save  useful information.  If there is a problem  writing to the',CR,LF
  670.     DB    '        output file, you will hear a "beep".',CR,LF,LF
  671.     DB    '                     SAVSCR V1.0  9/25/85 by Ted Shapin',CR,LF
  672.     DB    '                     SAVSCR V1.1 12/31/85 by Vince Cuomo',CR,LF
  673.     DB    '$'
  674.  
  675. ;    $PAGE -----------------------------------------------------------------
  676.  
  677. START:    MOV    AX,CS        ;[VAC001] Initial installation starts here
  678.     MOV    DS,AX        ;[VAC001]
  679.     MOV    ES,AX        ;[VAC001]
  680.     MOV    SS,AX        ;[VAC001]
  681.     MOV    SP,I5STK    ;[VAC001]
  682.     MOV    AH,30H        ; Check    DOS version
  683.     INT    21H
  684.     CMP    AL,2
  685.     JGE    DOSOK
  686.     MOV    DX,OFFSET BADDOS
  687.     CALL    WRTMSG
  688.     MOV    AL,-1        ;[VAC001]
  689.     JMP    LEAVE
  690.  
  691. DOSOK:    MOV    AH,35H        ; Get interrupt    vector
  692.     MOV    AL,5        ; for interrupt    5
  693.     INT    21H
  694.     CMP    WORD PTR ES:SIGNAT,2234H
  695.     JNE    NOTLOADED
  696.     TEST    BYTE PTR ES:ACTIVEF,1
  697.     JZ    NOTACTIV
  698.     MOV    DX,OFFSET ALREADY
  699.                 ; Already loaded and active
  700.     CALL    SHORT WRTMSG
  701.     CALL    ASK        ; Do you want to make us inactive?
  702.     JNE    ASKDOC
  703.     MOV    BYTE PTR ES:ACTIVEF,0
  704.                 ; Make us inactive
  705.     MOV    DX,OFFSET OKINACM
  706.     CALL    SHORT WRTMSG
  707.     XOR    AL,AL        ;[VAC001]
  708.     JMP    LEAVE
  709.  
  710. NOTACTIV:
  711.     MOV    DX,OFFSET INACTIV
  712.                 ; Already loaded and inactive
  713.     CALL    SHORT WRTMSG    ; Do you want to make us active?
  714.     CALL    ASK
  715.     JNE    ASKDOC
  716.     MOV    BYTE PTR ES:ACTIVEF,1
  717.                 ; Make us active
  718.     MOV    DX,OFFSET OKACTM
  719.     CALL    SHORT WRTMSG
  720.     XOR    AL,AL        ;[VAC001]
  721.     JMP    LEAVE
  722.  
  723. NOTLOADED:
  724.     MOV    BX,OFFSET INTSTK;[VAC001] Get the INT 10H stack location
  725.     MOV    INTSTK,BX    ;[VAC001] Remember it for out INT 10H handler
  726.     PUSH    CS
  727.     POP    ES
  728.     XOR    BH,BH
  729.     MOV    BL,DS:[0080H]    ; Look for a file name in the FCB1 location
  730.     OR    BL,BL        ; (BL has the length of    the string)
  731.     JNZ    SHORT GOTNAME
  732.     MOV    DX,OFFSET NOFILEM
  733.     CALL    SHORT WRTMSG
  734.  
  735. ASKDOC:    MOV    DX,OFFSET WOULDM
  736.     CALL    SHORT WRTMSG
  737.     CALL    SHORT ASK
  738.     JE    JINSTR        ;[VAC001]
  739.     XOR    AL,AL        ;[VAC001]
  740.     JMP    LEAVE
  741.  
  742. JINSTR:    JMP    INSTR        ;[VAC001]
  743.  
  744. GOTNAME:MOV    CX,BX
  745.     ADD    CX,2        ; Save length for later
  746.     ADD    BX,81H        ; Put null byte    at end to make ASCIIZ string
  747.     MOV    BYTE PTR [BX],0
  748.     MOV    BX,80H
  749.  
  750.     ; Find actual start of filename
  751. FINDNM:    DEC    CX
  752.     INC    BX
  753.     CMP    BYTE PTR [BX],SPACE
  754.                 ; by skipping leading spaces
  755.     JE    FINDNM
  756.     CMP    BYTE PTR [BX],TAB
  757.                 ; and tabs
  758.     JE    FINDNM
  759.  
  760.     PUSH    BX        ; Save the FCB1 filename pointer
  761.     MOV    DI,OFFSET FILEN    ; Where    to put the filename
  762.  
  763.     ;[VAC001]
  764.     ;[VAC001] Now see if the user passed us a pathname or device spec; if
  765.     ;[VAC001] not, then we'll default one for him.
  766.     ;[VAC001]
  767.     INC    BX        ;[VAC001] Move to the second filespec byte
  768.     CMP    BYTE PTR [BX],":"
  769.                 ;[VAC001] Is it a colon (a disk spec)?
  770.     JE    GOTPATH        ;[VAC001] If so, then user gave us a legit path
  771.  
  772.     ; User didn't give us a device to use, so we'll use the current disk
  773.     MOV    AH,19H        ;[VAC001] "Get current disk"
  774.     INT    21H        ;[VAC001]
  775.     MOV    DL,AL        ;[VAC001] Put a copy of it where 47H needs it
  776.     ADD    AL,"A"        ;[VAC001]
  777.     MOV    BYTE PTR DS:[DI],AL
  778.                 ;[VAC001] Put current disk drive in the buffer
  779.     INC    DI        ;[VAC001] Increment the buffer pointer
  780.     MOV    BYTE PTR DS:[DI],":"
  781.                 ;[VAC001] Put a colon in the buffer
  782.     INC    DI        ;[VAC001] Increment the buffer pointer
  783.     POP    BX        ;[VAC001] Restore the original byte pointer
  784.     PUSH    BX        ;[VAC001] Put the byte pointer on the stack
  785.  
  786.     CMP    BYTE PTR DS:[BX],"\"
  787.                 ;[VAC001] Is the first filespec byte a path
  788.                 ;[VAC001] designator?
  789.     JE    GOTPATH        ;[VAC001] If so, then proceed
  790.     MOV    BYTE PTR DS:[DI],"\"
  791.                 ;[VAC001] Otherwise, put a leading pathname
  792.                 ;[VAC001] designator in the buffer (DOS
  793.                 ;[VAC001] function 47H won't do it for us)
  794.     INC    DI        ;[VAC001] Increment the buffer pointer
  795.     MOV    AH,47H        ;[VAC001] Specify DOS function 47H
  796.     MOV    SI,DI        ;[VAC001] Put DI into SI for functin 47H
  797.     INC    DL        ;[VAC001] Increment current disk (saved above)
  798.     INT    21H        ;[VAC001] Put our current directory in the file
  799.                 ;[VAC001] name buffer
  800.     MOV    DI,OFFSET FILEN    ;[VAC001] Get the original buffer pointer back
  801.     ADD    DI,2        ;[VAC001] Skip past the device spec
  802.  
  803. EPLOOP:    MOV    DL,DS:[DI]    ;[VAC001] Get a byte from the filename buffer
  804.     OR    DL,DL        ;[VAC001] Is this a NUL byte (the end)?
  805.     JZ    ENDPATH        ;[VAC001] If so, then proceed
  806.     INC    DI        ;[VAC001] Otherwise, increment to the next byte
  807.     MOV    DH,DL        ;[VAC001] Save this byte as "last byte seen"
  808.     JMP    EPLOOP        ;[VAC001] And go check the next byte
  809.  
  810. ENDPATH:CMP    DH,"\"        ;[VAC001] Was the "last byte seen" a pathname
  811.                 ;[VAC001] terminator?
  812.     JE    GOTPATH        ;[VAC001] If so, then proceed
  813.     MOV    BYTE PTR DS:[DI],"\"
  814.                 ;[VAC001] Otherwise, put a pathname designator
  815.                 ;[VAC001] at the end of the filename buffer
  816.     INC    DI        ;[VAC001] And increment the buffer pointer
  817.  
  818.     ;
  819.     ; Now put the filespec passed by the user into our filename buffer, and
  820.     ; convert it to uppercase.
  821.     ;
  822.     ; Enter with DI pointing to the buffer, and the pointer to the FCB
  823.     ; filespec on the top of the stack.
  824.     ;
  825. GOTPATH:POP    BX        ;[VAC001] Get the FCB filename pointer
  826.  
  827. GPLOOP:    MOV    DL,BYTE PTR DS:[BX]
  828.                 ;[VAC001] Get a filespec byte
  829.     CMP    DL,"a"        ;[VAC001] Is it above the range of a lowercase
  830.                 ;[VAC001] character?
  831.     JL    NOTLC        ;[VAC001] If not, then proceed
  832.     CMP    DL,"z"        ;[VAC001] Is it a lowercase character?
  833.     JG    NOTLC        ;[VAC001] If not, then proceed
  834.     XOR    DL,32D        ;[VAC001] Otherwise, convert it to uppercase
  835.  
  836. NOTLC:    MOV    BYTE PTR DS:[DI],DL
  837.                 ;[VAC001] Put the character in our buffer
  838.     INC    BX        ;[VAC001] Increment the FCB filename byte ptr.
  839.     INC    DI        ;[VAC001] Increment our buffer pointer
  840.     OR    DL,DL        ;[VAC001] Set the CPU flags
  841.     JNZ    GPLOOP        ;[VAC001] Loop if the character isn't a NUL
  842.  
  843.     INC    DI        ;[VAC001] Otherwise, increment past the NUL
  844.     MOV    BYTE PTR DS:[DI],"$"
  845.                 ;[VAC001] And insert a DOS string terminator
  846.     CALL    APPENDF        ;[VAC001] Open the output file in "append" mode
  847.     JNC    CREATEOK
  848.     MOV    DX,OFFSET PROBM
  849.     CALL    SHORT WRTMSG
  850.     MOV    DI,OFFSET FILEN    ;[VAC001] Couldn't open the output file, so
  851.     CALL    SHORT PRTSTR    ;[VAC001] tell the user
  852.     MOV    DX,OFFSET QUOTEND
  853.                 ;[VAC001] Finish up the message
  854.     CALL    SHORT WRTMSG    ;[VAC001]
  855.     MOV    AL,-1        ;[VAC001]
  856.     JMP    LEAVE        ; And go exit back to the DOS CLI
  857.  
  858. CREATEOK:
  859.     CALL    CLOSEF
  860.     CALL    SCRINFO        ;[VAC001] Set the CS:EGA flag word
  861.     POP    DX        ;[VAC001] Clean up the stack (we don't care
  862.     POP    DX        ;[VAC001] about the screen size)
  863.  
  864.     ; Now install our interrupt handler in place of    PrtSc
  865.  
  866.     ;[VAC001] And install our INT 10H handler too...
  867.     MOV    AH,35h
  868.     MOV    AL,5
  869.     INT    21H        ; Get old interrupt vector
  870.     MOV    OLD5V1,BX
  871.     MOV    OLD5V2,ES    ; and save them
  872.     MOV    AL,10h        ;[VAC001]
  873.     INT    21H        ;[VAC001]
  874.     MOV    OLD10V1,BX    ;[VAC001]
  875.     MOV    OLD10V2,ES    ;[VAC001]
  876.     MOV    AH,25H        ; Set our interrupt handler entry
  877.     MOV    AL,5        ;[VAC001]
  878.     MOV    DX,OFFSET INTVEC; DS already has CS
  879.     INT    21H
  880.     MOV    AL,10H        ;[VAC001]
  881.     MOV    DX,OFFSET INTTENH
  882.                 ;[VAC001]
  883.     INT    21H        ;[VAC001]
  884.     MOV    BYTE PTR CS:ACTIVEF,1
  885.                 ; Mark our handler active
  886.     MOV    DX,OFFSET INITMSG
  887.                 ; and say we are loaded
  888.     MOV    AH,9
  889.     INT    21H
  890.     MOV    DI,OFFSET FILEN    ;[VAC001]
  891.     CALL    PRTSTR        ;[VAC001]
  892.     MOV    DX,OFFSET QUOTEND
  893.                 ;[VAC001]
  894.     MOV    AH,9        ;[VAC001]
  895.     INT    21H        ;[VAC001]
  896.  
  897.     ; Now exit
  898.     MOV    DX,OFFSET IEND    ; Figure out how many resident SAVSCR code
  899.                 ; bytes there are
  900.     ADD    DX,MAXCHRS    ;[VAC001] Leave room for a screen full of chars
  901.     ADD    DX,0FH        ;[VAC001] Round up to the next paragraph
  902.     MOV    CL,4
  903.     SHR    DX,CL        ; Convert to the number of paragraphs to stay
  904.                 ; resident in memory by shifting the byte count
  905.                 ; in DX by 4 bits to the right (which is the
  906.                 ; same as dividing it by 16 since 16=2^4)
  907.     MOV    AH,31H
  908.     XOR    AL,AL        ;[VAC001]
  909.     INT    21H        ; Terminate and    stay resident
  910.  
  911. INSTR:    MOV    DX,OFFSET DOCM
  912.     CALL    WRTMSG
  913.     XOR    AL,AL        ;[VAC001]
  914.     JMP    LEAVE
  915.  
  916. ;    $PAGE -----------------------------------------------------------------
  917.  
  918. ASK    PROC
  919.     MOV    AH,0CH        ; Clear    keyboard buffer
  920.     MOV    AL,1        ; keyboard input
  921.     INT    21H
  922.     PUSH    AX
  923.     MOV    DX,OFFSET CRLF    ; Go to    new line after answer
  924.     CALL    WRTMSG
  925.     POP    AX
  926.     AND    AL,7FH-20H    ; Make sure that the answer is upper-case
  927.     CMP    AL,'Y'
  928.     RET
  929. ASK    ENDP
  930.  
  931. ;    $PAGE -----------------------------------------------------------------
  932.  
  933.     ; Procedure to display a message on the    display    screen
  934. WRTMSG    PROC
  935.     MOV    AX,CS
  936.     MOV    DS,AX
  937.     MOV    AH,9H
  938.     INT    21H
  939.     RET
  940. WRTMSG    ENDP
  941.  
  942. ;    $PAGE -----------------------------------------------------------------
  943.  
  944. ;[VAC001]+
  945.  
  946. APPENDF    PROC    NEAR
  947.     MOV    DX,OFFSET FILEN
  948.     MOV    AL,1        ; Write    access
  949.     MOV    AH,3DH
  950.     INT    21H
  951.     JNC    APPMSG
  952.     PUSHF
  953.     CMP    AX,2
  954.     JE    APPND2
  955.     POPF
  956.     RET
  957.  
  958. APPND2:    POPF
  959.     CALL    CREATEF
  960.     RET
  961.  
  962. APPMSG:    PUSH    AX
  963.     MOV    DX,OFFSET FEXISTS
  964.     CALL    SHORT WRTMSG
  965.     POP    AX
  966.  
  967. APPEOF:    MOV    CS:FILEH,AX    ; Save file handle
  968.     MOV    BX,AX
  969.     MOV    AL,2        ; Move to end of file
  970.     XOR    CX,CX        ; (faster than MOV rX,0)
  971.     XOR    DX,DX
  972.     MOV    AH,42H
  973.     INT    21H
  974.     RET
  975.  
  976. APPENDF    ENDP
  977.  
  978. ;    $PAGE -----------------------------------------------------------------
  979.  
  980. PRTSTR    PROC NEAR
  981.     ;
  982.     ; Routine to print an ASCIIZ string
  983.     ;
  984.     ; Enter with DS:DI pointing to the string to be printed.  All registers
  985.     ; are preserved.
  986.     ;
  987.     PUSH    AX
  988.     PUSH    DX
  989.     PUSH    DI
  990.  
  991.     MOV    AH,2
  992.  
  993. L00:    MOV    DL,DS:[DI]
  994.     OR    DL,DL
  995.     JZ    L01
  996.     INT    21H
  997.     INC    DI
  998.     JMP    L00
  999.  
  1000. L01:    POP    DI
  1001.     POP    DX
  1002.     POP    AX
  1003.     RET
  1004.  
  1005. PRTSTR    ENDP
  1006.  
  1007. ;[VAC001]-
  1008.  
  1009. ;    $PAGE -----------------------------------------------------------------
  1010.  
  1011.     ; Enter with AL containing a return code (may be used with DOS "IF" and
  1012.     ; "ERRORLEVEL" batch subcommands)
  1013. LEAVE:    MOV    AH,4CH        ;[VAC001] "Terminate a Process"
  1014.     INT    21H        ;[VAC001]
  1015.  
  1016. ;    $PAGE -----------------------------------------------------------------
  1017.  
  1018. SAVSCR    ENDP
  1019. S00000    ENDS
  1020.  
  1021.     END    BEGIN
  1022.