initial commit
[gba_blender] / src / timer.c
1 /*
2 blender for the Gameboy Advance
3 Copyright (C) 2021  John Tsiombikas <nuclear@member.fsf.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18 #include "intr.h"
19 #include "timer.h"
20
21 #define F_CLK   16780000
22 /* clock is 16.78MHz
23  * - no prescale: 59.595ns
24  * - prescale 64: 3.814us
25  * - prescale 256: 15.256us
26  * - prescale 1024: 61.025us
27  */
28
29 static void timer_intr(void);
30
31 void init_timer(int tm, unsigned long rate_hz, void (*intr)(void))
32 {
33         static const unsigned long clk[] = {F_CLK, F_CLK / 64, F_CLK / 256, F_CLK / 1024};
34         unsigned long count;
35         int pscl = 0;
36
37         do {
38                 count = clk[pscl] / rate_hz;
39         } while(count >= 65536 && ++pscl < 4);
40
41         if(pscl >= 4) return;   /* impossible rate */
42
43         REG_TMCNT_H(tm) = 0;
44         REG_TMCNT_L(tm) = 65536 - count;
45         if(intr) {
46                 interrupt(INTR_TIMER0 + tm, intr);
47                 unmask(INTR_TIMER0 + tm);
48                 REG_TMCNT_H(tm) = TMCNT_IE;
49         }
50         REG_TMCNT_H(tm) |= TMCNT_EN | pscl;
51 }
52
53 void reset_msec_timer(void)
54 {
55         REG_TM0CNT_H &= ~TMCNT_EN;
56         interrupt(INTR_TIMER0, timer_intr);
57         timer_msec = 0;
58         REG_TM0CNT_L = 65535 - 16779;
59         REG_TM0CNT_H |= TMCNT_IE | TMCNT_EN;
60         unmask(INTR_TIMER0);
61 }
62
63 void delay(unsigned long ms)
64 {
65         unsigned long end = timer_msec + ms;
66         while(timer_msec < end);
67 }
68
69 static void timer_intr(void)
70 {
71         timer_msec++;
72 }