home *** CD-ROM | disk | FTP | other *** search
- PAGE ,132
- TITLE SAVSCR
-
- COMMENT \
- Usage: "SAVSCR filename". SAVSCR makes itself resident and handles
- PrtSc interrupts (interrupt 5). When the Shift-PrtSc keys are
- depressed, whatever is on the text screen will be saved in a
- disk file. You might do this in the middle of any program
- where you want to save a copy of what you see on the screen.
- You can do this repeatedly, and each screen will be appended to
- the file and separated by a form feed. Later you can edit this
- file and use it to document your programs.
-
- If there is a problem writing to the file, you will hear a
- "beep".
-
- Written by Ted Shapin, 9/26/85 (Thanks to the unknown author of
- SAVSC.COM).
-
- Edit History:
-
- Edit ID Who Date Description
- -------- --- -------- -----------------------------------------------
- [VAC001] VAC 12/31/85 (VAC=Vince Cuomo) - Added code to:
-
- 1) Properly handle EGA/ECD hardware in
- "43 line" mode;
- 2) Provide extra pathname support in the
- filespec;
- 3) Prevent screen saves when in graphics
- video modes (modes 4 through 6, and
- greater than 7);
- 4) Prevent system hangs when a user
- presses Shift-PrtSc while we're inside
- this code;
- 5) Support screens pages 1 through 7
- (CGA/PGA/EGA adapters only);
- 6) Initially open the output file in
- "append" mode (instead of creating the
- output file from scratch), so that the
- user doesn't accidentally lose useful
- screen images;
- 7) Make sure that we don't do anything
- while the INT 10H code in the BIOS is
- executing (because SAVSCR needs to use
- INT 10H also, and the BIOS isn't
- reentrant); instead, set a flag so
- that our INT 10H "shell" will perform
- an INT 5 if SAVSCR was invoked while
- INT 10H was executing;
- 8) Put some of the references to absolute
- memory addresses in the ROM BIOS data
- area back in the code to reduce the
- time it takes SAVSCR to execute and to
- reduce the chances of system "hangs"
- (absolute memory addresses really
- aren't much of a problem since we're
- already assuming that the monochrome
- and CGA/PGA/EGA adapter buffer
- addresses are at B000 or B800, plus
- there doesn't appear to be a clean way
- to get the video page address offset
- except to look directly at the BIOS
- data area in word 0000:044E);
- 9) Set the IBM-PC BIOS's "performing a
- PrtSc operation now" flag (memory
- location 0050:0000h) to 1 as
- documented in the BIOS code and in
- Peter Norton's book.
-
- Also cleaned up some of the SAVSCR messages
- and .DOC file, and did some general code
- "cleanup" (bet ya' won't recognize the code
- anymore Ted...).
-
- \
-
- ; $PAGE -----------------------------------------------------------------
-
- ;[VAC001]
- ;[VAC001] The following equates are the absolute memory addresses that SAVSCR
- ;[VAC001] uses. If references to additional absolute memory locations are
- ;[VAC001] needed, update these equates so that the memory locations are all
- ;[VAC001] defined in a single place in the SAVSCR code.
- ;[VAC001]
-
- B$CVPAO EQU 44EH ;[VAC001] BIOS Current Video Page Address
- ;[VAC001] Offset
- B$PSFLG EQU 50H ;[VAC001] BIOS "Performing PrtSc" Flag Segment
- ;[VAC001] Address
- B$GABAO EQU 0B800H ;[VAC001] BIOS Graphics Adapter Buffer Address
- ;[VAC001] Offset
- B$MABAO EQU 0B000H ;[VAC001] BIOS Monochrome Adapter Buffer
- ;[VAC001] Address Offset
-
- ; $PAGE -----------------------------------------------------------------
-
- BELL EQU 7 ; ASCII code (decimal) for a bell
- CR EQU 13 ; ASCII code (decimal) for a carriage-return
- LF EQU 10 ; ASCII code (decimal) for a line-feed
- SPACE EQU 32 ; ASCII code (decimal) for a space
- TAB EQU 9 ; ASCII code (decimal) for a tab
-
- MAXCHRS EQU 43d*132d ;[VAC001] The maximum number of display screen
- ;[VAC001] characters that we'll need to write
- ;[VAC001] out (25x80 isn't good enough because
- ;[VAC001] EGA/ECD hardware can be set to "43
- ;[VAC001] line" mode, and some of the newer
- ;[VAC001] video cards finally support 132
- ;[VAC001] columns)
-
- ; $PAGE -----------------------------------------------------------------
-
- S00000 SEGMENT BYTE PUBLIC 'CODE'
- ASSUME CS:S00000
-
- DB 100H DUP (0) ; Space for the INT 5 stack
- I5STK EQU 00FFH ;[VAC001] Pointer to the top-of-INT 5 stack
-
- SAVSCR PROC FAR
-
- BEGIN: ORG 100H ; All PC/MS-DOS .COM programs start at 100H
- JMP START ; Start address when run from COMMAND.COM
-
- ; $PAGE -----------------------------------------------------------------
-
- ;
- ; The following are storage allocations for various SAVSCR control variables.
- ;
- SIGNAT DW 2234H ; Signature of this routine
- OLD5V1 DW ? ; Storage for the old INT 5 (PrtSc) interrupt
- OLD5V2 DW ? ; vector
- ACTIVEF DB 0 ; Active flag (go to DOS INT 5 handler if 0)
- FILEH DW ? ; Storage for the output file handle
- CRLF DB CR,LF,'$' ; DOS INT 21H function code 9 format CR/LF
- FORMFD DB 0CH ; ASCII form-feed character
- EXITSP DW ? ;[VAC001] Storage for the SP value for IEXIT
- EGA DB 0 ;[VAC001] EGA hardware flag
- I5LOCK DB 0 ;[VAC001] "Ignore INT 5" flag
- I10LOCK DB 0 ;[VAC001] "INT 10H executing" flag
- IN_I10 DB 0 ;[VAC001] A "second level" INT 10H flag
- DOINT5 DB 0 ;[VAC001] "Do INT 5 after INT 10H" flag
- OLD10V1 DW ? ;[VAC001] Storage for INT 10h vector
- OLD10V2 DW ? ;[VAC001] " " " " "
- CURSMOD DW ? ;[VAC001] Storage for the cursor mode at entry
- CURSLOC DW ? ;[VAC001] Storage for the cursor location
- CURSCHR DW ? ;[VAC001] Storage for the char at cursor loc.
- SCRROWS DW ? ;[VAC001] Storage for the number of screen rows
- SCRCOLS DW ? ;[VAC001] Storage for the number of screen cols
- VIDSEGM DW ? ;[VAC001] Storage for the video buffer segment
- VIDEOM DB ? ;[VAC001] Storage for the current video mode
- ACTIVEP DB ? ;[VAC001] Storage for the active video page
- IN10_SS DW ? ;[VAC001] Storage for the SS register
- IN10_SP DW ? ;[VAC001] Storage for the SP register
- IN10_AX DW ? ;[VAC001] Storage for the AX register
- INT5_SS DW ? ;[VAC001] Storage for the SS register
- INT5_SP DW ? ;[VAC001] Storage for the SP register
- INT5_AX DW ? ;[VAC001] Storage for the AX register
- DW 100 DUP (0) ;[VAC001] INT 10H handler code stack
- INTSTK DW ? ;[VAC001] Mark the start of the INT 10H stack
-
- FILEN DB 75D DUP (0) ;[VAC001] Filename (paths OK)
-
- ; $PAGE -----------------------------------------------------------------
-
- ;[VAC001]+ (Start of edit VAC001 code block)
-
- ;
- ; This routine will be called in place of INT 10H (the normal BIOS
- ; interrupt code for video service). If an interrupt is being issued
- ; to set the screen to "43 line" mode (EGA/ECD hardware only), then we
- ; need to "trap" the call so that we can reset the INT 5 vector to our
- ; code again (for some reason, the firmware in the EGA resets the
- ; INT 5 vector, which means that SAVSCR is permanently deactivated).
- ; Also, we need to block our INT 5 handler when INT 10H code is
- ; executing in order to help prevent system "hangs".
- ;
- INTTENH:CLI ; Disable interrupts for now...
- PUSHF ; Save the CPU flags
- TEST BYTE PTR CS:IN_I10,1
- ; Did INT 10H call itself (who knows...), or
- ; did INT 5 call INT 10H?
- JZ INT10OK ; If not, then continue
- POPF ; If so, then restore the CPU flags
- JMP DWORD PTR CS:OLD10V1
- ; And go do a normal INT 10H call
-
- INT10OK:POPF ; Restore the CPU flags
- MOV BYTE PTR CS:IN_I10,1
- ; Set the "second level" lockout flag...
- MOV BYTE PTR CS:I10LOCK,1
- ; Set the "INT 10H executing" flag
- MOV CS:IN10_SS,SS ; Save the SS register
- MOV CS:IN10_SP,SP ; Save the SP register
- MOV CS:IN10_AX,AX ; Save the AX register
- PUSH CS ; Get our code segment address
- POP SS ; Make the INT 10H stack be in the same segment
- MOV SP,CS:INTSTK ; Make SP point to the beginning of the stack
- PUSHF ; Store the current CPU flags on the stack
- PUSH CS ; Store our code segment on the stack
- PUSH DS ; Store the original DS value on the stack
- PUSH CS ; Get our CS value
- POP DS ; And set DS to be the same as CS
- MOV AX,OFFSET INTTENR
- ; Get the return address for INT 10H
- POP DS ; Restore the original DS value
- PUSH AX ; Save the return address in our INT 10H
- ; handler on the stack (now INT 10H will IRET
- ; back into our code so that we can reset the
- ; INT 5 vector to us, if needed)
- MOV AX,CS:IN10_AX ; Restore the original contents of AX
-
- JMP DWORD PTR CS:OLD10V1
- ; And go to the BIOS's INT 10H code
-
- ;
- ; Get here after the real INT 10H code in the ROM BIOS is finished
- ;
- INTTENR:NOP ; Make sure that INT 10H really IRETs to the
- NOP ; right place...
- PUSH DX ; Save DX for now
- PUSH DS ; Save the current DS register contents
- PUSH CS ; Get the current CS register contents
- POP DS ; Set DS to be the same as CS for function 25H
- MOV DX,IN10_AX ; Get the INT 10H function code
- MOV IN10_AX,AX ; Save the AX value that INT 10H returned
- TEST BYTE PTR DS:EGA,1
- ; Is there an EGA card on this machine?
- JZ CHKINT5 ; If not, go see if we got an INT 5 request
- CMP DH,12H ; If so, then is this the 12H function code?
- JNE CHKINT5 ; If not, go see if we got an INT 5 request
- STI ; Enable interrupts again...
- MOV AH,25H ; And make sure that the interrupt handler
- MOV AL,5 ; for INT 5
- MOV DX,OFFSET INTVEC; is pointed at the code in SAVSCR
- INT 21H ; and is still active
- CLI ; Disable interrupts...
-
- CHKINT5:TEST BYTE PTR DS:DOINT5,1
- ; Did the user request a "SAVSCR" while INT 10H
- ; was executing?
- JZ I10EXIT ; If not, then go exit from the INT 10H code
- MOV BYTE PTR DS:DOINT5,0
- ; Otherwise, clear the "Do an INT 5" flag
- MOV BYTE PTR DS:I10LOCK,0
- ; Reset the INT 10H lockout flag
- STI ; Enable interrupts again...
- INT 5 ; And do an INT 5 to save the screen image
- MOV BYTE PTR DS:I10LOCK,1
- ; Set the INT 10H lockout flag again for a bit
-
- I10EXIT:POP DS ; Restore the original DS register contents
- POP DX ; Restore the original DX register contents
- MOV AX,CS:IN10_SS ; Get the original SS contents
- MOV SS,AX ; Restore the user's SS register
- MOV AX,CS:IN10_SP ; Get the original SP contents
- MOV SP,AX ; Restore the user's SP register
- MOV AX,CS:IN10_AX ; Restore the AX register
- MOV BYTE PTR CS:I10LOCK,0
- ; Clear the INT 10H lockout flag
- MOV BYTE PTR CS:IN_I10,0
- ; Clear the "second level" lockout flag too...
- IRET ; And return to the original caller's code
-
- ;[VAC001]- (End of edit VAC001 code block)
-
- ; $PAGE -----------------------------------------------------------------
-
- INTVEC: ; The INT 5 interrupt handler code starts here...
- CLI ;[VAC001] Disable interrupts
- TEST BYTE PTR CS:I5LOCK,1
- ;[VAC001] Are we executing INT 5 right now?
- JZ INT5OK ;[VAC001] If not, then continue
- IRET ;[VAC001] Otherwise, return right now...
-
- INT5OK: MOV BYTE PTR CS:I5LOCK,1
- ;[VAC001] Lock out any other INT 5's for now...
- TEST BYTE PTR CS:I10LOCK,1
- ;[VAC001] Is the INT 10H code executing now?
- JZ I10CLEAR ;[VAC001] If not, then go do the INT 5 code
- MOV BYTE PTR CS:DOINT5,1
- ;[VAC001] Otherwise, tell our INT 10H "shell"
- ;[VAC001] to activate the INT 5 code when thru
- MOV BYTE PTR CS:I5LOCK,0
- ;[VAC001] Reset the INT 5 lock out flag byte
- IRET ;[VAC001] And exit until called again...
-
- I10CLEAR: ;[VAC001]
- MOV CS:INT5_SS,SS ;[VAC001] Save caller's SS value
- MOV CS:INT5_SP,SP ;[VAC001] Save caller's SP value
- PUSH CS ;[VAC001]
- POP SS ;[VAC001] Initialize our stack segment address
- MOV SP,I5STK ;[VAC001] Initialize our stack pointer
- PUSH DS ; Save the caller's DS contents
- PUSH CS:INT5_AX ;[VAC001] Save the caller's AX contents
- TEST BYTE PTR CS:ACTIVEF,1
- ; Are we active?
- JNZ DOIT ; Yes, we are
- POP AX ;[VAC001] So restore AX
- POP DS ; Restore DS
- MOV SS,CS:INT5_SS ;[VAC001] Restore the original SS contents
- MOV SP,CS:INT5_SP ;[VAC001] Restore the original SP contents
- MOV BYTE PTR CS:I5LOCK,0
- ;[VAC001] Reset the INT 5 lock out flag byte
- JMP DWORD PTR CS:OLD5V1
- ; And go to the old INT 5 handler
-
- DOIT: PUSH BX
- PUSH CX
- PUSH DX
- PUSH SI
- PUSH DI
- PUSH ES
- PUSH BP ;[VAC001] (INT 10H can destroy the BP register)
- MOV CS:EXITSP,SP ;[VAC001] Remember the current stack pointer
- STI ; Allow interrupts
- CALL SCRINFO ;[VAC001] Get needed display screen information
- MOV SI,B$PSFLG ;[VAC001] Get the "performing PrtSc" flag's
- ;[VAC001] segment address
- MOV ES,SI ;[VAC001] Set ES
- XOR SI,SI ;[VAC001] Set SI to zero
- MOV BYTE PTR ES:[SI],1
- ;[VAC001] Set the "performing PrtSc" flag to 1
- ;[VAC001] (as documented in the BIOS code and
- ;[VAC001] in Peter Norton's book)
- MOV ES,SI ;[VAC001] Point ES at the ROM BIOS data segment
- MOV AX,B$GABAO ;[VAC001] Assume we're using a CGA/PGA/EGA card
- CMP BYTE PTR CS:VIDEOM,4
- ;[VAC001] Are we in modes 0-3 (text modes)?
- JL TXTMODE ;[VAC001] If so, then continue
- CMP BYTE PTR CS:VIDEOM,7
- ;[VAC001] If not, are we in monochrome mode?
- JNE IEXITNC ;[VAC001] If not, then go exit now (graphics
- ;[VAC001] mode screens look like junk when
- ;[VAC001] saved because the video fields are
- ;[VAC001] different)
- MOV AX,B$MABAO ;[VAC001] Set the buffer address for mono cards
-
- TXTMODE:MOV CS:VIDSEGM,AX ;[VAC001] Save the video RAM segment address
- MOV AH,1 ;[VAC001] "Set cursor type"
- MOV CX,2010H ;[VAC001] Make the cursor invisible
- INT 10H ;[VAC001]
- MOV AH,8 ;[VAC001] "Read attr/char at curr. curs. loc."
- MOV BH,CS:ACTIVEP ;[VAC001] Get the current active video page no.
- INT 10H ;[VAC001] Get the attibute/character
- MOV CS:CURSCHR,AX ;[VAC001] Save them...
- MOV SI,B$CVPAO ;[VAC001] Get the offset of the current video
- ;[VAC001] page address offset
- MOV SI,ES:[SI] ;[VAC001] Get the current video page address
- ;[VAC001] offset (this provides support for
- ;[VAC001] screen pages 1 through 7 on
- ;[VAC001] CGA/PGA/EGA cards) for MLOOP below...
- PUSH CS ;[VAC001] Get our CS value
- POP DS ;[VAC001] And set DS to our code segment value
- MOV DI,OFFSET IEND ; Our buffer is at the end of the INT 5 service
- ; routine in our code segment
- MOV CX,CS:SCRCOLS ;[VAC001] Get the number of columns to save
- MOV AX,CS:SCRROWS ;[VAC001] Get the number of rows to save
- MOV DS,CS:VIDSEGM ;[VAC001] Set DS to the current video RAM seg.
- MUL CX ;[VAC001] Put number of chars to save in AX
- XCHG AX,CX ;[VAC001] Put the number of chars in CX for the
- ;[VAC001] LOOP instruction
- CLD ;[VAC001] We want SI to be incremented
-
- MLOOP: LODSW ; Load a word from the video RAM segment
- MOV BYTE PTR CS:[DI],AL
- ;[VAC001] Put the byte in our buffer
- INC DI ;[VAC001] Increment the buffer pointer
- LOOP MLOOP ; Save display screen bytes until CX=0
-
- PUSH CS ; Get our CS contents
- POP DS ; And put it in DS
- CALL OPENEND ; Open the file and position to the end
- MOV SI,OFFSET IEND ; Point to our buffer
- MOV CX,CS:SCRCOLS ;[VAC001] Put number of screen columns in CX
- MOV DI,CS:SCRROWS ;[VAC001] Put number of screen rows in DI
- CALL WRITESC ;[VAC001] Write out the display screen lines
- CALL CLOSEF ; and close the output file
-
- IEXIT: MOV AX,CS:CURSCHR ;[VAC001] Get the char/attrib. to write
- MOV BL,AH ;[VAC001] Put the attribute where needed
- MOV AH,9 ;[VAC001] "Write attr/char at cursor location"
- MOV BH,CS:ACTIVEP ;[VAC001] Get the active video page
- MOV CX,1 ;[VAC001] Only write one character
- INT 10H ;[VAC001] Do it...
- MOV AH,1 ;[VAC001] "Set cursor type"
- MOV CX,CS:CURSMOD ;[VAC001] Get the original cursor mode
- INT 10H ;[VAC001] Restore the cursor mode to its value
- ;[VAC001] at entry
-
- IEXITNC:MOV SI,B$PSFLG ;[VAC001] Get the "performing PrtSc" flag's
- ;[VAC001] segment address
- MOV ES,SI ;[VAC001] Set ES
- XOR SI,SI ;[VAC001] Set SI to zero
- MOV BYTE PTR ES:[SI],0
- ;[VAC001] Set "performing PrtSc" flag back to 0
- POP BP ;[VAC001]
- POP ES
- POP DI
- POP SI
- POP DX
- POP CX
- POP BX
- POP AX
- POP DS
- MOV SS,CS:INT5_SS ;[VAC001]
- MOV SP,CS:INT5_SP ;[VAC001]
- MOV BYTE PTR CS:I5LOCK,0
- ;[VAC001] Tell the world that we're inactive
- IRET
-
- ; $PAGE -----------------------------------------------------------------
-
- ;[VAC001]+ (Beginning of edit VAC001 code block)
-
- SCRINFO PROC NEAR
- ;
- ; Determine the following screen information:
- ;
- ; EGA (byte) If 1, then we're running on an active EGA card
- ; VIDEOM (byte) The current video mode
- ; ACTIVEP (byte) The current active video page
- ; SCRROWS (word) The number of display screem rows
- ; SCRCOLS (word) The number of display screen columns
- ; CURSMOD (word) The cursor mode
- ; CURSLOC (word) The cursor location
- ;
- MOV AX,1130H ; EGA function code, "information" subfunction
- ; code
- PUSH BP ; Save the current BP contents
- PUSH ES ; Save the current ES contents
- XOR BH,BH ; The pointer value to be returned in ES:BP (we
- ; don't care about it...)
- MOV DX,-1 ; Put a ridiculous value in DX as the number of
- ; screen rows (on PC's which don't have the EGA
- ; installed, this value won't be changed when
- ; INT 10h returns to us; otherwise, DL will
- ; contain the last row number on the display
- ; screen)
- INT 10H ; Do it...
- POP ES ; Restore ES
- POP BP ; Restore BP
- MOV BYTE PTR CS:EGA,0
- ; Assume that we don't have an active EGA card
- XOR DH,DH ; Clear DX's high byte
- CMP DL,23D ; Are there at least 24 lines on the display
- ; screen?
- JL USE_25R ; If not, then assume 25 rows
- CMP DL,68D ; Are we set to a believable number?
- JG USE_25R ; If not, then assume that function 11h isn't
- ; implemented in this machine's BIOS (i.e.,
- ; the EGA card is not installed or is not
- ; active), and default to a 25 row display
- ; screen
- MOV BYTE PTR CS:EGA,1
- ; Otherwise, set "active EGA card" flag byte
- JMP GOT_NR ; And continue
-
- USE_25R:MOV DL,24D ; Assume row 24 is the last screen row...
-
- GOT_NR: INC DL ; Convert from offset zero to offset 1
- MOV CS:SCRROWS,DX ; Remember the number of screen rows
- MOV AH,15D ; "Current video state"
- INT 10H ; Get the current video state (places the
- ; current display page number in BH, the number
- ; of display screen columns in AH, and the
- ; current video mode in AL)
- MOV BYTE PTR CS:VIDEOM,AL
- ; Remember the current video mode
- MOV BYTE PTR CS:ACTIVEP,BH
- ; Remember the current active display page no.
- MOV DL,AH ; Put the last column number in DL
- XOR DH,DH ; Clear DH
- MOV CS:SCRCOLS,DX ; Remember the number of display screen columns
- MOV AH,3 ; "Read cursor position" (BH is set above...)
- INT 10H ; Get the current cursor mode and position
- MOV CS:CURSMOD,CX ; Save the current cursor mode
- MOV CS:CURSLOC,DX ; Save the current cursor location
- RET ; And return to our caller
-
- SCRINFO ENDP
-
- ;[VAC001]- (End of edit VAC001 code block)
-
- ; $PAGE -----------------------------------------------------------------
-
- OPENEND PROC NEAR
- MOV DX,OFFSET FILEN
- MOV AL,1 ; Write access
- MOV AH,3DH
- INT 21H
- JNC TOEOF
- CMP AX,2
- JE OPEN2
- CALL BEEP ; An open problem
- MOV SP,CS:EXITSP ;[VAC001] Fix up the stack pointer
- JMP IEXIT ; Go exit from SAVSCR
-
- OPEN2: CALL CREATEF
- JNC TOEOF ;[VAC001] If all is well, then position to EOF
- CALL BEEP ; Some kind of OPEN problem
- MOV SP,CS:EXITSP ;[VAC001] Fix up the stack pointer
- JMP IEXIT ;[VAC001] Go exit from SAVSCR
-
- TOEOF: MOV CS:FILEH,AX ; Save file handle
- MOV BX,AX
- MOV AL,2 ; Move to end of file
- XOR CX,CX ;[VAC001] (faster than MOV CX,0)
- XOR DX,DX
- MOV AH,42H
- INT 21H
- JC BADEOF ;[VAC001]
- RET
-
- BADEOF: CALL BEEP ;[VAC001] Some kind of problem
- MOV SP,CS:EXITSP ;[VAC001] Fix up the stack pointer
- JMP IEXIT ;[VAC001] Go exit from SAVSCR
-
- OPENEND ENDP
-
- ; $PAGE -----------------------------------------------------------------
-
- CREATEF PROC NEAR
- XOR CX,CX ; Attribute bits zero
- MOV AH,3CH ; Create a file or if it exists, set the
- INT 21H ; length to zero
- RET
-
- CREATEF ENDP
-
- ; $PAGE -----------------------------------------------------------------
-
- WRITESC PROC NEAR ;[VAC001] Enter with DI containing the number
- ;[VAC001] of display screen rows to be saved
- ;[VAC001] and CX containing the number of
- ;[VAC001] display screen columns to be saved,
- ;[VAC001] SI pointing to the start of the file
- ;[VAC001] buffer's area.
- PUSH DS ; Save the DS value at entry
- PUSH CS ;[VAC001] Get our CS value
- POP DS ;[VAC001] And set DS to be the same as CS
-
- WRITELP:PUSH CX ;[VAC001] Save the number of screen columns
- PUSH DI ;[VAC001] Save the number of screen rows
- MOV BX,CS:FILEH ;[VAC001] Put the output file handle in BX
-
- ;[VAC001]
- ;[VAC001] Now strip off trailing spaces from the line image that we're
- ;[VAC001] going to write out to the output file.
- ;[VAC001]
- WL2: MOV DI,CX ;[VAC001] Get the current char count
- ADD DI,SI ;[VAC001] Add it to the line image's video
- ;[VAC001] buffer address
- DEC DI ;[VAC001] Correct for offset by 1 (needs to be
- ;[VAC001] offset by 0)
- MOV DL,BYTE PTR CS:[DI]
- ;[VAC001] Get that byte
- CMP DL,SPACE ;[VAC001] Is it a SPACE?
- JNE WL3 ;[VAC001] If not, then we've stripped off any
- ;[VAC001] trailing spaces
- LOOP WL2 ;[VAC001] Otherwise, it's a SPACE, so backup
- ;[VAC001] another character
- POP DI ;[VAC001] We can only get here when the entire
- ;[VAC001] line is spaces, so fix up the stack
- JMP WRITEOK ;[VAC001] And just go write out a CR/LF
-
- WL3: MOV AH,40H
- MOV DX,SI
- INT 21H ; Do the write
- POP DI ;[VAC001] Restore DI
- JC BADWRT ;[VAC001] If there's a problem, then go "beep"
-
- WRITEOK:MOV AH,40H
- MOV CX,2
- MOV DX,OFFSET CRLF
- INT 21H ; Write a CRLF at the end of the line
- JNC NXTADDR ; If all is well, then continue
-
- BADWRT: POP CX ;[VAC001] Restore CX
- POP DS ;[VAC001] Restore DS
- JMP BEEP ;[VAC001] And go "beep" at the user
-
- NXTADDR:POP CX ;[VAC001] Restore the number of screen columns
- ADD SI,CX ; Advance to the next line
- DEC DI
- JNE WRITELP
- MOV DX,OFFSET FORMFD
- MOV CX,1
- MOV AH,40H
- INT 21H ; Write a FORM FEED
- POP DS
- JC BEEP ;[VAC001] If something is wrong, then go "beep"
- RET
-
- ; $PAGE -----------------------------------------------------------------
-
- CLOSEF PROC NEAR
- MOV BX,CS:FILEH
- MOV AH,3EH
- INT 21H ; Close the output file
- JNC CLOSEOK ;[VAC001]
-
- BEEP: MOV DL,7 ; Some kind of file error occured so
- MOV AH,2 ; just send a "beep" and return
- INT 21H
-
- CLOSEOK:RET
-
- CLOSEF ENDP
-
- WRITESC ENDP ;[VAC001]
-
- ; $PAGE -----------------------------------------------------------------
-
- IEND: ;
- ; End of the resident code. File buffer area overlays the following.
- ;
-
- ; $PAGE -----------------------------------------------------------------
-
- NOFILEM DB 'You must specify an output filename such as "C:\PATH\SCREEN.OUT".',CR,LF,'$'
-
- BADDOS DB 'Sorry, SAVSCR must run under PC/MS-DOS V2.0 or greater.',CR,LF,'$'
-
- PROBM DB 'I am unable to create the output file "$'
-
- FEXISTS DB CR,LF
- DB 'Warning, that output file already exists. Screen output will be',CR,LF
- DB "appended to the output file's current contents.",CR,LF,'$'
-
- ALREADY DB CR,LF
- DB 'SAVSCR is already resident in memory and is active.',CR,LF,LF
- DB 'Would you like to disable SAVSCR and return to the original',CR,LF
- DB 'DOS PrtSc interrupt handler (Y/N)? $'
-
- INACTIV DB CR,LF
- DB 'SAVSCR is already resident in memory but is inactive.',CR,LF,LF
- DB 'Would you like to enable SAVSCR (Y/N)? $'
-
- OKINACM DB CR,LF
- DB 'OK, SAVSCR is now inactive. To re-activate SAVSCR, just',CR,LF
- DB 'type "SAVSCR" and answer the questions.',CR,LF,'$'
-
- OKACTM DB CR,LF
- DB 'OK, SAVSCR is now active again. Press Shift-PrtSc to save',CR,LF
- DB 'the display screen to the output file.',CR,LF,'$'
-
- INITMSG DB CR,LF
- DB 'The SAVSCR V1.1 (save display screen) program is now resident',CR,LF
- DB 'in memory. Press Shift-PrtSc to save the display screen in',CR,LF
- DB 'the output file "$'
-
- QUOTEND DB '".',CR,LF,'$'
-
- WOULDM DB 'Would you like instructions (Y/N)? $'
-
- OPNMSG DB 'Attempting to create screen image output file $'
-
- DOCM DB CR,LF
- DB ' Usage: "SAVSCR [[d:]\path\]filename.ext"',CR,LF,LF
- DB ' SAVSCR makes itself resident and handles PrtSc interrupts (BIOS',CR,LF
- DB ' interrupt 5). When the Shift and PrtSc keys are pressed AT THE',CR,LF
- DB ' SAME TIME, whatever is on the display screen will be written to',CR,LF
- DB ' a disk file. You might do this in the middle of a program when',CR,LF
- DB ' you want to save a copy of what you see on the screen. You may',CR,LF
- DB ' do this repeatedly, and each screen will be appended at the end',CR,LF
- DB ' of the file and will be separated by a form feed. Later you can',CR,LF
- DB ' edit or print this file and use it to document your programs or',CR,LF
- DB ' save useful information. If there is a problem writing to the',CR,LF
- DB ' output file, you will hear a "beep".',CR,LF,LF
- DB ' SAVSCR V1.0 9/25/85 by Ted Shapin',CR,LF
- DB ' SAVSCR V1.1 12/31/85 by Vince Cuomo',CR,LF
- DB '$'
-
- ; $PAGE -----------------------------------------------------------------
-
- START: MOV AX,CS ;[VAC001] Initial installation starts here
- MOV DS,AX ;[VAC001]
- MOV ES,AX ;[VAC001]
- MOV SS,AX ;[VAC001]
- MOV SP,I5STK ;[VAC001]
- MOV AH,30H ; Check DOS version
- INT 21H
- CMP AL,2
- JGE DOSOK
- MOV DX,OFFSET BADDOS
- CALL WRTMSG
- MOV AL,-1 ;[VAC001]
- JMP LEAVE
-
- DOSOK: MOV AH,35H ; Get interrupt vector
- MOV AL,5 ; for interrupt 5
- INT 21H
- CMP WORD PTR ES:SIGNAT,2234H
- JNE NOTLOADED
- TEST BYTE PTR ES:ACTIVEF,1
- JZ NOTACTIV
- MOV DX,OFFSET ALREADY
- ; Already loaded and active
- CALL SHORT WRTMSG
- CALL ASK ; Do you want to make us inactive?
- JNE ASKDOC
- MOV BYTE PTR ES:ACTIVEF,0
- ; Make us inactive
- MOV DX,OFFSET OKINACM
- CALL SHORT WRTMSG
- XOR AL,AL ;[VAC001]
- JMP LEAVE
-
- NOTACTIV:
- MOV DX,OFFSET INACTIV
- ; Already loaded and inactive
- CALL SHORT WRTMSG ; Do you want to make us active?
- CALL ASK
- JNE ASKDOC
- MOV BYTE PTR ES:ACTIVEF,1
- ; Make us active
- MOV DX,OFFSET OKACTM
- CALL SHORT WRTMSG
- XOR AL,AL ;[VAC001]
- JMP LEAVE
-
- NOTLOADED:
- MOV BX,OFFSET INTSTK;[VAC001] Get the INT 10H stack location
- MOV INTSTK,BX ;[VAC001] Remember it for out INT 10H handler
- PUSH CS
- POP ES
- XOR BH,BH
- MOV BL,DS:[0080H] ; Look for a file name in the FCB1 location
- OR BL,BL ; (BL has the length of the string)
- JNZ SHORT GOTNAME
- MOV DX,OFFSET NOFILEM
- CALL SHORT WRTMSG
-
- ASKDOC: MOV DX,OFFSET WOULDM
- CALL SHORT WRTMSG
- CALL SHORT ASK
- JE JINSTR ;[VAC001]
- XOR AL,AL ;[VAC001]
- JMP LEAVE
-
- JINSTR: JMP INSTR ;[VAC001]
-
- GOTNAME:MOV CX,BX
- ADD CX,2 ; Save length for later
- ADD BX,81H ; Put null byte at end to make ASCIIZ string
- MOV BYTE PTR [BX],0
- MOV BX,80H
-
- ; Find actual start of filename
- FINDNM: DEC CX
- INC BX
- CMP BYTE PTR [BX],SPACE
- ; by skipping leading spaces
- JE FINDNM
- CMP BYTE PTR [BX],TAB
- ; and tabs
- JE FINDNM
-
- PUSH BX ; Save the FCB1 filename pointer
- MOV DI,OFFSET FILEN ; Where to put the filename
-
- ;[VAC001]
- ;[VAC001] Now see if the user passed us a pathname or device spec; if
- ;[VAC001] not, then we'll default one for him.
- ;[VAC001]
- INC BX ;[VAC001] Move to the second filespec byte
- CMP BYTE PTR [BX],":"
- ;[VAC001] Is it a colon (a disk spec)?
- JE GOTPATH ;[VAC001] If so, then user gave us a legit path
-
- ; User didn't give us a device to use, so we'll use the current disk
- MOV AH,19H ;[VAC001] "Get current disk"
- INT 21H ;[VAC001]
- MOV DL,AL ;[VAC001] Put a copy of it where 47H needs it
- ADD AL,"A" ;[VAC001]
- MOV BYTE PTR DS:[DI],AL
- ;[VAC001] Put current disk drive in the buffer
- INC DI ;[VAC001] Increment the buffer pointer
- MOV BYTE PTR DS:[DI],":"
- ;[VAC001] Put a colon in the buffer
- INC DI ;[VAC001] Increment the buffer pointer
- POP BX ;[VAC001] Restore the original byte pointer
- PUSH BX ;[VAC001] Put the byte pointer on the stack
-
- CMP BYTE PTR DS:[BX],"\"
- ;[VAC001] Is the first filespec byte a path
- ;[VAC001] designator?
- JE GOTPATH ;[VAC001] If so, then proceed
- MOV BYTE PTR DS:[DI],"\"
- ;[VAC001] Otherwise, put a leading pathname
- ;[VAC001] designator in the buffer (DOS
- ;[VAC001] function 47H won't do it for us)
- INC DI ;[VAC001] Increment the buffer pointer
- MOV AH,47H ;[VAC001] Specify DOS function 47H
- MOV SI,DI ;[VAC001] Put DI into SI for functin 47H
- INC DL ;[VAC001] Increment current disk (saved above)
- INT 21H ;[VAC001] Put our current directory in the file
- ;[VAC001] name buffer
- MOV DI,OFFSET FILEN ;[VAC001] Get the original buffer pointer back
- ADD DI,2 ;[VAC001] Skip past the device spec
-
- EPLOOP: MOV DL,DS:[DI] ;[VAC001] Get a byte from the filename buffer
- OR DL,DL ;[VAC001] Is this a NUL byte (the end)?
- JZ ENDPATH ;[VAC001] If so, then proceed
- INC DI ;[VAC001] Otherwise, increment to the next byte
- MOV DH,DL ;[VAC001] Save this byte as "last byte seen"
- JMP EPLOOP ;[VAC001] And go check the next byte
-
- ENDPATH:CMP DH,"\" ;[VAC001] Was the "last byte seen" a pathname
- ;[VAC001] terminator?
- JE GOTPATH ;[VAC001] If so, then proceed
- MOV BYTE PTR DS:[DI],"\"
- ;[VAC001] Otherwise, put a pathname designator
- ;[VAC001] at the end of the filename buffer
- INC DI ;[VAC001] And increment the buffer pointer
-
- ;
- ; Now put the filespec passed by the user into our filename buffer, and
- ; convert it to uppercase.
- ;
- ; Enter with DI pointing to the buffer, and the pointer to the FCB
- ; filespec on the top of the stack.
- ;
- GOTPATH:POP BX ;[VAC001] Get the FCB filename pointer
-
- GPLOOP: MOV DL,BYTE PTR DS:[BX]
- ;[VAC001] Get a filespec byte
- CMP DL,"a" ;[VAC001] Is it above the range of a lowercase
- ;[VAC001] character?
- JL NOTLC ;[VAC001] If not, then proceed
- CMP DL,"z" ;[VAC001] Is it a lowercase character?
- JG NOTLC ;[VAC001] If not, then proceed
- XOR DL,32D ;[VAC001] Otherwise, convert it to uppercase
-
- NOTLC: MOV BYTE PTR DS:[DI],DL
- ;[VAC001] Put the character in our buffer
- INC BX ;[VAC001] Increment the FCB filename byte ptr.
- INC DI ;[VAC001] Increment our buffer pointer
- OR DL,DL ;[VAC001] Set the CPU flags
- JNZ GPLOOP ;[VAC001] Loop if the character isn't a NUL
-
- INC DI ;[VAC001] Otherwise, increment past the NUL
- MOV BYTE PTR DS:[DI],"$"
- ;[VAC001] And insert a DOS string terminator
- CALL APPENDF ;[VAC001] Open the output file in "append" mode
- JNC CREATEOK
- MOV DX,OFFSET PROBM
- CALL SHORT WRTMSG
- MOV DI,OFFSET FILEN ;[VAC001] Couldn't open the output file, so
- CALL SHORT PRTSTR ;[VAC001] tell the user
- MOV DX,OFFSET QUOTEND
- ;[VAC001] Finish up the message
- CALL SHORT WRTMSG ;[VAC001]
- MOV AL,-1 ;[VAC001]
- JMP LEAVE ; And go exit back to the DOS CLI
-
- CREATEOK:
- CALL CLOSEF
- CALL SCRINFO ;[VAC001] Set the CS:EGA flag word
- POP DX ;[VAC001] Clean up the stack (we don't care
- POP DX ;[VAC001] about the screen size)
-
- ; Now install our interrupt handler in place of PrtSc
-
- ;[VAC001] And install our INT 10H handler too...
- MOV AH,35h
- MOV AL,5
- INT 21H ; Get old interrupt vector
- MOV OLD5V1,BX
- MOV OLD5V2,ES ; and save them
- MOV AL,10h ;[VAC001]
- INT 21H ;[VAC001]
- MOV OLD10V1,BX ;[VAC001]
- MOV OLD10V2,ES ;[VAC001]
- MOV AH,25H ; Set our interrupt handler entry
- MOV AL,5 ;[VAC001]
- MOV DX,OFFSET INTVEC; DS already has CS
- INT 21H
- MOV AL,10H ;[VAC001]
- MOV DX,OFFSET INTTENH
- ;[VAC001]
- INT 21H ;[VAC001]
- MOV BYTE PTR CS:ACTIVEF,1
- ; Mark our handler active
- MOV DX,OFFSET INITMSG
- ; and say we are loaded
- MOV AH,9
- INT 21H
- MOV DI,OFFSET FILEN ;[VAC001]
- CALL PRTSTR ;[VAC001]
- MOV DX,OFFSET QUOTEND
- ;[VAC001]
- MOV AH,9 ;[VAC001]
- INT 21H ;[VAC001]
-
- ; Now exit
- MOV DX,OFFSET IEND ; Figure out how many resident SAVSCR code
- ; bytes there are
- ADD DX,MAXCHRS ;[VAC001] Leave room for a screen full of chars
- ADD DX,0FH ;[VAC001] Round up to the next paragraph
- MOV CL,4
- SHR DX,CL ; Convert to the number of paragraphs to stay
- ; resident in memory by shifting the byte count
- ; in DX by 4 bits to the right (which is the
- ; same as dividing it by 16 since 16=2^4)
- MOV AH,31H
- XOR AL,AL ;[VAC001]
- INT 21H ; Terminate and stay resident
-
- INSTR: MOV DX,OFFSET DOCM
- CALL WRTMSG
- XOR AL,AL ;[VAC001]
- JMP LEAVE
-
- ; $PAGE -----------------------------------------------------------------
-
- ASK PROC
- MOV AH,0CH ; Clear keyboard buffer
- MOV AL,1 ; keyboard input
- INT 21H
- PUSH AX
- MOV DX,OFFSET CRLF ; Go to new line after answer
- CALL WRTMSG
- POP AX
- AND AL,7FH-20H ; Make sure that the answer is upper-case
- CMP AL,'Y'
- RET
- ASK ENDP
-
- ; $PAGE -----------------------------------------------------------------
-
- ; Procedure to display a message on the display screen
- WRTMSG PROC
- MOV AX,CS
- MOV DS,AX
- MOV AH,9H
- INT 21H
- RET
- WRTMSG ENDP
-
- ; $PAGE -----------------------------------------------------------------
-
- ;[VAC001]+
-
- APPENDF PROC NEAR
- MOV DX,OFFSET FILEN
- MOV AL,1 ; Write access
- MOV AH,3DH
- INT 21H
- JNC APPMSG
- PUSHF
- CMP AX,2
- JE APPND2
- POPF
- RET
-
- APPND2: POPF
- CALL CREATEF
- RET
-
- APPMSG: PUSH AX
- MOV DX,OFFSET FEXISTS
- CALL SHORT WRTMSG
- POP AX
-
- APPEOF: MOV CS:FILEH,AX ; Save file handle
- MOV BX,AX
- MOV AL,2 ; Move to end of file
- XOR CX,CX ; (faster than MOV rX,0)
- XOR DX,DX
- MOV AH,42H
- INT 21H
- RET
-
- APPENDF ENDP
-
- ; $PAGE -----------------------------------------------------------------
-
- PRTSTR PROC NEAR
- ;
- ; Routine to print an ASCIIZ string
- ;
- ; Enter with DS:DI pointing to the string to be printed. All registers
- ; are preserved.
- ;
- PUSH AX
- PUSH DX
- PUSH DI
-
- MOV AH,2
-
- L00: MOV DL,DS:[DI]
- OR DL,DL
- JZ L01
- INT 21H
- INC DI
- JMP L00
-
- L01: POP DI
- POP DX
- POP AX
- RET
-
- PRTSTR ENDP
-
- ;[VAC001]-
-
- ; $PAGE -----------------------------------------------------------------
-
- ; Enter with AL containing a return code (may be used with DOS "IF" and
- ; "ERRORLEVEL" batch subcommands)
- LEAVE: MOV AH,4CH ;[VAC001] "Terminate a Process"
- INT 21H ;[VAC001]
-
- ; $PAGE -----------------------------------------------------------------
-
- SAVSCR ENDP
- S00000 ENDS
-
- END BEGIN