2440bd581130294c134c1545bf96d99c11986bdb
[dosdemo] / src / dos / timer.c
1 /* for sound we use MIDAS, which takes over the PIT and we can't use it
2  * therefore only compile this file for NO_SOUND builds.
3  */
4 #ifdef NO_SOUND
5
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <assert.h>
9 #include <conio.h>
10 #include <dos.h>
11
12 #ifdef __WATCOMC__
13 #include <i86.h>
14 #endif
15
16 #ifdef __DJGPP__
17 #include <dpmi.h>
18 #include <go32.h>
19 #include <pc.h>
20 #endif
21
22 #include "pit8254.h"
23 #include "inttypes.h"
24 #include "util.h"
25
26 #define PIT_TIMER_INTR  8
27 #define DOS_TIMER_INTR  0x1c
28
29 /* macro to divide and round to the nearest integer */
30 #define DIV_ROUND(a, b) \
31         ((a) / (b) + ((a) % (b)) / ((b) / 2))
32
33 static void set_timer_reload(int reload_val);
34 static void cleanup(void);
35
36 #ifdef __WATCOMC__
37 #define INTERRUPT       __interrupt __far
38
39 static void INTERRUPT dos_timer_intr();
40
41 static void (INTERRUPT *prev_timer_intr)();
42 #endif
43
44 #ifdef __DJGPP__
45 #define INTERRUPT
46
47 static _go32_dpmi_seginfo intr, prev_intr;
48
49 #define outp(p, v)      outportb(p, v)
50 #endif
51
52 static void INTERRUPT timer_irq();
53
54 static volatile unsigned long ticks;
55 static unsigned long tick_interval, ticks_per_dos_intr;
56 static int inum;
57
58 void init_timer(int res_hz)
59 {
60         _disable();
61
62         if(res_hz > 0) {
63                 int reload_val = DIV_ROUND(OSC_FREQ_HZ, res_hz);
64                 set_timer_reload(reload_val);
65
66                 tick_interval = DIV_ROUND(1000, res_hz);
67                 ticks_per_dos_intr = DIV_ROUND(65535L, reload_val);
68
69                 inum = PIT_TIMER_INTR;
70 #ifdef __WATCOMC__
71                 prev_timer_intr = _dos_getvect(inum);
72                 _dos_setvect(inum, timer_irq);
73 #endif
74 #ifdef __DJGPP__
75                 _go32_dpmi_get_protected_mode_interrupt_vector(inum, &prev_intr);
76                 intr.pm_offset = (intptr_t)timer_irq;
77                 intr.pm_selector = _go32_my_cs();
78                 _go32_dpmi_allocate_iret_wrapper(&intr);
79                 _go32_dpmi_set_protected_mode_interrupt_vector(inum, &intr);
80 #endif
81         } else {
82                 tick_interval = 55;
83
84                 inum = DOS_TIMER_INTR;
85 #ifdef __WATCOMC__
86                 prev_timer_intr = _dos_getvect(inum);
87                 _dos_setvect(inum, dos_timer_intr);
88 #endif
89 #ifdef __DJGPP__
90                 assert(0);
91 #endif
92         }
93         _enable();
94
95         atexit(cleanup);
96 }
97
98 static void cleanup(void)
99 {
100         if(!inum) {
101                 return; /* init hasn't ran, there's nothing to cleanup */
102         }
103
104         _disable();
105         if(inum == PIT_TIMER_INTR) {
106                 /* restore the original timer frequency */
107                 set_timer_reload(65535);
108         }
109
110         /* restore the original interrupt handler */
111 #ifdef __WATCOMC__
112         _dos_setvect(inum, prev_timer_intr);
113 #endif
114 #ifdef __DJGPP__
115         _go32_dpmi_set_protected_mode_interrupt_vector(inum, &prev_intr);
116         _go32_dpmi_free_iret_wrapper(&intr);
117 #endif
118
119         _enable();
120 }
121
122 void reset_timer(void)
123 {
124         ticks = 0;
125 }
126
127 unsigned long get_msec(void)
128 {
129         return ticks * tick_interval;
130 }
131
132 void sleep_msec(unsigned long msec)
133 {
134         unsigned long wakeup_time = ticks + msec / tick_interval;
135         while(ticks < wakeup_time) {
136 #ifdef USE_HLT
137                 halt();
138 #endif
139         }
140 }
141
142 static void set_timer_reload(int reload_val)
143 {
144         outp(PORT_CMD, CMD_CHAN0 | CMD_ACCESS_BOTH | CMD_OP_SQWAVE);
145         outp(PORT_DATA0, reload_val & 0xff);
146         outp(PORT_DATA0, (reload_val >> 8) & 0xff);
147 }
148
149 #ifdef __WATCOMC__
150 static void INTERRUPT dos_timer_intr()
151 {
152         ticks++;
153         _chain_intr(prev_timer_intr);   /* DOES NOT RETURN */
154 }
155 #endif
156
157 /* first PIC command port */
158 #define PIC1_CMD        0x20
159 /* end of interrupt control word */
160 #define OCW2_EOI        (1 << 5)
161
162 static void INTERRUPT timer_irq()
163 {
164         static unsigned long dos_ticks;
165
166         ticks++;
167
168 #ifdef __WATCOMC__
169         if(++dos_ticks >= ticks_per_dos_intr) {
170                 /* I suppose the dos irq handler does the EOI so I shouldn't
171                  * do it if I am to call the previous function
172                  */
173                 dos_ticks = 0;
174                 _chain_intr(prev_timer_intr);   /* XXX DOES NOT RETURN */
175                 return; /* just for clarity */
176         }
177 #endif
178
179         /* send EOI to the PIC */
180         outp(PIC1_CMD, OCW2_EOI);
181 }
182
183 #endif  /* NO_SOUND */