filename | src/sh4/timer.c |
changeset | 1127:4b8194e3974c |
prev | 1124:aacaae9812ea |
author | nkeynes |
date | Fri Mar 02 23:49:10 2012 +1000 (12 years ago) |
permissions | -rw-r--r-- |
last change | Android WIP: * Rename gui_jni.c to gui_android.c - now quite android specific. * Implement generic EGL driver with very minimal Java wrapper * Run emulation in separate thread, and implement simple queue for inter-thread communication. * Add menu/action-bar items for start + reset |
file | annotate | diff | log | raw |
nkeynes@23 | 1 | /** |
nkeynes@561 | 2 | * $Id$ |
nkeynes@23 | 3 | * |
nkeynes@23 | 4 | * SH4 Timer/Clock peripheral modules (CPG, TMU, RTC), combined together to |
nkeynes@23 | 5 | * keep things simple (they intertwine a bit). |
nkeynes@23 | 6 | * |
nkeynes@23 | 7 | * Copyright (c) 2005 Nathan Keynes. |
nkeynes@23 | 8 | * |
nkeynes@23 | 9 | * This program is free software; you can redistribute it and/or modify |
nkeynes@23 | 10 | * it under the terms of the GNU General Public License as published by |
nkeynes@23 | 11 | * the Free Software Foundation; either version 2 of the License, or |
nkeynes@23 | 12 | * (at your option) any later version. |
nkeynes@23 | 13 | * |
nkeynes@23 | 14 | * This program is distributed in the hope that it will be useful, |
nkeynes@23 | 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
nkeynes@23 | 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
nkeynes@23 | 17 | * GNU General Public License for more details. |
nkeynes@23 | 18 | */ |
nkeynes@23 | 19 | |
nkeynes@619 | 20 | #include <assert.h> |
nkeynes@619 | 21 | #include "lxdream.h" |
nkeynes@23 | 22 | #include "mem.h" |
nkeynes@23 | 23 | #include "clock.h" |
nkeynes@619 | 24 | #include "eventq.h" |
nkeynes@619 | 25 | #include "sh4/sh4core.h" |
nkeynes@619 | 26 | #include "sh4/sh4mmio.h" |
nkeynes@619 | 27 | #include "sh4/intc.h" |
nkeynes@23 | 28 | |
nkeynes@23 | 29 | /********************************* CPG *************************************/ |
nkeynes@859 | 30 | /* This is the base clock from which all other clocks are derived. |
nkeynes@859 | 31 | * Note: The real clock runs at 33Mhz, which is multiplied by the PLL to |
nkeynes@859 | 32 | * run the instruction clock at 200Mhz. For sake of simplicity/precision, |
nkeynes@859 | 33 | * we instead use 200Mhz as the base rate and divide everything down instead. |
nkeynes@859 | 34 | **/ |
nkeynes@53 | 35 | uint32_t sh4_input_freq = SH4_BASE_RATE; |
nkeynes@53 | 36 | |
nkeynes@414 | 37 | uint32_t sh4_cpu_multiplier = 2000; /* = 0.5 * frequency */ |
nkeynes@414 | 38 | |
nkeynes@53 | 39 | uint32_t sh4_cpu_freq = SH4_BASE_RATE; |
nkeynes@859 | 40 | uint32_t sh4_bus_freq = SH4_BASE_RATE / 2; |
nkeynes@859 | 41 | uint32_t sh4_peripheral_freq = SH4_BASE_RATE / 4; |
nkeynes@53 | 42 | |
nkeynes@53 | 43 | uint32_t sh4_cpu_period = 1000 / SH4_BASE_RATE; /* in nanoseconds */ |
nkeynes@859 | 44 | uint32_t sh4_bus_period = 2* 1000 / SH4_BASE_RATE; |
nkeynes@1124 | 45 | uint32_t sh4_peripheral_period = 4 * 1000 / SH4_BASE_RATE; |
nkeynes@23 | 46 | |
nkeynes@929 | 47 | MMIO_REGION_READ_FN( CPG, reg ) |
nkeynes@23 | 48 | { |
nkeynes@929 | 49 | return MMIO_READ( CPG, reg&0xFFF ); |
nkeynes@23 | 50 | } |
nkeynes@975 | 51 | MMIO_REGION_READ_DEFSUBFNS(CPG) |
nkeynes@23 | 52 | |
nkeynes@53 | 53 | /* CPU + bus dividers (note officially only the first 6 values are valid) */ |
nkeynes@53 | 54 | int ifc_divider[8] = { 1, 2, 3, 4, 5, 8, 8, 8 }; |
nkeynes@53 | 55 | /* Peripheral clock dividers (only first 5 are officially valid) */ |
nkeynes@53 | 56 | int pfc_divider[8] = { 2, 3, 4, 6, 8, 8, 8, 8 }; |
nkeynes@53 | 57 | |
nkeynes@929 | 58 | MMIO_REGION_WRITE_FN( CPG, reg, val ) |
nkeynes@23 | 59 | { |
nkeynes@53 | 60 | uint32_t div; |
nkeynes@859 | 61 | uint32_t primary_clock = sh4_input_freq; |
nkeynes@929 | 62 | reg &= 0xFFF; |
nkeynes@53 | 63 | switch( reg ) { |
nkeynes@53 | 64 | case FRQCR: /* Frequency control */ |
nkeynes@859 | 65 | if( (val & FRQCR_PLL1EN) == 0 ) |
nkeynes@859 | 66 | primary_clock /= 6; |
nkeynes@736 | 67 | div = ifc_divider[(val >> 6) & 0x07]; |
nkeynes@859 | 68 | sh4_cpu_freq = primary_clock / div; |
nkeynes@736 | 69 | sh4_cpu_period = sh4_cpu_multiplier * div / sh4_input_freq; |
nkeynes@736 | 70 | div = ifc_divider[(val >> 3) & 0x07]; |
nkeynes@859 | 71 | sh4_bus_freq = primary_clock / div; |
nkeynes@736 | 72 | sh4_bus_period = 1000 * div / sh4_input_freq; |
nkeynes@736 | 73 | div = pfc_divider[val & 0x07]; |
nkeynes@859 | 74 | sh4_peripheral_freq = primary_clock / div; |
nkeynes@736 | 75 | sh4_peripheral_period = 1000 * div / sh4_input_freq; |
nkeynes@53 | 76 | |
nkeynes@736 | 77 | /* Update everything that depends on the peripheral frequency */ |
nkeynes@736 | 78 | SCIF_update_line_speed(); |
nkeynes@736 | 79 | break; |
nkeynes@53 | 80 | case WTCSR: /* Watchdog timer */ |
nkeynes@736 | 81 | break; |
nkeynes@53 | 82 | } |
nkeynes@736 | 83 | |
nkeynes@23 | 84 | MMIO_WRITE( CPG, reg, val ); |
nkeynes@23 | 85 | } |
nkeynes@23 | 86 | |
nkeynes@260 | 87 | /** |
nkeynes@260 | 88 | * We don't really know what the default reset value is as it's determined |
nkeynes@260 | 89 | * by the mode select pins. This is the standard value that the BIOS sets, |
nkeynes@260 | 90 | * however, so it works for now. |
nkeynes@260 | 91 | */ |
nkeynes@260 | 92 | void CPG_reset( ) |
nkeynes@260 | 93 | { |
nkeynes@260 | 94 | mmio_region_CPG_write( FRQCR, 0x0E0A ); |
nkeynes@260 | 95 | } |
nkeynes@260 | 96 | |
nkeynes@260 | 97 | |
nkeynes@23 | 98 | /********************************** RTC *************************************/ |
nkeynes@23 | 99 | |
nkeynes@53 | 100 | uint32_t rtc_output_period; |
nkeynes@53 | 101 | |
nkeynes@929 | 102 | MMIO_REGION_READ_FN( RTC, reg ) |
nkeynes@23 | 103 | { |
nkeynes@929 | 104 | return MMIO_READ( RTC, reg &0xFFF ); |
nkeynes@23 | 105 | } |
nkeynes@975 | 106 | MMIO_REGION_READ_DEFSUBFNS(RTC) |
nkeynes@23 | 107 | |
nkeynes@929 | 108 | MMIO_REGION_WRITE_FN( RTC, reg, val ) |
nkeynes@23 | 109 | { |
nkeynes@929 | 110 | MMIO_WRITE( RTC, reg &0xFFF, val ); |
nkeynes@23 | 111 | } |
nkeynes@23 | 112 | |
nkeynes@23 | 113 | /********************************** TMU *************************************/ |
nkeynes@23 | 114 | |
nkeynes@1124 | 115 | #define TCR_ICPF 0x0200 |
nkeynes@1124 | 116 | #define TCR_UNF 0x0100 |
nkeynes@1124 | 117 | #define TCR_UNIE 0x0020 |
nkeynes@1124 | 118 | |
nkeynes@1124 | 119 | #define TCR_IRQ_ACTIVE (TCR_UNF|TCR_UNIE) |
nkeynes@1124 | 120 | |
nkeynes@619 | 121 | #define TMU_IS_RUNNING(timer) (MMIO_READ(TMU,TSTR) & (1<<timer)) |
nkeynes@619 | 122 | |
nkeynes@1124 | 123 | struct TMU_timer { |
nkeynes@1124 | 124 | uint32_t timer_period; |
nkeynes@1124 | 125 | uint32_t timer_remainder; /* left-over cycles from last count */ |
nkeynes@1124 | 126 | uint32_t timer_run; /* cycles already run from this slice */ |
nkeynes@1124 | 127 | }; |
nkeynes@1124 | 128 | |
nkeynes@1124 | 129 | static struct TMU_timer TMU_timers[3]; |
nkeynes@1124 | 130 | |
nkeynes@260 | 131 | uint32_t TMU_count( int timer, uint32_t nanosecs ); |
nkeynes@1124 | 132 | void TMU_schedule_timer( int timer ); |
nkeynes@260 | 133 | |
nkeynes@619 | 134 | void TMU_event_callback( int eventid ) |
nkeynes@619 | 135 | { |
nkeynes@619 | 136 | TMU_count( eventid - EVENT_TMU0, sh4r.slice_cycle ); |
nkeynes@1124 | 137 | assert( MMIO_READ( TMU, TCR0 + (eventid - EVENT_TMU0)*12 ) & 0x100 ); |
nkeynes@619 | 138 | } |
nkeynes@619 | 139 | |
nkeynes@619 | 140 | void TMU_init(void) |
nkeynes@619 | 141 | { |
nkeynes@619 | 142 | register_event_callback( EVENT_TMU0, TMU_event_callback ); |
nkeynes@619 | 143 | register_event_callback( EVENT_TMU1, TMU_event_callback ); |
nkeynes@619 | 144 | register_event_callback( EVENT_TMU2, TMU_event_callback ); |
nkeynes@619 | 145 | } |
nkeynes@260 | 146 | |
nkeynes@1124 | 147 | void TMU_dump(unsigned timer) |
nkeynes@1124 | 148 | { |
nkeynes@1124 | 149 | fprintf(stderr, "Timer %d: %s %08x/%08x %dns run: %08X - %08X\n", |
nkeynes@1124 | 150 | timer, TMU_IS_RUNNING(timer) ? "running" : "stopped", |
nkeynes@1124 | 151 | MMIO_READ(TMU, TCNT0 + (timer*12)), MMIO_READ(TMU, TCOR0 + (timer*12)), |
nkeynes@1124 | 152 | TMU_timers[timer].timer_period, |
nkeynes@1124 | 153 | TMU_timers[timer].timer_run, |
nkeynes@1124 | 154 | TMU_timers[timer].timer_remainder ); |
nkeynes@1124 | 155 | } |
nkeynes@53 | 156 | |
nkeynes@23 | 157 | |
nkeynes@115 | 158 | void TMU_set_timer_control( int timer, int tcr ) |
nkeynes@53 | 159 | { |
nkeynes@53 | 160 | uint32_t period = 1; |
nkeynes@115 | 161 | uint32_t oldtcr = MMIO_READ( TMU, TCR0 + (12*timer) ); |
nkeynes@115 | 162 | |
nkeynes@115 | 163 | if( (oldtcr & TCR_UNF) == 0 ) { |
nkeynes@736 | 164 | tcr = tcr & (~TCR_UNF); |
nkeynes@115 | 165 | } else { |
nkeynes@736 | 166 | if( ((oldtcr & TCR_UNIE) == 0) && |
nkeynes@736 | 167 | (tcr & TCR_IRQ_ACTIVE) == TCR_IRQ_ACTIVE ) { |
nkeynes@736 | 168 | intc_raise_interrupt( INT_TMU_TUNI0 + timer ); |
nkeynes@736 | 169 | } else if( (oldtcr & TCR_UNIE) != 0 && |
nkeynes@736 | 170 | (tcr & TCR_IRQ_ACTIVE) != TCR_IRQ_ACTIVE ) { |
nkeynes@736 | 171 | intc_clear_interrupt( INT_TMU_TUNI0 + timer ); |
nkeynes@736 | 172 | } |
nkeynes@115 | 173 | } |
nkeynes@115 | 174 | |
nkeynes@53 | 175 | switch( tcr & 0x07 ) { |
nkeynes@53 | 176 | case 0: |
nkeynes@736 | 177 | period = sh4_peripheral_period << 2 ; |
nkeynes@736 | 178 | break; |
nkeynes@53 | 179 | case 1: |
nkeynes@736 | 180 | period = sh4_peripheral_period << 4; |
nkeynes@736 | 181 | break; |
nkeynes@53 | 182 | case 2: |
nkeynes@736 | 183 | period = sh4_peripheral_period << 6; |
nkeynes@736 | 184 | break; |
nkeynes@53 | 185 | case 3: |
nkeynes@736 | 186 | period = sh4_peripheral_period << 8; |
nkeynes@736 | 187 | break; |
nkeynes@53 | 188 | case 4: |
nkeynes@736 | 189 | period = sh4_peripheral_period << 10; |
nkeynes@736 | 190 | break; |
nkeynes@53 | 191 | case 5: |
nkeynes@736 | 192 | /* Illegal value. */ |
nkeynes@736 | 193 | ERROR( "TMU %d period set to illegal value (5)", timer ); |
nkeynes@736 | 194 | period = sh4_peripheral_period << 12; /* for something to do */ |
nkeynes@736 | 195 | break; |
nkeynes@53 | 196 | case 6: |
nkeynes@736 | 197 | period = rtc_output_period; |
nkeynes@736 | 198 | break; |
nkeynes@53 | 199 | case 7: |
nkeynes@736 | 200 | /* External clock... Hrm? */ |
nkeynes@736 | 201 | period = sh4_peripheral_period; /* I dunno... */ |
nkeynes@736 | 202 | break; |
nkeynes@53 | 203 | } |
nkeynes@1124 | 204 | |
nkeynes@1124 | 205 | if( period != TMU_timers[timer].timer_period ) { |
nkeynes@1124 | 206 | if( TMU_IS_RUNNING(timer) ) { |
nkeynes@1124 | 207 | /* If we're changing clock speed while counting, sync up and reschedule */ |
nkeynes@1124 | 208 | TMU_count(timer, sh4r.slice_cycle); |
nkeynes@1124 | 209 | TMU_timers[timer].timer_period = period; |
nkeynes@1124 | 210 | TMU_schedule_timer(timer); |
nkeynes@1124 | 211 | } else { |
nkeynes@1124 | 212 | TMU_timers[timer].timer_period = period; |
nkeynes@1124 | 213 | } |
nkeynes@1124 | 214 | } |
nkeynes@115 | 215 | |
nkeynes@115 | 216 | MMIO_WRITE( TMU, TCR0 + (12*timer), tcr ); |
nkeynes@53 | 217 | } |
nkeynes@23 | 218 | |
nkeynes@619 | 219 | void TMU_schedule_timer( int timer ) |
nkeynes@619 | 220 | { |
nkeynes@1124 | 221 | uint64_t duration = ((uint64_t)((uint32_t)(MMIO_READ( TMU, TCNT0 + 12*timer )))+1) * |
nkeynes@736 | 222 | (uint64_t)TMU_timers[timer].timer_period - TMU_timers[timer].timer_remainder; |
nkeynes@619 | 223 | event_schedule_long( EVENT_TMU0+timer, (uint32_t)(duration / 1000000000), |
nkeynes@736 | 224 | (uint32_t)(duration % 1000000000) ); |
nkeynes@1124 | 225 | // if( timer == 2 ) { |
nkeynes@1124 | 226 | // WARN( "Schedule timer %d: %lldns", timer, duration ); |
nkeynes@1124 | 227 | // TMU_dump(timer); |
nkeynes@1124 | 228 | // } |
nkeynes@619 | 229 | } |
nkeynes@619 | 230 | |
nkeynes@53 | 231 | void TMU_start( int timer ) |
nkeynes@23 | 232 | { |
nkeynes@260 | 233 | TMU_timers[timer].timer_run = sh4r.slice_cycle; |
nkeynes@53 | 234 | TMU_timers[timer].timer_remainder = 0; |
nkeynes@619 | 235 | TMU_schedule_timer( timer ); |
nkeynes@53 | 236 | } |
nkeynes@53 | 237 | |
nkeynes@264 | 238 | /** |
nkeynes@264 | 239 | * Stop the given timer. Run it up to the current time and leave it there. |
nkeynes@264 | 240 | */ |
nkeynes@53 | 241 | void TMU_stop( int timer ) |
nkeynes@53 | 242 | { |
nkeynes@264 | 243 | TMU_count( timer, sh4r.slice_cycle ); |
nkeynes@619 | 244 | event_cancel( EVENT_TMU0+timer ); |
nkeynes@53 | 245 | } |
nkeynes@53 | 246 | |
nkeynes@53 | 247 | /** |
nkeynes@53 | 248 | * Count the specified timer for a given number of nanoseconds. |
nkeynes@53 | 249 | */ |
nkeynes@53 | 250 | uint32_t TMU_count( int timer, uint32_t nanosecs ) |
nkeynes@53 | 251 | { |
nkeynes@619 | 252 | uint32_t run_ns = nanosecs + TMU_timers[timer].timer_remainder - |
nkeynes@736 | 253 | TMU_timers[timer].timer_run; |
nkeynes@53 | 254 | TMU_timers[timer].timer_remainder = |
nkeynes@736 | 255 | run_ns % TMU_timers[timer].timer_period; |
nkeynes@619 | 256 | TMU_timers[timer].timer_run = nanosecs; |
nkeynes@619 | 257 | uint32_t count = run_ns / TMU_timers[timer].timer_period; |
nkeynes@53 | 258 | uint32_t value = MMIO_READ( TMU, TCNT0 + 12*timer ); |
nkeynes@53 | 259 | uint32_t reset = MMIO_READ( TMU, TCOR0 + 12*timer ); |
nkeynes@1124 | 260 | // if( timer == 2 ) |
nkeynes@1124 | 261 | // WARN( "Counting timer %d: %d ns, %d ticks", timer, run_ns, count ); |
nkeynes@53 | 262 | if( count > value ) { |
nkeynes@736 | 263 | uint32_t tcr = MMIO_READ( TMU, TCR0 + 12*timer ); |
nkeynes@736 | 264 | tcr |= TCR_UNF; |
nkeynes@736 | 265 | count -= value; |
nkeynes@619 | 266 | value = reset - (count % reset) + 1; |
nkeynes@736 | 267 | MMIO_WRITE( TMU, TCR0 + 12*timer, tcr ); |
nkeynes@736 | 268 | if( tcr & TCR_UNIE ) |
nkeynes@736 | 269 | intc_raise_interrupt( INT_TMU_TUNI0 + timer ); |
nkeynes@736 | 270 | MMIO_WRITE( TMU, TCNT0 + 12*timer, value ); |
nkeynes@1124 | 271 | // if( timer == 2 ) |
nkeynes@1124 | 272 | // WARN( "Underflowed timer %d", timer ); |
nkeynes@736 | 273 | TMU_schedule_timer(timer); |
nkeynes@53 | 274 | } else { |
nkeynes@736 | 275 | value -= count; |
nkeynes@736 | 276 | MMIO_WRITE( TMU, TCNT0 + 12*timer, value ); |
nkeynes@23 | 277 | } |
nkeynes@53 | 278 | return value; |
nkeynes@23 | 279 | } |
nkeynes@23 | 280 | |
nkeynes@929 | 281 | MMIO_REGION_READ_FN( TMU, reg ) |
nkeynes@929 | 282 | { |
nkeynes@929 | 283 | reg &= 0xFFF; |
nkeynes@929 | 284 | switch( reg ) { |
nkeynes@929 | 285 | case TCNT0: |
nkeynes@1127 | 286 | if( TMU_IS_RUNNING(0) ) |
nkeynes@1127 | 287 | TMU_count( 0, sh4r.slice_cycle ); |
nkeynes@929 | 288 | break; |
nkeynes@929 | 289 | case TCNT1: |
nkeynes@1127 | 290 | if( TMU_IS_RUNNING(1) ) |
nkeynes@1127 | 291 | TMU_count( 1, sh4r.slice_cycle ); |
nkeynes@929 | 292 | break; |
nkeynes@929 | 293 | case TCNT2: |
nkeynes@1127 | 294 | if( TMU_IS_RUNNING(2) ) |
nkeynes@1127 | 295 | TMU_count( 2, sh4r.slice_cycle ); |
nkeynes@929 | 296 | break; |
nkeynes@929 | 297 | } |
nkeynes@929 | 298 | return MMIO_READ( TMU, reg ); |
nkeynes@929 | 299 | } |
nkeynes@975 | 300 | MMIO_REGION_READ_DEFSUBFNS(TMU) |
nkeynes@975 | 301 | |
nkeynes@929 | 302 | |
nkeynes@929 | 303 | MMIO_REGION_WRITE_FN( TMU, reg, val ) |
nkeynes@23 | 304 | { |
nkeynes@53 | 305 | uint32_t oldval; |
nkeynes@53 | 306 | int i; |
nkeynes@929 | 307 | reg &= 0xFFF; |
nkeynes@23 | 308 | switch( reg ) { |
nkeynes@53 | 309 | case TSTR: |
nkeynes@736 | 310 | oldval = MMIO_READ( TMU, TSTR ); |
nkeynes@736 | 311 | for( i=0; i<3; i++ ) { |
nkeynes@736 | 312 | uint32_t tmp = 1<<i; |
nkeynes@736 | 313 | if( (oldval & tmp) != 0 && (val&tmp) == 0 ) |
nkeynes@736 | 314 | TMU_stop(i); |
nkeynes@736 | 315 | else if( (oldval&tmp) == 0 && (val&tmp) != 0 ) |
nkeynes@736 | 316 | TMU_start(i); |
nkeynes@736 | 317 | } |
nkeynes@736 | 318 | break; |
nkeynes@53 | 319 | case TCR0: |
nkeynes@736 | 320 | TMU_set_timer_control( 0, val ); |
nkeynes@736 | 321 | return; |
nkeynes@53 | 322 | case TCR1: |
nkeynes@736 | 323 | TMU_set_timer_control( 1, val ); |
nkeynes@736 | 324 | return; |
nkeynes@53 | 325 | case TCR2: |
nkeynes@736 | 326 | TMU_set_timer_control( 2, val ); |
nkeynes@736 | 327 | return; |
nkeynes@619 | 328 | case TCNT0: |
nkeynes@736 | 329 | MMIO_WRITE( TMU, reg, val ); |
nkeynes@736 | 330 | if( TMU_IS_RUNNING(0) ) { // reschedule |
nkeynes@736 | 331 | TMU_timers[0].timer_run = sh4r.slice_cycle; |
nkeynes@736 | 332 | TMU_schedule_timer( 0 ); |
nkeynes@736 | 333 | } |
nkeynes@736 | 334 | return; |
nkeynes@619 | 335 | case TCNT1: |
nkeynes@736 | 336 | MMIO_WRITE( TMU, reg, val ); |
nkeynes@736 | 337 | if( TMU_IS_RUNNING(1) ) { // reschedule |
nkeynes@736 | 338 | TMU_timers[1].timer_run = sh4r.slice_cycle; |
nkeynes@736 | 339 | TMU_schedule_timer( 1 ); |
nkeynes@736 | 340 | } |
nkeynes@736 | 341 | return; |
nkeynes@619 | 342 | case TCNT2: |
nkeynes@736 | 343 | MMIO_WRITE( TMU, reg, val ); |
nkeynes@736 | 344 | if( TMU_IS_RUNNING(2) ) { // reschedule |
nkeynes@736 | 345 | TMU_timers[2].timer_run = sh4r.slice_cycle; |
nkeynes@736 | 346 | TMU_schedule_timer( 2 ); |
nkeynes@736 | 347 | } |
nkeynes@736 | 348 | return; |
nkeynes@23 | 349 | } |
nkeynes@23 | 350 | MMIO_WRITE( TMU, reg, val ); |
nkeynes@23 | 351 | } |
nkeynes@23 | 352 | |
nkeynes@619 | 353 | void TMU_count_all( uint32_t nanosecs ) |
nkeynes@23 | 354 | { |
nkeynes@23 | 355 | int tcr = MMIO_READ( TMU, TSTR ); |
nkeynes@23 | 356 | if( tcr & 0x01 ) { |
nkeynes@736 | 357 | TMU_count( 0, nanosecs ); |
nkeynes@23 | 358 | } |
nkeynes@23 | 359 | if( tcr & 0x02 ) { |
nkeynes@736 | 360 | TMU_count( 1, nanosecs ); |
nkeynes@23 | 361 | } |
nkeynes@23 | 362 | if( tcr & 0x04 ) { |
nkeynes@736 | 363 | TMU_count( 2, nanosecs ); |
nkeynes@23 | 364 | } |
nkeynes@23 | 365 | } |
nkeynes@53 | 366 | |
nkeynes@619 | 367 | void TMU_run_slice( uint32_t nanosecs ) |
nkeynes@619 | 368 | { |
nkeynes@619 | 369 | TMU_count_all( nanosecs ); |
nkeynes@619 | 370 | TMU_timers[0].timer_run = 0; |
nkeynes@619 | 371 | TMU_timers[1].timer_run = 0; |
nkeynes@619 | 372 | TMU_timers[2].timer_run = 0; |
nkeynes@619 | 373 | } |
nkeynes@619 | 374 | |
nkeynes@53 | 375 | void TMU_update_clocks() |
nkeynes@53 | 376 | { |
nkeynes@115 | 377 | TMU_set_timer_control( 0, MMIO_READ( TMU, TCR0 ) ); |
nkeynes@115 | 378 | TMU_set_timer_control( 1, MMIO_READ( TMU, TCR1 ) ); |
nkeynes@115 | 379 | TMU_set_timer_control( 2, MMIO_READ( TMU, TCR2 ) ); |
nkeynes@53 | 380 | } |
nkeynes@53 | 381 | |
nkeynes@53 | 382 | void TMU_reset( ) |
nkeynes@53 | 383 | { |
nkeynes@53 | 384 | TMU_timers[0].timer_remainder = 0; |
nkeynes@53 | 385 | TMU_timers[0].timer_run = 0; |
nkeynes@53 | 386 | TMU_timers[1].timer_remainder = 0; |
nkeynes@53 | 387 | TMU_timers[1].timer_run = 0; |
nkeynes@53 | 388 | TMU_timers[2].timer_remainder = 0; |
nkeynes@53 | 389 | TMU_timers[2].timer_run = 0; |
nkeynes@53 | 390 | TMU_update_clocks(); |
nkeynes@53 | 391 | } |
nkeynes@53 | 392 | |
nkeynes@53 | 393 | void TMU_save_state( FILE *f ) { |
nkeynes@53 | 394 | fwrite( &TMU_timers, sizeof(TMU_timers), 1, f ); |
nkeynes@53 | 395 | } |
nkeynes@53 | 396 | |
nkeynes@53 | 397 | int TMU_load_state( FILE *f ) |
nkeynes@53 | 398 | { |
nkeynes@53 | 399 | fread( &TMU_timers, sizeof(TMU_timers), 1, f ); |
nkeynes@53 | 400 | return 0; |
nkeynes@53 | 401 | } |
.