
;; Bitbanger - Copyright 2008 by Michael Kohn
;; Email: mike ta mikekohn.net
;;   Web: http://www.mikekohn.net/
;;
;; Bitbanging on a serial port example

.include "m8def.inc"

; 4MHz / 1200baud = 3333.333 cycles per bit

; note: CLKSEL 1111  (add 4MHz crystal resonator to schematic)

.equ TX_DATA  = SRAM_START
.equ TX_STATE = SRAM_START+1  ; 0 = idle  1 = go
.equ TX_BITS  = SRAM_START+2
.equ TX_BUSY  = SRAM_START+3  ; 0 = ready 1 = busy

.dseg

.cseg

.org 0x000
  rjmp start
.org 0x016
  rjmp service_bit
.org 0x01a
  rjmp service_bit

;; FIXME - erase this padding.. it's dumb
.org 0x020

start:
  ;; I'm busy.  Don't interrupt me!
  cli

  ;; clear the memory where servo info is
  eor r0, r0   ; r0 = constant 0
  eor r1, r1   ; r1 = constant 1
  inc r1

  ldi r17, 4
  ldi r27, TX_DATA>>8 
  ldi r26, TX_DATA&255
memset_serial:
  st X+, r0
  dec r17
  brne memset_serial

  ;; Set up stack ptr
  ldi r16, RAMEND>>8
  out SPH, r16
  ldi r16, RAMEND&255
  out SPL, r16

  ;; Set up PORTB
  ser r17
  ldi r18, 0
  out DDRB, r17             ; entire port B is output
  out PORTB, r17            ; turn off all of port B for fun
  out DDRC, r17             ; entire port C is output
  out PORTC, r17            ; turn on all of port C for fun

  ;; Set up TIMER1
  ;lds r17, PRR
  ;andi r17, 255 ^ (1<<PRTIM1)
  ;sts PRR, r17                   ; turn of power management bit on TIM1

  ldi r17, (3333>>8)              ; I think this could be 1 off
  out OCR1AH, r17
  ldi r17, (3333&0xff)            ; compare to 639 clocks (800 25MHz pixels)
  out OCR1AL, r17

  ldi r17, (1<<OCIE1A)
  out TIMSK, r17                  ; enable interrupt comare A 
  eor r17, r17
  ;sts TCCR1C, r17
  ; r17 = 0 still
  out TCCR1A, r17                ; normal counting (0xffff is top, count up)
  ldi r17, (1<<CS10)|(1<<WGM12) ; CTC OCR1A
  out TCCR1B, r17                ; prescale = 1 from clock source
  
  ; Fine, I can be interrupted now
  sei

  ldi r31, (message*2)>>8
  ldi r30, (message*2)&255

main:
  lds r20, TX_BUSY        ; poll uart to see if there is a data waiting
  sbrc r20, 0
  rjmp main               ; if no data, loop around

  ; TX_BUSY is false!  we can write!
  lpm r25, Z+
  ;ldi r25, 65 
  cpi r25, 0
  breq done

  sts TX_DATA, r25
  sts TX_BUSY, r1
  sts TX_STATE, r1
  rjmp main

done:                     ; nothing more to do.. LOOP!
  rjmp done


service_bit:
  ;; debug by toggling the hell out of PORTC (to see on the scope)
  eor r2, r2
  dec r2
  eor r26, r2
  out PORTC, r26

  ;; Check if new data needs to go out
  lds r18, TX_STATE  ; if (TX_STATE==0) goto no_strobe
  cpi r18, 0
  breq no_strobe

  out PORTB, r0      ; start bit
  sts TX_STATE, r0   ; unstrobe    TX_STATE=0
  ldi r18, 8
  sts TX_BITS, r18   ; TX_BITS=8
  ;sts TX_BUSY, r1   ; TX_BUSY=1
  reti

no_strobe:
  lds r18, TX_BUSY  ; if (TX_BUSY==1) goto output_bit
  cpi r18, 1
  breq output_bit   ; this could be done with a skip instead of branch
  reti              ; nothing to do.  leave.

output_bit:
  lds r19, TX_BITS  ; if (TX_BITS>0) goto output_more_bits
  cpi r19, 0
  brne output_more_bits
  out PORTB, r1     ; stop bit
  sts TX_BUSY, r0   ; done    TX_BUSY = 0
  reti

output_more_bits:
  lds r18, TX_DATA
  sbrs r18, 0
  out PORTB, r0
  sbrc r18, 0
  out PORTB, r1

  lsr r18
  sts TX_DATA, r18
  dec r19
  sts TX_BITS, r19 
 
  reti


signature:
.db "Atmel Bitbanger - Copyright 2008 - Michael Kohn - Version 0.01"

message:
.db "I'm now officially in the Bitbanger Club with Oliver Hillmann",0



