; CALENDAR_TSR.MAR Paul Klissner 1990 ; ; The following program draws a one month calendar in the upper ; right hand corner of the screen whenever the user presses the CTRL/D ; key at the terminal. (The calendar display format is illustrated below). ; ; The time and current day of the month are highlighted on the ; screen for added information and impact. ; ; The code that follows creates a self-contained TSR. It consists of ; loader and unloader sections, as well as a calendar drawing procedure ; that gets relocated into P1 space. ; ; ; Example: ; ; ; Mar. 12:45 pm 1991 ; Su Mo Tu We Th Fr Sa ; 1 2 ; 3 4 5 6 7 8 9 ; 10 11 12 13 14 15 16 ; 17 18 19 20 21 22 23 ; 24 25 26 27 28 29 30 ; 31 ; ; ;--- .library 'sys$library:lib' .link 'sys$system:sys.stb' $lnmdef ; Modifiable parameter (specifies the TSR activation key): trap_key = ^a'D' - 64 ; (CTRL/D) ; The following macros create conditional branchs that have destinations ; more than 127 bytes away. .macro BGTRU_W dest,?next blequ next brw dest next: .endm .macro BNEQU_W dest,?next beqlu next brw dest next: .endm .macro BLBC_W base,dest,?next blbs base,next brw dest next: .endm .macro BBC_W pos,base,dest,?next bbs pos,base,next brw dest next: .endm memloc: .blkl ; Receives parse result memlen: .blkl ; Receives parse result lendescr: .long 4,hexlen ; Descriptor for parsing logical name locdescr: .long 8,hexloc ; Descriptor for parsing logical name ttname: .ascid 'TT' ; Descriptor for current terminal userchan: .blkl ; User mode channel to terminal loadmsg: .ascid 'Loaded CALENDAR, ' ; Residency message namedsc: .long 132,10$ ; 10$: .blkb 132 ; rmvname: .ascii 'Removing CALENDAR, previously ' rmvname_len=.-rmvname crlf: .byte 10,13 ; Carriage return/Linefeed bytes .entry MAIN,0 $assign_s devnam=ttname,- ; Assign user mode channel to terminal chan=userchan $qiow_s chan=userchan,- ; Jump down one line func=#io$_writevblk,- p1=crlf,- p2=#2 ; **** Now see if process TSR is loaded *** 1$: $trnlnm_s tabnam = lnm_table,- ;Check process logical name table to lognam = lnm_name,- ;see if this code is already resident. itmlst = lnm_list ;(we don't want to load it twice) blbs r0, 3$ ;branch if translation was successful $cmkrnl_s routin = loader ;Load code and table into p1 space blbc r0,2$ ; **** Display loaded message, if TSR was installed **** $qiow_s chan=userchan,- ;Display 'loaded' message func=#io$_writevblk,- p1=loadmsg+8,- p2=loadmsg $qiow_s chan=userchan,- ;Describe TSR memory location func=#io$_writevblk,- p1=lnm_equiv,- p2=#lnm_equiv_len+2 2$: $exit_s r0 3$: ; **** Display removal message, if TSR was previously installed **** $qiow_s chan=userchan,- ;Display removal message func=#io$_writevblk,- p1=rmvname,- p2=#rmvname_len $qiow_s chan=userchan,- ;Describe previous location func=#io$_writevblk,- p1=lnm_equiv,- p2=#lnm_equiv_len+2 ; *** PARSE LOGICAL NAME TO GET TSR MEMORY POINTER VALUES *** ; Get memory address pushl #4 ;# bytes to convert pushal memloc ;Address of integer longword pushal locdescr ;Address of text source calls #3,g^ots$cvt_tz_l ;Convert from hex text to integer blbc r0,exit2 ;branch if error ; Get memory size pushl #2 ;2 bytes to cvt pushal memlen ;Address of variable to convert to pushal lendescr ;hex text length to convert from calls #3,g^ots$cvt_tz_l ;convert from hex text to integer blbc r0,exit2 ;branch if error $cmkrnl_s routin = unloader ;Unload the TSR exit2: $exit_s r0 ;Exit with status ;-- ; The following routine executes in Kernel mode. It allocates memory ; for the TSR, and relocates the TSR it into P1 space. It then activates ; The TSR by calling it at non-AST level to initialize it. This routine ; also creates a process private logical name to describe the TSR. ;-- .entry LOADER,0 $assign_s devnam=ttname,- ;get kernal mode channel to terminal chan=ttkrnl 1$: movl #code_size,r1 ;Get the size to allocate jsb g^exe$alop1proc ;Get memory to last beyond ;the life of the image blbc r0,return ;Return if we couldn't get the memory. movl r1,ast_memory_size ;Save size of actual allocation movl r2,ast_vector ;Save the address of the region movc #code_size,- ;Copy ast code into buffer ast_code,- @ast_vector calls #0,@ast_vector ;Activate AST code ; **** Fill in residency logical name with address and length of TSR code **** pushl #4 ;4 bytes to convert pushl #8 ;8 hex digits, minimum. pushal locdescr ;address of target text pushal ast_vector ;source value calls #4,g^ots$cvt_l_tz ;convert blbc r0,return ;branch if error pushl #2 ;2 bytes to convert pushl #4 ;4 hex digits, minimum. pushal lendescr ;address of target text pushal ast_memory_size ;source value calls #4,g^ots$cvt_l_tz ;convert blbc r0,return ;branch if error $crelnm_s attr = lnm_attributes,- ;Create the Logical name tabnam = lnm_table,- lognam = lnm_name,- itmlst = lnm_list return: ret ;--- ; The following routine gets called in kernel mode to remove the TSR ; completely from the process. Before being called, the location and size ; of the TSR memory is saved at MEMLOC, and MEMLEN. The logical name ; describing the TSR is removed, and the kernel mode channel is deassigned. ; (Note that the channel number is reserved at the beginning of the located ; code for easy accessability). ;-- .entry UNLOADER,0 ; **** Unloads the TSR from P1 space, at non-ast level **** $dellnm_s tabnam=lnm_table,- ;Delete the logical name lognam=lnm_name movl memloc,r0 ;Get the address of the tsr code $dassgn_s chan = 5(r0) ;Remove the kernel mode channel movl memloc,r0 ;get the address of the tsr code again beqlu 10$ movzwl memlen,r1 ;get the length beqlu 10$ jsb g^exe$deap1 ;Deallocate the code 10$: ret ;and return ;--- ; Everything from this point forward comprises the resident code ; and data that is be relocated into P1 pool at runtime. Note ; that part of the initialization logic updates descriptors ; with the MOVAL instruction after the code is relocated ; so that all of the memory references reflect the new location ; of the TSR routine. ;--- TSR_CODE_ADDRESS = . AST_CODE:: ;IMPORTANT: This routine must preserve all registers .word ^M ; The following two lines must immediately follow the entry mask ; so that the unloader can retrieve that channel number the out-of-band ; key is assigned on. brw ttkrnl+4 ttkrnl: .blkl tstl init_flag ; Initialized yet? bnequ 1$ ; Branch if so jsb initialize ; Otherwise initialize AST code ret ; And exit ;******************************************************************; ; THIS CODE CREATES AND DISPLAYS A CALENDAR FOR THE CURRENT MONTH ; ;******************************************************************; 1$: movb #^x30,col ;Initialize display column moval col,outbuf+4 ;Get pointer to column text $qiow_s chan=ttkrnl,- ;Get terminal charactersitics func=#io$_sensemode,- p1=ttcharbuf,- p2=#12 blbc_w r0,astexit ;Branch if error bbc_w #tt2$v_avo,ttcharbuf+8,astexit ;Exit if TT doesn't have AVO extzv #16,#16,ttcharbuf,ttwidth ;Get the terminal width subl #25,ttwidth ;Subtract calendar width from ;terminal width setting cmpl ttwidth,#99 ;How many digits in width? bgtr 5$ ;Branch if greater than two addl #1,outbuf+4 ;Shift descriptor reference 5$: $fao_s ctrstr=faobuf,- ;Convert width to ASCII string outbuf=outbuf,- p1=ttwidth blbc_w r0,astexit ;Branch if error $qiow_s chan=ttkrnl,- func=#io$_writevblk,- p1=save_pos,- p2=#2 ; *** Fill in major components of the Calendar header *** movc5 #0,dda,#32,#126,dda ;Blank fill display array $asctim_s timbuf=systime_dsc ;Get current VMS time movc #11,systime,work_date+8 ;Copy the current date movw #^x3130,work_date+8 ;Adjust to be 1st of month movc #3,systime_mo , header_mo ;Move month into header bisw #^x2020,header_mo+1 ;2nd two digits to lower case movc #4,systime_year, header_year ;Move year into header movl feb29+4,r3 ;Get address of FEB 29 string movc #4,systime_year, 7(r3) ;Fill in year for Feb 29 check movc #5,systime_hhmm, header_hhmm ;Move in unconverted HH:MM $bintim_s timbuf=feb29,timadr=work_quad1;Check for validity blbc r0,7$ ;Branch if not a leap year movb #29,month_tbl+3 ;Make Feb. a 29 day month 7$: $bintim_s timbuf=work_date,- ;Get binary time for the timadr=work_quad1 ; first day of the month ;Calculate the day of week movq work_quad1,r0 ;Get the current day ediv #60*10*1000*1000,r0,r0,r1 ;Get the time in min. from 1858 clrl r1 ;(only 32 bits) ediv #24*60,r0,r0,r1 ;Time in days into r0 addl2 #2,r0 ;Nov. 17,1858 is a Wednesday clrl r1 ;Again only 32 bits ediv #7,r0,r0,r1 ;R0 = # of weeks from ref. time. ;R1 = Day of week addl3 #1,r1,dow ;Adjust and save day of week ; ***** DETERMINE # OF DAYS IN CURRENT MONTH ***** clrq r6 ;Clear search indicies moval months,r8 ;Addr of month name table 8$: cmpc #3,(r8)[r6],systime_mo ;Month name match? beql 9$ ;Branch if so addl #3,r6 ;Check next array entry aobleq #11,r7,8$ ;Check next 9$: movl month_tbl[r7],r3 ;Get # of days in month ;*** Fill the calendar display array with the day number for ; the month, with the first day positioned under the correct ; day of the week. moval dda,r8 ;Addr. of array movq #1,r6 ;Initialize day counter mull3 #3,dow,r2 ;Calc. initial offset in ;Day Display Array (DDA) 10$: clrq r4 ;Clear R4/R5 ediv #10,r6,r4,r5 ;Get digits of month day bisl #^x30,r4 ;Make high digit ASCII bisl #^x30,r5 ;Make low digit ASCII cmpl r4,#^x30 ;High digit 0? bnequ 20$ ;Branch if not movl #^x20,r4 ;Convert to a blank 20$: movb r4,(r8)[r2] ;Move into display array slot movb r5,1(r8)[r2] ;Save for second digit incl r6 ;Increment day of month addl #3,r2 ;Point to next stash slot sobgtr r3,10$ ;Loop till day count exhausted ;***** ADJUST DISPLAY TIME TO CIVILIAN FORMAT **** adjust_time: movc #3,amtext,header_ampm-1 ;Reset AM/PM text state bicw #^x3030,header_hhmm ;Cvt from ASCII HH to decimal values clrl r1 ;Zero the longword mulb3 header_hhmm,#10,r1 ;Get high order digit of hour*10 addb header_hhmm+1,r1 ;add in the low order digit HH ;(result is integer value of HH) cmpl r1,#12 ;Compare to the hour of noon beqlu 10$ ;Branch if noon hour now blssu 20$ ;Branch if before hour of noon subl #12,r1 ;Its afternoon, cvt ;from military time by ;subtracting 12 clrl r2 ;Clean up high order part ediv #10,r1,r1,r2 ;Get High order digit value ;into r1, and low order into r2 movb r1,header_hhmm ;Restore high order into ;its position in time buffer movb r2,header_hhmm+1 ;Restore low order into time ;buffer 10$: movb #^a"p",header_ampm ;Set to PM time 20$: bisw #^x3030,header_hhmm ;Convert HH to displayable ASCII cmpb header_hhmm,#^a"0" ;Leading 0? bnequ 30$ ;Branch if not movb #^a" ",header_hhmm ;If so, convert it to a blank 30$: clrl r5 ;Line index 40$: addb3 #49,r5,row ;Setup the display $qiow_s chan=ttkrnl,- ;Clear line and position cursor func=#io$_writevblk,- p1=clreol,- p2=#clreol_size movl display_array[r5],r2 ;Addr. of next line to display movl #20,r0 ;Assume it is a short line tstl r5 ;Is it the long line? bnequ 41$ ;Branch if not movl #27,r0 ;Correct the line length var. ;Display current line from table 41$: $qiow_s chan=ttkrnl,- ;Display the line from the array func=#io$_writevblk,- p1=(r2),- p2=r0 aoblss #9,r5,40$ ;Branch to display [next] line ; *************** Highlight Current Day of Month on Calendar ***************** ; ; Following algorithm used: ; ; = + ; = ( /7 ) + 3 ; = ( *3 ) + ttwidth + 3 ; movb systime_day,r1 ;Get high order digit (HOD) cmpb r1,#^x30 ;Digit 0? bneq 50$ ;Branch if not movb #^x20,systime_day ;Change it to a blank for later 50$: movb systime_day+1,r0 ;Get LOD bicl #^x30,r0 ;Convert to integer bicl #^x30,r1 ;Convert to integer mulb3 r1,#10,r1 ;high order digit * 10 addb r0,r1 ;+low order digit ;(yields highlight day as integer) addl dow,r1 ;add day of week offset, ;yeilding clrl r2 ;Prep. for ediv. clrq r3 ; . decl r1 ;Correct day of week offset ediv #7,r1,r3,r4 ;Divide by 7 addb3 #^x33,r3,csr_row ;Cursor Row is calculated decl r4 ;Remainder - 1 mull #3,r4 ;horiz. index x ;(display width for single day) addl ttwidth,r4 ;Add pos offset to display region addl #6,r4 ;Add pos offset within display region moval csr_col,outbuf+4 ;Setup descriptor so FAO moves literal ; in to terminal cursor control area cmpl r4,#100 ;r4 > 100? bgtr 60$ ;branch if so incl outbuf+4 ;If not shift right 60$: movb #^x30,csr_col ;Initialize with a leading 0 movl r4,scratch ;Save $fao_s ctrstr=faobuf,- ;Convert cursor location for esc. seq. outbuf=outbuf,- p1=scratch $qiow_s chan=ttkrnl,- ;Relocate the cursor to point to func=#io$_writevblk,- ;Current day of month and turn on p1=csr_ctl,- ;highlighting p2=#ctlsize $qiow_s chan=ttkrnl,- ;Display day of month func=#io$_writevblk,- p1=systime_day,- p2=#2 $qiow_s chan=ttkrnl,- ;Turn off highlighting func=#io$_writevblk,- p1=attrib_off,- p2=#3 $qiow_s chan=ttkrnl,- ;Restore cursor to its position func=#io$_writevblk,- ;before the calendar was displayed p1=restore_pos,- p2=#2 astexit: ret ;-- ; The following code initializes the TSR by enabling the out-of-band ; key on the kernel mode channel, relocating pointers, and displaying ; an initialization message on the terminal. ;-- INITIALIZE: ; **** Initializes the AST level TSR code **** incl init_flag $qiow_s chan=ttkrnl,- ;Setup out of band key func=#io$_setmode!io$m_outband,- p1=@ast_vector,- p2=#omask 10$: moval faobuf+8,faobuf+4 ;Relocate descriptors moval feb29+8,feb29+4 ; . moval work_date+8,work_date+4 ; . moval systime,systime_dsc+4 ; . moval hdr1,display_array ; . moval hdr2,display_array+4 ; . moval lin1,display_array+8 ; . moval lin2,display_array+12 ; . moval lin3,display_array+16 ; . moval lin4,display_array+20 ; . moval lin5,display_array+24 ; . moval lin6,display_array+28 ; . moval lin7,display_array+32 ; . moval lnm_table+8,lnm_table+4 ; . moval lnm_name+8,lnm_name+4 ; . moval lnm_equiv+8,lnm_equiv+4 ; . $qiow_s chan=ttkrnl,- ;Display message func=#io$_writevblk!io$m_breakthru,- p1 = install_msg,- p2 = #install_size rsb ; **** The following code is not used by this calendar, but ; serves as an example of how a TSR can remove itself, ; and the memory the it resides in without attempting to ; execute in deleted address space. ; ; It involves some special stack manipulation. ; ; $dellnm_s tabnam=lnm_table,- ;Delete the logical name ; lognam=lnm_name ; ; $dassgn_s chan = ttkrnl ;Remove the kernel mode channel ; movl ast_vector,r0 ;Get the address of the TSR memory ; movzwl ast_memory_size,r1 ;Get the length of the memory block ; ; ;The following line pushes two macro instructions on the stack ; pushl #^x045e04c0 ; ADDL #4,SP / RET ; ; movl sp,r7 ;Get stack pointer ; pushl r7 ;Save on stack ; jmp g^exe$deap1 ;Deallocate the code, ; ;and return onto the kernel mode ; ;stack. The instructions on the ; ;stack will clean themselves off ; ;of the stack, and return from the ; ;AST routine. ;----------------------------------------------------------- ; **** AST routine data areas **** hdr1: .ascii 'MMM. ' ;Calendar header .byte 27,^a'[',^a'1',^a'm' ;Turns on highlight .ascii 'HH:MM AM' ;Template for current time display .byte 27,^a'[',^a'm' ;Turns off highlight .ascii ' YYYY ' ;Raw Calendar display template hdr2: .ascii 'Su Mo Tu We Th Fr Sa ' ;2nd line of header dda: ;Day Display Array lin1: .ascii 'xx xx xx xx xx xx xx ' ; " lin2: .ascii 'xx xx xx xx xx xx xx ' ; " lin3: .ascii 'xx xx xx xx xx xx xx ' ; " lin4: .ascii 'xx xx xx xx xx xx xx ' ; " lin5: .ascii 'xx xx xx xx xx xx xx ' ; " lin6: .ascii 'xx xx xx xx xx xx xx ' ; " ;Blank line to reset attrib lin7: .byte 27,^a'[',^a'm',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;Vector for line by line output display_array: .long hdr1,hdr2,lin1,lin2,lin3,lin4,lin5,lin6,lin7 amtext: .ascii " am" ;Text to initialize am/pm display field ttwidth: .blkl ;Current terminal width - display compensation ttcharbuf: .blkl 3 ;Buffer to receive current terminal chars faobuf: .ascid '!ZB' ;Control string for FAO outbuf: .long 3,col ;Adjustable output descriptor for FAO ;Look up table for month names months: .ascii "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC" ;Parallel table containing # of days in months month_tbl: .long 31,28,31,30,31,30,31,31,30,31,30,31 ;Used to check existance of FEB 29 for year. feb29: .ascid '29-FEB-xxxx' ;Work Date field for intermediary calculations work_date: .ascid 'DD-MMM-YYYY' work_quad1: .blkq ;Quadword time for intermediary date calcs dow: .blkl ;Field to hold the Day of Week number scratch: .blkl ;Work field save_pos: .byte 27,^a'7' ;Control terminal to save cursor postion/attrib. restore_pos: .byte 27,^a'8' ;Control terminal to restore cursor/attrib. attrib_off: .byte 27,^a'[',^a'm' ;Turn of attrib. csr_ctl: .byte 27,^a'[' ;Move cursor and turn on highlight csr_row: .ascii "0;" csr_col: .byte ^x30,^x30,^x30,^a'H',27,^a'[',^a'1',^a';',^a'7',^a'm' ctlsize = .-csr_ctl clreol: .byte 27,^a'[' ;Move cursor, clear to EOL, and cancel highlight row: .ascii "0;" col: .byte ^x30,^x30,^x30,^a'H',27,^a'[',^a'0',^a'm' .byte 27,^a'[',^a'K',^x20,^x20,^x20 clreol_size = .-clreol systime_dsc: .long 23,systime_dsc+8 ;Descriptor for time text buffer systime: .blkb 23 ;Time text buffer ; Define named offsets header_mo = hdr1 ;Offset to month (MMM) text in header header_hhmm = hdr1+10 ;Offset to hour/minute in header header_ampm = hdr1+16 ;Offset to AM/PM indicator header_year = hdr1+23 ;Offset to year (YYYY) systime_day = systime ;Offset to day of month in sys-time systime_mo = systime+3 ;Offset to month of year systime_year = systime+7 ;Offset to year systime_hhmm = systime+12 ;Offset to time screen_clear_sequence_132: ;Clr. Screen and set width to 132 col. .byte 27,91,72,27,91,74,27,91,63,51,104 screen_clear_size_132 = .- screen_clear_sequence_132 screen_clear_sequence_80: ;Clr. Screen and set width to 132 col. .byte 27,91,72,27,91,74,27,91,63,51,108 screen_clear_size_80 = .- screen_clear_sequence_80 remove_msg: .byte 10,13 ;Removal message .ascii 'Removing Calendar from process' .byte 10,13 remove_size = .-remove_msg install_msg: .byte 10,13 ;Installation message .ascii 'TSR Activation key: ^D - Displays time/calendar' .byte 10,10,13 install_size = .-install_msg .blkb 20 init_flag: .blkl ;One-shot initialization ast_vector: .blkl ;Address of relocated code ast_memory_size: .blkl ;Size of relocated code omask: .long 0,<1@trap_key> ;Out of band key mask lnm_attributes: .long lnm_table: .ascid 'LNM$PROCESS_TABLE';Specify process private table lnm_name: .ascid '* CALENDAR [TSR]' ;Logical name lnm_equiv: .ascii 'resident at ' ;Parsable equivalence string hexloc: .ascii '00000000' ;Location of p1 memory .ascii ' (' ; hexlen: .ascii '0000' ;Size of p1 block .ascii ' bytes).' ; lnm_equiv_len = .-lnm_equiv ; .byte 10,13 ; lnm_list: .word 34,lnm$_string ;Logical name item list .long lnm_equiv,0,0 ; descr: .long 34,lnm_equiv ; code_size = .-tsr_code_address ;Calculates the size of the ;code that gets relocated .end main