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, | ... | ... |