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