home *** CD-ROM | disk | FTP | other *** search
- ;TIMEKEY.COM for the IBM Personal Computer - 1986 by Jeff Prosise
- ;
- kb_data equ 60h ;keyboard data port
- kb_ctrl equ 61h ;keyboard control port
- l_key equ 38 ;scan code for 'L' key
- s_key equ 31 ;scan code for 'S' key
- t_key equ 20 ;scan code for 'T' key
- alt_key equ 8 ;shift code for Alt key
- eoi equ 20h ;EOI value
- int_ctrl_port equ 20h ;8259 port address
- ;
- bios_data segment at 40h ;BIOS data area
- org 1Ah
- buffer_head dw ? ;head of keyboard buffer
- buffer_tail dw ? ;tail of keyboard buffer
- org 80h
- buffer_start dw ? ;starting keyboard buffer address
- buffer_end dw ? ;ending keyboard buffer address
- bios_data ends
- ;
- code segment para public 'code'
- assume cs:code
- org 100h
- begin: jmp init1
- ;
- copyright db "TIMEKEY (C) 1986, Ziff-Davis Publishing Co.",1Ah
- author db "programmed by Jeff Prosise",1Ah
- long_date db 19 dup (?) ;long date string buffer
- short_date db 9 dup (?) ;short date string buffer
- time_buffer db 11 dup (?) ;time string buffer
- hour db ? ;current hour count
- minutes db ? ;current minutes count
- buffer_flag db 0 ;status of output routine
- buffer_ptr dw ? ;index into output buffer
- time_text db 32,'X.M.',0 ;AM/PM designator
- old_int_9 label dword ;old interrupt 9 vector
- old_keyboard_int dw 2 dup (?)
- old_int_1Ch label dword ;old interrupt 1Ch vector
- old_timer_int dw 2 dup (?)
- ;
- ;------------------------------------------------------------------------------
- ;This routine will be executed at every tick of the time-of-day clock.
- ;------------------------------------------------------------------------------
- timer_int proc near
- pushf ;push flags to simulate INT
- call old_int_1Ch ;call original timer routine
- cmp buffer_flag,0 ;anything waiting to be output?
- je timer_exit ;no, then exit
- push ax ;save registers that will be used
- push bx
- push dx
- push si
- call fill_buffer ;output to keyboard buffer
- pop si ;restore register values
- pop dx
- pop bx
- pop ax
- timer_exit: iret ;exit and enable interrupts
- timer_int endp
- ;
- ;------------------------------------------------------------------------------
- ;Execution will come here every time a key is pressed or released.
- ;------------------------------------------------------------------------------
- kb_int proc near
- sti ;enable interrupts
- push ax ;save AX
- mov ah,2 ;get status of shift keys
- int 16h
- test al,alt_key ;is the Alt key depressed?
- je kb_exit ;no, then exit to normal handler
- in al,kb_data ;get scan code
- cmp al,t_key ;is it the 'T' key?
- je kbint1 ;yes, then continue
- cmp al,l_key ;is it the 'L' key?
- je kbint1 ;yes, then continue
- cmp al,s_key ;is it the 'S' key?
- je kbint1 ;yes, then continue
- kb_exit: pop ax ;restore AX
- jmp old_int_9 ;goto normal int 9 handler
- ;
- ;One of the trigger key combinations was pressed. Save register values and
- ;reset the keyboard.
- ;
- kbint1: push bx ;save remaining registers
- push cx
- push dx
- push si
- push di
- push ds
- push es
- push ax ;save scan code
- in al,kb_ctrl ;get keyboard control byte
- mov ah,al ;save it in AH
- or al,80h ;set high bit
- out kb_ctrl,al ;send it to control port
- mov al,ah ;get original byte value
- out kb_ctrl,al ;OUT it to enable keyboard
- pop ax ;restore scan code
- push cs ;set DS and ES to code segment
- pop es
- push cs
- pop ds
- assume ds:code
- cmp al,t_key ;Alt-T pressed?
- je kbint2 ;yes, then branch
- ;
- ;Alt-L or Alt-S was pressed. Set BUFFER_PTR to proper date string.
- ;
- mov buffer_ptr,offset long_date ;set pointer for long date
- cmp al,l_key ;was Alt-L pressed?
- je kbint6 ;yes, then skip ahead
- mov buffer_ptr,offset short_date ;set pointer for short date
- jmp kbint6 ;goto output routine
- ;
- ;Alt-T was pressed. Convert time to ASCII form and set BUFFER_PTR.
- ;
- kbint2: mov ah,0 ;get clock count from BIOS
- int 1Ah ;time count in DX:CX
- mov ax,cx ;get high part of count (hours)
- mov bl,24 ;prepare BL for division
- div bl ;divide hours by 24
- mov hour,ah ;save remainder as current hour
- mov ax,60 ;now multiply DX by 60
- mul dx ;result in AX:DX
- mov minutes,dl ;save current count of minutes
- cld ;clear DF for string instructions
- lea di,time_buffer ;set buffer pointer
- mov al,hour ;get hour in AL
- cmp al,0 ;is the hour zero?
- jne kbint3 ;no
- add al,12 ;yes, then set it to 12
- kbint3: cmp al,12 ;hour > 12 ?
- jbe kbint4 ;no
- sub al,12 ;yes, then subtract 12
- kbint4: mov bl,30h ;set BL for call to BIN2ASC
- call bin2asc ;convert value to text form
- mov al,':' ;write colon to time string
- stosb
- mov al,minutes ;get minutes in AL
- mov bl,0 ;print leading zeroes
- call bin2asc ;output minutes
- mov time_text[1],'P' ;set designator to 'P.M.'
- cmp hour,11 ;hour > 11 ?
- ja kbint5 ;yes, then leave it
- mov time_text[1],'A' ;no, then set it for 'A.M.'
- kbint5: lea si,time_text ;add designator to time string
- mov cx,6 ;six characters including ASCII 0
- rep movsb ;write them
- mov buffer_ptr,offset time_buffer ;set pointer for output
- ;
- ;Output buffer is indexed by variable BUFFER_PTR. Insert as much as possible
- ;of the indicated string into the keyboard buffer, then exit.
- ;
- kbint6: call fill_buffer ;output until done or buffer full
- cli ;disable interrupts
- mov al,eoi ;end interrupt
- out int_ctrl_port,al
- pop es ;restore saved register values
- pop ds
- pop di
- pop si
- pop dx
- pop cx
- pop bx
- pop ax
- iret ;exit
- kb_int endp
- ;
- ;------------------------------------------------------------------------------
- ;BIN2ASC converts a binary value less than 100 into its text equivalent.
- ;Entry: ES:DI - buffer address
- ; AL - byte value
- ; BL - 0 = print leading zeroes, 30h = suppress leading zeroes
- ;------------------------------------------------------------------------------
- bin2asc proc near
- aam ;convert AL to BCD value in AX
- add ax,3030h ;binary to ASCII
- cmp ah,bl ;suppress leading zeroes?
- je bin1 ;yes, then jump
- xchg ah,al ;get first digit in AL
- stosb ;print first digit
- xchg ah,al ;restore original value of AL
- bin1: stosb ;print second digit
- ret
- bin2asc endp
- ;
- ;------------------------------------------------------------------------------
- ;FILL_BUFFER outputs a string of bytes delimited by a zero byte directly to
- ;the keyboard buffer.
- ;Entry: BUFFER_PTR - offset address of next byte to transmit
- ;------------------------------------------------------------------------------
- fill_buffer proc near
- push ds ;save DS
- mov ax,bios_data ;then set it to BIOS data segment
- mov ds,ax
- assume ds:bios_data
- mov bx,buffer_tail ;get location of buffer tail
- mov si,buffer_ptr ;get value of buffer index
- xor ah,ah ;zero AH
- filbuf1: mov al,cs:[si] ;get next character to output
- or al,al ;is it zero?
- je done ;yes, then we're done
- mov dx,bx ;get buffer tail into DX
- add dx,2 ;increment to next location
- cmp dx,buffer_end ;do we need to wrap around?
- jne filbuf2 ;no, so jump
- mov dx,buffer_start ;wrap around to start address
- filbuf2: cmp dx,buffer_head ;head = tail ?
- je buffer_full ;yes, then buffer is full
- mov [bx],ax ;deposit character into buffer
- mov bx,dx ;advance keyboard buffer pointer
- mov buffer_tail,bx ;update BIOS record of buffer tail
- inc si ;advance index
- inc buffer_ptr ;advance output buffer pointer
- jmp filbuf1 ;loop back for more
- buffer_full: mov buffer_flag,1 ;set flag to indicate not done
- jmp done1
- done: mov buffer_flag,0 ;all done - set flag
- done1: pop ds ;restore DS
- assume ds:code
- ret
- fill_buffer endp
- ;
- ;------------------------------------------------------------------------------
- ;INITIALIZE routine places the string equivalents of the current date (long
- ;form and short form) into their respective storage areas and sets interrupt
- ;vectors to enable the resident portion of the code.
- ;------------------------------------------------------------------------------
- initialize proc near
- ;
- month_text db 7,'January ' ;text of month names
- db 8,'February '
- db 5,'March '
- db 5,'April '
- db 3,'May '
- db 4,'June '
- db 4,'July '
- db 6,'August '
- db 9,'September'
- db 7,'October '
- db 8,'November '
- db 8,'December '
- ;
- month db ? ;month number (1-12)
- day db ? ;day number (1-31)
- year db ? ;year number (0-99)
- text1 db ', 19' ;partial text of long date string
- ;
- ;Get the current month, day, and year from DOS and save the values.
- ;
- init1: mov ah,42 ;get system date from DOS
- int 21h
- sub cx,1900 ;subtract 1900 from year
- mov month,dh ;save month, day, and year
- mov day,dl
- mov year,cl
- cld ;clear DF for string operations
- ;
- ;Create the long date string in the LONG_DATE buffer.
- ;
- dec dh ;find table offset of month text
- mov al,10 ;set multiplier
- mul dh ;(month-1) * 10
- mov si,ax ;transfer table offset to SI
- add si,offset month_text ;complete offset address
- mov cl,[si] ;get length of month name
- inc si ;point SI to text of string
- xor ch,ch ;byte to word in CX
- lea di,long_date ;point DI to LONG_DATE area
- rep movsb ;transfer name of month to buffer
- mov al,32 ;add space character after month
- stosb
- mov al,dl ;get day in AL
- mov bl,30h ;suppress zeroes
- call bin2asc ;add day to the buffer
- lea si,text1 ;add ', 19' text
- mov cx,4 ;four characters to transfer
- rep movsb
- mov al,year ;get year in AL
- mov bl,0 ;print zeroes
- call bin2asc ;add it to the buffer
- mov al,0 ;terminate string with ASCII zero
- stosb
- ;
- ;Create the short date string in the SHORT_DATE buffer.
- ;
- lea di,short_date ;point DI to short date buffer
- mov al,month ;get month in AL
- mov bl,30h ;suppress zeroes
- call bin2asc ;write it to the buffer
- mov al,'-' ;add dash separator
- stosb
- mov al,day ;get day of month in AL
- mov bl,30h ;suppress zeroes
- call bin2asc ;write it to the buffer
- mov al,'-' ;add dash separator
- stosb
- mov al,year ;get year in AL
- mov bl,0 ;print zeroes
- call bin2asc ;write it to the buffer
- mov al,0 ;terminate string with ASCII zero
- stosb
- ;
- ;Save and set the interrupt 9 and 1Ch vectors to enable our new routines.
- ;
- mov ah,35h ;get interrupt 1Ch vector
- mov al,1Ch
- int 21h
- mov old_timer_int,bx ;save it
- mov old_timer_int[2],es
- mov ah,25h ;set interrupt 1Ch vector
- mov al,1Ch
- lea dx,timer_int ;point it to our timer routine
- int 21h
- mov ah,35h ;get old interrupt 9 vector
- mov al,9
- int 21h
- mov old_keyboard_int,bx ;save it
- mov old_keyboard_int[2],es
- mov ah,25h ;set interrupt 9 vector
- mov al,9
- lea dx,kb_int ;point to our keyboard handler
- int 21h
- lea dx,initialize ;point DX to end of resident code
- int 27h ;terminate-but-stay-resident
- initialize endp
- ;
- code ends
- end begin
-