PAGE    ,80
;
TITLE * START.ASM *
SUBTTL Program to start processes at a predetermined time.
;
;       CREATED 06-DEC-85       :RICHARD B JOHNSON
;
;
; Misc equates.
;
CR              EQU     0DH
LF              EQU     0AH
MS_DOS          EQU     21H             ;SYSTEM
CLK             EQU     1CH             ;TIMER VECTOR
CLX             EQU     08H             ;BEGINNING OF TIMER VECTOR
INT_BASE        EQU     20H             ;INTERRUPT CONTROLLER
VID_ROM         EQU     10H             ;VIDEO ROM
K               EQU     1024
K16             EQU     16 * K
CMD_BUF         EQU     80H             ;COMMAND LINE
RAM             EQU     12H             ;GET AVAILABLE RAM
MAX_HRS         EQU     23
MAX_MIN         EQU     59
MAX_SEC         EQU     MAX_MIN
EQUIP           EQU     410H            ;EQUIPMENT FLAG
BWSEG           EQU     0B000H          ;SEGMENT FOR BLACK/WHITR
COSEG           EQU     0B800H          ;SEGMENT FOR COLOR
;
;
PSEG    SEGMENT
        ASSUME  CS:PSEG,SS:PSEG,DS:PSEG,ES:NOTHING
        ORG     100H
MAIN    PROC    NEAR
        JMP     INIT
        DB      'Copyright (C) 1985 Richard B. Johnson. '
        DB      'All rights reserved. '
PRP0    DB      CR,LF,'Process $'
PRP1    DB      CR,LF,'will start at '
HH      DB      '00:'
MM      DB      '00:'
SX      DB      '00. If not correct, hit any key.... $'
PRP2    DB      ' aborted.',CR,LF,'$'
PRP3    DB      ' loaded.',CR,LF,'$'
PRP4    DB      CR,LF,'Usage:'
        DB      CR,LF,'START HH:MM:SS \PATH\RUNFILE COMMAND STRING'
        DB      CR,LF,LF,'Examples:'
        DB      CR,LF,'START 23:55:00 C:\DOS\BACKUP.BAT TODAYS FILES'
        DB      CR,LF,'START 12:00:00 \SETCLOCK'
        DB      CR,LF,'START 15:00:00 COPY *.DAT E:'
        DB      CR,LF,'$'
PRP5:   DB      CR,LF,'The time entered must be LATER than the present '
        DB      'time!',CR,LF,'$'
;
BEEP    DB      7,'Your process will resume in a moment.'
        DB      CR,LF,'$'
;
ERRW    DB      CR,LF,'Not enough disk space to save the screen'
        DB      CR,LF,'(need 16k).$'
;
FILE1   DB      '\SCREEN.TMP',0
FILEN   EQU     $ - FILE1
FILE2   DB      '           ',0
HANDLE  DW      ?
;
CNT     DB      182                     ;LOCAL CLOCK TICK COUNT
SEC     DB      ?                       ;LOCAL CLOCK SECONDS
MIN     DB      ?                       ;LOCAL CLOCK MINUTES
HRS     DB      ?                       ;LOCAL CLOCK HOURS
SAV_SEC DB      ?                       ;COMMAND STRING SECS (BINARY)
SAV_MIN DB      ?                       ;COMMAND STRING MINS (BINARY)
SAV_HRS DB      ?                       ;COMMAND STRING HRS (BINARY)
SS_SAV  DW      ?                       ;USER'S STACK SEGMENT
SP_SAV  DW      ?                       ;USER'S STACK POINTER
COMMAND DB      '\COMMAND.COM',0
;
BLOCK   DW      0                       ;PARAMETER BLOCK FOR EXEC
        DW      CMD_BUF                 ;DEFALUT BUFFER
BP_1    DW      ?                       ;CS WRITTEN HERE
        DW      5CH                     ;DEFAULT FCB
BP_2    DW      ?                       ;CS WRITTEN HERE
        DW      6CH                     ;OTHER FCB
BP_3    DW      ?                       ;CS WRITTEN HERE
;
DTA_SEG DW      ?                       ;USER'S DTA (SEGMENT)
DTA_OFF DW      ?                       ;USER'S DTA (OFFSET)
SSP_SAV DW      ?                       ;OUR STACK POINTER
SAV_SEG DW      ?                       ;OLD INTERRUPT SEGMENT
SAV_OFF DW      ?                       ;OLD INTERRUPT OFFSET
CUR_SAV DW      ?                       ;CURSOR SAVE
CUR_TYP DW      ?                       ;CURSOR TYPE
CUR_PGE DB      ?                       ;CURRENT PAGE
OLD_CLK LABEL   DWORD                   ;OLD TIMER VECTOR
IP_CLK  DW      ?                       ;IP FOR OLD CLOCK
CS_CLK  DW      ?                       ;CS FOR OLD CLOCK
OLD_CLX LABEL   DWORD                   ;START OF CLOCK VECT
IP_CLX  DW      ?                       ;IP FOR START
CS_CLX  DW      ?                       ;CS FOR START
CRT_FLG LABEL   DWORD                   ;CRITICAL FLAG (DOS)
DOS_FLG DW      ?                       ;CRITICAL FLAG ADDRESS
        DW      ?
SEGMNT  DW      ?                       ;VIDEO SEGMENT
HERE    DB      0                       ;REENTRY FLAG
DISK    DB      ?                       ;CURRENT DISK
DIRZ    DB      '\'
DIRECT  DB      64 DUP (?)              ;CURRENT DIRECTORY
;
INFORM  DB      ' /C '
BUFFER  DB      ' '                     ;THE SPACE
ASC_HRS DB      'HH:'                   ;HOURS IN ASCII
ASC_MIN DB      'MM:'                   ;MINUTES IN ASCII
NEW_CMD EQU     $ - 2                   ;NEW COMMAND LINE
NEW_TXT EQU     NEW_CMD + 1             ;WHERE TEXT STARTS
ASC_SEC DB      'SS'                    ;SECONDS IN ASCII
DELIM   DB      ' '                     ;NORMAL SPACE
        DB      64 DUP (?)              ;REST OF COMMAND LINE
LINE    DB      CR,LF,'Batch '
TIME    DB      '00:00:00 '
DATE    DB      '00-JAN-00 '
LEN     EQU     $ - LINE
LINEBUF DB      64 DUP (0)
MONTH   DB      'JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC'
LOGF    DB      '\BATCH.LOG',0          ;PROCESS LOG FILE
LOGCHAN DW      ?
BYTES   DW      ?
;
EVEN
        DB      32 DUP ('STACK   ')
STK_TOP LABEL   WORD
;
INIT:   MOV     AX,CS
        MOV     WORD PTR [BP_1],AX      ;FOR 'LOAD AND EXECUTE'
        MOV     WORD PTR [BP_2],AX
        MOV     WORD PTR [BP_3],AX
        CALL    SET_TIM                 ;SET PRIVATE TIME
;
; Check for graphics board
;
        PUSH    DS                      ;SAVE DATA SEG
        XOR     AX,AX                   ;ZERO IT
        MOV     DS,AX
        MOV     AL,BYTE PTR DS:[EQUIP]  ;GET EQUIPMENT FLAG
        POP     DS                      ;RESTORE DATA SEGMENT
        AND     AL,00110000B            ;MASK TRASH
        MOV    WORD PTR [SEGMNT],BWSEG  ;ASSUME SEGMENT FOR B/W
        CMP     AL,00110000B            ;IS IT BLACK AND WHITE?
        JZ      SEG_OK                  ;YES
        MOV    WORD PTR [SEGMNT],COSEG  ;MUST BE  COLOR
;
SEG_OK: CALL    PARSE                   ;PARSE COMMAND LINE
        JC      FINIS                   ;BAD COMMAND LINE
        CALL    GET_FLAG                ;SET UP CRITICAL PROCESS FLAG
        CALL    SET_INT                 ;SET UP INTERRUPTS
        CALL    SET_TIM                 ;SET TIME AGAIN
        CALL    KEEP3K
KEEP:   MOV     AX,3100H                ;KEEP PROCESS FUNCTION
        INT     MS_DOS
        HLT                             ;IN CASE SOMETHING'S BUSTED

FINIS:  MOV     AX,4C00H                ;TERMINATE PROCESS
        INT     MS_DOS
MAIN    ENDP
;
CLK_INT PROC    FAR
        ASSUME  CS:PSEG,SS:PSEG,DS:PSEG,ES:NOTHING
        PUSH    AX                      ;SAVE
        PUSH    DS                      ;AND DS
        PUSH    CS                      ;COPY CODE SEGMENT
        POP     DS                      ;INTO DATA SEG
;
; We get interupted 18.2 times per second.
; Routine below produces 1 second 'Ticks' by
; dividing by 18.2 (honest). Subtracts 10 from 182
; each interupt and adds the remainder to 182 for
; the next run through the loop. Result is an AVERAGE
; division by 18.2.

        CMP     BYTE PTR [CNT],0FFH     ;FLAG SHOWS THAT WE TIMED OUT
        JZ      BYPASS                  ;ALREADY TIMED OUT
        SUB     BYTE PTR [CNT],10       ;RUNNING COUNT
        JNC     BYPASS                  ;NO CARRY YET
        ADD     BYTE PTR [CNT],182      ;CUTE EH?
;
; Simple 24 hour clock counter follows.
;
        INC     BYTE PTR [SEC]          ;BUMP SECONDS COUNTER
        CMP     BYTE PTR [SEC],60       ;ARE WE THERE YET?
        JNZ     TIM_OK                  ;NOPE
        MOV     BYTE PTR [SEC],0        ;YES, ZERO SECONDS AND ....
        INC     BYTE PTR [MIN]          ;BUMP MINUTES
        CMP     BYTE PTR [MIN],60       ;ARE WE THERE YET?
        JNZ     TIM_OK                  ;NOPE
        MOV     BYTE PTR [MIN],0        ;YES, ZERO MINUTES AND.... ETC
        INC     BYTE PTR [HRS]
        CMP     BYTE PTR [HRS],24
        JNZ     TIM_OK
        MOV     BYTE PTR [HRS],0
;
; Check new time against saved time from command line.
;
TIM_OK: MOV     AL,BYTE PTR [HRS]       ;GET HOURS
        CMP     AL,BYTE PTR [SAV_HRS]   ;SAME AS SAVED?
        JNZ     BYPASS                  ;NO
        MOV     AL,BYTE PTR [MIN]       ;GET MINUTES
        CMP     AL,BYTE PTR [SAV_MIN]   ;SAME AS SAVED?
        JNZ     BYPASS                  ;NO
        MOV     AL,BYTE PTR [SEC]       ;GET SECONDS
        CMP     AL,BYTE PTR [SAV_SEC]   ;SAME AS SAVED?
        JNZ     BYPASS                  ;NO
        MOV     BYTE PTR [CNT],0FFH     ;SET FLAG (WE ARE TIMED OUT)
BYPASS: POP     DS                      ;OLD DATA SEG
        POP     AX                      ;AND AX
        JMP     DWORD PTR CS:[OLD_CLK]  ;CHAIN TO NEXT (IF ANY)
CLK_INT ENDP
;
; This is the second clock interrupt vector.
;
CLX_INT PROC    FAR
        CMP     BYTE PTR CS:[HERE],0    ;SEE IF REENTRY IS OCCURING
        JNZ     HOME                    ;YES, DON'T ALLOW REENTRY
        CMP     BYTE PTR CS:[CNT],0FFH  ;SEE IF WE TIMED OUT
        JNZ     HOME                    ;NO, NOT YET
;
; Check to see if we interrupted something important.
;
        PUSH    BX
        PUSH    DS
        LDS     BX,DWORD PTR CS:[CRT_FLG]       ;GET CRITICAL FLAG
        CMP     BYTE PTR DS:[BX],0
        POP     DS
        POP     BX
        JZ      TIMOUT                  ;OK TO MAKE DOS CALLS
;
DONE:   MOV     BYTE PTR CS:[HERE],0    ;RESTORE REENTRY FLAG
HOME:   JMP     DWORD PTR CS:[OLD_CLX]  ;CONTINUE
;
; Coast is clear, save stack and continue.
;
TIMOUT: MOV     BYTE PTR CS:[HERE],0FFH ;SET ENTRY FLAG
        PUSH    AX                      ;SAVE USER'S
        PUSH    DS                      ;AND DS
        PUSH    CS                      ;COPY CODE SEGMENT
        POP     DS                      ;INTO DATA SEG
        CLI
        MOV     WORD PTR [SS_SAV],SS    ;SAVE IT
        MOV     WORD PTR [SP_SAV],SP    ;SAVE STACK POINTER
        MOV     AX,CS                   ;GET CODE SEG
        MOV     SS,AX                   ;INTO STACK SEG
        MOV     SP,OFFSET STK_TOP       ;LOCAL STACK
;
        PUSH    BX                      ;SAVE EVERYTHING IN LOCAL STACK
        PUSH    CX
        PUSH    DX
        PUSH    DI
        PUSH    SI
        PUSH    BP
        PUSH    ES
        MOV     ES,AX                   ;CODE SEGMENT INTO EXTRA SEG
;
; Run the program
;
CODE    PROC    NEAR
        CALL    RES_INT                 ;RESTORE OLD INTERRUPT VECTORS
        STI                             ;OK TO INTERRUPT
        CALL    RES_HRD                 ;RESET HARDWARE CONTROLLER
        CALL    SAV_SCR                 ;SAVE THE USER'S SCREEN
        CALL    TELL                    ;TELL USER WHAT WE'RE DOING
        CALL    LOG                     ;LOG TO DISK
        CALL    SAV_DIR                 ;SAVE CURRENT DIRECTORY
        CALL    SAV_DTA                 ;SAVE USERS DATA AREA
        CALL    SET_DTA                 ;SET UP LOCAL DATA AREA
        CALL    GET_MEM                 ;GET MORE MEMORY (dummy)
        JC      NOFREE                  ;NOTHING AVAILABLE
        MOV     ES,AX                   ;NEW SEGMENT ADDRESS OF AVAILABLE MEM
NOFREE: CALL    KEEP3K                  ;RESTORE TO POOL
        CALL    RUN_PG                  ;LOAD/EXECUTE COMMAND.COM
        CALL    RES_DTA                 ;RESTORE USERS DATA TRANSFER AREA
        CALL    RES_DIR                 ;RESTORE CURRENT DIRECTORY
        CALL    RES_SCR                 ;RESTORE USER'S SCREEN
        CALL    KEEP0K                  ;GIVE UP ALL MEMORY
CODE    ENDP
;
; Return to user program.
;
        POP     ES                      ;RESTORE USER'S REGISTERS
        POP     BP                      ;SAVED IN LOCAL STACK
        POP     SI
        POP     DI
        POP     DX
        POP     CX
        POP     BX
;
        CLI
        MOV     SS,WORD PTR [SS_SAV]    ;GET SAVED STACK SEG
        MOV     SP,WORD PTR [SP_SAV]    ;GET OLD STACK POINTER
        POP     DS                      ;RESTORE REGISTERS FROM USER'S STACK
        POP     AX
        STI                             ;ALLOW INTERRUPTS
        JMP     DONE                    ;CONTINUE
CLX_INT         ENDP
;
GET_MEM PROC    NEAR
        MOV     BX,40000                ;ASK FOR EVERYTHING IN THE COMPUTER
        MOV     AH,4AH
        INT     MS_DOS
        RET
GET_MEM ENDP
;
SET_TIM PROC    NEAR
        MOV     AH,2CH                  ;SET PRIVATE TIME
        INT     MS_DOS
        MOV     BYTE PTR [HRS],CH       ;SAVE TIME IN PRIVATE CLOCK
        MOV     BYTE PTR [MIN],CL
        MOV     BYTE PTR [SEC],DH
        RET
SET_TIM ENDP
;
SET_INT PROC    NEAR
        CLI
        CLD                             ;FOREWARD
        PUSH    DS                      ;SAVE SEGMENT
        XOR     AX,AX                   ;GET OLD INTERRUPT VECTOR
        MOV     DS,AX                   ;FIRST SEGMENT IN MEMORY
        MOV     SI,(CLK * 4)            ;INTERRUPT ADDRESS
        LODSW                           ;GET OFFSET
        MOV     WORD PTR CS:[IP_CLK],AX ;SAVE IN OUR CODE SEGMENT DS IS BUSY
        LODSW                           ;GET SEGMENT
        MOV     WORD PTR CS:[CS_CLK],AX ;SAVE THAT TOO
;
        MOV     SI,(CLX * 4)            ;INTERRUPT ADDRESS
        LODSW                           ;GET OFFSET
        MOV     WORD PTR CS:[IP_CLX],AX ;SAVE IN OUR CODE SEGMENT DS IS BUSY
        LODSW                           ;GET SEGMENT
        MOV     WORD PTR CS:[CS_CLX],AX ;SAVE THAT TOO
        POP     DS                      ;RESTORE OUR DATA SEG
;
        PUSH    ES                      ;SAVE SEGMENT
        XOR     AX,AX                   ;ZERO
        MOV     ES,AX                   ;ZERO THE SEGMENT
        MOV     DI,(CLK * 4)            ;GET INTERRUPT ADDRESS
        MOV     AX,OFFSET CLK_INT       ;GET PROGRAM COUNTER
        STOSW                           ;PATCH
        MOV     AX,CS                   ;GET CODE SEGMENT
        STOSW
;
        MOV     DI,(CLX * 4)            ;GET INTERRUPT ADDRESS
        MOV     AX,OFFSET CLX_INT       ;GET PROGRAM COUNTER
        STOSW                           ;PATCH
        MOV     AX,CS                   ;GET CODE SEGMENT
        STOSW
        POP     ES
        STI
        RET
SET_INT ENDP
;
; Restore old clock vector(s).
;
RES_INT PROC    NEAR
        CLD                             ;FOREWARD
        CLI                             ;NO INTERRUPTS
        PUSH    ES                      ;SAVE SEGMENT
        XOR     AX,AX                   ;ZERO
        MOV     ES,AX                   ;ZERO THE SEGMENT
        MOV     DI,(CLK * 4)            ;GET INTERRUPT ADDRESS
        MOV     AX,WORD PTR [IP_CLK]    ;GET PROGRAM COUNTER
        STOSW                           ;PATCH
        MOV     AX,WORD PTR [CS_CLK]    ;GET CODE SEGMENT
        STOSW
;
        MOV     DI,(CLX * 4)            ;GET INTERRUPT ADDRESS
        MOV     AX,WORD PTR [IP_CLX]    ;GET PROGRAM COUNTER
        STOSW                           ;PATCH
        MOV     AX,WORD PTR [CS_CLX]    ;GET CODE SEGMENT
        STOSW
;
        POP     ES                      ;RESTORE SEGMENT
        STI                             ;ALLOW INTERRUPTS
        RET
RES_INT ENDP
;
GET_FLAG        PROC    NEAR            ;GET CRITICAL DOS FLAG
        PUSH    ES                      ;WILL RETURN IN ES SO SAVE
        MOV     AH,34H                  ;FUNCTION
        INT     MS_DOS                  ;DO IT
        MOV     WORD PTR [DOS_FLG],BX   ;SAVE IP
        MOV     WORD PTR DOS_FLG[2],ES  ;SAVE CS
        POP     ES                      ;RESTORE EXTRA
        RET
GET_FLAG        ENDP
;
PARSE   PROC    NEAR
        MOV     SI,CMD_BUF              ;POINT TO THE STRING
        LODSB                           ;GET BYTE FROM BUFFER
        OR      AL,AL
        JZ      BAD                     ;NOTHING TYPED
        CBW
        MOV     CX,AX                   ;USE AS COUNT
        CALL    CASE                    ;FORCE TO UPPER CASE
        MOV     DI,SI                   ;COPY FOR COMPARE
        MOV     AL,' '                  ;LOOK FOR A SPACE
        REPZ    SCASB                   ;GET BY SPACES
        JCXZ    BAD                     ;NO SPACE
        MOV     AL,':'                  ;DELIMITER _AFTER_ CHARACTER
        CALL    GET_CHR                 ;GET CHARS, CONV TO BINARY
        JC      BAD                     ;NO GOOD
        CMP     AL,MAX_HRS              ;WITHIN RANGE?
        JG      BAD                     ;NOPE
        MOV     BYTE PTR [SAV_HRS],AL   ;SAVE GOOD BINARY
        MOV     AL,':'                  ;NEXT DELIMITER
        CALL    GET_CHR                 ;GET CHARACTER(S)
        JC      BAD                     ;BAD CHARACTER(S)
        CMP     AL,MAX_MIN              ;WITHIN RANGE?
        JG      BAD                     ;NOPE
        MOV     BYTE PTR [SAV_MIN],AL   ;SAVE GOOD BINARY
        MOV     AL,' '                  ;LOOK FOR SPACE
        CALL    GET_CHR                 ;GET LAST CHARACTER(S)
        JC      BAD                     ;IS GOOD
        CMP     AL,MAX_SEC              ;WITHIN RANGE?
        JG      BAD                     ;NO
        MOV     BYTE PTR [SAV_SEC],AL   ;SAVE GOOD BINARY
        JMP     SHORT CHKRES            ;NOW, CHECK RESULTS
;
BAD:    MOV     DX,OFFSET PRP4          ;POINT TO USAGE
        CALL    PROMPT                  ;PRINT TO SCREEN
        STC                             ;SHOW ERROR
        RET
;
CHKRES: CALL    CHK_TIM                 ;SEE IF WE ENTERED TIME PROPERLY
        JNC     TIMCHK                  ;IT WAS OK
        MOV     DX,OFFSET PRP5          ;POINT TO 'IMPROPER TIME'
        CALL    PROMPT                  ;PRINT
        STC                             ;SHOW ERROR
        RET
;
TIMCHK: PUSH    SI                      ;CHECK RESULTS
        MOV     SI,OFFSET SAV_HRS       ;WHERE BINARY IS
        MOV     DI,OFFSET HH            ;WHERE TO PUT ASCII
        CALL    ASCIIM                  ;DO IT
        MOV     SI,OFFSET SAV_MIN       ;WHERE BINARY IS
        MOV     DI,OFFSET MM            ;WHERE TO PUT ASCII
        CALL    ASCIIM                  ;DO IT
        MOV     SI,OFFSET SAV_SEC       ;WHERE BINARY IS
        MOV     DI,OFFSET SX            ;WHERE TO PUT ASCII
        CALL    ASCIIM                  ;DO IT
;
        MOV     DX,OFFSET PRP0          ;POINT TO 'PROCESS'
        CALL    PROMPT                  ;PRINT TO SCREEN
;
        MOV     DI,CMD_BUF              ;POINT TO COMMAND LINE
        MOV     AL,CL                   ;BYTE COUNT REMAINING
        ADD     AL,3                    ;BYTES FOR ' \C '
        STOSB
        PUSH    CX                      ;SAVE PRESENT COUNT
        MOV     CX,4                    ;BYTES TO MOVE (TO WHERE TIME WAS)
        MOV     SI,OFFSET INFORM        ;STRING TO MOVE
        REP     MOVSB                   ;MOVE INTO BUFFER
        POP     CX                      ;RESTORE COUNT
        SUB     CX,2                    ;NO LEADING SPACE OR CARRIAGE RETURN
        POP     SI                      ;GET SAVED STRING POINTER
        INC     SI                      ;GET BY SPACE
        JCXZ    ABRT                    ;NOTHING TO LOAD
DISPL:  LODSB                           ;GET BYTE
        STOSB                           ;SAVE IN START OF BUFFER
        CALL    PCHR                    ;OUTPUT TO SCREEN
        LOOP    DISPL                   ;UNTIL DONE
        MOV     AL,CR                   ;BUFFER TERMINATION
        STOSB
;
        MOV     DX,OFFSET PRP1          ;POINT TO 'IF NOT CORRECT..'
        CALL    PROMPT                  ;PRINT TO SCREEN
;
        MOV     CX,1000H                ;TIMER
WAIT0:  MOV     AH,06H                  ;DIRECT CONSOLE INPUT
        MOV     DL,0FFH                 ;WE WANT A CHARACTER
        INT     MS_DOS                  ;FROM DOS
        TEST    AL,AL                   ;ANY CHARACTER?
        JNZ     ABRT                    ;YES, ABORT
        TEST    CL,CL                   ;NOPE, LOOK FOR CX = XX00
        JNZ     NO_DIS                  ;DON'T DISPLAY
        MOV     AL,CH                   ;GET HIGH BYTE
        TEST    AL,11111000B            ;WITHIN RANGE YET?
        JNZ     NO_DIS                  ;NOPE, DON'T DISPLAY
        AND     AL,00000111B            ;YES, MASK HIGH BITS
        ADD     AL,'0'                  ;CONVERT TO ASCII
        CALL    PCHR                    ;PRINT TO SCREEN
        MOV     AL,' '                  ;GET A SPACE
        CALL    PCHR                    ;PRINT IT TOO
NO_DIS: LOOP    WAIT0
;
        MOV     DX,OFFSET PRP3          ;POINT TO 'LOADED'
        CALL    PROMPT                  ;PRINT TO SCREEN
        XOR     AX,AX                   ;NO CARRY
        RET
;
ABRT:   MOV     DX,OFFSET PRP2          ;POINT TO 'ABORTED'
        CALL    PROMPT                  ;PRINT TO SCREEN
        STC
        RET
PARSE   ENDP
;
CHK_TIM PROC    NEAR
        MOV     AL,BYTE PTR [SAV_HRS]   ;GET COMMAND INPUT HOURS
        CMP     AL,BYTE PTR [HRS]       ;GET PRESENT HOURS
        JZ      CHKMIN                  ;HOURS ARE THE SAME
        JMP     SHORT TIMBAD            ;CARRY WILL SHOW ERROR
CHKMIN: MOV     AL,BYTE PTR [SAV_MIN]   ;GET COMMAND INPUT MINUTES
        CMP     AL,BYTE PTR [MIN]       ;GET PRESENT MINUTES
        JZ      CHKSEC                  ;IF THE SAME
        JMP     SHORT TIMBAD            ;CARRY SET IF BAD
CHKSEC: MOV     AL,BYTE PTR [SAV_SEC]   ;GET COMMAND INPUT SECONDS
        CMP     AL,BYTE PTR [SEC]       ;GET PRESENT SECONDS
TIMBAD: RET
CHK_TIM ENDP
;
PCHR    PROC    NEAR                    ;PRINT CHARACTER IN AL, USES AX,DX
        MOV     DL,AL                   ;INTO DL
        MOV     AH,2                    ;CONTROL BYTE
        INT     MS_DOS                  ;TO DOS
        RET
PCHR    ENDP
;
TELL    PROC    NEAR
        MOV     DX,OFFSET BEEP          ;PRINT A BEEP
PROMPT  PROC    NEAR                    ;PRINT STRING ADDRESSED BY DX
        MOV     AH,9                    ;FUNCTION
        INT     MS_DOS                  ;SYSTEM
        RET
PROMPT  ENDP
TELL    ENDP
;
GET_CHR PROC    NEAR                    ;GET CHARACTER [DI] POINTER
        PUSH    CX                      ;SAVE FOR NOW
        REPNZ   SCASB                   ;GET BY DELIMITER
        POP     BX                      ;SAVED AS FIRST COUNT (CX)
        JCXZ    BADENTRY                ;WERE NO DEMITERS
        SUB     BX,CX                   ;BX= BYTES FROM SPACE TO ':'
        CMP     BX,2                    ;WAS IT TWO BYTES?
        PUSHF                           ;SAVE ANSWER TO THAT QUESTION
        DEC     DI                      ;BACK TO 'FOUND' CHARACTER
        MOV     SI,DI                   ;INTO SOURCE INDEX
        SUB     SI,BX                   ;BACK UP TO FIRST BYTE
        LODSB                           ;GET BYTE
        SUB     AL,'0'                  ;ASCII BIAS
        POPF                            ;ARE WE THROUGH?
        JNZ     ALLDUN
        MOV     BL,10                   ;MULTIPLIER
        MUL     BL                      ;TIMES TEN
        MOV     AH,AL                   ;SAVE FOR NOW
        LODSB                           ;GET NEXT BYTE IN STRING
        SUB     AL,'0'                  ;ASCII BIAS
        ADD     AL,AH                   ;ADD TOGETHER
ALLDUN: MOV     DI,SI                   ;NEXT ADDRESS FOR COMPARE
        INC     DI
        INC     DI                      ;GET PAST THE COLON
        OR      AL,AL                   ;CLEAR CARRY
        RET                             ;AL= BINARY FROM STRING
BADENTRY:
        STC                             ;SHOW BAD
        RET
GET_CHR ENDP
;
CASE    PROC    NEAR
        PUSH    SI                      ;SAVE INDEX
        PUSH    CX                      ;AND COUNT
        MOV     DI,SI                   ;DEST SAME AS SOURCE
CASE0:  LODSB                           ;GET BYTE
        CMP     AL,'z'                  ;CHECK UPPER LIMIT
        JG      NO_CVT                  ;NOT A LOWER CASE LETTER
        CMP     AL,'a'                  ;CHECK LOWER LIMIT
        JL      NO_CVT                  ;NOT A LOWER CASE LETTER
        AND     AL,95                   ;MASK LOWER CASE BITS
NO_CVT: STOSB                           ;SAVE IN STRING
        LOOP    CASE0
        POP     CX                      ;RESTORE COUNT
        POP     SI                      ;AND INDEX
        RET
CASE    ENDP
;
RUN_PG  PROC    NEAR                    ;LOAD AND EXECUTE PROGRAM
        MOV     WORD PTR [SSP_SAV],SP   ;SAVE STACK POINTER
        MOV     AX,4B00H                ;RUN PROGRAM EXE CALL
        MOV     BX,OFFSET BLOCK         ;POINT TO PARAMETER BLOCK
        MOV     DX,OFFSET COMMAND       ;POINT TO FILENAME
        INT     MS_DOS                  ;DO IT
        MOV     AX,CS                   ;GET GODE SEGMENT
        MOV     DS,AX                   ;SEGMENT FIXUPS
        MOV     ES,AX
        MOV     SS,AX                   ;FIX UP STACK SEGMENT
        MOV     SP,WORD PTR [SSP_SAV]   ;RESTORE LOCAL STACK
        RET
RUN_PG  ENDP
;
KEEP3K  PROC    NEAR
        MOV     BX,OFFSET TOP           ;PARAGRAPHS TO KEEP
        MOV     CL,4                    ;BITS TO SHIFT
        SHR     BX,CL
        INC     BX                      ;ROUND UP
        JMP     SHORT FRE_MEM           ;IMPLIED RETURN
KEEP3K  ENDP
;
KEEP0K  PROC    NEAR
        XOR     BX,BX                   ;NO PARAGRAPHS
FRE_MEM PROC    NEAR                    ;FREE UP MEMORY (PARAS IN BX)
        MOV     AH,4AH                  ;FUNCTION
        PUSH    ES                      ;SAVE, GET'S DESTROYED
        INT     MS_DOS                  ;DO IT
        POP     ES                      ;RESTORE EXTRA
        RET
FRE_MEM ENDP
KEEP0K  ENDP                            ;FALL THROUGH
;
SAV_SCR PROC    NEAR                    ;SAVE ALL SCREEN PAGES BY BRUTE FORCE!
        CALL    GET_CUR                 ;SAVE CURSOR POSITION
        CALL    CREATE                  ;CREATE A SCREEN DATA FILE
        CALL    WRITE                   ;WRITE SCREEN DATA TO FILE
        PUSHF                           ;SAVE POSSIBLE ERROR ON THE WRITE
        CALL    CLOSE                   ;CLOSE THE FILE
        CALL    CLR_SCR                 ;CLEAR THE SCREEN
        POPF                            ;GET ERROR CODE FROM WRITE
        JZ      SAV_OK                  ;WAS ENOUGH SPACE
        MOV     DX,OFFSET ERRW          ;POINT TO ERROR MESSAGE
        CALL    PROMPT                  ;PRINT TO SCREEN
SAV_OK: RET                             ;THAT'LL DO IT
SAV_SCR ENDP
;
GET_CUR PROC    NEAR                    ;GET CURSOR POSITION
        MOV     AH,15                   ;GET CURRENT STATE
        INT     VID_ROM
        XOR     BL,BL                   ;PAGE RETURNS IN BH
        MOV     BYTE PTR [CUR_PGE],BH   ;SAVE CURRENT PAGE
        MOV     AH,3                    ;FUNCTION
        INT     VID_ROM                 ;DO IT
        MOV     WORD PTR [CUR_SAV],DX   ;SAVE CURSOR POSITION
        MOV     WORD PTR [CUR_TYP],CX   ;SAVE CURSOR TYPE
        RET
GET_CUR ENDP
;
RES_SCR PROC    NEAR                    ;RESTORE SCREEN
        CALL    SET_CUR                 ;RESTORE OLD CURSOR POSITION
        CALL    OPEN                    ;OPEN THE SCREEN DATA FILE
        CALL    READ                    ;READ INTO SCREEN MEMORY
        CALL    CLOSE                   ;CLOSE THE FILE
        CALL    DELETE                  ;AND BLOW IT AWAY
        RET
RES_SCR ENDP
;
SET_CUR PROC    NEAR                    ;SET CURSOR TO SAVED POSITION
        MOV     AL,BYTE PTR [CUR_PGE]   ;GET 'OLD' CURRENT PAGE
        MOV     AH,5                    ;SELECT PAGE FUNCTION
        INT     VID_ROM                 ;DO IT
        MOV     AH,2                    ;FUNCTION
        MOV     BH,BYTE PTR [CUR_PGE]   ;PAGE
        MOV     DX,WORD PTR [CUR_SAV]   ;GET OLD POSITION
        INT     VID_ROM                 ;DO IT
        MOV     AH,1                    ;SET CURSOR TYPE
        MOV     CX,WORD PTR [CUR_TYP]   ;GET CURSOR TYPE
        INT     VID_ROM
        RET
SET_CUR ENDP
;
CLR_SCR PROC    NEAR                    ;CLEAR THE SCREEN
        MOV     AX,0600H                ;FUNCTION
        XOR     CX,CX                   ;TOP LEFT HAND CORNER
        MOV     DX,244FH                ;BOTTOM RIGHT
        MOV     BH,7                    ;NORMAL ATTRIBUTE
        INT     VID_ROM                 ;DO IT
        XOR     DX,DX                   ;TOP LEFT CORNER
        MOV     AH,2                    ;SET CURSOR FUNCTION
        MOV     BH,0                    ;PAGE ZERO
        INT     VID_ROM                 ;SET THE CURSOR
        MOV     AH,1                    ;SET CURSOR TYPE
        MOV     CX,0608H                ;START LINE 6, END LINE
        INT     VID_ROM
        RET
CLR_SCR ENDP
;
SAV_DTA PROC    NEAR                    ;SAVE USERS DTA
        PUSH    ES                      ;SAVE, SEGMENT IN RETURN
        MOV     AH,2FH                  ;GET PRESENT DTA
        INT     MS_DOS                  ;CALL DOS
        MOV     WORD PTR [DTA_SEG],ES   ;SAVE PRESENT DTA SEGMENT
        MOV     WORD PTR [DTA_OFF],BX   ;SAVE PRESENT DTA OFFSET
        POP     ES                      ;RESTORE SEGMENT
        RET
SAV_DTA ENDP
;
SET_DTA PROC    NEAR                    ;SET NEW DTA TO 'DEFAULT'
        MOV     AH,1AH                  ;FUNCTION
        MOV     DX,CMD_BUF              ;DS:DX = 80H
        INT     MS_DOS                  ;DO IT
        RET
SET_DTA ENDP
;
RES_DTA PROC    NEAR                    ;RESTORE USER'S DTA
        MOV     AH,1AH                  ;FUNCTION
        MOV     DX,WORD PTR [DTA_OFF]   ;GET OLD OFFSET
        PUSH    DS                      ;SAVE PRESENT DATA SEG
        MOV     DS,WORD PTR [DTA_SEG]   ;NEW (OLD USER'S) DATA SEG
        INT     MS_DOS                  ;RESET TO OLD
        POP     DS                      ;RESTORE DATA SEG
        RET
RES_DTA ENDP
;
RES_HRD PROC    NEAR                    ;RESET HARDWARE CONTROLLER
        MOV     CX,5                    ;FIVE POSSIBLE INTERRUPTS
        MOV     AL,20H                  ;NON-SPECIFIC END OF INTERRUPT
HRD:    OUT     INT_BASE,AL
        LOOP    HRD
        RET
RES_HRD ENDP
;
SAV_DIR PROC    NEAR
        MOV     AH,19H                  ;CURRENT DISK FUNCTION
        INT     MS_DOS
        MOV     BYTE PTR [DISK],AL      ;SAVE
;
        MOV     AH,47H                  ;GET DIRECTORY FUNCTION
        MOV     SI,OFFSET DIRECT
        MOV     DL,0                    ;CURRENT DISK
        INT     MS_DOS
        RET
SAV_DIR ENDP
;
RES_DIR PROC    NEAR
        MOV     AH,0EH                  ;SELECT DISK FUNCTION
        MOV     DL,BYTE PTR [DISK]      ;GET SAVED DRIVE
        INT     MS_DOS                  ;DO IT
        MOV     AH,3BH                  ;CHANGE DIRECTORY FUNCTION
        MOV     DX,OFFSET DIRZ
        INT     MS_DOS
        RET
RES_DIR ENDP
;
;
; The following routines are used to save the screen data on a disk.
;
COPY    PROC    NEAR                    ;COPY FILE NAME, DOS KILLS IT!!!
        MOV     SI,OFFSET FILE1         ;COPY FROM HERE
        MOV     DI,OFFSET FILE2         ;PUT IT THERE
        MOV     CX,FILEN                ;BYTES TO COPY
        REP     MOVSB                   ;DO IT
        RET
COPY    ENDP
;
CREATE  PROC    NEAR
        CALL    COPY
        MOV     AH,3CH                  ;MS DOS FUNCTION
        MOV     DX,OFFSET FILE2         ;POINTER TO FILE NAME
        XOR     CX,CX                   ;NORMAL ATTRIBUTES
        INT     MS_DOS                  ;DO IT
        MOV     WORD PTR [HANDLE],AX    ;SAVE HANDLE
        RET
CREATE  ENDP
;
OPEN    PROC    NEAR
        CALL    COPY                    ;GET NEW COPY OF FILE NAME
        MOV     AX,3D00H                ;OPEN FUNCTION FOR READING
        MOV     DX,OFFSET FILE2         ;POINT TO FILE NAME
        INT     MS_DOS
        MOV     WORD PTR [HANDLE],AX    ;SAVE ACCESS WORD
        RET
OPEN    ENDP
;
WRITE   PROC    NEAR
        MOV     AH,40H                  ;WRITE TO FILE FUNCTION
        MOV     BX,WORD PTR [HANDLE]    ;GET OPEN FILE HANDLE
        MOV     CX,K16                  ;16 K TO WRITE
        PUSH    DS                      ;SAVE DATA SEG
        MOV     DS,WORD PTR [SEGMNT]    ;GET SCREEN SEGMENT
        XOR     DX,DX                   ;START AT THE BOTTOM
        INT     MS_DOS                  ;WRITE IT
        POP     DS                      ;RESTORE SEGMENT
        CMP     AX,K16                  ;DID WE GET IT ALL?
        RET
WRITE   ENDP
;
READ    PROC    NEAR
        MOV     AH,3FH                  ;READ FILE FUNCTION
        MOV     BX,WORD PTR [HANDLE]    ;FILE ACCESS WORD
        MOV     CX,K16                  ;16 K TO READ INTO SCREEN
        PUSH    DS                      ;SAVE DATA SEGMENT
        MOV     DS,WORD PTR [SEGMNT]    ;GET SCREEN SEGMENT
        XOR     DX,DX                   ;START AT THE BOTTOM
        INT     MS_DOS                  ;READ INTO SCREEN MEMORY
        POP     DS                      ;RESTORE
        RET
READ    ENDP
;
CLOSE   PROC    NEAR                    ;CLOSE THE SCREEN DATA FILE
        MOV     AH,3EH                  ;FUNCTION
        MOV     BX,WORD PTR [HANDLE]    ;FILE ACCESS WORD
        INT     MS_DOS                  ;DO IT
        RET
CLOSE   ENDP
;
DELETE  PROC    NEAR                    ;DELETE SCREEN DATA FILE
        CALL    COPY                    ;GET FRESH COPY OF FILE NAME
        MOV     AH,41H                  ;DELETE FUNCTION
        MOV     DX,OFFSET FILE2         ;POINT TO FILE NAME
        INT     MS_DOS                  ;BLOW IT AWAY
        RET
DELETE  ENDP
;
; The following routines log the operation to disk
;
LOG     PROC    NEAR
        CALL    OPEN_LOG
        CALL    FIND_END
        CALL    GET_TIME
        CALL    GET_DATE
        CALL    GET_PROC
        CALL    WRITE_LOG
        CALL    CLOSE_LOG
        RET
LOG     ENDP
;
GET_TIME        PROC    NEAR
        MOV     AH,2CH                  ;GET TIME FUNCTION
        INT     MS_DOS
        MOV     DI,OFFSET TIME
        MOV     AL,CH                   ;HOURS TO AL
        CALL    ASCII                   ;CONV TO ASCII
        INC     DI                      ;GET BY ':'
        MOV     AL,CL                   ;MINUTES TO AL
        CALL    ASCII                   ;CONV TO ASCII
        INC     DI                      ;GET BY ':'
        MOV     AL,DH                   ;SECS TO AL
        CALL    ASCII                   ;CONVERT TO ASCII
        RET
GET_TIME        ENDP
;
GET_DATE        PROC    NEAR
        MOV     AH,2AH
        INT     MS_DOS
        PUSH    CX                      ;SAVE YEAR
        MOV     AL,DL                   ;GET DAY
        MOV     DI,OFFSET DATE
        CALL    ASCII

        MOV     DI,OFFSET DATE[3]
        MOV     AL,DH                   ;GET MONTHS
        DEC     AL                      ;JAN IS NOW ZERO OFFSET
        CBW                             ;A WORD
        MOV     CL,3                    ;MULTIPLIER (THREE CHRS PER MONTH)
        MUL     CL                      ;CALC OFFSET
        MOV     SI,OFFSET MONTH         ;POINT TO TABLE
        ADD     SI,AX                   ;NEW OFFSET
        MOV     CX,3                    ;CHRS TO MOVE
        REP     MOVSB                   ;DO IT
        POP     CX
        SUB     CX,1900                 ;NORMALIZE
        MOV     AL,CL
        MOV     DI,OFFSET DATE[7]
        CALL    ASCII
        RET
GET_DATE        ENDP
;
GET_PROC        PROC    NEAR
        MOV     WORD PTR [BYTES],0      ;BYTE COUNT
        MOV     SI,CMD_BUF              ;POINT TO COMMAND LINE
        MOV     DI,OFFSET LINEBUF       ;WHERE TO PUT THE STRING
        LODSB                           ;GET CHARACTERS TYPED
        SUB     AL,3                    ;SUBTRACT FOR ' /C'
        CBW
        MOV     CX,AX                   ;BYTE COUNT
        JCXZ    GETOUT                  ;NOTHING TYPED
        ADD     AX,LEN                  ;PLUS THE TIME/DATE STAMP
        MOV     WORD PTR [BYTES],AX     ;SAVE FOR FILE WRITE
        ADD     SI,3                    ;GET BY THE ' /C'
        REP     MOVSB                   ;MOVE THE STRING
GETOUT: RET
GET_PROC        ENDP
;
OPEN_LOG        PROC    NEAR
        MOV     DX,OFFSET LOGF          ;POINT TO LOG FILE NAME
        MOV     AX,3D02H                ;OPEN FOR R/W
        XOR     CX,CX                   ;NORMAL FILE
        INT     MS_DOS
        JNC     OPNGD                   ;GOOD OPEN
        MOV     AX,3C00H                ;CREATE THE FILE, NONE EXISTS
        MOV     DX,OFFSET LOGF          ;POINT TO FILE NAME
        XOR     CX,CX                   ;NORMAL FILE
        INT     MS_DOS
OPNGD:  MOV     WORD PTR [LOGCHAN],AX   ;SAVE CHANNEL ACCESS WORD
        RET
OPEN_LOG        ENDP
;
FIND_END        PROC    NEAR
        MOV     AX,4202H                ;MOVE TO END PLUS OFFSET
        XOR     CX,CX
        MOV     DX,CX                   ;NO OFFSET
        MOV     BX,WORD PTR [LOGCHAN]   ;GET HANDLE
        INT     MS_DOS                  ;DO IT
        RET
FIND_END        ENDP
;
WRITE_LOG       PROC    NEAR
        MOV     AX,4000H                ;WRITE TO FILE
        MOV     DX,OFFSET LINE          ;POINT TO THE STRING TO BE WRITTEN
        MOV     BX,WORD PTR [LOGCHAN]   ;HANDLE
        MOV     CX,WORD PTR [BYTES]     ;NOW MUCH TO WRITE
        JCXZ    NOWRT
        INT     MS_DOS
NOWRT:  RET
WRITE_LOG       ENDP
;
CLOSE_LOG       PROC    NEAR
        MOV     AX,3E00H                ;CLOSE FILE
        MOV     BX,WORD PTR [LOGCHAN]   ;FILE ACCESS WORD
        INT     MS_DOS
        RET
CLOSE_LOG       ENDP
;
ASCIIM  PROC    NEAR                    ;BINARY(M) TO ASCII
        LODSB
ASCII   PROC    NEAR                    ;CONV BINARY IN AL TO ASCII [DI]
        MOV     AH,'0'-1                ;ASCII BIAS
SUBT:   INC     AH                      ;TENS COUNTER
        SUB     AL,10                   ;SUBTRACT TENS
        JNC     SUBT                    ;CONTINUE
        ADD     AL,(10 + '0')           ;ONE TOO MANY ADD BACK PLUS ASCII BIAS
        XCHG    AH,AL                   ;SWAP FOR STRING STORE
        STOSW                           ;DO IT
        RET
ASCII   ENDP
ASCIIM  ENDP
;
TOP     EQU     $
PSEG    ENDS
        END     MAIN
