--- trunk/src/devices/dev_mc146818.c 2007/10/08 16:19:56 24 +++ trunk/src/devices/dev_mc146818.c 2007/10/08 16:20:58 32 @@ -25,7 +25,7 @@ * SUCH DAMAGE. * * - * $Id: dev_mc146818.c,v 1.86 2006/06/22 13:22:41 debug Exp $ + * $Id: dev_mc146818.c,v 1.91 2006/10/07 03:20:19 debug Exp $ * * MC146818 real-time clock, used by many different machines types. * (DS1687 as used in some other machines is also similar to the MC146818.) @@ -48,6 +48,7 @@ #include "machine.h" #include "memory.h" #include "misc.h" +#include "timer.h" #include "mc146818reg.h" @@ -63,28 +64,28 @@ /* 256 on DECstation, SGI uses reg at 72*4 as the Century */ #define N_REGISTERS 1024 struct mc_data { - int access_style; - int last_addr; + int access_style; + int last_addr; - int register_choice; - int reg[N_REGISTERS]; - int addrdiv; + int register_choice; + int reg[N_REGISTERS]; + int addrdiv; + + int use_bcd; + + int timebase_hz; + int interrupt_hz; + int old_interrupt_hz; + int irq_nr; + struct timer *timer; + volatile int pending_timer_interrupts; + + int previous_second; + int n_seconds_elapsed; + int uip_threshold; - int use_bcd; - - int timebase_hz; - int interrupt_hz; - int irq_nr; - - int previous_second; - int n_seconds_elapsed; - int uip_threshold; - - int interrupt_every_x_cycles; - int cycles_left_until_interrupt; - - int ugly_netbsd_prep_hack_done; - int ugly_netbsd_prep_hack_sec; + int ugly_netbsd_prep_hack_done; + int ugly_netbsd_prep_hack_sec; }; @@ -101,75 +102,46 @@ /* - * recalc_interrupt_cycle(): + * timer_tick(): * - * If automatic_clock_adjustment is turned on, then emulated_hz is modified - * dynamically. We have to recalculate how often interrupts are to be - * triggered. + * Called d->interrupt_hz times per (real-world) second. */ -static void recalc_interrupt_cycle(struct cpu *cpu, struct mc_data *d) +static void timer_tick(struct timer *timer, void *extra) { - int64_t emulated_hz = cpu->machine->emulated_hz; -#if 0 - static int warning_printed = 0; - - /* - * A hack to make Ultrix run, even on very fast host machines. - * - * (Ultrix was probably never meant to be run on machines with - * faster CPUs than around 33 MHz or so.) - */ - if (d->access_style == MC146818_DEC && emulated_hz > 30000000) { - if (!warning_printed) { - fatal("\n*********************************************" - "**********************************\n\n Your hos" - "t machine is too fast! The emulated CPU speed wil" - "l be limited to\n 30 MHz, and clocks inside the" - " emulated environment might go faster than\n in" - " the real world. You have been warned.\n\n******" - "*************************************************" - "************************\n\n"); - warning_printed = 1; - } - - emulated_hz = 30000000; - } -#endif - - if (d->interrupt_hz > 0) - d->interrupt_every_x_cycles = emulated_hz / d->interrupt_hz; - else - d->interrupt_every_x_cycles = 0; + struct mc_data *d = (struct mc_data *) extra; + d->pending_timer_interrupts ++; } -/* - * dev_mc146818_tick(): - */ -void dev_mc146818_tick(struct cpu *cpu, void *extra) +DEVICE_TICK(mc146818) { struct mc_data *d = extra; + int pti = d->pending_timer_interrupts; - recalc_interrupt_cycle(cpu, d); + if ((d->reg[MC_REGB * 4] & MC_REGB_PIE) && pti > 0) { + static int warned = 0; + if (pti > 800 && !warned) { + warned = 1; + fatal("[ WARNING: MC146818 interrupts lost, " + "host too slow? ]\n"); + } - if ((d->reg[MC_REGB * 4] & MC_REGB_PIE) && - d->interrupt_every_x_cycles > 0) { - d->cycles_left_until_interrupt -= (1 << TICK_SHIFT); - - if (d->cycles_left_until_interrupt < 0 || - d->cycles_left_until_interrupt >= - d->interrupt_every_x_cycles) { - /* fatal("[ rtc interrupt (every %i cycles) ]\n", - d->interrupt_every_x_cycles); */ - cpu_interrupt(cpu, d->irq_nr); +#if 0 + /* For debugging, to see how much the interrupts are + lagging behind the real clock: */ + { + static int x = 0; + if (++x == 1) { + x = 0; + printf("%i ", pti); + fflush(stdout); + } + } +#endif - d->reg[MC_REGC * 4] |= MC_REGC_PF; + cpu_interrupt(cpu, d->irq_nr); - /* Reset the cycle countdown: */ - while (d->cycles_left_until_interrupt < 0) - d->cycles_left_until_interrupt += - d->interrupt_every_x_cycles; - } + d->reg[MC_REGC * 4] |= MC_REGC_PF; } if (d->reg[MC_REGC * 4] & MC_REGC_UF || @@ -241,6 +213,12 @@ */ switch (d->access_style) { case MC146818_ALGOR: + /* + * NetBSD/evbmips sources indicate that the Algor year base + * is 1920. This makes the time work with NetBSD in Malta + * emulation. However, for Linux, commenting out this line + * works better. (TODO: Find a way to make both work?) + */ d->reg[4 * MC_YEAR] += 80; break; case MC146818_ARC_NEC: @@ -474,27 +452,29 @@ ; } - recalc_interrupt_cycle(cpu, d); - - d->cycles_left_until_interrupt = - d->interrupt_every_x_cycles; + if (d->interrupt_hz != d->old_interrupt_hz) { + debug("[ rtc changed to interrupt at %i Hz ]\n", + d->interrupt_hz); + + d->old_interrupt_hz = d->interrupt_hz; + + if (d->timer == NULL) + d->timer = timer_add(d->interrupt_hz, + timer_tick, d); + else + timer_update_frequency(d->timer, + d->interrupt_hz); + } d->reg[MC_REGA * 4] = data[0] & (MC_REGA_RSMASK | MC_REGA_DVMASK); - - debug("[ rtc set to interrupt every %i:th cycle ]\n", - d->interrupt_every_x_cycles); break; case MC_REGB*4: - if (((data[0] ^ d->reg[MC_REGB*4]) & MC_REGB_PIE)) - d->cycles_left_until_interrupt = - d->interrupt_every_x_cycles; d->reg[MC_REGB*4] = data[0]; if (!(data[0] & MC_REGB_PIE)) { cpu_interrupt_ack(cpu, d->irq_nr); - /* d->cycles_left_until_interrupt = - d->interrupt_every_x_cycles; */ } + /* debug("[ mc146818: write to MC_REGB, data[0] " "= 0x%02x ]\n", data[0]); */ break; @@ -587,8 +567,14 @@ if (relative_addr == MC_REGC*4) { cpu_interrupt_ack(cpu, d->irq_nr); - /* d->cycles_left_until_interrupt = - d->interrupt_every_x_cycles; */ + + /* + * Acknowledging an interrupt decreases the + * number of pending "real world" timer ticks. + */ + if (d->reg[MC_REGC * 4] & MC_REGC_PF) + d->pending_timer_interrupts --; + d->reg[MC_REGC * 4] = 0x00; } }