	.TITLE	"Precision Radio Clock for Station WWV/H Transmissions"
;
; See readme.txt file in this distribution for copyright notice, setup
; instructions and command summary.
;
; Include files
;
	.NOLIST
#INCLUDE "regs.inc"		; TMS320 registers
#INCLUDE "ports.inc"		; DSP93 ports
#INCLUDE "serial.inc"		; 16550 UART
	.LIST
;
; Program memory map
;
; External	1000-7fff	program
;
; Data memory map
;
; Block B2	0060-007f	interrupt variables and stack
; Block B0	0200-02ff	filter coefficients (mapped to prog mem)
; Block B1	0300-03ff	filter delay lines
; External	0400-7fff	program variables
;
; Auxiliary register usage
;
; AR0	utility compare and temporary register
; AR1	utility pointer
; AR2	utility counter/pointer
; AR3	reserved for interrupt routines
; AR4	reserved for interrupt routines
; AR5	interrupt stack pointer
; AR6	AIO buffer pointer
; AR7	AIO buffer pointer
;
; General purpose status bits (STATUS)
;
SSYNC	.EQU	0		; second sync
MSYNC	.EQU	1		; minute sync
DGATE	.EQU	2		; data gate
BGATE	.EQU	3		; data bit error
INSYNC	.EQU	4		; clock is synchronized
LEPSEC	.EQU	5		; leap second in progress
WWVH	.EQU	6		; WWV (0) WWVH (1) select
ERRSTA	.EQU	8		; error condition (buffer overrun)
LEAPYR	.EQU	9		; leap year flag
DGSYNC	.EQU	10		; digit compare error
POPOFF	.EQU	11		; frequency popoff
;
; Miscellaneous status bits (MISC)
;
; These bits correspond to designated bits in the IRIG timecode. The bit
; probabilities are exponentially averaged over several minutes and
; processed by a integrator and decision circuit.
;
DUT1	.EQU	0		; 56 DUT .1
DUT2	.EQU	1		; 57 DUT .2
DUT4	.EQU	2		; 58 DUT .4
DUTS	.EQU	3		; 50 DUT sign
DST1	.EQU	4		; 55 DST1 DST in progress
DST2	.EQU	5		; 2 DST2 DST change warning
SECWAR	.EQU	6		; 3 leap second warning
;
; Alarm status bits (ALARM)
;
; These bits indicate various alarm conditions, which are decoded to
; form the quality character included in the timecode. There are four
; four-bit nibble fields in the word, each corresponding to a specific
; alarm condition. At the end of each minute, the word is shifted left
; one position and the two least significant bits of each nibble
; cleared. The least significant two bits are set if the associated
; alarm condition is raised during the minute. The least significant bit
; is cleared at the end of each second. The least significant bit of
; some nibbles is indicated by a panel LED; the most significant bits
; are decoded for the quality character.
;
DECERR	.EQU	0		; BCD digit decoding error
SYMERR	.EQU	4		; low symbol probability
MODERR	.EQU	8		; too many data bit errors
SYNERR	.EQU	12		; low 1-s or 1-m sync pulse amplitude
;
; AIO Configuration constants: HPF out, sync, 3-V half scale, sin x/x.
; RA is chosen to get the SCF as close to 288 kHz as possible; RB is
; chosen to get the sample frequency as close to 8000 Hz as possible.
; This results in a nominal A/D sample frequency of 7949 Hz and 3-dB
; bandwidth of about 10-3700 Hz at the A/D input.
;
CFGA	.EQU	CMDA_VAL(17)	; AIO load RA=TA=17 for SCF 294.1 kHz
CFGB	.EQU	CMDB_VAL(37)	; AIO load RB=TB=37 for 7949 Hz
AIO_GAIN .EQU	AIO_GAIND60|AIO_GAIND70 ; AIO gain
CFGC	.EQU	AIO_CONFIG|AIO_SYNC|AIO_GAIN|AIO_INSSINX
;
; Tone frequency definitions. Note the nominal sample frequency of 7949
; Hz must be corrected to 8000 Hz by replicating samples at intervals of
; about 157 samples, which is equivalent to adding about 420 to the low
; order word of the epoch counter for each sample.
;
COMSIZ	.EQU	8000		; Sample clock frequency (Hz)
INCR2	.EQU	420		; LSW 420.47248 sample clock adjust
MS	.EQU	8		; actual samples per millisecond
IN1000	.EQU	10		; 1000 Hz increment, 4.5-deg sin table
IN1200	.EQU	12		; 1200 Hz increment, 4.5-deg sin table
;
; Sizes of arrays
;
OUTSIZ	.EQU	1000		; size of character circular buffers
BUFSIZ	.EQU	50		; size of AIO buffer (6.25 ms)
MEDSIZ	.EQU	5		; size of sync median filter (5 ms)
IGRSIZ	.EQU	170*MS		; size of data matched filter
DATSIZ	.EQU	60		; size of index/data second buffers
BCDSIZ	.EQU	4*10		; size of BCD coefficient table
;
; Odds and ends
;
P1_MSK	.EQU	PTT_R1+FREQ_UP_R1+FREQ_DN_R1 ; RADIO_CTRL port 1 mask
FIRLEN	.EQU	41		; number of FIR coefficients
P15	.EQU	32767		; max positive number
M15	.EQU	-P15		; max negative number
LIMIT	.EQU	10000		; limiter threshold (LED 1)
MAXAVG	.EQU	8-1		; max time constant (8 = 256 s)
AVGINC	.EQU	4000h		; LSW averaging interval increment
PDELAY	.EQU	188		; default propagation delay (23.5 ms)
TCONST	.EQU	4-1		; log2 time constant (about 16 minutes)
;
; Thresholds. These establish the minimum signal level, minimum SNR and
; maximum jitter thresholds which establish the error and false alarm
; rates of the receiver. The values defined here may be on the
; adventurous side in the interest of the highest sensitivity.
;
CTHR	.EQU	10		; 1-s synch compare threshold
STHR	.EQU	1500		; 1-s sync threshold
MTHR	.EQU	500		; 1-m sync threshold
DTHR	.EQU	1000		; data amplitude threshold
NTHR	.EQU	100		; data SNR threshold (dB * 10)
ETHR	.EQU	1000		; digit correlator threshold
TTHR	.EQU	30		; digit SNR threshold (dB * 10)
BTHR	.EQU	30		; max bit error count in second
;
; Nonprinting and special character codes
;
LF	.EQU	0Ah		; ASCII	line feed
CR	.EQU	0Dh		; ASCII carriage return
;
; Interesting block addresses and memory page pointers
;
B_PAGE	.EQU	0h		; base page pointer
U_PAGE	.EQU	8h		; user page pointer
U_ADDR	.EQU	U_PAGE<<7	; user page address
B0P	.EQU	0FF00h		; block B0 program address
B0D	.EQU	0200h		; block B0 data address
B1D	.EQU	0300h		; block B1 data address

;
; Following are variables accessed by either absolute or indirect
; addressing modes.
;
; Program variables in internal memory (block B0) initialized by
; program. Addresses ending in P are valid following a CNFP
; instructuion, which maps block B0 to program memory. Other addresses
; are valid following a CNFD instruction, which maps block B0 to data
; memory.
;
BCDDLD	.EQU	B0D		; matched filter coefficients
BCDDLP	.EQU	BCDDLD+B0P-B0D	; ibid in program memory
DELBPF	.EQU	BCDDLD+BCDSIZ	; bandpass filter delay line
DELLPF	.EQU	DELBPF+FIRLEN+1	; lowpass filter delay line
DELMF	.EQU	DELLPF+FIRLEN+1	; matched filter delay line
B0DEND	.EQU	DELMF+FIRLEN+1	; end of block B0
;
; Program variables in internal memory (block B1) cleared at reset.
;
; The decoding matrix consists of nine row vectors, one for each digit
; of the timecode. Each vector includes (1) the current clock digit, (2)
; an 11-stage shift register used to save the current probabilities for
; each of the ten possible digit values plus a carry value, (3) the most
; recent maximum-likelihood digit decoded and (4) a compare counter. The
; digits are stored from least to most significant order. The timecode
; is formed from the digits of maximum value reading in the opposite
; order: yy ddd hh:mm.
;
; Decoding vector format
;
DECVEC	.EQU	0		; current clock digit
DECTMP	.EQU	DECVEC+1	; temporary for shift
DECINT	.EQU	DECTMP+1	; digit integrator 0-9
DECLST	.EQU	DECINT+10	; (2) last digit decode and count
DECSIZ	.EQU	DECLST+2	; end of vector
;
MNPROB	.EQU	B1D		; minutes (2 digits)
HRPROB	.EQU	MNPROB+(2*DECSIZ) ; hours (2 digits)
DAPROB	.EQU	HRPROB+(2*DECSIZ) ; days (3 digits)
YRPROB	.EQU	DAPROB+(3*DECSIZ) ; years (2 digits)
EDPROB	.EQU	YRPROB+(2*DECSIZ) ; end of decoding table
STACK	.EQU	B1D+100h	; interrupt stack (grows down)
B1DEND	.EQU	STACK		; end of block B1
;
; Program variables in external memory (U_PAGE) cleared at reset. We
; protect one location at the beginning for I/O status.
;
USRBGN	.EQU	U_ADDR+1	; beginning of clear region
;
; The AIO buffer is a circular structure indexed by AR6 and AR7. AR6 is
; used only by interrupt routines. The receive interrupt routine stores
; the sample at AR6 and then increments AR6. The transmit interrupt
; routine loads the sample at AR6, which is the oldest in the circular
; buffer. The main program loops until AR7 is not equal to AR6, then
; loads the sample found at AR7, which is the oldest sample not yet
; processed. The program loads the output at AR7 and then increments
; AR7. Since input and output interrupts are synchronous, no data are
; lost or duplicated.
;
INTBUF	.EQU	U_ADDR+100h	; AIO buffer
;
; Circular buffers and buffer structures are used for character I/O, one
; set each for input (modem and echo to UART) and output (UART to
; modem).
;
; Buffer structure format
; 0	address of first word beyond buffer
; 1	address of first word of buffer
; 2	put pointer
; 4	get pointer
;
INPBCB	.EQU	INTBUF+BUFSIZ	; input buffer structure
OUTBCB	.EQU	INPBCB+4	; output buffer structure
INPBUF	.EQU	OUTBCB+4	; input circular buffer
OUTBUF	.EQU	INPBUF+OUTSIZ	; output circular buffer
;
; Filter structures
;
COMB	.EQU	OUTBUF+OUTSIZ	; 1000/1200-Hz comb filter
IGRTAB	.EQU	COMB+COMSIZ	; matched filter for timecode modulation
PPITAB	.EQU	IGRTAB+(IGRSIZ*2) ; minute buffer for 1-m sync
SECTAB	.EQU	PPITAB+(DATSIZ*2) ; minute buffer for data values
BCDCOD	.EQU	SECTAB+DATSIZ	; matched filter coefficients
USREND	.EQU	BCDCOD+BCDSIZ	; end of clear region
;
; Following are variables accessed by direct addressing mode. These are
; offsets relative the the data page pointer.
;
: Program variable offsets in block B2 (B_PAGE) initialized at startup.
;
TNC_BUF	.EQU	60h		; TNC_OUTPUT port status
PDC_BUF	.EQU	61h		; RADIO_CTRL port status
;
; Program variable offsets in block B2 (B_PAGE) uninitialized.
;
UARTI	.EQU	62h		; UART input buffer
UARTO	.EQU	63h		; UART output buffer
UARTC	.EQU	64h		; UART modem control buffer
UARTL	.EQU	65h		; UART divisor latch
XTMP	.EQU	66h		; temp
;
; Program variable offsets in external memory (U_PAGE) initialized at
; startup.
;
PIO_BUF	.EQU	00h		; RADIO_GAIN port status
;
; Program variable offsets in external memory (U_PAGE) cleared at reset.
;
; Analog signal variables
;
INPSIG	.EQU	01h		; AIO input buffer
F400	.EQU	02h		; 400-Hz bandpass filter
F150	.EQU	03h		; 150-Hz lowpass filter
SYNC	.EQU	04h		; 1-s sync signal
IRIG	.EQU	05h		; (2) 100-Hz I phase integrator
QRIG	.EQU	07h		; (2) 100-Hz Q phase integrator
IGRPTR	.EQU	09h		; 100-Hz integrator pointer
IGRATE	.EQU	0Ah		; (2) 1000/1200-Hz I phase integrator
QGRATE	.EQU	0Ch		; (2) 1000/1200-Hz Q phase integrator
PPIPTR	.EQU	0Eh		; 1000/1200-Hz integrator pointer
;
; Variables used to establish 1-s sync epoch within the second and
; discipline master oscillator frequency
;
SYNPTR	.EQU	10h		; 1-s sync integrator pointer
MAX	.EQU	11h		; 1-s sync max amplitude
TEPOCH	.EQU	12h		; 1-s sync epoch at max amplitude
TREG	.EQU	13h		; (3) 1-s sync epoch median filter
XEPOCH	.EQU	16h		; 1-s sync epoch (for compare)
YEPOCH	.EQU	17h		; 1-s sync epoch
ZEPOCH	.EQU	18h		; 1-s sync saved last interval
SYNCNT	.EQU	19h		; 1-s sync run-length counter
JITCNT	.EQU	1Ah		; 1-s sync run-length counter
AVGCNT	.EQU	1Bh		; averaging interval counter
AVGINT	.EQU	1Ch		; (2) log2 averaging time
PPIMAX	.EQU	1Eh		; 1-m sync max amplitude
PPIPOS	.EQU	1Fh		; 1-m sync epoch at max amplitude
;
; Variables used to establish basic system timing
;
EPOCH	.EQU	20h		; epoch ramp
FUDGE	.EQU	21h		; (2) epoch phase
INCRX	.EQU	23h		; (2) epoch frequency
MILLI	.EQU	25h		; 1000/1200-Hz ramp
CYCLE	.EQU	26h		; (2) 100-Hz ramp
RPHASE	.EQU	28h		; 1-s ramp at receiver
RSEC	.EQU	29h		; receiver seconds counter
;
; Variables used to establish the second, minute and day
;
CDELAY	.EQU	30h		; WWV propagation delay
HDELAY	.EQU	31h		; WWVH propagation delay
TPHASE	.EQU	32h		; 1-s ramp at transmitter
TSEC	.EQU	33h		; second of minute (0-59/60)
MINUTE	.EQU	34h		; minute of day (0-1439)
DAY	.EQU	35h		; day of year (1-365/366)
MINSET	.EQU	36h		; minutes since last set
ONE	.EQU	37h		; the one and only one
;
; Variables used for data bit decoding and threshold correction
;
DPULSE	.EQU	40h		; data bit pulse length
P0	.EQU	41h		; P(0) probability
P1	.EQU	42h		; P(1) probability
P2	.EQU	43h		; P(2) probability
BIT	.EQU	44h		; most recent bit value (P1-P0)
;
; Variables used to estimate signal levels and bit/digit probabilities
;
SECAMP	.EQU	45h		; 1-s sync amplitude (for debug)
MINAMP	.EQU	46h		; 1-m sync amplitude
DATAMP	.EQU	47h		; max signal amplitude
NOIAMP	.EQU	48h		; average noise amplitude
DATSNR	.EQU	49h		; data SNR (dB * 10)
DIGAMP	.EQU	4Ah		; max digit amplitude
DIGSNR	.EQU	4Bh		; digit SNR (dB * 10)
;
; Variables used to establish status and alarm conditions
;
STATUS	.EQU	50h		; status bits
MISC	.EQU	51h		; miscellaneous timecode bits
ALARM	.EQU	52h		; alarm flashers
ERRCNT	.EQU	53h		; data bit error counter
;
; Signal generators for AIO output
;
AIOPTR	.EQU	55h		; AIO output pointer
SECRMP	.EQU	56h		; 1-m ramp (AIO only)
HZ1000	.EQU	57h		; 1000/1200-Hz sinewave (AIO only)
HZ100	.EQU	58h		; 100-Hz sinewave (AIO only)
;
; Command interpreter variables and temporaries
;
UTMP1	.EQU	60h		; user temp 1
UTMP2	.EQU	61h		; user temp 2
UCHAR	.EQU	62h		; character temp
UPTR	.EQU	63h		; pointer temp
UADR	.EQU	64h		; address temp
UCMD	.EQU	65h		; command table pointer
UMOD	.EQU	66h		; saved command table pointer
DEBUG	.EQU	67h		; debug mode
CLKPTR	.EQU	68h		; clock pointer for s command
ACCUM	.EQU	69h		; accumulator for c and h commands
;
; Local temporaries. These are not preserved by any routine.
;
TMP	.EQU	70h		; global temp 1
TMP2	.EQU	71h		; global temp 2
TMP3	.EQU	72h		; global temp 3
TMP4	.EQU	73h		; global temp 4
ITMP	.EQU	74h		; reserved for interrupt routines
;
;  Program transfer vector
;
	.ORG	1000h		; monitor origin
	B       TINT		; timer interrupt
	B       RINT		; AIO receive interrupt
XIN10	B       XINT		; AIO transmit interrupt
	B       IINT		; TRAP instruction interrupt
	B       GO		; program starting address

;
; AIO receive interrupt. This is designed for minimal intrusion on
; variables used by interruptable code. AIO rx interrupt is enabled when
; tx initialization is complete.
;
; Note: This is the only place where AR6 is incremented. Immediately
; after increment, AR6 is checked for overflow and wrapped.
;
; Variables
; AR6		AIO buffer pointer
; DRR		AIO input buffer
;
RINT	LARP	AR5		; save context
	MAR	*-
	SST1	*-
	SST	*-
	SAR	AR0,*,AR6	; AR6 is AIO buffer pointer
	LDPK	B_PAGE
	LAR	AR0,DRR		; read AIO input buffer
	SAR	AR0,*+
	LRLK	AR0,INTBUF+BUFSIZ ; is pointer at buffer end
	CMPR	
	BBZ	RIN10		; branch if no
	LRLK	AR6,INTBUF	; yes. reset pointer
RIN10	LARP	AR5
	CALL	DUMP		; dump status
	LAR	AR0,*+		; restore context
	LST	*+
	LST1	*+
	EINT			; enable interrupts
	RET

;
; AIO transmit interrupt used in normal processing
;
; Variables
; AR6		AIO buffer pointer
; DXR		AIO output buffer
;
XINT	LARP	AR5		; save context
	MAR	*-
	SST1	*-
	SST	*-
	SAR	AR0,*,AR6	; AR6 is AIO buffer pointer
	LDPK	B_PAGE
	LAR	AR0,*,AR5	; write AIO output buffer
	SAR	AR0,DXR
	CALL	DUMP		; dump status
	LAR	AR0,*+		; restore context
	LST	*+
	LST1	*+
	EINT			; enable interrupts
	RET

;
; AIO transmit interrupt routine used only during AIO configuration.
; Note that the ARP and designated AR point to configuration data.
;
; Variables
; DXR		AIO output buffer
;
XINT2	LAC	*+		; this has to be quick and ugly
	SACL	DXR
	EINT
	RET

;
; Trap interrupt (for debug). TRAP instructions can be sprinkled in the
; code to watch critical sections and leave tracks useful for debugging.
;
IINT	DINT			; avoid interrupts here
	LARP	AR5		; save context
	MAR	*-
	SST1	*-
	SST	*-
	SAR	AR0,*
	LDPK	B_PAGE
	CALL	DUMP		; dump status
	LAR	AR0,*+		; restore context
	LST	*+
	LST1	*+
	EINT			; enable interrupts
	RET

;
; UART output interrupt routine. Preserved context includes AC.
;
URTINT	LARP	AR5		; save context
	MAR	*-
	SST1	*-
	SST	*-
	SACL	*-
	SACH	*-
	SAR	AR0,*,AR3
	LDPK	U_PAGE
	LRLK	AR3,INPBCB	; are characters hiding in the buffer
	CALL	GETBUF
	LDPK	B_PAGE
	BZ	URT10		; branch if no
	SACL	UARTO		; yes. output character
	LALK	UART_SEL_RX
	SACL	XTMP
	OUT	XTMP,UART_CTRL
	OUT	UARTO,UART_WRITE
	B	URT11
;
URT10	LALK	UART_SEL_IER	; disable transmit interrrupt
	SACL	XTMP
	OUT	XTMP,UART_CTRL
	ZAC
	SACL	XTMP
	OUT	XTMP,UART_WRITE
URT11	LARP	AR5		; restore context
	LAR	AR0,*+
	ZALH	*+
	ADDS	*+
	LST	*+
	LST1	*+
	EINT			; enable interrupts
	RET

;
; Routine to dump trace data for debug. This is done on every I/O and
; TRAP interrupt. At this point, SS1, SS0 and AR0 are already on the
; stack (AR5) and the data page pointer set to B_PAGE (0). The remaining
; registers and top two words on the internal CPU stack are saved.
;
DUMP	SAR	AR5,XTMP	; save stack pointer
	MAR	*-
	SAR	AR1,*-		; save aux registers AR1-AR7
	SAR	AR2,*-
	SAR	AR3,*-
	SAR	AR4,*-
	SAR	AR5,*-
	SAR	AR6,*-
	SAR	AR7,*-
	POPD	*-		; save calling address
	POPD	*		; save interrupting address
	PSHD	*+
	PSHD	*-
	LAR	AR5,XTMP	; restore stack pointer
	RET

;
; Timer interrupt (not used)
;
TINT	EINT			; enable interrupts
	RET

;
; Initialize processor configuration. These set the options as does the
; CPU upon first coming up; they are here in case the monitor screws up.
;
GO	CNFD			; configure block B0 as data
	SSXM			; set sign extension mode
	SOVM			; set overflow mode
	SPM	1		; set P register output shift for q15
	FORT	0		; set AIO 16-bit mode
	SFSM			; set FSM (1) frame sync mode
	RTXM			; set TXM (0) transmit mode
	LDPK	U_PAGE
	LALK	AIO_RESET+GAIN4+REC_IN_1 ; reset AIO
	SACL	PIO_BUF
	OUT	PIO_BUF,RADIO_GAIN
	CALL    WAIT4
	LALK    LED_202+GAIN4+REC_IN_1 ; disable AIO; LED 2 on (debug)
	SACL    PIO_BUF
	OUT     PIO_BUF,RADIO_GAIN 
	CALL    WAIT4
	LALK    AIO_ENABLE+GAIN4+REC_IN_1 ;enable AIO
	SACL    PIO_BUF        
	OUT     PIO_BUF,RADIO_GAIN
	CALL    WAIT4
	LDPK	B_PAGE
	RPTK	8-1		; flush stack
	POP
	LRLK	AR5,STACK	; initialize stack pointer
	LRLK	AR6,INTBUF	; initialize AIO buffer pointers
	LRLK	AR7,INTBUF
;
; Set up for AIO initialization. Keep this a deep dark secret and don't
; tell anybody the transmit interrupt routines are switched on-the-wing
; for configuration. This is because the AIO rascal has unreal latency
; requirements only during initialization. There seems to be no way for
; an interrupt routine to satisfy these without appearing non-
; transparent for general purpose processing.
;
	DINT			; turn out the lights
	LALK	XINT2
	SACL	XTMP
	LALK	XIN10+1		; clobber transmit vector
	TBLW	XTMP
	LARP	AR7		; copy AIO initialization vector
	RPTK	7-1
	BLKP	INITAB,*+
	LACK	IMR_XINT	; enable AIO tx interrupt only
	SACL	IMR
	ZAC			; flush AIO tx buffer
	SACL	DXR
	EINT			; turn on the lights
	LARP	AR6		; load initialization word
INI10	IDLE			; wait for interrupt and AC
	BNZ	INI10		; branch if more
	DINT			; done. darkness again
	LALK	XINT		; unclobber transmit vector
	SACL	XTMP
	LALK	XIN10+1
	TBLW	XTMP
	LALK	0FF80h		; (B) plant INT1 interrupt for UART
	SACL	XTMP
	LALK	4
	TBLW	XTMP
	LALK	URTINT
	SACL	XTMP
	LALK	4+1
	TBLW	XTMP
	LACK	IMR_XINT+IMR_RINT+IMR_INT1 ; enable interrupts
	SACL	IMR
	LDPK	U_PAGE
	CALL	RESET		; initialize program variables
	EINT			; enable interrupts
;
; Loop looking for a AIO input sample. When found, the RF input
; processing routine is called to filter and limit the input signal.
;
; Note: This is the only place where AR6 is compared to AR7. The CMPR
; insruction represents the atomic interaction in order to determine
; whether data are in the buffer or not; thus, there are no races. Even
; if there were, unread data would get caught on the next cycle through
; the loop.
;
LOOP	LARP	AR7		; AR7 is AIO buffer pointer
	SAR	AR6,TMP		; are there data in the buffer
	LAR	AR0,TMP
	CMPR	
	BBZ	LOP10		; branch if yes
	LARP	AR1		; no. preserve convention
	LALK	LED_202		; LED 2 on
	CALL	LEDON
	CALL	GETCH		; UART input processing
	CALL	URTCTL		; UART control registers
	LALK	LED_202		; LED 2 off
	CALL    LEDOFF
	CALL	LIGHTS		; panel display/control
	B       LOOP
;
; Save the AIO input buffer and call the RF input routines to filter the
; input signal and extract the sync and data signals. Also call the RF
; output routine to generate various test and monitoring signals.
;
LOP10	LAC	*,2,AR1		; load sample in q15 format
	SACL	INPSIG
	CALL	RFOUT		; call RF output routine
LOP17	CALL	RFINP		; call RF input routine

;
; There are three 1-s epoches used by this program, all spanning the
; range 0-7999 sample ticks and all repeating at intervals of exactly
; one second. The receiver epoch begins upon arrival of the 1-s sync
; pulse; while the transmitter epoch begins before it by the propagation
; delay specified by the C or H commands. The third epoch, called simply
; that in the comments, begins at an arbitrary time, depending on when
; this program actually starts up.
;
; This section scans the epoch looking for seconds sync and establishes
; the transmitter and receiver epoch. The seconds sync relative to the
; epoch is determined by the 1-s sync pulse detected in the RF input
; routine. The actual A/D sample frequency is determined by the hardware
; at 7949 samples/s, but this is stuffed to create 8000 samples/s for
; the actual sample clock. The phase and frequency of the sample clock
; are adjusted to match the 1-s sync pulse as broadcast. The receiver
; epoch establishes the modulation maxima, minima and data sample time.
; The transmitter epoch leads the receiver epoch by the propagation
; delay. It establishes the clock time and implements the sometimes
; idiosyncratic conventional clock time and civil calendar. 
;
; Each receiver epoch contains one of three data signals at 100 Hz, a
; 200-ms pulse (P0), 500-ms pulse (P1) or 800-ms pulse (P2), but the
; first 30 ms of every pusle is punched out for the 1-s sync pulse. The
; data are filtered by a 170-ms synchronous matched filter in the RF
; input routine. The noise reference level is the averaged in-phase
; filter output at the end of the first 30-ms segment, while the signal
; reference level is at the end of the first 200-ms segment. The slice
; level is midway between the signal and noise reference levels. The
; length of the data bit pulse is determined from the reciever epoch at
; the first negative-going crossing of the slice level following the
; first 200-ms segment. The probabilities of P0, P1 and P2 are
; determined from the signal crossing time, delayed by one-half the
; matched filter delay.
;
; Most communications radios use a lowpass filter in the audio stages,
; which can do nasty things to the 100-Hz subcarrier phase relative to
; the 1-s sync pulse. Therefore, the 100-Hz signal is disciplined to the
; averaged quadrature-phase filter output at the end of the first 200-ms
; segment.
;
; Epoch 30 ms - bottom fisher. Save the min for later.
;
	LAC	RPHASE		; is epoch 30 ms
	SBLK	30*MS
	BLZ	LOP20		; branch if no
	BGZ	LOP12		; branch if no
	ZALR	IRIG		; average noise amplitude
	ABS
	SUBH	NOIAMP 
	RPTK	TCONST
	SFR
	ADDH	NOIAMP
	SACH	NOIAMP
	B	LOP20
;
; Epoch 200 ms - top fisher. Save the max for later. 
;
LOP12	SBLK	170*MS		; is epoch 200 ms
	BLZ	LOP20		; branch if no
	BGZ	LOP13		; branch if no
	LAC	IRIG		; yes. latch maximum amplitudes
	BGEZ	LOP11
	ZAC
LOP11	SACL	DATAMP		; compute data SNR
	LAC	NOIAMP
	CALL	LOG10
	SACL	DATSNR
	LAC	DATAMP		; latch peak envelope amplitude
	CALL	LOG10
	SUB	DATSNR
	BGEZ	LOP20A
	ZAC
LOP20A	SFL			; 20 * log10()
	SACL	DATSNR
	BIT	STATUS,SSYNC	; is second in synch
	BBZ	LOP13		; branch if no
	LAC	AVGINT		; yes. compute loop gain
	ADDK	10
	SACL	TMP4
	ZALH	QRIG		; adjust subcarrier phase
	ADDS	QRIG+1
	NEG
	RPT	TMP4		; match the gain to the pain
	SFR
	ADDH	CYCLE
	ADDS	CYCLE+1
	SACH	CYCLE
	SACL	CYCLE+1
	LAC	CYCLE		; mod 80
	SUBK	80
	BGEZ	LOP16A
	ADDK	80
LOP16A	SACL	CYCLE
;
; Epoch 200-999 ms - data fisher. Save the zero crossing.
;
LOP13	LAC	DATAMP		; compute slice level
	ADD	NOIAMP
	SFR
	SUB	IRIG		; is transmitter key down
	BLZ	LOP20		; branch if yes
	LAC	DPULSE		; no. has epoch been stored
	BNZ	LOP20		; branch if yes
	LAC	RPHASE		; no. latch epoch
	SACL	DPULSE
LOP20
;
; At the end of the transmitter second, tick the clock and update the
; time of century.
;
	LAC	TPHASE		; advance transmitter epoch
	ADDK	1
	SACL	TPHASE
	BIT	STATUS,WWVH	; determine propagation delay
	BBNZ	LOP44
	LAC	CDELAY		; Ft. Collins
	B	LOP47
;
LOP44	LAC	HDELAY		; Kaui
LOP47	ADD	RPHASE
	SBLK	COMSIZ		; mod 8000
	BGEZ	LOP48		; is this end of transmitter second
	ADLK	COMSIZ
LOP48	BNZ	LOP46		; branch if no
	CALL	TOCK		; yes. process epoch data
	ZAC			; reset for next second
	SACL	TPHASE
LOP46
;
; At the end of the receiver second, process the data bit and update the
; decoding matrix probabilities.
;
	LAC	RPHASE		; advance receiver epoch
	ADDK	1
	SACL	RPHASE
	LAC	EPOCH		; is this end of receiver second
	SUB	YEPOCH
	BNZ	LOP19		; branch if no
	CALL	DATA		; yes. process epoch data
	CALL	VSEC
	ZAC			; reset for next second
	SACL	RPHASE
	SACL	DPULSE
LOP19
;
; Advance 1-ms and 10-ms ramps, and determine if stuffing cycle is
; needed. We don't care about the 1000/1200-Hz phase, but we do care
; about the 100-Hz phase, since that has been fiddled above.
;
	LACK	IN1000		; select ramp increment
	BIT	STATUS,WWVH
	BBZ	LOP18
	LACK	IN1200
LOP18	ADD	MILLI		; advance 1000/1200-Hz ramp
	SUBK	80
	BGEZ	LOP15
	ADDK	80
LOP15	SACL	MILLI
	LACK	1		; advance 100-Hz ramp
	ADD	CYCLE
	SUBK	80
	BGEZ	LOP16
	ADDK	80
LOP16	SACL	CYCLE
	LAC	FUDGE		; is a stuffing cycle needed
	BLZ	LOOP		; branch if no
	BIT	FUDGE+1,15
	BBNZ	LOOP		; branch if no
	SUBK	1		; yes. run through all this again
	SACL	FUDGE
	B	LOP17

;
; This routine is called at the end of the epoch. It determines scan
; phase and adjusts frequency using a frequency-lock loop.
;
; Seconds sync is determined in the RF input routine as the maximum over
; all 8000 samples in the seconds comb filter. To assure accurate and
; reliable time and frequency discipline, this routine performs a great
; deal of heavy-handed data filtering and conditioning.
;
ENDPOC	LAC	TREG+1		; shift new sample in median filter
	SACL	TREG+2
	LAC	TREG
	SACL	TREG+1
	LAC	TEPOCH
	SACL	TREG
;
; Use a three-stage median filter to toss outlyers
;
	LAC	TREG		; 0>1
	SUB	TREG+1
	CALL	MOD8K
	BLZ	EPC52		; branch if no
	LAC	TREG+1		; 1<2
	SUB	TREG+2
	CALL	MOD8K
	BGZ	EPC51		; branch if no 
	LAC	TREG+2		; 2<0
	SUB	TREG
	CALL	MOD8K
	BGZ	EPC50		; branch if no
	LAC	TREG		; 0>1, 1<2, 2<0 => 2
	SUB	TREG+1
	SACL	TMP3
	LAC	TREG+2
	B	EPC55
;
EPC50	LAC	TREG+2		; 0>1, 1<2, 2>0 => 0
	SUB	TREG+1
	SACL	TMP3
	LAC	TREG
	B	EPC55
;
EPC51	LAC	TREG		; 0>1, 1>2, 2<0 => 1
	SUB	TREG+2
	SACL	TMP3
	LAC	TREG+1
	B	EPC55
;
EPC52	LAC	TREG+1		; 1>2
	SUB	TREG+2
	CALL	MOD8K
	BLZ	EPC54		; branch if no 
	LAC	TREG+2		; 2<0
	SUB	TREG
	CALL	MOD8K
	BGZ	EPC53		; branch if no
	LAC	TREG+1		; 0<1, 1>2, 2<0 => 0
	SUB	TREG+2
	SACL	TMP3
	LAC	TREG
	B	EPC55
;
EPC53	LAC	TREG+1		; 0<1, 1>2, 2>0 => 2
	SUB	TREG
	SACL	TMP3
	LAC	TREG+2
	B	EPC55
;
EPC54	LAC	TREG+2		; 0<1, 1<2, 2>0 => 1
	SUB	TREG
	SACL	TMP3
	LAC	TREG+1
	B	EPC55
;
EPC55	SACL	TEPOCH		; update median
;
; The sample is discarded if the median filter span is greater than 1 ms
; or if the 1-s sync pulse amplitude is less than the decision
; threshold. If the difference between the current and the last sample
; is less than 1 ms, the sample is considered valid and the jitter
; counter is reset to zero; otherwise, the sample is ignored and the
; jitter counter is incremented. If the jitter counter exceeds the
; frequency averaging interval, the new sample is considered valid
; anyway and replaces the old one. The compare counter is incremented if
; the current sample is identical to the last one; otherwise, it is
; forced to zero. If the compare counter increments to 10, the receiver
; epoch is reset to the 1-s pulse epoch as broadcast.
;
	LAC	MAX		; is sync above threshold
	SACL	SECAMP
	SBLK	STHR
	BLZ	EPC22		; branch if no
	LAC	TMP3		; yes. is span less than 1 ms
	SUBK	MS
	BLZ	EPC27		; branch if yes
EPC22	ZAC			; no. reset counters
	SACL	SYNCNT
	SACL	JITCNT
	LALK	~(1<<SSYNC)	; dim second sync
	AND	STATUS
	SACL	STATUS
	LALK	0Fh<<SYNERR	; raise sync alarm
	OR	ALARM
	SACL	ALARM
	B	EPC35
;
EPC27	LAC	AVGCNT		; increment averaging interval
	ADDK	1
	SACL	AVGCNT
	LACK	1		; compute interval max
	RPT	AVGINT
	SFL
	SFL
	SFL
	SACL	TMP2
	LAC	TEPOCH		; is jitter threshold exceeded
	SUB	XEPOCH
	ABS
	SUBK	MS
	BLZ	EPC21		; branch if no
	LAC	JITCNT		; yes. increment jitter counter
	ADDK	1
	SACL	JITCNT
	SUB	TMP2		; is it less than averaging interval
	BLZ	EPC30		; branch if yes
EPC21	ZAC			; no. believe the new value
	SACL	JITCNT
	LAC	TEPOCH		; is sync same as last second
	SUB	XEPOCH
	BZ	EPC28		; branch if yes
	LAC	TEPOCH		; no. save new sync and reset
	SACL	XEPOCH
	ZAC
	SACL	SYNCNT
	B	EPC34
;
EPC28	LAC	SYNCNT		; increment compare counter
	SUBK	CTHR
	BZ	EPC24		; branch if high limit
	ADDK	CTHR+1
	SACL	SYNCNT
	B	EPC34
;
; Here the receiver epoch is finally reset. Note that this adjustment
; occurs only at the end of the epoch second, so must result in the
; receiver or transmitter end-of-second routines being called exactly
; once during the epoch.
;
EPC24	LALK	1<<SSYNC	; light seconds sync
	OR	STATUS
	SACL	STATUS
	LAC	TEPOCH		; update epoch
	SACL	YEPOCH
EPC26
;
; This section calculates the frequency error and adjusts the sample
; clock to nominal zero offset. Note that the averaging interval is in
; log2 units minus one for convenience in division by shifting. The
; frequency is updated by averaging in the epoch change over the
; averaging interval divided by the seconds in the interval. The
; interval is doubled if the absolute adjustment is less than 125
; us/interval for four intervals. The first adjustment greater
; than 500 us/interval is ignored; succeeding adjustments are believed.
;
; The averaging interval is set at four times the receiver time
; constant. It is important to note the time constant establishes other
; variables used elsewhere in the receiver, including the averaging
; constants for the 1000/1200-Hz comb filter, sample clock frequency
; loop, noise estimate, as well as the gain of the 100-Hz subcarrier
; phase loop and the bit and digit comparison counter thresholds. Most
; importantly, the time constant directly determines the averaging
; constants for the bit and decimal digit integrators.
;
EPC34	LAC	AVGCNT		; what epoch is this
	SUB	TMP2
	BGZ	EPC35		; branch if late (something was dropped)
	BLZ	EPC30		; branch if early
	LAC	ZEPOCH		; is jitter less than limit
	SUB	TEPOCH
	CALL	MOD8K
	SACL	TMP3
	ABS
	SUBK	MS/2
	BLEZ	EPC36		; branch if yes
	BIT	STATUS,POPOFF	; no. is this the second time
	BBNZ	EPC36		; branch if yes
	LALK	1<<POPOFF	; no. say we was here
	OR	STATUS
	SACL	STATUS
	B	EPC35
;
EPC36	LALK	~(1<<POPOFF)	; say we wasn't here
	AND	STATUS
	SACL	STATUS
	LAC	AVGINT		; compute averaging constant
	ADDK	5
	SACL	TMP4
	ZALH	TMP3		; compute frequency adjustment
	BGEZ	EPC32		; round toward zero
	ORK	8000h
EPC32	RPT	TMP4
	SFR
	ADDH	INCRX		; update frequency
	ADDS	INCRX+1
	SACH	INCRX
	SACL	INCRX+1
	LAC	INCRX		; if frequency greater than +62.5 ppm
	BLZ	EPC37		; branch if no
	BZ	EPC38		; branch if no
	LALK	7FFFh		; yes. clamp at max
	B	EPC38A
;
EPC37	CMPL			; is frequency less than -62.5 ppm
	BZ	EPC38		; branch if no
	LALK	8000h		; yes. clamp at min
EPC38A	SACH	INCRX
	SACL	INCRX+1
EPC38	LAC	TMP3		; is jitter significant
	ABS
	SUBK	1
	BGZ	EPC35		; branch if yes
	LAC	AVGINT		; no. increase averaging interval
	SUBK	MAXAVG
	BZ	EPC35		; branch if upper limit
	ZALH	AVGINT
	ORK	AVGINC
	ADDS	AVGINT+1
	SACH	AVGINT
	SACL	AVGINT+1
EPC35	LAC	TEPOCH		; set up for next time
	SACL	ZEPOCH
	ZAC
	SACL	AVGCNT
EPC30
;
; Dump variables for debug
;
	BIT	DEBUG,3		; debug mode 3
	BBZ	EPC31
	LAC	SECAMP		; 1-s sync amplitude
	CALL	NUMBER
	LACK	' '
	CALL	CHAR
	LAC	TEPOCH		; median filter epoch of 1-s sync
	CALL	NUMBER
	LACK	' '
	CALL	CHAR
	LAC	TEPOCH		; median filter time offset
	SUB	YEPOCH
	CALL	MOD8K
	CALL	NUMBER
	LACK	' '
	CALL	CHAR
	LAC	TEPOCH		; time offset since last freq update
	SUB	ZEPOCH
	CALL	MOD8K
	CALL	NUMBER
	LACK	' '
	CALL	CHAR
	LAC	SYNCNT		; compare counter
	CALL	NUMBER
	LACK	' '
	CALL	CHAR
	LAC	JITCNT		; jitter counter
	CALL	NUMBER
	LACK	' '
	CALL	CHAR
	LAC	AVGCNT		; averaging cycle counter
	CALL	NUMBER
	LACK	CR
	CALL	CHAR
	LACK	LF
	CALL	CHAR
EPC31	RET

;
; This routine is called at the end of the receiver second to calculate
; the probabilities that the previous second contained a zero (P0), one
; (P1) or position indicator (P2) pulse. If not in sync or if the data
; bit is bad, a bit error is declared and the probabilities are forced
; to zero. Otherwise, the data gate is enabled and the probabilities
; are calculated. 
;
DATA	ZAC			; initialize zero probabilities
	SACL	P0
	SACL	P1
	SACL	P2
	LALK	~(1<<DGATE)	; douse the data gate
	AND	STATUS
	SACL	STATUS
	BIT	STATUS,SSYNC
	BBZ	DAT24		; branch if no seconds sync
	BIT	STATUS,MSYNC
	BBZ	DAT24		; branch if no minutes sync
	LAC	DATAMP
	SBLK	DTHR
	BLZ	DAT24		; branch if low data amplitude
	LAC	DATSNR
	SBLK	NTHR
	BLZ	DAT24		; branch if low data SNR
	LALK	1<<DGATE	; okay. light the data gate
	OR	STATUS
	SACL	STATUS
	LAC	DPULSE		; calculate probabilities
	SBLK	200*MS
	BLZ	DAT24		; branch if runt
	SBLK	IGRSIZ/2
	BGEZ	DAT23		; branch if 0, 1 or 2
	LALK	300*MS		; must be 0
	SACL	P0
	B	DAT20
;
DAT24	LAC	ERRCNT		; bit error. bump the error count
	ADDK	1
	SACL	ERRCNT
	SBLK	BTHR		; is count over threshold
	BLZ	DAT20		; branch if no
	LALK	0Fh<<MODERR	; yes. raise modulation alarm
	OR	ALARM
	SACL	ALARM
	B	DAT20
;
DAT23	SBLK	300*MS
	BGEZ	DAT21		; branch if 1 or 2
	ADLK	300*MS		; 0 or 1
	SACL	P1
	NEG
	ADLK	300*MS
	SACL	P0
	B	DAT20
;
DAT21	SBLK	300*MS
	BGEZ	DAT22		; branch if 2
	ADLK	300*MS		; 1 or 2
	SACL	P2
	NEG
	ADLK	300*MS
	SACL	P1
	B	DAT20
;
DAT22	LALK	300*MS		; must be 2
	SACL	P2
DAT20	LAC	P1		; compute P1-P0 difference
	SUB	P0
	SACL	BIT,2
	LALK	SECTAB		; average misc bits
	ADD	RSEC
	SACL	TMP
	LAR	AR1,TMP
	ZALR	BIT
	SUBH	*
	RPTK	TCONST
	SFR
	ADDH	*
	SACH	*
	SACH	TMP4
;
; Dump variables for debug
;
	BIT	DEBUG,4		; debug mode 4
	BBZ	DAT38
	LAC	RSEC		; receiver second
	CALL	NUMBER
	LACK	' '
	CALL	CHAR
	LAC	DATAMP		; data bit max amplitude
	CALL	NUMBER
	LACK	' '
	CALL	CHAR
	LAC	DATSNR		; data bit SNR
	CALL	NUMBER
	LACK	' '
	CALL	CHAR
	LAC	BIT		; data value
	CALL	NUMBER
	LACK	' '
	CALL	CHAR
	LAC	TMP4		; averaged data value
	CALL	NUMBER
	LACK	' '
	CALL	CHAR
	LAC	QRIG		; averaged phase value
	CALL	NUMBER
	LACK	' '
	CALL	CHAR
	LAC	QRIG+1
	CALL	NUMBER
	LACK	CR
	CALL	CHAR
	LACK	LF
	CALL	CHAR
DAT38	RET

;
; This routine is called at the end of the receiver second to determine
; minute synchronization. A 1000/1200-Hz tone burst of 800-ms duration
; is sent during second zero of the minute. This is detected using a
; pair of 800-ms synchronous integrators, one for the I phase and the
; other for the Q phase. The sum of the I and Q squares is processed by
; a 60-stage comb filter to determine the second containing the tone
; burst.
;
; Normally, the minute has 60 seconds numbered 0-59. If the leap warning
; bit is set, the last minute (1439) of 30 June (day 181 or 182 for leap
; years) or 31 December (day 365 or 366 for leap years) is augmented by
; one second numbered 60. This is accomplished by extending the minute
; interval by one second and teaching the state machine to ignore it.
; BTW, stations WWV/WWVH cowardly kill the transmitter carrier for a few
; seconds around the leap to avoid icky details of transmission format
; during the leap.
;
VSEC	BIT	STATUS,LEPSEC	; is leap second in progress
	BBNZ	SEC49		; branch if yes
	MPYK	0		; no. compute integrator energy
	ZAC
	SQRA	IGRATE
	SQRA	QGRATE
	APAC
	SACH	TMP
	LAR	AR1,PPIPTR	; integrate energy
	ZALH	*+
	ADDS	*-
	BIT	STATUS,SSYNC	; is seconds sync lit
	BBZ	SEC37		; branch if no
	ADD	TMP,16-3	; yes. note weight factor 1/8
SEC37	SUB	*,16-3
	SACH	*+
	SACH	TMP3
	SACL	*+
	LAC	TMP3		; is this max
	SUB	PPIMAX
	BLZ	SEC38		; branch if no
	ADD	PPIMAX		; yes. save things
	SACL	PPIMAX
	LAC	RSEC
	SACL	PPIPOS
SEC38	SAR	AR1,PPIPTR
	LRLK	AR0,PPITAB+(DATSIZ*2) ; is this the last stage
	CMPR
	BBZ	SEC49		; branch if no
	LRLK	AR1,PPITAB	; yes. wrap to first stage
	SAR	AR1,PPIPTR
;
; This section is run after all 60 stages of the comb filter have been
; processed. It establishes minute sync and alarm threshold.
;
	LALK	~(1<<MSYNC)	; dim minute sync
	AND	STATUS
	SACL	STATUS
	LAC	PPIMAX		; is the minute integrator okay
	SACL	MINAMP
	SBLK	MTHR
	BGEZ	SEC44		; branch if yes
	LALK	0Fh<<SYNERR	; no. raise sync alarm
	OR	ALARM
	SACL	ALARM
	B	SEC47

SEC44	LALK	1<<MSYNC	; light minute sync
	OR	STATUS
	SACL	STATUS
	LAC	RSEC		; update minute variables
	SUB	PPIPOS
	BGEZ	SEC45
	ADDK	60
SEC45	SACL	RSEC
	SUBK	59
	BGEZ	SEC48
	ADDK	60
SEC48	SACL	TSEC
SEC47	ZAC			; set up for next minute
	SACL	PPIMAX
;
; Get command and arguments for this second. AR1 points to seconds
; integrator vector, AR2 is argument from command table.
;
SEC49	LALK	~1111h		; update second flashers
	AND	ALARM
	SACL	ALARM
	ZAC			; set up for next second
	SACL	IGRATE
	SACL	IGRATE+1
	SACL	QGRATE
	SACL	QGRATE+1
	LAC	RSEC,1		; get next routine address
	ADLK	PROG
	TBLR	TMP2
	ADDK	1		; get argument to AR2
	TBLR	TMP
	LAR	AR2,TMP
	LAC	RSEC		; get decode table address to AR1
	ADLK	SECTAB
	SACL	TMP
	LAR	AR1,TMP
	LAC	TMP2		; call routine
	CALA
	RET

;
; Command segments. One of these is called for each second, as directed
; by the command table.
;
; Advance the second. This is done for all seconds except the last in
; the minute.
;
IDLE	LAC	RSEC		; increment the second
	ADDK	1
	SACL	RSEC
	RET
;
; Save data in coefficient vector. AR2 is coefficient vector pointer.
; Note that all bits of the BCD character have to be above the data gate
; threshold to be valid. Note that the bits in all digits are assumed
; bad until the minutes unit digit is synchronized.
;
COEF2	LRLK	AR1,MNPROB+DECLST+1 ; is minutes digit in sync
	LAC	*
	SUB	AVGINT
	SUBK	2
	BGEZ	COEF		; branch if yes
	LALK	1<<DGSYNC	; no. show digit error
	OR	STATUS
	SACL	STATUS
	B	COEF
;
COEF1	LALK	~(1<<DGSYNC)	; minutes digit. reset digit error
	AND	STATUS
	SACL	STATUS
COEF	BIT	STATUS,DGATE	; is data gate okay
	BBZ	COE21		; branch if no
	BIT	STATUS,DGSYNC	; is previous digit error
	BBZ	COE20		; branch if no
COE21	LALK	1<<BGATE	; yes. BCD bit error
	OR	STATUS
	SACL	STATUS
COE20	LAC	BIT		; save the current sample
	LARP	AR2
	SACL	*,,AR1
	B	IDLE	
;
; Correlate coefficient vector with each valid symbol and save in
; decoding matrix. We use AR1 to copy BCD coefficients to program memory
; and then set AR0 equal to AR1 for later use to stop the coefficient
; correlator. AR2 is decoding matrix pointer. We step through the
; decoding matrix digits correlating each with the coefficients and
; saving the greatest and the next lower for later SNR calculation.
;
DECIM2	LRLK	AR1,BCDCOD	; digits 0-2
	RPTK	(3*4)-1
	BLKP	BCD2,*+
	B	DCM10
;
DECIM3	LRLK	AR1,BCDCOD	; digits 0-3
	RPTK	(4*4)-1
	BLKP	BCD3,*+
	B	DCM10
;
DECIM6	LRLK	AR1,BCDCOD	; digits 0-6
	RPTK	(7*4)-1
	BLKP	BCD6,*+
	B	DCM10
;
DECIM9	LRLK	AR1,BCDCOD	; digits 0-9
	RPTK	(10*4)-1
	BLKP	BCD9,*+
DCM10	SAR	AR1,TMP		; initialize
	LAR	AR0,TMP
	SAR	AR2,TMP4
	LARP	AR2
	ADRK	DECINT
	LARP	AR1
	LRLK	AR1,BCDCOD
	ZAC
	SACL	TMP
	LALK	M15
	SACL	DIGAMP
	SACL	DIGSNR
	CNFP			; map B0 to program space
DCM12	ZAC			; correlate bit vector and weight vector
	MPYK	0
	RPTK	4-1
	MAC	BCDDLP,*+
	APAC			; accumulate final product
	SACH	TMP2
	LARP	AR2		; integrate in decoding matrix
	ZALR	TMP2
	BIT	STATUS,BGATE	; is there a data bit error
	BBZ	DCM13A		; branch if no
	ZAC			; yes. ignore data value
DCM13A	SUBH	*
	RPTK	TCONST
	SFR
	ADDH	*
	SACH	*
	LAC	*+,,AR1
	SUB	DIGAMP		; is it greatest
	BLEZ	DCM13B		; branch if no
	ADD	DIGAMP		; yes. remember things
	SACL	TMP3
	LAC	DIGAMP		; former greatest now next lower
	SACL	DIGSNR
	LAC	TMP3		; latch greatest
	SACL	DIGAMP
	LAC	TMP		; remember digit
	SACL	TMP3
	B	DCM13
;
DCM13B	ADD	DIGAMP		; not greatest. is it greater than lower
	SUB	DIGSNR
	BLEZ	DCM13		; branch if no
	ADD	DIGSNR		; yes. now bext lower
	SACL	DIGSNR
DCM13	LAC	TMP		; increment digit
	ADDK	1
	SACL	TMP
	CMPR
	BBZ	DCM12		; repeat for all codewords
	CNFD			; map B0 to data space
	LALK	~(1<<BGATE)	; clear the data bit error
	AND	STATUS
	SACL	STATUS
;
; At this point we have identified the maximum likelihood digit. The
; next section determines if it passes sanity checks and then compares
; with the current clock digit. The difference between the maximum
; likelihood digit and current clock digit represents the phase error,
; which should remain constant (most of the time) as succeeding digits
; in the same position are decoded. When a sufficient number of digits
; have been accumulated with the same such difference, the decoded data
; can be considered authoritative and used to correct the current clock.
; Great care is taken here to avoid noise from changing a presumed
; correct clock.
;
	LAC	DIGSNR		; compute digit SNR
	CALL	LOG10
	SACL	DIGSNR
	LAC	DIGAMP
	CALL	LOG10
	SUB	DIGSNR
	BGEZ	DCM28D
	ZAC
DCM28D	SFL			; 20 * log10()
	SACL	DIGSNR
	LAR	AR1,TMP4
	LAC	DIGAMP		; is digit okay
	SBLK	ETHR
	BGZ	DCM28B		; branch if yes
	ADRK	DECLST+1	; no. fresh restart
	ZAC
	SACL	*-
	SBRK	DECLST
	B	DCM28C
;
DCM28B	LAC	DIGSNR		; is digit SNR okay
	SBLK	TTHR
	BGZ	DCM28A		; branch if yes
DCM28C	LALK	0Fh<<SYMERR	; no. raise symbol alarm
	OR	ALARM
	SACL	ALARM
	B	DCM28
;
DCM28A	LAC	*		; compute digit difference mod 10
	SUB	TMP3
	BGEZ	DCM27A
	ADDK	10
DCM27A	SACL	TMP
	ADRK	DECLST		; is difference same as last
	SUB	*
	BZ	DCM22		; branch if yes
	LAC	TMP		; no. remember difference
	SACL	*+
	ZAC			; reset compare counter
	SACL	*-
	SBRK	DECLST
	B	DCM28
;
DCM22	MAR	*+		; is compare counter at max
	LAC	*
	SUB	AVGINT
	SUBK	2
	BGEZ	DCM24		; branch if yes
	ADD	AVGINT		; no. increment compare counter
	ADDK	3
	SACL	*-
	SBRK	DECLST
	B	DCM28
;
DCM24	MAR	*-		; reset difference
	ZAC
	SACL	*
	SBRK	DECLST
	LAC	TMP3		; decode is now correct
	SACL	*
DCM28	LAC	*		; is decode correct
	SUB	TMP3
	BZ	DCM21		; branch if yes
	LALK	0Fh<<DECERR	; no. raise decode alarm
	OR	ALARM
	SACL	ALARM
DCM21
;
; Dump variables for debug
;
	BIT	DEBUG,2		; debug mode 2
	BBZ	DCM40
	LAC	*		; clock digit
	CALL	DIGIT
	LACK	' '
	CALL	CHAR
	LAC	TMP3		; maximum likelihood digit
	CALL	DIGIT
	LACK	' '
	CALL	CHAR
	LAR	AR1,TMP4
	ADRK	DECLST
	LAC	*		; difference (mod 10)
	CALL	DIGIT
	LACK	' '
	CALL	CHAR
	LAR	AR1,TMP4
	ADRK	DECLST+1
	LAC	*		; compare counter
	CALL	DIGIT
	LACK	' '
	CALL	CHAR
	CALL	QUAL		; quality character
	CALL	CHAR
	LACK	' '
	CALL	CHAR
	LAC	YEPOCH		; 1-s sync epoch
	CALL	NUMBER
	LACK	' '
	CALL	CHAR
	LAC	SECAMP		; 1-s sync amplitude
	CALL	NUMBER
	LACK	' '
	CALL	CHAR
	LAC	MINAMP		; 1-m sync amplitude
	CALL	NUMBER
	LACK	' '
	CALL	CHAR
	LAC	DATAMP		; signal amplitude
	CALL	NUMBER
	LACK	' '
	CALL	CHAR
	LAC	DATSNR		; signal SNR
	CALL	NUMBER
	LACK	' '
	CALL	CHAR
	LAC	DIGAMP		; digit correlator amplitude
	CALL	NUMBER
	LACK	' '
	CALL	CHAR
	LAC	DIGSNR		; digit SNR
	CALL	NUMBER
	LACK	CR
	CALL	CHAR
	LACK	LF
	CALL	CHAR
DCM40	B	IDLE
;
; Check for leap second and light tattletale (minute 58)
;
LEAP	BIT	MISC,SECWAR	; is a leap second in our future
	BBZ	LEP12		; branch if no
	LAC	MINUTE		; yes. is this last minute of the day
	SBLK	1439
	BNZ	LEP12		; branch if no
	LAC	DAY		; yes. is this designated leap day
	BIT	STATUS,LEAPYR
	BBZ	LEP10
	SUBK	1
LEP10	SUBK	181		; (30 June)
	BZ	LEP11		; branch if yes
	SUBK	184		; (31 December)
	BNZ	LEP12		; branch if no
LEP11	LALK	1<<LEPSEC	; yes. flag for last second
	OR	STATUS
	SACL	STATUS
LEP12	CALL	MSC30		; integrate DUT4
	BIT	DEBUG,5		; debug mode 5
	BBZ	IDLE
	LRLK	AR1,SECTAB+55	; DST1
	LAC	*
	CALL	NUMBER
	LACK	' '
	CALL	CHAR
	LRLK	AR1,SECTAB+2	; DST2
	LAC	*
	CALL	NUMBER
	LACK	' '
	CALL	CHAR
	LRLK	AR1,SECTAB+3	; LW
	LAC	*
	CALL	NUMBER
	LACK	' '
	CALL	CHAR
	LRLK	AR1,SECTAB+50	; DUT+-
	LAC	*
	CALL	NUMBER
	LACK	' '
	CALL	CHAR
	LRLK	AR1,SECTAB+56	; DUT1
	LAC	*
	CALL	NUMBER
	LACK	' '
	CALL	CHAR
	LRLK	AR1,SECTAB+57	; DUT2
	LAC	*
	CALL	NUMBER
	LACK	' '
	CALL	CHAR
	LRLK	AR1,SECTAB+58	; DUT4
	LAC	*
	CALL	NUMBER
	LACK	CR
	CALL	CHAR
	LACK	LF
	CALL	CHAR
	B	IDLE
;
; Integrator for miscellaneous bits. AR2 is bit mask. The bit is set if
; the integrator is above the positivie threshold, reset if below the
; minus threshold. Otherwise, the bit is left as-is and the low-
; probability alarm is raised.
;
MSCBIT	CALL	MSC30		; integrate all except DST1 and DUT4
	B	IDLE
;
MSC20	CALL	MSC30		; integrate DST1
	LRLK	AR2,YRPROB+DECSIZ ; continue in matched filter
	B	DECIM9
;
MSC30	LAC	*		; is bit value above threshold
	ABS
	SBLK	ETHR
	BGZ	MSC46		; branch if yes
	LALK	0Fh<<SYMERR	; no. raise symbol alarm
	OR	ALARM
	SACL	ALARM
	RET
;
MSC46	SAR	AR2,TMP		; fetch integrated bit value
	LAC	*
	BLZ	MSC31		; branch if zero
	LAC	TMP		; one
	OR	MISC
	SACL	MISC
	RET
;
MSC31	LAC	TMP		; zero
	CMPL
	AND	MISC
	SACL	MISC
	RET
;
; End-minute processing. The clock is marked synchronized if there have
; been no errors in the last two minutes.
;
MIN1	BIT	STATUS,LEPSEC	; is leap second in progress
	BBNZ	IDLE		; branch if yes
MIN2	LALK	~(1<<LEPSEC)	; no. kill the leap warning bit
	AND	STATUS
	SACL	STATUS	
	ZALH	MINSET		; bump time since last synchronization
	ADDH	ONE		; (overflow clamp at 22.8 days)
	SACH	MINSET
	LALK	~(0Fh<<MODERR)	; are there any errors
	AND	ALARM
	BNZ	MIN40		; branch if yes
	LALK	1<<INSYNC	; no. declare clock synchronized
	OR	STATUS
	SACL	STATUS
	ZAC
	SACL	MINSET
MIN40	BIT	DEBUG,1		; debug mode 1
	BBZ	MIN25
	CALL	TIME
MIN25	LALK	~8888h
	AND	ALARM		; update minute flashers
	SFL
	SACL	ALARM
	ZAC			; roll the second
	SACL	RSEC
	SACL	ERRCNT
	RET
;
; This routine is called at the end of the transmitter second. It
; implements a state machine that advances the logical clock subject to
; the funny rules that govern the conventional clock and calendar. Note
; that carries from the least significant (minutes) digit are inhibited
; until that digit is set.
;
TOCK	LAC	TSEC		; tick the clock
	ADDK	1
	SACL	TSEC
	SUBK	60		; which second is this
	BLZ	TOK70		; branch if not the last
	BGZ	TOK12		; branch if following a leap
	BIT	STATUS,LEPSEC
	BBNZ	TOK70		; branch if leap
TOK12	LRLK	AR1,MNPROB	; advance minute of the day
	LACK	10		; minute units
	CALL	CARRY
	BIT	STATUS,INSYNC	; is minute synchronized
	BBNZ	TOK10		; branch if yes
	BIT	STATUS,DGSYNC
	BBNZ	TOK60		; branch if no
TOK10	BNZ	TOK11
	LACK	6		; carry minute tens
	CALL	CARRY
	BNZ	TOK11
	LACK	10		; carry hour units
	CALL	CARRY
	BNZ	TOK11
	LACK	3		; carry hour tens
	CALL	CARRY
TOK11	LRLK	AR1,MNPROB	; decode the minute and day
	LALK	600/2
	SACL	TMP
	LAC	*		; minute units
	ADRK	DECSIZ
	LT	*		; minute tens
	ADRK	DECSIZ
	MPYK	10/2
	APAC
	LT	*		; hour units
	ADRK	DECSIZ
	MPYK	60/2
	APAC
	LT	*		; hour tens
	ADRK	DECSIZ
	MPY	TMP
	APAC
	SACL	MINUTE
	LAC	*		; day units
	ADRK	DECSIZ
	LT	*		; day tens
	ADRK	DECSIZ
	MPYK	10/2
	APAC
	LT	*		; day hundreds
	ADRK	DECSIZ
	MPYK	100/2
	APAC
	SACL	DAY
	LALK	~(1<<LEAPYR)	; is this a leap year
	AND	STATUS
	SACL	STATUS
	LAC	*
	ANDK	3		; (safe until 2400 CE)
	BNZ	TOK43		; branch if no
	LALK	1<<LEAPYR	; yes. so declare
	OR	STATUS
	SACL	STATUS
TOK43
;
; Roll the day if this the first minute.
;
	LAC	MINUTE		; is this the first minute of the day
	SBLK	1440
	BNZ	TOK60		; branch if no
	LRLK	AR1,HRPROB
TOK53	LACK	10		; yes. advance hour units 4 to 0
	CALL	CARRY
	SBRK	DECSIZ
	BNZ	TOK53
	ADRK	DECSIZ
	LACK	3		; advance hour tens 2 to 0
	CALL	CARRY
;
; Roll the year if this is the first day.
;
	LRLK	AR1,DAPROB	; is this the first day of the year
	LAC	DAY
	BIT	STATUS,LEAPYR
	BBZ	TOK54
	SUBK	1
TOK54	SBLK	365
	BLZ	TOK58		; branch if no
TOK55	LACK	10		; yes. advance day units 5/6 to 1
	CALL	CARRY
	SBRK	DECSIZ
	LAC	*
	SUBK	1
	BNZ	TOK55
	ADRK	DECSIZ
TOK56	LACK	10		; advance day tens 6 to 0
	CALL	CARRY
	SBRK	DECSIZ
	BNZ	TOK56
	ADRK	DECSIZ
TOK57	LACK	4		; advance day hundreds 3 to 0
	CALL	CARRY
	B	TOK59		; continue to carry the years
;
; Propagate carriers as the days roll on.
;
TOK58	LACK	10		; carry day units
	CALL	CARRY
	BNZ	TOK60
	LACK	10		; carry day tens
	CALL	CARRY
	BNZ	TOK60
	LACK	4		; carry day hundreds
	CALL	CARRY
	BNZ	TOK60
TOK59	LACK	10		; carry year units
	CALL	CARRY
	BNZ	TOK60
	LACK	10		; carry year tens
	CALL	CARRY
;
; No carries here; the world recycles at the century.
;
TOK60	ZAC			; reset for next minute
	SACL	TSEC
TOK70	LAC	TSEC,16-7	; generate ramp signal
	SACL	SECRMP
	RET
;
; This routine rotates the digit vector one position and increments the
; assigned digit. AR1 points to digit vector, AC has radix. CC is set if
; a carry was generated. If things are working properly, the value of
; each digit position will match the maximum likelihood digit
; corresponding to that position.
;
CARRY	SACL	TMP2		; save radix
	ADRK	DECTMP
	SAR	AR1,TMP		; point to radix entry
	ADD	TMP
	SACL	TMP
	LAR	AR0,TMP	
	LARP	AR0
	LAC	*,,AR1		; move radix entry
	SACL	*
CAR00	LARP	AR0
	MAR	*-		; shift up
	LAC	*+
	SACL	*-,,AR1
CAR01	CMPR			; continue for other entries
	BBZ	CAR00
	SBRK	DECTMP
	LAC	*		; increment digit value
	ADDK	1
	SUB	TMP2
	BGEZ	CAR02
	ADD	TMP2
CAR02	SACL	*		; stash where it counts
	ADRK	DECSIZ		; step to next digit vector
	RET			; return with CC set

;
; This tricky devil maps differences mod 8000 to signed offsets
; Map [-8000, -4001 -> [0, 3999], [-4000, 3999] -> [-4000, 3999],
; [4000, 7999] -> [-4000, -1]
;
MOD8K	SBLK	COMSIZ/2	; is it [4000, 7999]
	BLZ	MOD10		; branch if no
	SBLK	COMSIZ/2	; yes. [-4000, -1]
	B	MOD20
;
MOD10	ADLK	COMSIZ		; no. is it [-8000, -4001]
	BGZ	MOD30		; branch if no
	ADLK	COMSIZ		; yes. [0, 3999]
MOD30	SBLK	COMSIZ/2
MOD20	RET

;
; RF input processing
;
; Clip signals with energy above an arbitrary threshold (LIMIT). This
; helps to reduce errors due to noise spikes and static crashes, etc.
; The threshold is chosen rather arbitrary at 10 dB below overload. LED1
; is lit when in limiting condition.
;
RFINP	LAC	INPSIG		; is signal above threshold
	ABS
	SBLK	LIMIT
	BLZ	INP10		; branch if no
	LALK	LED_201		; yes. LED 1 on
	CALL	LEDON
	B	INP11
;
INP10	LALK	LED_201		; signal below threshold. LED 1 off
	CALL	LEDOFF
INP11	LAC	INPSIG		; clip signal at max
	SBLK	LIMIT
	BLZ	INP12
	ZAC
INP12	ADLK	2*LIMIT		; clip signal at min
	BGZ	INP13
	ZAC
INP13	SBLK	LIMIT
	SACL	TMP3
	LAC	SYNPTR		; determine epoch
	SBLK	COMB
	SACL	EPOCH
;
; For the data signal, use a 150-Hz lowpass filter to avoid aliasing.
;
INP29	LRLK	AR1,DELLPF	; AR1->z^-0
	LAC	TMP3		; initialize sum and product registers
	SACL	*
	ADRK	FIRLEN-1	; AR1->z^n
	MPYK	0
	ZAC
	RPTK	FIRLEN-1	; multiply and accumulate (q15)
	MACD	LPF150,*-
	APAC			; include last product
	SACH	F150
;
; Extract the data using a 170-ms matched filter at 100 Hz. We need both
; the I and Q phases in order to correct for the low frequency phase
; delay in some radios.
;
	LAC	CYCLE		; get sin table pointer
	ADLK	SINTAB
	TBLR	HZ100		; multiply signal by sin
	LT	HZ100
	MPY	F150
	PAC
	SACH	TMP,2
	LAR	AR1,IGRPTR	; get circular buffer pointer
	ZALH	IRIG		; add new sample
	ADDS	IRIG+1
	ADD	TMP,16-7	; note normalization 1/128 approximate
	SUB	*,16-7		; subtract old sample
	SACH	IRIG
	SACL	IRIG+1
	LAC	TMP		; save new sample
	SACL	*+
	LAC	CYCLE		; get cos table pointer
	SUBK	20
	BGEZ	INP31
	ADDK	80
INP31	ADLK	SINTAB
	TBLR	TMP
	LT	TMP		; multiply signal by cos
	MPY	F150
	PAC
	SACH	TMP,2
	ZALH	QRIG		; add new sample
	ADDS	QRIG+1
	ADD	TMP,16-7	; note normalization 1/128 approximate
	SUB	*,16-7		; subtract old sample
	SACH	QRIG
	SACL	QRIG+1
	LAC	TMP		; save new sample
	SACL	*+
	LRLK	AR0,IGRTAB+(IGRSIZ*2) ; update delay line pointer
	CMPR
	BBZ	INP30
	LRLK	AR1,IGRTAB
INP30	SAR	AR1,IGRPTR
;
; For the 1-s and 1-m sync signals, use a 400-Hz bandpass filter at
; 1100-Hz to avoid aliasing.
;
	LRLK	AR1,DELBPF	; AR1->z^-0
	LAC	TMP3		; initialize sum and product registers
	SACL	*
	ADRK	FIRLEN-1	; AR1->z^n
	MPYK	0
	ZAC
	RPTK	FIRLEN-1	; multiply and accumulate (q15)
	MACD	BPF400,*-
	APAC			; include last product
	SACH	F400
;
; Extract the 1-m sync signal using a 800-ms noncoherent integrator at
; 1000/1200 Hz.
;
	LAC	MILLI		; get sin table pointer
	ADLK	SINTAB
	TBLR	HZ1000
	LT	F400		; multiply signal by sin
	MPY	HZ1000
	PAC
	SACH	TMP
	ZALH	IGRATE		; add to I channel integrator
	ADDS	IGRATE+1
	ADD	TMP,16-9	; note normalization 1/512 approximate
	SACH	IGRATE
	SACL	IGRATE+1
	LAC	MILLI		; get cos table pointer
	SUBK	20
	BGEZ	INP27
	ADDK	80
INP27	ADLK	SINTAB
	TBLR	TMP
	LT	F400		; multiply signal by cos
	MPY	TMP
	PAC
	SACH	TMP
	ZALH	QGRATE		; add to Q channel integrator
	ADDS	QGRATE+1
	ADD	TMP,16-9	; note normalization 1/512 approximate
	SACH	QGRATE
	SACL	QGRATE+1
;
; Extract the 1-s sync signal using a 5-ms matched filter and 1-s comb
; filter at 1000/1200 Hz. Note that the averaging constant is 1/8 the
; averaging interval, which is a somewhat arbitrary choice. 
;
	LRLK	AR1,DELMF	; AR1->z^-0
	LAC	F400		; initialize sum and product registers
	SACL	*
	ADRK	FIRLEN-1	; AR1->z^n
	MPYK	0
	ZAC
	BIT	STATUS,WWVH	; is this Hawaii
	BNZ	INP21		; branch if yes
	RPTK	FIRLEN-1	; no. WWV multiply and accumulate (q15)
	MACD	MF1000,*-
	B	INP22

INP21	RPTK	FIRLEN-1	; WWVH multiply and accumulate (q15)
	MACD	MF1200,*-
INP22	APAC			; include last product
	LAR	AR1,SYNPTR	; get comb filter pointer
	SUBH	*
	RPT	AVGINT		; right shift for time constant
	SFR
	ADDH	*		; update comb filter
	SACH	*+
	SACH	SYNC
	LAC	SYNC		; is this max signal
	SUB	MAX
	BLEZ	INP25		; branch if no
	ADD	MAX		; yes. save max
	SACL	MAX
	LAC	EPOCH		; save tentative epoch phase
	SACL	TEPOCH
INP25	ZALH	FUDGE		; adjust for sample clock offset (51 Hz)
	ORK	INCR2
	ADDS	FUDGE+1
	SACH	FUDGE
	SACL	FUDGE+1
	LRLK	AR0,COMB+COMSIZ	; is this end of epoch
	CMPR
	BBZ	INP23		; branch if no
	CALL	ENDPOC		; yes. process epoch data
	ZALH	FUDGE		; adjust frequency
	ADDS	FUDGE+1
	ADDH	INCRX
	ADDS	INCRX+1
	SACH	FUDGE
	SACL	FUDGE+1
	ZAC			; reset for next second
	SACL	MAX
	LRLK	AR1,COMB
INP23	SAR	AR1,SYNPTR
	RET

;
; RF output processing
;
; Note: This is the only place where AR7 is incremented. Immediately
; after increment, AR7 is checked for overflow and wrapped.
;
RFOUT	LAR	AR1,AIOPTR	; fetch source signal in q15 format
	LAC	*
	ANDK    0FFFCh
	LARP	AR7		; AR7 is AIO buffer pointer
	SACL	*+		; store output sample in q15 format
	LRLK	AR0,INTBUF+BUFSIZ ; is pointer at buffer end
	CMPR	
	BBZ	OUT13		; branch if no
	LRLK	AR7,INTBUF	; yes. reset pointer
OUT13	LARP	AR1
	RET

;
; This routine returns 10 * log10(AC). It first normalizes the argument
; and counts the shifts, adding 3 dB for each one. Then, it appends the
; low order four bits from a table. Worst-case accuracy is within 0.3
; dB.
;
LOG10	BZ	LOG11		; no nonsense here
	SACL	TMP		; save argument
	ZALH	TMP		; normalize
	LARK	AR1,14
	RPTK	15-1
	NORM	*-
	SAR	AR1,TMP2	; save shift count
	SACH	TMP
	LAC	TMP
	RPTK	10-1		; fetch dB value
	SFR
	ANDK	0Fh
	ADLK	LOGTAB
	TBLR	TMP
	LAC	TMP
	LT	TMP2		; 3 dB for each shift
	MPYK	30/2
	APAC
LOG11	RET

;
; This routine decodes keyboard input and interprets simple commands. It
; consists of a crude, table-driven command decoder and routines to
; select various system parameters, such as station select, baud rate,
; timecode format, debug mode, etc. It operates in polling mode by
; sampling the UART receive-done bit.
;
GETCH	DINT			; bolt the windows
	LDPK	B_PAGE
	LALK	UART_SEL_LSR	; select line status register
	SACL	UARTI
	OUT	UARTI,UART_CTRL
	IN	UARTI,UART_READ
	LALK	UART_LSR_RBF	; is input buffer full
	AND	UARTI
	BNZ	GET14		; branch if yes
	LDPK	U_PAGE		; no. allow fresh air
	EINT
	RET
;
GET14	LALK	UART_SEL_RX	; select input buffer
	SACL	UARTI
	OUT	UARTI,UART_CTRL 
	IN	UARTI,UART_READ	; input character
	LAC	UARTI
	LDPK	U_PAGE
	EINT			; allow fresh air
	ANDK	07Fh		; unparity it
	SACL	UTMP2
	SUBK	060h		; uncase it
	BLZ	GET15
	SUBK	020h
GET15	ADDK	060h
	SACL	UTMP1
GET13	LAC	UCMD		; initialize table pointer
	SACL	UPTR
GET10	LAC	UPTR		; get next routine address
	TBLR	UADR
	ADDK	1		; get argument
	TBLR	UTMP2
	ADDK	1		; get next command character
	TBLR	UCHAR
	ADDK	1
	SACL	UPTR
	LAC	UCHAR		; is this last entry
	BZ	GET11		; branch if yes
	SUB	UTMP1		; no. does character match
	BNZ	GET10		; branch if no
GET11	LAC	UADR		; yes. call routine
	CALA
GET12	RET
;
; Housekeeping staff
;
GETPSH	LAC	UCMD		; save command table pointer
	SACL	UMOD
	LAC	UTMP2
	SACL	UCMD
	RET
;
GETPOP	LAC	UMOD		; restore command table pointer
	SACL	UCMD
	B	GET13		; continue in command decode
;
; Following are command execution segments.
;
; Select WWV and propagation delay c{0-9...}
; Select WWVH and propagation delay h{0-9...}
;
CMD22	LAC	ACCUM		; multiply previous digits by ten
	SFL
	SACL	TMP
	SFL
	SFL
	ADD	TMP
	ADD	UTMP2		; add present digit
	SACL	ACCUM
	RET
;
CMD23	LALK	~(1<<WWVH)	; Ft. Collins
	AND	STATUS
	SACL	STATUS
	LAC	ACCUM
	BZ	GETPOP
	SACL	CDELAY
	ZAC
	SACL	ACCUM
	B	GETPOP
;
CMD24	LALK	1<<WWVH		; Kauai
	OR	STATUS
	SACL	STATUS
	LAC	ACCUM
	BZ	GETPOP
	SACL	HDELAY
	ZAC
	SACL	ACCUM
	B	GETPOP
;
; Select baud rate b{0-9}
;
CMDBD	LAC	UTMP2		; set divisor latch
	LDPK	B_PAGE
	SACL	UARTL
	LDPK	U_PAGE
	RET
;
; Select debug mode d{0-5}
;
CMDBUG	LAC	UTMP2		; is mode no debug
	BZ	CMDB1		; branch if yes
	OR	DEBUG		; no. just OR bits
CMDB1	SACL	DEBUG
	RET
;
; Select DAC source o{0-9}
;
CMDD	LAC	UTMP2		; select monitor point
	SACL	AIOPTR
	LAR	AR1,AIOPTR	; output value
	LAC	*
	CALL	NUMBER
	LACK	CR		; output CR/LF
	CALL	CHAR
	LACK	LF
	CALL	CHAR
	RET
;
; Select PPS mode p{0-2}
;
CMDPPS	LAC	UTMP2		; set modem control
	LDPK	B_PAGE
	SACL	UARTC
	LDPK	U_PAGE
	RET
;
; Set clock s{0-9...}
;
CMDSET	LAR	AR1,CLKPTR	; get current digit pointer
	LAC	UTMP2		; save digit
	SACL	*+
	ZAC			; clear rest of digit data
	RPTK	DECSIZ-1-1
	SACL	*+
	SAR	AR1,CLKPTR	; remember digit pointer
	RET
;
CMDM1	LALK	MNPROB		; initialize digit pointer
	SACL	CLKPTR
	B	GETPOP

;
; These routines format and output the ASCII timecode in either
; Spectracom format or a designer format.
;
; Spectracom format. The CR/LF comes first in order to establish the on-
; time epoch. Note the quality character is a sham.
;
; CR/LFsq yy ddd hh:mm:ss.fff ld
; s	sync indicator ('?' or ' ')
; q	quality character (' ')
; yy	year of century
; ddd	day of year
; hh	hour of day
; mm	minute of hour
; fff	millisecond of second
; l	leap second warning ' ' or 'L'
; d	DST state 'S', 'D', 'I', or 'O'
;
STIME	LACK	CR		; on-time CR char
	CALL	CHAR
	ZAC			; convert to milliseconds with rounding
	ORK	8000h
	ADD	TPHASE,16-3
	SACH	TMP4
	LACK	LF		; LF
	CALL	CHAR
	LACK	'?'		; determine sync char
	BIT	STATUS,INSYNC
	BBZ	TIM43
	LACK	' '
TIM43	CALL	CHAR
	LACK	' '		; dummy quality char
	CALL	CHAR
	CALL	TOY		; yy ddd hh:mm:ss:fff
	LACK	' '		; space
	CALL	CHAR
	CALL	LD		; ld
	RET
;
; Prettytime format
;
; sq a yy ddd hh:mm:ss.fff ld dut ttttt fffff ggggg uuuuuCR/LF
;
; s	sync indicator ('?' or ' ')
; q	quality character (hex 0-F)
; a	transmitter identifier ('C' or 'H')
; yy	year of century
; ddd	day of year
; hh	hour of day
; mm	minute of hour
; fff	millisecond of second
; l	leap second warning ' ' or 'L'
; d	DST state 'S', 'D', 'I', or 'O'
; dut	DUT sign and magnitude in deciseconds
; bbbbb good data bit counter; fffff frequency offset in units of about 1.9 ns/s; ggggg frequency averaging time in seconds; uuuuu interval since last new data update in minutes
;
TIME	LACK	'?'		; determine sync char
	BIT	STATUS,INSYNC
	BBZ	TIM23
	LACK	' '
TIM23	CALL	CHAR		; on-time sync char
	ZAC			; convert to milliseconds with rounding
	ORK	8000h
	ADD	TPHASE,16-3
	SACH	TMP4
	CALL	QUAL		; quality char
	CALL	CHAR
	LACK	' '		; space
	CALL	CHAR
	LACK	'C'		; station select
	BIT	STATUS,WWVH
	BBZ	TIM25
	LACK	'H'
TIM25	CALL	CHAR
	LACK	' '		; space
	CALL	CHAR
	CALL	TOY		; yy ddd hh:mm:ss:fff
	LACK	' '		; space
	CALL	CHAR
	CALL	LD		; ld
	LACK	' '		; space
	CALL	CHAR
	CALL	DUT		; dut
	LACK	' '
	CALL	CHAR
	LAC	ERRCNT		; data bit error counter
	CALL	NUMBER
	LACK	' '
	CALL	CHAR
	LAC	INCRX+1		; frequency offset
	CALL	NUMBER
	LACK	' '
	CALL	CHAR
	LACK	1		; frequency averaging time
	RPT	AVGINT
	SFL
	SFL
	SFL
	CALL	NUMBER
	LACK	' '
	CALL	CHAR
	LAC	MINSET		; interval since last clock update
	CALL	NUMBER
	LACK	CR		; CR/LF
	CALL	CHAR
	LACK	LF
	CALL	CHAR
	RET
;
; Time of century. Note TMP4 contains millisecond timestamp
;
TOY	LRLK	AR1,YRPROB+DECSIZ ; yy
	CALL	DIG1
	LRLK	AR1,YRPROB
	CALL	DIG1
	LACK	' '		; space
	CALL	CHAR
	LRLK	AR1,DAPROB+(2*DECSIZ) ; ddd
	CALL	DIG1
	LRLK	AR1,DAPROB+DECSIZ
	CALL	DIG1
	LRLK	AR1,DAPROB
	CALL	DIG1
	LACK	' '		; space
	CALL	CHAR
	LRLK	AR1,HRPROB+DECSIZ ; hh
	CALL	DIG1
	LRLK	AR1,HRPROB
	CALL	DIG1
	LACK	':'		; :
	CALL	CHAR
	LRLK	AR1,MNPROB+DECSIZ ; mm
	CALL	DIG1
	LRLK	AR1,MNPROB
	CALL	DIG1
	LACK	':'		; :
	CALL	CHAR
	LACK	10		; ss
	SACL	TMP
	LAC	TSEC
	RPTK	16-1
	SUBC	TMP
	SACH	TMP2
	CALL	DIGIT
	LAC	TMP2
	CALL	DIGIT
	LACK	'.'		; .
	CALL	CHAR
	LACK	100		; fff
	SACL	TMP
	LAC	TMP4
	RPTK	16-1
	SUBC	TMP
	SACH	TMP4
	CALL	DIGIT
	LACK	10
	SACL	TMP
	LAC	TMP4
	RPTK	16-1
	SUBC	TMP
	SACH	TMP4
	CALL	DIGIT
	LAC	TMP4
	CALL	DIGIT
	RET
;
; Leap warning and DST code
;
LD	LACK	' '		; leap warning
	BIT	MISC,SECWAR
	BBZ	TIM11
	LACK	'L'
TIM11	CALL	CHAR
	LAC	MISC		; DST code
	RPTK	4-1
	SFR
	ANDK	03h
	ADLK	DSTCOD
	TBLR	TMP
	LAC	TMP
	CALL	CHAR
	RET
;
; DUT code
;
DUT	LACK	'-'		; DUT sign
	BIT	MISC,DUTS
	BBZ	TIM10
	LACK	'+'
TIM10	CALL	CHAR
	LAC	MISC		; DUT
	ANDK	07h
	CALL	DIGIT
	RET
;
; Quality character decode
;
QUAL	ZAC			; assemble bits
	BIT	ALARM,SYNERR+2	; low 1-s or 1-m sync pulse amplitude
	BBZ	TIM30
	ORK	1<<3
TIM30	BIT	ALARM,MODERR+2	; too many data bit errors
	BBZ	TIM31
	ORK	1<<2
TIM31	BIT	ALARM,SYMERR+2	; low symbol probability
	BBZ	TIM32
	ORK	1<<1
TIM32	BIT	ALARM,DECERR+2	; BCD digit decode error
	BBZ	TIM33
	ORK	1<<0
TIM33	ADLK	HEXASC		; convert to hex
	TBLR	TMP
	LAC	TMP
	RET

;
; Following are utility routines to display decimal numbers, digits and
; ASCII characters. 
;
; Output 16-bit signed value in decimal format. AC contains value.
;
NUMBER	SACL	TMP2		; unsign
	BGEZ	NUM11
	NEG
	SACL	TMP2
	LACK	'-'
	B	NUM10
;
NUM11	LACK	' '
NUM10	CALL	CHAR
	LALK	10000		; x10000
	SACL	TMP
	LAC	TMP2
	RPTK	16-1
	SUBC	TMP
	SACH	TMP2
	CALL	DIGIT
	LALK	1000		; x1000
	SACL	TMP
	LAC	TMP2
	RPTK	16-1
	SUBC	TMP
	SACH	TMP2
	CALL	DIGIT
	LACK	100		; x100
	SACL	TMP
	LAC	TMP2
	RPTK	16-1
	SUBC	TMP
	SACH	TMP2
	CALL	DIGIT
	LACK	10		; x10
	SACL	TMP
	LAC	TMP2
	RPTK	16-1
	SUBC	TMP
	SACH	TMP2
	CALL	DIGIT
	LAC	TMP2		; x1
	CALL	DIGIT
	RET

;
; Routine to produce digit/character. AC contains value.
;
DIG1	LAC	*		; output BCD digit
DIGIT	ANDK	0Fh
	ADDK	'0'
CHAR	DINT
	LARP	AR3
	LRLK	AR3,INPBCB	; output ASCII character
	CALL	PUTBUF
	LDPK	B_PAGE
	LALK	UART_SEL_IER	; enable transmit interrrupt
	SACL	XTMP
	OUT	XTMP,UART_CTRL
	LALK	UART_IER_ETBEI
	SACL	XTMP
	OUT	XTMP,UART_WRITE
	LARP	AR1		; allow fresh air
	LDPK	U_PAGE
	EINT
	RET
;
; Load UART control registers
;
; This routine controls the RTS/DTR leads with 1-s/1-m signals and loads
; the divisor latch for the selected buad rate.
;
URTCTL	DINT			; shut the doors
	LDPK	B_PAGE
	LALK	UART_SEL_MCR	; output modem control
	SACL	XTMP
	OUT	XTMP,UART_CTRL
	LALK	UART_MCR_RTS+UART_MCR_DTR ; raise the bits
	SACL	XTMP
	LALK	LED_204		; is flash enabled
	AND	PDC_BUF
	BNZ	URX31		; branch if no
	LAC	UARTC
	BZ	URX31		; branch if no
	CMPL			; yes flash with LED4
	AND	XTMP
	SACL	XTMP
URX31	OUT	XTMP,UART_WRITE
	LAC	UARTL		; is baud rate programmed
	BZ	URX20		; branch if no
	LALK	UART_SEL_LCR	; yes. set LCR DAB
	SACL	XTMP
	OUT	XTMP,UART_CTRL
	IN	XTMP,UART_READ
	LALK	UART_LCR_DLAB
	OR	XTMP
	SACL	XTMP
	OUT	XTMP,UART_WRITE
	LALK	UART_SEL_DL_MSB	; load divisor latch MSB
	SACL	XTMP
	OUT	XTMP,UART_CTRL
	LAC	UARTL,16-8
	SACH	XTMP
	OUT	XTMP,UART_WRITE
	LALK	UART_SEL_DL_LSB	; load divisor latch LSB
	SACL	XTMP
	OUT	XTMP,UART_CTRL
	LAC	UARTL
	SACL	XTMP
	OUT	XTMP,UART_WRITE
	LALK	UART_SEL_LCR	; clear LCR DAB
	SACL	XTMP
	OUT	XTMP,UART_CTRL
	IN	XTMP,UART_READ
	LALK	~UART_LCR_DLAB
	AND	XTMP
	SACL	XTMP
	OUT	XTMP,UART_WRITE
	ZAC			; do it only once
	SACL	UARTL
URX20	LDPK	U_PAGE		; open the doors
	EINT
	RET

;
; Routine to put a word in a circular buffer
;
; On entry, interrupts are disabled, AR3 points to the buffer structure
; and the data word is in AC. Zero words are not stored. ARP must point
; to AR3.
;
PUTBUF	BZ	BUF10		; brook no zeros
	LAR	AR0,*+		; get upper limit
	MAR	*+		; get put pointer
	LAR	AR4,*,AR4
	MAR	*+		; increment put pointer
	CMPR			; is it at upper limit
	LARP	AR3
	BBZ	BUF11		; branch if no
	MAR	*-		; yes. wrap to lower limit
	LAR	AR4,*+
BUF11	SAR	AR4,*,AR4	; save put pointer
	SACL	*,,AR3		; put char in circular buffer
	LAC	*+		; did the buffer overflow
	SUB	*
	BNZ	BUF10		; branch if no
	LALK	1<<ERRSTA	; yes. remember that
	OR	STATUS
	SACL	STATUS
BUF10	RET

;
; Routine to get a word from a circular buffer
;
; On entry, interrupts are disabled and AR3 points to the buffer
; structure. On exit, the data word is in AC or zero if the buffer is
; empty. ARP must point to AR3.
;
GETBUF	LAR	AR0,*+		; get upper limit
	MAR	*+		; get put pointer
	LAC	*+
	SUB	*		; get get pointer
	BZ	BUF20		; branch if equal (buffer empty)
	LAR	AR4,*,AR4	; unequal. get get pointer
	MAR	*+		; increment get pointer
	CMPR			; is it at upper limit
	LARP	AR3
	BBZ	BUF21		; branch if no
	SBRK	2		; yes. wrap to lower limit
	LAR	AR4,*
	ADRK	2
BUF21	SAR	AR4,*,AR4	; save get pointer
	LAC	*,,AR3		; get char from circular buffer
BUF20	RET

;
; Routine to get the number of words remaining in a circular buffer
;
; On entry, AR3 points to the buffer structure. On exit, the AC contains
; the number of words remaining in the buffer. ARP must point to AR3.
;
GETREM	LAC	*+		; compute upper - lower (buffer size)
	SUB	*+
	SACL	ITMP
	LAC	*+		; compute get - put
	SUB	*+
	NEG
	BGZ	REM11		; branch if wrap
	ADD	ITMP		; no wrap. add upper - lower
REM11	RET

;
; Routine to flush buffers
;
; This is used for initialization and switching between transmit and
; receive. It cleans up the buffers and conditions the modem for receive
; or transmit analog loopback. ARP must point to AR1.
;
FLUSH	DINT
	LRLK	AR1,INPBCB	; preset input buffer structure
	LALK	INPBUF+OUTSIZ
	SACL	*+
	LALK	INPBUF
	SACL	*+
	SACL	*+
	SACL	*+
	LRLK	AR1,OUTBCB	; preset output buffer structure
	LALK	OUTBUF+OUTSIZ
	SACL	*+
	LALK	OUTBUF
	SACL	*+
	SACL	*+
	SACL	*+
	EINT
	RET

;
; Subroutine to control panel LEDs
;
; This runs only when nothing else is waiting for processing. It
; controls the status LEDs and radio control lines.
;
LIGHTS	LDPK	B_PAGE		; go where the buffer action is
	LALK	~(LED_205+LED_206+LED_207) ; LEDs 5-7 off
	AND	TNC_BUF
	LDPK	U_PAGE
	BIT	STATUS,SSYNC	; is second in sync
	BBZ	PAN21C		; branch if no
	BIT	STATUS,DGATE	; yes. flash data gate LED5
	BBZ	PAN21A
	ORK	LED_205
PAN21A	BIT	ALARM,SYMERR	; flash symbol alarm LED6
	BBZ	PAN21B
	ORK	LED_206
PAN21B	BIT	ALARM,DECERR	; flash decode alarm LED7
	BBZ	PAN21C
	ORK	LED_207
PAN21C	SACL	TMP3
	ZAC
	SACL	TMP2
	BIT	STATUS,INSYNC	; is clock synchronized
	BBNZ	PAN51C		; branch if yes
	BIT	STATUS,SSYNC	; no. is second in sync
	BBZ	PAN51		; branch if no
PAN51C	LALK	MS*200		; yes. make short blink
	SACL	TMP
	BIT	STATUS,MSYNC	; is minute in sync
	BBZ	PAN51B		; branch if no
	LAC	TSEC
	BNZ	PAN51B		; branch if no
	LALK	MS*800		; yes. make long blink
	SACL	TMP
PAN51B	LAC	TPHASE		; blink LED4
	SUB	TMP
	BGEZ	PAN51
	LALK	LED_204
	OR	TMP2
	SACL	TMP2
PAN51	BIT	STATUS,SSYNC	; is second in sync
	BBZ	PAN52		; branch if no
	LAC	DPULSE		; yes. blink LED3 with modulation
	BNZ	PAN52
	LALK	LED_203
	OR	TMP2
	SACL	TMP2
PAN52	LAC	TMP2
	LDPK	B_PAGE
	SACL	PDC_BUF
	OUT     PDC_BUF,RADIO_CTRL ; LEDs 3-4
	LDPK	U_PAGE
	LAC	TMP3
	LDPK	B_PAGE
	SACL	TNC_BUF
	OUT	TNC_BUF,TNC_OUTPUT ; LEDs 5-7
PAN53	LDPK	U_PAGE
	RET

;
; Routine to turn selected LED on
;
LEDON	OR	PIO_BUF		; set LED bit
	B	LED10

;
; Routine to turn selected LED off
;
LEDOFF	CMPL			; clear LED bit
	AND	PIO_BUF
LED10	SACL	PIO_BUF
	OUT	PIO_BUF,RADIO_GAIN ; (U_PAGE must be 8 for hardware)
	RET
;
; Restore to initial state. Clear everything in data memory and
; preset nonzero values.
;
RESET	LARP	AR1		; (in case called directly)
	CNFD			; map B0 to data space
	ZAC			; initialize things
	LRLK	AR1,B0D		; clear block B0 (0200-02ff)
	RPTK	100h-1
	SACL	*+
	LRLK	AR1,B1D		; clear block B1 (0300-03ff)
	RPTK	100h-1
	SACL	*+
	LRLK	AR1,USRBGN	; clear data memory (0400-)
	LRLK	AR0,USREND
RST10	SACL	*+
	CMPR
	BBZ	RST10
	LACK	1		; constant used for counting
	SACL	ONE
	LALK	IGRTAB		; data matched filter pointer
	SACL	IGRPTR
	LALK	PPITAB		; 1-m comb filter pointer
	SACL	PPIPTR
	LALK	MNPROB		; decoding matrix pointer
	SACL	CLKPTR
	LALK	COMB		; 1-s comb filter pointer
	SACL	SYNPTR
	LALK	ST00		; command state machine
	SACL	UCMD
	LALK	0FFFFh		; flash all errors
	SACL	ALARM
	LALK	U_ADDR+INPSIG	; monitoring point
	SACL	AIOPTR
	LACK	PDELAY		; default propagation delay
	SACL	CDELAY
	SACL	HDELAY
	CALL	FLUSH		; flush input and output buffers
	RET

;
; Wait loops (used only at initialization)
;
WAIT4	LARK	AR2,0FFh
	CALL	WAIT2
	CALL	WAIT2
	RET

WAIT2	LARK	AR2,0FFh
	CALL	WAIT1
	CALL	WAIT1
	RET

WAIT1	LARK	AR2,0FFh
WAIT_A	LARK	AR3,0FFh
WAIT_B	LARP	AR3
	BANZ	WAIT_B		; loop through count
	LARP	AR2
	BANZ	WAIT_A		; loop through count
	RET

	.MSFIRST

;
; Command tables
;
ST00	.WORD	GETPSH,ST30,'B'	; select baud rate
	.WORD	GETPSH,ST70,'C'	; select WWV and set propagation delay
	.WORD	GETPSH,ST20,'D'	; select debug mode
	.WORD	GETPSH,ST80,'H'	; select WWVH and set propagation delay
	.WORD	GETPSH,ST10,'O'	; select DAC source
	.WORD	GETPSH,ST40,'P'	; enable/disable PPS on CTS line
	.WORD	GETPSH,ST90,'S'	; set clock minute/hour/day
	.WORD	GO,0,'R'	; master reset
	.WORD	STIME,0,'T'	; output Spectracom timecode
	.WORD	TIME,0,'U'	; output timecode
	.WORD	GET12,0,0	; default
;
; Enable/disable PPS on CTS line
;
ST40	.WORD	CMDPPS,0,'0'	; PPS off
	.WORD	CMDPPS,UART_MCR_RTS,'1' ; PPS on CTS line
	.WORD	CMDPPS,UART_MCR_DTR,'2' ; PPS on DTR line
	.WORD	GETPOP,0,0	; default
;
; Select baud rate (based on 4.9152 MHz clock)
;
ST30	.WORD	CMDBD,1024,'0'	; 300
	.WORD	CMDBD,512,'1'	; 600
	.WORD	CMDBD,256,'2'	; 1200
	.WORD	CMDBD,128,'3'	; 2400
	.WORD	CMDBD,64,'4'	; 4800
	.WORD	CMDBD,32,'5'	; 9600
	.WORD	CMDBD,16,'6'	; 19200
	.WORD	CMDBD,8,'7'	; 38400
	.WORD	CMDBD,4,'8'	; 76800
	.WORD	CMDBD,2,'9'	; 153600
	.WORD	GETPOP,0,0	; default
;
; Select debug mode
;
ST20	.WORD	CMDBUG,0,'0'	; debug off
	.WORD	CMDBUG,1<<1,'1'	; mode 1 BCD digit update
	.WORD	CMDBUG,1<<2,'2'	; mode 2 timecode/accuracy
	.WORD	CMDBUG,1<<3,'3'	; mode 3 seconds sync
	.WORD	CMDBUG,1<<4,'4'	; mode 4 seconds data
	.WORD	CMDBUG,1<<5,'5'	; mode 5 misc bits
	.WORD	GETPOP,0,0	; default
;
; Select WWV and set propagation delay
;
ST70	.WORD	CMD22,0,'0'	; 0
	.WORD	CMD22,1,'1'	; 1
	.WORD	CMD22,2,'2'	; 2
	.WORD	CMD22,3,'3'	; 3
	.WORD	CMD22,4,'4'	; 4
	.WORD	CMD22,5,'5'	; 5
	.WORD	CMD22,6,'6'	; 6
	.WORD	CMD22,7,'7'	; 7
	.WORD	CMD22,8,'8'	; 8
	.WORD	CMD22,9,'9'	; 9
	.WORD	CMD23,0,0	; end of entry
;
; Select WWVH and set propagation delay
;
ST80	.WORD	CMD22,0,'0'	; 0
	.WORD	CMD22,1,'1'	; 1
	.WORD	CMD22,2,'2'	; 2
	.WORD	CMD22,3,'3'	; 3
	.WORD	CMD22,4,'4'	; 4
	.WORD	CMD22,5,'5'	; 5
	.WORD	CMD22,6,'6'	; 6
	.WORD	CMD22,7,'7'	; 7
	.WORD	CMD22,8,'8'	; 8
	.WORD	CMD22,9,'9'	; 9
	.WORD	CMD24,0,0	; end of entry
;
; Set clock
;
ST90	.WORD	CMDSET,0,'0'	; 0
	.WORD	CMDSET,1,'1'	; 1
	.WORD	CMDSET,2,'2'	; 2
	.WORD	CMDSET,3,'3'	; 3
	.WORD	CMDSET,4,'4'	; 4
	.WORD	CMDSET,5,'5'	; 5
	.WORD	CMDSET,6,'6'	; 6
	.WORD	CMDSET,7,'7'	; 7
	.WORD	CMDSET,8,'8'	; 8
	.WORD	CMDSET,9,'9'	; 9
	.WORD	CMDM1,0,0	; end of entry
;
; Select DAC source
;
ST10	.WORD	CMDD,U_ADDR+INPSIG,'1'	; AIO input signal
	.WORD	CMDD,U_ADDR+F400,'2'	; sync comb filter
	.WORD	CMDD,U_ADDR+F150,'3'	; sync comb filter
	.WORD	CMDD,U_ADDR+SYNC,'4'	; sync comb filter
	.WORD	CMDD,U_ADDR+IRIG,'5'	; matched filter I signal
	.WORD	CMDD,U_ADDR+QRIG,'6'	; matched filter Q signal
	.WORD	CMDD,U_ADDR+TPHASE,'7'	; second ramp
	.WORD	CMDD,U_ADDR+HZ1000,'8'	; 1000/1200 Hz sine
	.WORD	CMDD,U_ADDR+HZ100,'9'	; 100 Hz sine
	.WORD	CMDD,U_ADDR+BIT,'0'	; bit P0-P1 probability
	.WORD	GETPOP,0,0	; default
;
; Initialization table. This is used to configure the AIO at initial
; startup.
;
INITAB	.WORD	3		; 0 load AIO configuration
	.WORD	CFGC		; 1
	.WORD	3		; 2 load TB register
	.WORD	CFGB		; 3
	.WORD	3		; 4 load TA register
	.WORD	CFGA		; 5
	.WORD	0		; 6 terminator
;
; DST decode (DST2 DST1)
;
DSTCOD	.WORD	'S'		; standard time
	.WORD	'I'		; daylight warning
	.WORD	'O'		; standard warning
	.WORD	'D'		; daylight time
;
; Command segment dispatch table indexed by second number. Each entry
; contains routine address and argument.
;
PROG	.WORD	IDLE,0		; 0 punched
	.WORD	IDLE,0		; 1
	.WORD	MSCBIT,1<<DST2	; 2 DST2
	.WORD	MSCBIT,1<<SECWAR ; 3 LW
	.WORD	COEF,BCDDLD	; 4 1 year
	.WORD	COEF,BCDDLD+1	; 5 2
	.WORD	COEF,BCDDLD+2	; 6 4
	.WORD	COEF,BCDDLD+3	; 7 8
	.WORD	DECIM9,YRPROB	; 8
	.WORD	IDLE,0		; 9 P1
	.WORD	COEF1,BCDDLD	; 10 1 minutes
	.WORD	COEF,BCDDLD+1	; 11 2
	.WORD	COEF,BCDDLD+2	; 12 4
	.WORD	COEF,BCDDLD+3	; 13 8
	.WORD	DECIM9,MNPROB	; 14
	.WORD	COEF2,BCDDLD	; 15 10
	.WORD	COEF,BCDDLD+1	; 16 20
	.WORD	COEF,BCDDLD+2	; 17 40
	.WORD	COEF,BCDDLD+3	; 18
	.WORD	DECIM6,MNPROB+DECSIZ ; 19 P2
	.WORD	COEF,BCDDLD	; 20 1 hours
	.WORD	COEF,BCDDLD+1	; 21 2
	.WORD	COEF,BCDDLD+2	; 22 4
	.WORD	COEF,BCDDLD+3	; 23 8
	.WORD	DECIM9,HRPROB	; 24
	.WORD	COEF,BCDDLD	; 25 10
	.WORD	COEF,BCDDLD+1	; 26 20
	.WORD	COEF,BCDDLD+2	; 27
	.WORD	COEF,BCDDLD+3	; 28
	.WORD	DECIM2,HRPROB+DECSIZ ; 29 P3
	.WORD	COEF,BCDDLD	; 30 1 days
	.WORD	COEF,BCDDLD+1	; 31 2
	.WORD	COEF,BCDDLD+2	; 32 4
	.WORD	COEF,BCDDLD+3	; 33 8
	.WORD	DECIM9,DAPROB	; 34
	.WORD	COEF,BCDDLD	; 35 10
	.WORD	COEF,BCDDLD+1	; 36 20
	.WORD	COEF,BCDDLD+2	; 37 40
	.WORD	COEF,BCDDLD+3	; 38 80
	.WORD	DECIM9,DAPROB+DECSIZ ; 39 P4
	.WORD	COEF,BCDDLD	; 40 100
	.WORD	COEF,BCDDLD+1	; 41 200
	.WORD	COEF,BCDDLD+2	; 42
	.WORD	COEF,BCDDLD+3	; 43
	.WORD	DECIM3,DAPROB+(2*DECSIZ) ; 44
	.WORD	IDLE,0		; 45
	.WORD	IDLE,0		; 46
	.WORD	IDLE,0		; 47
	.WORD	IDLE,0		; 48
	.WORD	IDLE,0		; 49 P5
	.WORD	MSCBIT,1<<DUTS	; 50 DUT+-
	.WORD	COEF,BCDDLD	; 51 10 year
	.WORD	COEF,BCDDLD+1	; 52 20
	.WORD	COEF,BCDDLD+2	; 53 40
	.WORD	COEF,BCDDLD+3	; 54 80
	.WORD	MSC20,1<<DST1	; 55 DST1
	.WORD	MSCBIT,1<<DUT1	; 56 0.1 DUT
	.WORD	MSCBIT,1<<DUT2	; 57 0.2
	.WORD	LEAP,1<<DUT4	; 58 0.4
	.WORD	MIN1,0		; 59 P6
	.WORD	MIN2,0		; 60 leap second
;
; Table of logs log10 times ten
;
LOGTAB	.WORD	00		; 0 not valid
	.WORD	00		; 1 0.000000
	.WORD	03		; 2 0.263289
	.WORD	05		; 3 0.511525
	.WORD	10		; 4 0.969100
	.WORD	12		; 5 1.180993
	.WORD	14		; 6 1.383027
	.WORD	16		; 7 1.576079
	.WORD	18		; 8 1.760913
	.WORD	19		; 9 1.938200
	.WORD	21		; A 2.108534
	.WORD	23		; B 2.272438
	.WORD	24		; C 2.430380
	.WORD	26		; D 2.582780
	.WORD	27		; E 2.730013
	.WORD	29		; F 2.872417
;
; BCD coefficients for maximum likelihood decode
;
; Digits 0-9
;
M9	.EQU	P15/4		; mark (+1)
S9	.EQU	M15/4		; space (-1)
;
BCD9	.WORD	S9,S9,S9,S9	; 0
	.WORD	M9,S9,S9,S9	; 1
	.WORD	S9,M9,S9,S9	; 2
	.WORD	M9,M9,S9,S9	; 3
	.WORD	S9,S9,M9,S9	; 4
	.WORD	M9,S9,M9,S9	; 5
	.WORD	S9,M9,M9,S9	; 6
	.WORD	M9,M9,M9,S9	; 7
	.WORD	S9,S9,S9,M9	; 8
	.WORD	M9,S9,S9,M9	; 9
;
; Digits 0-6 (10 minutes)
;
M6	.EQU	P15/3		; mark (+1)
S6	.EQU	M15/3		; space (-1)
;
BCD6	.WORD	S6,S6,S6,0	; 0
	.WORD	M6,S6,S6,0	; 1
	.WORD	S6,M6,S6,0	; 2
	.WORD	M6,M6,S6,0	; 3
	.WORD	S6,S6,M6,0	; 4
	.WORD	M6,S6,M6,0	; 5
	.WORD	S6,M6,M6,0	; 6
;
; Digits 0-3 (100 days)
;
M3	.EQU	P15/2		; mark (+1)
S3	.EQU	M15/2		; space (-1)
;
BCD3	.WORD	S3,S3,0,0	; 0
	.WORD	M3,S3,0,0	; 1
	.WORD	S3,M3,0,0	; 2
	.WORD	M3,M3,0,0	; 3
;
; Digits 0-2 (10 hours)
;
M2	.EQU	P15/2		; mark (+1)
S2	.EQU	M15/2		; space (-1)
;
BCD2	.WORD	S2,S2,0,0	; 0
	.WORD	M2,S2,0,0	; 1
	.WORD	S2,M2,0,0	; 2
;
; Hex decode for quality character
;
HEXASC	.WORD	'0'		; 0
	.WORD	'1'		; 1
	.WORD	'2'		; 2
	.WORD	'3'		; 3
	.WORD	'4'		; 4
	.WORD	'5'		; 5
	.WORD	'6'		; 6
	.WORD	'7'		; 7
	.WORD	'8'		; 8
	.WORD	'9'		; 9
	.WORD	'A'		; 10
	.WORD	'B'		; 11
	.WORD	'C'		; 12
	.WORD	'D'		; 13
	.WORD	'E'		; 14
	.WORD	'F'		; 15

;
; The following filter coefficient tables were generated using Math
; Works Inc. Matlab Signal Processing Toolkit. The performance data were
; obtained directly from the rounded and truncated coefficients, in
; order to accurately model the roundoff error. All of these use q15
; scaling and have stopband attenuation at least 40 dB.
;
; Bandpass 900-1300 Hz, stopband 0-400 Hz and 1800-4000 Hz
;
BPF400	.WORD	51		; b(0)
 	.WORD	-82		; b(1)
 	.WORD	-12		; b(2)
 	.WORD	42		; b(3)
 	.WORD	-66		; b(4)
 	.WORD	-360		; b(5)
 	.WORD	-516		; b(6)
 	.WORD	-181		; b(7)
 	.WORD	460		; b(8)
 	.WORD	741		; b(9)
 	.WORD	365		; b(10)
 	.WORD	-3		; b(11)
 	.WORD	491		; b(12)
 	.WORD	1454		; b(13)
 	.WORD	1166		; b(14)
 	.WORD	-1390		; b(15)
 	.WORD	-4570		; b(16)
 	.WORD	-4987		; b(17)
 	.WORD	-1054		; b(18)
 	.WORD	4663		; b(19)
 	.WORD	7371		; b(20)
 	.WORD	4663		; b(21)
 	.WORD	-1054		; b(22)
 	.WORD	-4987		; b(23)
 	.WORD	-4570		; b(24)
 	.WORD	-1390		; b(25)
 	.WORD	1166		; b(26)
 	.WORD	1454		; b(27)
 	.WORD	491		; b(28)
 	.WORD	-3		; b(29)
 	.WORD	365		; b(30)
 	.WORD	741		; b(31)
 	.WORD	460		; b(32)
 	.WORD	-181		; b(33)
 	.WORD	-516		; b(34)
 	.WORD	-360		; b(35)
 	.WORD	-66		; b(36)
 	.WORD	42		; b(37)
 	.WORD	-12		; b(38)
 	.WORD	-82		; b(39)
 	.WORD	51		; b(40)
;
; Lowpass 0-150 Hz, stopband 600-4000 Hz
;
LPF150	.WORD	-15		; b(0)
 	.WORD	-146		; b(1)
 	.WORD	-145		; b(2)
 	.WORD	-207		; b(3)
 	.WORD	-256		; b(4)
 	.WORD	-290		; b(5)
 	.WORD	-296		; b(6)
 	.WORD	-262		; b(7)
 	.WORD	-177		; b(8)
 	.WORD	-32		; b(9)
 	.WORD	175		; b(10)
 	.WORD	444		; b(11)
 	.WORD	768		; b(12)
 	.WORD	1134		; b(13)
 	.WORD	1521		; b(14)
 	.WORD	1909		; b(15)
 	.WORD	2272		; b(16)
 	.WORD	2586		; b(17)
 	.WORD	2828		; b(18)
 	.WORD	2981		; b(19)
 	.WORD	3033		; b(20)
 	.WORD	2981		; b(21)
 	.WORD	2828		; b(22)
 	.WORD	2586		; b(23)
 	.WORD	2272		; b(24)
 	.WORD	1909		; b(25)
 	.WORD	1521		; b(26)
 	.WORD	1134		; b(27)
 	.WORD	768		; b(28)
 	.WORD	444		; b(29)
 	.WORD	175		; b(30)
 	.WORD	-32		; b(31)
 	.WORD	-177		; b(32)
 	.WORD	-262		; b(33)
 	.WORD	-296		; b(34)
 	.WORD	-290		; b(35)
 	.WORD	-256		; b(36)
 	.WORD	-207		; b(37)
 	.WORD	-145		; b(38)
 	.WORD	-146		; b(39)
 	.WORD	-15		; b(40)
;
; Matched filter for WWV: five 1000-Hz sine cycles
;
MF1000	.WORD	0		; b(0)
 	.WORD	1384		; b(1)
 	.WORD	1957		; b(2)
 	.WORD	1384		; b(3)
 	.WORD	0		; b(4)
 	.WORD	-1384		; b(5)
 	.WORD	-1957		; b(6)
 	.WORD	-1384		; b(7)
 	.WORD	-0		; b(8)
 	.WORD	1384		; b(9)
 	.WORD	1957		; b(10)
 	.WORD	1384		; b(11)
 	.WORD	0		; b(12)
 	.WORD	-1384		; b(13)
 	.WORD	-1957		; b(14)
 	.WORD	-1384		; b(15)
 	.WORD	-0		; b(16)
 	.WORD	1384		; b(17)
 	.WORD	1957		; b(18)
 	.WORD	1384		; b(19)
 	.WORD	0		; b(20)
 	.WORD	-1384		; b(21)
 	.WORD	-1957		; b(22)
 	.WORD	-1384		; b(23)
 	.WORD	-0		; b(24)
 	.WORD	1384		; b(25)
 	.WORD	1957		; b(26)
 	.WORD	1384		; b(27)
 	.WORD	0		; b(28)
 	.WORD	-1384		; b(29)
 	.WORD	-1957		; b(30)
 	.WORD	-1384		; b(31)
 	.WORD	-0		; b(32)
 	.WORD	1384		; b(33)
 	.WORD	1957		; b(34)
 	.WORD	1384		; b(35)
 	.WORD	0		; b(36)
 	.WORD	-1384		; b(37)
 	.WORD	-1957		; b(38)
 	.WORD	-1384		; b(39)
 	.WORD	-0		; b(40)
;
; Matched filter for WWVH: six 1200-Hz sine cycles
;
MF1200	.WORD	0		; b(0)
 	.WORD	1583		; b(1)
 	.WORD	1861		; b(2)
 	.WORD	604		; b(3)
 	.WORD	-1150		; b(4)
 	.WORD	-1957		; b(5)
 	.WORD	-1150		; b(6)
 	.WORD	604		; b(7)
 	.WORD	1861		; b(8)
 	.WORD	1583		; b(9)
 	.WORD	0		; b(10)
 	.WORD	-1583		; b(11)
 	.WORD	-1861		; b(12)
 	.WORD	-604		; b(13)
 	.WORD	1150		; b(14)
 	.WORD	1957		; b(15)
 	.WORD	1150		; b(16)
 	.WORD	-604		; b(17)
 	.WORD	-1861		; b(18)
 	.WORD	-1583		; b(19)
 	.WORD	-0		; b(20)
 	.WORD	1583		; b(21)
 	.WORD	1861		; b(22)
 	.WORD	604		; b(23)
 	.WORD	-1150		; b(24)
 	.WORD	-1957		; b(25)
 	.WORD	-1150		; b(26)
 	.WORD	604		; b(27)
 	.WORD	1861		; b(28)
 	.WORD	1583		; b(29)
 	.WORD	0		; b(30)
 	.WORD	-1583		; b(31)
 	.WORD	-1861		; b(32)
 	.WORD	-604		; b(33)
 	.WORD	1150		; b(34)
 	.WORD	1957		; b(35)
 	.WORD	1150		; b(36)
 	.WORD	-604		; b(37)
 	.WORD	-1861		; b(38)
 	.WORD	-1583		; b(39)
 	.WORD	-0		; b(40)
;
; Table of sine function values at 4.5-degree increments, normalized by
; 1 / PI. This is used for the 100-Hz matched filter, the 1000/1200-
; Hz I/Q channels and the signal generators.
;
SINTAB	.WORD	0		; b(0)
	.WORD	818		; b(1)
	.WORD	1632		; b(2)
	.WORD	2435		; b(3)
	.WORD	3223		; b(4)
	.WORD	3992		; b(5)
	.WORD	4735		; b(6)
	.WORD	5450		; b(7)
	.WORD	6131		; b(8)
	.WORD	6774		; b(9)
	.WORD	7375		; b(10)
	.WORD	7931		; b(11)
	.WORD	8438		; b(12)
	.WORD	8893		; b(13)
	.WORD	9294		; b(14)
	.WORD	9636		; b(15)
	.WORD	9920		; b(16)
	.WORD	10142		; b(17)
	.WORD	10302		; b(18)
	.WORD	10398		; b(19)
	.WORD	10430		; b(20)
	.WORD	10398		; b(21)
	.WORD	10302		; b(22)
	.WORD	10142		; b(23)
	.WORD	9920		; b(24)
	.WORD	9636		; b(25)
	.WORD	9294		; b(26)
	.WORD	8893		; b(27)
	.WORD	8438		; b(28)
	.WORD	7931		; b(29)
	.WORD	7375		; b(30)
	.WORD	6774		; b(31)
	.WORD	6131		; b(32)
	.WORD	5450		; b(33)
	.WORD	4735		; b(34)
	.WORD	3992		; b(35)
	.WORD	3223		; b(36)
	.WORD	2435		; b(37)
	.WORD	1632		; b(38)
	.WORD	818		; b(39)
	.WORD	0		; b(40)
	.WORD	-818		; b(41)
	.WORD	-1632		; b(42)
	.WORD	-2435		; b(43)
	.WORD	-3223		; b(44)
	.WORD	-3992		; b(45)
	.WORD	-4735		; b(46)
	.WORD	-5450		; b(47)
	.WORD	-6131		; b(48)
	.WORD	-6774		; b(49)
	.WORD	-7375		; b(50)
	.WORD	-7931		; b(51)
	.WORD	-8438		; b(52)
	.WORD	-8893		; b(53)
	.WORD	-9294		; b(54)
	.WORD	-9636		; b(55)
	.WORD	-9920		; b(56)
	.WORD	-10142		; b(57)
	.WORD	-10302		; b(58)
	.WORD	-10398		; b(59)
	.WORD	-10430		; b(60)
	.WORD	-10398		; b(61)
	.WORD	-10302		; b(62)
	.WORD	-10142		; b(63)
	.WORD	-9920		; b(64)
	.WORD	-9636		; b(65)
	.WORD	-9294		; b(66)
	.WORD	-8893		; b(67)
	.WORD	-8438		; b(68)
	.WORD	-7931		; b(69)
	.WORD	-7375		; b(70)
	.WORD	-6774		; b(71)
	.WORD	-6131		; b(72)
	.WORD	-5450		; b(73)
	.WORD	-4735		; b(74)
	.WORD	-3992		; b(75)
	.WORD	-3223		; b(76)
	.WORD	-2435		; b(77)
	.WORD	-1632		; b(78)
	.WORD	-818		; b(79)
	.WORD	-0		; b(80)
;
	.END			; been there, done that.
