--- trunk/src/devices/dev_8253.c 2007/10/08 16:18:11 6 +++ trunk/src/devices/dev_8253.c 2007/10/08 16:21:17 34 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005 Anders Gavare. All rights reserved. + * Copyright (C) 2005-2007 Anders Gavare. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -25,11 +25,16 @@ * SUCH DAMAGE. * * - * $Id: dev_8253.c,v 1.4 2005/05/21 07:41:11 debug Exp $ - * - * 8253/8254 Programmable Interval Timer. + * $Id: dev_8253.c,v 1.18 2006/12/30 13:30:57 debug Exp $ * - * This is mostly bogus. + * Intel 8253/8254 Programmable Interval Timer + * + * TODO/NOTE: + * The timers don't really count down. Timer 0 causes clock interrupts + * at a specific frequency, but reading the counter register would not + * result in anything meaningful. + * + * (Split counter[] into reset value and current value.) */ #include @@ -38,74 +43,193 @@ #include "cpu.h" #include "device.h" -#include "devices.h" #include "emul.h" +#include "interrupt.h" #include "machine.h" #include "memory.h" #include "misc.h" +#include "timer.h" + +#include "i8253reg.h" +#define debug fatal + #define DEV_8253_LENGTH 4 #define TICK_SHIFT 14 struct pit8253_data { - int irq_nr; + int in_use; + int counter_select; + uint8_t mode_byte; + + int mode[3]; + int counter[3]; + + int hz[3]; + + struct timer *timer0; + struct interrupt irq; + int pending_interrupts_timer0; }; -/* - * dev_8253_tick(): - */ -void dev_8253_tick(struct cpu *cpu, void *extra) +static void timer0_tick(struct timer *t, void *extra) { struct pit8253_data *d = (struct pit8253_data *) extra; - cpu_interrupt(cpu, d->irq_nr); + d->pending_interrupts_timer0 ++; + +#if 0 + printf("%i ", d->pending_interrupts_timer0); fflush(stdout); +#endif } -/* - * dev_8253_access(): - */ -int dev_8253_access(struct cpu *cpu, struct memory *mem, - uint64_t relative_addr, unsigned char *data, size_t len, - int writeflag, void *extra) +DEVICE_TICK(8253) +{ + struct pit8253_data *d = (struct pit8253_data *) extra; + + if (!d->in_use) + return; + + switch (d->mode[0] & 0x0e) { + + case I8253_TIMER_INTTC: + if (d->pending_interrupts_timer0 > 0) + INTERRUPT_ASSERT(d->irq); + break; + + case I8253_TIMER_SQWAVE: + case I8253_TIMER_RATEGEN: + break; + + default:fatal("[ 8253: unimplemented mode 0x%x ]\n", d->mode[0] & 0x0e); + exit(1); + } +} + + +DEVICE_ACCESS(8253) { struct pit8253_data *d = (struct pit8253_data *) extra; uint64_t idata = 0, odata = 0; - idata = memory_readmax64(cpu, data, len); + if (writeflag == MEM_WRITE) + idata = memory_readmax64(cpu, data, len); - /* TODO: ack somewhere else */ - cpu_interrupt_ack(cpu, d->irq_nr); + d->in_use = 1; switch (relative_addr) { - case 0x00: + + case I8253_TIMER_CNTR0: + case I8253_TIMER_CNTR1: + case I8253_TIMER_CNTR2: if (writeflag == MEM_WRITE) { - /* TODO */ + switch (d->mode_byte & 0x30) { + case I8253_TIMER_LSB: + case I8253_TIMER_16BIT: + d->counter[relative_addr] &= 0xff00; + d->counter[relative_addr] |= (idata & 0xff); + break; + case I8253_TIMER_MSB: + d->counter[relative_addr] &= 0x00ff; + d->counter[relative_addr] |= ((idata&0xff)<<8); + if (d->counter[relative_addr] != 0) + d->hz[relative_addr] = + I8253_TIMER_FREQ / (float) + d->counter[relative_addr] + 0.5; + else + d->hz[relative_addr] = 0; + debug("[ 8253: counter %i set to %i (%i Hz) " + "]\n", relative_addr, d->counter[ + relative_addr], d->hz[relative_addr]); + switch (relative_addr) { + case 0: if (d->timer0 == NULL) + d->timer0 = timer_add( + d->hz[0], timer0_tick, d); + else + timer_update_frequency( + d->timer0, d->hz[0]); + break; + case 1: fatal("TODO: DMA refresh?\n"); + exit(1); + case 2: fatal("TODO: 8253 tone generation?\n"); + break; + } + break; + default:fatal("[ 8253: huh? writing to counter" + " %i but neither from msb nor lsb? ]\n", + relative_addr); + } } else { - /* TODO */ - odata = 1; -odata = random(); + switch (d->mode_byte & 0x30) { + case I8253_TIMER_LSB: + case I8253_TIMER_16BIT: + odata = d->counter[relative_addr] & 0xff; + break; + case I8253_TIMER_MSB: + odata = (d->counter[relative_addr] >> 8) & 0xff; + break; + default:fatal("[ 8253: huh? reading from counter" + " %i but neither from msb nor lsb? ]\n", + relative_addr); + } } + + /* Switch from LSB to MSB, if accessing as 16-bit word: */ + if ((d->mode_byte & 0x30) == I8253_TIMER_16BIT) + d->mode_byte &= ~I8253_TIMER_LSB; + break; - case 0x03: + + case I8253_TIMER_MODE: if (writeflag == MEM_WRITE) { + d->mode_byte = idata; + d->counter_select = idata >> 6; - /* TODO: other bits */ + if (d->counter_select > 2) { + debug("[ 8253: attempt to select counter 3," + " which doesn't exist. ]\n"); + d->counter_select = 0; + } + + d->mode[d->counter_select] = idata & 0x0e; + + debug("[ 8253: select=%i mode=0x%x ", + d->counter_select, d->mode[d->counter_select]); + if (idata & 0x30) { + switch (idata & 0x30) { + case I8253_TIMER_LSB: + debug("LSB "); + break; + case I8253_TIMER_16BIT: + debug("LSB+"); + case I8253_TIMER_MSB: + debug("MSB "); + } + } + debug("]\n"); + + if (idata & I8253_TIMER_BCD) { + fatal("[ 8253: BCD not yet implemented ]\n"); + exit(1); + } } else { - odata = d->counter_select << 6; + debug("[ 8253: read; can this actually happen? ]\n"); + odata = d->mode_byte; } break; - default: - if (writeflag == MEM_WRITE) { + + default:if (writeflag == MEM_WRITE) { fatal("[ 8253: unimplemented write to address 0x%x" " data=0x%02x ]\n", (int)relative_addr, (int)idata); } else { fatal("[ 8253: unimplemented read from address 0x%x " "]\n", (int)relative_addr); } + exit(1); } if (writeflag == MEM_READ) @@ -115,10 +239,7 @@ } -/* - * devinit_8253(): - */ -int devinit_8253(struct devinit *devinit) +DEVINIT(8253) { struct pit8253_data *d = malloc(sizeof(struct pit8253_data)); @@ -127,14 +248,25 @@ exit(1); } memset(d, 0, sizeof(struct pit8253_data)); - d->irq_nr = devinit->irq_nr; + + d->in_use = devinit->in_use; + + INTERRUPT_CONNECT(devinit->interrupt_path, d->irq); + + /* Don't cause interrupt, by default. */ + d->mode[0] = I8253_TIMER_RATEGEN; + d->mode[1] = I8253_TIMER_RATEGEN; + d->mode[2] = I8253_TIMER_RATEGEN; + + devinit->machine->isa_pic_data.pending_timer_interrupts = + &d->pending_interrupts_timer0; memory_device_register(devinit->machine->memory, devinit->name, devinit->addr, DEV_8253_LENGTH, dev_8253_access, (void *)d, - MEM_DEFAULT, NULL); + DM_DEFAULT, NULL); machine_add_tickfunction(devinit->machine, dev_8253_tick, - d, TICK_SHIFT); + d, TICK_SHIFT, 0.0); return 1; }