home *** CD-ROM | disk | FTP | other *** search
- {TOADTIME.INC}
- (* Copyright (C) 1988 David P Kirschbaum All Rights Reserved *)
-
- (*
- A number of handy DOS system date/time, clock,
- and timer control routines.
- I found out the hard way that ATs don't do quite what we expect
- with DOS's date/time.
- Since I don't quite understand the problem, I'm whipping the AT
- and DOS about the head and shoulders, forcing the AT to check its
- own fancy clock and update poor old neglected DOS.
-
- Since DOS's system date/time are quite different from the date/time
- it uses for files, there's some procedures to fiddle them back
- and forth as well.
-
- I used this stuff to:
- (1) force file date/time to a date/time of my own selection.
- (2) get current system date/time in various formats.
- (3) time certain processes (and brag about my code's speed!).
- (4) have some handy global variables around for year, hour, etc.
-
- David Kirschbaum
- Toad Hall
- kirsch@braggvax.ARPA
- (and in the other world...
- 7573 Jennings Lane
- Fayetteville NC 28303 (the howling wilderness of computerdom)
- (919) 868-3471
- )
- *)
-
- (* The following are global variables these routines use.
- I prefer to use stuff like this, rather than fiddle with a bunch of
- local, relative to BP, variables in the actual functions.
- Much cleaner, tighter code.
- If you don't like the names and wanna change them, be my guest.
- However, you'll have to find and replace them throughout the
- inline assembler! (An exercise for the student...)
- *)
-
- VAR
- year,month,day,dow : BYTE; {global date variables}
- DateStr : STRING[9]; {global date string dd-mm-yy,
- plus one safety space}
- hour,min,sec,deci : BYTE; {global time variables}
- TimeStr : STRING[12]; {global time string hh:mm:ss.dd
- plus one safety space}
-
- LT1 : STRING[12]; {global string for time functions}
-
-
- (* Clock and timer control routines *)
-
- PROCEDURE ATClock;
- { GETCLOCK.ASM A program to read the date and time from the AT
- real time clock and set the DOS date to the real date.
- This is to compensate for DOS's failure to advance the date
- before running applications that require the correct date.
-
- Written by Mark S. Zinzow on January 17, 1987.
- Based on SETCLOCK.ASM by Arthur Rothstein as published in
- PC MAGAZINE, February 10, 1987, Vol. 6, #3, pp. 347-348
- }
- BEGIN
- Inline(
- $1E { push DS ;save DS}
- /$B8/$FF/$FF { mov ax,$0FFFF ;check the CPU model}
- {;machine id byte is at F000:FFFE, or FFFF:000E}
- /$8E/$D8 { mov DS,ax ;point DS for machine id byte}
- /$80/$3E
- /$0E/$00/$FC { cmp BYTE [$0E],$0FC ;is this an AT?}
- {;FC is AT, FF-PC, FE-XT or portable, FD-PCjr}
- /$75/$4F { jne Exit ; nope}
-
- /$B4/$04 { mov ah,04 ;BIOS get real date function}
- /$CD/$1A { int $1A ;call BIOS}
- /$91 { xchg cx,ax ;stage the register}
- /$E8/$2A/$00 { call BCDCnv ;convert BCD century & year to binary bytes}
- /$91 { xchg cx,ax ;store the result}
-
- /$88/$E8 { mov al,ch ;binary century to al}
- /$B3/$64 { mov bl,100 ;multiplier}
- /$F6/$E3 { mul bl ;century * 100 to add to year for full year}
- /$30/$ED { xor ch,ch ;clear century leaving only year in cl to add}
- /$01/$C1 { add cx,ax ;add century to year for complete binary year}
- /$92 { xchg dx,ax ;stage the register}
- /$E8/$1B/$00 { call BCDCnv ;convert BCD month (dh) and day (dl) to binary}
- /$92 { xchg dx,ax ;store the result}
- /$B4/$2B { mov ah,$2B ;DOS set date function}
- /$CD/$21 { int $21 ;call DOS}
- /$88/$C3 { mov bl,al ;store DOS return status code (0 means ok)}
-
- /$B4/$02 { mov ah,02 ;BIOS read real time clock function}
- /$CD/$1A { int $1A ;call BIOS}
- /$91 { xchg cx,ax ;stage the register}
- /$E8/$0C/$00 { call BCDCnv ;convert hour (ch) and minute (cl) to binary}
- /$91 { xchg cx,ax ;store the result}
- /$92 { xchg dx,ax ;stage the register}
- /$E8/$07/$00 { call BCDCnv ;convert seconds (dh) to binary (ignore dl)}
- /$92 { xchg dx,ax ;store the result}
- /$B4/$2D { mov ah,$2D ;DOS set time function}
- /$CD/$21 { int $21 ;call DOS}
- /$EB/$1D { jmp short Exit}
-
- {; Routine BCDCnv: Convert a binary coded decimal number to binary}
- {; Entry: Two BCD bytes, one in ah, the other in al}
- {; Exit: ah and al are each converted to binary.}
- {; Registers used: bl}
-
- {BCDCnv:}
- /$51 { push cx ;save cx a sec}
- /$B1/$04 { mov cl,4 ;for shifting}
- /$88/$E3 { mov bl,ah ;save the high order argument}
- /$30/$E4 { xor ah,ah ;clear the high byte}
- /$D3/$E0 { shl ax,cl ;move the high nibble into the high byte}
- /$D2/$E8 { shr al,cl ;restore the low nibble from the last shift}
- /$D5/$0A { aad ;convert nibbles to binary
- ;( ah * 10 + al --> al )}
- /$86/$D8 { xchg al,bl ;exchange low order result with high order arg}
- /$30/$E4 { xor ah,ah ;clear the high byte}
- /$D3/$E0 { shl ax,cl ;move the high nibble into the high byte}
- /$D2/$E8 { shr al,cl ;restore the low nibble from the last shift}
- /$D5/$0A { aad ;convert nibbles to binary
- ;( ah * 10 + al --> al )}
- /$88/$C4 { mov ah,al ;store high order result in high byte
- ;(OR ah,al ok too)}
- /$88/$D8 { mov al,bl ;retrieve low order result}
- /$59 { pop cx ;restore cx}
- /$C3 { ret ;return to caller}
-
- {Exit:}
- /$1F { pop DS ;restore DS}
- );
- END; {of ATClock}
-
-
- PROCEDURE Date;
- {Assumes global byte variables year, month, day, dow
- string variable DateStr (STRING[8])
- Updates the byte variables and the string with the
- current date/time.
- DateStr comes out as dd-mm-yy like DOS usually does it.
- (Would have preferred the military yymmdd, but sheer
- graciousness and good will made me consider you civilians!)
- Per Norton's:
- year = 0..99 (tens only, you figure what to do in 2000)
- month = 1..12
- day = 1..28, 29, 30, or 31 as appropriate
- dow = 0..6 (Sunday thru Saturday)
- }
- BEGIN
- ATClock; {If an AT, update DOS date/time}
- Inline(
- $B4/$2A { mov ah,$2A ;DOS get date svc}
- /$CD/$21 { int $21 ;cx=yr,dh=mo,dl=day,al=day of wk}
- /$81/$E9/$6C/$07 { sub cx,1900 ;year (only keep in 10's)}
- /$88/$0E/>YEAR { mov byte [>year],cl}
- /$88/$36/>MONTH { mov byte [>month],dh}
- /$88/$16/>DAY { mov byte [>day],dl}
- /$A2/>DOW { mov byte [>dow],al}
- /$8C/$D8 { mov ax,DS}
- /$8E/$C0 { mov ES,ax ;ES=DS}
- /$BF/>DATESTR { mov di,>DateStr ;ES:DI}
- /$FC { cld ;insure forward}
- /$B8/$08/$00 { mov ax,8 ;8 chars, clear msb}
- /$AA { stosb ;stuff length}
-
- /$B3/$0A { mov bl,10 ;div by 10}
- /$B7/$2D { mov bh,'-' ;divider}
- /$88/$D0 { mov al,dl ;day}
- /$E8/$0E/$00 { call DoDiv}
- /$88/$F0 { mov al,dh ;month}
- /$E8/$09/$00 { call DoDiv}
- /$B7/$00 { mov bh,0 ;won't hurt}
- /$88/$C8 { mov al,cl ;year}
- /$E8/$02/$00 { call DoDiv}
- /$EB/$0C { jmp short Exit ;done}
-
- {DoDiv:}
- /$F6/$F3 { div bl}
- {;quotient (10's) in al, remainder in ah}
- /$05/$30/$30 { add ax,'00' ;asciify}
- /$AB { stosw}
- /$88/$F8 { mov al,bh ;our separator}
- /$AA { stosb}
- /$31/$C0 { xor ax,ax ;clear msb again}
- /$C3 { ret}
- {Exit:}
- );
- END; {of Date}
-
-
- PROCEDURE Convert_SysDTG (VAR long : long_int);
- {Uses global year,mon,day,hour,min,sec,deci (filled by StartTimer),
- returns long_int suitable for file dtg or TimeStr/DateStr functions.
- Expects you to have already called Time to update those globals.
- }
- BEGIN
- (* In Turbo:
- long.lo := (hour ShL 11) OR (min * 32);
- long.hi :=
- ( (year-80) ShL 9) {we only keep year in 10's}
- OR (month ShL 5)
- OR day ;
- *)
- Inline(
- $C4/$BE/>LONG { les di,>long[bp] ;get long's vector}
- /$A1/>HOUR { mov ax,[>hour]}
- /$B1/$0B { mov cl,11}
- /$D3/$E0 { shl ax,cl}
- /$89/$C2 { mov dx,ax ;save in dx}
- /$A1/>MIN { mov ax,[>min]}
- /$B1/$05 { mov cl,5 ;*32}
- /$D3/$E0 { shl ax,cl}
- /$09/$D0 { or ax,dx}
- /$FC { cld}
- /$AB { stosw ;long.lo}
- /$A1/>MONTH { mov ax,[>month]}
- /$D3/$E0 { shl ax,cl ;shl 5}
- /$89/$C3 { mov bx,ax ;keep month in bx}
- /$A1/>YEAR { mov ax,[>year]}
- /$2D/$50/$00 { sub ax,80 ;keep year in 10's}
- /$B1/$09 { mov cl,9}
- /$D3/$E0 { shl ax,cl}
- /$89/$C2 { mov dx,ax ;keep yr in dx}
- /$A1/>DAY { mov ax,[>day] ;get day}
- /$09/$D8 { or ax,bx ;or in month}
- /$09/$D0 { or ax,dx ;or in year}
- /$AB { stosw ;long.hi}
- );
- END; {of Conv_SysDTG}
-
-
- PROCEDURE Get_File_DTG(VAR file_DTG : long_int);
- {Gets current date, updates a long integer VAR (somewhere
- out there) in DOS's format for a file date.
- I use this in certain records to keep date and time to
- the same formats as DOS uses in files.
- Updates the global date variables and DateString.
- }
- BEGIN
- Date; {update date variables}
- Convert_SysDTG(file_DTG); {convert global vars to a long int}
- END; {of Get_DTG}
-
-
- FUNCTION Long_DateStr(VAR dt : long_int) : Str8;
- {Using long_int dt (such as you'd get from a file's date.
- Notice only provides yrs in 10's.
- Returns a date string 00-00-00.
- Uses a global string LT1 (STRING[8]) as a working
- string for speed and tightness. Sorry 'bout that.
- This code came out of a REALLY tweaked program
- where I was really optimizing stuff.
- }
- BEGIN
- Inline(
- $C4/$BE/>DT { les di,>dt[bp] ;get parm ofs,seg}
- /$81/$C7/$02/$00 { add di,2 ;bump to hi word}
- /$26 { ES:}
- /$8B/$05 { mov ax,[di] ;snarf hi word}
- /$89/$C3 { mov bx,ax ;save here}
-
- /$B1/$05 { mov cl,5}
- /$D3/$E8 { shr ax,cl ;hi shr 5}
- /$24/$0F { and al,$0F ;mask for month}
- /$88/$C6 { mov dh,al ;dh=month}
-
- /$89/$D8 { mov ax,bx ;get hi word back}
- /$24/$1F { and al,$1F ;mask for day}
- /$88/$C2 { mov dl,al ;dl=day}
-
- /$89/$D8 { mov ax,bx ;get hi word back}
- /$B1/$09 { mov cl,9}
- /$D3/$E8 { shr ax,cl ;hi word shr 9}
- /$04/$50 { add al,80 ;add back years}
- /$88/$C1 { mov cl,al ;cl=year}
-
- /$8C/$D8 { mov ax,DS}
- /$8E/$C0 { mov ES,ax ;ES=DS}
- /$BF/>LT1 { mov di,>LT1 ;*** a global string ***}
- /$B8/$08/$00 { mov ax,8 ;8 chars, clear msb}
- /$FC { cld ;insure forward}
- /$AA { stosb ;stuff length}
-
- /$B3/$0A { mov bl,10 ;div by 10}
- /$B7/$2D { mov bh,'-' ;divider}
- /$88/$D0 { mov al,dl ;day}
- /$E8/$0E/$00 { call DoDiv}
- /$88/$F0 { mov al,dh ;month}
- /$E8/$09/$00 { call DoDiv}
- /$B7/$00 { mov bh,0 ;won't hurt}
- /$88/$C8 { mov al,cl ;year}
- /$E8/$02/$00 { call DoDiv}
- /$EB/$0C { jmp short Exit ;done}
-
- {DoDiv:}
- /$F6/$F3 { div bl}
- {;quotient (10's) in al, remainder in ah}
- /$05/$30/$30 { add ax,'00' ;asciify}
- /$AB { stosw}
- /$88/$F8 { mov al,bh ;our separator}
- /$AA { stosb}
- /$31/$C0 { xor ax,ax ;clear msb again}
- /$C3 { ret}
-
- {Exit:}
- );
- Long_DateStr := Copy(LT1,1,8); {clumsy way to return the string}
- END; {of Long_DateStr}
-
-
- FUNCTION Long_TimeStr(VAR dt : long_int) : Str5;
- {Using long_int dt (such as you'd get from a file's date.
- Notice only provides hours and minutes.
- Returns a 5-character time string 00:00
- Again, uses that global working string LT1 (STRING[8])
- for speed and simplicity.
- }
- BEGIN
- Inline(
- $C4/$BE/>DT { les di,>dt[bp] ;point to parm}
- /$26 { ES:}
- /$8B/$05 { mov ax,[di] ;grab lo word}
- /$89/$C3 { mov bx,ax ;save here}
- /$B1/$0B { mov cl,11}
- /$D3/$E8 { shr ax,cl}
- /$88/$C5 { mov ch,al ;save hrs}
- /$89/$D8 { mov ax,bx ;get lo word back}
- /$80/$E4/$07 { and ah,7 ;mask out hours}
- /$B1/$05 { mov cl,5 ;div 32}
- /$D3/$E8 { shr ax,cl}
- /$88/$C1 { mov cl,al ;save minutes}
-
- /$8C/$D8 { mov ax,DS}
- /$8E/$C0 { mov ES,ax ;ES=DS}
- /$BF/>LT1 { mov di,>LT1 ;*** a global string ***}
- /$B8/$05/$00 { mov ax,5 ;5 chars, clear msb}
- /$FC { cld ;insure forward}
- /$AA { stosb ;stuff length}
-
- /$B3/$0A { mov bl,10 ;div by 10}
- /$B7/$3A { mov bh,':' ;divider}
- /$88/$E8 { mov al,ch ;hours}
- /$E8/$09/$00 { call DoDiv}
- /$30/$FF { xor bh,bh ;0 for terminator}
- /$88/$C8 { mov al,cl ;minutes}
- /$E8/$02/$00 { call DoDiv}
- /$EB/$0C { jmp short Exit ;done}
-
- {DoDiv:}
- /$F6/$F3 { div bl}
- {;quotient (10's) in al, remainder in ah}
- /$05/$30/$30 { add ax,'00' ;asciify}
- /$AB { stosw}
- /$88/$F8 { mov al,bh ;our separator}
- /$AA { stosb}
- /$31/$C0 { xor ax,ax ;clear msb again}
- /$C3 { ret}
-
- {Exit:}
- );
- Long_TimeStr := Copy(LT1,1,5); {clumsy way to return string}
- END; {of Long_TimeStr}
-
-
- PROCEDURE Time;
- {Updates global time variables, global string TimeStr.
- Equivalent (for time) to Date.
- }
- BEGIN
- ATClock; {If an AT, update DOS date/time}
- Inline(
- $B4/$2C { mov ah,$2C ;DOS get time svc}
- /$CD/$21 { int $21}
- /$88/$2E/>HOUR { mov byte [>hour],ch ;update globals}
- /$88/$0E/>MIN { mov byte [>min],cl}
- /$88/$36/>SEC { mov byte [>sec],dh}
- /$88/$16/>deci { mov byte [>deci],dl}
- /$8C/$D8 { mov ax,DS}
- /$8E/$C0 { mov ES,ax ;ES=DS}
- /$BF/>TIMESTR { mov di,>TimeStr ;ES:DI}
- /$FC { cld ;insure forward}
- /$B8/$0B/$00 { mov ax,11 ;11 chars, clear msb}
- /$AA { stosb ;stuff length}
-
- /$B3/$0A { mov bl,10 ;div by 10}
- /$B7/$3A { mov bh,':' ;divider}
- /$88/$E8 { mov al,ch ;hours}
- /$E8/$15/$00 { call DoDiv}
- /$88/$C8 { mov al,cl ;minutes}
- /$E8/$10/$00 { call DoDiv}
- /$B7/$2E { mov bh,'.' ;now a decimal}
- /$88/$F0 { mov al,dh ;seconds}
- /$E8/$09/$00 { call DoDiv}
- /$B7/$00 { mov bh,0 ;won't hurt}
- /$88/$D0 { mov al,dl ;decisec}
- /$E8/$02/$00 { call DoDiv}
- /$EB/$0C { jmp short Exit ;done}
-
- {DoDiv:}
- /$F6/$F3 { div bl}
- {;quotient (10's) in al, remainder in ah}
- /$05/$30/$30 { add ax,'00' ;asciify}
- /$AB { stosw}
- /$88/$F8 { mov al,bh ;our separator}
- /$AA { stosb}
- /$31/$C0 { xor ax,ax ;clear msb again}
- /$C3 { ret}
- {Exit:}
- );
- END; {of Time}
-
-
- PROCEDURE StopTimer;
- {Reads current system time, readjusts hr,min,sec,deci to show the
- time expended since last StartTimer.
- Idea (maybe code too, I forget) was cribbed from a CRC test program
- by David Dantowitz.
-
- WATCH IT! This is great for stopclock functions, but remember
- the globals are all EXPENDED TIME! Don't use them for system
- time functions until you call Time again to reset them to
- system time!
- }
- BEGIN
- ATClock; {If an AT, update DOS date/time}
- Inline(
- $B4/$2C { mov ah,$2C ;get current time}
- /$CD/$21 { int $21}
- /$8A/$1E/>deci { mov bl,[>deci] ;old decisecond}
- /$8A/$3E/>SEC { mov bh,[>sec] ;old seconds}
- /$B4/$3C { mov ah,60 ;constant}
- /$88/$D0 { mov al,dl ;get fraction}
- /$28/$D8 { sub al,bl ;deci = dl-old deci}
- /$73/$04 { jnb S1 ;no carry, so not <0}
- /$04/$64 { add al,100 ;<0, adjust}
- /$FE/$C7 { inc bh ;bump old seconds}
- {S1:}
- /$88/$C2 { mov dl,al ;save adj. deci}
-
- /$8A/$1E/>MIN { mov bl,[>min] ;get old Minute}
- /$88/$F0 { mov al,dh ;get new seconds}
- /$28/$F8 { sub al,bh ;sec = new sec-old sec}
- /$73/$06 { jnb S2 ;not <0}
- /$00/$E0 { add al,ah ;<0, add back 60}
- /$FE/$C3 { inc bl ;old min=old min+1}
- /$EB/$08 { jmp short S3}
-
- {S2:}
- /$38/$E0 { cmp al,ah ;>=60?}
- /$72/$04 { jb S3 ; nope}
- /$28/$E0 { sub al,ah ; sec=sec-60}
- /$FE/$CB { dec bl ;old min=old min-1}
- {S3:}
- /$88/$C6 { mov dh,al ;save adj. seconds}
-
- /$8A/$3E/>HOUR { mov bh,[>hour] ;old hour}
- /$88/$C8 { mov al,cl ;get new minutes}
- /$28/$D8 { sub al,bl ;min=new min-old min}
- /$73/$06 { jnb S4 ; not below 0}
- /$00/$E0 { add al,ah ;min=min+60}
- /$FE/$C7 { inc bh ;old hour=old hour+1}
- /$EB/$08 { jmp short S5}
-
- {S4:}
- /$38/$E0 { cmp al,ah ;>=60?}
- /$72/$04 { jb S5 ; nope}
- /$28/$E0 { sub al,ah ;min=min-60}
- /$FE/$CF { dec bh ;old hour=old hour-1}
- {S5:}
- /$88/$C1 { mov cl,al ;save adj. minutes}
-
- /$88/$E8 { mov al,ch ;get new hour}
- /$28/$F8 { sub al,bh ;hour=new hour-old hour}
- /$88/$C5 { mov ch,al}
-
- /$8C/$D8 { mov ax,DS}
- /$8E/$C0 { mov ES,ax ;ES=DS}
- /$BF/>TIMESTR { mov di,>TimeStr ;ES:DI}
- /$FC { cld ;insure forward}
- /$B8/$0B/$00 { mov ax,11 ;11 chars, clear msb}
- /$AA { stosb ;stuff length}
-
- /$B3/$0A { mov bl,10 ;div by 10}
- /$B7/$3A { mov bh,':' ;divider}
- /$88/$E8 { mov al,ch ;hours}
- {23:}
- /$E8/$15/$00 { call DoDiv}
- /$88/$C8 { mov al,cl ;minutes}
- /$E8/$10/$00 { call DoDiv}
- {23:59:}
- /$B7/$2E { mov bh,'.' ;now a decimal}
- /$88/$F0 { mov al,dh ;seconds}
- /$E8/$09/$00 { call DoDiv}
- {23:59.59.}
- /$B7/$00 { mov bh,0 ;won't hurt}
- /$88/$D0 { mov al,dl ;deci}
- /$E8/$02/$00 { call DoDiv}
- {23:59.59.99}
- /$EB/$0C { jmp short Exit ;done}
-
- {DoDiv:}
- /$F6/$F3 { div bl}
- {;quotient (10's) in al, remainder in ah}
- /$05/$30/$30 { add ax,'00' ;asciify}
- /$AB { stosw}
- /$88/$F8 { mov al,bh ;our separator}
- /$AA { stosb}
- /$31/$C0 { xor ax,ax ;clear msb again}
- /$C3 { ret}
- {Exit:}
- );
- END; { of StopTimer }
-
-
- FUNCTION Now : Str8;
- {Assume you wish to time a process.
- Call Time to update the global time variables from system time.
- Then start your process.
- When completed, call Now.
- Now will return a string (hours, minutes, seconds, deciseconds)
- of the time expended since Time was called.
- The global string TimeStr contains the FULL time:
- hours, minutes, seconds, deciseconds, as hh:mm:ss.dd
- In this particular application, I didn't want the hours,
- so I've stripped them out. You could return the whole string
- if you wish by defining Now to return a Str11.
-
- WATCH IT! All the time globals are now loaded with that expended
- time. Be sure to call Time again to reset them to system time
- before using them.
- }
- BEGIN
- StopTimer; {reset globals to time expended}
- Now := Copy(TimeStr,4,8); {return string showing time expended:
- mm:ss.dd}
- END; {of Now}