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