Metropoli BBS
VIEWER: conceal.asm MODE: TEXT (ASCII)
_TEXT          SEGMENT PUBLIC 'CODE'
               ASSUME  CS:_TEXT,DS:_TEXT,ES:_TEXT,SS:_TEXT

               ORG     80H
DTA            LABEL   BYTE

               ORG     100H
START:         JMP     INITIALIZE

;              DATA AREA
;              ---------
CR             EQU     13
LF             EQU     10
FF             EQU     12
CTRL_Z         EQU     26
SPACE          EQU     32
BOX            EQU     254
BELL           EQU     7
TAB            EQU     9

SIGNATURE      DB      SPACE,CR,CR,LF
COPYRIGHT      DB      "CONCEAL 1.0 (c) 1990 Ziff Communications Co.",CR,LF
PROGRAMMER     DB      "PC Magazine ",BOX," Michael J. Mefford",CR,LF,LF,"$"
               DB      CTRL_Z

KB_FLAG        EQU     17H
BUFFER_HEAD    EQU     1AH
BUFFER_TAIL    EQU     1CH
CTRL_STATE     EQU     4
ALT_STATE      EQU     8

COMBO          DB      "Z"
HOT_KEY_SCAN   DB      2CH                     ;"Z"
HOT_SHIFT_KEY  DB      ALT_STATE

OLD8           DW      ?,?                     ;Old interrupt addresses.
OLD9           DW      ?,?
OLD21          DW      ?,?


PORT_A         EQU     60H
PORT_B         EQU     61H
COMMAND_PORT   EQU     20H
EOI            EQU     20H

MATCHING       STRUC
RESERVED       DB      21 DUP (?)
ATTRIBUTE      DB              ?
FILE_TIME      DW              ?
FILE_DATE      DW              ?
SIZE_LOW       DW              ?
SIZE_HIGH      DW              ?
FILE_NAME      DB      13 DUP (?)
MATCHING       ENDS

PASS_CODE      EQU     95H
PASSWORD       DB      "S" XOR 95H, "H" XOR 96H, "A" XOR 97H, "Z" XOR 98H
               DB      "A" XOR 99H, "M" XOR 9AH
PASS_LEN       EQU     $ - PASSWORD
               DB      50 DUP (?)
PASSWORD_LEN   EQU     $ - PASSWORD
PASS_CNT       DW      PASS_LEN
PASS_MASK      DB      PASS_CODE
PASS_POINTER   DW      OFFSET DTA

TRAP_FUNCS     LABEL   BYTE
FCB_FUNCS      DB      0FH, 13H, 17H ;Open, Delete, Rename.
HANDLE_FUNCS   DB      3DH, 41H, 56H
TRAP_CNT       EQU     $ - TRAP_FUNCS

BOGUS_ARG      DB      12 DUP (0)
FILESPEC       DB      13 DUP (?)

EGA_INFO       EQU     87H
EGA_ACTIVE     EQU     00001000B
ADDR_6845      EQU     63H
CRT_MODE_SET   EQU     65H
VIDEO_SIGNAL   EQU     00001000B
CRT_MODE       EQU     49H
SAVE_MODE      DB      ?
CRT_PALETTE    EQU     66H
SAVE_PALETTE   DB      ?
CRT_COLS       EQU     4AH
ACTIVE_PAGE    EQU     62H
CURSOR_ADDR    DW      ?
CURSOR_POSN    EQU     50H
CURSOR_POS     DW      ?
CURSOR_MODE    DW      ?

ON             EQU     1
OFF            EQU     0
SOFT_BLANK     EQU     1
HARD_BLANK     EQU     2

CONCEAL_FLAG   DW      OFF              ; ON if files have been secured.
BLANK_FLAG     DB      OFF              ; ON if /G ON or /B ON.
CGA_MONO       EQU     0
EGA_VGA        EQU     1
SOFTWARE       EQU     2
BLANK_TYPE     DB      ?                ; CGA_MONO, EGA_VGA or SOFTWARE blanked.
BLANK_SCREEN   DB      OFF              ; ON if screen is blanked.
UNBLANK_FLAG   DB      OFF              ; ON if password required to unblank.
BLANK_IT       DB      OFF              ; ON if Hotkey pressed; timer will blank
UNBLANK_IT     DB      OFF              ; ON if key pressed; timer will unblank
GRAPHIC_APPS   DB      OFF              ; ON if Don't blank graphic apps.
BUFFER_FLAG    DB      OFF              ; ON if installed with /G.
CHANGE_FLAG    DB      OFF              ; ON if attribute to be changed.
CONCEAL_STATS  EQU     0
ATTR_STATS     EQU     1
STATS_FLAG     DB      ?

DEFAULT_TIME   EQU     3 * 1092
TIMER_BUSY     DB      0
TIME_OUT       DW      DEFAULT_TIME
COUNTER        DW      DEFAULT_TIME

CONCEAL_BUSY   DB      0                       ;=1 if DOS hook entered.

;----------------------------------------------;

DOS            PROC    FAR

               CMP     CS:CONCEAL_FLAG,ON
               JNZ     SKIP_CONCEAL2
               CMP     CONCEAL_BUSY,1
               JZ      SKIP_CONCEAL2
               MOV     CS:CONCEAL_BUSY,1

               PUSH    DS
               PUSH    ES
               PUSH    BX
               PUSH    CX
               PUSH    DX
               PUSH    SI
               PUSH    DI
               PUSH    AX

               CMP     AX,4301H                ;Change file mode.
               JNZ     TRAP
               PUSH    AX
               PUSH    CX
               MOV     AX,4300H
               INT     21H
               MOV     BX,CX
               POP     CX
               POP     AX
               JC      SKIP_CONCEAL1
               TEST    BX,4
               JZ      SKIP_CONCEAL1
               TEST    CX,4
               JZ      MAKE_CONCEAL
               JMP     SHORT SKIP_CONCEAL1

TRAP:          CLD
               MOV     SI,DS                   ;Segment.
               MOV     CX,CS
               MOV     DS,CX
               MOV     ES,CX
               XCHG    AH,AL
               MOV     DI,OFFSET TRAP_FUNCS
               MOV     CX,TRAP_CNT
               REPNZ   SCASB
               JNZ     SKIP_CONCEAL1
               MOV     CX,DX                   ;Offset.
               CMP     DI,OFFSET HANDLE_FUNCS + 1
               JAE     DO_CONCEAL
               CALL    CONVERT_FCB

DO_CONCEAL:    CALL    CK_CONCEAL              ;See if filespec has System attr.
               JC      MAKE_CONCEAL

SKIP_CONCEAL1: POP     AX
               POP     DI
               POP     SI
               POP     DX
               POP     CX
               POP     BX
               POP     ES
               POP     DS
               MOV     CS:CONCEAL_BUSY,0
SKIP_CONCEAL2: JMP     DWORD PTR CS:OLD21      ;Call keyboard handling routine.

MAKE_CONCEAL:  PUSH    CS
               POP     DS
               MOV     DX,OFFSET BOGUS_ARG     ;To fail, pass the call with
               POP     AX                      ; an invalid argument.
               PUSHF
               CALL    DWORD PTR CS:OLD21
               POP     DI
               POP     SI
               POP     DX
               POP     CX
               POP     BX
               POP     ES
               POP     DS
               MOV     CS:CONCEAL_BUSY,0
               RET     2                       ;Return with flags intact.

DOS            ENDP

;----------------------------------------------;
; INPUT: SI:CX -> FCB; OUTPUT: SI:CX -> FILESPEC

CONVERT_FCB:   XCHG    SI,CX                   ;Convert a FCB format to
               MOV     DS,CX                   ; a handle format.
               MOV     DI,OFFSET FILESPEC

               LODSB
               CMP     AL,-1                   ;Extended FCB?
               JNZ     DRIVE
               ADD     SI,6
               LODSB
DRIVE:         OR      AL,AL
               JNZ     STORE_DRIVE
               MOV     AH,19H                  ;Current disk.
               PUSHF
               CALL    DWORD PTR CS:OLD21
               INC     AL

STORE_DRIVE:   ADD     AL,"A" - 1
               STOSB
               MOV     AL,":"
               STOSB
               PUSH    SI
               MOV     CX,8

NEXT_NAME:     LODSB
               CMP     AL,SPACE
               JBE     EXTENSION
               STOSB
               LOOP    NEXT_NAME

EXTENSION:     MOV     AL,"."
               STOSB
               POP     SI
               ADD     SI,8
               MOV     CX,3
NEXT_EXT:      LODSB
               CMP     AL,SPACE
               JBE     ASCIIZ
               STOSB
               LOOP    NEXT_EXT

ASCIIZ:        XOR     AL,AL
               STOSB
               MOV     SI,CS
               MOV     CX,OFFSET FILESPEC
               RET

;----------------------------------------------;
; INPUT: SI:CX -> FILESPEC;  OUTPUT: CF = 1 if FILESPEC has system attribute.

CK_CONCEAL:    MOV     AH,2FH                  ;Get disk transfer address
               PUSHF
               CALL    DWORD PTR CS:OLD21
               PUSH    ES                      ; and save.
               PUSH    BX

               PUSH    CS
               POP     DS
               MOV     DX,OFFSET DTA
               MOV     AH,1AH                  ;Set disk transfer address
               PUSHF                           ; to our DTA.
               CALL    DWORD PTR CS:OLD21

               MOV     DS,SI
               MOV     DX,CX
               MOV     CX,1 OR 2 OR 4          ;Read-only, hidden and system
               MOV     AH,4EH                  ; attributes.
               PUSHF
               CALL    DWORD PTR CS:OLD21
               CMC
               JNC     CK_CONCEAL_END
               TEST    CS:DTA.ATTRIBUTE,4      ;System attribute?
               STC
               JNZ     CK_CONCEAL_END
               CLC

CK_CONCEAL_END: POP     DX
               POP     DS
               PUSHF
               MOV     AH,1AH                  ;Restore DTA.
               PUSHF
               CALL    DWORD PTR CS:OLD21
               POPF
               RET

;------------------------------------------------------------------------------
;Execution comes here thru interrupt 9 every time a key is pressed or released.
;------------------------------------------------------------------------------

KEYBOARD       PROC    NEAR
               STI
               PUSH    AX
               IN      AL,PORT_A               ;Get scan code.

               PUSHF
               CALL    CS:DWORD PTR OLD9

               CMP     CS:BLANK_FLAG,OFF       ;Blanking active?
               JNZ     CK_STATE
               JMP     KEYBOARD_END

CK_STATE:      PUSH    BX
               PUSH    SI
               PUSH    DI
               PUSH    DS
               PUSH    ES

               MOV     BX,CS
               MOV     DS,BX
               MOV     BX,40H
               MOV     ES,BX
               CMP     AL,HOT_KEY_SCAN         ;Our hot key?
               JNZ     CK_DISABLED

               MOV     AL,ES:[KB_FLAG]         ;Get keyboard shift status
               TEST    AL,HOT_SHIFT_KEY
               JZ      CK_DISABLED

               CLI
               MOV     AX,ES:BUFFER_HEAD       ;Flush keyboard buffer.
               MOV     ES:BUFFER_TAIL,AX
               CMP     BLANK_SCREEN,ON         ;Screen already blanked?
               JZ      LILLY_JMP
               MOV     BLANK_IT,ON             ;If no, request blank.
LILLY_JMP:     JMP     KEYBOARD_DONE

CK_DISABLED:   CMP     BLANK_SCREEN,ON
               JNZ     RESET_TIMER
               TEST    AL,80H                  ;Key release?
               JNZ     KEYBOARD_DONE           ;If yes, ignore.
               CMP     UNBLANK_FLAG,ON         ;Password required?
               JNZ     DO_UNBLANK

               MOV     BX,CS
               MOV     ES,BX
               CLD
               MOV     AH,1
               INT     16H
               JZ      KEYBOARD_DONE
               XOR     AH,AH                   ;Get password.
               INT     16H
               MOV     DI,PASS_POINTER
               CMP     AL,CR                   ;If carriage return, decode.
               JZ      DECODE
               CMP     DI,OFFSET DTA + PASSWORD_LEN
               JAE     KEYBOARD_DONE

               CMP     AL,"a"                  ;Capitalize.
               JB      DO_STORE
               CMP     AL,"z"
               JA      DO_STORE
               AND     AL,5FH

DO_STORE:      XOR     AL,PASS_MASK            ;Encode.
               STOSB
               MOV     PASS_POINTER,DI
               INC     PASS_MASK
               JMP     SHORT KEYBOARD_DONE

DECODE:        STOSB                           ;Carriage return.
               MOV     PASS_MASK,PASS_CODE
               MOV     DI,OFFSET DTA
               MOV     PASS_POINTER,DI
               MOV     SI,OFFSET PASSWORD
               PUSH    CX
               MOV     CX,PASS_CNT
               REPZ    CMPSB
               POP     CX
               JNZ     KEYBOARD_DONE           ;Valid password?
               CMP     BYTE PTR [DI],CR
               JNZ     KEYBOARD_DONE

DO_UNBLANK:    MOV     UNBLANK_IT,ON           ;Request unblank.
               MOV     BX,40H
               MOV     DS,BX
               CLI
               MOV     AX,DS:BUFFER_HEAD       ;Flush keyboard buffer.
               MOV     DS:BUFFER_TAIL,AX
               JMP     SHORT KEYBOARD_DONE

RESET_TIMER:   MOV     AX,TIME_OUT             ;Reset time-out.
               MOV     COUNTER,AX

KEYBOARD_DONE: POP     ES
               POP     DS
               POP     DI
               POP     SI
               POP     BX
KEYBOARD_END:  POP     AX
               IRET

KEYBOARD       ENDP

;------------------------------------------------------------------------------
;Interrupt 8 handling routine.
;------------------------------------------------------------------------------

TIMER          PROC    NEAR
               CLI
               PUSHF
               CALL    CS:DWORD PTR OLD8
               CMP     CS:BLANK_FLAG,OFF       ;Blanking active?
               JZ      TIMER_END

               PUSH    AX
               PUSH    DS
               MOV     AX,CS
               MOV     DS,AX

               CMP     TIMER_BUSY,0
               JNZ     TIMER_DONE
               INC     TIMER_BUSY

               CLD
               CMP     UNBLANK_IT,ON           ;Request to unblank?
               JZ      ENABLE
               CMP     BLANK_IT,ON             ;Request to blank?
               JZ      DO_BLANK

               CMP     COUNTER,0               ;If timed-out, keep blanked.
               JZ      KEEP_BLANK
               DEC     COUNTER                 ;Else, decrement counter.
               JNZ     TIMER_EXIT

DO_BLANK:      CALL    BLANK
               JMP     SHORT TIMER_EXIT

ENABLE:        CALL    UNBLANK
               JMP     SHORT TIMER_EXIT

KEEP_BLANK:    CALL    BLANK_MODE

TIMER_EXIT:    CLI
               DEC     TIMER_BUSY

TIMER_DONE:    POP     DS
               POP     AX

TIMER_END:     IRET

TIMER          ENDP

;----------------------------------------------;

BLANK:         MOV     BLANK_IT,OFF            ;Turn off blank request.
               PUSH    ES
               PUSH    BX
               PUSH    CX
               PUSH    DX
               MOV     AX,40H
               MOV     ES,AX

               MOV     AL,ES:[CRT_MODE]
               CMP     GRAPHIC_APPS,ON         ;Blank graphics modes?
               JNZ     CK_TYPE
               CMP     AL,7                    ;Mono?
               JZ      CK_TYPE
               CMP     AL,3                    ;Text?
               JBE     CK_TYPE
               MOV     AX,TIME_OUT
               MOV     COUNTER,AX
               JMP     BLANK_END

CK_TYPE:       MOV     BLANK_SCREEN,ON         ;Note screen will be blanked.
               MOV     COUNTER,0
               STI

               CMP     BLANK_FLAG,HARD_BLANK   ;Hardware blank?
               JZ      DO_HARDWARE
               CMP     AL,3                    ;Text mode?
               JA      DO_HARDWARE
               CMP     ES:BYTE PTR [ACTIVE_PAGE],0
               JNZ     DO_HARDWARE
               CMP     ES:WORD PTR CRT_COLS,80
               JA      DO_HARDWARE
               PUSH    BP
               PUSH    ES
               MOV     DL,24                   ;25 rows?
               MOV     AX,1130H
               INT     10H
               POP     ES
               POP     BP
               CMP     DL,24
               JA      DO_HARDWARE

               CALL    SAVE_SCREEN             ;Software blank.
               MOV     BLANK_TYPE,SOFTWARE
               JMP     SHORT BLANK_END

DO_HARDWARE:   MOV     BL,10H                  ;Ega or Vga?
               MOV     AH,12H
               INT     10H
               CMP     BL,10H
               JZ      CGA_OR_MONO
               TEST    ES:BYTE PTR [EGA_INFO],EGA_ACTIVE
               JNZ     CGA_OR_MONO

               CLI
               MOV     DX,3BAH                 ;Status register; read to
               IN      AL,DX                   ; initialize address register.
               ADD     DX,20H
               IN      AL,DX
               SUB     DX,1AH                  ;Attribute address register. 3C0h
               XOR     AL,AL                   ; Turn bit 5 off; palette address
               OUT     DX,AL                   ; source.
               MOV     BLANK_TYPE,EGA_VGA
               JMP     SHORT BLANK_END

CGA_OR_MONO:   CLI
               MOV     BLANK_TYPE,CGA_MONO
               MOV     DX,ES:[ADDR_6845]
               ADD     DX,4                    ;Control register.
               MOV     AL,ES:[CRT_MODE_SET]    ;Get current setting.
               AND     AL,NOT VIDEO_SIGNAL     ;Turn off video bit.
               OUT     DX,AL
               MOV     ES:[CRT_MODE_SET],AL    ;Save this setting.
               CMP     BYTE PTR ES:[CRT_MODE],3  ;Text?
               JA      BLANK_END
               MOV     AL,ES:[CRT_PALETTE]
               MOV     SAVE_PALETTE,AL
               INC     DX                      ;Color select register.
               AND     AL,NOT 1111B            ;Turn off border.
               OUT     DX,AL

BLANK_END:     STI
               POP     DX
               POP     CX
               POP     BX
               POP     ES
               RET

;----------------------------------------------;

UNBLANK:       MOV     UNBLANK_IT,OFF          ;Turn off unblank request.
               MOV     BLANK_SCREEN,OFF
               MOV     AX,TIME_OUT
               MOV     COUNTER,AX
               STI

               PUSH    ES
               PUSH    DX

               CMP     BLANK_TYPE,SOFTWARE
               JZ      UNBLANK_SOFT
               CMP     BLANK_TYPE,CGA_MONO
               JZ      UNBLANK_CGA

               CLI
               MOV     DX,3BAH                 ;Ega, Vga unblank.
               IN      AL,DX                   ;Initialize address register.
               ADD     DX,20H
               IN      AL,DX
               SUB     DX,1AH
               MOV     AL,100000B              ;Enable video signal.
               OUT     DX,AL
               JMP     SHORT UNBLANK_END

UNBLANK_CGA:   MOV     AX,40H
               MOV     ES,AX
               MOV     DX,ES:[ADDR_6845]
               ADD     DX,4
               CLI
               MOV     AL,ES:[CRT_MODE_SET]
               OR      AL,VIDEO_SIGNAL         ;Enable video signal.
               OUT     DX,AL
               MOV     ES:[CRT_MODE_SET],AL
               CMP     ES:BYTE PTR ES:[CRT_MODE],3
               JA      UNBLANK_END
               MOV     AL,SAVE_PALETTE
               INC     DX
               OUT     DX,AL                   ;Enable border.
               JMP     SHORT UNBLANK_END

UNBLANK_SOFT:  CALL    RESTORE_SCREEN

UNBLANK_END:   STI
               POP     DX
               POP     ES
               RET

;----------------------------------------------;

BLANK_MODE:    CMP     BLANK_TYPE,SOFTWARE
               JZ      BOUNCE_BALL
               JMP     BLANK

;----------------------------------------------;

SOFT_CODE      LABEL   BYTE

COUNT          DW      ?
PAUSE_CNT      DW      ?

OLD_X          DW      0
X_POS          DW      0
Y_POS          DW      0
DELTA_X        DB      56
DELTA_Y        DB      56
X_DIR          DW      +1
Y_DIR          DW      +1
ERROR_TERM     DB      0
SPIN_DIR       DB      +1

X_MAX          EQU     319 - 7
Y_MAX          EQU     199 - 6
SPIN_MIN       EQU     6
SPIN_MAX       EQU     106

BALL           DB      00000000B, 00000000B, 00000000B, 00000000B
BALL_START     LABEL   BYTE
BALL_WIDTH     EQU     $ - BALL
               DB      00000001B, 01010100B, 00000000B, 00000000B
               DB      00000101B, 01010101B, 00000000B, 00000000B
               DB      00010101B, 01010101B, 01000000B, 00000000B
               DB      00010101B, 01010101B, 01000000B, 00000000B
               DB      00000101B, 01010101B, 00000000B, 00000000B
               DB      00000001B, 01010100B, 00000000B, 00000000B
BALL_END       LABEL   BYTE
BALL_IMAGE     EQU     $ - BALL_START
               DB      00000000B, 00000000B, 00000000B, 00000000B
BALL_LEN       EQU     $ - BALL


BOUNCE_BALL:   STI
               PUSH    ES
               PUSH    BX
               PUSH    CX
               PUSH    DX
               PUSH    SI
               PUSH    DI

               MOV     AX,CS
               MOV     ES,AX

; Bresenham's line drawing algorithm.

               MOV     BX,X_POS
               MOV     DX,Y_POS
               MOV     CL,ERROR_TERM
               MOV     AH,DELTA_X
               MOV     AL,DELTA_Y
               CMP     AH,AL
               JB      STEEP

               ADD     BX,X_DIR
               JNS     CK_X_MAX
               INC     BX
               INC     BX
               JMP     SHORT NEG_X_DIR
CK_X_MAX:      CMP     BX,X_MAX
               JB      STORE_X
               DEC     BX
               DEC     BX
NEG_X_DIR:     NEG     X_DIR
               CALL    SPIN
STORE_X:       MOV     X_POS,BX

               ADD     CL,AL
               MOV     CH,AH
               SHR     CH,1
               CMP     CL,CH
               JBE     SAVE_ERROR
               SUB     CL,AH

               ADD     DX,Y_DIR
               JNS     CK_Y_MAX
               INC     DX
               INC     DX
               JMP     SHORT NEG_Y_DIR
CK_Y_MAX:      CMP     DX,Y_MAX
               JB      STORE_Y
               DEC     DX
               DEC     DX
NEG_Y_DIR:     NEG     Y_DIR
               CALL    SPIN
STORE_Y:       MOV     Y_POS,DX

SAVE_ERROR:    MOV     ERROR_TERM,CL
               JMP     SHORT CALC_POS

;----------------;

STEEP:         ADD     DX,Y_DIR
               JNS     CK_Y_MAX2
               INC     DX
               INC     DX
               JMP     SHORT NEG_Y_DIR2
CK_Y_MAX2:     CMP     DX,Y_MAX
               JB      STORE_Y2
               DEC     DX
               DEC     DX
NEG_Y_DIR2:    NEG     Y_DIR
               CALL    SPIN

STORE_Y2:      MOV     Y_POS,DX

               ADD     CL,AH
               MOV     CH,AL
               SHR     CH,1
               CMP     CL,CH
               JBE     SAVE_ERROR2
               SUB     CL,AL

               ADD     BX,X_DIR
               JNS     CK_X_MAX2
               INC     BX
               INC     BX
               JMP     SHORT NEG_X_DIR2
CK_X_MAX2:     CMP     BX,X_MAX
               JB      STORE_X2
               DEC     BX
               DEC     BX
NEG_X_DIR2:    NEG     X_DIR
               CALL    SPIN
STORE_X2:      MOV     X_POS,BX

SAVE_ERROR2:   MOV     ERROR_TERM,CL

;----------------;

CALC_POS:      XOR     DI,DI
               MOV     CX,DX
               SHR     CX,1
               JCXZ    DO_Y
NEXT_X:        ADD     DI,80                   ;Row address.
               LOOP    NEXT_X

DO_Y:          MOV     AX,BX
               AND     AX,NOT 3
               SHR     AX,1
               SHR     AX,1
               ADD     AX,DI                   ;Column address.

               CMP     BX,OLD_X                ;Did column move?
               MOV     OLD_X,BX
               JZ      MOVE_BALL
               CMP     X_DIR,+1                ;Direction of move.
               JNZ     REVERSE
               MOV     SI,OFFSET BALL_START
               MOV     BX,BALL_IMAGE - BALL_WIDTH

NEXT_ROTATE:   SHR     BYTE PTR [SI+BX],1      ;Shift two pixels right.
               RCR     BYTE PTR [SI+BX+1],1
               RCR     BYTE PTR [SI+BX+2],1
               RCR     BYTE PTR [SI+BX+3],1
               SHR     BYTE PTR [SI+BX],1
               RCR     BYTE PTR [SI+BX+1],1
               RCR     BYTE PTR [SI+BX+2],1
               RCR     BYTE PTR [SI+BX+3],1
               SUB     BX,BALL_WIDTH
               JNC     NEXT_ROTATE

               TEST    X_POS,3                 ;MOD 4 move pixels back a word.
               JNZ     MOVE_BALL
               MOV     DI,SI
               INC     SI
               MOV     CX,BALL_IMAGE
               REP     MOVSB
               JMP     SHORT MOVE_BALL

REVERSE:       AND     BX,3                    ;MOD 4, copy first.
               CMP     BX,3
               JNZ     SHIFT_REVERSE
               MOV     DI,OFFSET BALL_END - 1
               MOV     SI,DI
               DEC     SI
               STD
               MOV     CX,BALL_IMAGE
               REP     MOVSB
               CLD

SHIFT_REVERSE: MOV     SI,OFFSET BALL_START
               MOV     BX,BALL_IMAGE - BALL_WIDTH
NEXT_REVERSE:  SHL     BYTE PTR [SI+BX+3],1
               RCL     BYTE PTR [SI+BX+2],1    ;Shift two pixels left.
               RCL     BYTE PTR [SI+BX+1],1
               RCL     BYTE PTR [SI+BX],1
               SHL     BYTE PTR [SI+BX+3],1
               RCL     BYTE PTR [SI+BX+2],1
               RCL     BYTE PTR [SI+BX+1],1
               RCL     BYTE PTR [SI+BX],1
               SUB     BX,BALL_WIDTH
               JNC     NEXT_REVERSE

MOVE_BALL:     MOV     BX,AX                   ;Screen offset.
               MOV     SI,OFFSET BALL
               MOV     AX,0B800H               ;Screen segment.
               MOV     ES,AX
               MOV     DI,BX
               TEST    DX,1                    ;Odd?
               JZ      INTERLACE
               ADD     DI,2000H
INTERLACE:     MOV     CX,BALL_LEN / BALL_WIDTH / 2
NEXT_LACE:     MOVSW
               MOVSW
               ADD     SI,BALL_WIDTH
               ADD     DI,80 - BALL_WIDTH
               LOOP    NEXT_LACE

               MOV     SI,OFFSET BALL + BALL_WIDTH
               MOV     DI,BX
               ADD     DI,2000H
               TEST    DX,1
               JZ      INTERLACE2
               SUB     DI,2000H - 80
INTERLACE2:    MOV     CX,BALL_LEN / BALL_WIDTH / 2
NEXT_LACE2:    MOVSW
               MOVSW
               ADD     SI,BALL_WIDTH
               ADD     DI,80 - BALL_WIDTH
               LOOP    NEXT_LACE2

               POP     DI
               POP     SI
               POP     DX
               POP     CX
               POP     BX
               POP     ES  

               MOV     AX,PAUSE_CNT            ;Reset pause.
               MOV     COUNT,AX
NEXT_PAUSE:    CMP     UNBLANK_IT,ON           ;If unblank request, exit.
               JZ      BLANK_MODE_END
               DEC     COUNT
               JNZ     NEXT_PAUSE
               JMP     BOUNCE_BALL
BLANK_MODE_END:RET

;----------------------------------------------;

SPIN:          MOV     CH,AH                   ;Delta x - delta y.
               SUB     CH,AL

               ADD     AH,SPIN_DIR             ;Change delta x and y.
               SUB     AL,SPIN_DIR
               CMP     AH,SPIN_MIN             ;Check bounds.
               JA      CK_SPIN
               INC     AH
               INC     AH
               DEC     AL
               DEC     AL
               JMP     SHORT NEG_SPIN
CK_SPIN:       CMP     AH,SPIN_MAX
               JB      STORE_SPIN
               DEC     AH
               DEC     AH
               INC     AL
               INC     AL
NEG_SPIN:      NEG     SPIN_DIR                ;Reverse direction.

STORE_SPIN:    MOV     DELTA_X,AH
               MOV     DELTA_Y,AL
               PUSH    AX
               SUB     AH,AL                   ;Get difference and adjust
               SUB     CH,AH
               ADD     CL,CH                   ; error term.
               POP     AX
               RET

;----------------------------------------------;

SAVE_SCREEN:   PUSH    DS
               PUSH    SI
               PUSH    DI
               XOR     BH,BH
               MOV     AH,3                    ;Get cursor mode and save.
               INT     10H
               MOV     CURSOR_MODE,CX
               CALL    GET_CUR_ADDR
               CALL    VIDEO_SETUP
               MOV     AL,ES:[CRT_MODE]        ;Save video mode.
               MOV     SAVE_MODE,AL
               MOV     AX,ES:[CURSOR_POSN]     ;Cursor position.
               MOV     CURSOR_POS,AX
               MOV     DS,CX                   ;Video segment.
               PUSH    CS
               POP     ES
               XOR     SI,SI
               MOV     DI,OFFSET VIDEO_BUFFER
               CALL    DO_SCREEN               ;Save screen contents.
               MOV     AX,4                    ;CGA graphics video mode.
               INT     10H
               POP     DI
               POP     SI
               POP     DS
               RET

;----------------------------------------------;

RESTORE_SCREEN:PUSH    SI
               PUSH    DI
               PUSH    BX
               PUSH    CX

               MOV     AL,SAVE_MODE            ;Restore video mode.
               XOR     AH,AH
               INT     10H
               XOR     BH,BH                   ;Cursor position.
               MOV     DX,CURSOR_POS
               MOV     AH,2
               INT     10H
               MOV     CX,CURSOR_MODE          ;Cursor type.
               MOV     AH,1
               INT     10H

               MOV     AX,40H
               MOV     ES,AX
               MOV     DX,ES:[ADDR_6845]       ;Recover CRTC base address
               MOV     CX,CURSOR_ADDR          ;Also, program registers
               MOV     AL,14                   ; to cursor address for 1-2-3.
               OUT     DX,AL
               INC     DX
               MOV     AL,CH
               OUT     DX,AL
               DEC     DX
               MOV     AL,15
               OUT     DX,AL
               INC     DX
               MOV     AL,CL
               OUT     DX,AL

               CALL    VIDEO_SETUP
               MOV     ES,CX
               MOV     SI,OFFSET VIDEO_BUFFER
               XOR     DI,DI
               CALL    DO_SCREEN               ;Restore screen contents.

               POP     CX
               POP     BX
               POP     DI
               POP     SI
               RET

;----------------------------------------------;

DO_SCREEN:     MOV     CX,80 * 25

HORZ_RET:      IN      AL,DX                   ;Get status.
               RCR     AL,1                    ;Is it low?
               JC      HORZ_RET                ;If not, wait until it is.
               CLI                             ;No more interrupts.

HWAIT:         IN      AL,DX                   ;Get status.
               RCR     AL,1                    ;Is it high?
               JNC     HWAIT                   ;If no, wait until it is.

               MOVSW
               STI                             ;Interrupts back on.
               LOOP    HORZ_RET
               RET                             ;Return

;----------------------------------------------;

GET_CUR_ADDR:  MOV     AX,40H
               MOV     ES,AX
               MOV     DX,ES:[ADDR_6845]
               MOV     AL,14
               CLI
               OUT     DX,AL
               INC     DX
               IN      AL,DX
               MOV     AH,AL
               DEC     DX
               MOV     AL,15
               OUT     DX,AL
               INC     DX
               IN      AL,DX
               STI
               MOV     CURSOR_ADDR,AX          ;Cursor address.
               RET

;----------------------------------------------;
; OUTPUT: DX = Status register; CX = Video segment.  ES -> BIOS data area.

VIDEO_SETUP:   MOV     AX,40H
               MOV     ES,AX
               MOV     DX,ES:[ADDR_6845]
               ADD     DX,6
               MOV     CX,0B000H
               CMP     DL,0BAH                 ;Mono?
               JZ      SETUP_END
               ADD     CH,8
SETUP_END:     RET

;**********************************************;
;              End of resident code.           ;
;**********************************************;

VIDEO_SIZE     EQU     80 * 2 * 25
VIDEO_BUFFER   LABEL   BYTE
SOFT_STUFF     EQU     $ - SOFT_CODE + VIDEO_SIZE

RESIDENT_END   DW      SOFT_CODE

SYNTAX DB  "Syntax: Conceal [/P password] [filespec] [options]",CR,LF
       DB  "/S = Conceal filespec attribute [ON | OFF]; default = ON",CR,LF
       DB  "/F = File security toggle [ON | OFF]; default is ON",CR,LF
       DB  "/A = Attribute [+|-A] [+|-S] [+|-H] [+|-R]",CR,LF
       DB  "/N = New password",CR,LF
       DB  "/B = Hardware blank [nn][ON | OFF]",CR,LF
       DB  "/G = Graphic blank  [nn][ON | OFF]",CR,LF
       DB  "     nn = 0 - 60 minutes; 0 = OFF; default = 3 minutes, ON",CR,LF
       DB  "/O = Over-graphics blanking [ON | OFF]; default = ON",CR,LF
       DB  "/T = Password required to unblank [ON | OFF]; default = ON",CR,LF
       DB  "/H = Hotkey for immediate blank; Ctrl or Alt plus new hotkey",CR,LF
       DB  "     eg. /H Ctrl Y or /H Alt 1;  default is Alt Z",CR,LF
       DB  "/U = Uninstall",CR,LF,LF,"$"

NOT_INSTALLED  DB      "Conceal not installed",CR,LF,"$"
UNLOAD_MSG     DB      "Conceal can't be uninstalled",CR,LF
               DB      "Uninstall resident programs in reverse order",CR,LF
               DB      "Conceal and blank mode has been turn off.",CR,LF,"$"
NOT_ENOUGH     DB      "Not enough memory to install Conceal",CR,LF,"$"
ALLOCATE_MSG   DB      "Memory allocation error",CR,LF,BELL,"$"
INSTALL_MSG    DB      CR,LF,"Conceal installed",CR,LF,"$"
UNINSTALL_MSG  DB      CR,LF,"Conceal uninstalled",CR,LF,"$"

INVALID_MSG    DB      "Invalid syntax",CR,LF,"$"
PASS_MSG       DB      "Invalid password; Access denied",BELL,CR,LF,"$"
BUFFER_MSG     DB      "Can't allocate memory for Graphics blank once installed.",CR,LF
               DB      "/U(ninstall) and then reinstall with /G(raphics).",CR,LF,"$"
PERM_MSG       DB      "Make new password permanent?  Y/N $"

HOTKEY_MSG     DB      "Press $"
HOTKEY_MSG2    DB      " to immediately blank screen."
CR_LF          DB      CR,LF,"$"

WRONG_VERSION  DB      "Needs DOS 2.0 or later$"

CONCEAL_COM    DB      "CONCEAL.COM",0
CONCEAL_LEN    EQU     $ - CONCEAL_COM
NOT_FOUND      DB      "Could not find Conceal.com",CR,LF
               DB      "Conceal.com must be in current directory",CR,LF
               DB      "or in DOS PATH= directory.",CR,LF,"$"
PATH           DB      "PATH="
PATH_LEN       EQU     $ - PATH
WORKSPACE      DB      65 DUP (?)
ATTR_DTA       DB      SIZE MATCHING DUP (?)

PATH_END       DW      ?

NO_FILE        DB      "File not found",CR,LF,"$"

SCAN_CODES LABEL BYTE

DB "1",2,"2",3,"3",4,"4",5,"5",6,"6",7,"7",8,"8",9,"9",0AH,"0",0BH,"-",0CH
DB "=",0DH,"Q",10H,"W",11H,"E",12H,"R",13H,"T",14H,"Y",15H,"U",16H,"I",17H
DB "O",18H,"P",19H,"[",1AH,"]",1BH,"A",1EH,"S",1FH,"D",20H,"F",21H,"G",22H
DB "H",23H,"J",24H,"K",25H,"L",26H,";",27H,39,28H,96,29H,"\",2BH,"Z",2CH
DB "X",2DH,"C",2EH,"V",2FH,"B",30H,"N",31H,"M",32H,",",33H,".",34H,"/",35H

SCAN_COUNT     EQU     ($ - SCAN_CODES) / 2

TSR_SEGMENT    DW      ?
SYNTAX_FLAG    DB      0                       ; = 1 if syntax to be displayed.

INSTALL_FLAG   DB      0                       ; = 1 if should install.
PASS_FLAG      DB      0                       ; = 1 if correct password.
COM_FILESPEC   DW      0                       ; non-zero if filespec found.

AND_BITS       DB      ?                       ;Attributes.
OR_BITS        DB      ?
CONCEALED      DB      TAB,"Concealed",CR,LF,"$"
UNCONCEALED    DB      TAB,"Unconcealed",CR,LF,"$"

ALT            DB      " Alt $"
CTRL           DB      "Ctrl $"
ALT_CAPS       DB      "ALT"
CTRL_CAPS      DB      "CTRL"

SPACES         DB      9 DUP (SPACE), "$"
ATTR_NAMES     DB      "Archive  $System   $Hidden   $Read-Only$"

OPTIONS        DB      "PSFANBGOTHU"
OPTIONS_LEN    EQU     $ - OPTIONS
OPTIONS_CALLS  DW      PASS, CONCEAL, FILE_CONCEAL, ATTR, NEW_PASS, HARD, SOFT
               DW      DONT_BLANK, CONCEAL_BLANK, CHANGE_HOT, UNINSTALL
OPTIONS_END    EQU     $ - 2

;----------------------------------------------;

INITIALIZE     PROC    NEAR
               CLD                             ;All string operations forward.

               MOV     DX,OFFSET ATTR_DTA      ;Set up DTA.
               MOV     AH,1AH
               INT     21H

               MOV     BX,OFFSET SIGNATURE     ;Point to start of code.
               NOT     BYTE PTR [BX]           ;Change a byte so no false match.
               MOV     AX,CS                   ;Store our segment in AX.
               MOV     DX,0A000H - 1
NEXT_PARAG:    INC     DX                      ;Next paragraph.
               MOV     ES,DX
               CMP     DX,AX                   ;Is it our segment?
               JZ      ANNOUNCE                ;If yes, search is done.
               MOV     SI,BX                   ;Else, point to our signature.
               MOV     DI,BX                   ; and offset of possible match.
               MOV     CX,16                   ;Check 16 bytes for match.
               REP     CMPSB
               JNZ     NEXT_PARAG              ;If no match, keep looking.

;----------------------------------------------;

ANNOUNCE:      MOV     TSR_SEGMENT,ES
               MOV     DX,OFFSET SIGNATURE + 1 ;Display our signature.
               CALL    PRINT_STRING

               MOV     SI,81H                  ;Point to command line.
NEXT_CAP:      LODSB                           ;Capitalize parameters.
               CMP     AL,CR
               JZ      PARSE
               CMP     AL,"a"
               JB      NEXT_CAP
               CMP     AL,"z"
               JA      NEXT_CAP
               AND     BYTE PTR [SI - 1],5FH
               JMP     SHORT NEXT_CAP

;----------------------------------------------;

PARSE:         MOV     SI,81H                  ;Point to command line again.
NEXT_PARSE:    CALL    FIND_START
               LODSB
               CMP     AL,CR
               JZ      CK_INSTALL
               CMP     AL,"/"
               JNZ     FOUND_FILESPEC
               MOV     SYNTAX_FLAG,1           ;Parameter found; no need to
               LODSB                           ; display syntax, unless error
               PUSH    CS                      ; found.
               POP     ES
               MOV     DI,OFFSET OPTIONS
               MOV     CX,OPTIONS_LEN          ;Command line options.
               REPNZ   SCASB
               MOV     DX,OFFSET INVALID_MSG   ;Exit if invalid.
               JNZ     MSG_EXIT
               CALL    FIND_START
               SHL     CX,1
               MOV     DI,OFFSET OPTIONS_END
               SUB     DI,CX
               CALL    [DI]                    ;Process option.
               JMP     NEXT_PARSE

FOUND_FILESPEC:DEC     SI                      ;If not "/", then gotta be
               MOV     COM_FILESPEC,SI         ; filespec.
               CALL    FIND_END
               JMP     NEXT_PARSE

;-------------------------------------------------------------------;
; Exit.  Return ERRORLEVEL code 0 if successful, 1 if unsuccessful. ;
;-------------------------------------------------------------------;

MSG_EXIT:      PUSH    DX
               MOV     DX,OFFSET SYNTAX        ;Display syntax if error.
               CALL    PRINT_STRING
               POP     DX                      ;Display error.
               CALL    PRINT_STRING
ERROR_EXIT:    MOV     AL,1                    ;ERRORLEVEL = 1.
               JMP     SHORT TERMINATE

GOOD_EXIT:     CMP     SYNTAX_FLAG,1
               JZ      DO_HOT
               MOV     DX,OFFSET SYNTAX        ;And syntax.
               CALL    PRINT_STRING
DO_HOT:        CALL    DISP_HOTKEY             ;Display hot key if blank active.
               XOR     AL,AL                   ;ERRORLEVEL = 0.
TERMINATE:     MOV     AH,4CH                  ;Terminate.
               INT     21H

;----------------------------------------------;

CK_INSTALL:    CMP     INSTALL_FLAG,1          ;Function requiring installation
               JNZ     GOOD_EXIT               ; selected?
               CALL    CK_INSTALLED            ;Are we already installed?
               JNZ     GOOD_EXIT

;----------------------------------------------;

CK_AVAILABLE:  PUSH    CS
               POP     ES
               MOV     AH,49H
               INT     21H

               MOV     BX,0FFFFH
               MOV     AH,4AH
               INT     21H

               MOV     AX,RESIDENT_END
               ADD     AX,15                   ;Round up.
               MOV     CL,4
               SHR     AX,CL                   ;Convert to paragraphs.
               CMP     BX,AX
               JAE     GET_VERSION

               MOV     DX,OFFSET NOT_ENOUGH    ;Exit if not enough
               JMP     MSG_EXIT                ; with message.

GET_VERSION:   MOV     AH,30H                  ;Get DOS version
               INT     21H
               CMP     AL,2
               JAE     GET_VECTORS
               MOV     DX,OFFSET WRONG_VERSION ;If not DOS 2 or above, exit.
               JMP     MSG_EXIT

GET_VECTORS:   MOV     AX,3508H                ;INT 8
               INT     21H
               MOV     OLD8[0],BX
               MOV     OLD8[2],ES
               MOV     DX,OFFSET TIMER         ;Install new interrupt.
               MOV     AX,2508H
               INT     21H

               CALL    SETUP_TIME              ;Pause count for software blank.

               MOV     AX,3521H                ;Get INT 21 interrupt.
               INT     21H
               MOV     OLD21[0],BX             ;Save old interrupt.
               MOV     OLD21[2],ES
               MOV     DX,OFFSET DOS           ;Install new interrupt.
               MOV     AX,2521H
               INT     21H

               MOV     AX,3509H                ;Get INT 9 vector.
               INT     21H
               MOV     OLD9[0],BX              ;Save old interrupt.
               MOV     OLD9[2],ES
               MOV     DX,OFFSET KEYBOARD      ;Install new interrupt.
               MOV     AX,2509H
               INT     21H

               MOV     AX,DS:[2CH]             ;Get environment segment.
               MOV     ES,AX
               MOV     AH,49H                  ;Free up environment.
               INT     21H

               MOV     DX,OFFSET INSTALL_MSG   ;Display install message.
               CALL    PRINT_STRING
               CALL    DISP_HOTKEY

               MOV     DX,RESIDENT_END
               ADD     DX,15                   ;Round up.
               MOV     CL,4
               SHR     DX,CL                   ;Convert to paragraphs.
               MOV     AX,3100H                ;Return error code of zero.
               INT     21H                     ;Terminate but stay resident.

;----------------------------------------------;

SETUP_TIME:    PUSH    WORD PTR BLANK_FLAG
               MOV     BLANK_FLAG,ON
               XOR     CX,CX
               MOV     AX,COUNTER
GET_START:     MOV     BX,COUNTER              ;Wait till start of a count.
               CMP     BX,AX
               JZ      GET_START
GET_TIME:      INC     CX                      ;Get loops for one tick.
               MOV     AX,COUNTER
               CMP     AX,BX
               JZ      GET_TIME
               SHR     CX,1                    ;Adjust for delay loop.
               SHR     CX,1
               SHR     CX,1
               SHR     CX,1
               MOV     COUNT,CX
               MOV     PAUSE_CNT,CX
               POP     WORD PTR BLANK_FLAG
               RET

;----------------------------------------------;

PASS:          MOV     DX,SI
               MOV     ES,TSR_SEGMENT
               MOV     AH,PASS_CODE
               MOV     CX,ES:PASS_CNT
NEXT_PASS:     LODSB                           ;Get password.
               CMP     AL,SPACE
               JBE     DECODE2
               CMP     AL,"/"
               JZ      DECODE2
               XOR     [SI-1],AH
               INC     AH
               LOOP    NEXT_PASS
               JMP     SHORT DECODE3

DECODE2:       DEC     SI
DECODE3:       PUSH    SI
               MOV     SI,DX
               MOV     DI,OFFSET PASSWORD
               MOV     CX,ES:PASS_CNT
               REPZ    CMPSB                   ;Does it match?
               JNZ     BAD_PASS
               LODSB
               CMP     AL,SPACE
               JBE     PASS_END
               CMP     AL,"/"
               JZ      PASS_END
BAD_PASS:      MOV     DX,OFFSET PASS_MSG
               JMP     MSG_EXIT

PASS_END:      MOV     PASS_FLAG,1             ;Password valid.
               POP     SI
               RET

;----------------------------------------------;

CONCEAL:       MOV     ES,TSR_SEGMENT
               CALL    CK_ON_OFF               ;Check for ON or OFF.
               MOV     BP,ES:CONCEAL_FLAG
               MOV     ES:CONCEAL_FLAG,OFF     ;OFF so attributes can change.

               MOV     AND_BITS,4              ;System attribute.
               MOV     OR_BITS,0
               CMP     BL,OFF                  ;Turn it off?
               JZ      CONCEAL_PASS            ;If yes, need password.
               MOV     AND_BITS,0
               MOV     OR_BITS,4               ;System attribute ON.
               MOV     BP,BX
               MOV     INSTALL_FLAG,1          ;Need to install.
               JMP     SHORT CK_FILESPEC

CONCEAL_PASS:  CALL    CK_PASSWORD

CK_FILESPEC:   MOV     DX,COM_FILESPEC         ;Filespec?
               OR      DX,DX
               JZ      SAVE_CONCEAL            ;If no, just set security state.

               PUSH    SI
               MOV     CHANGE_FLAG,ON          ;Make a change to attributes.
               MOV     STATS_FLAG,CONCEAL_STATS ;Display secure message.
               CALL    ATTRIBUTES

CONCEAL_END:   MOV     ES,TSR_SEGMENT
               MOV     ES:CONCEAL_FLAG,BP      ;Restore security state.
               POP     SI
               RET

;----------------------------------------------;

FILE_CONCEAL:  MOV     ES,TSR_SEGMENT
               CALL    CK_ON_OFF
               CMP     BL,ON
               JZ      SAVE_CONCEAL
               CALL    CK_PASSWORD

SAVE_CONCEAL:  MOV     ES,TSR_SEGMENT
               MOV     ES:CONCEAL_FLAG,BX      ;New security state; ON or OFF.
               CMP     BL,ON
               JNZ     FILE_END
               MOV     INSTALL_FLAG,1          ;Install if ON.
FILE_END:      RET

;----------------------------------------------;

ATTRIBUTES:    PUSH    CS
               POP     ES
               MOV     DI,OFFSET WORKSPACE     ;Find delimiters in filespec.
               MOV     PATH_END,DI
               MOV     DX,COM_FILESPEC
               MOV     SI,DX
NEXT_ASCII:    LODSB
               STOSB
               CMP     AL,SPACE
               JBE     ASCII_END
               CMP     AL,"/"
               JZ      ASCII_END
               CMP     AL,":"
               JZ      FOUND_DELIMIT
               CMP     AL,"\"
               JNZ     NEXT_ASCII
FOUND_DELIMIT: MOV     PATH_END,DI
               JMP     NEXT_ASCII
ASCII_END:     MOV     BYTE PTR [SI - 1],0

               NOT     AND_BITS                ;Flip NOT bits.

               MOV     CX,7H
               MOV     AH,4EH                  ;Find first.
               INT     21H
               JNC     DISPLAY_NAME
               MOV     DX,OFFSET NO_FILE       ;Exit if none.
               JMP     MSG_EXIT

;-----------------------;

DISPLAY_NAME:  MOV     SI,OFFSET ATTR_DTA.FILE_NAME
               MOV     DI,PATH_END             ;Add filename to path, if any.
               MOV     CX,14
NEXT_NAME2:    LODSB
               STOSB
               OR      AL,AL
               JZ      PAD_NAME
               CALL    PRINT_CHAR              ;Print name.
               LOOP    NEXT_NAME2

PAD_NAME:      MOV     AL,SPACE                ;Tab over 14.
               CALL    PRINT_CHAR
               LOOP    PAD_NAME

               MOV     DX,OFFSET WORKSPACE
               CMP     CHANGE_FLAG,ON          ;Change attributes?
               JNZ     DO_STATS

CHANGE_ATTR:   MOV     CL,[ATTR_DTA.ATTRIBUTE]
               AND     CL,AND_BITS             ;Turn off some bits.
               OR      CL,OR_BITS              ;Turn on some bits.
               XOR     CH,CH
               MOV     AX,4301H                ;Change File Mode.
               INT     21H
               JC      ATTRS_END

DO_STATS:      CALL    DISPLAY_STATS           ;Display status message.
               MOV     AH,4FH                  ;Find next.
               INT     21H
               JNC     DISPLAY_NAME            ;Display it.
ATTRS_END:     RET

;----------------------------------------------;

DISPLAY_STATS: CMP     STATS_FLAG,CONCEAL_STATS ;Secure stats or attribute stats?
               JZ      STATS2

STATS1:        MOV     AX,4300H                ;Get attribute.
               INT     21H
               MOV     AL,TAB
               CALL    PRINT_CHAR

               MOV     BL,CL
               SHL     BL,1
               SHL     BL,1
               MOV     CX,4                    ;Four attributes.
               XOR     DI,DI

NEXT_STAT:     CMP     CX,3                    ;Skip volume and directory bits.
               JNZ     GET_BIT
               SHL     BL,1
               SHL     BL,1
GET_BIT:       MOV     DX,OFFSET SPACES        ;Assume it's off.
               SHL     BL,1
               JNC     DISPLAY_BIT
               MOV     DX,OFFSET ATTR_NAMES    ;Else, display the one that's on.
               ADD     DX,DI
DISPLAY_BIT:   CALL    PRINT_STRING
               ADD     DI,10
               LOOP    NEXT_STAT
               MOV     DX,OFFSET CR_LF         ;New line.
               CALL    PRINT_STRING
               JMP     SHORT STATS_END

STATS2:        MOV     DX,OFFSET CONCEALED      ;Display "Secured" or "Unsecured"
               CMP     OR_BITS,4
               JZ      DISP_STATS
               MOV     DX,OFFSET UNCONCEALED
DISP_STATS:    CALL    PRINT_STRING

STATS_END:     RET

;----------------------------------------------;
PLUS           EQU     0
MINUS          EQU     1
SIGN_FLAG      DB      ?
FLAGS          DB      "ASHR"
FLAGS_LEN      EQU     $ - FLAGS
FLAGS_BITS     DB      20H, 4, 2, 1
FLAGS_END      EQU     $ - 1

ATTR:          MOV     CHANGE_FLAG,OFF         ;Assume no changes.
               MOV     SIGN_FLAG,PLUS          ;Default is plus.
               MOV     AND_BITS,0
               MOV     OR_BITS,0

NEXT_ATTR:     CALL    FIND_START
               LODSB
               CMP     AL,CR
               JBE     GOT_ATTR
               CMP     AL,"/"
               JZ      GOT_ATTR
               CMP     AL,"+"
               JNZ     CK_MINUS
               MOV     SIGN_FLAG,PLUS
               JMP     NEXT_ATTR

CK_MINUS:      CMP     AL,"-"
               JNZ     CK_FLAGS
               MOV     SIGN_FLAG,MINUS
               JMP     NEXT_ATTR

CK_FLAGS:      MOV     DI,OFFSET FLAGS
               MOV     CX,FLAGS_LEN
               REPNZ   SCASB
               JZ      GOT_FLAG
               MOV     DX,OFFSET INVALID_MSG
               JMP     MSG_EXIT

GOT_FLAG:      MOV     CHANGE_FLAG,ON          ;Change attribute.
               MOV     BX,OFFSET FLAGS_END
               SUB     BX,CX
               MOV     AL,[BX]

               MOV     DI,OFFSET OR_BITS       ;Assume plus.
               CMP     SIGN_FLAG,PLUS
               JZ      STORE_BIT
               MOV     DI,OFFSET AND_BITS
STORE_BIT:     OR      [DI],AL
               JMP     NEXT_ATTR

GOT_ATTR:      DEC     SI
               PUSH    SI
               MOV     ES,TSR_SEGMENT
               PUSH    ES:CONCEAL_FLAG

               CMP     CHANGE_FLAG,ON          ;If no changes, don't need
               JNZ     ATTR_READY              ; password.
               CMP     ES:CONCEAL_FLAG,ON      ;Else, if security ON, then
               JNZ     ATTR_READY              ; need password to change
               CALL    CK_PASSWORD             ; attributes.
               MOV     ES:CONCEAL_FLAG,OFF     ;Turn off security temporarily.

ATTR_READY:    MOV     STATS_FLAG,ATTR_STATS   ;Display attribute messages.
               CALL    ATTRIBUTES

               MOV     ES,TSR_SEGMENT
               POP     ES:CONCEAL_FLAG         ;Restore security status.
               POP     SI                      ;Command line pointer.
               RET

;----------------------------------------------;

NEW_PASS:      CALL    CK_PASSWORD             ;Need password to change password
               MOV     ES,TSR_SEGMENT
               PUSH    ES:CONCEAL_FLAG
               MOV     ES:CONCEAL_FLAG,OFF     ;Security temporarily OFF.

               CMP     BYTE PTR [SI],SPACE     ;Is there a password?
               JA      GOT_PASS
               JMP     NEW_END
GOT_PASS:      MOV     AH,PASS_CODE
               MOV     BP,SI                   ;Start of password.
               XOR     CX,CX
NEXT_PASS2:    LODSB                           ;Get password.
               CMP     AL,SPACE
               JBE     STORE_PASS
               CMP     AL,"/"
               JZ      STORE_PASS
               XOR     BYTE PTR [SI - 1],AH
               INC     AH
               INC     CX
               JMP     NEXT_PASS2

STORE_PASS:    DEC     SI
               PUSH    SI
               MOV     ES:PASS_CNT,CX
               MOV     SI,BP
               MOV     DI,OFFSET PASSWORD      ;Store new password.
               REP     MOVSB

;----------------------------------------------;

               PUSH    CS
               POP     ES
               MOV     DX,OFFSET PERM_MSG      ;Make change permanent?
               CALL    PRINT_STRING
               MOV     AH,1
               INT     21H
               PUSH    AX
               MOV     DX,OFFSET CR_LF
               CALL    PRINT_STRING
               POP     AX
               AND     AL,5FH
               CMP     AL,"Y"
               JZ      GET_VER
               JMP     NEW_DONE

GET_VER:       MOV     AH,30H                  ;Get DOS version.
               INT     21H
               CMP     AL,3                    ;Filename in environment
               JB      TRY_CURRENT             ; not support before DOS 3.x.

               MOV     DS,DS:[2CH]             ;Segment of environment.
               XOR     SI,SI
FIND_ENV_END:  LODSB                           ;Find end of environment.
               OR      AL,AL
               JNZ     FIND_ENV_END
               LODSB
               OR      AL,AL
               JNZ     FIND_ENV_END

               INC     SI                      ;Bump pointer past word count.
               INC     SI
               MOV     DI,OFFSET WORKSPACE     ;Copy our name.
               CALL    COPY_NAME
               PUSH    CS
               POP     DS
               MOV     DX,OFFSET WORKSPACE     ;Try to write answer.
               CALL    WRITE_ANS
               JNC     NEW_DONE

TRY_CURRENT:   MOV     DX,OFFSET CONCEAL_COM   ;Try current directory.
               CALL    WRITE_ANS
               JNC     NEW_DONE

; See if PATH= is in environment.
               MOV     DS,DS:[2CH]             ;Segment of environment.
LOOK_AT_PATH:  MOV     BX,-1                   ;Offset zero in environment.
NEXT_PATH:     INC     BX
               MOV     SI,BX
               CMP     BYTE PTR [SI],0         ;Terminating null?
               JNZ     PATH_SEARCH
               CMP     BYTE PTR [SI+1],0       ;Double null?
               JNZ     NEXT_PATH
               JMP     SHORT CANT_SAVE         ;If so, end of environment.

PATH_SEARCH:   MOV     DI,OFFSET PATH          ;Search for "PATH=".
               MOV     CX,PATH_LEN
               REP     CMPSB
               JNZ     NEXT_PATH

; PATH= found; search for paths.
NEXT_STRING:   CMP     BYTE PTR [SI],0         ;Terminating null?
               JZ      CANT_SAVE
               CMP     BYTE PTR [SI-1],0       ;Double null?
               JZ      CANT_SAVE

MOVE_STRING:   MOV     DI,OFFSET WORKSPACE     ;Get path.
NEXT_BYTE:     LODSB
               OR      AL,AL
               JZ      SEARCH_PATH
               CMP     AL,";"
               JZ      SEARCH_PATH
               STOSB
               JMP     NEXT_BYTE

SEARCH_PATH:   CALL    COPY_PATH               ;Create filespec.
               PUSH    DS
               PUSH    CS
               POP     DS
               MOV     DX,OFFSET WORKSPACE
               CALL    WRITE_ANS               ;Try to write answer.
               POP     DS
               JC      NEXT_STRING             ;If failed, try next path.
               JMP     SHORT NEW_DONE          ;Else, done.

CANT_SAVE:     PUSH    CS
               POP     DS
               MOV     DX,OFFSET NOT_FOUND     ;Answer write error message.
               CALL    PRINT_STRING

NEW_DONE:      POP     SI

NEW_END:       MOV     ES,TSR_SEGMENT
               POP     ES:CONCEAL_FLAG
               RET

;----------------------------------------------;

COPY_NAME:     LODSB
               STOSB
               OR      AL,AL
               JNZ     COPY_NAME
               RET

;----------------------------------------------;

COPY_PATH:     PUSH    SI
               PUSH    DS
               CMP     DI,OFFSET WORKSPACE
               JZ      MOVE_NAME
               CMP     BYTE PTR ES:[DI-1],"\"  ;Filespec delimiter?
               JZ      MOVE_NAME
               CMP     BYTE PTR ES:[DI-1],":"
               JZ      MOVE_NAME
               MOV     AL,"\"                  ;If no, add one.
               STOSB

MOVE_NAME:     PUSH    CS
               POP     DS
               MOV     SI,OFFSET CONCEAL_COM   ;Tack on "CONCEAL.COM".
               MOV     CX,CONCEAL_LEN
               REP     MOVSB

COPY_END:      POP     DS
               POP     SI
               RET

;----------------------------------------------;
; INPUT:  DX -> Compute.com filespec.
; OUTPUT: CY=0 if successful; CY=1 if failed.

WRITE_ANS:     PUSH    SI
               MOV     AX,3D02H                ;Open for reading and writing.
               INT     21H
               JC      WRITE_END
               MOV     BX,AX
               MOV     DX,OFFSET WORKSPACE
               MOV     CX,SIZE WORKSPACE
               MOV     AH,3FH                  ;Read signature.
               INT     21H
               JC      WRITE_END

               MOV     SI,OFFSET SIGNATURE + 1     ;Make sure we found correct
               MOV     DI,OFFSET WORKSPACE - 100H  ; Secure.com.
               ADD     DI,SI
               SUB     CX,OFFSET SIGNATURE + 1 - 100H
               REP     CMPSB
               STC
               JNZ     WRITE_END

               XOR     CX,CX
               MOV     DX,OFFSET PASSWORD - 100H
               MOV     AX,4200H                ;Seek offset of new password.
               INT     21H
               JC      WRITE_END

               PUSH    DS
               MOV     DS,TSR_SEGMENT
               MOV     DX,OFFSET PASSWORD
               MOV     CX,PASSWORD_LEN + SIZE PASS_CNT
               MOV     AH,40H                  ;Write answer to disk.
               INT     21H
               POP     DS
               JC      WRITE_END

               MOV     AH,3EH                  ;Close file.
               INT     21H
WRITE_END:     POP     SI
               RET

;----------------------------------------------;

CK_PASSWORD:   CMP     PASS_FLAG,1             ;Correct password on command
               JZ      CK_PASS_END             ; line?
               MOV     DX,OFFSET PASS_MSG
               JMP     MSG_EXIT
CK_PASS_END:   RET

;----------------------------------------------;

HARD:          MOV     CH,HARD_BLANK
               JMP     SHORT GET_BLANK

;----------------------------------------------;

SOFT:          MOV     CH,SOFT_BLANK

GET_BLANK:     MOV     ES,TSR_SEGMENT
               CMP     BYTE PTR [SI],"0"       ;Time-out parameter?
               JB      CK_HARD
               CMP     BYTE PTR [SI],"9"
               JA      CK_HARD

               LODSB
               SUB     AL,"0"
               MOV     BL,AL
               LODSB
               SUB     AL,"0"
               JC      CK_TIME
               CMP     AL,9
               JA      CK_TIME
               XCHG    BL,AL
               MOV     BH,10
               MUL     BH
               ADD     BL,AL
               INC     SI

CK_TIME:       DEC     SI
               OR      BL,BL                   ;If no or zero time-out, just
               JZ      STORE_HARD              ; store status.
               CMP     BL,60                   ;60 minute max.
               JBE     MAKE_TIME
               MOV     DX,OFFSET INVALID_MSG
               JMP     MSG_EXIT

MAKE_TIME:     MOV     AX,1092                 ;Convert minutes to timer ticks.
               XOR     BH,BH
               MUL     BX
               MOV     ES:TIME_OUT,AX
               MOV     ES:COUNTER,AX


CK_HARD:       CALL    CK_ON_OFF
               CMP     BL,ON
               JZ      DO_ON
STORE_HARD:    MOV     ES:BLANK_FLAG,BL        ;Store blank status.
               JMP     SHORT HARD_END

DO_ON:         CMP     CH,SOFT_BLANK
               JNZ     STORE_ON
               MOV     BUFFER_FLAG,ON            ;Graphics buffer required.
               ADD     RESIDENT_END,SOFT_STUFF
               CALL    CK_INSTALLED
               JZ      STORE_ON
               CMP     ES:BUFFER_FLAG,ON       ;Is graphics buffer installed?
               JZ      STORE_ON
               MOV     DX,OFFSET BUFFER_MSG    ;If no, complain.
               JMP     MSG_EXIT

STORE_ON:      MOV     ES:BLANK_FLAG,CH        ;Store blank status.
               MOV     INSTALL_FLAG,1
HARD_END:      RET

;----------------------------------------------;

DONT_BLANK:    MOV     ES,TSR_SEGMENT
               CALL    CK_ON_OFF
               MOV     ES:GRAPHIC_APPS,BL      ;Store graphics app blank status.
               RET

;----------------------------------------------;

CONCEAL_BLANK: MOV     ES,TSR_SEGMENT          ;Password unblanking status.
               CALL    CK_ON_OFF
               CMP     BL,ON
               JZ      STORE_CONCEAL
               CALL    CK_PASSWORD
STORE_CONCEAL: MOV     ES:UNBLANK_FLAG,BL
               RET

;----------------------------------------------;

CHANGE_HOT:    MOV     BP,TSR_SEGMENT          ;Save segment.
               MOV     DX,CS
               CALL    FIND_START              ;Find start of parameter.
               CMP     AL,CR
               JZ      HOT_END
               MOV     BX,SI
               MOV     ES,DX
               MOV     DI,OFFSET ALT_CAPS      ;Is it "ALT"?
               MOV     CX,3
               REP     CMPSB
               JNZ     CK_CTRL

               MOV     ES,BP
               MOV     ES:HOT_SHIFT_KEY,ALT_STATE
               JMP     SHORT GET_HOT

CK_CTRL:       MOV     SI,BX
               MOV     DI,OFFSET CTRL_CAPS     ;Is it "CTRL"?
               MOV     CX,4
               REP     CMPSB
               JZ      GOT_CTRL
               MOV     SI,BX
               JMP     SHORT GET_HOT
GOT_CTRL:      MOV     ES,BP
               MOV     ES:HOT_SHIFT_KEY,CTRL_STATE

GET_HOT:       CALL    FIND_START              ;Find parameter start.
               CMP     AL,CR
               JZ      HOT_END
               LODSB
               MOV     CX,SCAN_COUNT
               MOV     ES,DX
               MOV     DI,OFFSET SCAN_CODES
NEXT_HOT:      SCASB                           ;Look up hotkey.
               JZ      FOUND_HOT
               INC     DI
               LOOP    NEXT_HOT
               JMP     SHORT HOT_END

FOUND_HOT:     MOV     AH,[DI]
               MOV     ES,BP
               MOV     ES:COMBO,AL             ;Store hotkey ASCII
               MOV     ES:HOT_KEY_SCAN,AH      ; and scan code.
HOT_END:       RET

;---------------------------------------------------;
; This subroutine uninstalls the resident TSR.      ;
;---------------------------------------------------;

UNINSTALL:     CALL    CK_INSTALLED            ;Are we installed?
               JNZ     DO_VECTORS
               MOV     DX,OFFSET NOT_INSTALLED
               JMP     MSG_EXIT

DO_VECTORS:    MOV     CX,AX                   ;Save segment in CX.
               MOV     ES,AX
               CMP     ES:UNBLANK_FLAG,ON      ;Password required to unblank?
               JZ      UN_PASS
               CMP     ES:CONCEAL_FLAG,OFF     ;Security ON ?
               JZ      VARS_OFF

UN_PASS:       CALL    CK_PASSWORD

VARS_OFF:      MOV     ES:BLANK_FLAG,OFF       ;Turn OFF in case can't uninstall
               MOV     ES:CONCEAL_FLAG,OFF

               MOV     AX,3508H                ;Get interrupt 8h.
               INT     21H
               CMP     BX,OFFSET TIMER         ;Has it been hooked by another?
               JNZ     UNINSTALL_ERR           ;If yes, exit with error message.
               MOV     BX,ES
               CMP     BX,CX                   ;Is the segment vector same?
               JNZ     UNINSTALL_ERR           ;If no, exit with error message.

               MOV     AX,3521H                ;Get interrupt 21h.
               INT     21H
               CMP     BX,OFFSET DOS           ;Has it been hooked by another?
               JNZ     UNINSTALL_ERR           ;If yes, exit with error message.
               MOV     BX,ES
               CMP     BX,CX                   ;Is the segment vector same?
               JNZ     UNINSTALL_ERR           ;If no, exit with error message.

               MOV     AX,3509H                ;Get interrupt 9h.
               INT     21H
               CMP     BX,OFFSET KEYBOARD      ;Has it been hooked by another?
               JNZ     UNINSTALL_ERR           ;If yes, exit with error message.
               MOV     BX,ES
               CMP     BX,CX                   ;Is the segment vector same?
               JZ      RESTORE_VECTOR          ;If no, exit with error message.

UNINSTALL_ERR: MOV     DX,OFFSET UNLOAD_MSG    ;And exit with error message.
LILLY_ERR:     JMP     MSG_EXIT

RESTORE_VECTOR:MOV     DX,ES:OLD9[0]           ;Restore old INT 9.
               MOV     DS,ES:OLD9[2]
               MOV     AX,2509H
               INT     21H

               MOV     DX,ES:OLD8[0]           ;Restore old INT 8.
               MOV     DS,ES:OLD8[2]
               MOV     AX,2508H
               INT     21H

               MOV     DX,ES:OLD21[0]          ;Restore old INT 21.
               MOV     DS,ES:OLD21[2]
               MOV     AX,2521H
               INT     21H

DEALLOCATE:    MOV     AH,49H                  ;Return memory to system pool.
               INT     21H
               PUSH    CS
               POP     DS
               MOV     DX,OFFSET ALLOCATE_MSG
               JC      LILLY_ERR               ;Display message if problem.

               MOV     DX,OFFSET UNINSTALL_MSG ;Display uninstall message.
               CALL    PRINT_STRING
               JMP     GOOD_EXIT

;----------------------------------------------;
; OUTPUT: BX = ON or OFF.  If neither found, BL = ON.

CK_ON_OFF:     CALL    FIND_START
               PUSH    SI
               MOV     BX,ON                   ;Assume ON.
               LODSB
               CMP     AL,"O"
               JNZ     ON_OFF_DONE
               ADD     SP,2
               LODSB
               CMP     AL,"N"
               JZ      ON_OFF_END
               CMP     AL,"F"
               JNZ     ON_OFF_ERR
               LODSB
               CMP     AL,"F"
               JNZ     ON_OFF_ERR
               MOV     BX,OFF
               JMP     SHORT ON_OFF_END

ON_OFF_DONE:   POP     SI
ON_OFF_END:    RET

ON_OFF_ERR:    MOV     DX,OFFSET INVALID_MSG
               JMP     MSG_EXIT

;-------------------------------------------------------;
; OUTPUT: ZR = 1 if not installed; ZR = 0 if installed. ;

CK_INSTALLED:  MOV     AX,TSR_SEGMENT
               MOV     BX,CS
               CMP     AX,BX                   ;Compare segments.
               RET

;----------------------------------------------;

FIND_START:    LODSB
               CMP     AL,CR
               JZ      START_END
               CMP     AL,SPACE
               JBE     FIND_START
START_END:     DEC     SI
               RET

;----------------------------------------------;

FIND_END:      LODSB
               CMP     AL,CR
               JZ      END_END
               CMP     AL,"/"
               JZ      END_END
               CMP     AL,SPACE
               JA      FIND_END
END_END:       DEC     SI
               RET

;----------------------------------------------;

DISP_HOTKEY:   MOV     ES,TSR_SEGMENT
               CMP     ES:BLANK_FLAG,OFF       ;Blank status active?
               JZ      HOTKEY_END
               PUSH    DS
               MOV     DX,OFFSET HOTKEY_MSG    ;Display hotkey message.
               CALL    PRINT_STRING
               MOV     DS,TSR_SEGMENT
               MOV     BL,COMBO
               MOV     DX,OFFSET ALT
               CMP     HOT_SHIFT_KEY,ALT_STATE
               JZ      DISP_STATE
               MOV     DX,OFFSET CTRL
DISP_STATE:    POP     DS
               CALL    PRINT_STRING            ;And current hotkey.
               MOV     DL,BL
               MOV     AH,2
               INT     21H
               MOV     DX,OFFSET HOTKEY_MSG2
               CALL    PRINT_STRING
HOTKEY_END:    RET

;----------------------------------------------;

PRINT_CHAR:    MOV     DL,AL
               MOV     AH,2                    ;Print character via DOS.
               JMP     SHORT DOS_INT

PRINT_STRING:  MOV     AH,9                    ;Print string via DOS.
DOS_INT:       INT     21H
               RET

INITIALIZE     ENDP

_TEXT          ENDS
               END     START

[ RETURN TO DIRECTORY ]