;Copyright 2004 Richard Cappels, projects@cappels.org
;Confidential - Not for distribution.
;Program Name: Send Morse

;mor040220BBeacon, version is "K60 040220B  ", QRSS is 1 second dot. Fast is 83.3 ms per dot with 6 MHz clock.


;Key subroutines:
;dottime - set of delay loops to set basic dot time. Interacts with interupt routine.
;The dot time is set by adjusting the value loaded into temp at the start of the routine.
;Loading 100 into temp gives 1 wpm. Loading 1 into temp gives 100 wmp. Loadign 20 gets 5 wpm.
	
;sendromstring -send a string from Flash ROM. See TypeGreeting for example of use.

;sendSerialMorse - sends ASCII Data via serial port and as Morse Code.
;	ldi	temp,'A'
;	rcall	sendSerialMorse

;sendnumberMorseASCII -Sends binary nummber in temp as a 3 digit number via serial port and as Morse Code.	
;	ldi	temp,123		;Send the nummber, 123
;	rcall	sendnumberMorseASCII
		
;interword - Delays one inter-word delay period	
;	rcall	interword
	
.include "2313def.inc"		;Include file in same directory as project.

;03e7
		;Interrupt timer parameters
.equ	intcountlow	=$E7	;Low byte of number of clocks between interrupts. 
.equ	intcounthigh	=$03	;High byte of number of clocks between interrupts.

		;UART baud rate calculation
.equ	clock = 4000000	;clock frequency
.equ	baudrate = 9600		;choose a baudrate
.equ	baudconstant = (clock/(16*baudrate))-1

.def	delaycount	= r2	;Counter for delay generation
.def	dotlength	= r3	;Used to set dot length. In 1/100 sec when dotlength 2 = 20
.def	dotlength2	= r4	;Used to set dot length.
.def	temp		= r16	;General purpose scratch register.
.def	temp2		= r17	;General purpose scratch register. 
.def	h		= r22	;Binary to decimal conversion.
.def	t		= r23   ;Binary to decmial conversion.
.def	u		= r24	;Binary to decimal conversion.
.def	flagreg		= r25	;Flags.


;definiton of flagreg bit assignments
;0	Status of code out bit last sent (memory used to toggle)
;1	True enables toggling of code output
;2
;3
;4
;5
;6
;7

.equ	codeport 	= PORTB
.equ	codeout		= DDRB
.equ	codebit 	= 2
.equ	pulsebit	= 3

;definition of I/O
;B0	+ comparitor input
;B1	- comparitor input
;B2	Tone (code) output	
;B3	Morse Code Key Pulses Out (positive corresponds with tone).	
;B4	(not assigned - configure as INPUT with weak pullup)	
;B5	(not assigned - configure as INPUT with weak pullup)	
;B6	(not assigned - configure as INPUT with weak pullup)	
;B7	(not assigned - configure as INPUT with weak pullup)	


;D0	Reserved FOR UART RECEIVE
;D1	Reserved FOR UART TRANSMIT -input has weak pullup.
;D2	(not assigned - configure as INPUT with weak pullup)
;D3	(not assigned - configure as INPUT with weak pullup)
;D4	(not assigned - configure as INPUT with weak pullup)
;D5	(not assigned - configure as INPUT with weak pullup)
;D6	(not assigned - configure as INPUT with weak pullup)
;D7	(not assigned - configure as INPUT with weak pullup)



.cseg	
.ORG $0000			;Initializaton code
	rjmp start
.ORG $0004 
	rjmp timerservice	;Timer/counter compare interrupt handler

start:
   
	ldi	r16,RAMEND		;Initialize Stack Pointer.
	out	spl,r16
					;Set PORTD.
	ldi	temp,0b00000010		
	out	DDRD,temp
	ldi	temp,0b10111111
	out	PORTD,temp
	
					;Set PORTB.
	ldi	temp,0b00001100		
	out	DDRB,temp
	ldi	temp,0b11110011
	out	PORTB,temp		
	
	
	clr	flagreg			;Clear flagreg (flag register).
	
	
	
					;UART SETUP.
	ldi     temp,baudconstant     
 	out     ubrr,temp     		;Load baudrate.
 	sbi	ucr,rxen		;Enable WAUT receive ;Uart receive not used so not enabled bere.
 	sbi	ucr,txen		;Enable UART Transmit.		


					;TIMER 1 SETUP.
	ldi	temp,$09		;Set timer 1 to reset 0000 after compare match. Prescaler = 1X.
	out	TCCR1B,temp
	ldi	temp,intcounthigh	;Set compare register to establish interrupt frequency.
	out	OCR1AH,temp
	ldi	temp,intcountlow
	out	OCR1AL,temp	
	ldi	temp,$40		;Enable interrupt on compare match.
	out	TIMSK,temp
	
	sei				;Enable interrupts.
	
	
	ldi	temp,$1E		;Set up for 83 millisecond dot period.
	mov	dotlength,temp		
	ldi	temp,$14
	mov	dotlength2,temp

	
	rcall	TypeGreeting		;Send greeting twice
	rcall	TypeGreeting

Main:


	ldi	temp,$1E		;Set up for 83 millisecond dot period.
	mov	dotlength,temp		
	ldi	temp,$14
	mov	dotlength2,temp		
	

	rcall	TypeMessage		;Send  message three times.
	rcall	TypeMessage
	rcall	TypeMessage


	ldi	temp,30		;Set to 1 second dot period.
	mov	dotlength,temp
	ldi	temp,252
	mov	dotlength2,temp

	
	rcall	interword
	rcall	TypeMessage		;Send message once
	rcall	interword
	
	
;	ldi	temp,123		;Send the nummber, 123
;	rcall	sendnumberMorseASCII



	rjmp	main	
	
;Main:		
	
	rcall	recchar
	push	temp
	rcall	emitchar
	pop	temp
	rcall	SendMorseAscii
	;emitchar
	rjmp	Main
	




TypeGreeting:				;Type greeting
	push	ZL
	push	ZH
	ldi		ZH,high(2*hellomessage)	;Load high part of byte address into ZH
	ldi		ZL,low(2*hellomessage)	;Load low part of byte address into ZL
	rcall	sendromstring		;Send it
	pop		ZH
	pop		ZL
	ret
	
	
	
hellomessage:
	.db     "K60 040220B   " 
;	.db	" A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9  "
;	.db	"0 1 2 3 4 5 6 7 8 9     "
	.db	$0A,$0D
	.db     $00,$00


TypeMessage:				;Type greeting
	push	ZL
	push	ZH
	ldi		ZH,high(2*Message)	;Load high part of byte address into ZH
	ldi		ZL,low(2*Message)	;Load low part of byte address into ZL
	rcall	sendromstring		;Send it
	pop		ZH
	pop		ZL
	ret
	
		
Message:
	.db     "K60 " 
	.db		$0A,$0D
	.db     $00,$00


sendromstring:		;call with location of string in Z.
	push	ZL		;Save Z on stack.
	push	ZH
srs1:
     	lpm			;Load byte from program memory into r0.
    	tst		r0	;Check if we've reached the end of the message.
    	breq	finishsendstering	;If so, return.
     	mov		temp,r0
		rcall	sendSerialMorse 	;Send via serial link and as Morse Code.
     	adiw	ZL,1			;Increment Z registers
     	rjmp	srs1
finishsendstering:
		pop		ZH		;Pop Z from stack.
		pop		ZL
	 	rcall	interword
     	ret


sendcodedcode:	;Send Coded Morse Code
		;Shift out morse code from coded character.
		;Enter with code for character in temp.
	
;Description of data format from David Robinson's web page:
;"At this point I was reminded of the N1KDO NHRC-2 repeater controller published 
;in February 97 QST that had Morse ID. Investigation of the assembler listing (1) 
;revealed a simple conversion scheme, where all morse characters are encoded in a 
;single Byte, bitwise, LSB to MSB.; “0” = dit, “1” = dah. The Byte is shifted out 
;to the right, until only a “1” remains. As an example 3 is encoded as binary 00111000,
;which translates to 38 in hexadecimal. "


morecode:
	cpi		temp,0b00000001
	breq	codedcodesent
	clc
	ror		temp
	brcs	senddash		;Send a dash if lsb was a one
	rcall	dot			;Send a dot if lsb was not a one
	rjmp	morecode
senddash:
	rcall	dash
	rjmp	morecode

codedcodesent:				;Finished sending the coded code.
	ret


SendMorseAscii:	;Look up coded Morse code and send, followed by rcall to interchar.
		;Enter with ASCII character in temp. Upper-case, don't process
		;control characters. 
	
	push	ZL
	push	ZH
		
	cpi		temp,$20		;If space character, do interword delay.
	brne	SMA1
	rcall	interword
	rjmp	lookupdone
SMA1:
	cpi		temp,$5B
	brmi	upperacse	
	andi	temp,$5F		;Make upper-case
upperacse:
	cpi		temp,$2A	
	brmi	lookupdone

	
				;Set up pointer into codechart.
	ldi		ZH,high(2*codechart);Load high part of byte address into ZH.
	ldi		ZL,low(2*codechart)	;Load low part of byte address into ZL.

	subi	temp,$2A		;Removed offset from ASCII value in temp.
	add		ZL,temp			;Add the value to the index.
	clr		temp
	adc		ZH,temp
	lpm						;Fetch the value from the table.
	mov		temp,r0
	rcall	sendcodedcode	;Send as Morse Code
	rcall	interchar		;Dealy one interchar time
lookupdone:

	pop		ZH
	pop		ZL
	ret				;Return



dot:		;Send dot, wait one dot time.
	sbr		flagreg,0b00000010	;Set flag to send tone.
	sbi		codeport,pulsebit	;Set on-off key pulse high.
	rcall	dottime
	cbr		flagreg,0b00000010	;Clear flag to send tone.
	cbi		codeport,pulsebit	;Set on-off key pulse low.
	rcall	dottime
	ret


dash:		;Send dash, wait one dot time.
	sbr		flagreg,0b00000010	;Set flag to send tone.
	sbi		codeport,pulsebit	;Set on-off key pulse high.
	rcall	dottime
	rcall	dottime
	rcall	dottime
	cbr		flagreg,0b00000010	;Clear flag to send tone.
	cbi		codeport,pulsebit	;Set on-off key pulse low.
	rcall	dottime
	ret

interchar:	;Wait interchear period with output off -3 dot times
	rcall	dottime
	rcall	dottime
	rcall	dottime
	ret
	
interword:	;Wait interword period with output off-6 dot times
	rcall	dottime
	rcall	dottime
	rcall	dottime
	rcall	dottime
	rcall	dottime
	rcall	dottime
	rcall	dottime
	ret


dottime:			;Delay one dot time.
;reload values for 100 wmp with 6 MHz Crystal:


	push	temp
	push	temp2
	
	mov		temp,dotlength  	; 1
moretime3:	
	clr		delaycount
moretime2:
	mov		temp2,dotlength2
moretime1:
	dec		temp2
	brne	moretime1
	dec		delaycount
	brne	moretime2
	dec		temp
	brne	moretime3	
	pop		temp2
	pop		temp
	ret

   
emitchar:	;Send character contained in temp with two stop bits.
 	sbis	usr,udre  		;wait until the register is cleared.
 	rjmp	emitchar     
 	cbi		ucr,0			;Set 9th bit to zero (second stop bit).
 	sbi		ucr,2			;Set to send 9 data bits.
 	out		udr,temp		;send the character.
 	ret
     
     
recchar:   		;Get incoming character into temp              
	sbis	usr,rxc			;wait for a byte
	rjmp	recchar
	in		temp,udr		;read 
	ret				;go back


sendSerialMorse:	;Send ASCII Character via serial port and via Morse Code.
			;Enter with char in temp.
			
	push	temp
 	rcall	emitchar
 	pop		temp
 	rcall	SendMorseAscii
 	ret			


sendnumberMorseASCII:	;Enter with value to be sent in temp, sends as three digit ascii
 		;via serial port and Morse code.
 	mov		U,temp			;Enter with 8 bit value in U, Exits with numerals in H,T,U
 	clr		H			;(Hundreds, Tens, and Units).
 	clr		T
 anotherh:
 	subi 	U,100			;Find out how many hundreds in U.
 	brcs	hdone
 	inc		H
 	rjmp	anotherh
 hdone:
 	subi	U,-100			;Subtracted one too many -add back.
 anothert:
 	subi	U,10
 	brcs	tdone
 	inc		T
 	rjmp	anotherT
 tdone:
 	subi	U,-10		
 	cpi		H,0		
 	breq	dontsendh
 	subi	H,-48
 	mov		temp,H
	rcall	sendSerialMorse
 	cpi		t,0
 	brne	dontsendh
 	ldi		temp,$30		;If U=0 then don't emit this zero
 	rcall	sendSerialMorse
 dontsendh:	
 	cpi		t,0
 	breq	dontsendt
 	subi	T,-48
 	mov		temp,T
	rcall	sendSerialMorse
 dontsendt:
 	subi	U,-48	
 	mov		temp,U
 	rcall	sendSerialMorse
 	rcall	interword
 	ret


codechart:	;Coded Morse Code look up table. Use ASCII value -$30, so zero = 0, "A" = $11, etc.
;Note: Some ASCII characters are silent, and are coded as 0b00000001
;Also note: BT (pause) is coded for ASCII "<" and SK (end of contanct) is coded for ASCII "*",
;and End of Message is coded for ASCII "+".


;	* (SK)		+ (End of Message)
.db	0b01101000,	0b00101010


;	,(comma)    -          .          /
.db	0b01110011,0b1011110,0b01111010,0b00101001


;	0          1          2          3
.db	0b00111111,0b00111110,0b00111100,0b00111000

;	4          5          6          7
.db	0b00110000,0b00100000,0b00100001,0b00100011
 	
 
;	8          9          :          ;
.db	0b00100111,0b00101111,0b01000111,0b01010101
  	
 	
;	<  (BT)        =          >          ?
.db	0b000110001,0b00000001,0b00000001,0b01001100
 	 	
 ;	@          A          B          C
.db	0b00000001,0b00000110,0b00010001,0b00010101
 
 
 ;	D          E          F          G
.db	0b00001100,0b00000010,0b00010100,0b00001011
 
  ;	H          I          J          K
.db	0b00010000,0b00000100,0b00011110,0b00001101
  
 
  ;	L          M          N          0
.db	0b00010010,0b00000111,0b00000101,0b00001111
  
  
   ;	P          Q          R          S
.db	0b00010110,0b00011011,0b00001010,0b0001000
   
  
    ;	T          U          V          W
.db	0b00000011,0b00001100,0b00011000,0b00001110
   

    ;	X          Y          Z          [
.db	0b00011001,0b00011101,0b00010011,0b00000001
   
    
  	 	
 	
timerservice:	;Service Timer 1
	push	temp	
	in		temp,sreg
	push	temp
	
	
	
	sbrs	flagreg,1
	rjmp	notoggle
	
		;Toggle tone output
	sbi		codeout,codebit		;Enable code output pin.
	mov		temp,flagreg		;Toggle it, using flagreg 0 as memory of last one.
	andi	temp,0b00000001
	inc		temp
	andi	temp,0b00000001
	brne	codehigh
	cbi		codeport,codebit
	andi	flagreg,0b11111110
	rjmp	toggledone
codehigh:
	sbi		codeport,codebit
	ori		flagreg,0b00000001
toggledone:
	pop		temp
	out		sreg,temp
	pop		temp

	reti			;Return from interrupt.
	
notoggle:	;Don't toggle port, but delay to equalize interrupt time toggling and not toggling.
	cbi		codeout,codebit		;Disable code out pin.
	nop
	nop
	nop
	nop
	nop
	nop
	rjmp	toggledone

