;-----------------------------------------------------------------------
; 6811 interface for a dual-axis wind sensor
; G. Forrest Cook	October 18, 1994
; Licensed under the Gnu General Public License (GPL)
;
; Chip: 68HC11A1FN 8Mhz 
;
; This code reads two quadrature inputs from spinning wind
; sensors, tallies the counts, and outputs the results in ASCII via
; the serial port.
;
; This program has C preprocessor #defines embedded in it to support
; a standalone EPROM version and a test version that runs under the
; Motorola Buffalo monitor program, you must run the C preprocessor
; on the source to produce code that assembles correctly.
; The following will run the C preprocessor and assemble the code:
; gcc -x c -E wind.asm > windcpp.asm
; as11 windcpp.asm
;
;-----------------------------------------------------------------------
#ifdef BUFFALO
;	Buffalo Interrupt Vector address locations in RAM
VSCI	EQU	$00C4		; SCI
VSPI	EQU	$00C7		; SPI
VPAIE	EQU	$00CA		; pulse accum input edge
VPAO	EQU	$00CD		; pulse accum overflow
VTOF	EQU	$00D0		; timer overflow
VTOC5	EQU	$00D3		; output compare timers
VTOC4	EQU	$00D6
VTOC3	EQU	$00D9
VTOC2	EQU	$00DC
VTOC1	EQU	$00DF
VTIC3	EQU	$00E2		; input compare timers
VTIC2	EQU	$00E5
VTIC1	EQU	$00E8
VRTI	EQU	$00EB		; real time interrupt
VIRQ	EQU	$00EE		; IRQ
VXIRQ	EQU	$00F1		; Ext IRQ
VSWI	EQU	$00F4		; software interrupt
VILLOP	EQU	$00F7		; illegal opcode interrupt
VCOP	EQU	$00FA		; cop timer interrupt
VCLM	EQU	$00FD		; clock monitor interrupt
#else
INTVEC  EQU     $FFC0           ; interrupt vector locations in EPROM
SCIVEC  EQU     $FFD6
SPIVEC  EQU     $FFD8
PAEVEC  EQU     $FFDA
PAOVEC  EQU     $FFDC
TOVVEC  EQU     $FFDE
TO5VEC  EQU     $FFE0
TO4VEC  EQU     $FFE2
TO3VEC  EQU     $FFE4
TO2VEC  EQU     $FFE6
TO1VEC  EQU     $FFE8
TI3VEC  EQU     $FFEA
TI2VEC  EQU     $FFEC
TI1VEC  EQU     $FFEE
RTIVEC  EQU     $FFF0
IRQVEC  EQU     $FFF2
XIRVEC  EQU     $FFF4
SWIVEC  EQU     $FFF6
IOPVEC  EQU     $FFF8
COPVEC  EQU     $FFFA
CLMVEC  EQU     $FFFC
RESVEC  EQU     $FFFE
#endif

;	68HC11A8 Special Control/Status Registers

PORTA	EQU	$1000		; Port A
PIOC	EQU	$1002
PORTC	EQU	$1003		; Port C
PORTB	EQU	$1004		; Port B
PORTCL	EQU	$1005
DDRC	EQU	$1007
PORTD	EQU	$1008		; Port D
DDRD	EQU	$1009
PORTE	EQU	$100A		; Port E
CFORC	EQU	$100B
OC1M	EQU	$100C
OC1D	EQU	$100D
TCNT	EQU	$100E
TIC1	EQU	$1010
TIC2	EQU	$1012
TIC3	EQU	$1014
TOC1	EQU	$1016
TOC2	EQU	$1018
TOC3	EQU	$101A
TOC4	EQU	$101C
TOC5	EQU	$101E
TCTL1	EQU	$1020
TCTL2	EQU	$1021
TMSK1	EQU	$1022
TFLG1	EQU	$1023
TMSK2	EQU	$1024
TFLG2	EQU	$1025
PACTL	EQU	$1026
PACNT	EQU	$1027
SPCR	EQU	$1028
SPSR	EQU	$1029
SPDR	EQU	$102A
BAUD	EQU	$102B
SCCR1	EQU	$102C
SCCR2	EQU	$102D
SCSR	EQU	$102E
SCDR	EQU	$102F
ADCTL	EQU	$1030
ADR1	EQU	$1031
ADR2	EQU	$1032
ADR3	EQU	$1033
ADR4	EQU	$1034
OPTION	EQU	$1039
COPRST	EQU	$103A
PROG	EQU	$103B
HPRIO	EQU	$103C
INIT	EQU	$103D
TEST1	EQU	$103E
CONFIG	EQU	$103F

INTRAM	EQU	$0000		; Internal RAM beginning
INTSTK	EQU	$0080		; Internal Stack (works with Buf vecs)
INTRTP	EQU	$00FF		; Internal RAM end
EEPROM	EQU	$B600		; EEPROM beginning
EXTRAM	EQU	$2000		; External Ram on proto board
EXTRTP	EQU	$3FFF		; Top of External Ram on proto board
EXTROM	EQU	$E000		; External ROM address
BUFALO	EQU	$E000		; Buffalo Monitor Entry Point

JMPOP	EQU	$7E		; 6811 jump opcode for interrupt vectors

CSET	EQU	$C0		; DDR C setup
DSET	EQU	$FE		; DDR D setup
PACSET	EQU	$80		; PACTL initial state
PIOSET	EQU	$86		; PIOC initial state
SPISET	EQU	$50		; SPI initial state
SCISE1	EQU	$00		; SCI setup 1
SCITOF	EQU	$2C		; SCI setup 2, !TIE RIE TE RE
SCITON	EQU	$AC		; SCI setup 2, TIE RIE TE RE
ITM1	EQU	$00		; TMSK1 set for all timers off
ITM2    EQU     $03             ; set timer prescale to /1
ITC1	EQU	$00		; Timer outputs off, PORTA on
T5ON	EQU	$08		; TMSK1 set for timer 5 on

T5CON	EQU	40000		; T5 initial time constant (5 seconds)
T5SVAL	EQU	249		; T5 slow time constant (N-1)

RDRF	EQU	$20		; Receiver Data Register Full bit
TC	EQU	$40		; SCI Transmit Complete bit
TDRE	EQU	$80		; Transmit Data Register Empty bit
SPIF	EQU	$80		; SPI Transmit Complete bit
COP1	EQU	$55		; cop reset sequence #1
COP2	EQU	$AA		; cop reset sequence #2
OPTINI	EQU	$12		; option init, ad off, sysclk, 1/4 sec cop
LO4ADC	EQU	$10		; ADCTL start word for low 4 chans
HI4ADC	EQU	$14		; ADCTL start word for high 4 chans
CCF	EQU	$80		; A/D Conversion Complete Flag

B9600	EQU	$30		; 9600 Baud
B4800	EQU	$31		; 4800 Baud
B2400	EQU	$32		; 2400 Baud
B1200	EQU	$33		; 1200 Baud
B600	EQU	$34		; 600 Baud
B300	EQU	$35		; 300 Baud

; Que data structure offsets
QLEN	EQU	0		; Universal Queue data structure def
QFRONT	EQU	1
QREAR	EQU	2
QEMPTY	EQU	3
QFULL	EQU	4
QDATA	EQU	5
QDSIZ	EQU	6		; size of this data structure

SIQLEN	EQU	128		; length of Serial in Queue
SOQLEN	EQU	128		; length of Serial out Queue

UEMASK	EQU	$01		; U event mask
UDMASK	EQU	$02		; U direction mask
VEMASK	EQU	$04		; U event mask
VDMASK	EQU	$08		; U direction mask

;-----------------------------------------------------------------------
; Variables

	ORG	INTRAM

T5CONS:	RMB	2		; Timer 5 time constant
T5SLOW:	RMB	1		; Slow timer reg.
UPSUM:	RMB	2		; U pulse sum
VPSUM:	RMB	2		; V pulse sum
CHKSUM:	RMB	1		; SIO Output Checksum
ULSUM:	RMB	1		; U LED sum reg
VLSUM:	RMB	1		; V LED sum reg

;-----------------------------------------------------------------------
; main ()
#ifdef BUFFALO
	ORG	EXTRAM
#else
	ORG	EXTROM
#endif

START:
	LDS	#INTSTK		; set up the stack (below int vecs)

#ifdef NOTDEF
	LDAA	#ITM2		; set up timer prescaler (before 64 Ecyc)
	STAA	TMSK2		; Note: buffalo screws this up.
#endif

	LDAA	#ITM1		; mask all timers
	STAA	TMSK1
	LDAA	#ITC1		; set up timer modes
	STAA	TCTL1

	LDAA	#PIOSET		; set up parallel port modes
	STAA	PIOC

	LDAA	#CSET		; set up ports c and d
	STAA	DDRC
	LDAA	#DSET
	STAA	DDRD

#ifdef BUFFALO
	LDAA	#JMPOP		; set up int vect jump ops
	STAA	VSWI
	STAA	VILLOP
	STAA	VCOP
	STAA	VCLM
	STAA	VTOC3
	STAA	VTOC4
	STAA	VTOC5
	STAA	VIRQ
	STAA	VSCI

	LDD	#START		; interrupts which restart the program
	STD	VSWI+1
	STD	VILLOP+1
	STD	VCOP+1
	STD	VCLM+1

	LDD	#NULINT		; null interrupt routine
	STD	VTOC3+1
	STD	VTOC4+1
	LDD	#TOINT5		; set timer 5 vect jump
	STD	VTOC5+1
	LDD	#IRQINT		; set IRQ (switch) vect jump
	STD	VIRQ+1
	LDD	#SCIINT		; set SCI (Serial i/o) vect jump
	STD	VSCI+1

	LDAA	#B9600		; set SCI baud rate
	STAA	BAUD

	LDAA	#OPTINI		; init the option reg (cop speed)
	STAA	OPTION

	LDAA	#PACSET		; set up the pactl
	STAA	PACTL

	LDAA	#SCISE1		; set up SCI port
	STAA	SCCR1
	LDAA	#SCITOF	
	STAA	SCCR2

	LDAA	#SPISET		; set up SPI port
	STAA	SPCR

	LDD	#T5CON		; timer 5 time constant
	STD	T5CONS

	LDAA	#T5SVAL		; init the slow midi timer
	STAA	T5SLOW

	CLR	PORTB
	CLR	PORTC		; clear wind interrupt ff
	LDAA	#$C0
	STA	PORTC

	LDX	#SIQUE		; init the serial in que
	LDAA	#SIQLEN
	JSR	INITQ

	LDX	#SOQUE		; init the serial out que
	LDAA	#SOQLEN
	JSR	INITQ

	LDD	#0
	STD	UPSUM		; zero the initial wind counts
	STD	VPSUM

	CLI			; enable interrupts

	JSR	TIMRGO		; turn on the timer

MAINLP:
	LDAA	COP1		; ping the cop timer
	STAA	COPRST
	LDAA	COP2
	STAA	COPRST

	BRA	MAINLP
;-----------------------------------------------------------------------
;	Start or stop the general timer
TIMRGO:
	LDX	#TMSK1		; Enable timer
	BSET	0,X T5ON

	RTS

TMRGOF:
	LDX	#TMSK1		; Disable timer
	BCLR	0,X T5ON

	RTS
;-----------------------------------------------------------------------
;	T5 interrupt routine	(general purpose timer)
TOINT5:
	LDD	TOC5		; calculate new timer compare value
	ADDD	T5CONS
	STD	TOC5

	LDAA	#T5ON		; clear timer flag bit
	STAA	TFLG1

	LDAA	T5SLOW		; count down the slow timer, act on 0
	BEQ	T5RECY

	DECA			; decrement and store new slow count
	STAA	T5SLOW		; store new slow value

	RTI

T5RECY:
	LDAA	#T5SVAL		; restart slow counter
	STAA	T5SLOW		; store new slow value

	CLR	CHKSUM		; zero the checksum

	LDAA	#'W'
	JSR	SCIENQ

	LDAA	#' '
	JSR	SCIENQ

	LDD	UPSUM		; output U in hex
	JSR	HEXWOT

	LDAA	#' '
	JSR	SCIENQ

	LDD	VPSUM		; output V in hex
	JSR	HEXWOT

	LDAA	#' '
	JSR	SCIENQ

	LDAA	CHKSUM		; output checksum
	NEGA			;2's complement
	ORAA	#$C0		;convert to psuedo ACSII
	JSR	SCIENQ

	LDAA	#$0d
	JSR	SCIENQ

	LDAA	#$0a
	JSR	SCIENQ

	LDD	#0
	STD	UPSUM		; clear the wind counts
	STD	VPSUM

	RTI
;-----------------------------------------------------------------------
;	IRQ pin interrupt routine (wind sensor input)
IRQINT:
	LDAA	PORTC		; get the wind pulse data, save
	TAB

	ANDA	#UEMASK		; look for U events
	BNE	UEVENT

	TBA
	ANDA	#VEMASK		; look for V events
	BNE	VEVENT
	BRA	IRQEX

UEVENT:
	TBA
	ANDA	#UDMASK		; check U direction
	BNE	UEVUP	

	LDD	UPSUM		; decrement U count
	SUBD	CONWRD
	DEC	ULSUM
	BRA	UEVST

UEVUP:
	LDD	UPSUM		; increment U count
	ADDD	CONWRD
	INC	ULSUM
UEVST:
	STD	UPSUM
	BRA	IRQEX

VEVENT:
	TBA
	ANDA	#VDMASK		; check V direction
	BNE	VEVUP	

	LDD	VPSUM		; decrement V count
	SUBD	CONWRD
	DEC	VLSUM
	BRA	VEVST

VEVUP:
	LDD	VPSUM		; increment V count
	ADDD	CONWRD
	INC	VLSUM
VEVST:
	STD	VPSUM
	BRA	IRQEX

IRQEX:
	LDAB	ULSUM		; Set U and V two LED rolling display.
	ANDB	#3
	LDX	#LEDLTB
	ABX
	LDAA	0,X
	LDAB	VLSUM
	ANDB	#3
	LDX	#LEDHTB
	ABX
	ORAA	0,X
	STAA	PORTB

	CLRA			; clear the wind flip flops
	STAA	PORTC
	LDAA	#$C0
	STAA	PORTC
	RTI
;-----------------------------------------------------------------------
;       OUTPUT A 4 hex digit word to the sio que
HEXWOT:
	PSHB			; save low byte
	JSR	HEXBOT		; output high byte
	PULA
	JSR	HEXBOT		; output low byte

	RTS
;-----------------------------------------------------------------------
;       OUTPUT A 2 hex digit byte to the sio que
HEXBOT:
        PSHA                    ; save low nib
        LSRA
        LSRA
        LSRA
        LSRA
        BSR     HEXNOT          ; output the high nibble
        PULA                    ; unsave
        BSR     HEXNOT          ; output the low nibble

        RTS
;-----------------------------------------------------------------------
;	Convert low nibble in A to ASCII hex value in A, output to sio q
HEXNOT:
	ANDA	#$0F		; mask for low nibble

	CMPA	#$09		; check for alpha or numeric
	BGT	OUTATF

	ADDA	#$30		; add numeric offset
	BRA	HNOTEX

OUTATF:
	ADDA	#$37		; add alpha offset
HNOTEX:
	JSR	SCIENQ
	RTS
;-----------------------------------------------------------------------
;	Output a string to the serial port.
;OUTSTR:
;	LDAA	0,X
;	CMPA	#$00
;	BEQ	OSTREX
;
;	JSR	SCIENQ		; loop until end of string
;
;	INX
;
;	BRA	OUTSTR
;
;OSTREX:
;	RTS
;-----------------------------------------------------------------------
;	Deque one byte (A) from the SCI input que, loop until done
SCIDEQ:
	LDX	#SIQUE		; Point to the sci in  que
GETSLP:
	SEI
	JSR	DEQUE
	CLI

	CMPB	#0
	BEQ	GETSLP		; loop until something shows up

	RTS
;----------------------------------------------------------------------
;	Enque one byte (A) onto the SCI output que
SCIENQ:
	PSHX
	LDX	#SOQUE		; point to sci output que

SCIELP
	SEI
	JSR	ENQUE		; keep trying until it fits
	CLI
	BEQ	SCIELP

	ADDA	CHKSUM		; update the checksum
	STAA	CHKSUM

	LDAA	#SCITON		; enable the tx interrupt
	STAA	SCCR2

	PULX
	RTS
;-----------------------------------------------------------------------
; SCI interrupt (serial I/O)
SCIINT:
	LDX	#SCSR
	BRCLR	0,X RDRF SCITXD	; check rx ready bit

	LDAA	SCDR		; get the data

	LDX	#SIQUE		; Stuff input que from the sio port.
	BSR	ENQUE		; if it doesn't fit, just drop it. (ovf flag?)
	RTI

SCITXD:
	BRCLR	0,X TDRE SCIEX	; check tx ready bit
	LDX	#SOQUE		; Deque output que into port

	BSR	DEQUE
	BEQ	SCIOEM		; output que is empty, turn off ints and exit.

	STAA	SCDR		; output the queued byte
SCIEX:
	RTI

SCIOEM:
	LDAA	#SCITOF		; que empty, disable the tx ints
	STAA	SCCR2
	RTI
;-----------------------------------------------------------------------
;	Init the Que pointed to by X and with length of A
INITQ:
	STAA	QLEN,X
	STAA	QEMPTY,X
	DECA
	STAA	QFRONT,X
	STAA	QREAR,X
	CLR	QFULL,X

	RTS
;-----------------------------------------------------------------------
;	Store the data in A in the Queue pointed to by X,
;	success in B and flags
ENQUE:
	PSHX
	PSHA			; save the data
	LDAA	QEMPTY,X	; Check for empty space in Q
	BEQ	ENQFULL
	LDAA	QLEN,X		; Get top index
	DECA
	CMPA	QREAR,X		; if QREAR is at top, Wrap
	BEQ	ENQWRP
	INC	QREAR,X		; else increment QREAR
	BRA	ENQPUT

ENQWRP:
	CLR	QREAR,X		; Wrap QREAR

ENQPUT:
	DEC	QEMPTY,X	; bump the empty and full counts
	INC	QFULL,X

	LDAB	QREAR,X		; Get QREAR
	ABX			; offset X with QREAR
	PULA			; get the data
	STAA	QDATA,X		; offset to actual que and stuff the data
	
	PULX
	LDAB	#1		; return B=1 for success
	RTS

ENQFULL:
	PULA			; align the stack
	PULX
	CLRB			; return B=0 for que full
	RTS
;-----------------------------------------------------------------------
;	Retrieve Queue data, Queue -> X into A
;	success in B and flags
DEQUE:
	LDAA	QFULL,X		; Check QFULL to see if Q has any data 
	BEQ	DEQEMP

	LDAA	QLEN,X		; Get top index
	DECA
	CMPA	QFRONT,X	; if QFRONT is at top, Wrap
	BEQ	DEQWRP
	INC	QFRONT,X	; else increment QFRONT
	BRA	DEQGET

DEQWRP:
	CLR	QFRONT,X	; wrap QFRONT

DEQGET:
	INC	QEMPTY,X	; bump the empty and full counts
	DEC	QFULL,X

	LDAB	QFRONT,X	; Get QFRONT
	ABX			; point X to ->QFRONT
	LDAA	QDATA,X		; get data from Q[FRONT]

	LDAB	#1		; return B=1 for success
	RTS

DEQEMP:
	CLRB			; return B=0 for no data
	RTS
;-----------------------------------------------------------------------
;	Retrieve Queue data, Queue -> X into A, success into B
;	Don't bump the pointers
;QSTAR:
;	LDAA	QFULL,X		; Check QFULL to see if Q has any data 
;	BEQ	QSTREM
;
;	LDAA	QLEN,X		; Get top index
;	DECA
;	CMPA	QFRONT,X	; if QFRONT is at top, Wrap
;	BEQ	QSTWRP
;	LDAB	QFRONT,X
;	INCB
;	BRA	QSTGET
;
;QSTWRP:
;	CLRB			; wrap ptr
;
;QSTGET:
;	ABX			; point X to ->QFRONT
;	LDAA	QDATA,X		; get data from Q[FRONT]
;
;	LDAB	#1		; return B=1 for success
;	RTS
;
;QSTREM:
;	CLRB			; return B=0 for no data
;	RTS
;-----------------------------------------------------------------------
;	Retrieve Queue status, Queue -> X into A
;QSTAT:
;	LDAA	QFULL,X		; Check QFULL to see if Q has any data 
;
;	RTS
;------------------------------------------------------------------------
NULINT:
	RTI
;-----------------------------------------------------------------------
; Constants
MSINIT:	FCC	'UV Wind RS-232 interface '
	FCC	'G. Forrest Cook October 24, 1994'
	FCB	$0d, $0a
	FCB	0
CONWRD:	FDB	1			; add subtract constant word
LEDLTB:	FCB	$01, $02, $04, $08	; low nibble LED output table
LEDHTB:	FCB	$10, $20, $40, $80	; high nibble LED output table
;-----------------------------------------------------------------------
; Queue storage

	ORG	EXTRTP-511

SIQUE:	RMB	SIQLEN+QDSIZ	; Serial input Queue
SOQUE:	RMB	SOQLEN+QDSIZ	; Serial output Queue
;-----------------------------------------------------------------------
; interrupt vector table (EPROM Version), done in code for the BUFFALO version

#ifndef BUFFALO
        ORG     SCIVEC

        FDB     SCIINT, NULINT, NULINT, NULINT, NULINT
        FDB     TOINT5, NULINT, NULINT, NULINT, NULINT
        FDB     NULINT, NULINT, NULINT, NULINT, IRQINT
        FDB     NULINT, START, START, START, START, START
#endif
;-----------------------------------------------------------------------

	END

