-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathlibmaple22.S
383 lines (318 loc) · 8.48 KB
/
libmaple22.S
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
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
; Functions for Maple bus support on devices with 22-bit program counters (Mega)
#include <avr/io.h>
; reg usage faq: http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage
; essentially we can play with regs 18 to 27 and 30 and 31.
.global debug
.global maple_tx_raw
.global maple_rx_raw
.global maple_timer_test
.global TIMER2_OVF_vect
#define intsave r31
#define intwork r20
#define tmp1 r18
#define tmp2 r19
#define rport2 r21
#define rport r30
; Maple1 is on bit 0 of PORTMAPLE -- mask of 1:
#define bitmaple1 0
#define maskmaple1 1
; maple5 is on bit 1 -- mask of 10:
#define bitmaple5 1
#define maskmaple5 2
#define PORTMAPLE PORTB
#define PINMAPLE PINB
#define DDRMAPLE DDRB
.macro maple_high pin
SBI _SFR_IO_ADDR(PORTMAPLE), \pin
.endm
.macro maple_low pin
CBI _SFR_IO_ADDR(PORTMAPLE), \pin
.endm
.macro debug_pin_high
SBI _SFR_IO_ADDR(PORTD), 7
.endm
.macro debug_pin_low
CBI _SFR_IO_ADDR(PORTD), 7
.endm
.macro debug_pin_flip
IN tmp1, _SFR_IO_ADDR(PORTD)
ANDI tmp1, 0x80
BREQ 1f
debug_pin_low
RJMP 2f
1: debug_pin_high
2:
.endm
.macro delayquarter
nop
nop
nop
nop
.endm
.macro delayhalf
nop
nop
nop
nop
nop
nop
nop
nop
.endm
maple_tx_start:
maple_high bitmaple1
maple_high bitmaple5
delayquarter
; Start of sequence: pin 1 goes low, pin 5 goes high.
maple_low bitmaple1
maple_high bitmaple5
delayquarter
; Toggle pin 5 four times: 1
maple_low bitmaple5
delayquarter
maple_high bitmaple5
delayquarter
; 2
maple_low bitmaple5
delayquarter
maple_high bitmaple5
delayquarter
; 3
maple_low bitmaple5
delayquarter
maple_high bitmaple5
delayquarter
; 4
maple_low bitmaple5
delayquarter
maple_high bitmaple5
delayquarter
; End of sequence: pin 1 goes high, pin 5 goes low.
maple_high bitmaple1
delayquarter
maple_low bitmaple5
delayquarter
ret
maple_tx_end:
; Start of sequence: pin 1 is high, pin 5 is low.
maple_high bitmaple1
maple_low bitmaple5
delayquarter
; Pin 5 goes high for a short time.
maple_high bitmaple5
delayquarter
; Pin 5 goes low
maple_low bitmaple5
delayquarter
; Pin 1 goes low for 2 cycles
maple_low bitmaple1
delayhalf
; Pin 1 goes high for 2 cycles
maple_high bitmaple1
delayhalf
; Pin 1 goes low for 2 cycles
maple_low bitmaple1
delayhalf
; Pin 1 goes high for 2 cycles.
maple_high bitmaple1
delayhalf
; End of sequence: pin 5 goes high
maple_high bitmaple5
ret
maple_tx_phase1:
BST r18, 7 ; Read the bit
BRTC 1f ; Is it set?
maple_high bitmaple5 ; Yes, put the bit on the wire
1:
maple_low bitmaple1 ; New data is ready
delayquarter ; Wait for other end to notice
maple_high bitmaple5 ; Phase complete
ret
maple_tx_phase2:
BST r18, 7 ; Read the bit
BRTC 1f ; Is it set?
maple_high bitmaple1 ; Set it
1:
maple_low bitmaple5 ; Notify
delayquarter ; Wait for other end to notice
maple_high bitmaple1 ; Phase complete
ret
; r22: length
; r23: unused
; r24: low order bit of buffer pointer
; r25: high order bit of buffer pointer
maple_tx_data:
MOVW r26, r24 ; X register <- the buffer
_maple_tx_next_byte:
LD r18, X+ ; Next byte of data into r18
; put the byte on the bus one bit at a time.
RCALL maple_tx_phase1 ; bit 7
LSL r18
RCALL maple_tx_phase2 ; bit 6
LSL r18
RCALL maple_tx_phase1 ; bit 5
LSL r18
RCALL maple_tx_phase2 ; bit 4
LSL r18
RCALL maple_tx_phase1 ; bit 3
LSL r18
RCALL maple_tx_phase2 ; bit 2
LSL r18
RCALL maple_tx_phase1 ; bit 1
LSL r18
RCALL maple_tx_phase2 ; bit 0
DEC r22 ; Are we done yet?
BRNE _maple_tx_next_byte ; No, get the next character
ret
; r22: length (1 byte)
; r23: unused
; r24: low order bit of buffer pointer
; r25: high order bit of buffer pointer
; Returns pointer to final character stored in r25:r24
maple_tx_raw:
; Set pins to output for transmission
SBI _SFR_IO_ADDR(DDRMAPLE), bitmaple1
SBI _SFR_IO_ADDR(DDRMAPLE), bitmaple5
RCALL maple_tx_start
RCALL maple_tx_data
RCALL maple_tx_end
; Tx over: set pins to input and enable pull-up resistors.
SBI _SFR_IO_ADDR(PORTMAPLE), bitmaple1
SBI _SFR_IO_ADDR(PORTMAPLE), bitmaple5
CBI _SFR_IO_ADDR(DDRMAPLE), bitmaple1
CBI _SFR_IO_ADDR(DDRMAPLE), bitmaple5
ret
; Watchdog timer
.macro timer_init
CLI
STS TIMSK2, r1 ; Normal mode (no PWM)
STS TCCR2A, r1 ; Normal mode (no PWM)
LDI r18, 1 << CS22 | 1 << CS21 | 1 << CS20
STS TCCR2B, r18 ; Timer increments every 1024 cycles
; and overflows every 1024 * 256 cycles,
; or about 61 times a second at 16MHz
STS TCNT2, r1 ; Timer count
LDI r18, 1 ; TIFR2[0] -> 1 to clear
STS TIFR2, r18 ; overflow irq flag
SEI
.endm
.macro timer_on
LDI r18, 1 << TOIE2
STS TIMSK2, r18
.endm
.macro timer_off
STS TIMSK2, r1
.endm
.macro timer_reset
STS TCNT2, r1 ; Count register -> 0
.endm
; Reading
.macro _rx_read clockbit
; Version 1:
; Worst-case scenario here: the port is set halfway through the IN -- total
; cost is most of the IN, the BST, 2 cycles for the BRTS, plus the next IN,
; for a total of 5 cycles, which is sometimes out of tolerance.
1: IN rport, _SFR_IO_ADDR(PINMAPLE) ; 1
BST rport, \clockbit ; 1
BRTS 1b ; 1 (if fall through)
; Version 2:
; This works a little better because by the time we detect the falling edge
; we already have the value.
;1: IN rport, _SFR_IO_ADDR(PINMAPLE)
; SBIC _SFR_IO_ADDR(PINMAPLE), \clockbit
; RJMP 1b
.endm
.macro _rx_store reg srcbit destbit
BST \reg, \srcbit ; 1
BLD r18, \destbit ; 1
;1: IN \reg, _SFR_IO_ADDR(PINMAPLE)
; BST \reg, \srcbit
; BRTC 1b
.endm
maple_rx_raw:
MOVW r26, r24 ; X register <- the buffer
; Set up our bailout routine: r25:24 point to an address to "reti" to.
ldi r25, hh8(pm(_maple_rx_raw_watchdog))
ldi r24, hi8(pm(_maple_rx_raw_watchdog))
ldi r23, lo8(pm(_maple_rx_raw_watchdog))
LDS r18, TIMSK0 ; disable Arduino interrupts
PUSH r18 ; to re-enable later
STS TIMSK0, r1
timer_init ; Set up our watchdog timer
; Tell the device it can start sending data. (this is from the tx lead-out)
; maple_high bitmaple5
; Wait for a start sequence
timer_on ; rx will start soon
1: IN rport2, _SFR_IO_ADDR(PINMAPLE)
BST rport2, bitmaple1 ; maple1
BRTC 1b ; must be high initially
2: IN rport2, _SFR_IO_ADDR(PINMAPLE)
BST rport2, bitmaple1
BRTS 2b ; must be low to continue
3: IN rport2, _SFR_IO_ADDR(PINMAPLE)
BST rport2, bitmaple1 ; maple1
BRTC 3b ; must be high now
_rx_read bitmaple1
_rx_loop:
_rx_store rport bitmaple5 7
_rx_read bitmaple5
_rx_store rport bitmaple1 6
_rx_read bitmaple1
_rx_store rport bitmaple5 5
_rx_read bitmaple5
_rx_store rport bitmaple1 4
_rx_read bitmaple1
timer_reset ; pat the watchdog
_rx_store rport bitmaple5 3
_rx_read bitmaple5
_rx_store rport bitmaple1 2
_rx_read bitmaple1
_rx_store rport bitmaple5 1
_rx_read bitmaple5
_rx_store rport bitmaple1 0 ; 2
ST X+, r18
_rx_read bitmaple1
RJMP _rx_loop
_maple_rx_raw_watchdog: ; Watchdog bailed us out. We're done!
; debug_pin_high
POP r18 ;
STS TIMSK0, r18 ; re-enable Arduino interrupts
MOVW r24, r26 ; end of buffer <- X register
ret
; Watchdog timer interrupt routine
; When this fires, it returns to r25:r24, effectively doing
; a longjmp().
TIMER2_OVF_vect:
IN intsave, _SFR_IO_ADDR(SREG) ; Save registers;
POP intwork ; Remove the old return address
POP intwork ; all three words
POP intwork ;
PUSH r23 ; Store the new return address
PUSH r24 ; all three words
PUSH r25 ;
STS TIMSK2, r1 ; Disable the timer
OUT _SFR_IO_ADDR(SREG), intsave ; Restore registers
reti
maple_timer_test:
; Set up our bailout routine: r25:24 point to an address to "reti" to.
; broken for 22 bit pc
ldi r25, hh8(pm(maple_timer_test_watchdog))
ldi r24, hi8(pm(maple_timer_test_watchdog))
ldi r23, lo8(pm(maple_timer_test_watchdog))
; Set up the timer
timer_init
timer_on
1:
nop
rjmp 1b
maple_timer_test_watchdog:
; Timer got us!
timer_off
ret;
; Turn the led on if r24 (first int parameter) is 1, off if r24 is 0.
debug:
IN r18, _SFR_IO_ADDR(PORTB)
bst r24, 0 ; first parameter: on or off?
bld r18, DEBUG_LED_BIT ; bit 5 on duemilanove, bit 7 on the mega
out _SFR_IO_ADDR(PORTB), r18 ; on port B
ret