;
;  Copyright (C) 2000 Jesper Hansen <jesperh@telia.com>.
;
;  This program is free software; you can redistribute it and/or
;  modify it under the terms of the GNU General Public License
;  as published by the Free Software Foundation; either version 2
;  of the License, or (at your option) any later version.
;
;  This program is distributed in the hope that it will be useful,
;  but WITHOUT ANY WARRANTY; without even the implied warranty of
;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;  GNU General Public License for more details.
;
;  You should have received a copy of the GNU General Public License
;  along with this program; if not, write to the Free Software Foundation, 
;  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
;
;
;*******************************************************************
;*******************************************************************
;
;Description
;
; Poor-mans DDS Synthesizer
; 
; Author = Jesper Hansen
; Target = AT90S2313
; Date   = 2001-02-15
;
; Code is written for use with AVR-GCC in assembler mode 
; flag:  -x assembler-with-cpp 
;
;
; PB0..7 = D/A Data out
;
; PD0		RXD
; PD1		TXD
; PD2..6	not used
;

;*******************************************************************
;*******************************************************************
;
;
;
; Output frequency (using 24 bit accumulator) :
;
;	f = deltaPhase * fClock/2^24
;
;   fClock is in this case the CPU clock divided by the
;	number of cycles to output the data ( 9 cycles )
;
;	f = r24/r25/r26 * (11059200/9)/16777216
;
;	f = r24/r25/r26 * 0.073242188
;
;	fMax (theoretical) = 0.5 * fClock
;


;******************************************************************************
; start of code
;******************************************************************************


#include <io2313.h>


	.section 	.text

	.org 0
		rjmp	RESET

	.org 14
		rjmp	RX_COMPLETE_INT


;******************************************************************************
; data tables
;******************************************************************************

	; force table to begin at 256 byte boundary

	.org 0x100

sine:		; 256 step sinewave table
	.byte	0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae
	.byte	0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8
	.byte	0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5
	.byte	0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff
	.byte	0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7
	.byte	0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc
	.byte	0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3
	.byte	0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83
	.byte	0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51
	.byte	0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27
	.byte	0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a
	.byte	0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00
	.byte	0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08
	.byte	0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23
	.byte	0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c
	.byte	0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c

sawtooth:	; 256 step sawtoothwave table
	.byte	0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f
	.byte	0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f
	.byte	0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f
	.byte	0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f
	.byte	0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f
	.byte	0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f
	.byte	0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f
	.byte	0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f
	.byte	0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f
	.byte	0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f
	.byte	0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf
	.byte	0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf
	.byte	0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf
	.byte	0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf
	.byte	0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef
	.byte	0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff

triangle:	; 256 step trianglewave table
	.byte	0x00,0x02,0x04,0x06,0x08,0x0a,0x0c,0x0e,0x10,0x12,0x14,0x16,0x18,0x1a,0x1c,0x1e
	.byte	0x20,0x22,0x24,0x26,0x28,0x2a,0x2c,0x2e,0x30,0x32,0x34,0x36,0x38,0x3a,0x3c,0x3e
	.byte	0x40,0x42,0x44,0x46,0x48,0x4a,0x4c,0x4e,0x50,0x52,0x54,0x56,0x58,0x5a,0x5c,0x5e
	.byte	0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6e,0x70,0x72,0x74,0x76,0x78,0x7a,0x7c,0x7e
	.byte	0x80,0x82,0x84,0x86,0x88,0x8a,0x8c,0x8e,0x90,0x92,0x94,0x96,0x98,0x9a,0x9c,0x9e
	.byte	0xa0,0xa2,0xa4,0xa6,0xa8,0xaa,0xac,0xae,0xb0,0xb2,0xb4,0xb6,0xb8,0xba,0xbc,0xbe
	.byte	0xc0,0xc2,0xc4,0xc6,0xc8,0xca,0xcc,0xce,0xd0,0xd2,0xd4,0xd6,0xd8,0xda,0xdc,0xde
	.byte	0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xee,0xf0,0xf2,0xf4,0xf6,0xf8,0xfa,0xfc,0xfe
	.byte	0xff,0xfd,0xfb,0xf9,0xf7,0xf5,0xf3,0xf1,0xef,0xef,0xeb,0xe9,0xe7,0xe5,0xe3,0xe1
	.byte	0xdf,0xdd,0xdb,0xd9,0xd7,0xd5,0xd3,0xd1,0xcf,0xcf,0xcb,0xc9,0xc7,0xc5,0xc3,0xc1
	.byte	0xbf,0xbd,0xbb,0xb9,0xb7,0xb5,0xb3,0xb1,0xaf,0xaf,0xab,0xa9,0xa7,0xa5,0xa3,0xa1
	.byte	0x9f,0x9d,0x9b,0x99,0x97,0x95,0x93,0x91,0x8f,0x8f,0x8b,0x89,0x87,0x85,0x83,0x81
	.byte	0x7f,0x7d,0x7b,0x79,0x77,0x75,0x73,0x71,0x6f,0x6f,0x6b,0x69,0x67,0x65,0x63,0x61
	.byte	0x5f,0x5d,0x5b,0x59,0x57,0x55,0x53,0x51,0x4f,0x4f,0x4b,0x49,0x47,0x45,0x43,0x41
	.byte	0x3f,0x3d,0x3b,0x39,0x37,0x35,0x33,0x31,0x2f,0x2f,0x2b,0x29,0x27,0x25,0x23,0x21
	.byte	0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x11,0x0f,0x0f,0x0b,0x09,0x07,0x05,0x03,0x01



square:		; 256 step squarewave table
	.byte	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
	.byte	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
	.byte	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
	.byte	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
	.byte	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
	.byte	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
	.byte	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
	.byte	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
	.byte	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
	.byte	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
	.byte	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
	.byte	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
	.byte	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
	.byte	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
	.byte	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
	.byte	0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff



;******************************************************************************
; code
;******************************************************************************


RESET:
		ldi		r16, RAMEND
		out		SPL, r16		; setup stack pointer

		ldi		r16,0x05		; set uart speed to 115.2 kbps
		out		UBRR,r16

		ldi		r16,0x98		; enable RXint and enable tx/rx
		out		UCR,r16

		sei						; global enable interrupts

		ser		r16				; 
		out		DDRB,r16		; set all PORTB bits as output
	

		; set sinewave output as default
		
		ldi		r31,hi8(sine)	; setup Z pointer hi
		ldi		r30,lo8(sine)	; setup Z pointer lo

		; clear accumulator 

		ldi 	r29,0x00		; clear accumulator 
		ldi 	r28,0x00		; clear accumulator 

		; setup adder registers		
		
		ldi 	r24,0x55		; setup adder value
		ldi 	r25,0x35		; to 1 kHz
		ldi 	r26,0x00		; 



; main loop
;
;	r28,r29,r30 is the phase accumulator
;  	r24,r25,r26 is the adder value determining frequency
;
; 	add value to accumulator
;	load byte from current table in ROM
;	output byte to port
;	repeat 
;
LOOP1:
		add		r28,r24			; 1
		adc		r29,r25			; 1
		adc		r30,r26			; 1
		lpm						; 3
		out		PORTB,r0		; 1
		rjmp	LOOP1			; 2 => 9 cycles


;**********************************************************************
; communication functionality
;**********************************************************************

;
; get char in r16
;
get_char:
		in 		r16,USR			; wait for a byte to be ready
		sbrs	r16,7			; ready ?
		rjmp	get_char		; no, wait some more
		in		r16,UDR			; get the byte
		ret						; and return
		
;
; send char in r16
;
send_char:
		push	r16				; save r16
send_c2:
		in 		r16,USR			; wait for the transmitter to be ready
		sbrs	r16,5			; ready ? 
		rjmp	send_c2			; no, wait some more
		pop		r16				; restore r16
		out		UDR,r16			; send char
		ret						; ans return

;
; send the current frequency to the PC
; as a 5 byte sequence :
; 'F' folowed by a 32 bit phase accumulator value
;
;
send_data:
		push	r16				; save r16
		ldi		r16,'F'			; flag
		rcall	send_char

		clr		r16				; zero byte for 32-bit compatibility
		rcall	send_char		; MSB

		mov		r16,r26
		rcall	send_char		; high add

		mov		r16,r25
		rcall	send_char		; mid add

		mov		r16,r24
		rcall	send_char		; low add

		ldi		r16,0x0a
		rcall	send_char		; terminator
		pop		r16
		ret


; add 1 to the phase accumulator		
up_one:
		adiw	r24,1	
		clr		r23
		adc		r26,r23
		ret

; add 10 to the phase accumulator		
up_ten:
		adiw	r24,10	
		clr		r23
		adc		r26,r23
		ret

; add 100 to the phase accumulator		
up_hundred:
		ldi		r23,0x64
		add		r24,r23
		clr		r23
		adc		r25,r23
		adc		r26,r23
		ret

; subtract 1 from the phase accumulator		
down_one:
		sbiw	r24,1		
		clr		r23
		sbc		r26,r23
		ret

; subtract 10 from the phase accumulator		
down_ten:
		sbiw	r24,10		
		clr		r23
		sbc		r26,r23
		ret

; subtract 100 from the phase accumulator		
down_hundred:
		clr		r23
		subi	r24,0x64		
		sbc		r25,r23
		sbc		r26,r23
		ret

; 
; read in 4 characters from the serial link
;
read_4:
		rcall	get_char		; read and ignore bits 32..24
		rcall	get_char		; read bits 23..16
		mov		r26,r16
		rcall	get_char		; read bits 15..8
		mov		r25,r16
		rcall	get_char		; read bits 7..0
		mov		r24,r16
		ret

;
; Interrupt routine for incoming bytes on the RS232 link
;
	
RX_COMPLETE_INT:
		push	r16
		in 		r16,UDR
		cpi		r16,'+'				; up one
		brne	tx_2
		rcall	up_one
		rjmp	tx_exit		
tx_2:
		cpi		r16,'u'				; up ten
		brne	tx_3
		rcall	up_ten
		rjmp	tx_exit		
tx_3:
		cpi		r16,'U'				; up hundred
		brne	tx_4
		rcall	up_hundred
		rjmp	tx_exit		
tx_4:
		cpi		r16,'-'				; down one
		brne	tx_5
		rcall	down_one
		rjmp	tx_exit		
tx_5:
		cpi		r16,'d'				; down ten
		brne	tx_6
		rcall	down_ten
		rjmp	tx_exit		
tx_6:
		cpi		r16,'D'				; down hundred
		brne	tx_7
		rcall	down_hundred		
		rjmp	tx_exit		
tx_7:
		cpi		r16,'s'				; frequency setting
		brne	tx_8
		rcall	read_4		
		rjmp	tx_exit		
tx_8:
		cpi		r16,'?'				; just force a reply
		brne	tx_9
		rjmp	tx_exit		
tx_9:	
		cpi		r16,'1'				; request sinewave output
		brne	tx_10
		ldi		r31,hi8(sine)		; setup Z pointer hi
		ldi		r30,lo8(sine)		; setup Z pointer lo
		rjmp	tx_exit
tx_10:
		cpi		r16,'2'				; request sawtooth output
		brne	tx_11
		ldi		r31,hi8(sawtooth)	; setup Z pointer hi
		ldi		r30,lo8(sawtooth)	; setup Z pointer lo
		rjmp	tx_exit
tx_11:
		cpi		r16,'3'				; request triangle output
		brne	tx_12
		ldi		r31,hi8(triangle)	; setup Z pointer hi
		ldi		r30,lo8(triangle)	; setup Z pointer lo
		rjmp	tx_exit
tx_12:
		cpi		r16,'4'				; request squarewave output
		brne	tx_13
		ldi		r31,hi8(square)		; setup Z pointer hi
		ldi		r30,lo8(square)		; setup Z pointer lo
		rjmp	tx_exit

; unknown command, just ignore it
tx_13:


; always reply with the current frequency
tx_exit:
		rcall	send_data
		pop		r16
		reti





;******************************************************************************
; end of file	
;******************************************************************************

