source: abuse/branches/pd/macabuse/imlib/port/dos4gw/timing.c @ 636

Last change on this file since 636 was 49, checked in by Sam Hocevar, 15 years ago
  • Imported original public domain release, for future reference.
  • Property svn:keywords set to Id
File size: 5.8 KB
Line 
1#include <time.h>
2#include "timing.hpp"
3#include <dos.h>
4#include <stdlib.h>
5#include <i86.h>
6#include <stdio.h>
7#include <conio.h>
8#include "dprint.hpp"
9#include <string.h>
10
11
12static void (__interrupt __far *prev_int8)()=NULL;
13volatile unsigned long tick_counter,start_tick_counter; // note : if timer handler is installed for more than 497 days then
14                                                        // tick_counter will overflow.  ** don't use this in OS code **
15
16void (*install_timer_handler)(void (*fun)())=NULL;   // function to install timer
17void (*uninstall_timer_handler)()=NULL;
18unsigned char timer_installed=0;
19
20
21static dostime_t dos_start_time;
22static dosdate_t dos_start_date;
23static chain_on=0,chain_counter=0;
24int total_timer_calls=0;
25
26#define MAX_TIMER_CALLS 10
27
28#define TICKS_PER_SEC 100
29
30struct
31{
32  int and_call_mask;
33  void ( *fun)(); 
34} timer_calls[MAX_TIMER_CALLS];
35
36void add_timer_call(void ( *fun)(), int and_call_mask)
37{
38  if (total_timer_calls>=MAX_TIMER_CALLS)
39  {
40    dprintf("Too many timer calls installed\n");
41    exit(0);
42  }
43  timer_calls[total_timer_calls].fun=fun;
44  timer_calls[total_timer_calls].and_call_mask=and_call_mask;
45  total_timer_calls++; 
46}
47
48void remove_timer_call(void ( *fun)())
49{
50  for (int i=0;i<total_timer_calls;i++)
51  {
52    if (timer_calls[i].fun==fun)
53    {
54      for (int j=i;j<total_timer_calls-1;j++)       
55        timer_calls[j]=timer_calls[j+1];
56      total_timer_calls--;
57      return ;
58    }
59  }
60  dprintf("remove_timer_call : call not installed\n");
61  exit(0);
62}
63
64void cli();
65#pragma aux cli = "cli";
66
67void sti();
68#pragma aux sti = "sti";
69
70
71
72static int inside=0;  // check for re-entry
73void __interrupt __far new_timer_int ()
74{
75  outp(0x20,0x20);          // signal int chip that ints can continue
76  tick_counter++;
77
78  if (!inside)
79  {   
80    inside=1;   
81    sti();                  // turn interrupts back on
82
83
84    for (int i=0;i<total_timer_calls;i++)
85      if ((tick_counter&timer_calls[i].and_call_mask)==0)
86        timer_calls[i].fun();
87    inside=0;
88  } else sti();                  // turn interrupts back on
89
90  chain_counter++;               // see if we need to call the normal DOS intr yet
91  if (chain_counter==chain_on)
92  {
93    chain_counter=0;
94    _chain_intr(prev_int8);
95  }
96
97}
98
99void timer_stub()  // this should be called int timer_init has been called and install_timer_handler!=NULL
100{
101  tick_counter++;
102
103  if (!inside)
104  {
105    inside=1;
106    for (int i=0;i<total_timer_calls;i++)
107      if ((tick_counter&timer_calls[i].and_call_mask)==0)
108        timer_calls[i].fun();
109    inside=0;
110  }
111}
112
113void timer_init()
114{
115  if (install_timer_handler)
116  {
117    install_timer_handler(timer_stub);
118    timer_installed=1;
119  } else
120  {
121    if (prev_int8) 
122      fprintf(stderr,"timer_init called twice (not good)\n");
123    else
124    {
125      _dos_gettime(&dos_start_time);             // get initail time
126      _dos_getdate(&dos_start_date);             // get initail date
127
128      prev_int8=_dos_getvect(8); 
129      _dos_setvect(0x8008,new_timer_int);       // set protected mode int 8
130
131      int new_divsor;
132      if (TICKS_PER_SEC<18.2)
133      {
134        new_divsor=0xffff;
135        chain_on=1;
136      }
137      else
138      {
139        chain_on=TICKS_PER_SEC/18.2;
140        new_divsor=0xffff/chain_on;
141      }
142
143      chain_counter=0;
144      cli();
145      outp(0x43,0x36);                      // set timer speed
146      outp(0x40,(new_divsor&0xff));
147      outp(0x40,((new_divsor&0xff00)>>8));
148      sti();
149
150      timer_installed=1;
151    }
152    atexit(timer_uninit);
153    start_tick_counter=tick_counter=clock();
154  }
155}
156
157static char dim[12]={31,  // Jan
158                     29,  // Feb
159                     31,  // March
160                     30,  // Apr
161                     31,  // May
162                     30,  // June
163                     31,  // July
164                     30,  // Aug
165                     31,  // Sept
166                     31,  // October
167                     30,  // Nov
168                     31}; // Dec
169
170static int days_in_month(long month, long year)
171{
172  if (month==1 && (((year-1)%4)==0))   // check for leap-year
173    return 28;
174  else return dim[month];
175}
176
177void timer_uninit()
178{
179  chain_on=0;
180  if (timer_installed)
181  {
182    if (uninstall_timer_handler)
183      uninstall_timer_handler();
184    else
185    {
186      if (prev_int8)
187      {
188        outp(0x43,0x36);                      // set timer speed back to 18.2
189        outp(0x40,0);
190        outp(0x40,0);
191        _dos_setvect(8,prev_int8);
192
193        unsigned long ticks_passed=tick_counter-start_tick_counter;
194        unsigned long hsec=ticks_passed*100/TICKS_PER_SEC;
195
196        // now calculate how much time we stole from DOS and adjust the clock
197        dos_start_time.hsecond=((long)dos_start_time.hsecond+hsec)%100;
198        dos_start_time.second=((long)dos_start_time.second+hsec/100)%60;
199        dos_start_time.minute=((long)dos_start_time.minute+hsec/(100*60))%60;
200        dos_start_time.hour=((long)dos_start_time.hour+hsec/(100*60*60))%24;
201       
202        long days=hsec/(100*60*60*24);
203        while (days)
204        {
205          dos_start_date.dayofweek=(dos_start_date.dayofweek+2)%7-1;
206          dos_start_date.day=dos_start_date.day+1;
207          if (days_in_month(dos_start_date.month,dos_start_date.year)>dos_start_date.day)
208          {
209            dos_start_date.day=1;
210            dos_start_date.month++;
211            if (dos_start_date.month>12)
212            {
213              dos_start_date.month=1;
214              dos_start_date.year++;
215            }
216          }
217          days--;
218        }
219        _dos_settime(&dos_start_time);
220        _dos_setdate(&dos_start_date);
221
222        prev_int8=NULL;   
223      }
224    }
225    timer_installed=0;
226  }
227}
228
229void time_marker::get_time()
230{
231  seconds=0;
232  if (timer_installed)
233    micro_seconds=tick_counter*1000/TICKS_PER_SEC;
234  else
235    micro_seconds=clock()*10;
236}
237
238time_marker::time_marker()
239{
240  seconds=0;
241  if (timer_installed)
242    micro_seconds=tick_counter*1000/TICKS_PER_SEC; 
243  else
244    micro_seconds=clock()*10;
245}
246
247double time_marker::diff_time(time_marker *other)
248{
249  return (double)(micro_seconds-other->micro_seconds)/1000.0; 
250}
251
252void milli_wait(unsigned wait_time)
253{
254  if (timer_installed)
255  {
256    unsigned long wait_tick=wait_time*10/TICKS_PER_SEC+tick_counter;
257    while (tick_counter<wait_tick) ;
258  } else delay(wait_time);                    // timer not installed
259}
260
Note: See TracBrowser for help on using the repository browser.