; BlueDataDistributor - data distribution module from ethernet to 32 serial ports
; version 0.1 date 2006-09-18
; Copyright (C) 2006 Stefan Schuermans <stefan@blinkenarea.org>
; a BlinkenArea project - http://www.blinkenarea.org/

#include <avr/io.h>

.section .text



; output  0 = PB0
; output  1 = PG2
; output  2 = PB2
; output  3 = PB3
; output  4 = PB4
; output  5 = PB5
; output  6 = PB6
; output  7 = PB7

; output  8 = PF0
; output  9 = PF1
; output 10 = PF2
; output 11 = PF3
; output 12 = PF4
; output 13 = PF5
; output 14 = PF6
; output 15 = PF7

; output 16 = PD0
; output 17 = PD1
; output 18 = PD2
; output 19 = PD3
; output 20 = PD4
; output 21 = PD5
; output 22 = PD6
; output 23 = PD7

; output 24 = PG0
; output 25 = PG1
; output 26 = PE2
; output 27 = PE3
; output 28 = PG4
; output 29 = PE5
; output 30 = PE6
; output 31 = PE7



.macro generatebit sourcemask, destreg, destbit
	ld	R16, Z+
	and	R16, \sourcemask
	breq	.+2					; jump over next instruction
	sbr	\destreg, 1 << \destbit
.endm
; 5 cycles



.macro generatebytes sourcemask
	clr	R17					; clear registers (5 cycles)
	clr	R18
	clr	R19
	clr	R20
	clr	R21

	generatebit	\sourcemask, R17, 0		; for PORTB
	generatebit	\sourcemask, R21, 2		; for PG2
	generatebit	\sourcemask, R17, 2
	generatebit	\sourcemask, R17, 3
	generatebit	\sourcemask, R17, 4
	generatebit	\sourcemask, R17, 5
	generatebit	\sourcemask, R17, 6
	generatebit	\sourcemask, R17, 7

	generatebit	\sourcemask, R20, 0		; for PORTF
	generatebit	\sourcemask, R20, 1
	generatebit	\sourcemask, R20, 2
	generatebit	\sourcemask, R20, 3
	generatebit	\sourcemask, R20, 4
	generatebit	\sourcemask, R20, 5
	generatebit	\sourcemask, R20, 6
	generatebit	\sourcemask, R20, 7

	generatebit	\sourcemask, R18, 0		; for PORTD
	generatebit	\sourcemask, R18, 1
	generatebit	\sourcemask, R18, 2
	generatebit	\sourcemask, R18, 3
	generatebit	\sourcemask, R18, 4
	generatebit	\sourcemask, R18, 5
	generatebit	\sourcemask, R18, 6
	generatebit	\sourcemask, R18, 7

	generatebit	\sourcemask, R21, 0		; for PG0
	generatebit	\sourcemask, R21, 1		; for PG1
	generatebit	\sourcemask, R19, 2		; for PORTE
	generatebit	\sourcemask, R19, 3
	generatebit	\sourcemask, R21, 4		; for PG4
	generatebit	\sourcemask, R19, 5
	generatebit	\sourcemask, R19, 6
	generatebit	\sourcemask, R19, 7
.endm
; 4 * 8 * 5 + 5 cycles = 165 cycles



.macro loadvalue value
	mov		R17, \value
	mov		R18, \value
	mov		R19, \value
	mov		R20, \value
	mov		R21, \value
.endm
; 5 cycles



.macro outputbytes
	ori	R17, 0x02			; PB1 is programming clock, its pull-up must stay on
	ori	R19, 0x01			; PE0 is programming data input, its pull-up must stay on
	andi	R19, 0xED			; PE1 is the programming data output, it must stay low
						; PE4 is the RTL8019 interrupt input, it must stay low
	andi	R21, 0x17			; PG3 is the status LED output, it must keep its level (PG7..5 do not exist)
	lds	R16, PORTG
	sbrc	R16, 3
	sbr	R21, 0x08
	out	PORTB-0x20, R17			; output
	out	PORTD-0x20, R18
	out	PORTE-0x20, R19
	sts	PORTF, R20
	sts	PORTG, R21
.endm
; 15 cycles



; output the specified bit of a byte on every serial port
;  - data must point to SERIAL_OUTPUT_CNT=32 bytes
;  - bit_mask must be 0x01, 0x02, ... 0x80 for bits 0, 1, ..., 7
;  - bit_mask must be 0x00 for the start bit and 0xFF for the stop bit
; extern void SerialOutputBit( unsigned char * data, unsigned char bit_mask );
.global SerialOutputBit
.func SerialOutputBit
SerialOutputBit:
	push	R16
	push	R17
	push	R18
	push	R19
	push	R20
	push	R21

	; parameters: R25:R24=data, R22=bit_mask

	mov	ZL, R24				; load data address into Z
	mov	ZH, R25

	cpi	R22,0x00			; check if to output a special bit
	breq	SerialOutputBitSpecial2nop
	cpi	R22,0xFF
	breq	SerialOutputBitSpecial
	rjmp	SerialOutputBitNormal

SerialOutputBitSpecial2nop:
	nop
	nop
SerialOutputBitSpecial:
	loadvalue	R22
	rcall	wait159
	rjmp	SerialOutputBitOutput

SerialOutputBitNormal:
	generatebytes	R22			; generate values to output

SerialOutputBitOutput:
	outputbytes				; output

	pop	R21
	pop	R20
	pop	R19
	pop	R18
	pop	R17
	pop	R16
	ret
.endfunc



; wait 159 cycles (including rcall and ret)
wait159:
	ldi	R16, 50				; 1 cycle
wait159_loop:
	dec	R16				; N cycles
	brne	wait159_loop			; 2N-1 cycles
	nop					; 2 cycles
	nop
	ret					; 7 cycles (including rcall)

