]> cloudbase.mooo.com Git - avrcpm.git/blob - avr/sw-uart.asm
SVN --> GIT
[avrcpm.git] / avr / sw-uart.asm
1 ; Serial interface using the ATmega8/88 USART.
2 ; This is part of the Z80-CP/M emulator written by Sprite_tm.
3 ;
4 ; Copyright (C) 2010 Leo C.
5 ;
6 ; This file is part of avrcpm.
7 ;
8 ; avrcpm is free software: you can redistribute it and/or modify it
9 ; under the terms of the GNU General Public License as published by
10 ; the Free Software Foundation, either version 3 of the License, or
11 ; (at your option) any later version.
12 ;
13 ; avrcpm is distributed in the hope that it will be useful,
14 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ; GNU General Public License for more details.
17 ;
18 ; You should have received a copy of the GNU General Public License
19 ; along with avrcpm. If not, see <http://www.gnu.org/licenses/>.
20 ;
21 ; $Id$
22 ;
23
24 #ifdef __ATmega8__
25 #error "ATmega8 is not supported (yet)! Please update this driver, or buy an ATmega88."
26 #endif
27
28 #define SSER_BIT_TC (F_CPU+BAUD/2) / BAUD
29 #define SSER_CHAR_TC (10 * 1000 / BAUD) + 2
30
31 #define RXBUFMASK RXBUFSIZE-1
32 #define TXBUFMASK TXBUFSIZE-1
33
34 .dseg
35
36 srx_state:
37 .byte 1
38 srx_char_to:
39 .byte 1
40 srx_char_time:
41 .byte 1
42 srx_dr:
43 .byte 1
44 srx_lastedge:
45 .byte 2
46 stx_bitcount:
47 .byte 1
48 stx_dr:
49 .byte 1
50 rxcount:
51 .byte 1
52 rxidx_w:
53 .byte 1
54 rxidx_r:
55 .byte 1
56 txcount:
57 .byte 1
58 txidx_w:
59 .byte 1
60 txidx_r:
61 .byte 1
62 rxfifo:
63 .byte RXBUFSIZE
64 txfifo:
65 .byte TXBUFSIZE
66
67
68 .cseg
69
70 ; Init
71 uart_init:
72
73 ; - Init clock/timer system and serial port
74
75 ; Init timer 1 as
76 ; - Soft UART TX (OC1A/OCR1A).
77 ; - Soft UART RX (ICP1/ICR1).
78 ; - 1ms System timer is already configured at this point.
79
80
81 cbi P_TXD-1,TXD ;TXD pin as input
82 ldi temp,(1<<COM1A1)|(1<<COM1A0) ;OC1A high on compare match (UART TX)
83 ldi temp2,(1<<FOC1A) ;force compare match
84 outm8 TCCR1A,temp
85 outm8 TCCR1C,temp2
86 sbi P_TXD-1,TXD ;TXD pin now output (OC1A)
87
88 ldi temp,(1<<ICF1) ;clear pending input capture int
89 out TIFR1,temp ;
90 inm8 temp,TIMSK1 ;
91 ori temp,(1<<ICIE1) ;Enable input capture int. (UART RX)
92 outm8 TIMSK1,temp ;
93
94 ldi temp,SSER_CHAR_TC ;Character TO
95 sts srx_char_time,temp
96
97 ret
98
99 ;------------------------------------------------------------------
100
101 .cseg
102
103 ; Timer/Counter1 Input Capture interrupt
104
105 INTERRUPT ICP1addr
106
107 push temp
108 in temp,sreg
109 push temp
110 push zh
111 push zl
112 inm8 zl,ICR1L
113 inm8 zh,ICR1H
114 push temp2
115 ldi temp2,(1<<ICES1)
116 inm8 temp,TCCR1B
117 eor temp,temp2 ;toggle edge
118 outm8 TCCR1B,temp
119 ldi temp,(1<<ICF1) ;clear pending int
120 out TIFR1,temp
121
122 #if 0
123 lds temp,srx_state
124 subi temp,-'0'
125 rcall uartputc
126 lds temp,srx_dr
127 rcall printhex
128 #endif
129 lds temp,srx_state
130 cpi temp,0
131 brne srxi_S1
132
133 ; State 0: Wait for start bit
134
135 sts srx_lastedge,zl ;save beginning of start bit
136 sts srx_lastedge+1,zh
137 ; movw srx_lastedgel,zl
138 sts srx_dr,_0
139 ldi temp,1
140 sts srx_state,temp
141 lds temp,srx_char_time
142 sts srx_char_to,temp
143 sbis P_RXD-2,RXD ;RXD still low?
144 rjmp srxi_end
145 ldi zl,(1<<ICNC1)|(1<<CS10) ;next edge is falling edge
146 outm8 TCCR1B,zl
147 ldi zh,(1<<ICF1) ;clear pending int
148 out TIFR1,zh
149 sts srx_state,_0
150 sts srx_char_to,_0
151 rjmp srxi_end
152
153 srxi_S1:
154 cpi temp,1
155 brne srxi_S2
156
157 ; State 1: Check start bit (and collect 0-bits)
158
159 lds temp,srx_lastedge
160 lds temp2,srx_lastedge+1
161 sts srx_lastedge,zl
162 sts srx_lastedge+1,zh
163
164 ; movw temp,srx_lastedgel
165 ; movw srx_lastedgel,zl
166
167 sub zl,temp
168 sbc zh,temp2
169 subi zl,low ((SSER_BIT_TC+1)/2)
170 sbci zh,high((SSER_BIT_TC+1)/2)
171 brcs srxi_sberr
172
173 ; mov temp,zh
174 ; rcall printhex
175 ; mov temp,zl
176 ; rcall printhex
177
178 ldi temp,0x80
179 srxi_1l:
180 subi zl,low(SSER_BIT_TC)
181 sbci zh,high(SSER_BIT_TC)
182 brcs srxi_1be
183 lsr temp
184 brcc srxi_1l
185
186 subi zl,low(SSER_BIT_TC) ; stop bit?
187 sbci zh,high(SSER_BIT_TC)
188 brcc srxi_1fe
189 rjmp srxi_complete0 ; ok, x00 (^@) received
190 srxi_1fe:
191 sts srx_char_to,_0 ; no stop bit --> framing error --> break
192 sts srx_state,_0
193 sbr intstat,(1<<i_break) ;
194 sts rxcount,_0 ;clear rx buffer
195 sts rxidx_w,_0
196 sts rxidx_r,_0
197
198 rjmp srxi_end
199
200 srxi_1be:
201 sts srx_dr,temp
202 ldi temp,2
203 sts srx_state,temp
204 rjmp srxi_end
205
206 srxi_sberr:
207 ldi temp,(1<<ICNC1)|(1<<CS10) ;next edge is falling edge
208 outm8 TCCR1B,temp
209 ldi temp,(1<<ICF1) ;clear pending int
210 out TIFR1,temp
211 sts srx_state,_0 ;next state
212 #if 1
213 ldi temp,'?'
214 rcall uartputc
215 subi zl,low (-(SSER_BIT_TC+1)/2)
216 sbci zh,high(-(SSER_BIT_TC+1)/2)
217 mov temp,zh
218 rcall printhex
219 mov temp,zl
220 rcall printhex
221 #endif
222 rjmp srxi_end
223
224 srxi_S2:
225 cpi temp,2
226 brne srxi_S3
227
228 ; State 2: collect 1-bits
229
230 lds temp,srx_lastedge
231 lds temp2,srx_lastedge+1
232 sts srx_lastedge,zl
233 sts srx_lastedge+1,zh
234
235 ; movw temp,srx_lastedgel
236 ; movw srx_lastedgel,zl
237
238 sub zl,temp
239 sbc zh,temp2
240 subi zl,low ((SSER_BIT_TC+1)/2)
241 sbci zh,high((SSER_BIT_TC+1)/2)
242
243 lds temp,srx_dr
244 srxi_2l:
245 sec ;one more 1 bit
246 ror temp
247 brcs srxi_complete1 ;8 bits recieved
248 subi zl,low(SSER_BIT_TC)
249 sbci zh,high(SSER_BIT_TC)
250 brcc srxi_2l
251
252 sts srx_dr,temp
253 ldi temp,3
254 sts srx_state,temp
255 rjmp srxi_end
256
257 srxi_complete1:
258 ldi temp2,1 ;We are in start bit now.
259 sts srx_state,temp2
260 lds temp2,srx_char_time
261 sts srx_char_to,temp2
262 rjmp srxi_complete
263
264 srxi_S3:
265 cpi temp,3
266 brne srxi_S4
267
268 ; State 3: collect 0-bits
269
270 lds temp,srx_lastedge
271 lds temp2,srx_lastedge+1
272 sts srx_lastedge,zl
273 sts srx_lastedge+1,zh
274
275 ; movw temp,srx_lastedgel
276 ; movw srx_lastedgel,zl
277
278 sub zl,temp
279 sbc zh,temp2
280 subi zl,low ((SSER_BIT_TC+1)/2)
281 sbci zh,high((SSER_BIT_TC+1)/2)
282
283 lds temp,srx_dr
284 srxi_3l:
285 ;one more 0 bit
286 lsr temp
287 brcs srxi_complete0 ;8 bits recieved
288 subi zl,low(SSER_BIT_TC)
289 sbci zh,high(SSER_BIT_TC)
290 brcc srxi_3l
291
292 sts srx_dr,temp
293 ldi temp,2
294 sts srx_state,temp
295 rjmp srxi_end
296
297 srxi_S4:
298 ldi zl,(1<<ICNC1)|(1<<CS10) ;next edge is falling edge
299 outm8 TCCR1B,zl
300 ldi zl,(1<<ICF1) ;clear pending int
301 sts srx_state,_0 ;next state
302 rjmp srxi_end
303
304 srxi_complete0:
305 sts srx_char_to,_0 ;clear timeout
306 sts srx_state,_0 ;next state
307 srxi_complete:
308 #if 0
309 ldi zl,(1<<ICNC1)|(1<<CS10) ;next edge is falling edge
310 outm8 TCCR1B,zl
311 ldi zl,(1<<ICF1) ;clear pending int
312 out TIFR1,zl
313 #endif
314
315 ; Save received character in a circular buffer. Do nothing if buffer overflows.
316
317 lds zh,rxcount ;2 if rxcount < RXBUFSIZE
318 cpi zh,RXBUFSIZE ;1 (room for at least 1 char?)
319 brsh srxi_ov ;1
320 inc zh ;1
321 sts rxcount,zh ;2 rxcount++
322
323 ldi zl,low(rxfifo) ;1
324 lds zh,rxidx_w ;2
325 add zl,zh ;1
326 inc zh ;1
327 andi zh,RXBUFMASK ;1
328 sts rxidx_w,zh ;2 rxidx_w = ++rxidx_w % RXBUFSIZE
329 ldi zh,high(rxfifo) ;1
330 adc zh,_0 ;1
331 st z,temp ;2 rxfifo[rxidx_w] = char
332 srxi_ov: ;=19 endif
333
334 srxi_end:
335 pop temp2
336 pop zl
337 pop zh
338 pop temp
339 out sreg,temp
340 pop temp
341 reti
342
343
344 ;----------------------------------------------------------------------
345
346 .cseg
347
348 ; Timer/Counter1 Compare Match A interrupt
349
350 INTERRUPT OC1Aaddr
351
352 push zl
353 in zl,sreg
354 push zl
355 push zh
356
357 inm8 zl,OCR1AL
358 inm8 zh,OCR1AH
359 subi zl,low(-SSER_BIT_TC)
360 sbci zh,high(-SSER_BIT_TC)
361 outm8 OCR1AH,zh
362 outm8 OCR1AL,zl
363
364 lds zl,stx_bitcount
365 dec zl
366 brpl stxi_nextbit
367
368 ; bit counter was 0, more characters?
369
370 stxi_nxtchar:
371 lds zl,txcount ;if txcount != 0
372 dec zl
373 brmi stxi_dis
374
375 ; get next char
376 sts txcount,zl ; --txcount
377 push temp ;
378 ldi zl,low(txfifo) ;
379 ldi zh,high(txfifo) ;
380 lds temp,txidx_r ;
381 add zl,temp ;
382 adc zh,_0
383 inc temp ;
384 andi temp,TXBUFMASK ;
385 sts txidx_r,temp ;
386 ld temp,z
387 com temp
388 sts stx_dr,temp
389 ldi temp,9
390 sts stx_bitcount,temp
391 pop temp
392
393 ldi zh,(1<<COM1A1)
394 rjmp stxi_ex
395
396 ; disable transmitter
397 stxi_dis:
398 inm8 zl,TIMSK1
399 andi zl,~(1<<OCIE1A)
400 outm8 TIMSK1,zl
401
402 ldi zh,(1<<COM1A1)|(1<<COM1A0)
403 rjmp stxi_ex
404
405 stxi_nextbit:
406 sts stx_bitcount,zl
407
408 ldi zh,(1<<COM1A1)
409 lds zl,stx_dr
410 sbrs zl,0
411 ldi zh,(1<<COM1A1)|(1<<COM1A0)
412 lsr zl
413 sts stx_dr,zl
414 stxi_ex:
415 outm8 TCCR1A,zh
416 pop zh
417 pop zl
418 out sreg,zl
419 pop zl
420 reti
421
422 ;------------------------------------------------------------------
423
424 srx_to:
425 #if 0
426 ldi zl,(1<<ICNC1)|(1<<CS10) ;next edge is falling edge
427 outm8 TCCR1B,zl
428 ldi zl,(1<<ICF1) ;clear pending int
429 out TIFR1,zl
430 #endif
431 sts srx_state,_0 ;next state
432 push temp
433
434 #if 0
435 ldi temp,'|'
436 rcall uartputc
437 #endif
438 lds temp,srx_dr ;only 0 if timeout after leading edge of start bit.
439 tst temp ; --> break
440 brne srxto_store
441 sbr intstat,(1<<i_break)
442 sts rxcount,_0 ;clear rx buffer
443 sts rxidx_w,_0
444 sts rxidx_r,_0
445 rjmp srxto_ov
446
447 srxto_store:
448 mov zl,temp
449 com zl
450 andi zl,0x80
451 srxto_l:
452 lsr temp
453 or temp,zl
454 brcc srxto_l
455
456 ; Save received character in a circular buffer. Do nothing if buffer overflows.
457
458 lds zh,rxcount ;if rxcount < RXBUFSIZE
459 cpi zh,RXBUFSIZE ; (room for at least 1 char?)
460 brsh srxto_ov ;
461 inc zh ;
462 sts rxcount,zh ; rxcount++
463
464 ldi zl,low(rxfifo) ;
465 lds zh,rxidx_w ;
466 add zl,zh ;
467 inc zh ;
468 andi zh,RXBUFMASK ;
469 sts rxidx_w,zh ; rxidx_w = ++rxidx_w % RXBUFSIZE
470 ldi zh,high(rxfifo) ;
471 brcc PC+2 ;
472 inc zh ;
473 st z,temp ; rxfifo[rxidx_w] = char
474 srxto_ov: ;endif
475
476 pop temp
477 ret
478
479
480 ;Fetches a char from the buffer to temp. If none available, waits till one is.
481
482 uartgetc:
483 push zh
484 push zl
485 push temp2
486 ugetc_w:
487 lds temp,rxcount ;Number of characters in buffer
488 tst temp
489 breq ugetc_w ;Wait for char
490
491 ldi zl,low(rxfifo)
492 ldi zh,high(rxfifo)
493 lds temp2,rxidx_r
494 add zl,temp2
495 adc zh,_0
496 inc temp2
497 andi temp2,RXBUFMASK
498 cli
499 lds temp,rxcount
500 subi temp,1
501 brcc ugetc_fin
502 sei
503 rjmp ugetc_w
504
505 ugetc_fin:
506 sts rxcount,temp
507 sts rxidx_r,temp2
508 sei
509 ld temp,z ;don't forget to get the char
510 pop temp2
511 pop zl
512 pop zh
513 ret
514
515 ;Sends a char from temp to the soft uart.
516
517 uartputc:
518 push zh
519 push zl
520 in zl,sreg
521 push zl
522 push temp
523 sputc_l:
524 lds temp,txcount ;do {
525 cpi temp,TXBUFSIZE ;
526 brsh sputc_l ;} while (txcount >= TXBUFSIZE)
527
528 cli
529 ldi zl,low(txfifo) ;
530 ldi zh,high(txfifo) ;
531 lds temp,txidx_w ;
532 add zl,temp ;
533 adc zh,_0 ;
534 inc temp ;
535 andi temp,TXBUFMASK ;
536 sts txidx_w,temp ; txidx_w = ++txidx_w % TXBUFSIZE
537 pop temp ;
538 st z,temp ; txfifo[txidx_w] = char
539 ; cli
540 lds zh,txcount
541 inc zh
542 sts txcount,zh
543 cpi zh,1
544 brne sputc_e
545 ; Enable transmitter
546 inm8 zh,TIMSK1
547 sbrc zh,OCIE1A
548 rjmp sputc_e
549 ori zh,(1<<OCIE1A)
550 outm8 TIMSK1,zh
551
552 inm8 zl,TCNT1L ;
553 inm8 zh,TCNT1H ;
554 adiw zl,30 ;
555 outm8 OCR1AH,zh ;
556 outm8 OCR1AL,zl ;
557 ldi zl,(1<<OCF1A)
558 outm8 TIFR1,zl
559
560 sputc_e:
561 pop zl
562 out sreg,zl
563 pop zl
564 pop zh
565 ret
566
567
568 ; Wait, till tx buffer is empty.
569
570 uart_wait_empty:
571 push temp
572 uwe_loop:
573 lds temp,txcount
574 tst temp
575 brne uwe_loop
576 pop temp
577 ret
578
579
580 ; vim:set ts=8 noet nowrap