/
restore-reset.asm
348 lines (296 loc) · 10.1 KB
/
restore-reset.asm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
/*
resstore-reset.asm by ALeX Kazik
Code+Docs: https://github.com/alexkazik/restore-reset
Homepage: http://alex.kazik.de/232/restore-reset
License: Creative Commons Attribution 3.0 Unported License
http://creativecommons.org/licenses/by/3.0/
*/
#include <avr/io.h>
/*
** REGISTER DEFINITION
*/
// Registers 0..15 are not used, because some AVRs don't have such
#define REG_SREG 16 // to store the SREG in the interrupt
#define REG_KEY_VALID 17 // is the REG_KEY_STATE valid (0) or invalid (!=0)
#define REG_KEY_STATE 18 // the state of the key (only valid if REG_KEY_VALID is 0)
#define REG_TIMER 19 // the timer
// Registers 20..29 are just not used
// Registers 30,31 are not used, so they can be used as a stack for those AVR's
// which don't have SRAM (two bytes are enough, just interrpt and return, no rcall)
/*
** I/O DEFINITION
*/
// ALL PINS MUST BE ON ONE PORT
#define PORT PORTB
#define PIN PINB
#define DDR DDRB
// THE PINS
#define RESTORE_IN_BIT 0
#define RESTORE_OUT_BIT 1
#define RESET_BIT 2
// you can comment the LEDs out (one, another or both) if you don't have/want LEDs
#define LED1_BIT 3
#define LED2_BIT 4
/*
** TIMING DEFINITION
*/
#define TIME_BASE 20000 // how many usec until the timer produces an overflow
#define TIME_LINE_LOW (200000/TIME_BASE) // how long should the restore/reset line low
#define TIME_LINE_PAUSE (200000/TIME_BASE) // how long the restore/reset line shouln't be pulled low again
#define TIME_RESET (2000000/TIME_BASE) // how long you should press the restore key to trigger a reset
#define TIME_BLINK_LED (500000/TIME_BASE) // how long should the LED blink
#define DOUBLE_RESET // do a second reset after the twice the blink time (once actually blinking)
#if TIME_LINE_LOW < 1 || TIME_LINE_PAUSE < 1 || TIME_RESET < 1 || TIME_BLINK_LED < 1
#error "times must be > BASE"
#elif TIME_RESET - (TIME_LINE_LOW + TIME_LINE_PAUSE) < 1
#error "time for reset is too low"
#elif TIME_RESET - (TIME_LINE_LOW + TIME_LINE_PAUSE) > 255
#error "time for reset is too high"
#elif (defined(LED1_BIT) || defined(LED2_BIT)) && (TIME_BLINK_LED - TIME_LINE_LOW < 1)
#error "time for blink must be larger than for holding the line low"
#endif
/*
** VECTORS (SHORT VERSION)
*/
.section .vectors,"ax",@progbits
.global __vectors
.func __vectors
__vectors:
#if defined(__AVR_ATtiny13__) || defined(__AVR_ATtiny13A__)
rjmp main // vector 0
.word 0xffff // vector 1, unused
.word 0xffff // vector 2, unused
rjmp sig_overflow0 // vector 3
#elif defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
rjmp main // vector 0
.word 0xffff // vector 1, unused
.word 0xffff // vector 2, unused
.word 0xffff // vector 3, unused
.word 0xffff // vector 4, unused
rjmp sig_overflow0 // vector 5
#elif defined(__AVR_ATtiny4__) || defined(__AVR_ATtiny5__) || defined(__AVR_ATtiny9__) || defined(__AVR_ATtiny10__)
rjmp main // vector 0
.word 0xffff // vector 1, unused
.word 0xffff // vector 2, unused
.word 0xffff // vector 3, unused
rjmp sig_overflow0 // vector 4
#elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny24A__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny44A__) || defined(__AVR_ATtiny84__)
rjmp main // vector 0
.word 0xffff // vector 1, unused
.word 0xffff // vector 2, unused
.word 0xffff // vector 3, unused
.word 0xffff // vector 4, unused
.word 0xffff // vector 5, unused
.word 0xffff // vector 6, unused
.word 0xffff // vector 7, unused
.word 0xffff // vector 8, unused
.word 0xffff // vector 9, unused
.word 0xffff // vector 10, unused
rjmp sig_overflow0 // vector 11
#else
#error "unknown AVR"
#endif
.endfunc
.section .text
/*
** POWER UP ROUTINE
*/
.global main
.func main
main:
// REG_SREG is used as a temporary register; it's only ised in the interrupt, which is disabled at this point
// setup stack
// hi byte of the stack, only used for systems with >128 bytes of sram
#ifdef SPH
ldi REG_SREG, RAMEND >> 8
out _SFR_IO_ADDR(SPH), REG_SREG
#endif
// low byte of the stack
ldi REG_SREG, RAMEND & 0xff
out _SFR_IO_ADDR(SPL), REG_SREG
// only for initial LED states
#ifdef LED1_BIT
#define LED1_MASK (1<<LED1_BIT)
#else
#define LED1_MASK 0
#endif
#ifdef LED2_BIT
#define LED2_MASK (1<<LED2_BIT)
#else
#define LED2_MASK 0
#endif
// init I/O
ldi REG_SREG, LED1_MASK | LED2_MASK // the mask is 0 if the the LED is not known
out _SFR_IO_ADDR(DDR), REG_SREG // LEDs output, others input
ldi REG_SREG, (1<<RESTORE_IN_BIT)
out _SFR_IO_ADDR(PORT), REG_SREG // pulllup for REST_IN, others without pullup / output low
// init timer! to the correct TIME_BASE
#if defined(__AVR_ATtiny13__) || defined(__AVR_ATtiny13A__) || defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny24A__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny44A__) || defined(__AVR_ATtiny84__)
#if TIME_BASE != 20000 || F_CPU != 1000000
#error "currently only 20ms@1Mhz as base are supported"
#endif
// time to overflow 78(.125) * (clock/256) ~= 20ms
ldi REG_SREG, 78
out _SFR_IO_ADDR(OCR0A), REG_SREG
// ctc mode (two of the three WGM bits)
ldi REG_SREG, (1<<WGM01) | (1<<WGM00)
out _SFR_IO_ADDR(TCCR0A), REG_SREG
// ctc mode (the last of the three WGM bits)
// AND scaler: clock/256
ldi REG_SREG, (1<<WGM02) | (1<<CS02)
out _SFR_IO_ADDR(TCCR0B), REG_SREG
// enable interrupt
ldi REG_SREG, (1<<TOIE0)
out _SFR_IO_ADDR(TIMSK0), REG_SREG
#elif defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
#if TIME_BASE != 20000 || F_CPU != 1000000
#error "currently only 20ms@1Mhz as base are supported"
#endif
// time to overflow 78(.125) * (clock/256) ~= 20ms
ldi REG_SREG, 78
out _SFR_IO_ADDR(OCR0A), REG_SREG
// ctc mode (two of the three WGM bits)
ldi REG_SREG, (1<<WGM01) | (1<<WGM00)
out _SFR_IO_ADDR(TCCR0A), REG_SREG
// ctc mode (the last of the three WGM bits)
// AND scaler: clock/256
ldi REG_SREG, (1<<WGM02) | (1<<CS02)
out _SFR_IO_ADDR(TCCR0B), REG_SREG
// enable interrupt
ldi REG_SREG, (1<<TOIE0)
out _SFR_IO_ADDR(TIMSK), REG_SREG // <-- the only differenct to attiny13/attinyX4
#elif defined(__AVR_ATtiny4__) || defined(__AVR_ATtiny5__) || defined(__AVR_ATtiny9__) || defined(__AVR_ATtiny10__)
#if TIME_BASE != 20000 || F_CPU != 1000000
#error "currently only 20ms@1Mhz as base are supported"
#endif
// time to overflow 78(.125) * (clock/256) ~= 20ms
ldi REG_SREG, 78 >> 8
out _SFR_IO_ADDR(OCR0AH), REG_SREG
ldi REG_SREG, 78 & 0xff
out _SFR_IO_ADDR(OCR0AL), REG_SREG
// ctc mode (two of the four WGM bits)
ldi REG_SREG, (1<<WGM01) | (1<<WGM00)
out _SFR_IO_ADDR(TCCR0A), REG_SREG
// ctc mode (the last two of the four WGM bits)
// AND scaler: clock/256
ldi REG_SREG, (1<<WGM02) | (1<<WGM03) | (1<<CS02)
out _SFR_IO_ADDR(TCCR0B), REG_SREG
// enable interrupt
ldi REG_SREG, (1<<TOIE0)
out _SFR_IO_ADDR(TIMSK0), REG_SREG
#else
#error "unknown AVR"
#endif
// prepare launch
ldi REG_KEY_VALID, (1<<RESTORE_IN_BIT) // key not valid
ldi REG_KEY_STATE, (0<<RESTORE_IN_BIT) // key pressed
// set LEDs (1 is already on, 2 will be switched off)
#ifdef LED2_BIT
cbi _SFR_IO_ADDR(DDR), LED2_BIT // set to input, port=0 -> n/c
#endif
// enable interrupt
sei
/*
** the real program
*/
state0: // wait for releasing the restore key
tst REG_KEY_VALID
brne state0 // not valid, try again
tst REG_KEY_STATE
breq state0 // key still pressed, loop
state1: // wait for pressing the restore key
tst REG_KEY_VALID
brne state1 // not valid, try again
tst REG_KEY_STATE
brne state1 // key not pressed, loop
// pull RESTORE_OUT low for TIME_LINE_LOW
sbi _SFR_IO_ADDR(DDR), RESTORE_OUT_BIT // set to output, port=0 -> low
ldi REG_TIMER, TIME_LINE_LOW // init timer
state2: // wait for TIME_LINE_LOW
tst REG_TIMER
brne state2 // not yet done -> loop
// restore RESTORE_OUT for (at least) TIME_LINE_PAUSE
cbi _SFR_IO_ADDR(DDR), RESTORE_OUT_BIT // set to input, port=0 -> n/c
ldi REG_TIMER, TIME_LINE_PAUSE // init timer
state3: // wait for TIME_LINE_PAUSE
tst REG_TIMER
brne state3 // not yet done -> loop
// wait?
ldi REG_TIMER, TIME_RESET - (TIME_LINE_LOW + TIME_LINE_PAUSE) // init timer
state4: // wait for either key released or time to reset
tst REG_KEY_VALID
brne 1f // not valid, skip key test
tst REG_KEY_STATE
brne state1 // key released -> start over
1:
tst REG_TIMER
brne state4 // not yet done -> loop
// reset!
sbi _SFR_IO_ADDR(DDR), RESET_BIT // set to output, port=0 -> low
ldi REG_TIMER, TIME_LINE_LOW // init timer
// toggle leds
#ifdef LED1_BIT
cbi _SFR_IO_ADDR(DDR), LED1_BIT // set to input, port=0 -> n/c
#endif
#ifdef LED2_BIT
sbi _SFR_IO_ADDR(DDR), LED2_BIT // set to output, port=0 -> low
#endif
state5: // wait for TIME_LINE_LOW
tst REG_TIMER
brne state5 // not yet done -> loop
// reset done
cbi _SFR_IO_ADDR(DDR), RESET_BIT // set to input, port=0 -> n/c
#if defined(DOUBLE_RESET) || defined(LED1_BIT) || defined(LED2_BIT)
ldi REG_TIMER, TIME_BLINK_LED - TIME_LINE_LOW // init timer
state6: // wait for TIME_BLINK_LED (minus the already waied TIME_LINE_LOW)
tst REG_TIMER
brne state6 // not yet done -> loop
// toggle leds
#ifdef LED1_BIT
sbi _SFR_IO_ADDR(DDR), LED1_BIT // set to output, port=0 -> low
#endif
#ifdef LED2_BIT
cbi _SFR_IO_ADDR(DDR), LED2_BIT // set to input, port=0 -> n/c
#endif
#endif
#if defined(DOUBLE_RESET)
ldi REG_TIMER, TIME_BLINK_LED // init timer
state7: // wait for TIME_BLINK_LED or release of the restore key
tst REG_KEY_VALID
brne 1f // not valid, skip key test
tst REG_KEY_STATE
brne state1 // key released -> start over
1:
tst REG_TIMER
brne state7 // not yet done -> loop
// reset again
sbi _SFR_IO_ADDR(DDR), RESET_BIT // set to output, port=0 -> low
ldi REG_TIMER, TIME_LINE_LOW // init timer
state8: // wait for TIME_LINE_LOW
tst REG_TIMER
brne state8 // not yet done -> loop
// reset done
cbi _SFR_IO_ADDR(DDR), RESET_BIT // set to input, port=0 -> n/c
#endif
// start over
rjmp state0
.endfunc
/*
** TIMER OVERFLOW
*/
.global sig_overflow0
.func sig_overflow0
sig_overflow0:
// save SREG
in REG_SREG, _SFR_IO_ADDR(SREG)
// debounce the RESTORE KEY
mov REG_KEY_VALID, REG_KEY_STATE
in REG_KEY_STATE, _SFR_IO_ADDR(PIN)
andi REG_KEY_STATE, (1<<RESTORE_IN_BIT)
eor REG_KEY_VALID, REG_KEY_STATE
// decrement timer
dec REG_TIMER
// restore SREG
out _SFR_IO_ADDR(SREG), REG_SREG
reti
.endfunc