;------ VGAMoire --------------------------------------------------------------
;
;       A text mode screen saver which saves the text display and draws a
;       pretty, moving graphic design on the screen.
;
;   ЪДД
;   і   moiіre (mw„r, m“r) n. [Fr, watered silk < MOHAIR] a fabric, esp. silk,
;   і      rayon, or acetate, having a watered, or wavy, pattern.
;   і                 _            _
;   і   moiіr‚ (mw„r ra', m“-; m“r'a) adj. [Fr, pp. of /moirer/, to
;   і      water < /moire/: see prec.] having a watered, or wavy, pattern,
;   і      as certain fabrics, stamps, or metal surfaces --n.  1 a watered
;   і      pattern pressed into cloth, etc. with engraved rollers  2 MOIRE
;   АВД
;    АДДДДДДДДД From Webster's New World Dictionary, Third College Edition,
;               Copyright (C) 1988 by Simon & Schuster, Inc.
;
;------ CREDITS ---------------------------------------------------------------
;
;       Moire pattern generator for the VGA.
;       Written by Christopher Antos, July 1990.
;       Copyright (C) 1990 by Christopher Antos.
;
;       Inspired by seeing MAGIC for Windows.  Also inspired by a lack of
;       intelligent, small, pretty screen savers that restored the video and
;       mouse states correctly.
;
;       Linedraw routines are from "Programmer's Guide to PC & PS/2 Video
;       Systems", from Microsoft Press.
;
;       If you have any questions or comments, please contact me at:
;
;               Christopher Antos
;               2115 Windsor Drive
;               Ann Arbor, MI  48103-5652
;               (313)663-7912
;
;       I'm not too picky about people using this source code.  It is not,
;       however, public domain.  It is copyrighted.  If you use this source
;       code at all, then you are using the program, and should feel obligated
;       to send a donation just as if you were using the program itself.  See
;       the documentation for more information.
;
;------ COMPILING -------------------------------------------------------------
;
;       VGAMoire is written for Turbo Assembler 2.0 and uses "SMART" mode for
;       maximum code optimization.
;
;       To compile:
;
;               tasm /zi vgamoire
;               tlink /v /s /l vgamoire
;               del vgamoire.obj
;               tdstrip -s -c vgamoire
;
;       This creates the excutable file, VGAMOIRE.COM; the debugger symbol
;       table file, VGAMOIRE.TDS; and the map file, VGAMOIRE.MAP.
;
;------ PLANS FOR REVISION ----------------------------------------------------
;
;       - Display new settings when N is used.
;       - Possibly some more code compaction.
;       - Possibly rearrange routines and only keep large routines (ie,
;         character RAM save/restore routines) resident if they will be used.
;       - Possibly implement XMS support, if in demand enough.
;
;------ VERSION HISTORY -------------------------------------------------------
;
;       v1.0    7/14/90
;               ю       Original version.
;               ю       Details:
;                         - Works with monochrome or color VGA cards only.
;                           VGAMoire will not work on EGA or MCGA, but it
;                           should work on just about any VGA card.  It uses
;                           the VGA 640x480x16 mode 12h, and uses 15 colors.
;                           It directly accesses the video DAC color registers
;                           and reprograms them to display different colors.
;                         - Will not work in graphics modes.  It is highly
;                           unlikely this will change, though someday, if
;                           the idea is popular and in demand enough, support
;                           *may* be added to save the graphics screen to EMS,
;                           allowing VGAMoire to be able to work even in
;                           graphics modes (though it still would conflict with
;                           most games or other "poorly behaved" programs).  I
;                           just don't see the point in it, especially since
;                           graphical applications are leaning towards
;                           Microsoft Windows as a standard.  Real-mode DOS
;                           TSRs like VGAMoire would not function, anyhow.
;                         - Does not beep if it times out in graphics mode, but
;                           beeps any other time it cannot pop up, including
;                           if the user hits Ctrl-Alt-Shift while in graphics
;                           mode.
;                         - Respects serial (and some parallel) communications,
;                           and restarts its countdown while any data transfer
;                           is going on.  It will not break carrier when it
;                           pops up or down (unless the remote host times out).
;                         - Saves and restores the complete mouse state.
;                         - Restores the display when mouse movement or a
;                           keypress is detected.  Note that VGAMoire suspends
;                           the current process.  It is NOT a background
;                           process.
;                         - With EMS memory installed, VGAMoire can save the
;                           *entire* video state, including the character
;                           generator RAM.  If this feature is not used, or EMS
;                           memory is not available, then VGAMoire just saves
;                           and restores the video mode, page, cursor positions
;                           (for each of the 8 video pages), and the cursor
;                           shape.  It will reset the screen to 25 lines.  No
;                           video data is lost besides the character generator
;                           RAM and the screen size.
;                           NOTE:  saving the character generator RAM adds an
;                           extra 64k to the total EMS memory that VGAMoire
;                           uses, in order to save all 8 character generator
;                           RAM banks (at 8k each).
;                         - VGAMoire should never pop up when it might damage
;                           the system or cause errors.  It monitors a number
;                           of I/O interrupts and restarts its countdown if any
;                           I/O is detected.  If you need an ABSOLUTE guarantee
;                           that it will not disrupt the system during a vital
;                           operation, you can turn it off by pressing
;                           Ctrl-Alt-E (press Ctrl-Alt-B to turn it on again).
;                           If you press Ctrl-Alt-Shift to pop VGAMoire up, it
;                           WILL pop up, even if a vital system operation is in
;                           progress (eg, disk I/O).  Because this can cause
;                           errors or even crash the system, you should be
;                           careful about when you pop VGAMoire up manually.
;                           The only times VGAMoire will not pop up is if the
;                           display is in a graphics mode or if you are using
;                           EMS and there is an error while trying to use EMS.
;                         - Cannot (as yet) detect mouse movement except when
;                           popped up, so mouse movement will not restart the
;                           countdown.
;
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;
;       v1.1    8/2/90
;               ю       Now deallocates its environment block.
;               ю       Can now select how many character generator RAM blocks
;                       to save, from 1 to 8.  Allocates an extra one EMS page
;                       (16k) per every two character generator RAM blocks that
;                       are saved, so to save 1 or 2 blocks, it takes 16k of
;                       EMS; for 3 or 4, it takes 32k; etc.
;               ю       At Karen's request, VGAMoire can now mirror the
;                       pattern in one of four ways:  no mirror, horizontally,
;                       vertically, or both horizontally and vertically.
;               ю       Now detects both mouse movement and mouse button
;                       activity and will reset its countdown.  A new option
;                       has also been added to disable this feature, in case
;                       a mouse is not present or an old mouse driver causes
;                       problems with this feature.
;               ю       Bug fix:  might not have worked correctly before if no
;                       mouse driver were installed (it might have allocated
;                       an unpredictably-too-large block of memory).
;               ю       Bug fix:  when only using conventional memory, the
;                       keyboard would lock up once a key was pressed to
;                       return from the moire design.  I don't remember quite
;                       what I did to fix it, although I know I tried a number
;                       of things.
;               ю       Now only keeps one color set resident.  When a color
;                       set is picked, it is copied to the resident copy's
;                       run-time colorset array (assuming we are changing the
;                       color set for the resident copy.  It does the same
;                       thing the first time it is installed, except that of
;                       course it copies the selected color set into the main
;                       run-time colorset array).
;               ю       Frost colors added.
;
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;
;       v1.2    8/7/90
;               ю       Bug fix:  forgot to use a CS: segment override when
;                       determining how many font blocks to save/restore, and
;                       the result was that essentially no fonts were restored.
;                       VGAMoire now restores fonts correctly.
;               ю       Also, to avoid some nasty screen flickering during
;                       some of the special video accesses, we disable the
;                       video display while operating on video memory, and
;                       then reenable it after we're done screwing with video
;                       memory (only when starting up the moire design or
;                       returning to text mode).
;               ю       Before, we only redefined 15 colors.  It occured to me
;                       that VGAMoire should also reprogram the color
;                       definition for BLACK, to make sure that the black is
;                       really black.  This is necessary because there are a
;                       number of programs out that play with the VGA palette,
;                       and we can't assume that black is really black--we've
;                       got to MAKE it be black.  The whole purpose of VGAMoire
;                       is to prevent the monitor from burning out!  If the
;                       background were always, oh, say WHITE, then VGAMoire
;                       wouldn't be much use, now would it?  So, anyway, we
;                       took care of that possibility in this version.
;
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;
;       v1.3    8/14/90
;               ю       OOPS!  I apologize.  I made a stupid error.
;                       The [N] option didn't work right.
;                       Sigh.  This should now be a solid version, w/o
;                       version changes for a while.  Sigh.
;
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;
;       v1.4    9/4/90
;               ю       Improved mouse state saving and restoring.  Works
;                       even with programs making very sophisticated use of
;                       the mouse driver (for instance, Norton Advanced
;                       Utilities 5.0).
;               ю       New method of reading the keyboard and mouse while
;                       the screen saver is working.  This ensures that
;                       VGAMoire doesn't receive any "false alerts" and
;                       refuse to pop up even when it's supposed to.
;               ю       VGAMoire will now blank out the screen if a graphics
;                       mode is active.  The moire design will not appear, but
;                       at least VGAMoire can still function as a screen saver
;                       even in graphics modes.  Special care was taken so that
;                       THE PROGRAM CAN STILL RUN IN THE BACKGROUND WHILE THE
;                       SCREEN IS BLANKED OUT.  The screen will come back if
;                       VGAMoire detects any activity--mouse, keyboard, serial
;                       communications, parallel communications (via int 17h),
;                       video activity (via int 10h), or disk activity.  It
;                       can be configured to blank the screen in text mode,
;                       too.
;
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;
;       v1.4b   9/21/90
;               ю       Source code was reorganized to facilitate future
;                       support for video cards besides the VGA.  Enough
;                       people indicated an interest that I decided to
;                       look into adding support for MDA, CGA, MCGA, EGA,
;                       VGA, Hercules, and InColor cards.  Note that this is
;                       NOT a guarantee for support, just a guarantee I'll
;                       look into it!  Even if I do add support for multiple
;                       cards, not all of the above cards may be implemented.
;               ю       Improved video state save/restore routines mean that
;                       the entire video state is always saved and restored,
;                       even if EMS memory is not used.  If a non-standard RAM
;                       font is loaded and active, and the [Fn] option is not
;                       in effect, then the display will revert to the 8x16
;                       ROM font.  If one of the ROM fonts was being used
;                       before VGAMoire took over, then that font will be
;                       restored.
;                    ** This also remedies a problem with MS Word (or any
;                       program that used bright background colors) when the
;                       [E] option was not used.  Before, VGAMoire was unable
;                       restore the bright mode, but now it does.
;               ю       Bug fix:  I forgot to use a CS: segment override when
;                       dereferencing the "useEMS" flag when popping up
;                       VGAMoire.  In most situations, this meant VGAMoire
;                       would nuke any data a program had in the EMS page
;                       frame (OOPS!).  This has been fixed.
;
;------------------------------------------------------------------------------


LOCALS                                  ;allow local labels in procedures (@@xxx)
SMART                                   ;enable maximum code optimization


__DeallocateEnvironment         equ     1       ;1=kill env. block, 0=keep env. block so map pgms can ID us


VERSION         equ     '1.4b'                  ;current version of VGAMoire
DATE            equ     'September 1990'        ;date of this version


MaxRev          equ     6               ;max times an endpoint direction can reverse before a new random velocity is picked--must be at least 1
MaxLines        equ     50              ;maximum number of lines in the moire pattern




cseg    segment para    public  'CODE'
        assume  cs:cseg,ds:cseg,ss:nothing,es:nothing
        org 100h



main    proc    near
        jmp     setup                   ;go to end of program
main    endp



oldmx           dw      0               ;old mouse x-coordinate
oldmy           dw      0               ;old mouse y-coordinate
mouse           db      1               ;flag:  1=check mouse, 0=ignore mouse
delay           db      0               ;delay factor
mirror          db      0               ;mirror setting
length          db      20              ;number of lines in the moire pattern
colorspeed      db      1               ;# of lines to draw before changing color
wait            dw      0               ;number of int 08h's to wait before popping up
buffersize      dw      1000h           ;size of video text save buffer
mousesize       dw      0800h           ;size of mouse state save buffer
fontblocks      db      0               ;number of font RAM blocks to save (0=no font saving, 1-8=that many blocks)
onoff           db      1               ;onoff:  1=on, 0=off
useEMS          db      0               ;useEMS:  1=yes, 0=no
EMShandle       dw      ?               ;our EMS block's handle
counter         dw      0               ;count since a key was pressed etc.
videomode       db      ?               ;previous video mode
storetext       dw      ?               ;where to store text (default for conventional memory)
textbuff        dw      ?               ;store address of text buffer
keybuff         dw      0               ;keyboard buffer
gonow           db      0               ;delay when Ctrl-Alt-Shift is pressed
colorwait       db      0               ;countdown to changing color
curcolor        db      1               ;color of current line
gfxmode         db      0               ;flag:  non-zero indicates we are performing a cop-out version of a screen saver because we're in graphics mode
gfxcopout       db      0               ;flag:  non-zero means always do the cop-out, never draw the moire design
gfxinstalled    db      0               ;flag:  was this copy of VGAMoire installed with the [Bn] option?
timeout         db      1               ;popped up because:  0 if ctrl-alt-shift was hit, 1 if timed out
blackcolor      db      0,0,0           ;video DAC color definition for black
colorset        label   byte            ;run-time array holding color set for moire design--color reg values are loaded in at install time, or these defaults are used
                db      63,  0,  0      ;color #1       [red]
                db      63, 30,  0      ;
                db      63, 45,  0      ;
                db      63, 63,  0      ;               [yellow]
                db      42, 63,  0      ;
                db       0, 63,  0      ;               [green]
                db       0, 63, 42      ;
                db       0, 63, 63      ;               [cyan]
                db       0, 45, 63      ;
                db       0, 31, 63      ;
                db       0,  0, 63      ;               [blue]
                db      30,  0, 63      ;
                db      45,  0, 63      ;
                db      63,  0, 63      ;               [violet]
                db      63,  0, 47      ;color #15
maxcolor        db      15              ;highest defined color number
index           dw      0               ;index to last line in moire pattern
; current line endpoint data
; data for each coordinate is grouped so loop at [revdir] works w/offsets
xv1             dw      ?               ;x1 endpoint velocity
x1              dw      ?               ;endpoint of current line
x1rev           db      MaxRev          ;times endpoint direction has reversed
xv2             dw      ?               ;x2 endpoint velocity
x2              dw      ?               ;endpoint of current line
x2rev           db      MaxRev          ;times endpoint direction has reversed
yv1             dw      ?               ;y1 endpoint velocity
y1              dw      ?               ;endpoint of current line
y1rev           db      MaxRev          ;times endpoint direction has reversed
yv2             dw      ?               ;y2 endpoint velocity
y2              dw      ?               ;endpoint of current line
y2rev           db      MaxRev          ;times endpoint direction has reversed
switchdir       dw      0               ;# lines to draw before changing to new random velocities
linesx1         dw      MaxLines dup (?)        ;arrays holding endpoints of last MaxLines lines
linesy1         dw      MaxLines dup (?)
linesx2         dw      MaxLines dup (?)
linesy2         dw      MaxLines dup (?)
old08h          dw      ?               ;hold addresses of original interrupts
                dw      ?
old09h          dw      ?
                dw      ?
old10h          dw      ?
                dw      ?
old13h          dw      ?
                dw      ?
;old14h          dw      ?
                 dw      ?
old17h          dw      ?
                dw      ?
int10hOK        db      0               ;0 if we should reset [counter] on calls to int 10h -- while our program is running, this is 0ffh (-1)
sax             dw      ?               ;saves ax
sbx             dw      ?               ;saves bx
scx             dw      ?               ;saves cx
sdx             dw      ?               ;saves dx
sdi             dw      ?               ;saves di
ssi             dw      ?               ;saves si
sbp             dw      ?               ;saves bp
ses             dw      ?               ;saves es
sds             dw      ?               ;saves ds
sip             dw      ?               ;saves ip
scs             dw      ?               ;saves cs
sfl             dw      ?               ;saves flags
sax2            dw      ?               ;saves ax in our08h
sbx2            dw      ?               ;saves bx in out08h
scx2            dw      ?               ;saves cx in our08h
sdx2            dw      ?               ;saves dx in our08h
sds2            dw      ?               ;saves ds in out08h



reset   proc    near                    ;resets countdown, restores screen if in "gfx cop-out" mode
        push    cs:[wait]
        pop     cs:[counter]
        cmp     cs:[gfxmode],0
        je      @@done
        mov     cs:[gfxmode],0
        xor     al,al                   ;restore screen if gfx cop-out mode was active
        call    refresh
@@done:
        ret
reset   endp



our08h  proc    near                    ;our int 08h handler
        cmp     cs:[gonow],0            ;gonow > 0 if Ctrl-Alt-Shift was pressed
        jz      @@deccnt
        dec     cs:[gonow]              ;subtract 1 from gonow if it is > 0
        jg      @@deccnt
        mov     cs:[counter],1          ;if gonow=0 set counter=1 to trigger fireworks
@@deccnt:
        dec     cs:[counter]            ;decrement counter
        jz      @@moire                 ;if counter = 0 draw moire pattern
        jg      @@checkMS
        inc     cs:[counter]            ;if counter was 0 set it 0 again
@@checkMS:
        cmp     cs:[mouse],0
        je      @@chain
        mov     cs:[sds2],ds            ;save ds
        push    cs
        pop     ds                      ;set up addressability
        mov     [sax2],ax               ;save ax
        mov     [sbx2],bx               ;save bx
        mov     [scx2],cx               ;save cx
        mov     [sdx2],dx               ;save dx
        mov     ax,3
        xor     bx,bx                   ;in case no mouse driver, zero all regs that would hold return values
        xor     cx,cx
        xor     dx,dx
        int     33h
        cmp     bx,0                    ;is mouse button pressed
        jne     @@reset
        cmp     cx,[oldmx]
        jne     @@reset
        cmp     dx,[oldmy]
        je      @@doneMS
@@reset:
        mov     [oldmx],cx
        mov     [oldmy],dx
        call    reset                   ;reset countdown
@@doneMS:
        mov     dx,[sdx2]
        mov     cx,[scx2]
        mov     bx,[sbx2]
        mov     ax,[sax2]
        mov     ds,[sds2]
@@chain:
        jmp     DWord Ptr cs:[old08h]   ;chain to old int 08h handler
@@moire:
        mov     cs:[sds],ds             ;save ds first, so we can
        push    cs                      ;  point ds to us to crunch code by
        pop     ds                      ;  avoiding segment overrides
        mov     [sax],ax                ;save ax
        mov     [sbx],bx                ;save bx
        mov     [scx],cx                ;save cx
        mov     [sdx],dx                ;save dx
        mov     [sdi],di                ;save di
        mov     [ssi],si                ;save si
        mov     [sbp],bp                ;save bp
        mov     [ses],es                ;save es
        pop     [sip]                   ;save ip
        pop     [scs]                   ;save cs
        pop     [sfl]                   ;save flags
        push    [sfl]                   ;push flags back on stack
        push    cs                      ;push new cs on stack
        mov     cx,offset runthis       ;return to here
        push    cx                      ;save offset of runthis
        jmp     @@chain                 ;return to do int 8
our08h  endp



our09h  proc    near                    ;process keyboard interrupt
        push    ax                      ;save ax
        push    es                      ;save ds
        mov     es,cs:[keybuff]         ;set es = 0
        mov     al,byte ptr es:[417h]   ;get keyboard status byte
        pop     es
        and     al,0fh                  ;mask off top 4 bits
        cmp     al,12                   ;if Ctrl-Alt is pressed
        jg      @@signal                ;if Ctrl-Alt-Shift is pressed
        jnz     @@chain
        in      al,60h                  ;get extended key code
        cmp     al,18                   ;is it Ctrl-Alt-E
        jnz     @@enable
        mov     cs:[onoff],0            ;turn off screen saver
@@enable:
        cmp     al,48                   ;is key Ctrl-Alt-B
        jnz     @@chain
        mov     cs:[onoff],1            ;turn on screen saver
        jmp     @@chain
@@signal:
        mov     cs:[gonow],6            ;delay half a second then invoke screen saver
        mov     cs:[timeout],0          ;say we're popping up because of user request (ctrl-alt-shift)
@@chain:
        call    reset                   ;reset countdown
        pop     ax                      ;restore ax
        jmp     DWord Ptr cs:[old09h]   ;chain to old int 09h handler
our09h  endp



our10h  proc    near                    ;new video interrupt
        cmp     ax,0caffh               ;CA are my initials - check if we're installed
        jne     @@norm10h
        mov     bx,cs                   ;return our PSP
        mov     ax,0faceh               ;return code
        iret
@@norm10h:
        cmp     cs:[int10hOK],0         ;is it us requesting an interrupt
        jne     @@pass                  ;if so, don't reset counter, or we'll exit!
        call    reset                   ;reset countdown
        @@pass:
        jmp     DWord Ptr cs:[old10h]   ;chain to old int 10h handler
our10h  endp



our13h  proc    near                    ;new disk drive interrupt
        call    reset                   ;reset countdown
        jmp     DWord Ptr cs:[old13h]   ;chain to old int 13h handler
our13h  endp



;our14h  proc    near                    ;new modem interrupt
;        call    reset                   ;reset countdown
;        jmp     DWord Ptr cs:[old14h]   ;chain to old int 14h handler
;our14h  endp



our17h  proc    near                    ;new printer interrupt
        call    reset                   ;reset countdown
        jmp     DWord Ptr cs:[old17h]   ;chain to old int 17h handler
our17h  endp



;------------------------------------------------------------------------------
;
;       CARD-SPECIFIC CODE
;
;       any include file used here must declare the following items so it
;       can link itself with this VGAMOIRE.ASM module:
;
;               SETCOLORS       (macro) sets the colors
;               STORE           (macro) stores the current video state
;               SETMODE         (macro) sets the graphics mode
;               RESTORE         (macro) restores the saved video state
;               Line10          (proc)  draws a line in graphics mode
;
;               MinBound        equ     [minimum endpoint velocity]
;               MaxBound        equ     [maximum endpoint velocity]
;
;               MaxX            dw      [maximum x coordinate]
;               MaxY            dw      [maximum y coordinate]
;
include vga.inc
;
;------------------------------------------------------------------------------



getbuf  proc    near
        cmp     cs:[useEMS],0
        jz      @@done
        mov     ah,47h                  ;save page map
        mov     dx,cs:[EMShandle]
        int     67h
        or      ah,ah
        jnz     @@error
        mov     ah,44h                  ;map our logical pages to physical page frame
        mov     al,cs:[useEMS]          ;al=number of pages we reserved
        dec     al                      ;al=number of highest logical page
@@maploop:
        push    ax
        xor     bh,bh
        mov     bl,al                   ;map logical page to same physical page number
        mov     dx,cs:[EMShandle]
        int     67h
        or      ah,ah
        jnz     @@error3                ;@@error3 makes sure to pop ax, too
        pop     ax
        or      al,al
        jz      @@getframe
        dec     al
        jmp     @@maploop
@@getframe:
        mov     ah,41h                  ;get page frame address
        int     67h
        or      ah,ah
        jnz     @@error2
        mov     cs:[storetext],bx
@@done:
        clc                             ;success
        ret
@@error3:
        pop     ax
@@error2:
        call    freebuf                 ;uh, oh, better restore page map...
@@error:
        stc                             ;failure
        ret
getbuf  endp



freebuf proc    near
        cmp     cs:[useEMS],0
        jz      @@done
        mov     ah,48h                  ;restore page map
        mov     dx,cs:[EMShandle]
        int     67h
        or      ah,ah
@@done:
        ret
freebuf endp



rndcnt          dw      ?               ;ceiling of random number range
seed            dw      ?               ;seed for random number generator
aval            equ     2743            ;for pseudorandom generator
cval            equ     5923            ;for pseudorandom generator
rnum            dw      rval            ;number of times the generator was used
rval            equ     200             ;how high rnum can get before getting a new seed and reseting rnum

rnd     proc    near                    ;return pseudorandom number in range [0,rndcnt], uses a standard algorithm
        push    cx
        push    dx
        inc     [rnum]
        cmp     [rnum],rval             ;is it time to get a new seed
        jl      @@getrnd
        push    es
        push    bx
        xor     ax,ax
        mov     es,ax
        mov     bx,46ch
        push    es:[bx]                 ;read timer
        pop     [seed]                  ;store it to seed
        mov     [rnum],ax
        pop     bx
        pop     es
@@getrnd:
        mov     ax,[seed]               ;seed = (seed*aval + cval) mod mval
        mov     cx,aval
        mul     cx
        add     ax,cval
        mov     [seed],ax
        xor     dx,dx
        mov     cx,[rndcnt]
        inc     cx
        or      cx,cx
        jz      @@nomod                 ;if modulus 65536 (which is effectively mod 0 and thusly divide-by-zero because we're using word values)
        div     cx
        mov     ax,dx                   ;ax in [0,rndcnt]
@@nomod:                                ;jump point if rndcnt=65535 to avoid divide by zero interrupt
        pop     dx
        pop     cx
        ret
rnd     endp



randvel proc    near                    ;pick a random endpoint velocity
;       ARGS:   none
;       RETS:   ax = velocity
        mov     rndcnt,MaxBound*2       ;random number range [0,MaxBound*2]
@@rv1:
        call    rnd                     ;get random number in ax
        sub     ax,MaxBound             ;ax in [-MaxBound,MaxBound]
        cmp     ax,MinBound
        jb      @@rv1
        cmp     ax,-MinBound
        ja      @@rv1
        ret                             ;return w/ax
randvel endp



getcoords       proc    near            ;load coordinates into regs
        cmp     [curcolor],0
        je      @@erase
        mov     ax,[y2]
        mov     bx,[x2]
        mov     cx,[y1]
        mov     dx,[x1]
        ret
@@erase:
        mov     di,[index]              ;get index to last line in moire pattern
        shl     di,1                    ;convert to word index
        mov     ax,linesy2[di]
        mov     bx,linesx2[di]
        mov     cx,linesy1[di]
        mov     dx,linesx1[di]
        ret
getcoords       endp



doline  proc    near                    ;draw a line from dx,cx to bx,ax
        push    word ptr [curcolor]     ;pass parameters for line to Line10
        push    ax
        push    bx
        push    cx
        push    dx
        call    Line10
        ret
doline  endp



alllines        proc    near            ;draw all lines, including mirroring
        call    getcoords
        call    doline                  ;draw normal first line
        test    [mirror],1              ;mirror right/left?
        jz      @@noRL
        call    getcoords
        neg     bx
        add     bx,[MaxX]
        neg     dx
        add     dx,[MaxX]
        call    doline
@@noRL:
        test    [mirror],2              ;mirror top/bottom?
        jz      @@noTB
        call    getcoords
        neg     ax
        add     ax,[MaxY]
        neg     cx
        add     cx,[MaxY]
        call    doline
@@noTB:
        cmp     [mirror],3              ;mirror left/right/top/bottom?
        jne     @@noBTH
        call    getcoords
        neg     ax
        add     ax,[MaxY]
        neg     cx
        add     cx,[MaxY]
        neg     bx
        add     bx,[MaxX]
        neg     dx
        add     dx,[MaxX]
        call    doline
@@noBTH:
        ret
alllines        endp



drawmoire       proc    near            ;draw moire pattern
        mov     [counter],0             ;zero the counter to avoid "false alerts"
        ; reset tables holding old line coordinates
        push    cs
        pop     es
        mov     di,offset linesx1
        mov     cx,MaxLines*4           ;full table size (4 coordinates per line)
        mov     ax,-1
        rep     stosw                   ;set all elements in tables to -1 (word size)
        ; get random directions, velocities, and coordinates to start the pattern
        mov     [rndcnt],400
        call    rnd
        add     ax,200
        mov     [switchdir],ax          ;store new # of lines to draw before randomly changing endpoint velocities
        mov     di,(offset xv2 - offset xv1)*4  ;di is offset
randir:
        or      di,di
        jz      ranex
        sub     di,(offset xv2 - offset xv1)
        cmp     di,(offset xv2 - offset xv1)*2  ;is it doing x or y?
        jb      dox
        mov     cx,[MaxY]
        jmp     doxy
dox:
        mov     cx,[MaxX]
doxy:
        call    randvel                 ;get random endpoint velocity
        mov     xv1[di],ax              ;store it
        mov     [rndcnt],cx
        call    rnd                     ;pick random endpoint coordinate
        mov     x1[di],ax               ;store it
        jmp     randir
ranex:

di1:
        cmp     [delay],0               ;if no delay factor requested
        jz      de1
        push    cx                      ;save cx
        mov     ch,[delay]              ;set cx = 100h * delay
        xor     cl,cl
de0:
        cmp     [counter],0             ;do this to waste time
        cmp     [counter],0
        loop    de0                     ;repeat cx times
        pop     cx                      ;restore cs
de1:
        cmp     [counter],0             ;if a key was pressed counter > 0
        jng     di4
        jmp     ex1
di4:

        ; is it time to randomly switch directions yet?
        cmp     [switchdir],0
        jnz     @@sdex
        mov     [rndcnt],400            ;pick new random number of lines to draw before changing velocities
        call    rnd
        add     ax,200                  ;ax in [200,600]
        mov     [switchdir],ax          ;store new limit
        mov     di,(offset xv2 - offset xv1)*4  ;di is offset & counter in one
@@sd1:
        or      di,di
        jz      @@sdex
        sub     di,(offset xv2 - offset xv1)
        call    randvel                 ;pick a random velocity
        mov     xv1[di],ax              ;ax in [-MaxBound,-MinBound] or [MinBound,MaxBound]
        jmp     @@sd1
@@sdex:
        dec     [switchdir]

        ; reverse directions if hit edges of screen -- loop crunches 4 essentially duplicate blocks of code into one
        mov     di,(offset xv2 - offset xv1)*4  ;di is offset & counter in one
revdir:
        or      di,di
        jz      revex
        sub     di,(offset xv2 - offset xv1)
        mov     ax,x1[di]
        add     ax,xv1[di]
        cmp     di,(offset xv2 - offset xv1)*2  ;is it checking x or y?
        jb      checkx
        cmp     ax,[MaxY]
        jmp     checkxy
checkx:
        cmp     ax,[MaxX]
checkxy:
        jbe     revdir
        cmp     x1rev[di],0             ;has xv_ reversed directions too many times?
        jnz     rd1
        mov     [rndcnt],MaxRev-1       ;pick new random limit for how many times to reverse
        call    rnd
        inc     al
        mov     x1rev[di],al            ;store new limit
        call    randvel                 ;pick a random velocity
        mov     bx,xv1[di]              ;grab current velocity value
        mov     bl,ah
        and     bx,1000000010000000b    ;compare signs (directions) of current and new values of velocities
        cmp     bh,bl
        je      rb20
        neg     ax                      ;if different, make same
rb20:
        mov     xv1[di],ax              ;ax in [-MaxBound,-MinBound] or [MinBound,MaxBound]
rd1:
        dec     x1rev[di]
        neg     xv1[di]                 ;reverse the direction
        jmp     revdir
revex:

        mov     di,[index]              ;get index into saved line positions
        shl     di,1                    ;convert to word index
        mov     ax,[x1]
        mov     linesx1[di],ax          ;put x1 into array
        mov     ax,[x2]
        mov     linesx2[di],ax          ;put x2 into array
        mov     ax,[y1]
        mov     linesy1[di],ax          ;put y1 into array
        mov     ax,[y2]
        mov     linesy2[di],ax          ;put y2 into array
        ; increment index - this gives us the line to erase
        inc     [index]                 ;index = index+1
        xor     ah,ah
        mov     al,[length]
        cmp     [index],ax              ;is it time to wrap around
        jl      nw1
        xor     ax,ax
        mov     [index],ax              ;reset index to 0
nw1:
        ; get color
        cmp     [colorwait],0           ;is it time to change color
        ja      @@samecolor             ;if not, jump
        mov     al,[colorspeed]
        mov     [colorwait],al
        mov     al,[maxcolor]
        cmp     [curcolor],al
        jb      @@nowrap
        mov     [curcolor],0            ;wrap around
@@nowrap:
        inc     [curcolor]              ;get next color
@@samecolor:
        dec     [colorwait]             ;get closer to time to change color
        mov     ax,[yv2]
        add     [y2],ax                 ;add yv2 to y2
        mov     ax,[xv2]
        add     [x2],ax                 ;add xv2 to x2
        mov     ax,[yv1]
        add     [y1],ax                 ;add yv1 to y1
        mov     ax,[xv1]
        add     [x1],ax                 ;add xv1 to x1
        call    alllines                ;draw lines, including mirroring
        ; erase last line(s)
        mov     di,[index]              ;get index to last line in moire pattern
        shl     di,1                    ;convert to word index
        cmp     linesx1[di],-1          ;is there really a line to erase yet
        je      di2
        push    word ptr [curcolor]     ;save current color value
        mov     [curcolor],0            ;color for erasing
        call    alllines                ;clear last line(s) in pattern, including mirroring
        pop     ax
        mov     [curcolor],al           ;restore current color
di2:
        jmp     di1                     ;next iteration
ex1:
;        mov     dx,3ceh                 ;dx = graphics controller port address
;        xor     ax,ax                   ;restore set/reset register
;        out     dx,ax
;        inc     ax                      ;restore enable set/reset register
;        out     dx,ax
;        mov     al,3                    ;data rotate/func select register #
;        out     dx,ax
;        mov     ax,0ff08h               ;restore bit mask register
;        out     dx,ax
        ret
drawmoire       endp



moire   proc    near
        ; store text video screen
        cmp     [gfxcopout],0           ;if gfx cop-out mode is set to "always", then jump to the cop-out routine
        jne     @@gfxcopout
        mov     [textbuff],TextBufferSeg+800h   ;address of text
        mov     es,[keybuff]            ;set es = 0
        mov     bl,byte ptr es:[449h]   ;get byte at 449h
        cmp     bl,4                    ;what video mode are we in
        jl      @@go                    ;if we are in text mode, go!
        cmp     bl,7                    ;are we at mode 7, possibly ega
        jz      @@mono
@@gfxcopout:
        mov     al,1                    ;in graphics mode, so just blank the screen
        call    refresh
        mov     [gfxmode],1             ;set flag that we're doing our graphics mode cop-out version of a screen saver
        ret                             ;return to previous program
@@gofail:
        jmp     @@fail                  ;jump point if something goes wrong
@@mono:
        mov     [textbuff],TextBufferSeg        ;set textbuff to b000h if mono
@@go:
        mov     al,1                    ;disable screen refresh (avoid flickering during mode change, etc)
        call    refresh
        call    getbuf                  ;set up EMS page map if using EMS
        jc      @@gofail                ;if we can't
        mov     ax,2                    ;hide pointer, in case it's showing
        int     33h
        push    es
        mov     ax,16h                  ;save mouse state
        push    [storetext]             ;get segment of save buffer
        pop     es
        mov     dx,[buffersize]         ;offset past stored video screen
        int     33h
        mov     ax,21h                  ;reset the mouse
        int     33h
        pop     es
        mov     bl,byte ptr es:[449h]   ;save video mode
        mov     [videomode],bl
        STORE                           ;[macro] save current video state
        call    freebuf                 ;restore EMS page map (if using EMS)
        SETMODE                         ;[macro] sets the graphics video mode
        push    cs                      ;set ds = cs
        pop     ds
        SETCOLORS                       ;[macro] set new video DAC color registers
        xor     al,al                   ;reenable video refresh (the BIOS should have taken care of this when we set the graphics mode, but we do it, too, just in case)
        call    refresh
        call    drawmoire               ;draw moire pattern
        mov     al,1                    ;disable screen refresh (avoid flickering during mode change, etc)
        call    refresh
        push    cs
        pop     ds
        xor     ah,ah                   ;reset to saved mode
        mov     al,[videomode]          ;  must do this, even if we're restoring the video state afterwards, otherwise the
        int     10h                     ;  mouse driver thinks we're still in gfx mode and the mouse pointer gets mangled.
        mov     al,1                    ;disable screen refresh (avoid flickering during mode change, etc)
        call    refresh
        RESTORE                         ;[macro] restore saved video state
@@done:
        xor     al,al                   ;reenable video refresh
        call    refresh
        push    cs
        pop     ds
        call    reset                   ;reset the countdown
        ret
@@fail:
        cmp     [timeout],0             ;if failed but we timed out, don't do anything
        jnz     @@done
        mov     ah,0eh
        mov     al,7                    ;cheapie way to sound a bell
        xor     bx,bx
        int     10h
        jmp     @@done
moire   endp



runthis proc    near                    ;run graphics then restore to program
        mov     ax,cs                   ;set ds = cs
        mov     ds,ax
        not     [int10hOK]              ;tell our int 10h handler we're calling it
        cmp     [onoff],0               ;was VGAMoire turned off
        jz      @@nope
        call    moire                   ;save screen, draw moire pattern, restore screen
@@nope:
        mov     ax,[sax]                ;restore ax
        mov     bx,[sbx]                ;restore bx
        mov     cx,[scx]                ;restore cx
        mov     dx,[sdx]                ;restore dx
        mov     di,[sdi]                ;restore di
        mov     si,[ssi]                ;restore si
        mov     bp,[sbp]                ;restore bp
        mov     es,[ses]                ;restore es
        push    [sfl]                   ;put flags on stack
        push    [scs]                   ;put cs on stack
        push    [sip]                   ;put ip on stack
        mov     ds,[sds]                ;restore ds
        not     cs:[int10hOK]           ;tell our int 10h handler to reset [counter] on int requests
        mov     cs:[timeout],1          ;reset timeout indicator to say true timeout, not ctrl-alt-shift hit
        iret                            ;return to previous program
runthis endp



        align   16                      ;paragraph aligned so we can store just a segment address
EndOfResident   label   byte



_mypsp          dw      ?

deinstall       proc    near
;       This could be done by simply deallocating our PSP block and environment
;       block (if not deallocated at installation), but this is more
;       "well-behaved", in that if a DOS extender allocates an extra info block
;       per program, this will also deallocate that block, because it simply
;       deallocates anything that belongs to us.
;       This isn't resident code, so we might as well take the extra effort.
        mov     ax,0caffh               ;are we installed yet
        int     10h
        cmp     ax,0faceh
        je      @@deinstall
        mov     ah,9                    ;if not, print error message and abort
        mov     dx,offset nomsg
        int     21h
        mov     ax,4c01h
        int     21h
@@deinstall:
        mov     cs:[_mypsp],bx          ;save the PSP of the resident copy
        mov     es,bx
        mov     bl,es:[useEMS]          ;find out if resident copy is using EMS
        mov     cs:[useEMS],bl
        mov     bx,es:[EMShandle]       ;get EMS handle resident copy is using
        mov     cs:[EMShandle],bx
        mov     ax,3508h                ;check if another program covered us
        int     21h
        mov     ax,es
        cmp     ax,cs:[_mypsp]
        jne     @@yousureb
        cmp     bx,offset our08h
        jne     @@yousureb
        mov     ax,3509h
        int     21h
        mov     ax,es
        cmp     ax,cs:[_mypsp]
        jne     @@yousureb
        cmp     bx,offset our09h
        jne     @@yousureb
        mov     ax,3510h
        int     21h
        mov     ax,es
        cmp     ax,cs:[_mypsp]
        jne     @@yousureb
        cmp     bx,offset our10h
        jne     @@yousureb
        mov     ax,3513h
        int     21h
        mov     ax,es
        cmp     ax,cs:[_mypsp]
        jne     @@yousureb
        cmp     bx,offset our13h
        jne     @@yousureb
;        mov     ax,3514h
;        int     21h
;        mov     ax,es
;        cmp     ax,cs:[_mypsp]
;        jne     @@yousureb
;        cmp     bx,offset our14h
;        jne     @@yousureb
        mov     ax,3517h
        int     21h
        mov     ax,es
        cmp     ax,cs:[_mypsp]
        jne     @@yousureb
        cmp     bx,offset our17h
        jne     @@yousureb
        jmp     @@yessure
        @@yousureb:
        jmp     @@yousure
        @@yessure:
        mov     bx,cs:[_mypsp]
        mov     es,bx
        mov     dx,word ptr es:[old08h]         ; get old 8h vector
        mov     ds,word ptr es:[old08h+2]
        mov     ax,2508h                        ; restore old 8h vector
        int     21h
        mov     dx,word ptr es:[old09h]         ; get old 9h vector
        mov     ds,word ptr es:[old09h+2]
        mov     ax,2509h                        ; restore old 9h vector
        int     21h
        mov     dx,word ptr es:[old10h]         ; get old 10h vector
        mov     ds,word ptr es:[old10h+2]
        mov     ax,2510h                        ; restore old 10h vector
        int     21h
        mov     dx,word ptr es:[old13h]         ; get old 13h vector
        mov     ds,word ptr es:[old13h+2]
        mov     ax,2513h                        ; restore old 13h vector
        int     21h
;        mov     dx,word ptr es:[old14h]         ; get old 14h vector
;        mov     ds,word ptr es:[old14h+2]
;        mov     ax,2514h                        ; restore old 14h vector
;        int     21h
        mov     dx,word ptr es:[old17h]         ; get old 17h vector
        mov     ds,word ptr es:[old17h+2]
        mov     ax,2517h                        ; restore old 17h vector
        int     21h
        push    cs
        pop     ds
        mov     ah,52h
        int     21h
        mov     ax,word ptr es:[bx-2]
        mov     es,ax
        @@looptop:
        cmp     byte ptr es:[0],4dh
        jne     @@end
        mov     ax,word ptr es:[1]
        cmp     ax,word ptr cs:[_mypsp]
        jne     @@notourblock
        push    es
        mov     ax,es
        inc     ax
        mov     es,ax
        mov     ah,49h
        int     21h
        pop     es
        @@notourblock:
        mov     cx,word ptr es:[3]
        inc     cx
        mov     ax,es
        add     ax,cx
        mov     es,ax
        jmp     @@looptop
        @@end:
        cmp     cs:[useEMS],0
        jz      @@noEMS
        mov     ah,45h                  ;release resident copy's EMS block, if present
        mov     dx,cs:[EMShandle]
        int     67h                     ;if there was an error, it's out of our hands
        @@noEMS:
        mov     ah,9                    ;print deinstallation msg
        mov     dx,offset demsg
        int     21h
        mov     ax,4c00h
        int     21h
        @@yousure:
        mov     dx,offset suremsg
        call    @@str29
        @@getch:
        mov     ah,8
        int     21h
        cmp     al,'y'
        je      @@dok
        cmp     al,'Y'
        je      @@dok
        cmp     al,'n'
        je      @@dok
        cmp     al,'N'
        je      @@dok
        cmp     al,27
        je      @@dok
        jmp     @@getch
        @@dok:
        push    ax
        cmp     al,27
        je      @@nopr
        int     29h                     ;undocumented character output routine
        @@nopr:
        mov     al,13                   ;go to next line
        int     29h
        mov     al,10
        int     29h
        pop     ax
        cmp     al,'y'
        je      @@yes
        cmp     al,'Y'
        je      @@yes
        mov     dx,offset stilmsg
        call    @@str29
        pop     es
        pop     di
        mov     ax,4c01h                ;exit, return ERRORLEVEL 1
        int     21h
        @@yes:
        jmp     @@yessure
        @@str29:
        mov     di,dx
        @@sloop:                        ;output routine emulates int 21h fnc 9h
        mov     al,cs:[di]
        inc     di
        cmp     al,'$'
        je      @@sexit
        int     29h                     ;undocumented character output routine
        jmp     @@sloop
        @@sexit:
        ret
deinstall       endp



newopts proc    near
        push    ax
        push    bx
        push    dx
        mov     ax,0caffh               ;are we installed yet
        int     10h                     ;returns bx=resident PSP if installed, and ax=0faceh
        cmp     ax,0faceh
        je      @@setnew
        mov     ah,9                    ;if not, print error message and abort
        mov     dx,offset nomsg
        int     21h
        mov     ax,4c01h
        int     21h
@@setnew:
        mov     es,bx                   ;set up to write subsequent params to resident copy
        pop     dx
        pop     bx
        pop     ax
        ret
newopts endp



setminutes      proc    near
        call    finddigit               ;convert string to digit
        jc      @@done
        cmp     cl,30                   ;make sure it is less than 30 minutes
        ja      @@done
        mov     ax,444h                 ;multiply digit by 444h
        mul     cx
        mov     es:[wait],ax            ;save, this is # of int 8's performed in x mins
@@done:
        ret
setminutes      endp



setdelay        proc    near
        call    finddigit               ;convert string to digit
        jc      @@done
        mov     es:[delay],cl           ;save digit as a delay factor
@@done:
        ret
setdelay        endp



setlines        proc    near
        call    finddigit               ;get digit
        jc      @@done
        cmp     cl,MaxLines             ;was number less than MaxLines
        ja      @@done
        mov     es:[length],cl          ;save number of lines
@@done:
        ret
setlines        endp



setpages        proc    near
        call    finddigit               ;get value specified
        jc      @@done
        cmp     cl,8                    ;if greater than 8 ignore it
        ja      @@done
        mov     ax,1000h
        xor     ch,ch
        mul     cx                      ;number of bytes to save = 1000h * cx = ax
        mov     [buffersize],ax         ;save number of bytes to save
@@done:
        ret
setpages        endp



setEMS  proc    near
        mov     cs:[useEMS],1
        ret
setEMS  endp



setsavfon       proc    near
        call    finddigit               ;get value specified
        jc      @@done
        cmp     cl,8                    ;if greater than 8 ignore it
        ja      @@done
        mov     cs:[fontblocks],cl
@@done:
        ret
setsavfon       endp



setvidact       proc    near
        call    finddigit               ;get value specified
        jc      @@done
        cmp     cl,2                    ;if greater than 2 ignore it
        ja      @@done
        dec     cl
        mov     es:[int10hOK],cl        ;set video activity monitor
@@done:
        ret
setvidact       endp



setmouse        proc    near
        call    finddigit               ;get value specified
        jc      @@done
        cmp     cl,2                    ;if greater than 2 ignore it
        ja      @@done
        dec     cl
        mov     es:[mouse],cl           ;set mouse activity monitor
@@done:
        ret
setmouse        endp



setblank        proc    near
        cmp     es:[gfxinstalled],0     ;was VGAMoire installed with the [Bn] option?
        jne     @@done                  ;yep, tough luck, can't change it then
        call    finddigit               ;get value specified
        jc      @@done
        cmp     cl,2                    ;if greater than 2 ignore it
        ja      @@done
        dec     cl
        mov     es:[gfxcopout],cl       ;set blank mode (graphics "cop-out" mode)
        mov     cs:[gfxinstalled],cl    ;set [gfxinstalled] to 1 if B2 is used
@@done:
        ret
setblank        endp



setmirror       proc    near
        call    finddigit               ;get value specified
        jc      @@done
        cmp     cl,4                    ;if greater than 4 ignore it
        ja      @@done
        dec     cl
        mov     es:[mirror],cl          ;set mirror setting
@@done:
        ret
setmirror       endp



setspeed        proc    near
        call    finddigit               ;get value specified
        jc      @@done
        mov     es:[colorspeed],cl      ;store speed
@@done:
        ret
setspeed        endp



setcolor        proc    near
        call    finddigit               ;get value specified
        jc      @@done
        cmp     cl,numsets              ;if greater than number of color sets we know of, ignore it
        ja      @@done
        push    di                      ;save di & si
        push    si
        mov     si,offset rainbow       ;get base address of color sets
        dec     cl                      ;zero-based for indexing
        xor     ah,ah
        mov     al,cl
        mov     cl,(15*3)+1             ;calc offset from [rainbow] to color set
        mul     cl
        add     si,ax                   ;calc pointer to color set to use
        mov     cx,(15*3)+1             ;number of colors to copy + maxcolor descriptor
        mov     di,offset colorset      ; es:di -> location of colorset array (where to copy to)
        rep     movsb                   ;copy color set from list of color sets into the run-time array
        pop     si
        pop     di                      ;restore di & si
@@done:
        ret
setcolor        endp



seton   proc    near
        mov     es:[onoff],1
        ret
seton   endp



setoff  proc    near
        mov     es:[onoff],0
        ret
setoff  endp



getEMS  proc    near
        push    di
        push    si
        mov     ax,3567h                ;test for presense of EMS
        int     21h                     ;returns es=segment ptr
        mov     di,10                   ;es:di -> byte at offset 10 in device header
        mov     si,offset emmname
        mov     cx,8
        cld
        repz    cmpsb                   ;compare strings
        jz      @@EMSthere
        mov     ah,9                    ;print message saying no EMS m.m.
        mov     dx,offset noEMS
        int     21h
        jmp     @@error
@@EMSthere:
        mov     ah,46h                  ;get version, if lower than 3.0, can't use (actually, it might work, but since i'm not sure...)
        int     67h
        cmp     al,30h
        jae     @@EMS30
        mov     ah,9                    ;print message saying we need at least EMS 3.0
        mov     dx,offset EMS30
        int     21h
        jmp     @@error
@@EMS30:
        mov     ah,43h                  ;allocate an EMS handle to use
        mov     bx,1
        cmp     cs:[buffersize],3000h   ;three vid pages or less, just one EMS page
        jbe     @@getpages
        inc     bx
        cmp     cs:[buffersize],7000h   ;seven or less, two EMS pages
        jbe     @@getpages
        inc     bx                      ;eight vid pages needs three EMS pages
@@getpages:
        mov     cs:[useEMS],bl          ;store number of pages we're allocating for video text save area
        cmp     cs:[fontblocks],0       ;should we save character RAM also
        jz      @@nochr
        xor     dh,dh
        mov     dl,cs:[fontblocks]      ;how many font blocks do we need to save
        inc     dl                      ;round up and
        shr     dl,1                    ;  convert to EMS pages
        add     bx,dx                   ;add in pages for font RAM
@@nochr:
        int     67h
        or      ah,ah
        jz      @@gotEMS
        mov     ah,9                    ;error allocating EMS, so just use conventional
        mov     dx,offset EMSerr
        int     21h
        jmp     @@error
@@gotEMS:
        mov     cs:[EMShandle],dx
        mov     ah,46h                  ;get version, if 4.0 or higher, we're going to name our block
        int     67h
        cmp     al,40h
        jb      @@done
        mov     ax,5301h                ;give our handle a name
        mov     dx,cs:[EMShandle]
        mov     si,offset EMSname
        int     67h
@@done:
        pop     si
        pop     di
        ret
@@error:
        mov     ah,9                    ;say we'll use conventional memory instead
        mov     dx,offset cnvmsg
        int     21h
        mov     cs:[useEMS],0
        mov     cs:[fontblocks],0       ;can't save fonts anymore!!
        jmp     @@done
emmname db      'EMMXXXX0'
EMSname db      'VGAMoire'
noEMS   db      'EMS memory manager not found.',13,10,'$'
EMS30   db      'VGAMoire requires EMS 3.0 or higher.',13,10,'$'
EMSerr  db      'Error allocating EMS memory.',13,10,'$'
cnvmsg  db      'Using conventional memory instead.',13,10,'$'
getEMS  endp



reznew          db      0               ;flag:  are we setting new params for resident copy

setup   proc    near                    ;main procedure for setting up program
        mov     ah,9                    ;print id string
        mov     dx,offset msg
        int     21h
        ; process the command line
        mov     di,80h                  ;set si and di to command tail
        mov     si,81h
@@nextchr:
        lodsb                           ;get next character
        cmp     al,' '                  ;is it a whitespace, space or tab or enter
        jz      @@nextchr
        cmp     al,9
        jz      @@nextchr
        cmp     al,0dh                  ;if enter quit processing
        jz      @@endln
        dec     si                      ;adjust si
        xor     al,al                   ;mark end of string with 0
        stosb
@@getchr:
        lodsb                           ;get character
        cmp     al,' '                  ;is it a whitespace
        jz      @@nextchr
        cmp     al,9
        jz      @@nextchr
        cmp     al,0dh
        jz      @@endln
        cmp     al,'a'                  ;is the character a small letter
        jl      @@storchr
        cmp     al,'z'
        jg      @@storchr
        and     al,223                  ;if so capitalize it
        cmp     al,'N'                  ;send new params?
        jne     @@storchr
        mov     [reznew],1
@@storchr:
        stosb                           ;store character again
        jmp     @@getchr
@@endln:
        xor     ax,ax                   ;put double zero in tail to mark end of buffer
        stosw
        cmp     [reznew],1              ;should we set options for resident copy?
        jne     @@parse
        call    newopts
@@parse:
        ; parse the command line and act on parameters
        mov     si,80h                  ;start reading here
        cmp     word ptr [si+1],'?'     ;was '?' input
        jnz     @@findnull
        mov     ah,9                    ;then print message describing switches
        mov     dx,offset usage
        int     21h
        mov     ax,4c00h                ;and quit with error level 0
        int     21h
@@findnull:
        lodsb                           ;read character until we get a 0
        or      al,al
        jnz     @@findnull
        cmp     byte ptr [si],al        ;is next character a 0, are we finished ?
        jnz     @@continue
        jmp     @@donepar
@@continue:
        lodsb                           ;get character
        xor     bp,bp                   ;compare to valid options
@@nextone:
        mov     ah,cs:[bp + offset options]
        or      ah,ah
        jz      @@findnull
        cmp     ah,al
        je      @@gotone
        inc     bp
        jmp     @@nextone
@@gotone:
        shl     bp,1                    ;convert to word index
        call    word ptr cs:[bp + offset optaddr]
        jmp     @@findnull
@@donepar:
        cmp     [useEMS],0
        jnz     @@possiblefont
        mov     [fontblocks],0
@@possiblefont:
;        push    cs
;        pop     es
        cmp     [reznew],0
        jz      @@start
        ; -- possible enhancement: display current settings before exitting
        mov     ah,9                    ;display msg that params have been updated
        mov     dx,offset newmsg
        int     21h
        mov     ax,4c00h
        int     21h
@@start:
        ; start the moire pattern without going resident
        call    noresident
        cmp     [useEMS],0
        jz      @@noEMS
        call    getEMS
@@noEMS:
        mov     ax,0caffh               ;are we installed yet
        int     10h
        cmp     ax,0faceh
        jne     @@storevec
        mov     ah,9                    ;if so, print error message and abort
        mov     dx,offset ainmsg
        int     21h
        mov     ax,4c01h
        int     21h
@@storevec:
        ; store interrupt vectors
        mov     ax,3508h                ;save int 08h vector
        int     21h
        mov     word ptr [old08h],bx
        mov     word ptr [old08h+2],es
        mov     ah,25h                  ;install our int 08h
        mov     dx,offset our08h
        int     21h
        mov     ax,3509h                ;save int 09h vector
        int     21h
        mov     word ptr [old09h],bx
        mov     word ptr [old09h+2],es
        mov     ah,25h                  ;install our int 09h
        mov     dx,offset our09h
        int     21h
        mov     ax,3510h                ;save int 10h vector
        int     21h
        mov     word ptr [old10h],bx
        mov     word ptr [old10h+2],es
        mov     ah,25h                  ;install our int 10h
        mov     dx,offset our10h
        int     21h
        mov     ax,3513h                ;save int 13h vector
        int     21h
        mov     word ptr [old13h],bx
        mov     word ptr [old13h+2],es
        mov     ah,25h                  ;install our int 13h
        mov     dx,offset our13h
        int     21h
;        mov     ax,3514h                ;save int 14h vector
;        int     21h
;        mov     word ptr [old14h],bx
;        mov     word ptr [old14h+2],es
;        mov     ah,25h                  ;install our int 14h
;        mov     dx,offset our14h
;        int     21h
        mov     ax,3517h                ;save int 17h vector
        int     21h
        mov     word ptr [old17h],bx
        mov     word ptr [old17h+2],es
        mov     ah,25h                  ;install our int 17h
        mov     dx,offset our17h
        int     21h
if __DeallocateEnvironment
        mov     ax,word ptr ds:[2ch]    ; deallocate our environment block
        mov     es,ax
        mov     ah,49h
        int     21h
endif
        mov     ah,9                    ;print install message
        mov     dx,offset inmsg
        int     21h
        ; set up and install VGAMoire
        push    [wait]                  ;set counter = wait
        pop     [counter]
        xor     dx,dx
        cmp     [useEMS],0
        jnz     @@yesEMS
        mov     [storetext],cs          ;make pointer to video save area
        mov     ax,offset EndOfResident
        mov     cl,4
        shr     ax,cl
        add     [storetext],ax          ;got new address for conventional memory video save buffer
        xor     bx,bx                   ;set bx=0 in case no mouse driver!!!
        mov     ax,15h                  ;find size of mouse state buffer
        int     33h
        mov     [mousesize],bx          ;store size of mouse buffer
        push    bx
;+*VGA
        mov     ax,1c00h                ;get size of video state save buffer
        mov     cx,7
        int     10h
        mov     cl,6                    ;multiple by 64
        shl     bx,cl
        mov     dx,bx
;-*VGA
        pop     bx
        cmp     [gfxcopout],0           ;if gfx cop-out mode is in effect, don't reserve any memory
        jne     @@yesEMS
        add     dx,[buffersize]         ;plus space to reserve for text buffer
        add     dx,bx                   ;and for mouse state buffer
@@yesEMS:
        add     dx,offset EndOfResident ;actual program code to reserve
        inc     dx                      ;for int 27h, dx = (offset last_byte) + 1
        int     27h
setup endp



finddigit       proc    near            ;convert string to digit
        xor     ax,ax                   ;set ax = 0 = cx, bx = 10
        mov     bx,10
        mov     cx,ax
@@nextdigit:
        lodsb                           ;get next character
        or      al,al                   ;are we at end
        jz      @@done
        cmp     al,'9'                  ;was character bigger than 9
        jg      @@fail
        sub     al,'0'                  ;or less than zero
        jl      @@fail
        xchg    ax,cx                   ;cx = cx * 10 + ax
        mul     bx
        add     cx,ax
        or      ch,ch                   ;is cx > 255
        jz      @@nextdigit
@@fail:
        dec     si                      ;return with failure
        stc
        ret
@@done:
        or      cx,cx                   ;is cx = 0 then return failure
        jz      @@fail
        dec     si                      ;return with success
        clc
        ret
finddigit       endp



;memprob         db      'Memory allocation error.',13,10,'$'

noresident      proc    near            ;run graphics w/o staying resident
        cmp     [wait],0                ;was wait time specified
        jz      nr1
        ret                             ;if so, return and install
nr1:
;        mov     ah,4ah                  ;free unneeded memory
;        mov     bx,sp
;        mov     cl,4
;        shr     bx,cl
;        inc     bx                      ;extra paragraph
;        push    cs
;        pop     es
;        int     21h
;        jnc     @@nonres_ok
;        mov     ah,9
;        mov     dx,offset memprob
;        int     21h
;        mov     ax,4c01h
;        int     21h
;@@nonres_ok:
        mov     [buffersize],8000h      ;save all video text RAM
        cmp     [useEMS],0
        jz      @@noEMS1
        call    getEMS
@@noEMS1:
        mov     [wait],444h             ;give wait a value so we don't loop endlessly!
        mov     [counter],444h
        cmp     [useEMS],0
        jnz     @@yesEMS
        mov     [storetext],cs          ;make our video save buffer be after all important code
        mov     ax,offset EndOfNonresident
        mov     cl,4
        shr     ax,cl
        add     [storetext],ax          ;got new address for conventional memory video save buffer
        @@yesEMS:
        mov     ax,3509h                ;save int 09h vector
        int     21h
        mov     word ptr [old09h],bx
        mov     word ptr [old09h+2],es
        mov     ax,2509h                ;install our int 09h
        mov     dx,offset our09h
        int     21h
        mov     ax,3508h                ;save int 08h vector
        int     21h
        mov     word ptr [old08h],bx
        mov     word ptr [old08h+2],es
        mov     ax,2508h                ;install our int 08h
        mov     dx,offset our08h
        int     21h
        push    es                      ;timer-based pause loop
        mov     es,[keybuff]
        mov     bx,46ch
        mov     cx,es:[bx]              ;read timer
@@tryagain:
        mov     dx,es:[bx]              ;read timer
        sub     dx,cx                   ;find difference
        test    dx,8000h                ;negative?
        jz      @@pos
        neg     dx                      ;take absolute value
@@pos:
        cmp     dx,3                    ;wait a smidgen
        jb      @@tryagain
        pop     es
        mov     [timeout],1
        mov     [counter],1
        @@untildone:
        cmp     [counter],10            ;wait until counter gets reset (ie, mouse or keyboard activity)
        jb      @@untildone
        mov     ax,2508h                ;restore old int 08h handler
        mov     dx,word ptr [old08h]    ;  (don't need to use a CS: override yet)
        mov     ds,word ptr [old08h+2]
        int     21h
        mov     ax,2509h                ;restore old int 09h handler
        mov     dx,word ptr cs:[old09h] ;  (from here on we need CS: overrides)
        mov     ds,word ptr cs:[old09h+2]
        int     21h
        push    cs                      ;restore DS: addressability
        pop     ds
        cmp     [useEMS],0
        jz      @@noEMS2
        mov     ah,45h                  ;release EMS block, if present
        mov     dx,[EMShandle]
        int     67h                     ;if there was an error, it's out of our hands
        @@noEMS2:
        mov     ax,4c00h                ;exit, errorlevel 0
        int     21h
noresident endp



        align   16                      ;paragraph aligned so we can just store a segment address
EndOfNonresident        label   byte



options         db      'BCDEFLMPRSUVW-+',0
optaddr         label   word
                dw      setblank
                dw      setcolor
                dw      setdelay
                dw      setEMS
                dw      setsavfon
                dw      setlines
                dw      setmouse
                dw      setpages
                dw      setmirror
                dw      setspeed
                dw      deinstall
                dw      setvidact
                dw      setminutes
                dw      setoff
                dw      seton

msg             db      13,10
                db      'VGAMoire ',VERSION,',  by Christopher Antos,  ',DATE,',  (C)1990',13,10,'$'
inmsg           db      '++ VGAMoire now installed.',13,10,'$'
nomsg           db      '** VGAMoire has not been installed yet.',13,10,'$'
ainmsg          db      '** VGAMoire is already installed.',13,10
                db      '   Use VGAMOIRE U to deinstall.',13,10
                db      '   Use VGAMOIRE N to set new parameters.',13,10,'$'
demsg           db      '-- VGAMoire now deinstalled.',13,10,'$'
suremsg         db      13,10
                db      'Another program has captured VGAMoire''s interrupt vectors.',13,10
                db      'Deinstalling VGAMoire may cause a system crash.',13,10
                db      'Are you sure you want to deinstall VGAMoire? $'
stilmsg         db      '** VGAMoire is still installed.',13,10,'$'
newmsg          db      '++ New parameters sent to resident VGAMoire.',13,10,'$'
usage           db      13,10
                db      'Usage:  VGAMOIRE [Bn] [Cn] [Dn] [E] [Fn] [Ln] [Mn] [N]',13,10
                db      '                 [Pn] [Rn] [Sn] [U] [Vn] [Wn] [-] [+]',13,10
                db      10
                db      'Summary of options:                      * = invalid if used with [N]',13,10
                db      '  Bn    blank:  1-graphics, 2-always     Pn  * save <n> video pages (1-8)',13,10
                db      '  Cn    colors:  1-rainbow, 2-fire,      Rn    mirror:  1-none, 2-H, 3-V, 4-H/V',13,10
                db      '        3-frost, 4-pastel                Sn    color speed:  (1-255)',13,10
                db      '  Dn    delay:  (1-255)                  U     uninstall',13,10
                db      '  E   * use EMS                          Vn    video:  1-monitor, 2-ignore',13,10
                db      '  Fn  * save <n> font blocks (1-8)       Wn    wait <n> minutes (1-30)',13,10
                db      '  Ln    lines:  (1-50)                   -     turn off',13,10
                db      '  Mn    mouse:  1-monitor, 2-ignore      +     turn on',13,10
                db      '  N     send options to resident copy    ',13,10
                db      10
                db      '    Press Ctrl-Alt-Shift to pop up screen saver.  The screen saver may be',13,10
                db      '    disabled by pressing Ctrl-Alt-E and reenabled by pressing Ctrl-Alt-B.',13,10
                db      '    Use  VGAMOIRE - N  to turn off resident copy (or  + N  to turn back on).',13,10
                db      '$'



numsets         equ     5               ;number of color sets defined

; R,G,B values for video DAC color registers

rainbow         db      63,  0,  0      ;color #1       [red]
                db      63, 30,  0      ;
                db      63, 45,  0      ;
                db      63, 63,  0      ;               [yellow]
                db      42, 63,  0      ;
                db       0, 63,  0      ;               [green]
                db       0, 63, 42      ;
                db       0, 63, 63      ;               [cyan]
                db       0, 45, 63      ;
                db       0, 31, 63      ;
                db       0,  0, 63      ;               [blue]
                db      30,  0, 63      ;
                db      45,  0, 63      ;
                db      63,  0, 63      ;               [violet]
                db      63,  0, 47      ;color #15
                db      15

fire            db      63,  0,  0      ;color #1       [red]
                db      63,  7,  0
                db      63, 13,  0
                db      63, 22,  0
                db      63, 27,  0
                db      63, 32,  0
                db      63, 36,  0
                db      63, 42,  0
                db      63, 48,  0
                db      63, 52,  0
                db      63, 58,  0
                db      63, 63,  0      ;color #13      [yellow]
                db      63, 49,  0
                db      63, 40,  0
                db      63, 30,  0
                db      15

frost           db      63, 21, 63      ;color #1       [purple]
                db      52, 21, 63
                db      42, 21, 63
                db      31, 21, 63
                db      21, 21, 63      ;color #5       [blue]
                db      21, 29, 63
                db      21, 38, 63
                db      21, 46, 63
                db      21, 54, 63
                db      21, 63, 63      ;color #10      [cyan]
                db      35, 63, 63
                db      49, 63, 63
                db      63, 63, 63      ;color #13      [white]
                db      63, 49, 63
                db      63, 35, 63
                db      15

pastel          db      63, 47, 47      ;color #1       [red]
                db      63, 50, 47      ;
                db      63, 54, 47      ;
                db      63, 58, 47      ;
                db      63, 63, 47      ;               [orange]
                db      59, 63, 47      ;
                db      55, 63, 47      ;               [yellow]
                db      47, 63, 49      ;
                db      47, 63, 58      ;               [green]
                db      47, 62, 63      ;
                db      47, 57, 63      ;               [cyan]
                db      47, 50, 63      ;
                db      51, 47, 63      ;
                db      58, 47, 63      ;               [violet]
                db      63, 47, 57      ;color #15
                db      15

test            db      63, 63, 63      ;
                db      33, 33, 33      ;
                db      63, 63, 63      ;
                db      33, 33, 33      ;
                db      63, 63, 63      ;
                db      33, 33, 33      ;
                db      63, 63, 63      ;
                db      63, 63, 63      ;
                db      33, 33, 33      ;
                db      63, 63, 63      ;
                db      33, 33, 33      ;
                db      63, 63, 63      ;
                db       0,  0,  0      ;
                db       0,  0,  0      ;
                db       0,  0,  0      ;
                db      12

cseg    ends



public  EMS30
public  EMSerr
public  EMShandle
public  EMSname
public  EndOfNonresident
public  EndOfResident
public  MaxLines
public  MaxRev
public  _mypsp
public  ainmsg
public  alllines
public  aval
public  blackcolor
public  buffersize
public  checkx
public  checkxy
public  cnvmsg
public  colorset
public  colorspeed
public  colorwait
public  counter
public  curcolor
public  cval
public  de0
public  de1
public  deinstall
public  delay
public  demsg
public  di1
public  di2
public  di4
public  doline
public  dox
public  doxy
public  drawmoire
public  emmname
public  ex1
public  finddigit
public  fire
public  fontblocks
public  freebuf
public  frost
public  getEMS
public  getbuf
public  getcoords
public  gfxcopout
public  gfxinstalled
public  gfxmode
public  gonow
public  index
public  inmsg
public  int10hOK
public  keybuff
public  length
public  linesx1
public  linesx2
public  linesy1
public  linesy2
public  main
public  maxcolor
public  mirror
public  moire
public  mouse
public  mousesize
public  msg
public  newmsg
public  newopts
public  noEMS
public  nomsg
public  noresident
public  nr1
public  numsets
public  nw1
public  old08h
public  old09h
public  old10h
public  old13h
public  old17h
public  oldmx
public  oldmy
public  onoff
public  optaddr
public  options
public  our08h
public  our09h
public  our10h
public  our13h
public  our17h
public  pastel
public  rainbow
public  randir
public  randvel
public  ranex
public  rb20
public  rd1
public  reset
public  revdir
public  revex
public  reznew
public  rnd
public  rndcnt
public  rnum
public  runthis
public  rval
public  sax
public  sax2
public  sbp
public  sbx
public  sbx2
public  scs
public  scx
public  scx2
public  sdi
public  sds
public  sds2
public  sdx
public  sdx2
public  seed
public  ses
public  setEMS
public  setblank
public  setcolor
public  setdelay
public  setlines
public  setminutes
public  setmirror
public  setmouse
public  setoff
public  seton
public  setpages
public  setsavfon
public  setspeed
public  setup
public  setvidact
public  sfl
public  sip
public  ssi
public  stilmsg
public  storetext
public  suremsg
public  switchdir
public  textbuff
public  timeout
public  usage
public  useEMS
public  videomode
public  wait
public  x1
public  x1rev
public  x2
public  x2rev
public  xv1
public  xv2
public  y1
public  y1rev
public  y2
public  y2rev
public  yv1
public  yv2



        end     main

;______ EOF ___________________________________________________________________
