Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/libmeteor/source/timer.cpp
2 views
1
// Meteor - A Nintendo Gameboy Advance emulator
2
// Copyright (C) 2009-2011 Philippe Daouadi
3
//
4
// This program is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// This program is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17
#include "ameteor/timer.hpp"
18
#include "globals.hpp"
19
#include "ameteor.hpp"
20
21
#include "debug.hpp"
22
23
namespace AMeteor
24
{
25
static const uint16_t Prescalers[] = {1, 64, 256, 1024};
26
27
void Timer::Reset ()
28
{
29
m_reload = 0;
30
m_count = 0;
31
m_control.w = 0;
32
}
33
34
void Timer::Reload ()
35
{
36
// FIXME to test on hardware, vba update the prescaler even if the timer
37
// didn't restart
38
// if the timer is at 33, 63 cycles have passed since the last timer
39
// increment and prescaler is 1/64, setting TM0CNT even to the same value
40
// it had will reset the prescaler part, so the timer would be at 33 and we
41
// will have to wait another 64 cycles before having it go to 34
42
//
43
// the current behaviour for the above example is that it will reach 34 in
44
// 1 cycle instead of 64
45
if (!m_control.b.start &&
46
(IO.DRead16(Io::TM0CNT_H + m_num * Io::TIMER_SIZE)
47
& (0x1 << 7)))
48
// if start has changed from 0 to 1
49
{
50
m_control.w = IO.DRead16(Io::TM0CNT_H + m_num * Io::TIMER_SIZE);
51
m_count = 65536 - m_reload;
52
if (!m_control.b.countup)
53
{
54
m_count *= Prescalers[m_control.b.prescaler];
55
56
// here, the str instruction which have triggered this function
57
// will be taken in account by this timer
58
// in other words, if the str instruction takes 3 cycles, the
59
// timer will be incremented by 3 cycles just after its start
60
CLOCK.SetTimer(m_num, m_count);
61
}
62
}
63
else
64
{
65
uint16_t cnt = IO.DRead16(Io::TM0CNT_H + m_num * Io::TIMER_SIZE);
66
if (m_control.b.start && (cnt & (0x1 << 7))
67
&& m_control.b.prescaler != (cnt & 0x3))
68
met_abort("Prescaler changed while timer " << (int)m_num << " was up");
69
70
m_control.w = IO.DRead16(Io::TM0CNT_H + m_num * Io::TIMER_SIZE);
71
72
if (!m_control.b.start)
73
CLOCK.DisableTimer(m_num);
74
}
75
76
if (m_num == 0 && m_control.b.countup)
77
met_abort("Count-up on first timer !");
78
}
79
80
uint16_t Timer::GetCount () const
81
{
82
if (m_control.b.countup)
83
return 65536 - m_count;
84
else
85
return 65536 -
86
CLOCK.GetTimer(m_num) / Prescalers[m_control.b.prescaler];
87
}
88
89
void Timer::TimeEvent ()
90
{
91
//debug("Timer" << (int)m_num << " overflow");
92
SOUND.TimerOverflow(m_num);
93
94
m_count = 65536 - m_reload;
95
if (!m_control.b.countup)
96
{
97
m_count *= Prescalers[m_control.b.prescaler];
98
99
// GetTimer should be zero or less since this function was called
100
if (m_count >= (unsigned short)-CLOCK.GetTimer(m_num))
101
{
102
m_count += CLOCK.GetTimer(m_num);
103
104
CLOCK.SetTimer(m_num, m_count);
105
}
106
else
107
{
108
CLOCK.AddTimer(m_num, m_count);
109
}
110
}
111
112
if (m_control.b.irq)
113
CPU.SendInterrupt(0x1 << (3 + m_num));
114
115
if (m_num != 3)
116
m_next->Countup();
117
}
118
119
void Timer::Countup ()
120
{
121
if (m_control.b.countup)
122
{
123
--m_count;
124
if (m_count == 0)
125
TimeEvent();
126
}
127
}
128
129
bool Timer::SaveState (std::ostream& stream)
130
{
131
SS_WRITE_VAR(m_reload);
132
SS_WRITE_VAR(m_count);
133
SS_WRITE_VAR(m_control);
134
135
return true;
136
}
137
138
bool Timer::LoadState (std::istream& stream)
139
{
140
SS_READ_VAR(m_reload);
141
SS_READ_VAR(m_count);
142
SS_READ_VAR(m_control);
143
144
return true;
145
}
146
}
147
148