SDL static config fix (Roman Zippel)
[qemu] / hw / i8254.c
1 /*
2  * QEMU 8253/8254 interval timer emulation
3  * 
4  * Copyright (c) 2003-2004 Fabrice Bellard
5  * 
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 #include "vl.h"
25
26 //#define DEBUG_PIT
27
28 #define RW_STATE_LSB 0
29 #define RW_STATE_MSB 1
30 #define RW_STATE_WORD0 2
31 #define RW_STATE_WORD1 3
32 #define RW_STATE_LATCHED_WORD0 4
33 #define RW_STATE_LATCHED_WORD1 5
34
35 PITChannelState pit_channels[3];
36
37 static void pit_irq_timer_update(PITChannelState *s, int64_t current_time);
38
39 static int pit_get_count(PITChannelState *s)
40 {
41     uint64_t d;
42     int counter;
43
44     d = muldiv64(qemu_get_clock(vm_clock) - s->count_load_time, PIT_FREQ, ticks_per_sec);
45     switch(s->mode) {
46     case 0:
47     case 1:
48     case 4:
49     case 5:
50         counter = (s->count - d) & 0xffff;
51         break;
52     case 3:
53         /* XXX: may be incorrect for odd counts */
54         counter = s->count - ((2 * d) % s->count);
55         break;
56     default:
57         counter = s->count - (d % s->count);
58         break;
59     }
60     return counter;
61 }
62
63 /* get pit output bit */
64 int pit_get_out(PITChannelState *s, int64_t current_time)
65 {
66     uint64_t d;
67     int out;
68
69     d = muldiv64(current_time - s->count_load_time, PIT_FREQ, ticks_per_sec);
70     switch(s->mode) {
71     default:
72     case 0:
73         out = (d >= s->count);
74         break;
75     case 1:
76         out = (d < s->count);
77         break;
78     case 2:
79         if ((d % s->count) == 0 && d != 0)
80             out = 1;
81         else
82             out = 0;
83         break;
84     case 3:
85         out = (d % s->count) < ((s->count + 1) >> 1);
86         break;
87     case 4:
88     case 5:
89         out = (d == s->count);
90         break;
91     }
92     return out;
93 }
94
95 /* return -1 if no transition will occur.  */
96 static int64_t pit_get_next_transition_time(PITChannelState *s, 
97                                             int64_t current_time)
98 {
99     uint64_t d, next_time, base;
100     int period2;
101
102     d = muldiv64(current_time - s->count_load_time, PIT_FREQ, ticks_per_sec);
103     switch(s->mode) {
104     default:
105     case 0:
106     case 1:
107         if (d < s->count)
108             next_time = s->count;
109         else
110             return -1;
111         break;
112     case 2:
113         base = (d / s->count) * s->count;
114         if ((d - base) == 0 && d != 0)
115             next_time = base + s->count;
116         else
117             next_time = base + s->count + 1;
118         break;
119     case 3:
120         base = (d / s->count) * s->count;
121         period2 = ((s->count + 1) >> 1);
122         if ((d - base) < period2) 
123             next_time = base + period2;
124         else
125             next_time = base + s->count;
126         break;
127     case 4:
128     case 5:
129         if (d < s->count)
130             next_time = s->count;
131         else if (d == s->count)
132             next_time = s->count + 1;
133         else
134             return -1;
135         break;
136     }
137     /* convert to timer units */
138     next_time = s->count_load_time + muldiv64(next_time, ticks_per_sec, PIT_FREQ);
139     /* fix potential rounding problems */
140     /* XXX: better solution: use a clock at PIT_FREQ Hz */
141     if (next_time <= current_time)
142         next_time = current_time + 1;
143     return next_time;
144 }
145
146 /* val must be 0 or 1 */
147 void pit_set_gate(PITChannelState *s, int val)
148 {
149     switch(s->mode) {
150     default:
151     case 0:
152     case 4:
153         /* XXX: just disable/enable counting */
154         break;
155     case 1:
156     case 5:
157         if (s->gate < val) {
158             /* restart counting on rising edge */
159             s->count_load_time = qemu_get_clock(vm_clock);
160             pit_irq_timer_update(s, s->count_load_time);
161         }
162         break;
163     case 2:
164     case 3:
165         if (s->gate < val) {
166             /* restart counting on rising edge */
167             s->count_load_time = qemu_get_clock(vm_clock);
168             pit_irq_timer_update(s, s->count_load_time);
169         }
170         /* XXX: disable/enable counting */
171         break;
172     }
173     s->gate = val;
174 }
175
176 static inline void pit_load_count(PITChannelState *s, int val)
177 {
178     if (val == 0)
179         val = 0x10000;
180     s->count_load_time = qemu_get_clock(vm_clock);
181     s->count = val;
182     pit_irq_timer_update(s, s->count_load_time);
183 }
184
185 static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val)
186 {
187     int channel, access;
188     PITChannelState *s;
189
190     addr &= 3;
191     if (addr == 3) {
192         channel = val >> 6;
193         if (channel == 3)
194             return;
195         s = &pit_channels[channel];
196         access = (val >> 4) & 3;
197         switch(access) {
198         case 0:
199             s->latched_count = pit_get_count(s);
200             s->rw_state = RW_STATE_LATCHED_WORD0;
201             break;
202         default:
203             s->mode = (val >> 1) & 7;
204             s->bcd = val & 1;
205             s->rw_state = access - 1 +  RW_STATE_LSB;
206             /* XXX: update irq timer ? */
207             break;
208         }
209     } else {
210         s = &pit_channels[addr];
211         switch(s->rw_state) {
212         case RW_STATE_LSB:
213             pit_load_count(s, val);
214             break;
215         case RW_STATE_MSB:
216             pit_load_count(s, val << 8);
217             break;
218         case RW_STATE_WORD0:
219         case RW_STATE_WORD1:
220             if (s->rw_state & 1) {
221                 pit_load_count(s, (s->latched_count & 0xff) | (val << 8));
222             } else {
223                 s->latched_count = val;
224             }
225             s->rw_state ^= 1;
226             break;
227         }
228     }
229 }
230
231 static uint32_t pit_ioport_read(void *opaque, uint32_t addr)
232 {
233     int ret, count;
234     PITChannelState *s;
235     
236     addr &= 3;
237     s = &pit_channels[addr];
238     switch(s->rw_state) {
239     case RW_STATE_LSB:
240     case RW_STATE_MSB:
241     case RW_STATE_WORD0:
242     case RW_STATE_WORD1:
243         count = pit_get_count(s);
244         if (s->rw_state & 1)
245             ret = (count >> 8) & 0xff;
246         else
247             ret = count & 0xff;
248         if (s->rw_state & 2)
249             s->rw_state ^= 1;
250         break;
251     default:
252     case RW_STATE_LATCHED_WORD0:
253     case RW_STATE_LATCHED_WORD1:
254         if (s->rw_state & 1)
255             ret = s->latched_count >> 8;
256         else
257             ret = s->latched_count & 0xff;
258         s->rw_state ^= 1;
259         break;
260     }
261     return ret;
262 }
263
264 static void pit_irq_timer_update(PITChannelState *s, int64_t current_time)
265 {
266     int64_t expire_time;
267     int irq_level;
268
269     if (!s->irq_timer)
270         return;
271     expire_time = pit_get_next_transition_time(s, current_time);
272     irq_level = pit_get_out(s, current_time);
273     pic_set_irq(s->irq, irq_level);
274 #ifdef DEBUG_PIT
275     printf("irq_level=%d next_delay=%f\n",
276            irq_level, 
277            (double)(expire_time - current_time) / ticks_per_sec);
278 #endif
279     s->next_transition_time = expire_time;
280     if (expire_time != -1)
281         qemu_mod_timer(s->irq_timer, expire_time);
282     else
283         qemu_del_timer(s->irq_timer);
284 }
285
286 static void pit_irq_timer(void *opaque)
287 {
288     PITChannelState *s = opaque;
289
290     pit_irq_timer_update(s, s->next_transition_time);
291 }
292
293 static void pit_save(QEMUFile *f, void *opaque)
294 {
295     PITChannelState *s;
296     int i;
297     
298     for(i = 0; i < 3; i++) {
299         s = &pit_channels[i];
300         qemu_put_be32s(f, &s->count);
301         qemu_put_be16s(f, &s->latched_count);
302         qemu_put_8s(f, &s->rw_state);
303         qemu_put_8s(f, &s->mode);
304         qemu_put_8s(f, &s->bcd);
305         qemu_put_8s(f, &s->gate);
306         qemu_put_be64s(f, &s->count_load_time);
307         if (s->irq_timer) {
308             qemu_put_be64s(f, &s->next_transition_time);
309             qemu_put_timer(f, s->irq_timer);
310         }
311     }
312 }
313
314 static int pit_load(QEMUFile *f, void *opaque, int version_id)
315 {
316     PITChannelState *s;
317     int i;
318     
319     if (version_id != 1)
320         return -EINVAL;
321
322     for(i = 0; i < 3; i++) {
323         s = &pit_channels[i];
324         qemu_get_be32s(f, &s->count);
325         qemu_get_be16s(f, &s->latched_count);
326         qemu_get_8s(f, &s->rw_state);
327         qemu_get_8s(f, &s->mode);
328         qemu_get_8s(f, &s->bcd);
329         qemu_get_8s(f, &s->gate);
330         qemu_get_be64s(f, &s->count_load_time);
331         if (s->irq_timer) {
332             qemu_get_be64s(f, &s->next_transition_time);
333             qemu_get_timer(f, s->irq_timer);
334         }
335     }
336     return 0;
337 }
338
339 void pit_init(int base, int irq)
340 {
341     PITChannelState *s;
342     int i;
343
344     for(i = 0;i < 3; i++) {
345         s = &pit_channels[i];
346         if (i == 0) {
347             /* the timer 0 is connected to an IRQ */
348             s->irq_timer = qemu_new_timer(vm_clock, pit_irq_timer, s);
349             s->irq = irq;
350         }
351         s->mode = 3;
352         s->gate = (i != 2);
353         pit_load_count(s, 0);
354     }
355
356     register_savevm("i8254", base, 1, pit_save, pit_load, NULL);
357
358     register_ioport_write(base, 4, 1, pit_ioport_write, NULL);
359     register_ioport_read(base, 3, 1, pit_ioport_read, NULL);
360 }
361