Fixed problem in DeviceSettings::strParam, returned wrong string
[avr-sim.git] / src / Timer8.cpp
blob2a2a814e08a5733b041474d82c342b1e669879d0
1 /*
2 * avr-sim: An atmel AVR simulator
3 * Copyright (C) 2008 Tom Haber
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.
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.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "Timer8.h"
20 #include "Registers.h"
21 #include "Bus.h"
22 #include "ImplementationException.h"
23 #include <cstring>
25 enum {
26 bit_CS0 = 0, /* timer/counter clock select bit 0 */
27 bit_CS1 = 1,
28 bit_CS2 = 2,
31 enum {
32 mask_CS0 = 1 << bit_CS0,
33 mask_CS1 = 1 << bit_CS1,
34 mask_CS2 = 1 << bit_CS2,
35 mask_CS = (mask_CS0 | mask_CS1 | mask_CS2),
38 enum {
39 CS_STOP = 0x00, /* Stop, the Timer/Counter is stopped */
40 CS_CK = 0x01, /* CK */
41 CS_CK_8 = 0x02, /* CK/8 */
42 CS_CK_64 = 0x03, /* CK/64 */
43 CS_CK_256 = 0x04, /* CK/256 */
44 CS_CK_1024 = 0x05, /* CK/1024 */
45 CS_EXT_FALL = 0x06, /* External Pin Tn, falling edge */
46 CS_EXT_RISE = 0x07, /* External Pin Tn, rising edge */
49 enum {
50 bit_WGM1 = 3,
51 bit_WGM0 = 6,
52 bit_FOC = 7,
53 bit_COM0 = 4,
54 bit_COM1 = 5
57 enum {
58 WGM1 = (1<<bit_WGM1),
59 WGM0 = (1<<bit_WGM0),
60 FOC = (1<<bit_FOC),
61 COM0 = (1<<bit_COM0),
62 COM1 = (1<<bit_COM1),
63 mask_WGM = (WGM1|WGM0),
64 mask_COM = (COM1|COM0),
67 enum {
68 MODE_NORMAL = 0,
69 MODE_PWM = (WGM0),
70 MODE_CTC = (WGM1),
71 MODE_FASTPWM = (WGM0|WGM1)
74 enum {
75 COM_NORMAL = 0,
76 COM_TOGGLE = COM0,
77 COM_CLEAR = COM1,
78 COM_SET = COM0|COM1
81 namespace avr {
83 #define overflow() *tifr |= tovmask
84 #define compareMatch() *tifr |= ocfmask
86 void OutputCompareUnit8::flush() {
87 ocrBuf = ocr->get();
90 void OutputCompareUnit8::regChanged(IORegister *reg) {
91 if( reg == ocr )
92 ocrBuf = ocr->get();
95 void OutputCompareUnit8::forceOutputCompare(IORegister *tifr, unsigned char tcnt) {
96 compareMatchOutput( tifr, tcnt );
99 void OutputCompareUnit8::compareMatchOutput(IORegister *tifr, unsigned char tcnt) {
100 // the non-PWM modes are NORMAL and CTC
101 // under NORMAL, there is no pin action for a compare match
102 // under CTC, the action is to clear the pin.
103 if( tcnt == ocrBuf ) {
104 switch( compareMode ) {
105 case COM_NORMAL:
106 break;
108 case COM_TOGGLE:
109 //outputComparePin.toggle();
110 break;
112 case COM_CLEAR:
113 //outputComparePin.low();
114 break;
116 case COM_SET:
117 //outputComparePin.high();
118 break;
121 compareMatch();
126 Timer8::Timer8(Bus & bus, unsigned char tovmask, int compunits,
127 unsigned char ocfmask[])
128 : Hardware(bus), tovmask(tovmask), direction(1) {
129 blockCompareMatch = true;
130 timerMode = 0;
131 period = 0;
133 nUnits = compunits;
134 for(int i = 0; i < nUnits; ++i)
135 units[i].setMask( ocfmask[i] );
138 bool Timer8::attachReg(const char *name, IORegister *reg) {
139 if( strcmp(name, "tcnt") == 0 ) {
140 tcnt = reg;
141 } else if( strcmp(name, "tccr") == 0 ) {
142 tccr = reg;
143 } else if( strcmp(name, "ocra") == 0 || strcmp(name, "ocr") == 0 ) {
144 units[0].setOcrRegister(reg);
145 } else if( strcmp(name, "ocrb") == 0 ) {
146 units[1].setOcrRegister(reg);
147 } else if( strcmp(name, "ocrc") == 0 ) {
148 units[2].setOcrRegister(reg);
149 } else if( strcmp(name, "tifr") == 0 ) {
150 tifr = reg;
151 return true;
152 } else
153 return false;
155 reg->registerHW(this);
156 return true;
159 bool Timer8::finishBuild() {
160 bool unitsOk = true;
161 for(int i = 0; i < nUnits; ++i)
162 unitsOk = unitsOk && units[i].ocrSet();
164 return (tcnt != 0) && (tccr != 0) && (tifr != 0) && unitsOk;
167 void Timer8::regChanged( IORegister *reg ) {
168 if( reg == tccr ) {
169 unsigned char val = tccr->get();
170 setClock(val);
172 unsigned char timerModeNew = val & mask_WGM;
173 if( timerMode != timerModeNew )
174 direction = +1;
175 timerMode = timerModeNew;
177 units[0].setCompareMode( val & mask_COM );
179 if( val & FOC ) {
180 for(int cntr = 0; nUnits; cntr++ )
181 units[cntr].forceOutputCompare(tifr, *tcnt);
184 } else if( reg == tcnt ) {
185 blockCompareMatch = true;
186 } else {
187 if( (timerMode == MODE_NORMAL) || (timerMode == MODE_CTC) ) {
188 for(int i = 0; i < nUnits; ++i)
189 units[i].regChanged(reg);
194 void Timer8::setClock(unsigned char tccr) {
195 byte clk = tccr & mask_CS;
196 switch( clk ) {
197 case CS_STOP:
198 bus.clearBreak(this);
199 return;
201 case CS_CK:
202 period = 1;
203 break;
205 case CS_CK_8:
206 period = 8;
207 break;
209 case CS_CK_64:
210 period = 64;
211 break;
213 case CS_CK_256:
214 period = 256;
215 break;
217 case CS_CK_1024:
218 period = 1024;
219 break;
221 case CS_EXT_FALL:
222 case CS_EXT_RISE:
223 throw util::ImplementationException(
224 "external timer/counter sources not implemented" );
227 bus.setBreakDelta(period, this);
230 #define TOP 0xff
231 #define BOTTOM 0x00
233 void Timer8::step() {
234 byte tcntVal = tcnt->get();
235 byte tcntSave = tcntVal;
237 bool tov = false;
238 switch( timerMode ) {
239 case MODE_NORMAL:
240 if( tcntVal == TOP ) {
241 tov = true;
242 tcntVal = 0;
244 break;
246 case MODE_CTC:
247 if( units[0].compare(tcntVal) ) {
248 tov = true;
249 tcntVal = 0;
251 break;
253 case MODE_PWM:
254 if( (tcntVal == TOP) && (direction == +1) ) {
255 direction = -1;
256 flushOCRn();
257 } else if( (tcntVal == BOTTOM) && (direction == -1) ) {
258 direction = +1;
259 tov = true;
261 break;
263 case MODE_FASTPWM:
264 if( tcntVal == TOP ) {
265 tov = true;
266 flushOCRn();
268 break;
271 if( tov )
272 overflow();
273 else
274 tcntVal += direction;
276 if( ! blockCompareMatch )
277 for(int cntr = 0; cntr < nUnits; cntr++ )
278 units[cntr].compareMatchOutput(tifr, tcntSave);
279 blockCompareMatch = false;
281 tcnt->set( tcntVal );
282 bus.setBreakDelta(period, this);
285 void Timer8::flushOCRn() {
286 for(int cntr = 0; cntr < nUnits; cntr++ )
287 units[cntr].flush();