home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS 1992 June / SIMTEL_0692.cdr / msdos / turbopas / toadlong.arc / TOADTIME.INC < prev   
Encoding:
Text File  |  1988-05-24  |  21.3 KB  |  540 lines

  1. {TOADTIME.INC}
  2. (* Copyright (C) 1988 David P Kirschbaum  All Rights Reserved *)
  3.  
  4. (*
  5.   A number of handy DOS system date/time, clock,
  6.   and timer control routines.
  7.   I found out the hard way that ATs don't do quite what we expect
  8.   with DOS's date/time.
  9.   Since I don't quite understand the problem, I'm whipping the AT
  10.   and DOS about the head and shoulders, forcing the AT to check its
  11.   own fancy clock and update poor old neglected DOS.
  12.  
  13.   Since DOS's system date/time are quite different from the date/time
  14.   it uses for files, there's some procedures to fiddle them back
  15.   and forth as well.
  16.  
  17.   I used this stuff to:
  18.     (1) force file date/time to a date/time of my own selection.
  19.     (2) get current system date/time in various formats.
  20.     (3) time certain processes (and brag about my code's speed!).
  21.     (4) have some handy global variables around for year, hour, etc.
  22.  
  23.   David Kirschbaum
  24.   Toad Hall
  25.   kirsch@braggvax.ARPA
  26.   (and in the other world...
  27.     7573 Jennings Lane
  28.     Fayetteville NC  28303 (the howling wilderness of computerdom)
  29.     (919) 868-3471
  30.   )
  31. *)
  32.  
  33. (* The following are global variables these routines use.
  34.    I prefer to use stuff like this, rather than fiddle with a bunch of
  35.    local, relative to BP, variables in the actual functions.
  36.    Much cleaner, tighter code.
  37.    If you don't like the names and wanna change them, be my guest.
  38.    However, you'll have to find and replace them throughout the
  39.    inline assembler!  (An exercise for the student...)
  40. *)
  41.  
  42. VAR
  43.   year,month,day,dow : BYTE;      {global date variables}
  44.   DateStr : STRING[9];            {global date string dd-mm-yy,
  45.                                    plus one safety space}
  46.   hour,min,sec,deci  : BYTE;      {global time variables}
  47.   TimeStr : STRING[12];           {global time string hh:mm:ss.dd
  48.                                    plus one safety space}
  49.  
  50.   LT1 : STRING[12];               {global string for time functions}
  51.  
  52.  
  53. (* Clock and timer control routines *)
  54.  
  55. PROCEDURE ATClock;
  56.   { GETCLOCK.ASM    A program to read the date and time from the AT
  57.     real time clock and set the DOS date to the real date.
  58.     This is to compensate for DOS's failure to advance the date
  59.     before running applications that require the correct date.
  60.  
  61.     Written by Mark S. Zinzow on January 17, 1987.
  62.     Based on SETCLOCK.ASM by Arthur Rothstein as published in
  63.     PC MAGAZINE, February 10, 1987, Vol. 6, #3, pp. 347-348
  64.   }
  65.   BEGIN
  66.     Inline(
  67.   $1E          {  push DS       ;save DS}
  68.   /$B8/$FF/$FF {  mov  ax,$0FFFF  ;check the CPU model}
  69.                {;machine id byte is at F000:FFFE, or FFFF:000E}
  70.   /$8E/$D8     {  mov  DS,ax    ;point DS for machine id byte}
  71.   /$80/$3E
  72.   /$0E/$00/$FC {  cmp  BYTE [$0E],$0FC ;is this an AT?}
  73.                {;FC is AT, FF-PC, FE-XT or portable, FD-PCjr}
  74.   /$75/$4F     {  jne  Exit     ; nope}
  75.  
  76.   /$B4/$04     {  mov  ah,04    ;BIOS get real date function}
  77.   /$CD/$1A     {  int  $1A      ;call BIOS}
  78.   /$91         {  xchg cx,ax    ;stage the register}
  79.   /$E8/$2A/$00 {  call BCDCnv   ;convert BCD century & year to binary bytes}
  80.   /$91         {  xchg cx,ax    ;store the result}
  81.  
  82.   /$88/$E8     {  mov  al,ch    ;binary century to al}
  83.   /$B3/$64     {  mov  bl,100   ;multiplier}
  84.   /$F6/$E3     {  mul  bl       ;century * 100 to add to year for full year}
  85.   /$30/$ED     {  xor  ch,ch    ;clear century leaving only year in cl to add}
  86.   /$01/$C1     {  add  cx,ax    ;add century to year for complete binary year}
  87.   /$92         {  xchg dx,ax    ;stage the register}
  88.   /$E8/$1B/$00 {  call BCDCnv   ;convert BCD month (dh) and day (dl) to binary}
  89.   /$92         {  xchg dx,ax    ;store the result}
  90.   /$B4/$2B     {  mov  ah,$2B   ;DOS set date function}
  91.   /$CD/$21     {  int  $21      ;call DOS}
  92.   /$88/$C3     {  mov  bl,al    ;store DOS return status code (0 means ok)}
  93.  
  94.   /$B4/$02     {  mov  ah,02    ;BIOS read real time clock function}
  95.   /$CD/$1A     {  int  $1A      ;call BIOS}
  96.   /$91         {  xchg cx,ax    ;stage the register}
  97.   /$E8/$0C/$00 {  call BCDCnv   ;convert hour (ch) and minute (cl) to binary}
  98.   /$91         {  xchg cx,ax    ;store the result}
  99.   /$92         {  xchg dx,ax    ;stage the register}
  100.   /$E8/$07/$00 {  call BCDCnv   ;convert seconds (dh) to binary (ignore dl)}
  101.   /$92         {  xchg dx,ax    ;store the result}
  102.   /$B4/$2D     {  mov  ah,$2D   ;DOS set time function}
  103.   /$CD/$21     {  int  $21      ;call DOS}
  104.   /$EB/$1D     {  jmp short Exit}
  105.  
  106. {;  Routine BCDCnv:  Convert a binary coded decimal number to binary}
  107. {;  Entry:  Two BCD bytes, one in ah, the other in al}
  108. {;  Exit:   ah and al are each converted to binary.}
  109. {;  Registers used:  bl}
  110.  
  111.                {BCDCnv:}
  112.   /$51         {  push cx       ;save cx a sec}
  113.   /$B1/$04     {  mov  cl,4     ;for shifting}
  114.   /$88/$E3     {  mov  bl,ah    ;save the high order argument}
  115.   /$30/$E4     {  xor  ah,ah    ;clear the high byte}
  116.   /$D3/$E0     {  shl  ax,cl    ;move the high nibble into the high byte}
  117.   /$D2/$E8     {  shr  al,cl    ;restore the low nibble from the last shift}
  118.   /$D5/$0A     {  aad           ;convert nibbles to binary
  119.                                 ;( ah * 10 + al --> al )}
  120.   /$86/$D8     {  xchg al,bl    ;exchange low order result with high order arg}
  121.   /$30/$E4     {  xor  ah,ah    ;clear the high byte}
  122.   /$D3/$E0     {  shl  ax,cl    ;move the high nibble into the high byte}
  123.   /$D2/$E8     {  shr  al,cl    ;restore the low nibble from the last shift}
  124.   /$D5/$0A     {  aad           ;convert nibbles to binary
  125.                                 ;( ah * 10 + al --> al )}
  126.   /$88/$C4     {  mov  ah,al    ;store high order result in high byte
  127.                                 ;(OR ah,al ok too)}
  128.   /$88/$D8     {  mov  al,bl    ;retrieve low order result}
  129.   /$59         {  pop  cx       ;restore cx}
  130.   /$C3         {  ret           ;return to caller}
  131.  
  132.                {Exit:}
  133.   /$1F         {  pop  DS       ;restore DS}
  134. );
  135.   END;  {of ATClock}
  136.  
  137.  
  138. PROCEDURE Date;
  139.   {Assumes global byte variables year, month, day, dow
  140.                   string variable DateStr  (STRING[8])
  141.    Updates the byte variables and the string with the
  142.    current date/time.
  143.    DateStr comes out as dd-mm-yy like DOS usually does it.
  144.    (Would have preferred the military yymmdd, but sheer
  145.    graciousness and good will made me consider you civilians!)
  146.    Per Norton's:
  147.    year  = 0..99 (tens only, you figure what to do in 2000)
  148.    month = 1..12
  149.    day   = 1..28, 29, 30, or 31 as appropriate
  150.    dow   = 0..6  (Sunday thru Saturday)
  151.   }
  152.   BEGIN
  153.     ATClock;             {If an AT, update DOS date/time}
  154.     Inline(
  155.   $B4/$2A          {  mov  ah,$2A      ;DOS get date svc}
  156.   /$CD/$21         {  int  $21         ;cx=yr,dh=mo,dl=day,al=day of wk}
  157.   /$81/$E9/$6C/$07 {  sub  cx,1900     ;year (only keep in 10's)}
  158.   /$88/$0E/>YEAR   {  mov  byte [>year],cl}
  159.   /$88/$36/>MONTH  {  mov  byte [>month],dh}
  160.   /$88/$16/>DAY    {  mov  byte [>day],dl}
  161.   /$A2/>DOW        {  mov  byte [>dow],al}
  162.   /$8C/$D8         {  mov  ax,DS}
  163.   /$8E/$C0         {  mov  ES,ax       ;ES=DS}
  164.   /$BF/>DATESTR    {  mov  di,>DateStr ;ES:DI}
  165.   /$FC             {  cld              ;insure forward}
  166.   /$B8/$08/$00     {  mov  ax,8        ;8 chars, clear msb}
  167.   /$AA             {  stosb            ;stuff length}
  168.  
  169.   /$B3/$0A         {  mov  bl,10       ;div by 10}
  170.   /$B7/$2D         {  mov  bh,'-'      ;divider}
  171.   /$88/$D0         {  mov  al,dl       ;day}
  172.   /$E8/$0E/$00     {  call DoDiv}
  173.   /$88/$F0         {  mov  al,dh       ;month}
  174.   /$E8/$09/$00     {  call DoDiv}
  175.   /$B7/$00         {  mov  bh,0        ;won't hurt}
  176.   /$88/$C8         {  mov  al,cl       ;year}
  177.   /$E8/$02/$00     {  call DoDiv}
  178.   /$EB/$0C         {  jmp  short Exit  ;done}
  179.  
  180.                    {DoDiv:}
  181.   /$F6/$F3         {  div  bl}
  182.                    {;quotient (10's) in al, remainder in ah}
  183.   /$05/$30/$30     {  add  ax,'00'     ;asciify}
  184.   /$AB             {  stosw}
  185.   /$88/$F8         {  mov  al,bh       ;our separator}
  186.   /$AA             {  stosb}
  187.   /$31/$C0         {  xor  ax,ax       ;clear msb again}
  188.   /$C3             {  ret}
  189.                    {Exit:}
  190. );
  191.   END;  {of Date}
  192.  
  193.  
  194. PROCEDURE Convert_SysDTG (VAR long : long_int);
  195.   {Uses global year,mon,day,hour,min,sec,deci (filled by StartTimer),
  196.    returns long_int suitable for file dtg or TimeStr/DateStr functions.
  197.    Expects you to have already called Time to update those globals.
  198.   }
  199.   BEGIN
  200. (* In Turbo:
  201.     long.lo := (hour ShL 11) OR (min * 32);
  202.     long.hi :=
  203.         ( (year-80) ShL 9)  {we only keep year in 10's}
  204.         OR (month ShL 5)
  205.         OR day ;
  206. *)
  207.     Inline(
  208.   $C4/$BE/>LONG    { les  di,>long[bp]  ;get long's vector}
  209.   /$A1/>HOUR       { mov  ax,[>hour]}
  210.   /$B1/$0B         { mov  cl,11}
  211.   /$D3/$E0         { shl  ax,cl}
  212.   /$89/$C2         { mov  dx,ax    ;save in dx}
  213.   /$A1/>MIN        { mov  ax,[>min]}
  214.   /$B1/$05         { mov  cl,5     ;*32}
  215.   /$D3/$E0         { shl  ax,cl}
  216.   /$09/$D0         { or   ax,dx}
  217.   /$FC             { cld}
  218.   /$AB             { stosw         ;long.lo}
  219.   /$A1/>MONTH      { mov  ax,[>month]}
  220.   /$D3/$E0         { shl  ax,cl    ;shl 5}
  221.   /$89/$C3         { mov  bx,ax    ;keep month in bx}
  222.   /$A1/>YEAR       { mov  ax,[>year]}
  223.   /$2D/$50/$00     { sub  ax,80    ;keep year in 10's}
  224.   /$B1/$09         { mov  cl,9}
  225.   /$D3/$E0         { shl  ax,cl}
  226.   /$89/$C2         { mov  dx,ax    ;keep yr in dx}
  227.   /$A1/>DAY        { mov  ax,[>day] ;get day}
  228.   /$09/$D8         { or   ax,bx    ;or in month}
  229.   /$09/$D0         { or   ax,dx    ;or in year}
  230.   /$AB             { stosw         ;long.hi}
  231. );
  232.   END;  {of Conv_SysDTG}
  233.  
  234.  
  235. PROCEDURE Get_File_DTG(VAR file_DTG : long_int);
  236.   {Gets current date, updates a long integer VAR (somewhere
  237.    out there) in DOS's format for a file date.
  238.    I use this in certain records to keep date and time to
  239.    the same formats as DOS uses in files.
  240.    Updates the global date variables and DateString.
  241.   }
  242.   BEGIN
  243.     Date;                      {update date variables}
  244.     Convert_SysDTG(file_DTG);  {convert global vars to a long int}
  245.   END;  {of Get_DTG}
  246.  
  247.  
  248. FUNCTION Long_DateStr(VAR dt : long_int) : Str8;
  249.   {Using long_int dt (such as you'd get from a file's date.
  250.    Notice only provides yrs in 10's.
  251.    Returns a date string 00-00-00.
  252.    Uses a global string LT1 (STRING[8]) as a working
  253.    string for speed and tightness.  Sorry 'bout that.
  254.    This code came out of a REALLY tweaked program
  255.    where I was really optimizing stuff.
  256.   }
  257.   BEGIN
  258.     Inline(
  259.   $C4/$BE/>DT      {  les  di,>dt[bp]  ;get parm ofs,seg}
  260.   /$81/$C7/$02/$00 {  add  di,2        ;bump to hi word}
  261.   /$26             {  ES:}
  262.   /$8B/$05         {  mov  ax,[di]     ;snarf hi word}
  263.   /$89/$C3         {  mov  bx,ax       ;save here}
  264.  
  265.   /$B1/$05         {  mov  cl,5}
  266.   /$D3/$E8         {  shr  ax,cl       ;hi shr 5}
  267.   /$24/$0F         {  and  al,$0F      ;mask for month}
  268.   /$88/$C6         {  mov  dh,al       ;dh=month}
  269.  
  270.   /$89/$D8         {  mov  ax,bx       ;get hi word back}
  271.   /$24/$1F         {  and  al,$1F      ;mask for day}
  272.   /$88/$C2         {  mov  dl,al       ;dl=day}
  273.  
  274.   /$89/$D8         {  mov  ax,bx       ;get hi word back}
  275.   /$B1/$09         {  mov  cl,9}
  276.   /$D3/$E8         {  shr  ax,cl       ;hi word shr 9}
  277.   /$04/$50         {  add  al,80       ;add back years}
  278.   /$88/$C1         {  mov  cl,al       ;cl=year}
  279.  
  280.   /$8C/$D8         {  mov  ax,DS}
  281.   /$8E/$C0         {  mov  ES,ax       ;ES=DS}
  282.   /$BF/>LT1        {  mov  di,>LT1     ;*** a global string ***}
  283.   /$B8/$08/$00     {  mov  ax,8        ;8 chars, clear msb}
  284.   /$FC             {  cld              ;insure forward}
  285.   /$AA             {  stosb            ;stuff length}
  286.  
  287.   /$B3/$0A         {  mov  bl,10       ;div by 10}
  288.   /$B7/$2D         {  mov  bh,'-'      ;divider}
  289.   /$88/$D0         {  mov  al,dl       ;day}
  290.   /$E8/$0E/$00     {  call DoDiv}
  291.   /$88/$F0         {  mov  al,dh       ;month}
  292.   /$E8/$09/$00     {  call DoDiv}
  293.   /$B7/$00         {  mov  bh,0        ;won't hurt}
  294.   /$88/$C8         {  mov  al,cl       ;year}
  295.   /$E8/$02/$00     {  call DoDiv}
  296.   /$EB/$0C         {  jmp  short Exit  ;done}
  297.  
  298.                    {DoDiv:}
  299.   /$F6/$F3         {  div     bl}
  300.                    {;quotient (10's) in al, remainder in ah}
  301.   /$05/$30/$30     {  add  ax,'00'     ;asciify}
  302.   /$AB             {  stosw}
  303.   /$88/$F8         {  mov  al,bh       ;our separator}
  304.   /$AA             {  stosb}
  305.   /$31/$C0         {  xor  ax,ax       ;clear msb again}
  306.   /$C3             {  ret}
  307.  
  308.                    {Exit:}
  309. );
  310.     Long_DateStr := Copy(LT1,1,8);     {clumsy way to return the string}
  311.   END;  {of Long_DateStr}
  312.  
  313.  
  314. FUNCTION Long_TimeStr(VAR dt : long_int) : Str5;
  315.   {Using long_int dt (such as you'd get from a file's date.
  316.    Notice only provides hours and minutes.
  317.    Returns a 5-character time string 00:00
  318.    Again, uses that global working string LT1 (STRING[8])
  319.    for speed and simplicity.
  320.   }
  321.   BEGIN
  322.     Inline(
  323.   $C4/$BE/>DT      {  les  di,>dt[bp]  ;point to parm}
  324.   /$26             {  ES:}
  325.   /$8B/$05         {  mov  ax,[di]     ;grab lo word}
  326.   /$89/$C3         {  mov  bx,ax       ;save here}
  327.   /$B1/$0B         {  mov  cl,11}
  328.   /$D3/$E8         {  shr  ax,cl}
  329.   /$88/$C5         {  mov  ch,al       ;save hrs}
  330.   /$89/$D8         {  mov  ax,bx       ;get lo word back}
  331.   /$80/$E4/$07     {  and  ah,7        ;mask out hours}
  332.   /$B1/$05         {  mov  cl,5        ;div 32}
  333.   /$D3/$E8         {  shr  ax,cl}
  334.   /$88/$C1         {  mov  cl,al       ;save minutes}
  335.  
  336.   /$8C/$D8         {  mov  ax,DS}
  337.   /$8E/$C0         {  mov  ES,ax       ;ES=DS}
  338.   /$BF/>LT1        {  mov  di,>LT1     ;*** a global string ***}
  339.   /$B8/$05/$00     {  mov  ax,5        ;5 chars, clear msb}
  340.   /$FC             {  cld              ;insure forward}
  341.   /$AA             {  stosb            ;stuff length}
  342.  
  343.   /$B3/$0A         {  mov  bl,10       ;div by 10}
  344.   /$B7/$3A         {  mov  bh,':'      ;divider}
  345.   /$88/$E8         {  mov  al,ch       ;hours}
  346.   /$E8/$09/$00     {  call DoDiv}
  347.   /$30/$FF         {  xor  bh,bh       ;0 for terminator}
  348.   /$88/$C8         {  mov  al,cl       ;minutes}
  349.   /$E8/$02/$00     {  call DoDiv}
  350.   /$EB/$0C         {  jmp  short Exit  ;done}
  351.  
  352.                    {DoDiv:}
  353.   /$F6/$F3         {  div  bl}
  354.                    {;quotient (10's) in al, remainder in ah}
  355.   /$05/$30/$30     {  add  ax,'00'     ;asciify}
  356.   /$AB             {  stosw}
  357.   /$88/$F8         {  mov  al,bh       ;our separator}
  358.   /$AA             {  stosb}
  359.   /$31/$C0         {  xor  ax,ax       ;clear msb again}
  360.   /$C3             {  ret}
  361.  
  362.                    {Exit:}
  363. );
  364.      Long_TimeStr := Copy(LT1,1,5);    {clumsy way to return string}
  365.   END;  {of Long_TimeStr}
  366.  
  367.  
  368. PROCEDURE Time;
  369.   {Updates global time variables, global string TimeStr.
  370.    Equivalent (for time) to Date.
  371.   }
  372.   BEGIN
  373.     ATClock;             {If an AT, update DOS date/time}
  374.     Inline(
  375.   $B4/$2C          {  mov  ah,$2C      ;DOS get time svc}
  376.   /$CD/$21         {  int  $21}
  377.   /$88/$2E/>HOUR   {  mov  byte [>hour],ch ;update globals}
  378.   /$88/$0E/>MIN    {  mov  byte [>min],cl}
  379.   /$88/$36/>SEC    {  mov  byte [>sec],dh}
  380.   /$88/$16/>deci   {  mov  byte [>deci],dl}
  381.   /$8C/$D8         {  mov  ax,DS}
  382.   /$8E/$C0         {  mov  ES,ax       ;ES=DS}
  383.   /$BF/>TIMESTR    {  mov  di,>TimeStr ;ES:DI}
  384.   /$FC             {  cld              ;insure forward}
  385.   /$B8/$0B/$00     {  mov  ax,11       ;11 chars, clear msb}
  386.   /$AA             {  stosb            ;stuff length}
  387.  
  388.   /$B3/$0A         {  mov  bl,10       ;div by 10}
  389.   /$B7/$3A         {  mov  bh,':'      ;divider}
  390.   /$88/$E8         {  mov  al,ch       ;hours}
  391.   /$E8/$15/$00     {  call DoDiv}
  392.   /$88/$C8         {  mov  al,cl       ;minutes}
  393.   /$E8/$10/$00     {  call DoDiv}
  394.   /$B7/$2E         {  mov  bh,'.'      ;now a decimal}
  395.   /$88/$F0         {  mov  al,dh       ;seconds}
  396.   /$E8/$09/$00     {  call DoDiv}
  397.   /$B7/$00         {  mov  bh,0        ;won't hurt}
  398.   /$88/$D0         {  mov  al,dl       ;decisec}
  399.   /$E8/$02/$00     {  call DoDiv}
  400.   /$EB/$0C         {  jmp  short Exit  ;done}
  401.  
  402.                    {DoDiv:}
  403.   /$F6/$F3         {  div  bl}
  404.                    {;quotient (10's) in al, remainder in ah}
  405.   /$05/$30/$30     {  add  ax,'00'     ;asciify}
  406.   /$AB             {  stosw}
  407.   /$88/$F8         {  mov  al,bh       ;our separator}
  408.   /$AA             {  stosb}
  409.   /$31/$C0         {  xor  ax,ax       ;clear msb again}
  410.   /$C3             {  ret}
  411.                    {Exit:}
  412. );
  413.   END;  {of Time}
  414.  
  415.  
  416. PROCEDURE StopTimer;
  417.   {Reads current system time, readjusts hr,min,sec,deci to show the
  418.    time expended since last StartTimer.
  419.    Idea (maybe code too, I forget) was cribbed from a CRC test program
  420.    by David Dantowitz.
  421.  
  422.    WATCH IT!  This is great for stopclock functions, but remember
  423.    the globals are all EXPENDED TIME!  Don't use them for system
  424.    time functions until you call Time again to reset them to
  425.    system time!
  426.   }
  427.    BEGIN
  428.     ATClock;             {If an AT, update DOS date/time}
  429.     Inline(
  430.   $B4/$2C          {  mov  ah,$2C      ;get current time}
  431.   /$CD/$21         {  int  $21}
  432.   /$8A/$1E/>deci   {  mov  bl,[>deci]  ;old decisecond}
  433.   /$8A/$3E/>SEC    {  mov  bh,[>sec]   ;old seconds}
  434.   /$B4/$3C         {  mov  ah,60       ;constant}
  435.   /$88/$D0         {  mov  al,dl       ;get fraction}
  436.   /$28/$D8         {  sub  al,bl       ;deci = dl-old deci}
  437.   /$73/$04         {  jnb  S1          ;no carry, so not <0}
  438.   /$04/$64         {  add  al,100      ;<0, adjust}
  439.   /$FE/$C7         {  inc  bh          ;bump old seconds}
  440.                    {S1:}
  441.   /$88/$C2         {  mov  dl,al       ;save adj. deci}
  442.  
  443.   /$8A/$1E/>MIN    {  mov  bl,[>min]   ;get old Minute}
  444.   /$88/$F0         {  mov  al,dh       ;get new seconds}
  445.   /$28/$F8         {  sub  al,bh       ;sec = new sec-old sec}
  446.   /$73/$06         {  jnb  S2          ;not <0}
  447.   /$00/$E0         {  add  al,ah       ;<0, add back 60}
  448.   /$FE/$C3         {  inc  bl          ;old min=old min+1}
  449.   /$EB/$08         {  jmp short S3}
  450.  
  451.                    {S2:}
  452.   /$38/$E0         {  cmp  al,ah       ;>=60?}
  453.   /$72/$04         {  jb   S3          ; nope}
  454.   /$28/$E0         {  sub  al,ah       ; sec=sec-60}
  455.   /$FE/$CB         {  dec  bl          ;old min=old min-1}
  456.                    {S3:}
  457.   /$88/$C6         {  mov  dh,al       ;save adj. seconds}
  458.  
  459.   /$8A/$3E/>HOUR   {  mov  bh,[>hour]  ;old hour}
  460.   /$88/$C8         {  mov  al,cl       ;get new minutes}
  461.   /$28/$D8         {  sub  al,bl       ;min=new min-old min}
  462.   /$73/$06         {  jnb  S4          ; not below 0}
  463.   /$00/$E0         {  add  al,ah       ;min=min+60}
  464.   /$FE/$C7         {  inc  bh          ;old hour=old hour+1}
  465.   /$EB/$08         {  jmp  short S5}
  466.  
  467.                    {S4:}
  468.   /$38/$E0         {  cmp  al,ah       ;>=60?}
  469.   /$72/$04         {  jb   S5          ; nope}
  470.   /$28/$E0         {  sub  al,ah       ;min=min-60}
  471.   /$FE/$CF         {  dec  bh          ;old hour=old hour-1}
  472.                    {S5:}
  473.   /$88/$C1         {  mov  cl,al       ;save adj. minutes}
  474.  
  475.   /$88/$E8         {  mov  al,ch       ;get new hour}
  476.   /$28/$F8         {  sub  al,bh       ;hour=new hour-old hour}
  477.   /$88/$C5         {  mov  ch,al}
  478.  
  479.   /$8C/$D8         {  mov  ax,DS}
  480.   /$8E/$C0         {  mov  ES,ax       ;ES=DS}
  481.   /$BF/>TIMESTR    {  mov  di,>TimeStr ;ES:DI}
  482.   /$FC             {  cld              ;insure forward}
  483.   /$B8/$0B/$00     {  mov  ax,11       ;11 chars, clear msb}
  484.   /$AA             {  stosb            ;stuff length}
  485.  
  486.   /$B3/$0A         {  mov  bl,10       ;div by 10}
  487.   /$B7/$3A         {  mov  bh,':'      ;divider}
  488.   /$88/$E8         {  mov  al,ch       ;hours}
  489. {23:}
  490.   /$E8/$15/$00     {  call DoDiv}
  491.   /$88/$C8         {  mov  al,cl       ;minutes}
  492.   /$E8/$10/$00     {  call DoDiv}
  493. {23:59:}
  494.   /$B7/$2E         {  mov  bh,'.'      ;now a decimal}
  495.   /$88/$F0         {  mov  al,dh       ;seconds}
  496.   /$E8/$09/$00     {  call DoDiv}
  497. {23:59.59.}
  498.   /$B7/$00         {  mov  bh,0        ;won't hurt}
  499.   /$88/$D0         {  mov  al,dl       ;deci}
  500.   /$E8/$02/$00     {  call DoDiv}
  501. {23:59.59.99}
  502.   /$EB/$0C         {  jmp  short Exit  ;done}
  503.  
  504.             {DoDiv:}
  505.   /$F6/$F3         {  div  bl}
  506.                    {;quotient (10's) in al, remainder in ah}
  507.   /$05/$30/$30     {  add  ax,'00'     ;asciify}
  508.   /$AB             {  stosw}
  509.   /$88/$F8         {  mov  al,bh       ;our separator}
  510.   /$AA             {  stosb}
  511.   /$31/$C0         {  xor  ax,ax       ;clear msb again}
  512.   /$C3             {  ret}
  513.                    {Exit:}
  514. );
  515.   END; { of StopTimer }
  516.  
  517.  
  518. FUNCTION Now : Str8;
  519.   {Assume you wish to time a process.
  520.    Call Time to update the global time variables from system time.
  521.    Then start your process.
  522.    When completed, call Now.
  523.    Now will return a string (hours, minutes, seconds, deciseconds)
  524.    of the time expended since Time was called.
  525.    The global string TimeStr contains the FULL time:
  526.      hours, minutes, seconds, deciseconds, as hh:mm:ss.dd
  527.    In this particular application, I didn't want the hours,
  528.    so I've stripped them out.  You could return the whole string
  529.    if you wish by defining Now to return a Str11.
  530.  
  531.    WATCH IT!  All the time globals are now loaded with that expended
  532.    time.  Be sure to call Time again to reset them to system time
  533.    before using them.
  534.   }
  535.   BEGIN
  536.     StopTimer;                         {reset globals to time expended}
  537.     Now := Copy(TimeStr,4,8);          {return string showing time expended:
  538.                                         mm:ss.dd}
  539.   END;  {of Now}
  540.