Commit ec844b96c083340a4c04849c2c1cdc09e85e0595
1 parent
f72e8ff4
pit fixes
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@781 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
4 changed files
with
175 additions
and
88 deletions
Changelog
hw/i8254.c
... | ... | @@ -25,14 +25,36 @@ |
25 | 25 | |
26 | 26 | //#define DEBUG_PIT |
27 | 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 | |
28 | +#define RW_STATE_LSB 1 | |
29 | +#define RW_STATE_MSB 2 | |
30 | +#define RW_STATE_WORD0 3 | |
31 | +#define RW_STATE_WORD1 4 | |
34 | 32 | |
35 | -PITChannelState pit_channels[3]; | |
33 | +typedef struct PITChannelState { | |
34 | + int count; /* can be 65536 */ | |
35 | + uint16_t latched_count; | |
36 | + uint8_t count_latched; | |
37 | + uint8_t status_latched; | |
38 | + uint8_t status; | |
39 | + uint8_t read_state; | |
40 | + uint8_t write_state; | |
41 | + uint8_t write_latch; | |
42 | + uint8_t rw_mode; | |
43 | + uint8_t mode; | |
44 | + uint8_t bcd; /* not supported */ | |
45 | + uint8_t gate; /* timer start */ | |
46 | + int64_t count_load_time; | |
47 | + /* irq handling */ | |
48 | + int64_t next_transition_time; | |
49 | + QEMUTimer *irq_timer; | |
50 | + int irq; | |
51 | +} PITChannelState; | |
52 | + | |
53 | +struct PITState { | |
54 | + PITChannelState channels[3]; | |
55 | +}; | |
56 | + | |
57 | +static PITState pit_state; | |
36 | 58 | |
37 | 59 | static void pit_irq_timer_update(PITChannelState *s, int64_t current_time); |
38 | 60 | |
... | ... | @@ -61,7 +83,7 @@ static int pit_get_count(PITChannelState *s) |
61 | 83 | } |
62 | 84 | |
63 | 85 | /* get pit output bit */ |
64 | -int pit_get_out(PITChannelState *s, int64_t current_time) | |
86 | +static int pit_get_out1(PITChannelState *s, int64_t current_time) | |
65 | 87 | { |
66 | 88 | uint64_t d; |
67 | 89 | int out; |
... | ... | @@ -92,6 +114,12 @@ int pit_get_out(PITChannelState *s, int64_t current_time) |
92 | 114 | return out; |
93 | 115 | } |
94 | 116 | |
117 | +int pit_get_out(PITState *pit, int channel, int64_t current_time) | |
118 | +{ | |
119 | + PITChannelState *s = &pit->channels[channel]; | |
120 | + return pit_get_out1(s, current_time); | |
121 | +} | |
122 | + | |
95 | 123 | /* return -1 if no transition will occur. */ |
96 | 124 | static int64_t pit_get_next_transition_time(PITChannelState *s, |
97 | 125 | int64_t current_time) |
... | ... | @@ -144,8 +172,10 @@ static int64_t pit_get_next_transition_time(PITChannelState *s, |
144 | 172 | } |
145 | 173 | |
146 | 174 | /* val must be 0 or 1 */ |
147 | -void pit_set_gate(PITChannelState *s, int val) | |
175 | +void pit_set_gate(PITState *pit, int channel, int val) | |
148 | 176 | { |
177 | + PITChannelState *s = &pit->channels[channel]; | |
178 | + | |
149 | 179 | switch(s->mode) { |
150 | 180 | default: |
151 | 181 | case 0: |
... | ... | @@ -173,6 +203,12 @@ void pit_set_gate(PITChannelState *s, int val) |
173 | 203 | s->gate = val; |
174 | 204 | } |
175 | 205 | |
206 | +int pit_get_gate(PITState *pit, int channel) | |
207 | +{ | |
208 | + PITChannelState *s = &pit->channels[channel]; | |
209 | + return s->gate; | |
210 | +} | |
211 | + | |
176 | 212 | static inline void pit_load_count(PITChannelState *s, int val) |
177 | 213 | { |
178 | 214 | if (val == 0) |
... | ... | @@ -182,33 +218,62 @@ static inline void pit_load_count(PITChannelState *s, int val) |
182 | 218 | pit_irq_timer_update(s, s->count_load_time); |
183 | 219 | } |
184 | 220 | |
221 | +/* if already latched, do not latch again */ | |
222 | +static void pit_latch_count(PITChannelState *s) | |
223 | +{ | |
224 | + if (!s->count_latched) { | |
225 | + s->latched_count = pit_get_count(s); | |
226 | + s->count_latched = s->rw_mode; | |
227 | + } | |
228 | +} | |
229 | + | |
185 | 230 | static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val) |
186 | 231 | { |
232 | + PITState *pit = opaque; | |
187 | 233 | int channel, access; |
188 | 234 | PITChannelState *s; |
189 | 235 | |
190 | 236 | addr &= 3; |
191 | 237 | if (addr == 3) { |
192 | 238 | 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; | |
239 | + if (channel == 3) { | |
240 | + /* read back command */ | |
241 | + for(channel = 0; channel < 3; channel++) { | |
242 | + s = &pit->channels[channel]; | |
243 | + if (val & (2 << channel)) { | |
244 | + if (!(val & 0x20)) { | |
245 | + pit_latch_count(s); | |
246 | + } | |
247 | + if (!(val & 0x10) && !s->status_latched) { | |
248 | + /* status latch */ | |
249 | + /* XXX: add BCD and null count */ | |
250 | + s->status = (pit_get_out1(s, qemu_get_clock(vm_clock)) << 7) | | |
251 | + (s->rw_mode << 4) | | |
252 | + (s->mode << 1) | | |
253 | + s->bcd; | |
254 | + s->status_latched = 1; | |
255 | + } | |
256 | + } | |
257 | + } | |
258 | + } else { | |
259 | + s = &pit->channels[channel]; | |
260 | + access = (val >> 4) & 3; | |
261 | + if (access == 0) { | |
262 | + pit_latch_count(s); | |
263 | + } else { | |
264 | + s->rw_mode = access; | |
265 | + s->read_state = access; | |
266 | + s->write_state = access; | |
267 | + | |
268 | + s->mode = (val >> 1) & 7; | |
269 | + s->bcd = val & 1; | |
270 | + /* XXX: update irq timer ? */ | |
271 | + } | |
208 | 272 | } |
209 | 273 | } else { |
210 | - s = &pit_channels[addr]; | |
211 | - switch(s->rw_state) { | |
274 | + s = &pit->channels[addr]; | |
275 | + switch(s->write_state) { | |
276 | + default: | |
212 | 277 | case RW_STATE_LSB: |
213 | 278 | pit_load_count(s, val); |
214 | 279 | break; |
... | ... | @@ -216,13 +281,12 @@ static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val) |
216 | 281 | pit_load_count(s, val << 8); |
217 | 282 | break; |
218 | 283 | case RW_STATE_WORD0: |
284 | + s->write_latch = val; | |
285 | + s->write_state = RW_STATE_WORD1; | |
286 | + break; | |
219 | 287 | 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; | |
288 | + pit_load_count(s, s->write_latch | (val << 8)); | |
289 | + s->write_state = RW_STATE_WORD0; | |
226 | 290 | break; |
227 | 291 | } |
228 | 292 | } |
... | ... | @@ -230,33 +294,53 @@ static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val) |
230 | 294 | |
231 | 295 | static uint32_t pit_ioport_read(void *opaque, uint32_t addr) |
232 | 296 | { |
297 | + PITState *pit = opaque; | |
233 | 298 | int ret, count; |
234 | 299 | PITChannelState *s; |
235 | 300 | |
236 | 301 | 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) | |
302 | + s = &pit->channels[addr]; | |
303 | + if (s->status_latched) { | |
304 | + s->status_latched = 0; | |
305 | + ret = s->status; | |
306 | + } else if (s->count_latched) { | |
307 | + switch(s->count_latched) { | |
308 | + default: | |
309 | + case RW_STATE_LSB: | |
310 | + ret = s->latched_count & 0xff; | |
311 | + s->count_latched = 0; | |
312 | + break; | |
313 | + case RW_STATE_MSB: | |
255 | 314 | ret = s->latched_count >> 8; |
256 | - else | |
315 | + s->count_latched = 0; | |
316 | + break; | |
317 | + case RW_STATE_WORD0: | |
257 | 318 | ret = s->latched_count & 0xff; |
258 | - s->rw_state ^= 1; | |
259 | - break; | |
319 | + s->count_latched = RW_STATE_MSB; | |
320 | + break; | |
321 | + } | |
322 | + } else { | |
323 | + switch(s->read_state) { | |
324 | + default: | |
325 | + case RW_STATE_LSB: | |
326 | + count = pit_get_count(s); | |
327 | + ret = count & 0xff; | |
328 | + break; | |
329 | + case RW_STATE_MSB: | |
330 | + count = pit_get_count(s); | |
331 | + ret = (count >> 8) & 0xff; | |
332 | + break; | |
333 | + case RW_STATE_WORD0: | |
334 | + count = pit_get_count(s); | |
335 | + ret = count & 0xff; | |
336 | + s->read_state = RW_STATE_WORD1; | |
337 | + break; | |
338 | + case RW_STATE_WORD1: | |
339 | + count = pit_get_count(s); | |
340 | + ret = (count >> 8) & 0xff; | |
341 | + s->read_state = RW_STATE_WORD0; | |
342 | + break; | |
343 | + } | |
260 | 344 | } |
261 | 345 | return ret; |
262 | 346 | } |
... | ... | @@ -269,7 +353,7 @@ static void pit_irq_timer_update(PITChannelState *s, int64_t current_time) |
269 | 353 | if (!s->irq_timer) |
270 | 354 | return; |
271 | 355 | expire_time = pit_get_next_transition_time(s, current_time); |
272 | - irq_level = pit_get_out(s, current_time); | |
356 | + irq_level = pit_get_out1(s, current_time); | |
273 | 357 | pic_set_irq(s->irq, irq_level); |
274 | 358 | #ifdef DEBUG_PIT |
275 | 359 | printf("irq_level=%d next_delay=%f\n", |
... | ... | @@ -292,14 +376,21 @@ static void pit_irq_timer(void *opaque) |
292 | 376 | |
293 | 377 | static void pit_save(QEMUFile *f, void *opaque) |
294 | 378 | { |
379 | + PITState *pit = opaque; | |
295 | 380 | PITChannelState *s; |
296 | 381 | int i; |
297 | 382 | |
298 | 383 | for(i = 0; i < 3; i++) { |
299 | - s = &pit_channels[i]; | |
384 | + s = &pit->channels[i]; | |
300 | 385 | qemu_put_be32s(f, &s->count); |
301 | 386 | qemu_put_be16s(f, &s->latched_count); |
302 | - qemu_put_8s(f, &s->rw_state); | |
387 | + qemu_put_8s(f, &s->count_latched); | |
388 | + qemu_put_8s(f, &s->status_latched); | |
389 | + qemu_put_8s(f, &s->status); | |
390 | + qemu_put_8s(f, &s->read_state); | |
391 | + qemu_put_8s(f, &s->write_state); | |
392 | + qemu_put_8s(f, &s->write_latch); | |
393 | + qemu_put_8s(f, &s->rw_mode); | |
303 | 394 | qemu_put_8s(f, &s->mode); |
304 | 395 | qemu_put_8s(f, &s->bcd); |
305 | 396 | qemu_put_8s(f, &s->gate); |
... | ... | @@ -313,6 +404,7 @@ static void pit_save(QEMUFile *f, void *opaque) |
313 | 404 | |
314 | 405 | static int pit_load(QEMUFile *f, void *opaque, int version_id) |
315 | 406 | { |
407 | + PITState *pit = opaque; | |
316 | 408 | PITChannelState *s; |
317 | 409 | int i; |
318 | 410 | |
... | ... | @@ -320,10 +412,16 @@ static int pit_load(QEMUFile *f, void *opaque, int version_id) |
320 | 412 | return -EINVAL; |
321 | 413 | |
322 | 414 | for(i = 0; i < 3; i++) { |
323 | - s = &pit_channels[i]; | |
415 | + s = &pit->channels[i]; | |
324 | 416 | qemu_get_be32s(f, &s->count); |
325 | 417 | qemu_get_be16s(f, &s->latched_count); |
326 | - qemu_get_8s(f, &s->rw_state); | |
418 | + qemu_get_8s(f, &s->count_latched); | |
419 | + qemu_get_8s(f, &s->status_latched); | |
420 | + qemu_get_8s(f, &s->status); | |
421 | + qemu_get_8s(f, &s->read_state); | |
422 | + qemu_get_8s(f, &s->write_state); | |
423 | + qemu_get_8s(f, &s->write_latch); | |
424 | + qemu_get_8s(f, &s->rw_mode); | |
327 | 425 | qemu_get_8s(f, &s->mode); |
328 | 426 | qemu_get_8s(f, &s->bcd); |
329 | 427 | qemu_get_8s(f, &s->gate); |
... | ... | @@ -336,13 +434,14 @@ static int pit_load(QEMUFile *f, void *opaque, int version_id) |
336 | 434 | return 0; |
337 | 435 | } |
338 | 436 | |
339 | -void pit_init(int base, int irq) | |
437 | +PITState *pit_init(int base, int irq) | |
340 | 438 | { |
439 | + PITState *pit = &pit_state; | |
341 | 440 | PITChannelState *s; |
342 | 441 | int i; |
343 | 442 | |
344 | 443 | for(i = 0;i < 3; i++) { |
345 | - s = &pit_channels[i]; | |
444 | + s = &pit->channels[i]; | |
346 | 445 | if (i == 0) { |
347 | 446 | /* the timer 0 is connected to an IRQ */ |
348 | 447 | s->irq_timer = qemu_new_timer(vm_clock, pit_irq_timer, s); |
... | ... | @@ -353,9 +452,9 @@ void pit_init(int base, int irq) |
353 | 452 | pit_load_count(s, 0); |
354 | 453 | } |
355 | 454 | |
356 | - register_savevm("i8254", base, 1, pit_save, pit_load, NULL); | |
455 | + register_savevm("i8254", base, 1, pit_save, pit_load, pit); | |
357 | 456 | |
358 | - register_ioport_write(base, 4, 1, pit_ioport_write, NULL); | |
359 | - register_ioport_read(base, 3, 1, pit_ioport_read, NULL); | |
457 | + register_ioport_write(base, 4, 1, pit_ioport_write, pit); | |
458 | + register_ioport_read(base, 3, 1, pit_ioport_read, pit); | |
459 | + return pit; | |
360 | 460 | } |
361 | - | ... | ... |
hw/pc.c
... | ... | @@ -39,6 +39,7 @@ int speaker_data_on; |
39 | 39 | int dummy_refresh_clock; |
40 | 40 | static fdctrl_t *floppy_controller; |
41 | 41 | static RTCState *rtc_state; |
42 | +static PITState *pit; | |
42 | 43 | |
43 | 44 | static void ioport80_write(void *opaque, uint32_t addr, uint32_t data) |
44 | 45 | { |
... | ... | @@ -169,15 +170,15 @@ static void cmos_init(int ram_size, int boot_device) |
169 | 170 | static void speaker_ioport_write(void *opaque, uint32_t addr, uint32_t val) |
170 | 171 | { |
171 | 172 | speaker_data_on = (val >> 1) & 1; |
172 | - pit_set_gate(&pit_channels[2], val & 1); | |
173 | + pit_set_gate(pit, 2, val & 1); | |
173 | 174 | } |
174 | 175 | |
175 | 176 | static uint32_t speaker_ioport_read(void *opaque, uint32_t addr) |
176 | 177 | { |
177 | 178 | int out; |
178 | - out = pit_get_out(&pit_channels[2], qemu_get_clock(vm_clock)); | |
179 | + out = pit_get_out(pit, 2, qemu_get_clock(vm_clock)); | |
179 | 180 | dummy_refresh_clock ^= 1; |
180 | - return (speaker_data_on << 1) | pit_channels[2].gate | (out << 5) | | |
181 | + return (speaker_data_on << 1) | pit_get_gate(pit, 2) | (out << 5) | | |
181 | 182 | (dummy_refresh_clock << 4); |
182 | 183 | } |
183 | 184 | |
... | ... | @@ -381,7 +382,7 @@ void pc_init(int ram_size, int vga_ram_size, int boot_device, |
381 | 382 | register_ioport_write(0x92, 1, 1, ioport92_write, NULL); |
382 | 383 | |
383 | 384 | pic_init(); |
384 | - pit_init(0x40, 0); | |
385 | + pit = pit_init(0x40, 0); | |
385 | 386 | |
386 | 387 | fd = serial_open_device(); |
387 | 388 | serial_init(0x3f8, 4, fd); | ... | ... |
vl.h
... | ... | @@ -481,26 +481,12 @@ void pic_info(void); |
481 | 481 | |
482 | 482 | #define PIT_FREQ 1193182 |
483 | 483 | |
484 | -typedef struct PITChannelState { | |
485 | - int count; /* can be 65536 */ | |
486 | - uint16_t latched_count; | |
487 | - uint8_t rw_state; | |
488 | - uint8_t mode; | |
489 | - uint8_t bcd; /* not supported */ | |
490 | - uint8_t gate; /* timer start */ | |
491 | - int64_t count_load_time; | |
492 | - /* irq handling */ | |
493 | - int64_t next_transition_time; | |
494 | - QEMUTimer *irq_timer; | |
495 | - int irq; | |
496 | -} PITChannelState; | |
497 | - | |
498 | -extern PITChannelState pit_channels[3]; | |
499 | - | |
500 | -void pit_init(int base, int irq); | |
501 | -void pit_set_gate(PITChannelState *s, int val); | |
502 | -int pit_get_out(PITChannelState *s, int64_t current_time); | |
503 | -int pit_get_out_edges(PITChannelState *s); | |
484 | +typedef struct PITState PITState; | |
485 | + | |
486 | +PITState *pit_init(int base, int irq); | |
487 | +void pit_set_gate(PITState *pit, int channel, int val); | |
488 | +int pit_get_gate(PITState *pit, int channel); | |
489 | +int pit_get_out(PITState *pit, int channel, int64_t current_time); | |
504 | 490 | |
505 | 491 | /* pc.c */ |
506 | 492 | void pc_init(int ram_size, int vga_ram_size, int boot_device, | ... | ... |