*
* EZ-MON-LITE for the 6811
* by Ara Knaian  (ara@mit.edu) and  Randy Sargent (rsargent@media.mit.edu)
*
*********************************************************************
* Control Register Definitions
*

BASE    EQU     $1000

PORTA   EQU     $1000   ; Port A data register
RESV1   EQU     $1001   ; Reserved
PIOC    EQU     $1002   ; Parallel I/O Control register
PORTC   EQU     $1003   ; Port C latched data register
PORTB   EQU     $1004   ; Port B data register
PORTCL  EQU     $1005   ;
DDRC    EQU     $1007   ; Data Direction register for port C
PORTD   EQU     $1008   ; Port D data register
DDRD    EQU     $1009   ; Data Direction register for port D
PORTE   EQU     $100A   ; Port E data register
CFORC   EQU     $100B   ; Timer Compare Force Register
OC1M    EQU     $100C   ; Output Compare 1 Mask register
OC1D    EQU     $100D   ; Output Compare 1 Data register

* Two-Byte Registers (High,Low -- Use Load & Store Double to access)
TCNT    EQU     $100E   ; Timer Count Register
TIC1    EQU     $1010   ; Timer Input Capture register 1
TIC2    EQU     $1012   ; Timer Input Capture register 2
TIC3    EQU     $1014   ; Timer Input Capture register 3
TOC1    EQU     $1016   ; Timer Output Compare register 1
TOC2    EQU     $1018   ; Timer Output Compare register 2
TOC3    EQU     $101A   ; Timer Output Compare register 3
TOC4    EQU     $101C   ; Timer Output Compare register 4
TI4O5   EQU     $101E   ; Timer Input compare 4 or Output compare 5 register

TCTL1   EQU     $1020   ; Timer Control register 1
TCTL2   EQU     $1021   ; Timer Control register 2
TMSK1   EQU     $1022   ; main Timer interrupt Mask register 1
TFLG1   EQU     $1023   ; main Timer interrupt Flag register 1
TMSK2   EQU     $1024   ; misc Timer interrupt Mask register 2
TFLG2   EQU     $1025   ; misc Timer interrupt Flag register 2
PACTL   EQU     $1026   ; Pulse Accumulator Control register
PACNT   EQU     $1027   ; Pulse Accumulator Count register
SPCR    EQU     $1028   ; SPI Control Register
SPSR    EQU     $1029   ; SPI Status Register
SPDR    EQU     $102A   ; SPI Data Register
BAUD    EQU     $102B   ; SCI Baud Rate Control Register
SCCR1   EQU     $102C   ; SCI Control Register 1
SCCR2   EQU     $102D   ; SCI Control Register 2
SCSR    EQU     $102E   ; SCI Status Register
SCDR    EQU     $102F   ; SCI Data Register
ADCTL   EQU     $1030   ; A/D Control/status Register
ADR1    EQU     $1031   ; A/D Result Register 1
ADR2    EQU     $1032   ; A/D Result Register 2
ADR3    EQU     $1033   ; A/D Result Register 3
ADR4    EQU     $1034   ; A/D Result Register 4
BPROT   EQU     $1035   ; Block Protect register
RESV2   EQU     $1036   ; Reserved
RESV3   EQU     $1037   ; Reserved
RESV4   EQU     $1038   ; Reserved
OPTION  EQU     $1039   ; system configuration Options
COPRST  EQU     $103A   ; Arm/Reset COP timer circuitry
PPROG   EQU     $103B   ; EEPROM Programming register
HPRIO   EQU     $103C   ; Highest Priority Interrupt and misc.
INIT    EQU     $103D   ; RAM and I/O Mapping Register
TEST1   EQU     $103E   ; factory Test register
CONFIG  EQU     $103F   ; Configuration Control Register

* Interrupt Vector locations

* This is for 6.270 board (in special mode)
VECTOR_PAGE	 EQU $BF00

* This is for 6811E2 (in normal single-chip mode)
*VECTOR_PAGE	 EQU $FF00

SCIVEC   EQU $D6+VECTOR_PAGE     ; SCI serial system
SPIVEC   EQU $D8+VECTOR_PAGE     ; SPI serial system
PAIVEC   EQU $DA+VECTOR_PAGE     ; Pulse Accumulator Input Edge
PAOVVEC  EQU $DC+VECTOR_PAGE     ; Pulse Accumulator Overflow
TOVEC    EQU $DE+VECTOR_PAGE     ; Timer Overflow
TOC5VEC  EQU $E0+VECTOR_PAGE     ; Timer Output Compare 5
TOC4VEC  EQU $E2+VECTOR_PAGE     ; Timer Output Compare 4
TOC3VEC  EQU $E4+VECTOR_PAGE     ; Timer Output Compare 3
TOC2VEC  EQU $E6+VECTOR_PAGE     ; Timer Output Compare 2
TOC1VEC  EQU $E8+VECTOR_PAGE     ; Timer Output Compare 1
TIC3VEC  EQU $EA+VECTOR_PAGE     ; Timer Input Capture 3
TIC2VEC  EQU $EC+VECTOR_PAGE     ; Timer Input Capture 2
TIC1VEC  EQU $EE+VECTOR_PAGE     ; Timer Input Capture 1
RTIVEC   EQU $F0+VECTOR_PAGE     ; Real Time Interrupt
IRQVEC   EQU $F2+VECTOR_PAGE     ; IRQ External Interrupt
XIRQVEC  EQU $F4+VECTOR_PAGE     ; XIRQ External Interrupt
SWIVEC   EQU $F6+VECTOR_PAGE     ; Software Interrupt
BADOPVEC EQU $F8+VECTOR_PAGE     ; Illegal Opcode Trap Interrupt
NOCOPVEC EQU $FA+VECTOR_PAGE     ; COP Failure (Reset)
CMEVEC   EQU $FC+VECTOR_PAGE     ; COP Clock Monitor Fail (Reset)
RESETVEC EQU $FE+VECTOR_PAGE     ; RESET Interrupt

* Serial port

PORTD_WOM	EQU	$20
BAUD1200	EQU	$B3	; Assumes 8 MHz crystal
BAUD9600	EQU	$B0     ; Assumes 8 MHz crystal
TRENA		EQU	$0C	; Transmit, Receive ENAble
RDRF		EQU	$20	; Receive Data Register Full
TDRE		EQU	$80	; Transmit Data Register Empty

* ASCII characters

SPACE	EQU	32
CR	EQU	13
LF	EQU	10
DEL	EQU	127
BS	EQU	8
BELL	EQU	7

*
*
**********************************************************

**********************************************************
*
* Start of program
*

	org	0		; Start of internal RAM

input_buf:	rmb	130	; Input buffer length
input_buf_end:	rmb	1	; plus one for null termination
tempword1:	rmb	2       ; Temporary word of storage (2 byte value)

half_period:	rmb	2
cycle_count_end:
		rmb	2	; Stores end of cycles
cycle_count_buf:
		rmb	70	

	org	$FF
stack_high:	rmb	1	; High point of stack (grows downward)

*old start of eeprom = f800

	org	$B600		; Start of EEPROM

startup:
	lds	#stack_high     ; Set stack pointer to top of ram
	jsr	init_serial
	jsr	init_analogs

interaction_loop:	
	jsr	gets

* 	;ldaa	#255		;  !!!!!!!!!!!!!!!!!!!!!1
*	staa	$1000		; 
	  
	jsr	execute
	bra	interaction_loop

execute:
	jsr	get_character
	cmpb	#$FF
	beq	execute_done ; Hit null termination, done
  
	cmpb	#0
	blo	error_handler	; if underflow, error
	cmpb	#8
	bhi	error_handler	; if larger than 8, error

	aslb			; Multiply B by 2
	ldy	#command_table  
	aby			; X has command_table + 2*(command#)

	ldy	0,y	        ; get vector for command
	jsr	0,y		; go there
	bra	execute

execute_done:
	rts

**************************************
* 
* error_handler
*
* If there is an error reading input, go here.
* Gives help message, restarts interaction loop
* 
* It is OK to jump to error_handler from
* anywhere -- even inside a subroutine.
* Error_hander sets the stack pointer
* back to the beginning before it restarts
* the interaction loop
*

error_handler:
	lds	#stack_high	; reset stack (sort of like C longjmp)
	jmp	interaction_loop

**************************************
* 
* command_table
*
* This table contains jump points for each command
* There are entries for 'a' through 'z'.  If a command
* hasn't yet been defined, it simply vectors to 'help_command',
* which gives the help message
*
* In order to add your own command, modify one of the following
* lines to point to your new command routine.  You should
* probably add a line to the help information as well
* (see "help_message:")
*
* Be sure not to delete or add lines inadvertently.  The command
* dispatch code simply does an address offset from `command_table'.
* The `command_a' through `command_z' labels are ignored;  if you
* get them out of order
*

command_table:
command_a:	fdb analog_command
command_c:	fdb clear_command
command_e:	fdb  error_handler
command_l:	fdb loop_command
command_o:	fdb timer_output_command
command_r:	fdb read_command
command_s:	fdb set_command
command_w:	fdb write_command
command_z:	fdb sleep_command

loop_command:
	jsr	read_y
	pshx				; Save pointer to rest of line
loop_command_again:  
	pulx				; Point back to rest of line
	pshx
	pshy				; Save Y

	jsr	execute			; execute rest of line

	puly				; Restore Y
	dey				
	bne	loop_command_again	; Keep going?
	puly				; Get rid of stored pointer
	rts

sleep_command:
	jsr	read_y
	pshx
outer_sleep_loop:	
	ldx	#333			; # of inner loops in 1 ms
inner_sleep_loop:	
	dex				; 3 cycles
	bne 	inner_sleep_loop	; 3 cycles
	dey
	bne	outer_sleep_loop
	pulx
	rts

read_command:
	jsr	read_y
	ldab	0,y
	jsr	put_b
	rts

write_command:
	jsr	read_y
	jsr	read_b
	stab	0,y
	
	jsr	return_one
	rts
 
set_command
	jsr	read_y
	jsr	read_b
	orab	0,y
	stab	0,y		; mem = mem | val
	jsr	return_one	
	rts
	
clear_command:
	jsr	read_y
	jsr	read_b
	comb			; 1's complement
	andb	0,y
	stab	0,y		; mem = mem & ~val
	jsr	return_one
	rts
	
analog_command:
	jsr	read_b
	jsr	measure_analog
	jsr	put_b
	jsr	return_one
	rts

timer_output_command:

* First get the port number.  
* For now we ignore this and just output everything to PORTA:6

	jsr	read_b
	
* Next, get number of E clocks per half cycle

	jsr	read_y
	sty	half_period

* Next, store up all the cycle counts
	ldy	#cycle_count_buf
get_cycle_count_loop:
	
	xgdy			; D now has ptr
	jsr	read_y
	cmpy	#$FFFF
	beq	got_all_cycle_counts
	xgdy			; Y has ptr, D has data
	std	0,Y
	iny
	iny
	bra	get_cycle_count_loop

*
* For now this code only deals with OC2,
* which controls bit 6 of PORTA
*
got_all_cycle_counts:
	clr	TCTL1

output_loop:
	ldy	0,X

*	jsr	put_y
*	jsr	put_space

	ldaa	#%01000000	; Toggle on OC2
	eora	TCTL1
	staa	TCTL1		; Swap from toggle pin to leave pin alone

	pshx
	ldx	#$1000
	ldd	TCNT
	addd	half_period	; 5

*
* This is the tightest code I've thought of so far.
* It handles half_period of 27 and higher, which
* gives freqeuecies up to 37 KHz.  I believe this should
* be OK for most remote control devices
*

toggle_again:
wait_toc2:
	cpd	0xff&TCNT,X	; 6+
	bpl	wait_toc2	; 3+
	addd	half_period	; 5
	std	0xff&TOC2,X	; 5
	dey			; 4
	bne	toggle_again	; 3

*
* The following implementation is a fair bit slower,
* which is why it is commented out.
*

* toggle_again:
* 	bset	TFLG1,X #%01000000	; 7 Clear compare
* 	addd	half_period		; 5
* 	std	0xff&TOC2,X		; 5
* 	
* wait_toc2:
* 	brclr	TFLG1,X #%01000000 wait_toc2 ; 7+ Wait until TOC2 has compared
* 	
* 	dey				; 4
* 	bne	toggle_again		; 3

	bclr	PORTA,X #%01000000	; Clear PORTA:6 (OC2)

	pulx
	inx
	inx
ol_20:
	cpx	cycle_count_end
	bne	output_loop

	clr	TCTL1

	pulx			; Restore X
	jsr	return_one
	rts

*************************************
*
* return_one - returns a completion message to the system

return_one: 	pshb
		ldab	#1
		jsr	putchar
		ldab	#1
		jsr	putchar
		ldab	#1
		jsr	putchar
		pulb
		rts
			
**************************************
*
* get_character: gets a tagged character from the buffer
*
*
* Puts a "character" into the B register from the buffer
* the character is #$FF for the end
*

get_character:
	ldab	0,x
	cmpb	#$0FF
	beq	endget
	inx
	inx
	ldab	0,x
	inx
	rts  
endget:
	ldab	#$FF
	inx
	inx
	inx
	rts

**************************************
*
* put_b
*
* Puts 8-bit unsigned value in b out serial
* port (in decimal)
*

put_b:	pshb
	ldab	#0
	jsr	putchar
	jsr	putchar
	pulb
	jsr	putchar
	rts
	
**************************************
*
* read_b
*
* Read 8 bit unsigned number from tagged string
* String pointed to by X
* Output in B
*
* X is modified to point to first char after the last
* digit of the number
* 
* Default base is decimal;  if preceded
* by $, will be in hexadecimal
*
* Jumps to "error_handler" if error parsing
* number

read_b:	inx
	inx
	ldab	0,X
	inx
	rts

**************************************
*
* read_y
*
* Read 16 bit unsigned number from ascii string
* String pointed to by X
* Output in Y
* X is modified to point to first char after the last
* digit of the number
* 
* Default base is decimal;  if preceded
* by $, will be in hexadecimal
*
* Jumps to "error_handler" if error parsing
* number

read_y:
	psha
	pshb
	ldaa	0,X
	cmpa	#$FF
	beq	read_y_almost_done
	inx
	ldaa	0,X
	inx
	ldab	0,X
	inx
ryd:
	xgdy
	pulb
	pula
	rts

read_y_almost_done:
	ldd	#$FFFF
	inx
	inx
	inx
	bra	ryd

		
**************************************
* 
* getchar
*
* Get a character from the serial port.
* Returns character in B

getchar:			
	ldab	SCSR
	andb	#RDRF		; Receive register full?
	beq	getchar		; If not, loop
	ldab	SCDR		; Get received character
	rts

**************************************
*
* putchar
*
* Puts character to serial port.
* Character passed in B

putchar:
	psha			; Save A
putchar_wait:
	ldaa	SCSR
	anda	#TDRE		; Transmit register empty?
	beq	putchar_wait	; If not, loop
	stab	SCDR		; Transmit B
	pula			; Restore A
	rts

**************************************
*
* gets
*
* Gets line from serial port
* Places into "input_buf"
* Allows backspace (8 or 127) PUNTED
* Waits for carriage return (13)
* Kills line on ctrl-x (24) PUNTED

gets:
	pshb			; save B
	ldx	#input_buf
gets_loop:
	jsr	getchar
	stab	0,X
	cmpb	#$FF
	beq	gets_almost_done
	inx
	jsr	getchar
	stab	0,x
	inx
	jsr	getchar
	stab	0,x
	inx
	bra	gets_loop
gets_almost_done:
	ldx	#input_buf
	pulb
	rts

*
* init_serial
*
* Turns on serial port and initializes
* to reasonable values, speed 9600 baud
*
* Unfortunately, given an 8 MHz crystal, we
* can't get accurate divisors for
* 19.2K, 38.4K, 57.6K, 115.2K baud

init_serial:
	psha			; Save A

	ldaa	SPCR            ; Turn off wired-or serial output
	anda	#0xff^PORTD_WOM
	staa	SPCR

        ldaa	#BAUD9600       ; Set baud rate to 9600
	staa	BAUD
	
	ldaa	#TRENA		; Enable transmit and receive
	staa	SCCR2

	pula			; Restore A
	rts

**************************************
*
* measure_analog
*
* Analog channel 0-7 input in B
* Output analog value 0-255 in B
*
* Be sure to turn on analog subsystem
* first with "init_analogs"

measure_analog:
	andb	#15		; Mask off top 4 bits
	stab	ADCTL		; Start conversion

* Note that the completion flag is set only after _4_ conversions
* take place.  You could be more efficient by waiting the exact #
* of clocks required for the first reading to be good.
*
* On the other hand, 4 conversions gives the internal capacitance of
* the A/D time to charge up in case you have an input that's has
* an impedance that's a bit too high.

ma_wait:
	ldab	ADCTL
	andb	#$80		; CCF = conversion complete flag
	beq	ma_wait		; Not yet complete

	ldab	ADR1		; Load result
	rts

**************************************
*
* init_analogs
*
* Turns on analog ports

init_analogs:
	ldaa	OPTION
	oraa	0x80		; ADPU = AD Power Up
	staa	OPTION
	rts


**************************************
*
* Jump Vectors
*
* These are typically located in the
* $FF00 page (unless the chip is running
* in special mode, in which case they
* should be in the $BF00 page)

	org	RESETVEC
	fdb	startup			; reset -> jump to startup




