home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
magazine
/
pcmagazi
/
1990
/
06
/
schedule.asm
next >
Wrap
Assembly Source File
|
1989-12-12
|
142KB
|
3,489 lines
_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
ESC_SCAN EQU 1
ENTER_SCAN EQU 1CH
Y_SCAN EQU 15H
N_SCAN EQU 31H
F1_SCAN EQU 3BH
F2_SCAN EQU 3CH
F6_SCAN EQU 40H
F7_SCAN EQU 41H
F8_SCAN EQU 42H
KB_FLAG EQU 17H
APP_LEN EQU 29 ;Chars per appointment.
APP_HEIGHT EQU 16 ;Appointment rows.
NOTE_LEN EQU 73 ;Notepad line length.
NOTE_HEIGHT EQU 3 ;Notepad rows.
DATE_LEN EQU 11 ;Date length in ASCII.
NOTEPAD_LEN EQU (81 - 4) + 81 + (81 - 4) ;Total notepad size.
DATA_RECORD STRUC
DATE_BINARY DW 2 DUP (?)
DATE_ASCII DB DATE_LEN DUP (?)
APPOINT_TEXT DB (APP_HEIGHT * APP_LEN * 2) DUP (?)
NOTEPAD_TEXT DB NOTEPAD_LEN DUP (?)
DATA_RECORD ENDS
BUFFER DB (SIZE DATA_RECORD - ($ - OFFSET DTA)) DUP (?)
SIGNATURE DB SPACE,CR,CR,LF
COPYRIGHT DB "SCHEDULE 1.0 (C) 1989 Ziff Communications Co.",CR,LF
PROGRAMMER DB "PC Magazine ",BOX," Michael J. Mefford",CR,LF,LF,"$"
DB CTRL_Z
COLOR_ATTRIBS STRUC
B DB 017H ;White on blue.
H DB 071H ;Blue on white.
Y DB 01EH ;Yellow on blue.
C DB 03BH ;Bright cyan on cyan.
A DB 01BH ;Bright cyan on blue.
P DB 07EH ;Yellow on white.
COLOR_ATTRIBS ENDS
COLOR COLOR_ATTRIBS <>
COLOR_ATTR COLOR_ATTRIBS <>
MONO_ATTR COLOR_ATTRIBS <07H, 70H, 70H, 70H, 07H, 07H>
MONO_FLAG DB 0 ; =1 if forced Black and white.
NOTE DW 1046 ;C note
DOS_SEGMENT DW ? ;Segment of internal DOS flags.
INDOS_OFFSET DW ? ;Offset of INDOS flag.
ERRFLAG_OFFSET DW ? ;Offset of critical error flag.
PROGRAM_STATUS DB 0 ;Popup status; non-zero=popped up.
BUSY_FLAGS LABEL WORD
FLAG_10h DB 0 ;Status of interrupt 10h.
FLAG_13h DB 0 ;Status of interrupt 13h.
BACK_FLAGS LABEL WORD
FLAG_8h DB 0 ;Status of interrupt 8h.
FLAG_28h DB 0 ;Status of interrupt 28h.
REQUEST_FLAG DB 0 ;Status of processing request.
SS_REGISTER DW ? ;SS register storage.
SP_REGISTER DW ? ;SP register storage.
OLDPSP DW ? ;PSP segment storage.
BIOS_ACTIVE_PAGE EQU 62H
ACTIVE_PAGE DB ?
ADDR_6845 DW ?
BIOS_CRT_MODE EQU 49H
CRT_MODE DB ?
CRT_COLS DW ?
CRT_LEN DW ?
CRT_START DW ?
CRT_DATA_LENGTH EQU $ - CRT_MODE
CRT_WIDTH DW ? ;Width in bytes of CRT.
CRT_ROWS DB ?
VIDEO_SEG DW ?
STATUS_REG DW ?
CURSOR_MODE DW ? ;Cursor shape.
CURSOR_POS DW ? ;Cursor position.
CURSOR_ADDR DW ? ;Cursor CRTC address.
DOS_VERSION DW ?
TSR_SEGMENT DW ?
OLD8 DW ?,? ;Old interrupt addresses.
OLD9 DW ?,?
OLD10 DW ?,?
OLD13 DW ?,?
OLD28 DW ?,?
OLD1B DW ?,?
OLD23 DW ?,?
OLD24 DW ?,?
OLD_DTA DW ?,?
BREAK DB ? ;Ctrl break state.
CLOCK DB "xx:xxxm",0
STATE DW CALENDAR ;Pop up screen.
EXIT_FLAG DB 0 ; =1 if Hotkey pressed.
CTRL_STATE EQU 4
ALT_STATE EQU 8
COMBO DB "C"
HOT_KEY_SCAN DB 2EH ;"C"
HOT_SHIFT_KEY DB ALT_STATE
MODIFY_FLAG DB 0 ; =1 if changes made in apps.
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
;---------------------------------------------------------------------------;
; Code Format: 0 = compressed string; followed by string length and char. ;
; 1 = display drop shade char. ;
; 2 = color follows. ;
; 3 = string end; if followed by -1 then menu end. ;
; 4 = drop shade string end followed by string length. ;
; 5 = insert date ;
; 6 = insert appointment blocks ;
; 7 = skip count / 2 follows. ;
;---------------------------------------------------------------------------;
CAL_MENU LABEL BYTE
DB 2,B
DB " F2 Save F3 Today F4 Purge F5 Print ─┘= Appoint. PgUp/Dn=Month Esc=Exit ",3
DB " ┌─────────────────────────────────────────────────┬──────────────────────────┐ ",3
DB " │ Schedule ■ PC Magazine ■ Michael J. Mefford │ ",2,Y,SPACE
CAL_DATE LABEL BYTE
DB " ",7,14,2,B," │ ",3
DB " ╞══════════╤══════════╤══════════╤══════════╤═════╧════╤══════════╤══════════╡ ",3
DB " │ "
DAYS LABEL BYTE
DB "Sun │ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │ ",3
DB " ├──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┤ ",3,-1
CAL_DUP LABEL BYTE
DB " ├──┴┴┴┴┴┴┴┴┼──┴┴┴┴┴┴┴┴┼──┴┴┴┴┴┴┴┴┼──┴┴┴┴┴┴┴┴┼──┴┴┴┴┴┴┴┴┼──┴┴┴┴┴┴┴┴┼──┴┴┴┴┴┴┴┴┤ ",3
CAL_MIDDLE LABEL BYTE
DB " │",7 DUP (5,6,"┤"),SPACE,3
DB " │",7 DUP (SPACE,SPACE,6,"┤"),SPACE,3,-1
DB " └──┴┴┴┴┴┴┴┴┴──┴┴┴┴┴┴┴┴┴──┴┴┴┴┴┴┴┴┴──┴┴┴┴┴┴┴┴┴──┴┴┴┴┴┴┴┴┴──┴┴┴┴┴┴┴┴┴──┴┴┴┴┴┴┴┴┘ ",3
DB " Appointment block 1 hour periods; 6:00am - 1:30 1st row; 2:00pm - 9:30 2nd row ",3,-1
APPOINTMENT_BLOCKS LABEL BYTE ;Storage for small block chars.
BLOCK_COUNT EQU 31 * 16
DB BLOCK_COUNT DUP (SPACE)
APP_MENU LABEL BYTE
DB 2,B
DB " F2 Save F3 Today F4 Purge F5 Print F6 Clear Line PgUp/Dn=Day Esc=Exit ",3
DB " ┌┬────────────────────────────────────────────────┬──────────────────────────┐ ",3
DB " ╞╡ Appointment ■ PC Magazine ■ Michael J. Mefford │ ",2,Y,SPACE
APP_DATE LABEL BYTE
DB " ",7,14,2,B," │ ",3
DB " ╞╪════════════════════════════════════════════════╧══════════════════════════╡ ",3
APP_START LABEL BYTE
APP_COL_SPACE EQU 7 ;Distance from end left to start right.
APP_ROW_SPACE EQU 16 ;Distance from end right to start left.
APP_LEFT_START EQU $ + 12
APP_RIGHT_START EQU APP_LEFT_START + APP_LEN + APP_COL_SPACE
APP_NOTE_SPACE EQU 8
DB " ╞╡ 6:00am 2:00 │ ",3
DB " ╞╡ 6:30 2:30 │ ",3
DB " ╞╡ 7:00 3:00 │ ",3
DB " ╞╡ 7:30 3:30 │ ",3
DB " ╞╡ 8:00 4:00 │ ",3
DB " ╞╡ 8:30 4:30 │ ",3
DB " ╞╡ 9:00 5:00 │ ",3
DB " ╞╡ 9:30 5:30 │ ",3
DB " ╞╡ 10:00 6:00 │ ",3
DB " ╞╡ 10:30 6:30 │ ",3
DB " ╞╡ 11:00 7:00 │ ",3
DB " ╞╡ 11:30 7:30 │ ",3
DB " ╞╡ 12:00pm 8:00 │ ",3
DB " ╞╡ 12:30 8:30 │ ",3
DB " ╞╡ 1:00 9:00 │ ",3
DB " ╞╡ 1:30 9:30 │ ",3
DB " ╞╪══════════════════════════════[Mini Notepad]═══════════════════════════════╡ ",3
DB " ╞╡ "
NOTEPAD LABEL BYTE
DB " │ ",3
DB " ╞╡ │ ",3
DB " ╞╡ │ ",3
APP LABEL BYTE
DB " └┴───────────────────────────────────────────────────────────────────────────┘ ",3,-1
APP_WIDTH EQU $ - APP - 1
;----------------------------------------------;
DISK_FULL_MSG DB 2,H,"╔", 0,25,"═", "╗",3
DB "║ Not enough disk space. ║",1
DB "║ Press any key. ║",1
DB "╚", 0,25,"═", "╝",1,4,27
PERMANENT_MSG DB 2,H,"╔", 0,25,"═", "╗",3
DB "║ Do you wish to save the ║",1
DB "║ change to disk? Y/N ║",1
DB "╚", 0,25,"═", "╝",1,4,27
PERMANENT_FAIL DB 2,H,"╔", 0,25,"═", "╗",3
DB "║ Update failed. Press ║",1
DB "║ any key to continue. ║",1
DB "╚", 0,25,"═", "╝",1,4,27
PRINTER_NO DB 2,H,"╔", 0,35,"═", "╗",3
DB "║ Select printer by pressing 1 or 2 ║",1
DB "╚", 0,35,"═", "╝",1,4,37
SAVED_MSG DB 2,H,"╔",0,9, "═╗",3
DB "║ Saved ║",1
DB "╚",0,9, "═╝",1,4,11
DISK LABEL BYTE
DB 2,H,"╔", 0,27,"═", "╗",3
DB "║", 0,27,SPACE, "║",1
DISK_NAME LABEL BYTE
DB "║ Error reading drive X ║",1,3,-1
ERR_BOT LABEL BYTE
DB "║ (R)etry or (A)bort ? ║",1
DB "║", 0,27,SPACE, "║",1
DB "╚", 0,27,"═", "╝",1,4,ERR_WIDTH
PRN LABEL BYTE
DB 2,H,"╔", 0,27,"═", "╗",3
DB "║", 0,27,SPACE, "║",1
DB "║",0,7," Printer Error",0,7," ║",1,3,-1
ERR_WIDTH EQU 29
ERR_HEIGHT EQU 6
ERR_ROW EQU ((25 - ERR_HEIGHT) / 2) + 3
ERR_COL EQU ((80 - ERR_WIDTH) / 2)
PURGE_MSG DB 2,H,"╔",0,41, "═", "╗",3
DB "║ Appointment Purge Date: ",2,P
PUR_DATE DB " ",2,H,"║",1
DB "║ Use: PgUp/Dn to change Purge Date ║",1
DB "║ F7 Purge up to, but excluding Date ║",1
DB "║ F8 Purge Date Only Esc to Cancel ║",1
DB "╚",0,41, "═", "╝",1,4,43
ARCHIVE_MSG DB 2,H,"╔",0,17, "═╗",3
DB "║ Archive Y/N? ║",1
DB "╚",0,17, "═╝",1,4,19
PURGING_MSG DB 2,H,"╔",0,11, "═╗",3
DB "║ Purging ║",1
DB "╚",0,11, "═╝",1,4,13
;------------------------------------------------------------------------------
;Execution comes here thru interrupt 9 every time a key is pressed or released.
;------------------------------------------------------------------------------
KEYBOARD PROC NEAR
PUSH AX
PUSH DS
MOV AX,40H
MOV DS,AX
MOV AH,DS:[KB_FLAG] ;Get keyboard shift status
PUSH CS
POP DS
KEYSTROKE: IN AL,PORT_A
TEST AH,HOT_SHIFT_KEY ;Our shift key?
JZ KB_EXIT ;No, then exit
CMP AL,HOT_KEY_SCAN ;Our hotkey?
JNZ KB_EXIT
CMP PROGRAM_STATUS,0 ;Popup routine already active?
JNZ KB_EXIT ;Yes, then ignore keypress
IN AL,PORT_B ;Retrieve Port B.
OR AL,80H ;Turn bit 7 on to reset
JMP $ + 2 ;I/O delay.
OUT PORT_B,AL ;Reset KBD.
AND AL,NOT 80H ;Turn bit 7 back off.
JMP $ + 2 ;I/O delay.
OUT PORT_B,AL ;Restore port.
CLI ;Interrupts off.
MOV AL,EOI ;Send End Of Interrupt
OUT COMMAND_PORT,AL ; to 8259A PIC.
MOV REQUEST_FLAG,18 ;Try to popup for one second.
POP DS
POP AX
IRET
KB_EXIT: POP DS
POP AX
JMP DWORD PTR CS:OLD9 ;Call keyboard handling routine.
KEYBOARD ENDP
;------------------------------------------------------------------------------
;Interrupt 8 handling routine.
;------------------------------------------------------------------------------
LAST_MIN DB ? ;Last minute.
BELL_FLAG DB 0 ; =1 if chiming.
BELL_CNT DB ? ; no. of chimes.
POPUP_FLAG DB 0 ; =1 if alarm or day rollover.
NOPOPUP_FLAG DB 0 ; =1 if ignore POPUP_FLAG.
NOBELL_FLAG DB 0 ; =1 if ignore chime.
NOMIDNIGHT_FLAG DB 0 ; =1 if ignore rollover pop up.
ALARM_FLAG DB 0 ; =1 only on alarm to avoid homing cursor.
REREAD_FLAG DB 0 ; =1 if forced reread of disk.
TIMER PROC NEAR
CLI
PUSHF
CALL CS:DWORD PTR OLD8
PUSH AX
PUSH BX
PUSH CX
PUSH DX
PUSH SI
PUSH DI
PUSH DS
PUSH ES
PUSH BP
CLD
MOV BX,CS
MOV DS,BX
CMP PROGRAM_STATUS,0 ;Already popped up?
JNZ CK_TIME
CMP FLAG_8h,0
JNZ CK_TIME
CMP POPUP_FLAG,1 ;Alarm or rollover popup?
JNZ CK_REQ_FLAG
MOV REQUEST_FLAG,18 ;Request to popup for 1 sec.
CK_REQ_FLAG: CMP REQUEST_FLAG,0
JZ CK_TIME
CK_TSRSTATE: INC FLAG_8h
STI
CALL TSR_STATE ;Save to enter Dos?
JC DECTIME
CALL MAIN
JMP SHORT TIMER_DONE
DECTIME: CMP REQUEST_FLAG,0
JZ TIMER_DONE
DEC REQUEST_FLAG
JZ TIMER_BEEP
CMP REQUEST_FLAG,9 ;Beep twice, once every 1/2 sec.
JNZ TIMER_DONE
TIMER_BEEP: CALL BEEP
TIMER_DONE: CLI
DEC FLAG_8h
TIMER_END: POP BP
POP ES
POP DS
POP DI
POP SI
POP DX
POP CX
POP BX
POP AX
IRET
;----------------------------------------------;
CK_TIME: STI
MOV AX,40H
MOV ES,AX
MOV CX,ES:[6CH] ;TIMER_LOW
MOV AX,ES:[6EH] ;TIMER_HIGH
MOV BP,AX ;Save hour.
PUSH CS
POP ES
MOV BL,"a" ;Convert time to 12 hour version.
CMP AX,24
JZ GOT_MUNDI
CMP AX,11
JBE GOT_MUNDI
MOV BL,"p"
GOT_MUNDI: OR AX,AX
JNZ CK_NOON
MOV AX,12
CK_NOON: CMP AX,12
JBE GOT_HOUR
SUB AX,12
GOT_HOUR: MOV DI,OFFSET CLOCK
XOR SI,SI ;Suppress leading zero.
CALL STORE_NUMBER
MOV AX,CX
XOR DX,DX
MOV CX,1093 ;Convert ticks to minutes.
DIV CX
INC SI ;Store leading zero.
MOV CL,CLOCK + 4 ;Retrieve current minute.
MOV LAST_MIN,CL ;Store new current minute.
CALL STORE_NUMBER
MOV [DI - 1],BL ;Store am or pm.
CK_DISP_CLOCK: CMP PROGRAM_STATUS,2 ;If not popped up, no
JNZ CK_BELL ; clock display.
DISP_TIME: MOV AX,2
CALL CALC_ADDR
ADD DI,70 * 2
MOV BH,COLOR.Y
MOV SI,OFFSET CLOCK
MOV DX,STATUS_REG
PUSH ES
MOV ES,VIDEO_SEG
JMP SHORT WRITE_CLOCK
NEXT_CLOCK: CALL WRITE_SCREEN ;Display clock.
WRITE_CLOCK: LODSB
OR AL,AL
JNZ NEXT_CLOCK
POP ES
CK_BELL: CMP BELL_FLAG,0 ;Is alarm chiming?
JZ CK_MINUTE
JMP DO_BELL
CK_MINUTE: MOV AX,WORD PTR CLOCK + 3 ;Retrieve current minute.
CMP AH,LAST_MIN ;Same as last minute?
JNZ CK_ALARM
JMP TIMER_END ;If yes, done.
CK_ALARM: CMP AX,"1" SHL 8 + "0" ;Else, is it :01?
JZ CK_ROLLOVER ;If yes, check if 12:01.
SUB AX,"0" SHL 8 + "0" ;Else, adjust to binary.
OR AH,AH ;Minute zero?
JZ ADJUST_TIME
TIMER_LILLY: JMP TIMER_END ;If no, done.
CK_ROLLOVER: OR BP,BP ;Is it 12:01am?
JNZ TIMER_LILLY ;If no, done.
MOV REREAD_FLAG,1 ;Else, reread disk alarms.
CMP PROGRAM_STATUS,0 ;Are we popped up?
JNZ FIX_DATE ;If yes, fix date now.
CMP NOMIDNIGHT_FLAG,1 ;Else, ignore rollover?
JZ TIMER_LILLY ;If yes, done.
MOV POPUP_FLAG,1 ;Else, popup request.
JMP TIMER_LILLY
FIX_DATE: CALL UPDATE_DATE ;Fix date.
JMP TIMER_LILLY
ADJUST_TIME: SUB BP,6 ; 6:00am
JS EXIT_TIMER
CMP BP,15 ; 9:00pm
JA EXIT_TIMER
OR AL,AL ; xx:00
JZ CK_MATCH
CMP AL,3 ; xx:30
JNZ EXIT_TIMER
AND AL,1 ;Convert to index.
CK_MATCH: CMP PROGRAM_STATUS,0 ;Popped up?
JNZ EXIT_TIMER ;If yes, no alarms.
CBW
SHL BP,1 ;Hour * 2.
ADD AX,BP ; + half hour index.
MOV DI,AX
CMP BYTE PTR ALARMS[DI],1 ;Appointment?
JNZ EXIT_TIMER ;If no, done.
CMP NOPOPUP_FLAG,1 ;No pop up request by user?
JZ CK_BELL_FLAG ;If yes, check chime.
MOV ALARM_FLAG,1 ;Else, flag no cursor change
MOV APP_INDEX,APP_LEFT ; when disk reread to today.
MOV DX,DI
XCHG DH,DL ;Place cursor on appointment.
MOV DL,12
CMP DH,16
JB MOVE_CURSOR
MOV APP_INDEX,APP_RIGHT
SUB DH,16
MOV DL,48
MOVE_CURSOR: ADD DH,4 ;Starts on fourth row.
MOV APP_CURSOR,DX
CK_BELL_FLAG: CMP NOBELL_FLAG,1 ;No chime request by user?
JNZ START_BELL
MOV POPUP_FLAG,1 ;If yes, just pop up.
JMP SHORT EXIT_TIMER
START_BELL: MOV BELL_CNT,14 * 2 ;14 chirps for chime.
MOV BELL_FLAG,1
CALL SETUP_BELL
DO_BELL: CALL FLIP_BELL
EXIT_TIMER: JMP TIMER_END
TIMER ENDP
;-------------------------------------------------------------
; INPUT: SI = 0 not to suppress leading zero; SI = 1 suppress.
STORE_NUMBER: MOV BH,10
DIV BH
ADD AX,"00"
OR SI,SI
JNZ STORE_IT
CMP AL,"0"
JNZ STORE_IT
MOV AL,SPACE
STORE_IT: STOSB
XCHG AH,AL
STOSB
INC DI
RET
;------------------------------------------------------------------------------
;Interrupt 10h handling routine.
;------------------------------------------------------------------------------
VIDEO PROC NEAR
INC CS:FLAG_10H ;No popup while in a int 10h.
PUSHF
CLI
CALL DWORD PTR CS:OLD10
DEC CS:FLAG_10H
IRET
VIDEO ENDP
;------------------------------------------------------------------------------
;Interrupt 13h handling routine.
;------------------------------------------------------------------------------
BDISK PROC FAR
PUSHF
INC CS:FLAG_13H ;No popup while disk activity.
CLI
CALL DWORD PTR CS:OLD13 ;Call BIOS routine
PUSHF
DEC CS:FLAG_13H
POPF
STI
RET 2 ;Preserve flags.
BDISK ENDP
;------------------------------------------------------------------------------
;Interrupt 28h handling routine.
;------------------------------------------------------------------------------
BACKPROC PROC NEAR
PUSH AX
PUSH DS
MOV AX,CS
MOV DS,AX
CLI
PUSHF
CALL DWORD PTR OLD28
CMP REQUEST_FLAG,0 ;Popup requested?
JZ BP_EXIT
CMP BACK_FLAGS,0 ;Save to popup?
JNZ BP_EXIT
CMP PROGRAM_STATUS,0 ;Already popped up?
JNZ BP_EXIT
INC FLAG_28h
STI
CLD
PUSH BX
PUSH CX
PUSH DX
PUSH SI
PUSH DI
PUSH ES
PUSH BP
CALL TSR_STATE ;Save to enter Dos?
JC BP_DONE
CALL MAIN
BP_DONE: POP BP
POP ES
POP DI
POP SI
POP DX
POP CX
POP BX
DEC FLAG_28h
BP_EXIT: POP DS
POP AX
IRET
BACKPROC ENDP
;----------------------------------------------;
TSR_STATE: PUSH AX
CMP BUSY_FLAGS,0
JNZ TSR_BUSY
MOV ES,DOS_SEGMENT ;Check INDOS flag.
MOV BX,INDOS_OFFSET
MOV AL,ES:[BX]
MOV BX,ERRFLAG_OFFSET ;Check critical error flag.
MOV AH,ES:[BX]
XOR BX,BX
CMP BL,FLAG_28h
RCL BL,1
CMP BX,AX
JC TSR_END
; Checking for hardware interrupts will avoid lost of chars in Async.
MOV AX,00001011B
OUT 20H,AL
JMP $ + 2
IN AL,20H
CMP AH,AL
JC TSR_END
TSR_OK: CLC
JMP SHORT TSR_END
TSR_BUSY: STC
TSR_END: POP AX
RET
;-----------------------------------------------;
; This is the new Critical Error interrupt 24h. ;
;-----------------------------------------------;
ERR_CURSOR DW ? ; Current cursor position.
ERR_CORNER DW ? ; Address for error message.
ABORT DB 0 ; =1 if user chose to abort.
IOERR: STI
PUSH BX
PUSH CX
PUSH DX
PUSH SI
PUSH DI
PUSH BP
PUSH DS
PUSH ES
MOV BX,CS
MOV DS,BX
MOV ES,BX
CLD
ADD AL,"A"
MOV DISK_NAME + 24,AL ;Convert and save drive letter.
MOV SI,OFFSET DISK ;Disk or printer critical error?
TEST AH,10000000B
JZ SAVE_IT
MOV SI,OFFSET PRN
SAVE_IT: PUSH SI ;Save screen contents we are
MOV BH,ACTIVE_PAGE ; going to write over.
MOV AH,3
INT 10H
MOV ERR_CURSOR,DX ;Save cursor position and
CALL HIDE_CURSOR ; hide it off screen.
MOV AX,ERR_ROW ;Popup error message.
CALL CALC_ADDR
ADD AX,ERR_COL * 2
MOV ERR_CORNER,AX
MOV SI,AX
MOV DI,OFFSET ERR_SAVE
MOV CX,ERR_HEIGHT + 1
MOV BP,ERR_WIDTH + 2
CALL DO_SAVE
POP SI
MOV DI,ERR_CORNER
CALL POP_WINDOW
MOV DI,ERR_CORNER
MOV AX,CRT_WIDTH
ADD DI,AX
ADD DI,AX
ADD DI,AX
MOV SI,OFFSET ERR_BOT
CALL POP_WINDOW
CALL BEEP
WAIT_KEY: XOR AH,AH ;Get a user response.
INT 16H
MOV AL,1
CMP AH,13H ;Was it scan code for "R"?
JZ ERR_END
CMP AH,1EH ;Was it "A"?
JNZ WAIT_KEY ;If no, wait until valid
MOV ABORT,1 ; response.
MOV AL,3 ;Fail.
CMP DOS_VERSION,300H ;DOS 2 can't handle fail request.
JAE ERR_END
XOR AL,AL
ERR_END: PUSH AX
MOV DI,ERR_CORNER ;Restore screen.
MOV SI,OFFSET ERR_SAVE
MOV CX,ERR_HEIGHT + 1
MOV BP,ERR_WIDTH + 2
CALL DO_RESTORE
MOV DX,ERR_CURSOR
CALL SET_CURSOR ;Restore cursor position.
POP AX
POP ES ;Restore registers.
POP DS
POP BP
POP DI
POP SI
POP DX
POP CX
POP BX
IOEXIT: IRET
;------------------------------------------------------------------------------
;MAIN is the routine called to pop up the window.
;------------------------------------------------------------------------------
MAIN PROC NEAR
MOV PROGRAM_STATUS,1 ;Flag in process of popping up.
MOV REQUEST_FLAG,0 ;Reset request counter.
MOV SS_REGISTER,SS ;Setup own stack.
MOV SP_REGISTER,SP
MOV AX,CS
MOV SS,AX
MOV SP,OFFSET OUR_STACK
STI
MOV ES,AX
CALL GET_BIOS_DATA
MOV AL,CRT_MODE ;Is it video mode BW80, CO80
CMP AL,3 ; or MONO?
JZ MAIN1
CMP AL,2
JZ MAIN1
CMP AL,7
JZ MAIN1
QUICK_EXIT: CMP POPUP_FLAG,1 ;If no, can't popup; beep to tell
JZ EXIT ; user only if was hotkey popup.
CALL BEEP
;----------------------------------------------;
; MAIN exit. ;
;----------------------------------------------;
EXIT: CLI
MOV SS,SS_REGISTER ;Restore stack.
MOV SP,SP_REGISTER
MOV PROGRAM_STATUS,0
RET
;----------------------------------------------;
MAIN1: CMP CRT_COLS,80 ;At least 80 columns?
JB QUICK_EXIT
GET_COLORS: CALL GET_CUR_ADDR ;Save cursor address.
CMP MONO_FLAG,1 ;Forced Black and white?
JZ DO_MONO
MOV SI,OFFSET COLOR_ATTR ;Select color or mono
CMP CRT_MODE,3 ; attributes.
JZ GOT_ATTR
DO_MONO: MOV SI,OFFSET MONO_ATTR
GOT_ATTR: MOV DI,OFFSET COLOR
MOV CX,SIZE COLOR_ATTRIBS
REP MOVSB
MAIN5: MOV AH,51H
CALL INT21_PSP ;Get PSP.
MOV OLDPSP,BX
PUSH CS
POP BX
MOV AH,50H ;Set PSP.
CALL INT21_PSP
MOV AH,2FH
INT 21H
MOV OLD_DTA[0],BX ;Get DTA.
MOV OLD_DTA[2],ES
MOV DX,OFFSET DTA
MOV AH,1AH ;Set DTA.
INT 21H
MOV AX,3300H ;Get break.
INT 21H
MOV BREAK,DL
XOR DL,DL ;Break off.
MOV AX,3301H
INT 21H
MAIN6: PUSH CS
POP ES
CALL IOSET ;Set up critical error handler.
CALL UPDATE_DATE
CALL SAVE_SCREEN
MOV EXIT_FLAG,0
CMP POPUP_FLAG,1 ;Alarm or rollover?
JNZ READY
MOV POPUP_FLAG,0
MOV STATE,OFFSET APPOINTMENT ;If yes, popup in appointments
CALL CTODAY ; on today.
READY: MOV PROGRAM_STATUS,2 ;Flag so clock displayed.
;------------------------------;
; M A I N L O O P ;
;------------------------------;
NEXT_STATE: CALL HIDE_CURSOR
CALL STATE
CMP EXIT_FLAG,1 ;If Esc Calendar or Hotkey, exit.
JNZ NEXT_STATE
ESCAPE: MOV PROGRAM_STATUS,1 ;Disable clock display.
CALL RESTORE_SCREEN
MOV BX,OLDPSP ;Restore PSP.
MOV AH,50H
CALL INT21_PSP
PUSH DS ;Restore DTA.
LDS DX,DWORD PTR OLD_DTA
MOV AH,1AH
INT 21H
POP DS
MOV DL,BREAK ;Restore BREAK.
MOV AX,3301H
INT 21H
CALL IORESET ;Restore critical handler.
MOV BH,ACTIVE_PAGE ;Restore cursor.
MOV DX,CURSOR_POS
MOV AH,2
INT 10H
MOV DX,ADDR_6845 ;Recover CRTC base address
MOV CX,CURSOR_ADDR
MOV AL,14
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
JMP EXIT
MAIN ENDP
;----------------------------------------------;
INT21_PSP: CMP DOS_VERSION,30AH ;PSP call DOS 3 or above.
JB INT21_PSP2
INT 21H
RET
INT21_PSP2: PUSH DS ;Else, fake via error.
MOV DI,ERRFLAG_OFFSET
MOV DS,DOS_SEGMENT
INC BYTE PTR [DI]
INT 21H
DEC BYTE PTR [DI]
POP DS
RET
;------------------------------------------;
; OUTPUT: ZF=1 if aborted. CY=1 if failed ;
;------------------------------------------;
ZR EQU 1000000B ;Bit for zero flag in register.
INT21_IO: INT 21H
PUSH AX
LAHF ;Save flag's results of DOS call.
AND AH,NOT ZR ;Assume zero flag zero.
CMP ABORT,1 ;Was there an abort from
JNZ INT21_IO_END ; critical error?
OR AH,ZR ;If yes, turn zero bit on.
MOV ABORT,0 ;Reset abort flag.
INT21_IO_END: SAHF ;Return results in flags reg.
POP AX
RET
;----------------------------------------------;
UPDATE_DATE: MOV AH,2AH ;Get the date from DOS.
INT 21H
MOV YEAR_TODAY,CX
MOV DAY_MON_TODAY,DX
RET
;**********************************************;
; F U N C T I O N C A L L S
;**********************************************;
MONTHS DB "JanFebMarAprMayJunJulAugSepOctNovDec"
NUMDAYS DB 31,28,31,30,31,30,31,31,30,31,30,31
YEAR_CAL DW ? ;Year displayed.
DAY_MON_CAL LABEL WORD
DAY_CAL DB ? ;Day cursor is on.
MONTH_CAL DB ? ;Month displayed.
YEAR_TODAY DW ? ;Current year.
DAY_MON_TODAY LABEL WORD
DAY_TODAY DB ? ;Current day.
MONTH_TODAY DB ? ;Current month.
YEAR_CUR DW ? ;Last year displayed.
DAY_MON_CUR LABEL WORD
DAY_CUR DB ? ;Last day displayed.
MONTH_CUR DB -1 ;Last month displayed.
WEEKDAY DB ? ;Day of week for 1st.
LAST_DAY DB ? ;Last day of month.
DAY_COUNTER DB ? ;Day display counter.
BLOCK_COUNTERS LABEL WORD
BLOCK_COUNTER DB ?,? ;Block display counters.
BLOCK_ROW DW 0 ;Block row index.
CTABLE DB 3CH, 3DH, 3EH, 3FH
DB 4BH, 4DH, 48H, 50H, 47H, 4FH
DB 49H, 51H, 84H, 76H, 77H, 75H
DB 73H, 74H
CTABLE_LEN EQU $ - CTABLE
DW SAVE, CTODAY, PPURGE, PRINT
DW CLEFT, CRIGHT, CUP, CDOWN, CHOME, CEND
DW CPGUP, CPGDN, CCTRLPGUP, CCTRLPGDN, CCTRLHOME, CCTRLEND
DW CHOME, CEND
CALENDAR: CALL CK_DATE ;See if date changed.
MOV SI,OFFSET CAL_MENU ;Pop up calendar screen.
CALL POP_MENU
MOV DAY_COUNTER,0 ;Counters for day and
MOV BLOCK_COUNTERS,0 ; cursor display.
MOV SI,OFFSET CAL_MIDDLE
CALL POP_WINDOW
MOV CX,5
NEXT_CALROW: PUSH CX
MOV SI,OFFSET CAL_DUP
CALL POP_WINDOW
POP CX
LOOP NEXT_CALROW
INC SI
CALL POP_WINDOW
CALL GETKEY ;Get a keystroke.
JC CALENDAR_DONE ;Exit if Esc or hotkey.
CMP AL,ENTER_SCAN ;If Enter, then go to
JZ GOTO_APP ; appointment screen.
CMP AL,31H ;If "N" scan code.
JZ GOTO_NOTE
CMP AL,F6_SCAN ; or F6, go directly
JNZ CAL_DISPATCH ; to Notepad.
GOTO_NOTE: MOV STATE,OFFSET APPOINTMENT
CALL CK_DATE
MOV APP_CURSOR,A_NOTE_CURSOR
MOV APP_INDEX,APP_NOTE
JMP SHORT CALENDAR_END
GOTO_APP: MOV STATE,OFFSET APPOINTMENT
JMP SHORT CALENDAR_END
CAL_DISPATCH: MOV SI,YEAR_CAL ;Pass variables in registers
MOV DL,DAY_CAL ; to calendar keyboard
MOV DH,MONTH_CAL ; functions.
MOV BL,WEEKDAY
MOV BH,LAST_DAY
MOV DI,OFFSET CTABLE
MOV CX,CTABLE_LEN
CALL DISPATCH
CMP EXIT_FLAG,1
JZ CALENDAR_END
JMP CALENDAR
CALENDAR_DONE: MOV EXIT_FLAG,1 ;Tell MAIN to exit.
CALENDAR_END: RET
;----------------------------------------------;
; INPUT: SI=YEAR_CAL; DL=DAY_CAL; DH=MONTH_CAL; BL=WEEKDAY; BH=LAST_DAY
CTODAY: MOV SI,YEAR_TODAY ;Today's date.
MOV DX,DAY_MON_TODAY
JMP SHORT CFUNC_UPDATE
CPGDN: INC DH ;Next month.
CMP DH,12 ;Past Dec?
JBE CFUNC_UPDATE
MOV DH,1 ;Jan.
INC SI ;Next year.
JMP SHORT CK_YEAR
CPGUP: DEC DH ;Previous month.
JNZ CFUNC_UPDATE ;Before Jan?
MOV DH,12 ;Dec.
DEC SI ;Previous year.
JMP SHORT CK_YEAR
CCTRLPGDN: INC SI ;Next year.
JMP SHORT CK_YEAR
CCTRLPGUP: DEC SI ;Previous year.
CK_YEAR: CMP SI,10000 ;Stay within Gregorian
JAE CFUNC_END ; calendar.
CMP SI,1582
JA CFUNC_UPDATE
JMP SHORT CFUNC_END
CLEFT: DEC DL ;Previous day.
JNZ CFUNC_UPDATE
JMP SHORT CFUNC_END
CRIGHT: INC DL ;Next day.
CMP DL,BH
JBE CFUNC_UPDATE
JMP SHORT CFUNC_END
CUP: MOV AL,7
SUB AL,BL ;Adjust for weekday.
CMP DL,AL ;If first week, ignore
JBE CFUNC_END
SUB DL,7 ;Previous week.
JA CFUNC_UPDATE
MOV DL,1 ;First day, if out of bounds.
JMP SHORT CFUNC_UPDATE
CDOWN: MOV CL,7
MOV AL,CL
SUB AL,BL
FIND_BOTTOM: ADD AL,CL ;Find last week row.
CMP AL,BH
JBE FIND_BOTTOM
SUB AL,CL
CMP DL,AL
JA CFUNC_END ;Ignore if on last row.
ADD DL,7 ;Next week.
CMP DL,BH
JBE CFUNC_UPDATE
MOV DL,BH ;Last day if out of bounds.
JMP SHORT CFUNC_UPDATE
CCTRLHOME: MOV DL,1 ;First day.
JMP SHORT CFUNC_UPDATE
CCTRLEND: MOV DL,BH ;Last day.
CFUNC_UPDATE: MOV YEAR_CAL,SI ;Store new day/year.
MOV DAY_MON_CAL,DX
CFUNC_END: RET
CHOME: MOV AL,DL ;Adjust for weekday.
ADD AL,BL
DEC AL
CBW
MOV CL,7
DIV CL
MOV DL,1 ;First day if at start of row.
OR AH,AH
JZ CFUNC_UPDATE
MUL CL
SUB AL,BL
INC AL
JBE CFUNC_UPDATE
MOV DL,AL ;Else go to start of row.
JMP CFUNC_UPDATE
CEND: MOV AL,DL ;Adjust for weekday.
ADD AL,BL
CBW
MOV CL,7
DIV CL
MOV DL,BH ;Last day if at end of row.
OR AH,AH
JZ CFUNC_UPDATE
MUL CL
ADD AL,CL
SUB AL,BL
MOV DL,AL ;Else, end of row.
CMP DL,BH
JBE CFUNC_UPDATE
MOV DL,BH
JMP CFUNC_UPDATE
;----------------------------------------------;
CK_DATE: MOV AX,DAY_MON_CAL ;Retrieve calendar day/year.
MOV BX,YEAR_CAL
CMP REREAD_FLAG,1 ;Forced reread?
JZ GET_CAL
CMP AH,MONTH_CUR ;Has month changed?
JNZ GET_CAL
CMP BX,YEAR_CUR ;Has year changed?
JNZ GET_CAL
CMP STATE,OFFSET APPOINTMENT
JNZ CK_DATE_END ;If appointment screen,
CMP AL,DAY_CUR ; has day changed?
JZ CK_DATE_END
GET_CAL: MOV REREAD_FLAG,0 ;Reset flag.
PUSH AX
PUSH BX
CALL PERMANENT ;See if changes have been made
POP BX ; in appointment screen.
POP AX
MOV MONTH_CUR,AH ;Store current day/year.
MOV YEAR_CUR,BX
CALL FIRSTDAY ;Find first day of month.
CALL READ_DATA ;Read data off of disk.
CMP ALARM_FLAG,1 ;Was this alarm?
JZ RESET_ALARM ;If yes, leave cursor on app.
MOV A_LEFT.CURSOR,A_LEFT_CURSOR
MOV A_RIGHT.CURSOR,A_RIGHT_CURSOR
MOV A_NOTE.CURSOR,A_NOTE_CURSOR
MOV APP_INDEX,APP_LEFT
MOV APP_CURSOR,A_LEFT_CURSOR
RESET_ALARM: MOV ALARM_FLAG,0
CK_DATE_END: RET
;----------------------------------------------;
FIRSTDAY: MOV SI,YEAR_CAL ;Year.
MOV NUMDAYS[1],28 ;Assume this isn't a leap year.
TEST SI,3 ;Year divisible by 4?
JNZ GETDAY ;Assumed right.
MOV NUMDAYS[1],29 ;Leap year = 29 days.
GETDAY: MOV BX,6 ;Saturday,(weekday of 1/1/1583)
MOV AX,SI ;Calendar year.
SUB AX,1583 ;No. years since our base year.
ADD BX,AX ;Calendar advances 1 weekday per
; year unless leap year.
ADD AX,2 ;Adj diff, so even #, if year
MOV CX,4 ; was after a leap year.
CWD
DIV CX ;No. leap years before this.
ADD BX,AX ;Add 1 day, for each leap yr.
CMP SI,1700 ;Things work normally 'til 1700.
JL CNVRT ;Not a leap year.
MOV AX,SI
SUB AX,1600 ;Get no. years since 1600.
MOV CL,100
CWD ;Convert to number of centuries.
DIV CX
OR DX,DX ;If remain=0 this is centennial.
JNZ DEDUCT
TEST AX,3 ;If century divisible by
JZ DECAX ; 400, it is also leap year.
MOV NUMDAYS[1],28 ;Other centennial years have 28.
DECAX: DEC AX ;If centennial, sub 1 from cent.
DEDUCT: SUB BX,AX ;Subtract 1 weekday per century.
CWD
MOV CL,4
DIV CX ;Calculate centuries mod 400.
ADD BX,AX ;Add back 1 day per 400 years.
CNVRT: MOV SI,OFFSET NUMDAYS ;Add in days per month, this yr.
XOR AH,AH
MOV CL,MONTH_CAL ;Calendar month.
DEC CL
MOV DI,CX
MOV DL,NUMDAYS[DI]
MOV LAST_DAY,DL
JCXZ FINAL ;If Jan, ready for final calc.
ADDMNTH: LODSB
ADD BX,AX ;Add to days count.
LOOP ADDMNTH ;Cont. until prior months added.
FINAL: MOV AX,BX ;Days advanced since 1/1/1583.
CWD
MOV CL,7 ;Find no. full weeks since 1583.
DIV CX ;Weekday of 1st of cal's month.
MOV WEEKDAY,DL ;Result is first day of week.
MOV AL,DAY_CAL ;Fix last day.
FIX_CURSOR: CMP AL,LAST_DAY
JBE STORE_CURSOR2
DEC AL
JMP FIX_CURSOR
STORE_CURSOR2: MOV DAY_CAL,AL ;Store fix.
MOV DAY_CUR,AL
CALL STORE_DATE
RET
;----------------------------------------------;
STORE_DATE: MOV BP,3 ;Three places need dates.
MOV DI,OFFSET CAL_DATE ;Calendar screen.
NEXT_DATE: PUSH DI
MOV AL,MONTH_CAL ;Month
DEC AL
MOV CX,3
MUL CL
ADD AX,OFFSET MONTHS
MOV SI,AX
REP MOVSB
INC DI
CMP BP,3
JZ STORE_YEAR
MOV AL,DAY_CAL ;Number of day.
CBW
CALL STORE_NUM
MOV AL,SPACE
STOSB
STORE_YEAR: MOV AX,YEAR_CAL ;Year.
CALL STORE_NUM
MOV AL,SPACE
STOSB
POP DI
CMP BP,3
JZ LOOP_DATE
ADD DI,12
MOV AL,WEEKDAY
ADD AL,DAY_CAL
DEC AL
CBW
MOV CL,7
DIV CL
MOV AL,AH
MOV CL,11
MUL CL
ADD AX,OFFSET DAYS ;Name of weekday; ie. Mon etc.
MOV SI,AX
MOV CX,3
REP MOVSB
LOOP_DATE: DEC BP
JZ STORE_DATE_END
MOV DI,OFFSET APP_DATE ;Appointment screen.
CMP BP,2
JZ NEXT_DATE
MOV DI,OFFSET PUR_DATE ;Purge window.
JMP NEXT_DATE
STORE_DATE_END:RET
;----------------------------------------------;
STORE_NUM: MOV BX,10
XOR CX,CX ;Zero in counter.
NEXT_COUNT: CWD ;Zero in high half.
DIV BX ;Divide by ten.
ADD DL,"0" ;Convert to ASCII.
PUSH DX ;Save results.
INC CX ;Also increment count.
OR AX,AX ;Are we done?
JNZ NEXT_COUNT ;Continue until zero.
NEXT_NUMBER: POP AX ;Retrieve numbers.
STOSB
LOOP NEXT_NUMBER
RET
;----------------------------------------------;
SCAN_DAY: CALL SCAN_APP ;Find appointments.
MOV AL,DAY_CAL ;Store small box char.
CALL STORE_BLOCKS ; if appointment found.
MOV AX,YEAR_CAL
CMP AX,YEAR_TODAY
JNZ SCAN_DAY_END
MOV AX,DAY_MON_CAL
CMP AX,DAY_MON_TODAY
JNZ SCAN_DAY_END
CALL STORE_ALARMS ;Store alarm if today.
SCAN_DAY_END: RET
;----------------------------------------------;
SCAN_APP: PUSH BP
MOV SI,OFFSET WORK_SPACE ;Spaces in work space.
MOV AL,SPACE
MOV BP,OFFSET APP_LEFT_START ;Scan appointments for
MOV CX,2 ; non-space chars.
NEXT_COL: PUSH CX
MOV DX,APP_HEIGHT
NEXT_DAY: MOV DI,BP
MOV CX,APP_LEN
XOR AH,AH ;Mark with zero if no app.
REP SCASB
JZ TEMP_APP
INC AH ;Else, mark with one for alarm.
TEMP_APP: MOV [SI],AH
INC SI
ADD BP,APP_WIDTH
DEC DX
JNZ NEXT_DAY
MOV BP,OFFSET APP_RIGHT_START
POP CX
LOOP NEXT_COL
POP BP
RET
;----------------------------------------------;
; INPUT: AL = Calendar day.
STORE_BLOCKS: DEC AL
CBW
MOV CL,3
SHL AX,CL
MOV DI,AX
ADD DI,OFFSET APPOINTMENT_BLOCKS
MOV SI,OFFSET WORK_SPACE
MOV CX,2
NEXT_SET: PUSH CX
MOV DX,APP_HEIGHT / 2
NEXT_BLOCK2: LODSW
OR AX,AX
MOV AL,SPACE ;Store a space if no appointment.
JZ STORE_BLOCK
MOV AL,"■" ;Else, store little block char.
STORE_BLOCK: STOSB
DEC DX
JNZ NEXT_BLOCK2
ADD DI,(BLOCK_COUNT / 2) - 8
POP CX
LOOP NEXT_SET
RET
;----------------------------------------------;
ALARMS DB APP_HEIGHT * 2 DUP (0)
WORK_SPACE DB APP_HEIGHT * 2 DUP (?)
STORE_ALARMS: MOV SI,OFFSET WORK_SPACE ;Copy results of work space
MOV DI,OFFSET ALARMS ; into alarm array.
MOV CX,SIZE ALARMS / 2
REP MOVSW
RET
;----------------------------------------------;
TODAY_FLAG EQU 001B ;Data applies to today.
CAL_FLAG EQU 010B ;Data applies to calendar.
APP_FLAG EQU 100B ;Data applies to appointment.
READ_DATA: MOV DI,OFFSET APPOINTMENT_BLOCKS
MOV CX,BLOCK_COUNT / 2
MOV AX,SPACE SHL 8 + SPACE
REP STOSW ;Spaces in Appointment blocks.
MOV BP,APP_HEIGHT
MOV BX,APP_LEN
MOV DI,OFFSET APP_LEFT_START
NEXT_BLANK: MOV CX,BX
REP STOSB
ADD DI,APP_COL_SPACE
MOV CX,BX
REP STOSB ;Spaces in Appointment book.
ADD DI,APP_ROW_SPACE
DEC BP
JNZ NEXT_BLANK
MOV BP,3
MOV BX,NOTE_LEN
MOV DI,OFFSET NOTEPAD
NEXT_PAD: MOV CX,BX
REP STOSB ;Spaces in Notepad.
ADD DI,APP_NOTE_SPACE
DEC BP
JNZ NEXT_PAD
MOV DI,OFFSET ALARMS
MOV CX,SIZE ALARMS / 2
REP STOSW ;Spaces in Alarms.
CALL OPEN_TSR ;Open data file.
JBE READ_DONE ;If none, then done.
NEXT_READ: CALL READ_DATE ;Read the date.
JBE READ_FAILED
OR AX,AX
JZ READ_END
XOR BP,BP ;Initialize date flag.
CMP CX,YEAR_TODAY ;Data same day/month/year
JNZ CK_YEAR_CAL ; as today?
CMP DX,DAY_MON_TODAY
JNZ CK_YEAR_CAL
OR BP,TODAY_FLAG ;If yes, today.
CK_YEAR_CAL: CMP CX,YEAR_CAL ;Same as day/month/year
JNZ CK_READ ; as calendar?
CMP DX,DAY_MON_CAL
JNZ CK_MON_CAL
OR BP,CAL_FLAG OR APP_FLAG ;If yes, then applies to
JMP SHORT DO_READ ; calendar and appointment.
CK_MON_CAL: CMP DH,MONTH_CAL ;Same as month/year only.
JNZ CK_READ
OR BP,CAL_FLAG ;If yes, applies to calendar.
CK_READ: OR BP,BP ;Does data apply to anything?
JNZ DO_READ
CALL NEXT_RECORD ;If no, bump file pointer
JBE READ_FAILED ; to next record.
JMP NEXT_READ
DO_READ: CALL READ_APP ;If applies, read in the data.
JBE READ_FAILED
TEST BP,APP_FLAG ;Apply to appointments?
JZ CK_TODAY_CAL
CALL MOVE_APP ;If yes, move data into app.
CK_TODAY_CAL: CALL SCAN_DTA ;Scan the data for appointments.
TEST BP,TODAY_FLAG ;Does it apply to today.
JZ CK_MON_CAL2
CALL STORE_ALARMS ;If yes, store alarms.
CK_MON_CAL2: TEST BP,CAL_FLAG ;Apply to calendar?
JZ NEXT_READ
MOV AL,BYTE PTR DTA.DATE_BINARY + 2
CALL STORE_BLOCKS ;If yes, store small block chars.
JMP NEXT_READ
READ_END: MOV AH,3EH ;Close data file.
CALL INT21_IO
READ_DONE: RET
READ_FAIL: CALL FAILED ;If failed, display message.
RET
READ_FAILED: CALL FAILED_CLOSE
RET
;----------------------------------------------;
SCAN_DTA: PUSH BP
MOV SI,OFFSET WORK_SPACE
MOV AL,SPACE
MOV BP,OFFSET DTA.APPOINT_TEXT
MOV CX,2
NEXT_DTA: PUSH CX
MOV DX,APP_HEIGHT ;Scan data read off disk
NEXT_DTA2: MOV DI,BP ; and store a 1 in work space
MOV CX,APP_LEN ; if non-space char.
XOR AH,AH
REP SCASB
JZ TEMP_APP2
INC AH
TEMP_APP2: MOV [SI],AH
INC SI
ADD BP,APP_LEN * 2
DEC DX
JNZ NEXT_DTA2
MOV BP,OFFSET DTA.APPOINT_TEXT + APP_LEN
POP CX
LOOP NEXT_DTA
POP BP
RET
;----------------------------------------------;
MOVE_APP: MOV SI,OFFSET DTA.APPOINT_TEXT
MOV DI,OFFSET APP_LEFT_START
MOV DX,APP_HEIGHT
NEXT_MOVE: MOV CX,APP_LEN
REP MOVSB ;Move DTA appointments into
ADD DI,APP_COL_SPACE ; screen app. storage space.
MOV CX,APP_LEN
REP MOVSB
ADD DI,APP_ROW_SPACE
DEC DX
JNZ NEXT_MOVE
MOV SI,OFFSET DTA.NOTEPAD_TEXT
MOV DI,OFFSET NOTEPAD ;Move DTA Notepad into
MOV CX,NOTEPAD_LEN ; screen Notepad space.
REP MOVSB
RET
;***************************************************************************;
APP_BOUNDS STRUC ;Cursor bounds in 3 sections.
COL_HOME DB ?
COL_END DB ?
ROW_HOME DB ?
ROW_END DB ?
CURSOR DW ?
APP_BOUNDS ENDS
APP_LEFT EQU 0
APP_RIGHT EQU SIZE APP_BOUNDS
APP_NOTE EQU APP_RIGHT + SIZE APP_BOUNDS
APP_INDEX DW APP_LEFT ;Section index.
A_LEFT_CURSOR EQU 4 SHL 8 + 12 ;Home cursor positions.
A_RIGHT_CURSOR EQU 4 SHL 8 + 48
N_ROW EQU 4 + APP_HEIGHT + 1
A_NOTE_CURSOR EQU N_ROW SHL 8 + 4
BOUNDS LABEL BYTE
A_LEFT APP_BOUNDS < 12, 12 + APP_LEN, 4, 4 + APP_HEIGHT - 1, A_LEFT_CURSOR >
A_RIGHT APP_BOUNDS < 48, 48 + APP_LEN, 4, 4 + APP_HEIGHT - 1, A_RIGHT_CURSOR >
A_NOTE APP_BOUNDS <4,4+NOTE_LEN,N_ROW,4+APP_HEIGHT+NOTE_HEIGHT,A_NOTE_CURSOR >
APP_CURSOR LABEL WORD
APP_CUR_COL DB 12 ;Appointment cursor position.
APP_CUR_ROW DB 4
REPAINT_FLAG DB 1 ; 1 = Repaint the screen.
ATABLE DB 3CH, 3DH, 3EH, 3FH, 40H, 59H
DB 63H, 4BH, 4DH, 48H, 50H, 47H
DB 4FH, 49H, 51H, 84H, 76H, 77H
DB 75H, 73H, 74H, 0FH, 53H
ATABLE_LEN EQU $ - ATABLE
DW SAVE, ATODAY, PPURGE, PRINT, CLEAR, SHIFT_CLEAR
DW CTRL_CLEAR, ALEFT, ARIGHT, AUP, ADOWN, AHOME
DW AEND, APGUP, APGDN, ACTRLPGUP, ACTRLPGDN, ACTRLHOME
DW ACTRLEND, AHOME, AEND, SHIFT_TAB, ADEL
APPOINTMENT: CALL CK_DATE ;Check if date changed.
CMP REPAINT_FLAG,1
JNZ NEXT_APPOINT
MOV SI,OFFSET APP_MENU ;Display appointment screen.
CALL POP_MENU
MOV REPAINT_FLAG,0
NEXT_APPOINT: MOV DX,APP_CURSOR
CALL SET_CURSOR
CALL GETKEY ;Get a keystroke.
CMP EXIT_FLAG,1 ;Exit if hotkey.
JZ APPOINT_END
CMP AL,ESC_SCAN ;Back to calendar if Esc.
JNZ APP_DISPATCH
MOV STATE,OFFSET CALENDAR
JMP SHORT APPOINT_END
APP_DISPATCH: CALL CALC_APP_ADDR ;Get cursor bounds.
MOV BP,APP_INDEX
MOV BX,WORD PTR BOUNDS[BP]
OR AH,AH ;Extended scan code?
JZ GOT_DISPATCH
CALL CK_ENTRY
JMP NEXT_APPOINT
GOT_DISPATCH: MOV DI,OFFSET ATABLE
MOV CX,ATABLE_LEN
CALL DISPATCH
CMP EXIT_FLAG,1
JNZ APPOINTMENT
APPOINT_END: MOV REPAINT_FLAG,1 ;Flag to repaint screen.
CALL PERMANENT ;Check for changes.
CALL SCAN_DAY ;Scan appointments.
RET
;----------------------------------------------;
; INPUT: DX = Cursor address; OUTPUT: SI -> Start of row.
CALC_APP_ADDR: PUSH AX
MOV CX,DX
SUB CH,4 ;Appointments start on line 4.
MOV AX,APP_WIDTH
MUL CH
MOV SI,AX
XOR CH,CH
ADD SI,CX
ADD SI,OFFSET APP_START
POP AX
RET
;----------------------------------------------;
; INPUT: SI -> appointments; DX=Cursor; BP=APP_INDEX; BH=COL_END; BL=COL_HOME
CK_ENTRY: XCHG AH,AL
CMP AL,TAB ;Tab?
JNZ CK_BACKSPACE
SHIFT_TAB: MOV CX,SIZE APP_BOUNDS
MOV BX,BP ;Save index.
MOV AH,2
INT 16H ;Get shift status.
TEST AL,11B ;Left or Right Shift?
JZ DO_TAB
NEG CX
DO_TAB: MOV AX,APP_NOTE
ADD BP,CX
JNS CK_HIGH
MOV BP,AX
CK_HIGH: CMP BP,AX
JBE NEW_FIELD
XOR BP,BP
NEW_FIELD: MOV APP_INDEX,BP ;New section.
MOV BOUNDS.CURSOR[BX],DX ;Save current cursor position.
MOV DX,BOUNDS.CURSOR[BP] ;Get new cursor position.
JMP SHORT ENTRY_UPDATE
CK_BACKSPACE: CMP AL,8 ;Backspace?
JNZ CK_PAD_CR
CMP DL,BL
JZ ENTRY_END
DEC SI ;Move back one space.
DEC DL
MOV APP_CURSOR,DX ;Save cursor position.
CALL ADEL ;Delete that character.
JMP SHORT ENTRY_END
CK_PAD_CR: CMP AL,CR ;Carriage return?
JNZ APP_ASCII
MOV DL,BL
CMP DH,BOUNDS.ROW_END[BP] ;Move to start of line.
JZ ENTRY_UPDATE
INC DH ;Next row if not at bottom.
JMP SHORT ENTRY_UPDATE
APP_ASCII: CMP AL,SPACE ;Text character?
JB ENTRY_END
INC DL ;Next column.
SUB BH,DL
JC ENTRY_END ;If last column, ignore.
JZ STORE_APP ;If next to last column, store.
MOV CL,BH
XOR CH,CH
ADD SI,CX
MOV DI,SI
DEC SI
STD
REP MOVSB ;Else, insert mode so shove
CLD ; everything in front up one.
INC SI
STORE_APP: MOV [SI],AL ;Store character.
ENTRY_CHANGE: MOV MODIFY_FLAG,1 ;Flag that change made.
ENTRY_UPDATE: MOV APP_CURSOR,DX ;Store new cursor position.
CALL DISPLAY_LINE ;Display just that changed line
; for better response time.
ENTRY_END: RET
;----------------------------------------------;
ACTRLHOME: MOV DL,BL ;Beginning of field.
JMP SHORT DO_HOME
AHOME: CMP DL,BL ;Beginning of field unless
MOV DL,BL ; already there, then go
JNZ ENTRY_UPDATE ; to section home.
DO_HOME: MOV DH,BOUNDS.ROW_HOME[BP]
JMP ENTRY_UPDATE
ACTRLEND: MOV DL,BH ;End of field
JMP SHORT DO_END
AEND: CMP DL,BH ;End of field unless
MOV DL,BH ; already there, then go
JNZ ENTRY_UPDATE ; to section home.
DO_END: MOV DH,BOUNDS.ROW_END[BP]
JMP ENTRY_UPDATE
ALEFT: CMP DL,BL ;Left one space unless
JNZ DO_LEFT ; already column home.
CMP BP,APP_RIGHT
JNZ ENTRY_END
MOV APP_INDEX,APP_LEFT ;If in col home of right section,
SUB DL,6 ; go to left section.
DO_LEFT: DEC DL
JMP ENTRY_UPDATE
ARIGHT: INC DL ;Right on space unless
CMP DL,BH ; already column end.
JBE ENTRY_UPDATE
CMP BP,APP_LEFT
JNZ ENTRY_END
MOV APP_INDEX,APP_RIGHT ;If in col end of left section,
ADD DL,6 ; go to right section.
JMP ENTRY_UPDATE
ADOWN: CMP DH,BOUNDS.ROW_END[BP] ;If at bottom, go and not
JNZ UPDATE_DOWN ; in Notepad, go to Notepad.
CMP BP,APP_NOTE
JZ ENTRY_END
INC DH ;Else, next row.
MOV APP_INDEX,APP_NOTE
UPDATE_DOWN: INC DH
JMP ENTRY_UPDATE
AUP: CMP DH,BOUNDS.ROW_HOME[BP] ;If in top row of NotePad
JNZ DO_UP ; go to either left or right
CMP BP,APP_NOTE ; section.
JNZ ENTRY_END
DEC DH
MOV BP,APP_RIGHT
CMP DL,BOUNDS.COL_HOME[BP]
JAE UPDATE_UP
MOV BP,APP_LEFT
MOV BX,WORD PTR BOUNDS[BP]
CMP DL,BL
JA CK_UP_END
MOV DL,BL
JMP SHORT UPDATE_UP
CK_UP_END: CMP DL,BH
JBE UPDATE_UP
MOV DL,BH
UPDATE_UP: MOV APP_INDEX,BP
DO_UP: DEC DH ;Else, just decrement row.
JMP ENTRY_UPDATE
ADEL: SUB BH,DL ;Ignore if in last column.
MOV CL,BH
XOR CH,CH
JCXZ ADEL_END
MOV DI,SI
INC SI
REP MOVSB ;Else, move everything to right
CALL DISPLAY_LINE ; of cursor left one space.
MOV MODIFY_FLAG,1
ADEL_END: RET
;----------------------------------------------;
ATODAY: CALL PERMANENT
MOV SI,YEAR_TODAY
MOV YEAR_CAL,SI ;Go to today.
MOV DX,DAY_MON_TODAY
MOV DAY_MON_CAL,DX
JMP SHORT DAY_UPDATE
APGUP: CALL PERMANENT
PPGUP: MOV AL,-1 ;Previous day.
JMP SHORT CK_DAY
APGDN: CALL PERMANENT
PPGDN: MOV AL,1 ;Next day.
CK_DAY: MOV DL,DAY_CAL
ADD DL,AL
JZ NEW_MONTH
CMP DL,LAST_DAY ;Past month bounds?
JA NEW_MONTH
UPDATE_MONTH: MOV DAY_CAL,DL ;If no, OK.
JMP SHORT DAY_UPDATE
NEW_MONTH: MOV DAY_CAL,1 ;Move to new month.
JA PCTRLPGDN
MOV DAY_CAL,31
JMP SHORT PCTRLPGUP
ACTRLPGUP: CALL PERMANENT
PCTRLPGUP: MOV AX,-1 ;Previous month.
JMP SHORT CK_MONTH
ACTRLPGDN: CALL PERMANENT
PCTRLPGDN: MOV AX,1 ;Next month.
CK_MONTH: MOV DH,MONTH_CAL
ADD DH,AL
JNZ CK_MONTH2
MOV DH,12 ;Past year bounds?
JMP SHORT CK_YEAR2
CK_MONTH2: CMP DH,12
JBE UPDATE_MONTH2
MOV DH,1
JMP SHORT CK_YEAR2
UPDATE_MONTH2: MOV MONTH_CAL,DH
JMP SHORT DAY_UPDATE
CK_YEAR2: MOV MONTH_CAL,DH
MOV SI,YEAR_CAL
ADD SI,AX ;Next/Previous year.
CMP SI,10000
JAE DAY_END
CMP SI,1582 ;Check bounds.
JB DAY_END
MOV YEAR_CAL,SI
DAY_UPDATE: MOV REPAINT_FLAG,1 ;Repaint the screen.
DAY_END: RET
;----------------------------------------------;
CLEAR: MOV DL,BL
CALL CALC_APP_ADDR
MOV DI,SI ;Appointment address line.
MOV CX,BX
SUB CH,CL
MOV CL,CH
XOR CH,CH
MOV AL,SPACE ;Store spaces.
REP STOSB
MOV APP_CURSOR,DX ;Home cursor.
MOV BOUNDS.CURSOR[BP],DX
MOV APP_INDEX,BP
JMP SHORT F6_END
SHIFT_CLEAR: MOV CX,WORD PTR BOUNDS.ROW_HOME[BP]
MOV DH,CH
SUB CH,CL
MOV CL,CH
XOR CH,CH
INC CX
NEXT_SHIFT_F6: PUSH CX ;Rows in section.
CALL CLEAR ;Clear all rows.
DEC DH
POP CX
LOOP NEXT_SHIFT_F6
JMP SHORT F6_END
CTRL_CLEAR: MOV BP,APP_NOTE ;Start with Notepad.
MOV CX,3 ;Three sections to clear.
NEXT_CTRL_F6: PUSH CX
MOV BX,WORD PTR BOUNDS[BP]
CALL SHIFT_CLEAR ;Clear section.
SUB BP,SIZE APP_BOUNDS
POP CX
LOOP NEXT_CTRL_F6
F6_END: MOV MODIFY_FLAG,1
MOV REPAINT_FLAG,1
RET
;----------------------------------------------;
; INPUT: DX = Cursor position; SI -> Storage.
DISPLAY_LINE: MOV AL,DH
SUB AL,4 ;Calc start of app. line
MOV CX,APP_WIDTH ; to display.
MUL CL
MOV SI,AX
ADD SI,OFFSET APP_START
MOV AL,DH
XOR AH,AH
CALL CALC_ADDR ;Calc corresponding display
PUSH ES ; address.
MOV DX,STATUS_REG
MOV ES,VIDEO_SEG
MOV BH,COLOR.B
DEC CX
NEXT_LINE: LODSB ;Display line.
CALL WRITE_SCREEN
LOOP NEXT_LINE
POP ES
RET
;***************************************************************************;
DATEONLY_FLAG DB ? ; =1 if purge date only.
PTABLE DB 49H, 51H, 84H, 76H, 3DH
PTABLE_LEN EQU $ - PTABLE
DW PPGUP, PPGDN, PCTRLPGUP, PCTRLPGDN, CTODAY
PPURGE: PUSH YEAR_CAL ;Preserve calendar variables.
PUSH DAY_MON_CAL
PUSH YEAR_CUR
PUSH DAY_MON_CUR
CALL HIDE_CURSOR
NEXT_F4: CALL FIRSTDAY ;Store purge date in window.
MOV AX,1
CALL CALC_ADDR
ADD DI,9 * 2
MOV SI,OFFSET PURGE_MSG
CALL POP_WINDOW ;Display purge window.
CALL GETKEY ;Get a keystroke.
JC F4_DONE ;If Esc, exit.
MOV DATEONLY_FLAG,0 ;Assume F7.
CMP AL,F7_SCAN ;Is it F7?
JZ CK_APPEND
MOV DATEONLY_FLAG,1 ;Assume F8.
CMP AL,F8_SCAN ;Is it F8.
JZ CK_APPEND
MOV DI,OFFSET PTABLE
MOV CX,PTABLE_LEN
CALL DISPATCH
CMP EXIT_FLAG,1
JNZ NEXT_F4
JMP SHORT F4_DONE
F4_END: MOV REREAD_FLAG,1
F4_DONE: POP DAY_MON_CUR ;Restore calendar variables.
POP YEAR_CUR
POP DAY_MON_CAL
POP YEAR_CAL
CALL FIRSTDAY ;Fix calendar and appointment
MOV REPAINT_FLAG,1 ; ASCII date; repaint screen.
RET
;----------------------------------------------;
ARCHIVE_FLAG DB 0 ; =1 if archive.
READ_HANDLE DW -1 ;File handles.
WRITE_HANDLE DW -1
ARCHIVE_HANDLE DW -1
CK_APPEND: MOV ARCHIVE_FLAG,0 ;Reset archive flag.
MOV AX,6
CALL CALC_ADDR
ADD DI,40 * 2
MOV SI,OFFSET ARCHIVE_MSG
CALL POP_WINDOW ;Display archive window.
NEXT_APPEND: CALL GETKEY ;Get a keystroke.
JC F4_DONE
CMP AL,N_SCAN
JZ DO_PURGE
CMP AL,Y_SCAN ;"Y" = archive.
JNZ NEXT_APPEND
MOV ARCHIVE_FLAG,1
DO_PURGE: CALL OPEN_TSR ;Open data file for reading.
JBE F4_DONE ;If doesn't exist, nothing to
MOV READ_HANDLE,AX ; purge; else save handle.
MOV AX,8
CALL CALC_ADDR
ADD DI,50 * 2
MOV SI,OFFSET PURGING_MSG
CALL POP_WINDOW ;Display purging window.
CALL OPEN_TSR ;Open data file for writing.
JBE PURGE_FAIL
MOV WRITE_HANDLE,AX
CMP ARCHIVE_FLAG,1 ;Archive?
JNZ NEXT_PURGE
MOV DX,OFFSET ARCHIVE_PATH
CALL OPEN_TSR2 ;If yes, open archive file.
JA SAVE_HANDLE2
XOR CX,CX
MOV AH,3CH ;If doesn't exist, create one.
CALL INT21_IO
JBE PURGE_FAIL
SAVE_HANDLE2: MOV ARCHIVE_HANDLE,AX
MOV BX,AX
XOR CX,CX
XOR DX,DX
MOV AX,4202H ;Move to end of archive file
CALL INT21_IO ; to append.
JA NEXT_PURGE
PURGE_FAIL: CALL CLOSE_HANDLES ;Failure exit.
CALL FAILED
JMP F4_END
NEXT_PURGE: MOV CX,SIZE DATA_RECORD
MOV BX,READ_HANDLE
CALL READ_RECORD ;Read a record.
JBE PURGE_FAIL
OR AX,AX ;EOF?
JZ PURGE_DONE
CMP DATEONLY_FLAG,1 ;Purge everything < purge date.
JZ CK_THISDATE
CMP CX,YEAR_CAL ;If no, year < purge date?
JB CK_ARCHIVE ;If yes, purge.
JA WRITE_DATA ;If above write data back.
CMP DX,DAY_MON_CAL ;Else, day/month < purge date?
JB CK_ARCHIVE ;Is yes, purge.
JMP SHORT WRITE_DATA ;Else, write data back.
CK_THISDATE: CMP CX,YEAR_CAL ;Purge date only.
JNZ WRITE_DATA
CMP DX,DAY_MON_CAL
JZ CK_ARCHIVE
WRITE_DATA: MOV DX,OFFSET DTA ;Write data back to data file.
MOV CX,SIZE DATA_RECORD
MOV BX,WRITE_HANDLE
CALL WRITE_RECORD
JBE PURGE_FAIL
JMP NEXT_PURGE
CK_ARCHIVE: CMP ARCHIVE_FLAG,1 ;Archive?
JNZ NEXT_PURGE
CALL ARCHIVE
JBE PURGE_FAIL
JMP NEXT_PURGE
PURGE_DONE: MOV BX,WRITE_HANDLE
XOR CX,CX ;Truncate data file to
CALL WRITE_RECORD ; current pointer location.
JBE PURGE_FAIL
CALL CLOSE_HANDLES ;Close all file handles.
JMP F4_END
;----------------------------------------------;
CLOSE_HANDLES: MOV BX,READ_HANDLE
CALL CLOSE_SAVE
MOV BX,WRITE_HANDLE
CALL CLOSE_SAVE
MOV BX,ARCHIVE_HANDLE
CALL CLOSE_SAVE
RET
;----------------------------------------------;
ARCHIVE: MOV BX,ARCHIVE_HANDLE
MOV DX,OFFSET DTA.DATE_ASCII
MOV CX,SIZE DATE_ASCII ;Write date in ASCII.
CALL WRITE_RECORD
JBE ARCHIVE_END
MOV DTA,CR
MOV DTA + 1,LF
CALL WRITE_CRLF ;Add Carriage return/linefeed.
JBE ARCHIVE_END
MOV BP,APP_HEIGHT
MOV SI,OFFSET APP_START + 5
MOV DI,OFFSET DTA.APPOINT_TEXT
NEXT_ARCHIVE: MOV DX,SI
MOV CX,8
CALL WRITE_RECORD ;Write times.
JBE ARCHIVE_END
MOV DX,DI
MOV CX,APP_LEN
CALL WRITE_RECORD ;Write appointments.
JBE ARCHIVE_END
ADD SI,36
ADD DI,APP_LEN
MOV DX,SI
MOV CX,10
CALL WRITE_RECORD
JBE ARCHIVE_END
MOV DX,DI
MOV CX,APP_LEN
CALL WRITE_RECORD
JBE ARCHIVE_END
CALL WRITE_CRLF
JBE ARCHIVE_END
ADD SI,45
ADD DI,APP_LEN
DEC BP
JNZ NEXT_ARCHIVE
MOV BP,3
MOV SI,OFFSET DTA.NOTEPAD_TEXT
NEXT_NOTEPAD: MOV DX,SI
MOV CX,73
CALL WRITE_RECORD ;Write Notepad.
JBE ARCHIVE_END
CALL WRITE_CRLF
JBE ARCHIVE_END
ADD SI,APP_WIDTH
DEC BP
JNZ NEXT_NOTEPAD
OR AL,1 ;Indicate successful.
ARCHIVE_END: RET
WRITE_CRLF: MOV DX,OFFSET DTA
MOV CX,2
CALL WRITE_RECORD
RET
;***************************************************************************;
SETUP_CODES DB 10 DUP (0), 0 ;Printer setup codes.
PRINT: CALL HIDE_CURSOR
MOV AX,1
CALL CALC_ADDR
ADD DI,30 * 2
MOV SI,OFFSET PRINTER_NO
CALL POP_WINDOW ;Display printer selection window.
NF5_KEY: CALL GETKEY ;Get a keystroke.
JC NF5_END ;If Esc, exit.
XCHG AH,AL
SUB AL,"1" ;Adjust to binary.
JC NF5_KEY
CMP AL,1
JA NF5_KEY
MOV DL,AL
XOR DH,DH
PUSH DX
MOV SI,OFFSET CAL_MENU
CMP STATE,OFFSET CALENDAR
JZ FIX_SCREEN
MOV SI,OFFSET APP_MENU
FIX_SCREEN: CALL POP_MENU ;Fix screen cause that's where
; we're getting our data.
MOV AX,1
CALL CALC_ADDR ;Printing starts with row 1.
MOV BX,DI
MOV BP,24
POP DX
MOV SI,OFFSET SETUP_CODES ;Send printer setup codes.
NEXT_SETUP: LODSB
OR AL,AL
JZ NEXT_PRINT
CALL PRINT_IT
JNC NEXT_SETUP
JMP SHORT NF5_END
NEXT_PRINT: MOV SI,BX
MOV CX,80 ;80 characters/line.
NEXT_PRINT2: CALL GET_CHAR
CALL PRINT_IT
JC NF5_END
LOOP NEXT_PRINT2
CALL PRINT_CRLF
JC NF5_END
ADD BX,CRT_WIDTH
DEC BP
JNZ NEXT_PRINT
MOV CX,9
NEXT_CRLF: CALL PRINT_CRLF
JC NF5_END
LOOP NEXT_CRLF
NF5_END: MOV REPAINT_FLAG,1
RET
;----------------------------------------------;
IBM_FLAG DB 0 ; If 1, use line drawing chars.
IBM_CHARS DB "╞╡│╪┬─╤═╧■┌"
EPSON_CHARS DB "||||--===*+" ;Replacements for line chars.
CHARS_LEN EQU $ - EPSON_CHARS
PRINT_IT: PUSH AX
MOV AH,2
INT 17H
TEST AH,00101001B ;Printer ready?
JNZ NOT_READY
TEST AH,11111001B
JNZ DO_PRINT
NOT_READY: POP AX
CALL BEEP ;If no, beep and exit.
STC
JMP SHORT PRINT_END
DO_PRINT: POP AX
TEST AL,80H ;High bit char?
JZ DO_PRINT2 ;If no, print as is.
CMP IBM_FLAG,1 ;Else, print line drawing?
JZ DO_PRINT2 ;If yes, print as is.
MOV DI,OFFSET IBM_CHARS ;Else, replace with appropriate
PUSH CX ; text char.
MOV CX,CHARS_LEN
REPNZ SCASB
POP CX
ADD DI,CHARS_LEN - 1
MOV AL,[DI]
DO_PRINT2: XOR AH,AH ;Print via BIOS.
INT 17H
CLC
PRINT_END: RET
;----------------------------------------------;
PRINT_CRLF: MOV AL,CR
CALL PRINT_IT
JC CRLF_END
MOV AL,LF
CALL PRINT_IT
CRLF_END: RET
;**********************************************;
; S U B R O U T I N E S ;
;**********************************************;
;INPUT: AL = Scan code; DI -> Valid scan codes table; CX = length of table
DISPATCH: PUSH AX
PUSH DX
PUSH BP
MOV BP,CX
MOV DX,DI
ADD DX,CX
REPNZ SCASB ;Scan for match.
JZ DO_DISPATCH
POP BP
POP DX
JMP SHORT NO_DISPATCH
DO_DISPATCH: SUB BP,CX
DEC BP
SHL BP,1
ADD BP,DX
MOV DI,BP ;Calc address of subroutine.
POP BP
POP DX
CALL [DI] ;Process the command.
NO_DISPATCH: CLC
DISPATCH_END: POP AX
RET
;----------------------------------------------;
PERMANENT: CMP MODIFY_FLAG,1 ;Any changes been made?
JNZ PERMANENT_END ;If no, done.
MOV MODIFY_FLAG,0
CALL HIDE_CURSOR
MOV AX,10
CALL CALC_ADDR
ADD DI,25 * 2
MOV SI,OFFSET PERMANENT_MSG
CALL POP_WINDOW ;Display "Changes to disk?"
QUERY: CALL GETKEY
JC PERMANENT_END
CMP AL,N_SCAN
JZ PERMANENT_END
CMP AL,Y_SCAN ;If "Y", then save.
JNZ QUERY
JMP SHORT SAVE2
PERMANENT_END: RET
;----------------------------------------------;
SAVE: CALL SAVE2 ;Save appointments.
JC F2_END
MOV AX,1
CALL CALC_ADDR
ADD DI,2
MOV SI,OFFSET SAVED_MSG
CALL POP_WINDOW ;Display saved message.
CALL PAUSE ;Pause for keystroke.
F2_END: RET
SAVE2: MOV MODIFY_FLAG,0
CALL OPEN_TSR ;Open data file.
JNC SAVE_HANDLE
XOR CX,CX ;If doesn't exist, create one.
MOV AH,3CH
CALL INT21_IO
JBE FAILED
MOV BX,AX
SAVE_HANDLE: MOV BP,AX
CK_EOF: CALL READ_DATE ;Read date of data.
JBE FAILED_CLOSE
OR AX,AX ;EOF?
JNZ MATCH_DATE ;If yes, append data.
CALL GET_SPACE
JBE FAILED_CLOSE
CMP BX,2 ;At least two clusters free?
MOV BX,BP ;Retrieve file handle.
JAE ENOUGH_ROOM
CALL CLOSE_SAVE
MOV SI,OFFSET DISK_FULL_MSG
JMP SHORT FAILED_MSG
ENOUGH_ROOM: CALL WRITE_APP ;Write the data.
JMP SHORT CLOSE_SAVE ;Done.
MATCH_DATE: CMP CX,YEAR_CUR ;Date of data on disk
JNZ NO_MATCH ; match appointment date?
CMP DX,DAY_MON_CUR
JZ GOT_DATE ;If yes, write over.
NO_MATCH: CALL NEXT_RECORD ;Else, search next record.
JBE FAILED_CLOSE
JMP CK_EOF
GOT_DATE: CALL WRITE_APP2
JBE FAILED_CLOSE
CLOSE_SAVE: MOV AH,3EH ;Close data file.
CALL INT21_IO
MOV REPAINT_FLAG,1
SAVE_END: RET
FAILED_CLOSE: MOV AH,3EH
CALL INT21_IO
FAILED: MOV SI,OFFSET PERMANENT_FAIL
FAILED_MSG: MOV AX,10
CALL CALC_ADDR
ADD DI,25 * 2
CALL POP_WINDOW ;If failed, display failed
CALL FLUSH_KEY ; message.
XOR AH,AH ;Pause.
INT 16H
MOV REPAINT_FLAG,1
STC
RET
;----------------------------------------------;
OPEN_TSR: MOV DX,OFFSET DATA_PATH
OPEN_TSR2: MOV AX,3D02H ;Open data file for reading
CALL INT21_IO ; and writing.
MOV BX,AX ;Filehandle.
RET
;----------------------------------------------;
; OUTPUT: CX = Year; DH = Month; DL = Day
READ_DATE: MOV CX,SIZE DATE_BINARY
READ_RECORD: MOV DX,OFFSET DTA
MOV AH,3FH ;Read first four bytes.
CALL INT21_IO
MOV CX,WORD PTR DTA.DATE_BINARY ;Return binary date.
MOV DX,WORD PTR DTA.DATE_BINARY + 2
RET
;----------------------------------------------;
GET_SPACE: MOV DL,BYTE PTR DATA_PATH
SUB DL,"A" - 1
MOV AH,36H ;See if room to tack on app.
CALL INT21_IO
RET
;----------------------------------------------;
NEXT_RECORD: XOR CX,CX
MOV DX,SIZE DATE_ASCII+SIZE APPOINT_TEXT+SIZE NOTEPAD_TEXT
MOV AX,4201H
CALL INT21_IO ;Bump pointer to next record.
RET
;----------------------------------------------;
; OUTPUT: CY = 1 OR ZF = 1 if write failed.
WRITE_APP: MOV DX,OFFSET YEAR_CUR ;Write binary date.
MOV CX,SIZE DATE_BINARY
CALL WRITE_RECORD
JBE WRITE_APP_END
WRITE_APP2: MOV DX,OFFSET APP_DATE ;Write ASCII date.
MOV CX,DATE_LEN
CALL WRITE_RECORD
JBE WRITE_APP_END
MOV SI,APP_HEIGHT ;16 Rows.
MOV DI,OFFSET APP_LEFT_START
MOV CX,APP_LEN
NEXT_WRITE: MOV DX,DI
CALL WRITE_RECORD ;Write appointments.
JBE WRITE_APP_END
ADD DX,APP_LEN + APP_COL_SPACE
CALL WRITE_RECORD
JBE WRITE_APP_END
ADD DI,APP_WIDTH
DEC SI
JNZ NEXT_WRITE
WRITE_NOTEPAD: MOV DX,OFFSET NOTEPAD
MOV CX,NOTEPAD_LEN
CALL WRITE_RECORD ;Write Notepad.
JBE WRITE_APP_END
INC SI ;Flag as successful.
CLC
WRITE_APP_END: RET
WRITE_RECORD: MOV AH,40H ;DOS write.
CALL INT21_IO
RET
;----------------------------------------------;
READ_APP: MOV DX,OFFSET DTA.DATE_ASCII
MOV CX,SIZE DATE_ASCII+SIZE APPOINT_TEXT+SIZE NOTEPAD_TEXT
MOV AH,3FH
CALL INT21_IO ;Read data record.
RET
;----------------------------------------------;
BEEP: PUSH AX
PUSH BX
PUSH CX
PUSH DX
CALL SETUP_BELL
MOV CX,1 ;One timer tick.
CALL DELAY ;Wait till clock rolls over.
CALL TOGGLE_BELL
MOV CX,1 ;Number of seconds.
CALL DELAY ;Delay seconds.
CALL RESET_BELL
POP DX
POP CX
POP BX
POP AX
RET
SETUP_BELL: MOV BX,CS:NOTE
XOR AX,AX
MOV DX,12H ;120000h dividend constant.
DIV BX ; Divide to get
MOV BX,AX ; 8253 countdown.
CALL RESET_BELL
MOV AL,0B6H ;Channel 2 speaker functions.
OUT 43H,AL ;8253 Mode Control.
JMP $+2 ;IO delay.
MOV AX,BX ;Retrieve countdown.
OUT 42H,AL ;Channel 2 LSB.
JMP $+2
MOV AL,AH ;Channel 2 MSB.
OUT 42H,AL
RET
RESET_BELL: IN AL,PORT_B ;Port B.
AND AL,NOT 3 ;Turn off speaker.
JMP $+2
OUT PORT_B,AL
RET
FLIP_BELL: DEC BELL_CNT
JNZ TOGGLE_BELL
CALL RESET_BELL
CMP PROGRAM_STATUS,0 ;Are we popped up.
JNZ FLIP_END ;If yes, done.
CMP NOPOPUP_FLAG,1 ;User requested no-popup?
JZ FLIP_END ;If yes, done.
MOV POPUP_FLAG,1 ;Else, try to popup.
FLIP_END: MOV BELL_FLAG,0 ;Reset bell flag.
RET
TOGGLE_BELL: IN AL,PORT_B
XOR AL,3
JMP $+2
OUT PORT_B,AL
RET
;----------------------------------------------;
; INPUT: CX = 1/18 seconds. ;
DELAY: PUSH DS ;Preserve data segment.
MOV AX,40H ;Point to BIOS data segment.
MOV DS,AX
NEXT_TICK: MOV AX,DS:[6CH] ;Retrieve timer low.
NEXT_DELAY: MOV DX,DS:[6CH] ;Retrieve timer low.
CMP DX,AX ;Have we timed out?
JZ NEXT_DELAY ;If not, wait until timer tick.
LOOP NEXT_TICK
POP DS ;Restore data segment.
RET
;----------------------------------------------;
SAVE_SCREEN: XOR AX,AX ;Top left of screen.
CALL CALC_ADDR
MOV SI,AX
MOV DI,OFFSET WIN_SAVE ;Storage space.
MOV CX,25
MOV BP,80
CALL DO_SAVE ;Save entire screen.
RET
DO_SAVE: PUSH DS
MOV DX,STATUS_REG
MOV DS,VIDEO_SEG
NEXT_SAVE: PUSH CX
PUSH SI
MOV CX,BP
NEXT_SAVE2: IN AL,DX ;Get status.
RCR AL,1 ;Is it low?
JC NEXT_SAVE2 ;If not, wait until it is.
CLI ;No more interrupts.
HWAIT3: IN AL,DX ;Get status.
RCR AL,1 ;Is it high?
JNC HWAIT3 ;If no, wait until it is.
MOVSW
STI
LOOP NEXT_SAVE2
POP SI
ADD SI,CS:CRT_WIDTH ;Next row.
POP CX
LOOP NEXT_SAVE
POP DS
RET
;----------------------------------------------;
RESTORE_SCREEN:XOR AX,AX ;Top left of screen.
CALL CALC_ADDR
MOV SI,OFFSET WIN_SAVE
MOV CX,25
MOV BP,80
CALL DO_RESTORE ;Restore all 25 rows.
RET
DO_RESTORE: PUSH ES
MOV ES,VIDEO_SEG
MOV DX,STATUS_REG
NEXT_SCREEN: PUSH CX
PUSH DI
MOV CX,BP
NEXT_SCREEN2: IN AL,DX ;Get status.
RCR AL,1 ;Is it low?
JC NEXT_SCREEN2 ;If not, wait until it is.
CLI ;No more interrupts.
HWAIT2: IN AL,DX ;Get status.
RCR AL,1 ;Is it high?
JNC HWAIT2 ;If no, wait until it is.
MOVSW
STI
LOOP NEXT_SCREEN2
POP DI
ADD DI,CRT_WIDTH ;Next row.
POP CX
LOOP NEXT_SCREEN
POP ES
RET
;----------------------------------------------;
POP_MENU: XOR AX,AX ;Top left of screen.
CALL CALC_ADDR
;--------------------------------------------------------;
; INPUT: DI -> Video destination; SI -> Window to popup. ;
POP_WINDOW: PUSH ES
MOV DX,STATUS_REG
MOV ES,VIDEO_SEG
NEXT_POP: PUSH DI
NEXT_WIN: LODSB
CMP AL,7 ;Format code?
JBE CK_COMPRESS
CALL WRITE_SCREEN ;If no, write to screen.
JMP NEXT_WIN
CK_COMPRESS: DEC AL ;Character to repeat?
JNS CK_DROP
LODSW ;If yes, get count and char.
MOV CL,AL
XOR CH,CH
XCHG AL,AH
CALL REPEAT_CHAR ;Repeat to screen.
JMP NEXT_WIN
CK_DROP: DEC AL ;Transparent black attribute?
JNS CK_COLOR
MOV CX,2
CALL DROP_SHADE ;If yes, display drop shade.
JMP SHORT STRING_END
CK_COLOR: DEC AL ;New color?
JNS CK_STRING
LODSB
MOV BL,AL
XOR BH,BH
MOV BH,BYTE PTR COLOR[BX] ;If yes, look it up.
JMP NEXT_WIN
CK_STRING: DEC AL ;End of string?
JNS DROP_STRING
STRING_END: POP DI
ADD DI,CRT_WIDTH ;If yes, go to next line.
CMP BYTE PTR [SI],-1 ;End of window?
JNZ NEXT_POP ;If yes, done.
JMP SHORT POP_END
DROP_STRING: DEC AL ;Bottom drop shade?
JNS INSERT_DATE
LODSB ;Get length of window.
CBW
MOV CX,AX
ADD DI,4 ;Shift right 2 columns.
CALL DROP_SHADE
POP DI
JMP SHORT POP_END
INSERT_DATE: DEC AL ;Special date insert?
JNS INSERT_APP
CALL DATE_INSERT
JMP NEXT_WIN
INSERT_APP: DEC AL ;Special small char. insert?
JNS SKIP
CALL APP_INSERT
JMP NEXT_WIN
SKIP: LODSB ;Else, skip some characters.
XOR AH,AH
ADD DI,AX
JMP NEXT_WIN
POP_END: POP ES
RET
;----------------------------------------------;
DROP_SHADE: MOV BL,8
INC DI
ATTR: IN AL,DX ;Get status.
RCR AL,1 ;Is it low?
JC ATTR ;If not, wait until it is.
CLI ;No more interrupts.
HWAIT6: IN AL,DX ;Get status.
RCR AL,1 ;Is it high?
JNC HWAIT6 ;If no, wait until it is.
MOV AL,BL
STOSB
STI ;Interrupts back on.
INC DI
LOOP ATTR
RET
;----------------------------------------------;
DATE_INSERT: PUSH BX
MOV AH,WEEKDAY
INC DAY_COUNTER
MOV AL,DAY_COUNTER
CMP AL,AH ;Are we to first day of month?
JBE DO_BLANK ;If no, blanks.
SUB AL,AH
CBW
CMP AL,LAST_DAY ;Are we past last day of month?
JA DO_BLANK ;If yes, blanks.
CMP AL,DAY_TODAY
JNZ DO_DAY
MOV CL,MONTH_TODAY
CMP CL,MONTH_CAL
JNZ DO_DAY
MOV CX,YEAR_TODAY
CMP CX,YEAR_CAL
JNZ DO_DAY
MOV BH,COLOR.Y ;Use highlight color if today.
DO_DAY: CALL DECIMAL_ASCII ;Write the date.
JMP SHORT D_INSERT_END
DO_BLANK: MOV AL,SPACE
CALL WRITE_SCREEN
MOV AL,SPACE
CALL WRITE_SCREEN
D_INSERT_END: POP BX
RET
;----------------------------------------------;
APP_INSERT: PUSH BX
MOV BH,COLOR.A ;Assume small block color.
MOV AH,WEEKDAY
MOV BP,BLOCK_ROW ;Block row index (0 or 1).
INC BLOCK_COUNTER[BP] ;Block day counter.
MOV AL,BLOCK_COUNTER[BP]
CMP AL,AH ;If before first day, blanks.
JBE DO_BLANK2
PUSH AX ;Save block counter.
CBW
MOV CL,7
DIV CL
OR AH,AH
JNZ CALC_BLOCK
XOR BLOCK_ROW,1 ;Once a week flip block row index
CALC_BLOCK: POP AX
SUB AL,AH
CMP AL,LAST_DAY ;Past last day?
JA DO_BLANK2 ;If so, blanks.
CBW
CMP AL,DAY_CAL
JNZ CK_ROW
MOV BH,COLOR.C ;If today, user cursor color.
CK_ROW: MOV CL,3
DEC AX
SHL AX,CL ;Index * 8 into array.
TEST BP,1
JZ GOT_INDEX
ADD AX,BLOCK_COUNT / 2 ; + Array size / 2 for odd rows.
GOT_INDEX: MOV BP,AX
MOV CX,8
NEXT_BLOCK: MOV AL,APPOINTMENT_BLOCKS[BP]
CALL WRITE_SCREEN ;Write the 8 block chars.
INC BP
LOOP NEXT_BLOCK
JMP SHORT A_INSERT_END
DO_BLANK2: MOV AL,SPACE
MOV CX,8
CALL REPEAT_CHAR
A_INSERT_END: POP BX
RET
;----------------------------------------------;
CALC_ADDR: MUL CRT_WIDTH ;Address = row * screen width
ADD AX,CRT_START ; + start of screen buffer.
MOV DI,AX
RET
;----------------------------------------------;
;INPUT: CX=char count; AX=character
REPEAT_CHAR: PUSH AX
CALL WRITE_SCREEN
POP AX
LOOP REPEAT_CHAR
RET
;----------------------------------------------;
WRITE_SCREEN: MOV BL,AL ;Store character in BL.
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.
MOV AX,BX ;Retrieve character; now it's OK
STOSW ; to write to screen buffer.
STI ;Interrupts back on.
RET ;Return
;----------------------------------------------;
GET_CHAR: PUSH DS
PUSH DX
MOV DX,STATUS_REG
MOV DS,VIDEO_SEG
HORZ_RET2: IN AL,DX ;Get status.
RCR AL,1 ;Is it low?
JC HORZ_RET2 ;If not, wait until it is.
CLI ;No more interrupts.
HWAIT4: IN AL,DX ;Get status.
RCR AL,1 ;Is it high?
JNC HWAIT4 ;If no, wait until it is.
LODSW
STI
POP DX
POP DS
RET
;----------------------------------------------;
DECIMAL_ASCII: MOV CL,10
DIV CL
ADD AX,"0" SHL 8 + "0"
PUSH AX
CMP AL,"0"
JNZ DO_DECIMAL
MOV AL,SPACE
DO_DECIMAL: CALL WRITE_SCREEN
POP AX
MOV AL,AH
CALL WRITE_SCREEN
RET
;------------------------------------------------------------------------------
; OUTPUT: AL=scan code; AH=char; CY=1 if hotkey or Esc; EXIT_FLAG=1 if hotkey
GETKEY: MOV AH,1 ;Keystroke waiting?
INT 16H
JNZ GETKEY1
INT 28H ;DOS idle interrupt.
JMP GETKEY
GETKEY1: XOR AH,AH ;Get keystroke.
INT 16H
XCHG AL,AH
CMP AL,ESC_SCAN ;Esc key?
JZ EXIT_KEY
CK_HOT: CMP AL,HOT_KEY_SCAN ;Hotkey?
JNZ GETKEY_END
PUSH AX
MOV AH,2
INT 16H
TEST AL,HOT_SHIFT_KEY
POP AX
JZ GETKEY_END
MOV EXIT_FLAG,1
EXIT_KEY: STC
RET
GETKEY_END: CLC
RET
;----------------------------------------------;
FLUSH_IT: XOR AH,AH ;Flush keyboard buffer.
INT 16H
FLUSH_KEY: MOV AH,1
INT 16H
JNZ FLUSH_IT
RET
;----------------------------------------------;
PAUSE: MOV AH,1 ;Wait till keystroke in buffer.
INT 16H
JZ PAUSE
RET
;---------------------------------------------;
; Move BIOS video data into our data segment. ;
;---------------------------------------------;
GET_BIOS_DATA: PUSH DS
PUSH ES
MOV AX,40H ;BIOS data area.
MOV DS,AX
PUSH CS
POP ES
MOV SI,BIOS_ACTIVE_PAGE ;Start with active page.
MOV DI,OFFSET ACTIVE_PAGE
MOVSB ;Retrieve active page
MOVSW ; and address of 6845 port.
MOV SI,BIOS_CRT_MODE ;Retrieve CRT mode, CRT columns,
MOV CX,CRT_DATA_LENGTH ; CRT length, CRT start.
REP MOVSB
XOR BH,BH
MOV DL,24 ;Assume 24 logical rows.
MOV AX,1130H ;Get Information via BIOS.
INT 10H
STORE_ROWS: POP ES
POP DS
MOV CRT_ROWS,DL ;Store rows.
MOV BH,ACTIVE_PAGE
MOV AH,3
INT 10H ;Get Cursor mode.
MOV CURSOR_MODE,CX
MOV CURSOR_POS,DX
MOV DX,ADDR_6845
ADD DX,6
MOV STATUS_REG,DX
MOV AX,0B000H
CMP DX,3BAH
JZ STORE_VIDEO
ADD AX,800H
STORE_VIDEO: MOV VIDEO_SEG,AX ;Video address.
MOV AX,CRT_COLS
SHL AX,1
MOV CRT_WIDTH,AX ;CRT width = columns * 2.
RET
;------------------------------------------------------------------------------;
; Read port directly for programs like 123 that do not use BIOS to set address.;
;------------------------------------------------------------------------------;
GET_CUR_ADDR: MOV DX,ADDR_6845
MOV AL,14
OUT DX,AL
INC DX
IN AL,DX
MOV AH,AL
DEC DX
MOV AL,15
OUT DX,AL
INC DX
IN AL,DX
MOV CURSOR_ADDR,AX
RET
;----------------------------------------------;
HIDE_CURSOR: MOV DH,CRT_ROWS
INC DH
XOR DL,DL
SET_CURSOR: PUSH AX
MOV BH,ACTIVE_PAGE
MOV AH,2
INT 10H
POP AX
RET
;------------------------------------------------------------------------------
;IOSET vectors interrupts 1Bh, 23h and 24h to internal handlers. IORESET
;restores the original vector values.
;------------------------------------------------------------------------------
IOSET PROC NEAR
PUSH ES
MOV AX,351BH ;BIOS Ctrl break interrupt.
INT 21H
MOV OLD1B[0],BX
MOV OLD1B[2],ES
MOV DX,OFFSET IOEXIT ;Ignore.
MOV AX,251BH
INT 21H
MOV AX,3523H ;DOS Ctrl break interrupt.
INT 21H
MOV OLD23[0],BX
MOV OLD23[2],ES
MOV DX,OFFSET IOEXIT ;Ignore.
MOV AX,2523H
INT 21H
MOV AX,3524H ;Critical error interrupt.
INT 21H
MOV OLD24[0],BX
MOV OLD24[2],ES
MOV DX,OFFSET IOERR ;Install ours.
MOV AX,2524H
INT 21H
POP ES
RET
IOSET ENDP
;-------------------------------------------;
IORESET PROC NEAR
PUSH DS
MOV DX,OLD24[0]
MOV DS,OLD24[2]
MOV AX,2524H ;Restore Critical.
INT 21H
MOV DX,CS:OLD23[0]
MOV DS,CS:OLD23[2]
MOV AX,2523H ;Restore DOS Ctrl break.
INT 21H
MOV DX,CS:OLD1B[0]
MOV DS,CS:OLD1B[2]
MOV AX,251BH ;Restore BIOS Ctrl break.
INT 21H
POP DS
RET
IORESET ENDP
;------------------------------------------------------------------------------
;INITIALIZE prepares the program for residency.
;------------------------------------------------------------------------------
PATH_LEN EQU 100
DATA_PATH DB PATH_LEN DUP (0)
ARCHIVE_PATH DB PATH_LEN DUP (0)
DB (256 / 8) DUP ("STACK ")
OUR_STACK = $
ERR_SAVE DB (((ERR_WIDTH + 2) * 2) * (ERR_HEIGHT + 1)) DUP (?)
WIN_SAVE DB ((80 * 2) * 25) DUP (?)
RESIDENT_END = $
SYNTAX DB "Syntax: Schedule [/I] [/U] [/Hn] [/Pn...n] [/G] [/A] [/C] [/M] [/B]",CR,LF
DB "/I = Install Memory-resident",CR,LF
DB "/U = Uninstall",CR,LF
DB "/H = Hotkey assignment; Ctrl or Alt plus new hotkey",CR,LF
DB " eg. /H Ctrl Y or /H Alt 1; default is Alt C",CR,LF
DB "/P = Printer setup string in decimal; 10 maximum.",CR,LF
DB " eg. /P 27 40 115 66 is boldface on a LaserJet",CR,LF
DB "/G = Graphical characters in printing; Use appropriate /P",CR,LF
DB " setup for your printer to switch to IBM character set",CR,LF
DB "/A = Appointment pop up OFF",CR,LF
DB "/C = Chime OFF",CR,LF
DB "/M = Midnight update pop up OFF",CR,LF
DB "/B = Black and white attributes"
DB CR,LF,LF,"$"
CANT_FIND DB "Can't find Schedule.com",CR,LF
DB "Change to Schedule's directory before running.",CR,LF,"$"
NOT_INSTALLED DB "Schedule not installed",CR,LF,"$"
ALREADY_MSG DB "Schedule already installed",CR,LF,"$"
UNLOAD_MSG DB "Schedule can't be uninstalled",CR,LF
DB "Uninstall resident programs in reverse order",CR,LF,"$"
NOT_ENOUGH DB "Not enough memory to install Schedule",CR,LF,"$"
ALLOCATE_MSG DB "Memory allocation error",CR,LF,BELL,"$"
INSTALL_MSG DB "Installed",CR,LF,"$"
UNINSTALL_MSG DB "Uninstalled",CR,LF,"$"
TSR DB "SCHEDULE.COM",0
TSR_LEN EQU $ - TSR
ARCHIVE_FILE DB "SCHEDULE.ASC",0
ARC_LEN EQU $ - ARCHIVE_FILE
DATA_FILE DB "SCHEDULE.DAT",0
DAT_LEN EQU $ - DATA_FILE
CRITICAL_MSG DB "DOS Critical Error Flag not found",CR,LF
DB "Can't install",CR,LF,"$"
HOTKEY_MSG DB "Press $"
HOTKEY_MSG2 DB " to pop-up Schedule",CR,LF,"$"
SIDEKICK_MSG DB "SideKick must be loaded last.",CR,LF
DB "Uninstall SideKick and then install Schedule",CR,LF,"$"
WRONG_VERSION DB "Needs DOS 2.0 or later$"
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
INSTALL_FLAG DB 0 ; =1 if /I found.
UNINSTALL_FLAG DB 0 ; =1 if /U found.
ALT DB " Alt $"
CTRL DB "Ctrl $"
ALT_CAPS DB "ALT"
CTRL_CAPS DB "CTRL"
;----------------------------------------------;
INITIALIZE PROC NEAR
CLD ;All string operations forward.
MOV BX,OFFSET SIGNATURE ;Point to start of code.
NOT BYTE PTR [BX] ;Change a byte so no false match.
XOR DX,DX ;Start at segment zero.
MOV AX,CS ;Store our segment in AX.
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 DX,OFFSET SYNTAX ;And syntax.
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_SWITCH: LODSB
CMP AL,CR ;Is it carriage return?
JNZ CK_SPACE
JMP INSTALL ;If yes, done here.
CK_SPACE: CMP AL,SPACE ;If space or below, ignore.
JBE NEXT_SWITCH
CMP AL,"/" ;Is it a switch character?
JZ GET_SWITCH ;If yes, continue.
JMP TERMINATE ;Else, error; exit.
GET_SWITCH: LODSB
CMP AL,CR
JZ INSTALL
CMP AL,"U" ;Is it "U" ?
JNZ CK_I
MOV UNINSTALL_FLAG,1
CALL CK_INSTALLED ;Else, see if installed.
JZ NO_TSR
JMP UNINSTALL ;Else, uninstall.
NO_TSR: MOV DX,OFFSET NOT_INSTALLED ;If no, exit with error message.
JMP MSG_EXIT
CK_I: CMP AL,"I" ;Is it (I)nstall?
JNZ CK_G
MOV INSTALL_FLAG,1
JMP NEXT_SWITCH
CK_G: CMP AL,"G" ;Is it (G)raphic chars?
JNZ CK_P
MOV ES:IBM_FLAG,1
JMP NEXT_SWITCH
CK_P: CMP AL,"P" ;Is it (P)rinter setup codes?
JNZ CK_H
CALL GET_SETUP
JMP NEXT_SWITCH
CK_H: CMP AL,"H" ;Is it (H)otkey?
JNZ CK_C
CALL CHANGE_HOT
JMP NEXT_SWITCH
CK_C: CMP AL,"C" ;Is it (C)hime OFF?
JNZ CK_A
MOV ES:NOBELL_FLAG,1
JMP NEXT_SWITCH
CK_A: CMP AL,"A" ;Is it (A)ppointment popup OFF?
JNZ CK_M
MOV ES:NOPOPUP_FLAG,1
JMP NEXT_SWITCH
CK_M: CMP AL,"M" ;Is it (M)idnight rollover OFF?
JNZ CK_B
MOV ES:NOMIDNIGHT_FLAG,1
JMP NEXT_SWITCH
CK_B: CMP AL,"B" ;Is it (B)lack and white switch
JZ GOT_B
JMP TERMINATE
GOT_B: MOV ES:MONO_FLAG,1
JMP NEXT_SWITCH
;----------------------------------------------;
INSTALL: CALL CK_INSTALLED ;Check if already installed.
JZ CK_AVAILABLE ;If no, see if enough memory.
MOV DX,OFFSET ALREADY_MSG ;If yes, display "Already
CALL PRINT_STRING ; installed" and
CALL DISP_HOTKEY ; current hotkey.
XOR AL,AL ; EL = 0
JMP TERMINATE
;----------------------------------------------;
CK_AVAILABLE: MOV BX,OFFSET RESIDENT_END
ADD BX,15 ;Round up.
MOV CL,4
SHR BX,CL ;Convert to paragraphs.
MOV AH,4AH
INT 21H ;Allocate memory.
JNC 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
XCHG AL,AH
MOV DOS_VERSION,AX ; and save.
CMP AH,2
JAE GET_PATH
MOV DX,OFFSET WRONG_VERSION ;If not DOS 2 or above, exit.
JMP MSG_EXIT
GET_PATH: MOV DX,OFFSET TSR ;Open .COM file.
MOV AX,3D00H
INT 21H
JNC FOUND_PATH
NOT_FOUND: MOV DX,OFFSET CANT_FIND ;If can't find, exit
JMP MSG_EXIT ; with message.
FOUND_PATH: MOV BX,AX ;Close .COM file.
MOV AH,3EH
INT 21H
MOV AH,19H ;Get default drive.
INT 21H
ADD AL,"A"
MOV DI,OFFSET DATA_PATH
STOSB
MOV AL,":" ;Add delimiters.
STOSB
MOV AL,"\"
STOSB
MOV SI,DI
XOR DL,DL
MOV AH,47H ;Get default directory.
INT 21H
JC NOT_FOUND
FIND_END: LODSB ;Find end of path.
OR AL,AL
JNZ FIND_END
MOV DI,SI
DEC DI
MOV AL,"\" ;Add on delimiter if root.
CMP [DI-1],AL
JZ ADD_TSR
STOSB
ADD_TSR: PUSH DI
MOV SI,OFFSET DATA_FILE
MOV CX,TSR_LEN
REP MOVSB ;Tack on data filename.
MOV SI,OFFSET DATA_PATH
MOV DI,OFFSET ARCHIVE_PATH
MOV CX,PATH_LEN
REP MOVSB ;Make a copy.
POP DI
SUB DI,OFFSET DATA_PATH
ADD DI,OFFSET ARCHIVE_PATH
MOV SI,OFFSET ARCHIVE_FILE
MOV CX,ARC_LEN
REP MOVSB ;Make this one archive filename
GET_INDOS: MOV AH,34H ;Undocumented INDOS call.
INT 21H
MOV DOS_SEGMENT,ES
MOV INDOS_OFFSET,BX
MOV AX,3E80H ;CMP opcode
MOV CX,2000H ;Max search length
MOV DI,BX ;Look for Critical error address.
INIT4: REPNZ SCASW
JCXZ INIT5
CMP BYTE PTR ES:[DI+5],0BCH
JZ FOUND
JMP INIT4
INIT5: MOV CX,2000H
INC BX ;Odd addresses.
MOV DI,BX
INIT6: REPNZ SCASW
JCXZ NOTFOUND
CMP BYTE PTR ES:[DI+5],0BCH
JZ FOUND
JMP INIT6
NOTFOUND: MOV DX,OFFSET CRITICAL_MSG
JMP MSG_EXIT
FOUND: MOV AX,ES:[DI]
MOV ERRFLAG_OFFSET,AX
CMP INSTALL_FLAG,1 ;Install request?
JZ CK_SIDEKICK
MOV AH,0FH ;If no, change to text
INT 10H ; video mode if necessary.
CMP AL,2
JZ GET_VECTORS
CMP AL,7
JZ GET_VECTORS
CMP AL,3
JZ GET_VECTORS
MOV AX,3
INT 10H
JMP SHORT GET_VECTORS
CK_SIDEKICK: XOR AX,AX ;Check if SideKick installed.
MOV ES,AX
MOV ES,ES:[(9 * 4) + 2] ;INT 9 segment
CMP WORD PTR ES:[16CH],"KS" ;SideKick signature.
JNZ GET_VECTORS
MOV DX,OFFSET SIDEKICK_MSG
JMP MSG_EXIT
GET_VECTORS: PUSH CS
POP ES
CALL UPDATE_DATE ;Update calendar variables.
MOV YEAR_CAL,CX
MOV DAY_MON_CAL,DX
CALL GET_BIOS_DATA ;Get BIOS variables.
CALL CK_DATE
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
MOV AX,3510H ;Get INT 10 interrupt.
INT 21H
MOV OLD10[0],BX ;Save old interrupt.
MOV OLD10[2],ES
MOV DX,OFFSET VIDEO ;Install new interrupt.
MOV AX,2510H
INT 21H
MOV AX,3513H ;Get INT 13 vector.
INT 21H
MOV OLD13[0],BX ;Save old interrupt.
MOV OLD13[2],ES
MOV DX,OFFSET BDISK ;Install new interrupt.
MOV AX,2513H
INT 21H
MOV AX,3528H ;Get INT 28 interrupt.
INT 21H
MOV OLD28[0],BX ;Save old interrupt.
MOV OLD28[2],ES
MOV DX,OFFSET BACKPROC ;Install new interrupt.
MOV AX,2528H
INT 21H
CMP INSTALL_FLAG,1 ;Install request?
JZ INSTALL_INT9 ;If no, skip INT 9
CALL MAIN ; and just popup now.
JMP SHORT UNINSTALL ;Uninstall vectors on return.
STAY_RESIDENT: CALL PRINT_STRING
INSTALL_INT9: 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,OFFSET 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.
;-------------------------------------------------------------------;
; Exit. Return ERRORLEVEL code 0 if successful, 1 if unsuccessful. ;
;-------------------------------------------------------------------;
MSG_EXIT: CALL PRINT_STRING
ERROR_EXIT: MOV AL,1 ;ERRORLEVEL = 1.
TERMINATE: MOV AH,4CH ;Terminate.
INT 21H
;---------------------------------------------------;
; This subroutine uninstalls the resident TSR. ;
;---------------------------------------------------;
UNINSTALL: MOV CX,ES ;Save segment in CX.
DO_VECTORS: 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,3510H ;Get interrupt 10h.
INT 21H
CMP BX,OFFSET VIDEO ;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,3513H ;Get interrupt 13h.
INT 21H
CMP BX,OFFSET BDISK ;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,3528H ;Get interrupt 28h.
INT 21H
CMP BX,OFFSET BACKPROC ;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
CMP UNINSTALL_FLAG,1 ;Uninstall request?
JNZ DO_UNINSTALL ;If no, skip INT 9.
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 DEALLOCATE ;If no, exit with error message.
UNINSTALL_ERR: MOV DX,OFFSET UNLOAD_MSG ;And exit with error message.
CMP UNINSTALL_FLAG,1 ;Uninstall request?
JZ LILLY_ERR ;If yes, just exit.
JMP STAY_RESIDENT ;Else, go resident.
LILLY_ERR: JMP MSG_EXIT
DEALLOCATE: MOV AH,49H ;Return memory to system pool.
INT 21H
MOV DX,OFFSET ALLOCATE_MSG
JC LILLY_ERR ;Display message if problem.
MOV DX,ES:OLD9[0] ;Restore old INT 9.
MOV DS,ES:OLD9[2]
MOV AX,2509H
INT 21H
DO_UNINSTALL: MOV DX,ES:OLD8[0] ;Restore old INT 8.
MOV DS,ES:OLD8[2]
MOV AX,2508H
INT 21H
MOV DX,ES:OLD10[0] ;Restore old INT 10.
MOV DS,ES:OLD10[2]
MOV AX,2510H
INT 21H
MOV DX,ES:OLD13[0] ;Restore old INT 13.
MOV DS,ES:OLD13[2]
MOV AX,2513H
INT 21H
MOV DX,ES:OLD28[0] ;Restore old INT 28.
MOV DS,ES:OLD28[2]
MOV AX,2528H
INT 21H
PUSH CS
POP DS ;Point to our data.
CMP UNINSTALL_FLAG,1
JNZ UNINSTALL_END
MOV DX,OFFSET UNINSTALL_MSG ;Display uninstall message.
CALL PRINT_STRING
UNINSTALL_END: XOR AL,AL ;Exit with ERRORLEVEL = 0.
JMP TERMINATE
INITIALIZE ENDP
;-------------------------------------------------------;
; OUTPUT: ZR = 1 if not installed; ZR = 0 if installed. ;
;-------------------------------------------------------;
CK_INSTALLED: MOV AX,ES
MOV BX,CS
CMP AX,BX ;Compare segments.
RET
;----------------------------------------------;
CHANGE_HOT: MOV BP,ES ;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: MOV ES,BP
RET
;----------------------------------------------;
FIND_START: LODSB
CMP AL,CR
JZ START_END
CMP AL,SPACE
JBE FIND_START
START_END: DEC SI
RET
;----------------------------------------------;
GET_SETUP: MOV DI,OFFSET SETUP_CODES ;Printer setup code storage.
MOV BP,10 ;Ten codes maximum.
NEXT_SETUP2: CALL FIND_START ;Find parameter.
CMP AL,CR
JZ SETUP_END
CMP AL,"/"
JZ SETUP_END
XOR BL,BL
MOV CL,10
NEXT_DECIMAL: LODSB ;Get a character.
CMP AL,"/"
JZ STORE_SETUP
CMP AL,SPACE
JBE STORE_SETUP
SUB AL,"0" ;ASCII to binary.
JC SETUP_END ;If not between 0 and 9, skip.
CMP AL,9
JA SETUP_END
XCHG AL,BL ;Swap old and new number.
MUL CL ; last entry by ten.
ADD BL,AL ;Add new number and store in BX.
JMP NEXT_DECIMAL
STORE_SETUP: MOV AL,BL
STOSB ;Store printer code.
DEC SI
DEC BP
JNZ NEXT_SETUP2
SETUP_END: XOR AL,AL ;NULL terminate.
STOSB
RET
;----------------------------------------------;
DISP_HOTKEY: 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
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
_TEXT ENDS
END START