1 //
2 // Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
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 version 2 as
6 // published by the Free Software Foundation.
7 //
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License version 2 for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // version 2 along with this program; if not, write to the
15 // Free Software Foundation, Inc.,
16 // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 //
18
19 #include "tima.h"
20 #include "savestate.h"
21
22 static unsigned char const timaClock[4] = { 10, 4, 6, 8 };
23
24 namespace gambatte {
25
Tima()26 Tima::Tima()
27 : lastUpdate_(0)
28 , tmatime_(disabled_time)
29 , tima_(0)
30 , tma_(0)
31 , tac_(0)
32 {
33 }
34
saveState(SaveState & state) const35 void Tima::saveState(SaveState &state) const {
36 state.mem.timaLastUpdate = lastUpdate_;
37 state.mem.tmatime = tmatime_;
38 }
39
loadState(SaveState const & state,TimaInterruptRequester timaIrq)40 void Tima::loadState(SaveState const &state, TimaInterruptRequester timaIrq) {
41 lastUpdate_ = state.mem.timaLastUpdate;
42 tmatime_ = state.mem.tmatime;
43 tima_ = state.mem.ioamhram.get()[0x105];
44 tma_ = state.mem.ioamhram.get()[0x106];
45 tac_ = state.mem.ioamhram.get()[0x107];
46
47 unsigned long nextIrqEventTime = disabled_time;
48 if (tac_ & 4) {
49 nextIrqEventTime = tmatime_ != disabled_time && tmatime_ > state.cpu.cycleCounter
50 ? tmatime_
51 : lastUpdate_ + ((256u - tima_) << timaClock[tac_ & 3]) + 3;
52 }
53
54 timaIrq.setNextIrqEventTime(nextIrqEventTime);
55 }
56
resetCc(unsigned long const oldCc,unsigned long const newCc,TimaInterruptRequester timaIrq)57 void Tima::resetCc(unsigned long const oldCc, unsigned long const newCc, TimaInterruptRequester timaIrq) {
58 if (tac_ & 0x04) {
59 updateIrq(oldCc, timaIrq);
60 updateTima(oldCc);
61
62 unsigned long const dec = oldCc - newCc;
63 lastUpdate_ -= dec;
64 timaIrq.setNextIrqEventTime(timaIrq.nextIrqEventTime() - dec);
65
66 if (tmatime_ != disabled_time)
67 tmatime_ -= dec;
68 }
69 }
70
updateTima(unsigned long const cc)71 void Tima::updateTima(unsigned long const cc) {
72 unsigned long const ticks = (cc - lastUpdate_) >> timaClock[tac_ & 3];
73 lastUpdate_ += ticks << timaClock[tac_ & 3];
74
75 if (cc >= tmatime_) {
76 if (cc >= tmatime_ + 4)
77 tmatime_ = disabled_time;
78
79 tima_ = tma_;
80 }
81
82 unsigned long tmp = tima_ + ticks;
83 while (tmp > 0x100)
84 tmp -= 0x100 - tma_;
85
86 if (tmp == 0x100) {
87 tmp = 0;
88 tmatime_ = lastUpdate_ + 3;
89
90 if (cc >= tmatime_) {
91 if (cc >= tmatime_ + 4)
92 tmatime_ = disabled_time;
93
94 tmp = tma_;
95 }
96 }
97
98 tima_ = tmp;
99 }
100
setTima(unsigned const data,unsigned long const cc,TimaInterruptRequester timaIrq)101 void Tima::setTima(unsigned const data, unsigned long const cc, TimaInterruptRequester timaIrq) {
102 if (tac_ & 0x04) {
103 updateIrq(cc, timaIrq);
104 updateTima(cc);
105
106 if (tmatime_ - cc < 4)
107 tmatime_ = disabled_time;
108
109 timaIrq.setNextIrqEventTime(lastUpdate_ + ((256u - data) << timaClock[tac_ & 3]) + 3);
110 }
111
112 tima_ = data;
113 }
114
setTma(unsigned const data,unsigned long const cc,TimaInterruptRequester timaIrq)115 void Tima::setTma(unsigned const data, unsigned long const cc, TimaInterruptRequester timaIrq) {
116 if (tac_ & 0x04) {
117 updateIrq(cc, timaIrq);
118 updateTima(cc);
119 }
120
121 tma_ = data;
122 }
123
setTac(unsigned const data,unsigned long const cc,TimaInterruptRequester timaIrq)124 void Tima::setTac(unsigned const data, unsigned long const cc, TimaInterruptRequester timaIrq) {
125 if (tac_ ^ data) {
126 unsigned long nextIrqEventTime = timaIrq.nextIrqEventTime();
127
128 if (tac_ & 0x04) {
129 updateIrq(cc, timaIrq);
130 updateTima(cc);
131
132 lastUpdate_ -= (1u << (timaClock[tac_ & 3] - 1)) + 3;
133 tmatime_ -= (1u << (timaClock[tac_ & 3] - 1)) + 3;
134 nextIrqEventTime -= (1u << (timaClock[tac_ & 3] - 1)) + 3;
135
136 if (cc >= nextIrqEventTime)
137 timaIrq.flagIrq();
138
139 updateTima(cc);
140
141 tmatime_ = disabled_time;
142 nextIrqEventTime = disabled_time;
143 }
144
145 if (data & 4) {
146 lastUpdate_ = (cc >> timaClock[data & 3]) << timaClock[data & 3];
147 nextIrqEventTime = lastUpdate_ + ((256u - tima_) << timaClock[data & 3]) + 3;
148 }
149
150 timaIrq.setNextIrqEventTime(nextIrqEventTime);
151 }
152
153 tac_ = data;
154 }
155
tima(unsigned long cc)156 unsigned Tima::tima(unsigned long cc) {
157 if (tac_ & 0x04)
158 updateTima(cc);
159
160 return tima_;
161 }
162
doIrqEvent(TimaInterruptRequester timaIrq)163 void Tima::doIrqEvent(TimaInterruptRequester timaIrq) {
164 timaIrq.flagIrq();
165 timaIrq.setNextIrqEventTime(timaIrq.nextIrqEventTime()
166 + ((256u - tma_) << timaClock[tac_ & 3]));
167 }
168
169 }
170