Bug fix: check if vm exists
[avr-sim.git] / Timer8.cpp
blobcf13b6a8e3477a2ccb1cefa4a41707f0b31895ae
1 #include "Timer8.h"
2 #include "Registers.h"
3 #include "Bus.h"
4 #include "ImplementationException.h"
6 enum {
7 bit_CS0 = 0, /* timer/counter clock select bit 0 */
8 bit_CS1 = 1,
9 bit_CS2 = 2,
12 enum {
13 mask_CS0 = 1 << bit_CS0,
14 mask_CS1 = 1 << bit_CS1,
15 mask_CS2 = 1 << bit_CS2,
16 mask_CS = (mask_CS0 | mask_CS1 | mask_CS2),
19 enum {
20 CS_STOP = 0x00, /* Stop, the Timer/Counter is stopped */
21 CS_CK = 0x01, /* CK */
22 CS_CK_8 = 0x02, /* CK/8 */
23 CS_CK_64 = 0x03, /* CK/64 */
24 CS_CK_256 = 0x04, /* CK/256 */
25 CS_CK_1024 = 0x05, /* CK/1024 */
26 CS_EXT_FALL = 0x06, /* External Pin Tn, falling edge */
27 CS_EXT_RISE = 0x07, /* External Pin Tn, rising edge */
30 enum {
31 bit_WGM1 = 3,
32 bit_WGM0 = 6,
33 bit_FOC = 7,
34 bit_COM0 = 4,
35 bit_COM1 = 5
38 enum {
39 WGM1 = (1<<bit_WGM1),
40 WGM0 = (1<<bit_WGM0),
41 FOC = (1<<bit_FOC),
42 COM0 = (1<<bit_COM0),
43 COM1 = (1<<bit_COM1),
44 mask_WGM = (WGM1|WGM0),
45 mask_COM = (COM1|COM0),
48 enum {
49 MODE_NORMAL = 0,
50 MODE_PWM = (WGM0),
51 MODE_CTC = (WGM1),
52 MODE_FASTPWM = (WGM0|WGM1)
55 enum {
56 COM_NORMAL = 0,
57 COM_TOGGLE = COM0,
58 COM_CLEAR = COM1,
59 COM_SET = COM0|COM1
62 namespace avr {
64 #define overflow() *tifr |= tovmask
65 #define compareMatch() *tifr |= ocfmask
67 bool Timer8::attachReg(const char *name, IORegister *reg) {
68 if( strcmp(name, "tcnt") == 0 ) {
69 tcnt = reg;
70 reg->registerHW(this);
71 } else if( strcmp(name, "tccr") == 0 ) {
72 tccr = reg;
73 reg->registerHW(this);
74 } else if( strcmp(name, "ocr") == 0 ) {
75 ocr = reg;
76 reg->registerHW(this);
77 } else if( strcmp(name, "tifr") == 0 )
78 tifr = reg;
79 else
80 return false;
82 return true;
85 void Timer8::regChanged( IORegister *reg ) {
86 if( reg == tccr ) {
87 unsigned char val = tccr->get();
88 setClock(val);
90 unsigned char timerModeNew = val & mask_WGM;
91 if( timerMode != timerModeNew )
92 direction = +1;
93 timerMode = timerModeNew;
95 compareMode = val & mask_COM;
97 if( val & FOC )
98 forceOutputCompare();
99 } else if( reg == tcnt ) {
100 blockCompareMatch = true;
101 } else if( reg == ocr ) {
102 if( (timerMode == MODE_NORMAL) || (timerMode == MODE_CTC) )
103 ocrBuf = *ocr;
107 void Timer8::setClock(unsigned char tccr) {
108 byte clk = tccr & mask_CS;
109 switch( clk ) {
110 case CS_STOP:
111 bus.clearBreak(this);
112 return;
114 case CS_CK:
115 period = 1;
116 break;
118 case CS_CK_8:
119 period = 8;
120 break;
122 case CS_CK_64:
123 period = 64;
124 break;
126 case CS_CK_256:
127 period = 256;
128 break;
130 case CS_CK_1024:
131 period = 1024;
132 break;
134 case CS_EXT_FALL:
135 case CS_EXT_RISE:
136 throw util::ImplementationException(
137 "external timer/counter sources not implemented" );
140 bus.setBreakDelta(period, this);
143 void Timer8::forceOutputCompare() {
144 compareMatchOutput( tcnt->get() );
147 void Timer8::compareMatchOutput(unsigned char tcnt) {
148 // the non-PWM modes are NORMAL and CTC
149 // under NORMAL, there is no pin action for a compare match
150 // under CTC, the action is to clear the pin.
151 if( tcnt == ocrBuf ) {
152 switch( compareMode ) {
153 case COM_NORMAL:
154 break;
156 case COM_TOGGLE:
157 //outputComparePin.toggle();
158 break;
160 case COM_CLEAR:
161 //outputComparePin.low();
162 break;
164 case COM_SET:
165 //outputComparePin.high();
166 break;
169 compareMatch();
173 #define TOP 0xff
174 #define BOTTOM 0x00
176 void Timer8::step() {
177 byte tcntVal = tcnt->get();
178 byte tcntSave = tcntVal;
180 bool tov = false;
181 switch( timerMode ) {
182 case MODE_NORMAL:
183 if( tcntVal == TOP ) {
184 tov = true;
185 tcntVal = 0;
187 break;
189 case MODE_CTC:
190 if( tcntVal == ocrBuf ) {
191 tov = true;
192 tcntVal = 0;
194 break;
196 case MODE_PWM:
197 if( (tcntVal == TOP) && (direction == +1) ) {
198 direction = -1;
199 ocrBuf = *ocr;
200 } else if( (tcntVal == BOTTOM) && (direction == -1) ) {
201 direction = +1;
202 tov = true;
204 break;
206 case MODE_FASTPWM:
207 if( tcntVal == TOP ) {
208 tov = true;
209 ocrBuf = *ocr;
211 break;
214 if( tov )
215 overflow();
216 else
217 tcntVal += direction;
219 if( ! blockCompareMatch )
220 compareMatchOutput(tcntSave);
221 blockCompareMatch = false;
223 tcnt->set( tcntVal );
224 bus.setBreakDelta(period, this);