/* This file is included into vb.c and cannot be compiled on its own. */ #ifdef VBAPI /***************************** Module Functions ******************************/ /* Update the counter to a new value */ static void tmrUpdate(VB *sim, uint16_t value) { if (value == 0 && sim->tmr.counter != 0) { if (sim->tmr.t_enb) sim->tmr.z_stat = 1; if (sim->tmr.tim_z_int) sim->cpu.irq |= 0x0002; } sim->tmr.counter = value; } /***************************** Library Functions *****************************/ /* Process component */ static void tmrEmulate(VB *sim, uint32_t clocks) { /* Timer is disabled */ if (!sim->tmr.t_enb) return; /* Process all clocks */ for (;;) { /* Next tick is after time to process */ if (sim->tmr.clocks > clocks) { sim->tmr.clocks -= clocks; sim->tmr.until -= clocks; return; } /* Advance forward the component's number of clocks */ clocks -= sim->tmr.clocks; sim->tmr.until -= sim->tmr.clocks; sim->tmr.clocks = sim->tmr.t_clk_sel ? 400 : 2000; /* Advance to the next counter value */ tmrUpdate(sim, sim->tmr.counter == 0 ? sim->tmr.reload : sim->tmr.counter - 1); if (sim->tmr.counter == 0) sim->tmr.until = sim->tmr.clocks * ((uint32_t)sim->tmr.reload + 1); } } /* Read a value from TCR */ static int32_t tmrReadControl(VB *sim) { return 0xE4 | sim->tmr.t_clk_sel << 4 | sim->tmr.tim_z_int << 3 | sim->tmr.z_stat << 1 | sim->tmr.t_enb ; } /* Simulate a hardware reset */ static void tmrReset(VB *sim) { /* Normal */ sim->tmr.t_enb = 0; sim->tmr.t_clk_sel = 0; sim->tmr.tim_z_int = 0; sim->tmr.z_stat = 0; /* Other */ sim->tmr.counter = 0xFFFF; sim->tmr.reload = 0x0000; } /* Determine how many clocks are guaranteed to process */ static uint32_t tmrUntil(VB *sim, uint32_t clocks) { return sim->tmr.t_enb && sim->tmr.until < clocks ? sim->tmr.until : clocks; } /* Write a value to TCR */ static void tmrWriteControl(VB *sim, uint8_t value) { /* Clear interrupt status */ if ( (value & 0x04) && /* Z-Stat-Clr */ ( sim->tmr.counter != 0 || !sim->tmr.t_enb ) ) { sim->tmr.z_stat = sim->tmr.counter != 0; sim->cpu.irq &= ~0x0002; } /* Parse fields */ sim->tmr.t_clk_sel = value >> 4 & 1; sim->tmr.tim_z_int = value >> 3 & 1; sim->tmr.t_enb = value & 1; /* The interrupt is disabled */ if (!sim->tmr.tim_z_int) sim->cpu.irq &= ~0x0002; /* Configure countdowns */ sim->tmr.clocks = sim->tmr.t_clk_sel ? 400 : 2000; sim->tmr.until = sim->tmr.clocks * (sim->tmr.counter == 0 ? (uint32_t) sim->tmr.reload + 1 : sim->tmr.counter); /* TODO: Will Z-Stat raise an interrupt when Tim-Z-Int is set? */ } /* Write to the high data register */ static void tmrWriteHigh(VB *sim, uint8_t value) { sim->tmr.reload = (uint16_t) value << 8 | (sim->tmr.reload & 0x00FF); tmrUpdate(sim, sim->tmr.reload); } /* Write to the low data register */ static void tmrWriteLow(VB *sim, uint8_t value) { sim->tmr.reload = (sim->tmr.reload & 0xFF00) | value; tmrUpdate(sim, sim->tmr.reload); } #endif /* VBAPI */