page 60,132 ; Copyright (C) 1988 Mark Adler Pasadena, CA ; All rights reserved. title ZAP - screen saver for the EGA or VGA comment # Version history - 1.0 18 Nov 1988 First public version 1.1 14 Dec 1988 Fixed bug in EGAZAP ZAP - ZAP is a resident utility that blanks the screen after a specified period of inactivity. The ZAP.ASM program actually generates two different programs depending on the assembler options: EGAZAP and VGAZAP. The first is for the IBM Enhanced Graphics Adapter or compatibles, and the second is for IBM PS/2's, the IBM Video Graphics Adpater, and comaptibles. From here on, ZAP will be used to mean EGAZAP or VGAZAP, whichever is appropriate for your hardware. To install ZAP, simply put the command ZAP in your AUTOEXEC.BAT file. From then on, when there is five minutes of inactivity at the keyboard, ZAP will blank the screen until any key (even just a shift key) is hit. The time can be changed with a command line option which specifies the time in seconds. For example: zap 600 will install ZAP (if not already installed) and change the time to 10 minutes. You can also turn the installed ZAP on and off using ZAP. For example: zap off will turn off the installed ZAP, so it will not blank the screen. Then the command: zap on will turn ZAP back on. ZAP can also be installed in the "off" state, awaiting a "zap on" command, simply by using: zap off in AUTOEXEC.BAT. You can also specify on or off along with a new inactivity time. For example, the command: zap off 600 in AUTOEXEC.BAT will install ZAP in the off state with a 10 minute inactivity time. Then a subsequent "zap on" will turn it on. The time must be in the range of one to 3600 seconds (one hour). Specifying a new time does not change whether ZAP is enabled or not and turning ZAP on or off does not change the time. Also, the two options can be in either order. For example, "zap 600 off" does the same thing as the example above. If ZAP is already installed, subsequent invocations of ZAP will not install it again. If the command: zap is entered after ZAP is already installed, this has the same effect as the command "zap on", that is it turns on ZAP if it was off. ZAP turns the display back on not only when any key is hit, but also if any video BIOS calls (int 10h) are made. Since most application programs seem to bypass the BIOS for video, this added feature has little effect. There are a few, rare programs that also completely take the keyboard from the BIOS, in which case ZAP can no longer find out when keystrokes occur. An example is STSC APL (a programming language). In this case, ZAP will blank your display, even though you have been merrily typing away for the last five minutes in the application. And the only way to get the display back on is to return to DOS (assuming you can get out while driving the application blindfolded). For such applications, you should include the commands "zap off" and "zap on" in a batch file that runs the application to disable ZAP before entering and enabling ZAP after leaving the application. You will quickly discover if you have any such applications. This program assumes that the current display is the (only) active display, but does not check for it either at installation or when running. For the EGA, the screen is blanked by setting the number of characters displayed to one, and the start of horizontal blanking to the first character. The number of displayed characters needs to be small (set to one for convenience) since the horizontal blanking interval is limited to 32 characters. The screen is restored by getting the current screen mode information from the BIOS and from that and the table of Start Horizontal Blanking values in this program (copied from the BIOS listing---not entirely kosher, but the only decent approach), the proper register values are restored. For the VGA, the screen is blanked and restored using the Screen Off bit in the Clocking Mode Register (port 0x3c4, address 01, bit 5). To minimize the resident memory required, this program is assembled separately for the EGA or VGA. When assembling, define the symbol EGA or VGA in the command line using /d (see the manual for MASM or TASM). If no symbol is defined, you will be warned and the resulting .COM file will be asssembled for the VGA. For example, the commands: tasm /dega zap tlink /t zap,egazap tasm /dvga zap tlink /t zap,vgazap will generate EGAZAP.COM and VGAZAP.COM, assuming that you have the Borland Turbo Assembler. The fact that the number of bytes in VGAZAP (666) is the same as Reagan's retirement home's address in Bel Air should be given no religious significance. # ifndef VGA ifndef EGA %out !No display specified---assembling for the VGA. endif endif ; ; Macros for blanking screens. ; egaoff macro local ismono,CRTMON,CRTCOL CRTMON equ 03B4h ;;CRT controller register in Mono mode. CRTCOL equ 03D4h ;;CRT controller register in Color mode. push AX ;;Save registers. push BX push CX push DX mov AH,12h ;;Get Mono/Color in BH. mov BL,10h pushf ;;Do Int 10h push CS call altvid mov DX,CRTMON ;;Point DX to CRT port in Mono mode. test BH,BH ;;See if color mode. jnz ismono ;;If not, then DX is correct. mov DX,CRTCOL ;;If color, use color address. ismono: mov AX,1 ;;Set number of characters to 1. out DX,AX inc AX ;;Set horizontal start of blanking to char 0. out DX,AX pop DX ;;Restore registers. pop CX pop BX pop AX endm egaon macro local ismono,enhance,shbget,tbl,CRTMON,CRTCOL CRTMON equ 03B4h ;;CRT controller register in Mono mode. CRTCOL equ 03D4h ;;CRT controller register in Color mode. ;; ;; Restore the display by getting the correct register values and ;; setting them. ;; ;; save registers used by routine, except AX. push BX push CX push DX ;; get CRT port, switch setting. mov AH,12h ;;Get switch setting into CL, Mono/Color in BH. mov BL,10h pushf ;;Do Int 10h push CS call altvid mov DX,CRTMON ;;Point DX to CRT port in Mono mode. test BH,BH ;;See if color mode. jnz ismono ;;If not, then DX is correct. mov DX,CRTCOL ;;If color, use color address. ismono: ;; get values for CRT registers 1 and 2. mov AH,0Fh ;;Get mode into AL, columns into AH. pushf ;;Do Int 10h push CS call altvid dec AH ;;Compute Horizontal Display End setting (1). cmp AL,3 ;;See if color mode. ja shbget ;;If not, go on to get Start Horiz Blank (2). cmp CL,3 ;;See if in secondary enhanced mode. je enhance ;;If so, use enhanced 0-3 settings. cmp CL,9 ;;See if in primary enhanced mode. jne shbget ;;If not, use normal 0-3 settings. enhance: add AL,11h ;;Add offset for table. shbget: mov BX,(offset trap)+(tbl-set) ;;Point to table. xlat byte ptr CS:tbl ;;Get Start Horizontal Blank setting. ;; Now AH is CRT register 1, AL is register 2 - set them. mov CL,AL ;;Save register 2 setting. mov AL,1 ;;Set register 1. out DX,AX mov AH,CL ;;Get register 2 setting. inc AX ;;Set register 2. out DX,AX ;; done, restore registers and return. pop DX pop CX pop BX ret ;;Start horiz blank values for modes 0-10, 0*-3*. tbl label byte db 2Dh,2Dh,5Ch,5Ch,2Dh,2Dh,59h,56h ;;0-7. db 2Dh,2Dh,2Dh,5Ch,56h,2Dh,59h,56h,53h ;;8-10h. db 2Bh,2Bh,53h,53h ;;0*-3*. endm vgaoff macro local CRTSEQ CRTSEQ equ 03C4h ;;CRT Sequencer register set. push AX ;;Save registers. push DX mov DX,CRTSeq ;;Sequencer registers. mov AL,1 ;;Point to Clocking Mode register. out DX,AL inc DX in AL,DX ;;Get current value. or AL,020h ;;Turn on Screen Off bit. out DX,AL pop DX ;;Restore registers. pop AX endm vgaon macro local CRTSEQ CRTSEQ equ 03C4h ;;CRT Sequencer register set. ;; save registers used by routine, except AX. push DX ;; turn Screen Off bit off. mov DX,CRTSEQ ;;Sequencer registers. mov AL,1 ;;Point to Clocking Mode register. out DX,AL inc DX in AL,DX ;;Get current value. and AL,0DFh ;;Turn off Screen Off bit. out DX,AL ;; done, restore registers and return. pop DX ret endm ; ; Interrupt vector segment definitions. ; ints segment at 0 dummy label far org 8*4 ;Point to Int 8 (tick) vector. int8 label word org 9*4 ;Point to Int 9 (scan) vector. int9 label word org 10h*4 ;Point to Int 10h (video) vector. int10 label word ints ends ; ; Program segment. ; zap segment assume CS:zap,DS:zap,ES:zap,SS:zap ; ; Put service code as low as DOS will allow. org 5Ch time dw ? ;Time before blanking in ticks. count dw ? ;Tick countdown. trap label word ;Location for interrupt traps. ; ; Partially parsed command line. org 5Ch f1 db 12 dup(?) org 6Ch f2 db 12 dup(?) ; ; Constants. DTIME equ 5462 ;Default time of about five minutes. ; ; Start of .COM code - jump to installation. org 100h start: jmp cpyrt db 13,'ZAP version 1.0 Copyright (C) 1988 Mark Adler',13,10 db 'All rights reserved.',13,10,'Z'-64 cpyrt: ; ; process arguments. cld ;This stays in effect for the whole program. mov onoff,1 ;Set ZAP to on. mov id,'Z' ;So we don't find COM file in a buffer. mov BX,offset f1 call chknon ;See if no arg. je afin call chkoff ;Have first arg---see if on or off. je do2 call chknum ;Not on or off---see if number. jz erra ;If no number there, then bad args. do2: mov BX,offset f2 call chknon ;See if second arg. je afin call chkoff ;Have second arg---see if on or off. je aok call chknum ;Not on or off---see if number. jz erra ;If no number there, then bad args. aok: afin: ; look to see if ZAP is installed yet. mov DX,DS ;Start checking backwards from this segment. assume ES:nothing search: dec DX jz nowhere ;If at bottom of memory, then it ain't loaded. mov SI,offset id ;Point DS:SI to id string to look for. mov ES,DX ;Point ES:DI to where to look. sub DI,DI ;The id string should be on a 16 byte boundary. mov CX,idlen repe cmpsb jne search ;If no match, look one back. ; now ES:DI point to byte after id. mov AL,onoff ;Set enable byte of ZAP. stosb add DI,16-(1+idlen+4) ;Point to time and count. dec DX mov ES,DX mov AX,newtime ;Get time setting. test AX,AX ;See if it is a new setting. jnz usenew mov AX,ES:[DI] ;If no new setting, use old setting. usenew: stosw stosw ;Reset count as well. mov AH,0Fh ;Turn display on in case it's off. int 10h ;(dummy int 10 call.) int 20h ;Done. ; ZAP not installed---install with specified options. nowhere: mov AX,DS ;Restore ES. mov ES,AX assume ES:zap mov AL,onoff ;Get on/off setting. mov enbl,AL ;Put in enbl flag in traps before moving. mov AX,newtime ;Set time and count. test AX,AX jnz timok mov AX,DTIME ;Get default time (five minuntes). timok: mov time,AX mov count,AX jmp copy ;Go set up traps and stay resident. erra: mov DX,offset err2 jmp short errx chknon proc near mov SI,BX mov DI,offset snone mov CX,6 repe cmpsw ret chknon endp chkoff proc near ; Compare [BX] with soff and son, return NE if neither match. mov SI,BX mov DI,offset soff mov CX,6 repe cmpsw jne noff mov onoff,0 ret noff: mov SI,BX mov DI,offset son mov CX,6 repe cmpsw ret chkoff endp chknum proc near lea SI,[BX+1] ;Point to arg (BX loaded already). sub BX,BX ;Initial value. mov DI,10 ;Base for decimal. mov CX,8 ;Digits allowed in name. numlp: lodsb sub AL,'0' jb nend cmp AL,9 ja nend cbw xchg AX,BX mul DI add BX,AX loop numlp nend: test BX,BX ;See if got a value. jz nret ;If not, just return. mov AX,91 ;Multiply by 18.2. mul BX shr DI,1 ;Put 5 in DI. cmp DX,DI jae errn ;If number of seconds too high, then error. div DI ;18.2 = 91/5. cmp DX,3 ;See if need to round up. jb rdown ;If remainder 0, 1, or 2, then round down. inc AX ;Else, round up. rdown: mov newtime,AX ;(Note, Z flag is false here.) nret: ret ;Return NZ if got a valid time. chknum endp errn: mov DX,offset err1 errx: mov AH,9 int 21h int 20h err1 db '?Seconds out of range---must be in 1..3600',13,10,'$' err2 db '?Invalid argument---can be "on", "off", or time' db 10,13,'$' soff db 0,'OFF ' son db 0,'ON ' snone db 0,' ' newtime dw 0 onoff db 1 ; ; Traps - only assume CS points here. assume DS:nothing,ES:nothing,SS:nothing set: ;Traps. id db ' APTSR' idlen equ $-id enbl db 1 ;Enable flag. tick: ; ; Timer tick action - if tick count not at already at zero, decrement ; it. If this makes the count zero, blank the screen. ; cmp byte ptr trap+(enbl-set),1 ;See if enabled. jne tfin ;If not, skip this. cmp count,0 ;See if count at zero. je tfin ;If so, don't decrement. dec count ;Decrement tick count. jnz tfin ;Wait until counts down to zero. ; disable display. ifdef EGA egaoff else vgaoff endif ; go on to service routine. tfin: jmp dummy ;Go on to service routine. tvec equ $-4 ;Address in jmp. scan: ; ; Keyboard scan code action - reset the count to the maximum. If the ; count was zero, then the screen is blanked. In that case, restore ; the display. ; push AX cmp count,0 ;See if count at zero. jne sfin ;If not, just reset count. call don ;Display blanked - restore it. sfin: mov AX,time mov count,AX ;Reset count. pop AX jmp dummy ;Go on to service routine. svec equ $-4 ;Address in jmp. vid: ; ; Video service request action - reset the count to the maximum. If ; the count was zero, then the screen is blanked. In that case, ; restore the display. ; push AX cmp count,0 ;See if count at zero. jne vfin ;If not, just reset count. call don ;Display blanked - restore it. vfin: mov AX,time mov count,AX ;Reset count. pop AX altvid: ;(For using Int 10h within these routines.) jmp dummy ;Go on to service routine. vvec equ $-4 ;Address in jmp. don: ifdef EGA egaon else vgaon endif setlen equ $-set ;End of traps. ; ; Installation code - all segment registers set. assume DS:zap,ES:zap,SS:zap copy: ; ; Install traps and tell DOS to leave them in memory. ; ; copy traps to lower memory. mov SI,offset set mov DI,offset trap mov CX,setlen rep movsb ;Assume direction flag cleared. ; set up to insert traps into interrupt vectors. sub AX,AX mov ES,AX ;Point to interrupt area. assume ES:ints cli ;Disable interrupts during change. ; insert trap in timer tick interrupt vector. mov AX,int8 ;Get old vector. mov trap+(tvec-set),AX ;Put in jump instruction. mov AX,int8+2 mov trap+(tvec+2-set),AX mov AX,offset trap+(tick-set) mov int8,AX ;Change Int 8 to the trap. mov AX,CS mov int8+2,AX ; insert trap in keyboard scan code interrupt vector. mov AX,int9 ;Get old vector. mov trap+(svec-set),AX ;Put in jump instruction. mov AX,int9+2 mov trap+(svec+2-set),AX mov AX,offset trap+(scan-set) mov int9,AX ;Change Int 9 to the trap. mov AX,CS mov int9+2,AX ; insert trap in video service interrupt vector. mov AX,int10 ;Get old vector. mov trap+(vvec-set),AX ;Put in jump instruction. mov AX,int10+2 mov trap+(vvec+2-set),AX mov AX,offset trap+(vid-set) mov int10,AX ;Change Int 10h to the trap. mov AX,CS mov int10+2,AX ; tell DOS to keep the traps in memory and exit. sti ;Interrupts OK now. mov DX,offset trap+setlen ;Amount to keep. int 27h ;Exit and remain resident. zap ends end start