;****************************************************************************
;* Filename: PROBE.ASM
;****************************************************************************
;* Author:   Voja Antonic
;* Company:  PC Press
;* Revision: RevA1
;* Date:     07-09-98
;* Assembled using MPASM rev 01.50
;****************************************************************************
;* Include files:
;*      p16f84.inc
;****************************************************************************
;* This is the program for multi-purpose laboratory instrument which
;*  consists of logic probe, single-channel logic state analyzer,
;*  serial code receiver and frequency counter. As this is single-chip
;*  instrument, all functions are supported by software.
;*  
;* LCD module used is Hitachi's LM032L with 2 lines of 20 columns.
;*  
;* Note: The code is optimized for code space, and for that reason the
;*  most of code could not be written in modular format. For the same
;*  reason a lot of subroutines have more than one entry point and some
;*  of them are terminated by GOTO instead of RETURN.
;*
;* I/O port usage (all PORTA bits are inputs, all PORTB bits outputs)
;*  (note: bits 0, 5, 6 and 7 in port B have two functions each):
;*  
;*  PORTA,0 (input)   probe input
;*  PORTA,1 (input)   voltage monitor (high if battery voltage < 4 V)
;*  PORTA,2 (input)   left key (key 1) (low = key pressed)
;*  PORTA,3 (input)   right key (key 2) (low = key pressed)  
;*  PORTA,4 (input)   probe input
;*  PORTB,0 (output)  eneble LCD (high = select LCD), discharge (high = on)
;*  PORTB,1 (output)  register select LCD (low = instruction, high = data)
;*  PORTB,2 (output)  hi-imp output for probe pin
;*  PORTB,3 (output)  current hold for MCU supply (low = off)
;*  PORTB,4 (output)  LCD module D4
;*  PORTB,5 (output)  LCD module D5, LED P (high = on)
;*  PORTB,6 (output)  LCD module D6, LED H (high = on)
;*  PORTB,7 (output)  LCD module D7, LED L (high = on)
;*
;* External Clock Frequency: 10 MHz
;* Config Bit Settings: CP=OFF, PWRTE=ON, WDT=OFF, OSC=HS 
;* Program Memory Usage: 1023 words
;* Data RAM Usage: 68 bytes
;* Data EEPROM Usage: 61 bytes
;*  Note: This is read-only data, so the Data EEPROM must be programmed
;*  before the unit is ready to use. MCU will not affect data EEPROM contents.
;****************************************************************************

	list p=16f84, f=inhx8m, n=0
	include "p16f84.inc"

FLAG	equ	0ch	;  1 by  flag register
RXBITS	equ	0dh	;  1 by  bit0=parity, bit 1=7/8 bits, bit 2=inverse
DJNZ	equ	0eh	;  1 by  general purpose, e.g. loop counter
SCRATCH	equ	0fh	;  1 by  general purpose scratchpad
PCOUNT	equ	10h	;  1 by  timing count for led P (monostable simulator)
SUBMODE	equ	11h	;  1 by  submode (cursor horizontal position)
DEBO1	equ	12h	;  1 by  rotor for key 1 debouncing
DEBO2	equ	13h	;  1 by  rotor for key 2 debouncing
COUNT	equ	14h	;  1 by  general purpose counter
RATE	equ	15h	;  1 by  analyzer sample rate, 0...15
CHARCOU	equ	16h	;  1 by  char counter for fixed format display
SHOWCOU	equ	17h	;  1 by  1-4, which group of 60 samples is displayed
DELAYL	equ	18h	;  1 by  delay for led P on when led L is on
DELAYH	equ	19h	;  1 by  delay for led P on when led H is on
PRESC	equ	1ah	;  1 by  prescaler rate for frequency counter
TIMOUTL	equ	1bh	;  1 by  timeout counter lo, for auto power off
TIMOUTH	equ	1ch	;  1 by  timeout counter hi, for auto power off
RXRATE	equ	1dh	;  1 by  rx baud rate, 0...7
BIN4	equ	1eh	;  4 by  arith buf bin value, 4 by, lo byte first
CMP4	equ	22h	;  4 by  arith buf for comparing, 4 by, lo byte first
BUFFER	equ	26h	; 42 by  42 by receive buffer for analyzer and RX

REL	equ	1	; =1 to place cursor on 1st char of command (default)
			; =0 to place cursor before the command

; Bits definitions for FLAG register (bit 0 not used):

DP	equ	1	; decimal point in 3-digit bin2dec conv
PTIP	equ	2	; prev.state of probe input (for edge detect)
RIPPLE	equ	3	; zero blanking bit
XTOX	equ	4	; analyzer start at: 1=rising, 0=falling edge
LEDP	equ	5	; led Pulse, 1=on
LEDH	equ	6	; led High, 1=on
LEDL	equ	7	; led Low, 1=on


;*****************************************************************************
;* Reset vector
;*****************************************************************************

	goto	Start

;*****************************************************************************
;* Get1MHz
;* This subroutine fetches 307 samples (last 7 will be ignored) from PORTA.0
;*  rotating throughu CARRY at 1 MHz rate - 2.5 instruction cycles for each
;*  sample, realized mostly as as 2 and 3 cycles alternatively, at the
;*  following order:
;*  
;*  4t-2t-2t (not in main loop, executed only once), and then
;*  
;*  2t-2t-3t-2t-3t-2t-3t-2t-3t-2t-3t-2t-3t-2t-2t-4t    (repeated 19 times)
;*  
;* Call Common initializes loop counter (COUNT) to make 19 cycles before
;*  exiting (16 samples are fetched at each pass), and FSR to point to
;*  BUFFER. It also presets T0SE bit depended on XTOX bit (in FLAG register)
;*  to enable proper edge detecting, as it will affect TMR0 state.
;*  State of key 2 (Break) is tested while waiting for starting condition.
;* Write pointer FSR is incremented after every 8 samples. COUNT initial
;*  value is 01101101 and, after ANDing 0c0h and subtracting 33h from it,
;*  makes 0dh, even if COUNT is incremented 18 times. After 19 passes,
;*  COUNT is incremented to b'10000000', which after AND 0c0h and
;*  SUB 33h makes 4dh. Those jumps are location sensitive, and it makes the
;*  whole subroutine unrelocateable.
;* Between this subroutine and the instruction goto Finished (below), which
;*  must be at location 4dh, there are 25 free locations. They are used for
;*  tables DecTab and CurTab, which causes that those tables must have the
;*  fixed length. If anything is relocated here, take care not to affect
;*  location of instruction goto Finished.
;*
;* Input variables: None
;* Output variables: None
;*****************************************************************************

  	org	1		; this subroutine must start at address 1

Get1MHz				; 2.5 t read cycle
  	movlw	80h-.19		; loop end after 19 cycles (38 by=304 samples)
	call	Common		; initialize COUNT, FSR, hi-imp out...
				; ...bit XTOX and T0SE bit
GetEdge
	btfss	PORTA,3		; test status of key 2 and...
	goto	Break		; ...jump to Break handling routine if pressed

	movf	TMR0,W		; TMR0 = logic level edge detector
	rrf	PORTA,F	 ; <---	; the first sample is a little earlier
				; ... to compensate starting delay
	btfsc	STATUS,Z	; test if there was egde...
	goto	GetEdge		; ...and loop if not

	rrf	INDF,F		; rotate bit into destination byte
	rrf	PORTA,F	 ; <---   get bit from input to C
	rrf	INDF,F		; rotate bit into destination byte
	rrf	PORTA,F	 ; <---   get bit from input to C
				; movwf PCL will jump here at 18 passes
				; ...@addr 0100 0000 (40h) - 33h = 0dh
	rrf	INDF,F		; rotate bit into destination byte
	rrf	PORTA,F	 ; <---   get bit from input to C
	rrf	INDF,F		; rotate bit into destination byte
	rrf	PORTA,F	 ; <---   get bit from input to C
	rrf	INDF,F		; rotate bit into destination byte
	rrf	PORTA,F	 ; <---   get bit from input to C
	rrf	INDF,F		; rotate bit into destination byte

	incf	COUNT,F		; COUNT = loop counter

	rrf	PORTA,F	 ; <---   get bit from input to C
	rrf	INDF,F		; rotate bit into destination byte
	rrf	PORTA,F	 ; <---   get bit from input to C
	rrf	INDF,F		; rotate bit into destination byte

	incf	FSR,F		; must be exactly 8 read cycles distance
				; ... between FSR incrementing
	rrf	PORTA,F	 ; <---   get bit from input to C
	rrf	INDF,F		; rotate bit into destination byte
	rrf	PORTA,F	 ; <---   get bit from input to C
	rrf	INDF,F		; rotate bit into destination byte

	movf	COUNT,W		; COUNT = loop counter

	rrf	PORTA,F	 ; <---   get bit from input to C
	rrf	INDF,F		; rotate bit into destination byte
	rrf	PORTA,F	 ; <---   get bit from input to C
	rrf	INDF,F		; rotate bit into destination byte

	andlw	0c0h		; this will make first 18 jumps to 0dh,
				; ...and the 19th one to 4dh
	rrf	PORTA,F	 ; <---   get bit from input to C
	rrf	INDF,F		; rotate bit into destination byte
	rrf	PORTA,F	 ; <---   get bit from input to C
	rrf	INDF,F		; rotate bit into destination byte

	addlw	-33h		; this will make first 18 jumps to 0dh,
				; ...and the 19th one to 4dh
	rrf	PORTA,F	 ; <---   get bit from input to C
	rrf	INDF,F		; rotate bit into destination byte
	rrf	PORTA,F	 ; <---   get bit from input to C
	rrf	INDF,F		; rotate bit into destination byte

	incf	FSR,F		; must be exactly 8 read cycles distance
				; ... between FSR incrementing
	rrf	PORTA,F	 ; <---   get bit from input to C
	rrf	INDF,F		; rotate bit into destination byte
	rrf	PORTA,F	 ; <---   get bit from input to C
	rrf	INDF,F		; rotate bit into destination byte
	rrf	PORTA,F	 ; <---   get bit from input to C
 
	movwf	PCL		; jumps to 0dh in first 18 passes
				; jumps to 4dh at 19th pass

;*****************************************************************************
;* This table is used for bin2ascii (4-byte to 8-digit) conversion
;*****************************************************************************

DecTab
	dt	098h,096h,080h	; decimal 10 000 000
	dt	00fh,042h,040h	; decimal 1 000 000
	dt	001h,086h,0a0h	; decimal 100 000
	dt	000h,027h,010h	; decimal 10 000
	dt	000h,003h,0e8h	; decimal 1 000
	dt	000h,000h,064h	; decimal 100
	dt	000h,000h,00ah	; decimal 10

;*****************************************************************************
;* Cursor position table for all SUBMODEs in mode 4 (battery manager)
;*****************************************************************************

CurTab4
	dt	0d2h+REL,0c0h,0c3h+REL,0cah+REL

;*****************************************************************************
;* This is exit point for subroutine Get1MHz. Do not move this
;*  instruction, it must be at address 4dh!
;*****************************************************************************

	org	80h-33h		; addr 1000 0000 (80h) - 33h = 4dh
				; Get1MHz jumps here
	goto	Finished

;*****************************************************************************
;* Table for LCD module character generator redefinition, to enable
;*  pseudographic representation of bit samples in analyzer mode. It defines
;*  first 8 characters, which are stored in LCD module's RAM.
;*****************************************************************************

Graphs
	;	...  ..*  .*.  .**  *..  *.*  **.  ***
	dt	00h, 01h, 04h, 05h, 10h, 11h, 14h, 15h

;*****************************************************************************
;* Cursor position table for all SUBMODEs in mode 1 (Analyzer)
;*****************************************************************************

CurTab1
	dt	0d2h+REL,0c0h,0cch+REL,0ceh+REL,0d0h+REL

;*****************************************************************************
;* Cursor position table for all SUBMODEs in mode 2 (Serial rcvr)
;*****************************************************************************

CurTab2
	dt	0d2h+REL,0c7h+REL,0cch+REL,0ceh+REL,0d0h+REL

;*****************************************************************************
;* Prescaler table for resolution display in mode 3 (freq counter)
;*****************************************************************************

PrescTab
	dt	.4, .8, .16, .32

;*****************************************************************************
;* Range table for max. frequency display in mode 3 (freq counter)
;*****************************************************************************

RangeTab
	dt	.5, .10, .20, .40
;*****************************************************************************
;* Timing constants for serial code receiver
;*****************************************************************************

BaudRate
	dt	.188, .94, .46, .23, .11, .5, .3, .1

;*****************************************************************************
;* Text strings (terminator = last character with bit 7 set)
;*****************************************************************************

TxtHz
	dt	"MHz",'/'+80h
DisTxt
	dt	"Off Disch. Charg",'e'+80h
Head1
	dt	"Analyze",'r'+80h
Head2
	dt	"Seria",'l'+80h
Head3
	dt	"Frequenc",'y'+80h
Head4
	dt	"Batter",'y'+80h
BrkMes
	dt	"Brea",'k'+80h

;;*******
;*******  START
;*******

;*****************************************************************************
;* Power Up sequence: I/O port B defined as all outputs, PORTB,3 set to
;*  switch power supply on and the internal Data Ram is cleared
;*****************************************************************************

Start
	bsf	STATUS,RP0
	clrf	TRISB		; port b: all bits outputs, port a: inputs
	bcf	STATUS,RP0
	movlw	0ch		; start of RAM clear, also PORTB output byte
	movwf	PORTB		; switch power supply ON (set PORTB,3)
	call	ClrRam		; clear internal RAM and wait 33.8 ms

;*****************************************************************************
;* LCD module initialization. 4-bit mode selected, display data RAM cleared,
;*  cursor set to blink mode, and the pseudographics character for
;*  character set 00h-07h preset from table Graphs.
;*****************************************************************************

	bcf	PORTB,1		; rs lo (instruction)
	movlw	2		; 4-bit mode
	call	Nibble		; write 4-bit mode command
	movlw	28h		; function set: 4 bit mode, 2 lines, 5*7 dots
	call	WrComL		; write command and wait 130 us
	movlw	06h		; mode set: cursor direction right, no shift
	call	WrComL		; write command and wait 130 us
	movlw	0dh		; display on, no cursor, blink cursor pos
	call	WrComL		; write command and wait 130 us

	movlw	40h		; cg ram addr 0
	call	WrComL		; write address and wait 130 us
	movlw	Graphs		; start address of graph set for lcd disp
	movwf	SCRATCH		; move start address to pointer
	movlw	.8*.4		; 8 special characters to define
				; CHARCOU is decremented in Char routine,
				; ...that is why here is 8*4
	movwf	CHARCOU		; loop counter for 8 special characters
GoGraph
	movlw	.5		; five rows are equal
	movwf	COUNT		; counter for 5 rows
FiveRows			; inner loop: 5 rows are equal
	call	PclSub1		; move SCRATCH to PCL
	call	CharNCC		; rows 1-5 from the table
	decfsz	COUNT,F		; five passes over?
	goto	FiveRows	; no, loop

	movlw	15h		; 15h = b'10101' = dot-space-dot-space-dot
	call	CharBl		; row 6: all dots set, row 7: all dots cleared
	call	Blank		; row 8: all dots cleared
	incf	SCRATCH,F	; inc ptr
	decfsz	CHARCOU,F	; 8 characters defined?
	goto	GoGraph		; no, loop 8 x

;;*******
;*******  USER INTERFACE
;*******

;*****************************************************************************
;* This is home point for mode 1 (Analyzer): prints text "Analyzer" and
;*  command line in line 2, with cursor placed depended on variable SUBMODE.
;*  Then the keyboard routine is called, where it will wait for key to be
;*  pressed.
;* Break entry point prints message Break in line 1 and readraws line 2
;*****************************************************************************

Mode1				; Mode 1: Analyzer
 	movlw	Head1-1		; start address of string -1
	call	Headline	; print "Analyzer"
	goto	Farm1		; avoid "Break" message
Break				; Break entry pt (if Break pressed in Mode 1)
	call	PrintBrk	; print "Break"
Farm1
	call	PrintM1		; print string in line 2
Farm1B
	movlw	CurTab1		; get address of cursor table in analyzer mode
	call	CurPosKb	; place cursor on proper position
				; test keys and probe input, service leds
				; return if key pressed (C:key1, NC:key2)

;*****************************************************************************
;* If key 1 pressed (C), SUBMODE is advanced (range 0...4, then wrap to 0
;* If key 2 pressed (NC), program vectors to corresponding routine
;*  (except if SUBMODE=1, then the sample rate is advanced and displayed)
;*****************************************************************************

	btfsc	STATUS,C	; test which key was pressed
	goto	Key1A		; jump if key 1
				; continue if key 2 pressed
	call	Range5		; advance variable SUBMODE in range 0...4
	goto	Farm1B		; go wait next key
Key1A				; key 1 pressed
	decfsz	SUBMODE,W	; test SUBMODE (set Z if SUBMODE=1)
	goto	NoRate		; jump if SUBMODE <>1
				; continue if SUBMODE=1
	incf	RATE,F		; advance sample rate
	bcf	RATE,4		; RATE range 0...15

	call	ClrRow1		; prepare line 1 to print sample rate No.
	incf	RATE,W		; readjust RATE from 0...15 to 1...16
	call	Print255	; print serial number of sample rate 1...16
	goto	Farm1		; go redraw row 2 and wait for next command

EdgeSet				; Change start condition (L-to-H or H-to-L)
	movlw	10h		; bit 4 is flag XTOX
	xorwf	FLAG,F		; change flag
	goto	Farm1		; go redraw row 2 and wait for next command
NoRate
	btfsc	SUBMODE,0	; bit 0 will be set here only if SUBMODE=3
	goto	Mode1Go		; if SUBMODE=3
	btfsc	SUBMODE,1	; bit 1 will be set here only if SUBMODE=2
	goto	EdgeSet		; if SUBMODE=2
	btfsc	SUBMODE,2	; bit 2 will be set here only if SUBMODE=4
	goto	Mode1Show	; if SUBMODE=4
				; if SUBMODE=0 then program drops to Mode2...

;*****************************************************************************
;* This is home point for mode 2 (RS232 receiver): prints text "Serial" and
;*  command line in line 2, with cursor placed depended on variable SUBMODE.
;* BrkRS entry point prints message Break in line 1 and readraws line 2
;*  if no bytes are received, and displays first 7 bytes if any byte received
;*****************************************************************************

Mode2				; mode 2: Serial receiver
	movlw	Head2-1		; start address of string -1
	call	Headline	; print "Serial"
	goto	Farm2		; avoid "Break" message
BrkRS				; Break entry pt (if Break pressed in mode 2)
	movf	FSR,W		; FSR points where to write next rcvd byte
	xorlw	BUFFER		; if FSR = literal BUFFER the no bytes rcvd
	btfss	STATUS,Z	; test if FSR = literal BUFFER
	goto	Show2		; no - then some bytes received, show them
	call	PrintBrk	; yes - no bytes received, print "Break"

;*****************************************************************************
;* Prints baud rate in KBaud on LCD
;* 
;* Input variables: RXRATE in range 0...7
;* Output variables:
;*     CHARCOU is decremented by the number of characters printed
;*****************************************************************************

Farm2
	movlw	0c8h		; baud rate position on LCD
	call	WrComL		; move cursor command

	bcf	FLAG,DP		; no decimal point printing if RATE=0
	clrf	BIN4+1		; BIN4+1 is high byte for baud rate display

	incf	RXRATE,W	; move RXRATE from range 0...7 to 1...8
	movwf	DJNZ		; DJNZ = RXRATE+1

	movlw	.115		; case RXRATE=7: then Baud rate=115 Kbaud
	btfsc	DJNZ,3		; test if DJNZ=8 (same as RXRATE=7)
	goto	Lth256		; yes, go case 115.2 (RXRATE=7)

	bsf	FLAG,DP		; for rete 0...6 there is a decimal point
	bsf	BIN4+1,1	; case RXRATE=6: this is hi byte for 57.6
	movlw	.576-.512	; case RXRATE=6: this is lo byte for 57.6
	incf	DJNZ,F		; DJNZ=RXRATE+2
	btfsc	DJNZ,3		; test if DJNZ=8 (same as RXRATE=6)
	goto	Lth256		; yes, go case 57.6 (RXRATE=6)

	clrf	BIN4+1		; for rates 0...5 hi byte is zero
	movlw	.3		; constant for rates 0...5
	movwf	BIN4		; BIN4 will be rotated (multiplied by 2)
				; RXRATE+2 times to get 1.2 - 2.4 - 4.8
				; - 9.6 - 19.2 - 38.4
X2Loop
	bcf	STATUS,C	; clear bit C to get multiplying by 2
	rlf	BIN4,F		; multiply low byte
	rlf	BIN4+1,F	; multiply hi byte, that is 16-bit rotate
	decfsz	DJNZ,F		; test if RXRATE+2 times multiplied
	goto	X2Loop		; no, loop
	movf	BIN4,W		; yes, get result to print it
Lth256
	call	PrintBR		; print baud rate, including decimal point
	call	Blank		; to delete last numeric from previous rate

;*****************************************************************************
;* Prints number of bits to be received (7 or 8), with suffix "p" if parity
;*  bit will be received (not written to RAM!), and with prefix "i" if
;*  inverse input polarity is expected. Input variable RXBITS, bit 0 set if
;*  parity bit expected, bit 1 set if 8-bit word and bit 2 set if inverse
;*  polarity (low start bit, inverse data bits and high stop bit)
;*****************************************************************************

	movlw	0cch		; pos for bit No. (7/8/7p/8p/i7/i8/i7p/i8p) -1
	call	WrComL		; write command

	movlw	'R'		; space: true polarity  (R = RS232)
	btfsc	RXBITS,2	; let it be space if RXBITS,2 cleared
	movlw	'T'		; "i": inverse polarity	(T = TTL)
	call	Char		; print blank or "i"	("R" or "T")

	movlw	'7'		; "7": 7 bits
	btfss	RXBITS,1	; let it be 7 if RXBITS,1 set
	movlw	'8'		; "8": 8 bits
	call	Char		; print "7" or "8" (bits)

	movlw	' '		; space: no parity
	btfsc	RXBITS,0	; let it be space if RXBITS,0 cleared
	movlw	'p'		; "p": parity bit exists
	call	Char		; print "p" (parity bit present) or blank

;*****************************************************************************
;* This call prints number of group displayed and "*" (execution) symbol
;*****************************************************************************

	call	KaoAna		; print rest of line - it is the same as
				; on mode 1 (analyzer)

;*****************************************************************************
;* Places cursor on proper position (input variable SUBMODE) and calls
;*  keyboard subroutine, where it will wait for key to be pressed
;*****************************************************************************

	movlw	CurTab2		; table with cursor positions
	call	CurPosKb	; place cursor on proper pos
				; test keys and probe input, service leds
				; return if key pressed (C:key1, NC:key2)

;*****************************************************************************
;* If key 1 pressed (C), SUBMODE is advanced (range 0...4, then wrap to 0
;* If key 2 pressed (NC), program vectors to corresponding routine
;*  (except if SUBMODE=1, then the Baud rate is advanced and displayed)
;*****************************************************************************

	btfsc	STATUS,C	; test which key was pressed
	goto	Key1B		; jump if C set, it means that key 1 pressed
				; key 2 pressed
	call	Range5		; increment SUBMODE in range 0...4
	goto	Farm2		; go redraw row 2 and wait for next command
Key1B				; key 1 pressed
	btfsc	SUBMODE,2	; bit 2 is set only if SUBMODE = 4
	goto	Mode2Show	; jump if SUBMODE = 4

	btfss	SUBMODE,1	; bit 1 is cleared only if SUBMODE = 0 or 1
	goto	Sub01		; jump if SUBMODE = 0 or SUBMODE = 1

	btfsc	SUBMODE,0	; bit 0 is set here only if SUBMODE = 3
	goto	Mode2Go		; jump if SUBMODE = 3
				; drops here if SUBMODE = 2
	incf	RXBITS,F	; advance RXBITS (command)
	bcf	RXBITS,3	; RXBITS cycle in range = 0...7
	goto	Farm2		; go redraw row 2 and wait for next command
Sub01
	btfss	SUBMODE,0	; bit 0 is set here only if SUBMODE = 0
	goto	FreqEp		; if SUBMODE = 0 then goto frequency entry pt

	incf	RXRATE,F	; if SUBMODE = 1 then advance RXRATE
	bcf	RXRATE,3	; RXRATE cycle in range 0...7
	goto	Farm2		; go redraw row 2 and wait for next command

;*****************************************************************************
;* This subroutine increments variable SUBMODE, and if the result is >4 it
;*  wraps to 0
;*****************************************************************************

Range5				; increment SUBMODE in range 0...4
	incf	SUBMODE,F	; advance SUBMODE
	btfsc	SUBMODE,2	; if SUBMODE,2 cleared then no overflow
	btfss	SUBMODE,0	; if SUBMODE,0 cleared then no overflow
	return			; no overflow: return
	clrf	SUBMODE		; SUBMODE cycle in range 0...4
	return			; SUBMODE wrapped to 0, return

;*****************************************************************************
;* Mode4 is home point for mode 4 (off/discharge/charge): prints text
;*  "Battery" and command line in line 2, with cursor placed depended on
;*  variable SUBMODE. Then the keyboard routine is called, where it will
;*  wait for key to be pressed
;* Break4 entry point prints message Break in line 1 and readraws line 2
;* ExitDis is the entry point if key 2 is pressed during discharging
;*****************************************************************************

ExitDis				; exit discharge entry point (if disch Break)
	call	DisEna30	; switch off PORTB,0 for discharge transistor
Break4				; exit Charge entry point (if Charge Break)
	call	PrintBrk	; print "Break"
	goto	Contm4		; avoid headline printing
Mode4				; mode 4: discharge/charge
	call	Headline2	; print "Battery"
Contm4
	call	Row2		; move cursor to line 2
	movlw	DisTxt-1	; point to message -1
	call	Write		; print Off Disch Charge
Farm4
	movlw	CurTab4		; point to cursor table for mode 4
	call	CurPosKb	; place cursor @ Off/Disch/Charge/-->
				; test keys and probe input, service leds
				; return if key pressed (C:key1, NC:key2)

;*****************************************************************************
;* If key 1 pressed (C), SUBMODE is advanced (range 0...3, then wrap to 0
;* If key 2 pressed (NC), program jumps to corresponding routine
;*****************************************************************************

	btfsc	STATUS,C	; test which key was pressed
	goto	Key1D		; C set: key 1 pressed
				; key 2 pressed
	incf	SUBMODE,F	; advance SUBMODE
	bcf	SUBMODE,2	; SUBMODE cycle in range 0...3
	goto	Farm4		; go redraw row 2 and wait for next command
Key1D				; key 1 pressed
	btfsc	SUBMODE,1	; bit 1 set only if Charge or Disch. submode
	goto	ChargDis	; goto Charge or Discharge process
				; depended on bit 0 in variable SUBMODE
	btfss	SUBMODE,0	; SUBMODE,0 cleared here if SUBMODE=0
	goto	Mode1		; shortcut to mode 1 (Analyzer)

	goto	Suicide		; manual power off
;;*******
;*******  CHARGE/DISCHARGE
;*******

;*****************************************************************************
;* Charge/Discharge entry point. If bit SUBMODE,0 set, then go to Charge
;*  SUBMODE,1 is set in this point. (SUBMODE=2 or 3)
;*****************************************************************************

ChargDis
	call	ClrRow1		; in both cases row 1 must be cleared
	btfsc	SUBMODE,0	; test if SUBMODE,0 set, if so...
	goto	Charge		; ...jump to Charge (SUBMODE=3)
				; ...else continue to discharge (SUBMODE=2)

;*****************************************************************************
;* Discharge starts here (SUBMODE=2)
;* Cursor moved to line 1 under text "Disch."
;*  Then command 02h (Home Cursor) is issued to LCD controller, but this is
;*  dummy command - the sense is to freeze it after first nibble, and thus to
;*  leave PORTB,0 (ENA) in high state as long as discharging lasts. After the
;*  discharging termination (if voltage monitor detects <4V or key 2 pressed),
;*  the command for LCD controller will be completed, switching
;*  discharging transistor off.
;*  If discharging is broken by key, program returns to user interface for
;*  mode 4, if terminated by voltage monitor, charging takes place
;*****************************************************************************

	bcf	PORTB,1		; pull LCD Register Select low (=instruction)
	clrw			; high nibble of instruction 02h = 0h
	call	Hinib_B		; output W,4-7 to 4-bit LCD data bus
	call	EnaLCD		; generate Enable signal (1200us) for LCD
	movlw	20h		; command 02h = home cursor (nibbles swapped)

	call	Hinib_B		; output W,4-7 to 4-bit LCD data bus
	bsf	PORTB,0		; ENA activated (the command won't be
				; finished until Break or voltage < 4V)
DisLoop
	btfss	PORTA,3		; test key 2 status...
	goto	ExitDis		; if low,discharging is manually broken by key

	btfss	PORTA,1		; test voltage monitor...
	goto	DisLoop		; if still >=4V, loop
				; discharging terminated here (voltage < 4V)

	call	DisEna30	; switch off discahge transistor

;*****************************************************************************
;* Charging starts here (by command or after successful discharging)
;* Minute and hour counters are initialized and counting process starts.
;* Clock (in format HH:MM) is displayed in line 1 under text "Charge".
;* If charging broken by key, program returns to user interface for
;*  mode 4, if terminated by timeout (14 hours), the unit jumps to SUICIDE
;*  (switches off the unit forcing the output PORTB,3 low).
;*****************************************************************************

Charge				; Charge entry point

	clrf	TIMOUTL		; initialize minute counter 0...59
	clrf	TIMOUTH		; initialize hour counter 0...13
ChLoop
	movlw	8bh		; position of digital clock on LCD
	call	WrComL		; cursor to digital clock pos
	movf	TIMOUTH,W	; TIMOUTH=hours in binary format
	call	Print255	; print hour in format HH
	movlw	':'		; ":" = separator
	call	PrintTL		; print ":" and minute in format MM

	movlw	.228		; 228 x 263270.4 us = 60 sec
	movwf	SCRATCH		; high byte loop counter for 1 minute loop
Min1
	call	GoLoop		; 1283t (513.2us) inclusive	; 1283t
	call	GoLoop		; total 1026.4us		; 1283t

	btfss	PORTA,3		; 2t  test status of key 2...
	goto	Break4		; -   ... if low,charging manually terminated

	decfsz	COUNT,F		; 1t  low byte loop counter
	goto	Min1		; 2t  inner pass 1028.4 us

	decfsz	SCRATCH,F	; high byte loop counter for 1 minute
	goto	Min1		; one pass 263270.4 us

	incf	TIMOUTL,F	; advance minute counter
	movf	TIMOUTL,W	; TIMOUTL = minute up counter
	addlw	-.60		; test if 60 minutes of charging passed...
	btfss	STATUS,C	; if 60 minutes passed, C should be set
	goto	NotHour		; not yet hour advance
	clrf	TIMOUTL		; if 60 minutes passed, clear minute counter
	incf	TIMOUTH,F	; ...and advance TIMOUTH = hour up counter
NotHour
	movf	TIMOUTH,W	; TIMOUTH = hour up counter
	addlw	-.14		; test if 14 hours of charging
	btfss	STATUS,C	; if 14 hours passed, C should be set
	goto	ChLoop		; ...if not yet 14 hours, loop
	goto	Suicide		; charging terminated after 14 h

;;*******
;*******  KEYBOARD AND PROBE
;*******

;*****************************************************************************
;* CurPosKb
;* This subroutine places cursor in line 2 at position taken from the
;*  lookup table: table offset is addressed by W at input, and the table read
;*  location by variable SUBMODE.
;* High timeout counter (TIMOUTH) is initialized. This sets automatic
;*  Power Off timing to about 8 minutes. TIMOUTL is of minor importance
;*  here (it affects less than 2 seconds of timing), so it was not worth
;*  waisting one instruction word.
;* Bit DEBO,0 is set to disable false recognizing of PORTA,3 low level as
;*  falling edge (as if key 2 was just pressed). This could happen if some
;*  function was broken by pressing key 2, as those are simple port tests
;*  without affecting debouncer.
;* This subroutine continues to keyboard scan subroutine.
;*
;* Input variables: SUBMODE, affects cursor position
;* Output variables: TIMOUTH=0, high timeout counter
;*****************************************************************************

CurPosKb
	addwf	SUBMODE,W	; add SUBMODE to lookup table offset
	call	PclSub		; get cursor position from table
	call	WrComL		; write new cursor position to LCD
	clrf	TIMOUTH

	bsf	DEBO1,0		; set any bit in both debouncers...
	bsf	DEBO2,0		; ...to disable false recognizing of low...
				; ...level as falling edge

;*****************************************************************************
;* GoKbd
;* This is the main keyboard subroutine, in which program loops all the time
;*  except in frequency counter mode, or while waiting for start condition or
;*  executing some command. Program exits this subroutine only if some key is
;*  just pressed (not if continuosly pressed), or when timeout counter
;*  (TIMOUTH, TIMOUTL) after 8 min reaches zero. If key 1 was pressed, flag
;*  STATUS,C will be reset at exit, if key 2 was pressed, flag STATUS,C will
;*  be set. If timeout detected, the routine SUICIDE is executed (the unit
;*  switched off forcing the output PORTB,3 low). 
;* During keyboard scan, LEDs L, H and P are serviced. Logic state at
;*  PORTA,4 affects LEDs L and H directly, and LED P is under control of down
;*  counter PCOUNT. This counter is initialized at every logic level
;*  transition at PORTA,4, and while counting down, if PCOUNT>0, LED P is
;*  switched on.
;* Loop labeled "Unstable" adds the extra delay which timing is not
;*  constant, but changes from 3 to 49t. This minimizes the interference
;*  between the input scan and tested signal frequency.
;* Counter TMR0 is used for detecting of short pulses. At each
;*  transition detected, TMR0 is cleared, and then periodicaly tested if
;*  counter state was incremented. If so, PCOUNT is initialized and LED P
;*  turned on.
;* Register DJNZ is used as a freerunning counter, which divides loop count
;*  by 256 and slows down PCOUNT countdown and keys scanning. Keys are
;*  debounced and falling edge (pressing moment) detected by rotating
;*  registers DEBO1 and DEBO2, and testing them if the key was unpressed at
;*  least at 7 passes and then pressed at 1 pass.
;*  
;* Input variables: none
;* Output variables: Bit STATUS,C reset if key 1 was pressed, set if key 2
;*****************************************************************************

GoKbd
	movf	DJNZ,W		; 1t   to avoid interference, total avg 29t
	iorlw	0f0h		; 1t   extra timing range between -.16 and -1
	movwf	SCRATCH		; 1t   here SCRATCH varies from 0f0h to 0ffh
UnStable
	incfsz	SCRATCH,F	; 1-17t (avg .9)  advance extra timing count
	goto	UnStable	; 2-32t (avg .17) loop loosing extra time

	movf	TMR0,W		; TMR0 = hardware transition detector
	btfsc	STATUS,Z	; test if there was transition at PORTA,4...
	goto	NoIniP		; ...if not, do not affect LED P status
	bsf	PCOUNT,3	; initialize PCOUNT for LED P timing
	clrf	TMR0		; reinitialize hardware transition detector
NoIniP
	movlw	28h		; bit 4 (TOSE) RESET: low-to-hi transition
	btfsc	PORTA,4		; if probe tip low,leave TOSE reset...
	movlw	38h		; if high, set TOSE: hi-to-low transition

	bsf	STATUS,RP0	; select bank 1 of registers
	movwf	OPTION_REG	; set/reset TOSE
	bcf	STATUS,RP0	; reselect bank 0

	bcf	DELAYH,0	; initialize input bit in delay HI rotor
	bcf	DELAYL,0	; initialize input bit in delay LOW rotor
	movlw	4		; value 4 = bit 2 set
	xorwf	PORTB,F		; changes state of PORTB.2 (square wave...
				; generation for probe tip hi-impedance output
	btfsc	PORTA,4		; test probe input logic level...
	goto	InputHi		; ...and jump if case 2: input high
				; ...or continue if case 1: input low
	btfss	PORTB,2		; test state of hi-impedance output and...
	bsf	DELAYL,0	; ...switch on led L because hi-ipm out was lo
	bcf	FLAG,PTIP	; reset flag to notify that previoust state...
				; ...of probe tip was low
	goto	ContInpX	; jump to skip case 2
InputHi				; entry point for case 2: input high
	btfsc	PORTB,2		; test state of hi-impedance output and...
	bsf	DELAYH,0	; ...switch on led L because PORTB,2 was high
	bsf	FLAG,PTIP	; set flag to notify that previoust state...
				; ...of probe tip was high
ContInpX			; moves LHP leds from FLAG to PORTB
	bcf	FLAG,LEDL	; initialize input bit for led L delay rotor
	movf	DELAYL,F	; test DEALYL status...
	btfss	STATUS,Z	; ...and skip if DELAYL=0
	bsf	FLAG,LEDL	; turn on led L if DELAYL rotor > 0
	rlf	DELAYL,F	; propagate bit thru DELAYL rotor

	bcf	FLAG,LEDH	; initialize input bit for led H delay rotor
	movf	DELAYH,F	; test DEALYH status...
	btfss	STATUS,Z	; ...and skip if DELAYH=0
	bsf	FLAG,LEDH	; turn on led H if DELAYH rotor > 0
	rlf	DELAYH,F	; propagate bit thru DELAYH rotor

	call	MoveLESd	; transfer leds status from flag bits to PORTB

	incfsz	DJNZ,F		; test if this is 256th pass...
	goto	GoKbd		; ...if not, loop

;------------------------------ passes here at each 256. pass (about 8.9 ms)

	bcf	FLAG,LEDP	; reset led P flag (will be set if PCOUNT>0)
	movf	PCOUNT,F	; test PCOUNT status...
	btfsc	STATUS,Z	; ...and skip jump if PCOUNT>0
	goto	PCount0		; ...else jump if PCOUNT = 0

	decf	PCOUNT,F	; if PCOUNT>0, then decrement it
	movf	DELAYL,W	; W>0 if led L is on
	iorwf	DELAYH,W	; W>0 if led L or led H is on
	btfss	STATUS,Z	; ...skip if both L and H leds are off
	bsf	FLAG,LEDP	; if PCOUNT>0 then dec PCOUNT, and set led P
PCount0
				; ----------- key 2 test (right key)
	swapf	PORTA,W		; let key 1 and 2 status move to bits 6 and 7
	movwf	SCRATCH		; SCRATCH,7=key2, SCARTCH,6=key1
	comf	SCRATCH,F	; complement to set bit if key pressed

	rlf	SCRATCH,F	; set C if key 2 pressed

	rlf	DEBO2,F		; propagate key 2 bit thru rotor
	bcf	STATUS,C	; reset C to notify at exit that key 2 pressed
	decf	DEBO2,W		; DEBO2 = b'00000001' if just pressed
	btfsc	STATUS,Z	; skip return if key 2 was not just pressed
	return			; *** exit 1: key 2 just pressed (NC)

				; ----------- key 1 test (left key)

	rlf	SCRATCH,F	; set C if key 1 pressed

	rlf	DEBO1,F		; propagate key 1 bit thru rotor
	bsf	STATUS,C	; set C to notify at exit that key 1 pressed
	decf	DEBO1,W		; DEBO1 = b'00000001' if just pressed
	btfsc	STATUS,Z	; skip return if key 1 was not just pressed
	return			; *** exit 2: key 1 just pressed (C)

	decfsz	TIMOUTL,F	; timeout lo counter...
	goto	GoKbd		; ... not yet zero: loop

	decfsz	TIMOUTH,F	; timeout hi counter...
	goto	GoKbd		; ... not yet zero: loop
				; *** exit 3: continue with timeout process

;*****************************************************************************
;* Power Off entry point
;* Wait until both keys off for 34 ms, and then switch power off.
;* PORTB,3, when low, switches the unit off.
;*****************************************************************************

Suicide
	call	KeysOff		; test keys off to avoud re-trigering
	clrf	PORTB		; pull PORTB,3 low to switch power off
	goto	$		; loop until power off

;*****************************************************************************
;* KeysOff
;* Loop until both keys off for 34 ms, then exit
;*  
;* Input variables: none
;* Output variables: TIMOUTH is cleared to 0
;*****************************************************************************

KeysOff
	clrf	TIMOUTH		; initialize pointer
BothOff
	btfsc	PORTA,2		; skip if key 1 on
	btfss	PORTA,3		; do not skip if key 2 on
	goto	KeysOff		; reinitialize ptr if any key on
	call	loop130		; loop 130us
	decfsz	TIMOUTH,F	; test pointer
	goto	BothOff		; loop 256 times to verify both keys off
	return			; both keys are off for at least 34 ms
;;*******
;*******  ANALYZER
;*******

;*****************************************************************************
;*  Pointer2
;*  Writes two measuring points for analyzer reference. First is TIMOUTL*10+5,
;*  then TIMOUTL is incremented by 2 and the second one is TIMOUTL*10+0
;*  
;*  Input variables: TIMOUTL will first be printed as TIMOUTL*10+5
;*  Output variables: TIMOUTL is incremented by 3 (pointer advanced by 30)
;*****************************************************************************

Pointer2
	call	Pointer1	; first two digits
	movlw	'5'		; third digit is 5 for odd pointer
	incf	TIMOUTL,F	; each incrementing advances pointer by 10
	call	AdvToCh		; advance ptr and print "5"
	call	Pointer1	; first two digits
	movlw	'O'		; third digit is 0 for even pointer
AdvToCh
	incf	TIMOUTL,F	; each incrementing advances pointer by 10
	goto	Char		; print 0 or 5

;*****************************************************************************
;*  Pointer1
;*  Writes blank, then symbol "^" and then converts TIMOUTL and prints
;*   as 2 digits.
;*  
;*  Input variables: TIMOUTL, bin value which will be printed as 2-digits
;*  Output variables: none
;*****************************************************************************

Pointer1
	call	Blank		; skip first 3 samples (one character)
	movlw	'^'		; "^" (pointing tool)
PrintTL
	call	Char		; print "^"
	movf	TIMOUTL,W	; TIMOUTL is the main pointer for...
	goto	Print255	; ...first two digits

;*****************************************************************************
;* Mode1Show
;* Write measuring points at row 2, and wait until keys are released.
;*  Then increment SHOWCOU in range 0...4 and continue to Draw subroutine
;* This command, which executes when key 2 is pressed in analyzer mode,
;*  while the cursor is on the number of 60-sample group, advances the
;*  pointer.
;* It continues to Draw routine.
;*****************************************************************************

Mode1Show
	call	Row2		; pointers are in row 2
	bcf	STATUS,C	; prepare for multiplying by 2
	rlf	SHOWCOU,W	; W=SHOWCOU*2
	movwf	TIMOUTL		; TIMOUTL=SHOWCOU*2
	rlf	TIMOUTL,F	; TIMOUTL-SHOWCOU*4
	addwf	TIMOUTL,F	; TIMOUTL-SHOWCOU*6

	call	Pointer2	; print 1st and 2nd pointer
	call	Pointer2	; print 3rd and 4th pointer

	call	KeysOff		; wait until key off
	call	PrintM1		; restore normal row 2

	incf	SHOWCOU,F	; advance number of groups displayed
	btfsc	SHOWCOU,2	; test if SHOWCOU=5: first test bit 2...
	btfss	SHOWCOU,0	; test if SHOWCOU=5: ...then test bit 0
	goto	Draw		; skip wrapping if SHOWCOU<5
	clrf	SHOWCOU		; SHOWCOU cycle in range 0...4

;*****************************************************************************
;* Draw
;* This subroutine writes 20 pseudographic characters in line 1 in analyzer
;*  mode. First the whole buffer is rotated 0/60/120/180/240 bit places to
;*  the right (if SHOWCOU=0/1/2/3/4, respectively) to adjust the sequence
;*  to be displayed to the start of the buffer. Then the string of 20
;*  3-bit groups is rotated right, and displayed as 20 special characters
;*  (codes 0-7), defined at program setup (at loop labeled GoGraph). Then
;*  the buffer is rotated again, to the total of 304 bit places, so the
;*  buffer contents is unmodified on exit.
;*  
;* Input variables:
;*         SHOWCOU, denotes which group of 60 samples will be displayed
;* Output variables: none
;*****************************************************************************

Draw
	movf	SHOWCOU,W	; prepare to rotate buffer SHOWCOU*60 times
	btfss	STATUS,Z	; avoid rotating if SHOWCOU=0
	call	Carusel		; rotate buffer SHOWCOU*60 times

	call	Row1		; samples must be written in row 1
	movlw	.20		; .20 characters to write
	movwf	CHARCOU		; CHARCOU is the main character counter
Go20Chars
	clrf	SCRATCH		; register for 3-bit code generation (0...7)
	call	RRBuf		; bit from buffer in C...
	rlf	SCRATCH,F	; ...bit from C in code register
	call	RRBuf		; bit from buffer in C...
	rlf	SCRATCH,F	; ...bit from C in code register
	call	RRBuf		; bit from buffer in C...
	rlf	SCRATCH,W	; ...bit from C in code register and in W
	call	CharNCC		; draw one char = 3 samples
	decfsz	CHARCOU,F	; 20 characters written?
	goto	Go20Chars	; no, loop

	movf	SHOWCOU,W	; prepare to rotate to total of 304 bits
	sublw	.4		; W=4-SHOWCOU
	btfss	STATUS,Z	; avoid rotating if SHOWCOU=0
	call	Carusel		; rotate buffer again to restore it
	call	RRBuf4		; four more times to get 304 times
	goto	Farm1		; done, go back to user interface

;*****************************************************************************
;* Carusel
;* This subroutine rotates BUFFER right W*60 times
;*    Note: if W=0, rotating will be performed 1024 times
;*  
;* Input variables:
;*         W, how many (*60) times the buffer will be rotated right (W>0)
;* Output variables: none
;*****************************************************************************

Carusel
	movwf	SCRATCH		; SCRATCH=W
	swapf	SCRATCH,F	; SCRATCH=W*16
	subwf	SCRATCH,F	; SCRATCH=W*15
RRLoop
	call	RRBuf4		; 4 rotates in every pass
	decfsz	SCRATCH,F	; done W*15*4 times?
	goto	RRLoop		; no, continue rotating BUFFER
	return			; done

;*****************************************************************************
;* RRBuf4 executes RRBuf 4 times
;* RRBuf rotates buffer (38 bytes=304 bits) right for one bit position. Bit
;*  STATUS,C is first loaded from the first bit in buffer, so rotating will
;*  be completely performed at 304 bits, not through C.
;*  
;* Input variables: none
;* Output variables: none
;*****************************************************************************

RRBuf4				; 4 times rotates right 38 bytes
	call	RRBuf2		; rotate BUFFER 2* and then again 2*
RRBuf2
	call	RRBuf		; rotate BUFFER once and then again once
RRBuf				; rotates right 38 bytes, bit in C at exit
	movlw	BUFFER+.37	; start with last byte to be rotated
	movwf	FSR		; FSR = pointing register for rotating
	movlw	.38		; 38 bytes total buffer
	movwf	COUNT		; byte counter

	bcf	STATUS,C	; C will be rotated into BUFFER+.37, so it...
	btfsc	BUFFER,0	; ...must be equal to bit BUFFER+0,0, to...
	bsf	STATUS,C	; ...perform non-destructive rotating
ByteLoop
	rrf	INDF,F		; byte rotated here
	decf	FSR,F		; let FSR point to next byte
	decfsz	COUNT,F		; COUNT is byte counter, done?
	goto	ByteLoop	; no, loop
	return			; here the output bit is in STATUS,C

;*****************************************************************************
;* Mode1Go
;* This is entry point for analyzer start command (symbol * ). After clearing
;*  line 1 of LCD, the program vectors to routines which handle different
;*  sampling rates. Three highest rates (1 MHz, 500 KHz and 228 KHz are
;*  sampled at individual routines (Get1MHz, Get500KHz and Get228KHz), and
;*  the remining rates at routine GetSlowClk. All those routines (except
;*  Get1MHz, which is location-sensitive (so it is at the very beginning of
;*  the program), are listed here.
;*  
;* Input variables: none
;* Output variables: none
;*****************************************************************************

Mode1Go				; *** ept: analyzer start

	call	ClrRow1		; clear LCD line 1 and turn LEDs off

	movf	RATE,W		; RATE = sample rate in range 0...15
	btfsc	STATUS,Z	; skip if sample rate>0
	goto	Get1MHz		; jump to individual routine if RATE=0

				; --- try 500 KHz rate
	movwf	SCRATCH		; SCRATCH = RATE
	decfsz	SCRATCH,F	; test if RATE=1
	goto	Try228KHz	; if not RATE=1, then try if RATE=2
				; if RATE=1, then drop to Get500KHz

;*****************************************************************************
;* Get500KHz
;* This subroutine fetches 304 samples at 2us rate (5 instruction cycles
;*  timing). 
;* Call Common initializes loop counter (COUNT) to 38*8=304 samples
;*  and FSR to point to BUFFER. It also presets T0SE bit depended on
;*  XTOX bit (in FLAG register)to enable proper edge detecting, as it
;*  will affect TMR0 state.
;* State of key 2 (Break) is tested while waiting for starting condition.
;*  
;* Input variables: none
;* Output variables: none
;*****************************************************************************

Get500KHz			; 5 t read cycle
	movlw	.38		; 38 bytes * 8 bits = 304 samples
	call	Common		; initialize COUNT, FSR, hi-imp out...
				; ...bit XTOX and T0SE bit
Edge500
	btfss	PORTA,3		; test status of key 2 and...
	goto	Break		; ...jump to Break handling routine if pressed

	movf	TMR0,W		; TMR0 = logic level edge detector
	btfsc	STATUS,Z	; test if there was egde...
	goto	Edge500		; ...and loop if not
	decf	FSR,F		; adjust pointer as it will be advanced...
				; ... before data write
Get500Loop
	rrf	PORTA,F ; <--	  move input status to C
	incf	FSR,F		; advance write pointer
	rrf	INDF,F		; write bit C in destination rotor
	movlw	.6		; initialize count for 6 bits
	movwf	DJNZ		; DJNZ = bit counter
Go6Bits
	rrf	PORTA,F ; <-- 6*  move input status to C 
	rrf	INDF,F		; write bit C in destination rotor
 	decfsz	DJNZ,F		; DJNZ = bit counter
	goto	Go6Bits		; loop if not yet 6 bits fetched

	rrf	PORTA,F ; <--     move input status to C 
	rrf	INDF,F		; write bit C in destination rotor
 	decfsz	COUNT,F		; COUNT = byte counter
	goto	Get500Loop	; loop if not yet 38 bytes fetched
	goto	Finished	; all bits fetched; go display them

;*****************************************************************************
;* Test if register SCRATCH reaches 0 after decrementing (this happens
;*  if RATE = 2), if so drop to Get228KHz else go to GetSlowClk
;*****************************************************************************

Try228KHz
	decfsz	SCRATCH,F	; test RATE status (SCRATCH=RATE-1)
	goto	GetSlowClk	; jump if not RATE=2

;*****************************************************************************
;* Get228KHz
;* This subroutine fetches 304 samples at 4.4us rate (11 instruction
;*  cycles timing). 
;*  Call Common304 initializes loop counter (COUNT) to 304 samples
;*  and FSR to point to BUFFER. It also presets T0SE bit depended on
;*  XTOX bit (in FLAG register)to enable proper edge detecting, as it
;*  will affect TMR0 state.
;* State of key 2 (Break) is tested while waiting for starting condition.
;*  
;* Input variables: none
;* Output variables: none
;*****************************************************************************

Get228KHz			; 11 t read cycle
	call	Common304	; initialize COUNT(lo), TIMOUTH(hi byte)...
				; ...FSR, hi-imp outbit XTOX and T0SE bit
Edge228
	btfss	PORTA,3		; test status of key 2 and...
	goto	Break		; ...jump to Break handling routine if pressed

	movf	TMR0,W		; TMR0 = logic level edge detector
	btfsc	STATUS,Z	; test if there was egde...
	goto	Edge228		; ...and loop if not
Go228
	goto	$+1		; 2     two extra cycles to make 11t
Go228B
	rrf	PORTA,F	; <---	; 1     move input bit to C
	rrf	INDF,F		; 1     write bit C in destination rotor

	decf	COUNT,W		; 1     COUNT = bit counter
	andlw	7		; 1     test if 8th pass...
	btfsc	STATUS,Z	; 1 (2) ...and skip if not
	incf	FSR,F		; 1 (0) ...else advance pointer

	decfsz	COUNT,F		; 1     COUNT = (lo byte) bit counter
	goto	Go228		; 2     loop if not yet = 0

	decfsz	TIMOUTH,F	; TIMOUTH = (hi byte) bit counter
	goto	Go228B		; this does not add extra cycles, as it
				; ...jumps after goto $+1
	goto	Finished	; all bits fetched; go display them

;*****************************************************************************
;* GetSlowClk
;* This subroutine fetches 304 samples at variable rate, depended on
;*  RATE (SCRATCH=RATE-3). Timing constant is loaded from lookup table
;*  located at DATA EEPROM (locations 30h-3ch).
;*  
;* Call Common304 initializes 16-bit loop counter to 304 samples
;*  (lo byte: COUNT=.304-.256, hi byte: TIMOUTH=.1)
;*  and FSR to point to BUFFER. It also presets T0SE bit depended on
;*  XTOX bit (in FLAG register)to enable proper edge detecting, as it
;*  will affect TMR0 state.
;*  
;* Rates 3-11 (100KHz-2.4KHz) have loop period of factor took from EEPROM
;*  table multiplied by 5 instruction cycles and adding 20 instruction
;*  cycles (Factor*5T+20T), and rates 12-15 (1.2KHz-40Hz) multilied the
;*  factor by 417 instruction cycles and adding 415 instruction cycle
;*  (Factor*417T+415T)
;*  
;*  RATE =  3, factor:   1, T/cycle:    25, Freq:  100   KHz
;*  RATE =  4, factor:   6, T/cycle:    50, Freq:   50   KHz
;*  RATE =  5, factor:   9, T/cycle:    65, Freq:   38.4 KHz
;*  RATE =  6, factor:  16, T/cycle:   100, Freq:   25   KHz
;*  RATE =  7, factor:  22, T/cycle:   130, Freq:   19.2 KHz
;*  RATE =  8, factor:  46, T/cycle:   250, Freq:   10   KHz
;*  RATE =  9, factor:  48, T/cycle:   260, Freq:    9.6 KHz
;*  RATE = 10, factor: 100, T/cycle:   520, Freq:    4.8 KHz
;*  RATE = 11, factor: 204, T/cycle:  1040, Freq:    2.4 KHz
;*  RATE = 12, factor:   5, T/cycle:  2500, Freq:    1   KHz
;*  RATE = 13, factor:  14, T/cycle:  6253, Freq:  400    Hz
;*  RATE = 14, factor:  59, T/cycle: 25018, Freq:  100    Hz
;*  RATE = 15, factor: 149, T/cycle: 62548, Freq:   40    Hz
;*  
;* State of key 2 (Break) is tested while waiting for starting condition.
;* LED P is turned on while sampling, to indicate sample period at slower
;*  rates, in which it appears to be visible.
;*  
;* Input variables: RATE, affects timing factor
;* Output variables: none
;*****************************************************************************

GetSlowClk
	decf	SCRATCH,W	; W = RATE-3
	addlw	.48		; rate table in data eeprom starts at addr .48

	call	AGet_EE		; get time constant from table in data eeprom
	movwf	SCRATCH		; time constant for rates 3-15 move to SCRATCH

	call	Common304	; initialize COUNT(lo), TIMOUTH(hi byte)...
				; ...FSR, hi-imp outbit XTOX and T0SE bit
EdgeSlow
	btfss	PORTA,3		; test status of key 2 and...
	goto	Break		; ...jump to Break handling routine if pressed

	movf	TMR0,W		; TMR0 = logic level edge detector
	btfsc	STATUS,Z	; test if there was egde... 
	goto	EdgeSlow	; ...and loop if not

	bsf	PORTB,5		; turn led P on to notify samplig in progress

GoSlow
	rrf	PORTA,F	; <---	; 1     move input bit to C
	rrf	INDF,F		; 1     write bit C in destination rotor

	decf	COUNT,W		; 1     COUNT = bit counter
	andlw	7		; 1     test if 8th pass...
	btfsc	STATUS,Z	; 1 (2) ...and skip if not
	incf	FSR,F		; 1 (0) ...else advance pointer

	btfsc	RATE,3		; 1 1   if bits 2 and 3 cleared, that...
	btfss	RATE,2		; 2 1   ...means that rate<12...
	goto	Not417		; - 2   ...if so, jump to short timing
				; ---------- 417 (RATE 12-15, bits 2,3 set)
	movf	SCRATCH,W	; 1       SCRATCH = timing constant
	movwf	CHARCOU		; 1       CHARCOU = loop counter
Loop417				;   \
	movlw	.82		; 1  \
	movwf	DJNZ		; 1   \
	call	Loop7		; 411  >  total cycles here 417*SCRATCH - 1
	nop			; 1   /
	decfsz	CHARCOU,F	; 1  / 2t at exit only
	goto	Loop417		; 2 /  0t at exit only

	movf	PORTB,W		; 1
	andlw	0dfh		; 1     reset bit 5 (LED P)
	btfss	COUNT,4		; 1 2
	iorlw	20h		; 1 0  set bit 5 (LED P) if COUNT,4 = 0
	movwf	PORTB		; 1    blink LED P while sampling ea 16th pass

	movlw	.78		; 1    constant for long timing
	goto	SameAs5		; 2    skip short timing
Not417				; ---------- 5 (RATE 3-11)
	goto	$+1		; 2    waist 2 cycles
	movf	SCRATCH,W	; 1    SCRATCH = timing constant
SameAs5				; ---------- 417 and 5 (RATE 3-15)
	movwf	DJNZ		; 1    DJNZ = loop counter
	call	GoLoop		; short timing: 3T+5T*SCRATCH, long: 393T

	decfsz	COUNT,F		; 1    COUNT = (lo byte) bit counter
	goto	GoSlow		; 2    loop if not yet = 0

	decfsz	TIMOUTH,F	; TIMOUTH = (hi byte) bit counter
	goto	GoSlow		; this adds 2T extra only once,after 48th pass
Finished
	clrf	SHOWCOU		; reset pointer to 1st group of 60 samples
	goto	Draw		; all bits fetched; display them

;*****************************************************************************
;* Common304
;* This subroutine initializes low byte loop counter (COUNT) to 304 samples
;*  (lo byte: COUNT=.304-.256, hi byte: TIMOUTH=.1+1)
;*  and FSR to point to BUFFER. It also presets T0SE bit depended on
;*  XTOX bit (in FLAG register)to enable proper edge detecting, as it
;*  will affect TMR0 state.
;* Entry point Common allows subroutines Get1MHz and GetSlowClk to preset
;*  COUNT to another values.
;*  
;* Input variables:
;*    For entry point COMMON, register W is placed in COUNT
;* Output variables:
;*    COUNT is initialized to number of loop passes (W or low byte of 304)
;*    TMR0 is cleared
;*    T0SE and PORTB,2 are copied from FLAG,XTOX
;*****************************************************************************

Common304
	movlw	.2		; hi byte = 2 for regular lo byte value...
				; ...plus extra 256 passes
	movwf	TIMOUTH		; hi byte loop counter for 304 passes
	movlw	.304-.256	; lo byte value for 304 passes
Common
	movwf	COUNT		; COUNT = loop counter
	movlw	BUFFER		; first byte of destination
	movwf	FSR		; FSR = destination pointer

	movlw	28h		; initialize T0SE fot low-to-hi transition
	bcf	PORTB,2		; clear hi-imp out if expecting rising edge

	btfsc	FLAG,XTOX	; test which edge was slctd as start condition
	goto	ToOption	; and skip falling edge if rising selected

	movlw	38h		; initialize T0SE fot hi-to-low transition
	bsf	PORTB,2		; set hi-imp out if expecting falling edge
ToOption
	bsf	STATUS,RP0	; select bank 1 of registers
	movwf	OPTION_REG	; set/reset TOSE
	bcf	STATUS,RP0	; reselect bank 0
	clrf	TMR0		; initialize TMR0 as edge detector
	return			; finished

;*****************************************************************************
;* PrintM1
;* Print string at line 2 in analyzer mode.
;* At pos 0, rate in format XX[[.]X][M|K]Hz/XX[[.]X][u|m]s is printed. Those
;*  values are picked from table located in Data EEPROM, locations 0-2Fh.
;*  Register CHARCOU is used to fill blanks up to pos 13 in line 2, to
;*  disable phantom characters appearance when changing rate from some
;*  long-string to short-string value.
;* Symbol for starting or rising edge for starting event is written at pos
;*  13, symbol "*" for start command at pos 15 and the number of group
;*  displayed at pos. 17.
;*  
;* Input variables:
;*    RATE will be printed in pos 0, row2
;*    bit FLAG,XTOX affects the printed symbol of rising/falling edge
;*    SHOWCOU (number of group displayed) is printed as numeric (+1)
;* Output variables: none
;*****************************************************************************

PrintM1
	call	Row2		; move cursor to row 2

	movlw	.13		; initialize counter for 13 char fix format
	movwf	CHARCOU		; CHARCOU = character counter

	movf	RATE,W		; W = RATE
	addwf	RATE,W		; W = 2 * RATE
	addwf	RATE,W		; W = 3 * RATE (ea rate has 3 bytes in table)

	call	AGet_EE		; get first byte from table in data eeprom
	movwf	TIMOUTL		; TIMOUTL = 1st byte from table
	swapf	TIMOUTL,W	; move to bits 0-3 as bits for freq are 4-7

	call	Get_EE		; get second byte from table in data eeprom

	btfsc	TIMOUTL,6	; bit 6 = decimal point for frequency
	bsf	FLAG,DP		; set decimal point bit if bit 6 set

	call	Print3		; display sampling frequency

	movlw	'M'		; for "MHz" display
	movf	RATE,F		; if RATE=0...
	btfss	STATUS,Z	; ...then let it be MHz
	movlw	'K'		; for "KHz" display
	btfsc	TIMOUTL,7	; bit 7 = KHz or MHz, skip 1st char if cleared
	call	Char		; print "M" or "K" if bit 7 set
	movlw	TxtHz-1+1	; for "Hz" display
	call	Write		; print "Hz"

	movf	TIMOUTL,W	; TIMOUTL = 1st byte from table

	call	Get_EE		; get second byte from table in data eeprom

	btfsc	TIMOUTL,2	; bit 2 = decimal point for period
	bsf	FLAG,DP		; set decimal point bit if bit 2 set

	call	Print3		; display digits for period

	movlw	0e4h		; Greek "micro"
	btfss	TIMOUTL,3	; if bit 3 set, let it be "micro"
	movlw	'm'		; ...if not, convert to "milli" (m)
	call	Char		; print "micro" or "m"
	movlw	's'		; s stands for seconds
	call	Char		; print "s"
XtraChar			; adds extra (CHARCOU) blanks
	movlw	' '		; print blank to overprint previous string
	call	CharNCC		; print blank without affecting CHARCOU
	decfsz	CHARCOU,F	; CHARCOU=character counter
	goto	XtraChar	; loop if not yet pos 13

				; ----- here follows starting condition symbol
	movlw	1		; symbol of rising edge
	btfss	FLAG,XTOX	; let it be rising if XTOX set
	movlw	4		; symbol of falling edge
	call	CharBl		; print symbol and blank
KaoAna				; ----- here follows "start" ("*") symbol
	movlw	'*'		; "start" symbol
	call	CharBl		; print "start" symbol and blank
				; ---- here follows number of 60 samples group
	incf	SHOWCOU,W	; SHOWCOU =  number of 60 samples group
	call	Num		; print it (incremented by 1)
	call	Blank		; print blank
Arrow
	movlw	7eh		; 7Eh = right arrow in LM032L character set
	goto	Char		; print arrow in rightmost pos

;*****************************************************************************
;* AGet_EE
;* Get_EE
;*  This is routine for reading from Data EEPROM. Writing to BIN4+0 and
;*  BIN4+1 is also integrated here, as those variables are used for bin to
;*  decimal convertsion.
;*  
;* Input variables: W, data address at AGet_EE
;* Output variables: BIN4, binary data for rate display from DATA EEPROM
;*****************************************************************************

AGet_EE
	movwf	EEADR		; initialize eeprom address pointer
Get_EE
	andlw	3		; hi byte BIN4 for freq display
	movwf	BIN4+1		; BIN4+1 = hi byte for range 0...999

	bsf	STATUS,RP0	; select bank 1 of registers
	bsf	EECON1,RD	; set handshaking bit for data eeprom read
	bcf	STATUS,RP0	; reselect bank 0
	movf	EEDATA,W	; reading from data eeprom
	incf	EEADR,F		; advance address pointer for future reading
	movwf	BIN4		; lo byte BIN4 for freq display
	bcf	FLAG,DP		; clear decimal point flag
	return			; finished

;*****************************************************************************
;* ClrRow1
;* SameAs20
;* This subroutine clears line 1 of LCD and switches off LEDs. Entry point
;*  SameAs20 allows clearing some other number of character positions
;*  starting from line 1 of LCD. Alll LEDs are turned off, flags for LEDs
;*  also Cursor pointer of LCD is restored to first pos of line 1 at exit of
;*  subroutine.
;*  
;* Input variables:
;*          entry point ClrRow1:  none
;*          entry point SameAs20: W=number of characters to be cleared
;* Output variables:
;*          CHARCOU is decrementeted by the number of cleared characters
;*          SHOWCOU is cleared to 0
;*****************************************************************************
;* Row1
;*  This subroutine moves cursor to pos 0 of row 1 on LCD.
;*  
;*  Input variables: none
;*  Output variables: none
;*****************************************************************************

ClrRow1
	movlw	.20		; 20 spaces (one row) to write
SameAs20
	movwf	SHOWCOU		; SHOWCOU = space counter
	call	Row1		; move cursor to row 1
LoopClD
	call	Blank		; print one space
	decfsz	SHOWCOU,F	; SHOWCOU = space counter
	goto	LoopClD		; loop if not yet 20 spaces
 
	movlw	1fh		; bits 7,6,5 (LED flags) reset
	andwf	FLAG,F		; turn LED flags off
Row1
	movlw	080h		; command for line 1
	goto	WrComL		; go write command
;;*******
;*******  SERIAL CODE RECEIVER
;*******

;*****************************************************************************
;* Mode2Show
;* This subroutine advances SHOWCOU in range 0...5, and then continues to
;*  subroutine Show2
;*****************************************************************************

Mode2Show
	incf	SHOWCOU,F	; advance SHOWCOU
	btfsc	SHOWCOU,2	; bits 1 and 2 will be set if SHOWCOU...
	btfss	SHOWCOU,1	; ...is equal to 5...
	goto	Show2		; ...if not, skip clearing
	clrf	SHOWCOU		; cycle show counter in range 0...5

;*****************************************************************************
;* Show2
;* This subroutine prints the 7 bytes of Buffer (+0, +7, +14, +21, +28 or
;*  +35) in hex mode at line 1, and the same bytes in ASCII at line 2. ASCII
;*  representation is with bit 7 reset, and non-printables (<20h) are printed
;*  as dots
;*  
;* Input variables:
;*     SHOWCOU, denotes which group of 7 bytes will be displayed
;* Output variables:
;*     CHARCOU is decremented by the number of characters printed
;*****************************************************************************

Show2
	movlw	BUFFER-1	; source pointer for reading -1
	btfsc	SHOWCOU,2	; if bit 2 of SHOWCOU set...
	addlw	.28		; ...then add 28 (4 groups) to pointer
	btfsc	SHOWCOU,1	; if bit 1 of SHOWCOU set...
	addlw	.14		; ...then add 14 (2 groups) to pointer
	btfsc	SHOWCOU,0	; if bit 0 of SHOWCOU set...
	addlw	.7		; ...then add 7 to pointer
	movwf	FSR		; FSR on (1st byte pos)-1 to display

	call	Row1		; move cursor to row 1
	movlw	.7		; bytes to display
	movwf	SCRATCH		; SCRATCH = byte display counter
Hex7
	incf	FSR,F		; advance pointer (it was x-1 at beginning)
	swapf	INDF,W		; move hi nibble first to display 1. hex digit
	call	HexDigit	; display 1st digit in hex mode
	movf	INDF,W		; move lo nibble to display second hex digit
	call	HexDigit	; display 2nd digit in hex mode
	call	Blank		; blank after hex number

	decfsz	SCRATCH,F	; SCRATCH = byte counter
	goto	Hex7		; loop if not yet 7 bytes

	movf	FSR,W		; FSR = read pointer
	addlw	-.7		; restore it for ASCII mode printing
	movwf	FSR		; FSR on (1st byte pos)-1 to display

	call	Row2		; move cursor to row 2
	movlw	.7		; bytes to display
	movwf	SCRATCH		; SCRATCH = byte display counter
Ascii7
	incf	FSR,F		; advance pointer (it was x-1 at beginning)
	movf	INDF,W		; read byte
	andlw	7fh		; reduce ascii representation to 7 bits

	addlw	-20h		; test if byte < 20h
	addlw	20h		; restore previous value
	btfsc	STATUS,C	; C is set if byte < 20h
	movlw	0a5h		; represent non-printables as dots (0a5h=dot)

	call	Char		; display ascii char

	decfsz	SCRATCH,F	; SCRATCH = byte counter
	goto	Ascii7		; loop if not yet 7 bytes

	goto	Farm2		; go back to user interface

;*****************************************************************************
;* Mode2Go
;* This is entry point for Start command in mode 2 (serial code receiver)
;*  Line 1 and first 7 positions (ASCII chars) of line 2 on LCD is cleared.
;*  Here the buffer is cleared and a sequence of 42 bytes are received and
;*  written to buffer. Manual break (key 2) jumps to Break handling point.
;* This protocol is used: High start bit, 7 or 8 data (true) bits, 0 or
;*  1 parity bit (not written to memory) and 1 low stop bit (not tested
;*  for validity). If RXBITS,2 is set then the input is inverted.
;* Baud rates 1200-115200 are supported.
;* Note: no receive errors are detected nor indicated.
;*  
;* Input variables:
;*  RXRATE (range 0...7), which affects timing loaded from table BaudRate
;*  RXBITS, bits 0-2 significant: bit0=parity, bit 1=7/8 bits, bit 2=inverse
;*  
;*  Output variables:
;*  Buffer (42 bytes) loaded with bytes received, all unreceived bytes
;*  represented as 00s.
;*****************************************************************************

Mode2Go
	bcf	PORTB,2		; clear hi-imp probe output...
	btfsc	RXBITS,2	; ...let it be low if polarity bit cleared
	bsf	PORTB,2		; set hi-imp output if polarity bit set
	call	ClrBuf		; clear wholw buffer
	movlw	.47		; .47 blanks to clear displayed values
	call	SameAs20	; clear displayed HEX and ASCII values

	movlw	BUFFER		; start of buffer...
	movwf	FSR		; ...assigned to destination pointer

	movf	RXRATE,W	; RXRATE = selected rate in range 0...7
	addlw	BaudRate	; add to timing constant table offset
	call	PclSub		; get rate to W
RX42Bytes
	movwf	DJNZ		; baud rate timing factor to timing counter

	clrf	COUNT		; this is to preset bit counter to 8 and...
	bsf	COUNT,3		; ...not to disturb W

	btfss	RXBITS,0	; RXBITS,0 is set if 7 bits selected
	btfss	RXBITS,1	; RXBITS,1 is set if parity bit selected
	incf	COUNT,F		; if not(RXBITS and 3 = 2) then COUNT=9

	btfsc	RXBITS,0	; RXBITS,0 is set if 7 bits selected
	btfsc	RXBITS,1	; RXBITS,1 is set if parity bit selected
	decf	COUNT,F		; if not(RXBITS and 3 = 1) reduce to 8 or 7

	btfss	RXBITS,2	; RXBITS,2 is set if inverse polarity slctd
	goto	GetSp2		; jump to true polarity if RXBITS,2 is cleared

GetSp1				; ----- inverse rx
	btfss	PORTA,4		; test input status...
	goto	GetSp1		; ...and loop if still low
GetStart1
	btfss	PORTA,3		; test status of key 2...
	goto	BrkRS		; ...and jump to Break if presseed
	btfsc	PORTA,4		; test input status...
	goto	GetStart1	; ...and loop if still high
	goto	StartFound	; falling edge detected: start reception

GetSp2				; ----- true rx
	btfsc	PORTA,4		; test input status...
	goto	GetSp2		; ...and loop if still high
GetStart2
	btfss	PORTA,3		; test status of key 2...
	goto	BrkRS		; ...and jump to Break if presseed
	btfss	PORTA,4		; test input status...
	goto	GetStart2	; ...and loop if still low
				; rising edge detected: start reception
StartFound
				; 2-9 t from starting edge
HalfBit
	bsf	PORTB,5		; 1*W   led P on
	nop			; 1*W   waist one cycle
	decfsz	DJNZ,F		; 1*W   DJNZ = timing constant counter
	goto	HalfBit		; 2*W   loop if not yet half bit timing passed

RX8Bits
	movwf	DJNZ		;  1        move timing constant to counter
OneBit
	call	Waist8T		; 8 8 * W   waist 8 t
	decfsz	DJNZ,F		; 1 2 * W   DJNZ = timing constant counter
	goto	OneBit		; 2 - * W   loop if not yet 1 bit time passed

	call	Waist6T		;  6        waist 6 t

	rrf	PORTA,F		;  1   <--- move input status to C
	rrf	INDF,F		;  1        place C in input rotor

	decfsz	COUNT,F		;  1        COUNT = bit counter
	goto	RX8Bits		;  2 total 22t + (w-1) * 11
				;-------------------- received byte @ INDF
	btfsc	RXBITS,0	; test if parity bit selected...
	rlf	INDF,F		; ...and discard parity bit if received

	btfss	RXBITS,2	; test if inverse polarity selected...
	comf	INDF,F		; ...and complement if true parity

	bcf	STATUS,C	; clear 8th bit for 7-bit mode
	btfsc	RXBITS,1	; test if 7-bit mode selected...
	rrf	INDF,F		; ...and rotate 8th (zero) bit if 7 bits slctd

	incf	FSR,F		; advance destination pointer
	btfsc	FSR,6		; bits 4 and 6 will both be set if...
	btfss	FSR,4		; ... end of buffer+1 reached
	goto	RX42Bytes	; loop if not yet FSR=50h
	goto	Show2		; over: go show received bytes
;;*******
;*******  FREQUENCY COUNTER
;*******

;*****************************************************************************
;* GoPresc
;* Prescaler factor (variable PRESC, in range 0...3) is advanced
;*  (executes when key 2 is pressed in frequency counter mode)
;*****************************************************************************

GoPresc
	incf	PRESC,F		; advance prescaler
	bcf	PRESC,2		; and cycle prescaler in range 0...3

;*****************************************************************************
;* FreqEp
;* Frequency counter entry point.
;* Subroutine WrParam does this: Displays message "Frequency" in row 1,
;*  counter range (taken from table RangeTab) and resolution (taken from
;*  table PrescTab) in row2.
;* LEDs are turned OFF, and the main counter (BIN4, 4 bytes) cleared. The
;*  following is used to count pulses:
;* State of TMR0 is written to BIN4+0 and sequencialy tested, and when the
;*  bit 7 of current value detected to be 0 and the previous one was 1, the
;*  overflow is considerd. In that case, the state of BIN4+1 is advanced,
;*  extended to BIN4+2. After 500 ms, the 32- bit value of BIN4 is shifted
;*  left in a total of PRESC+2 times, to get multiplication by 4, 8, 16 or
;*  32. Then BIN+4 (4 bytes) is converted to ASCII and printed on LCD.
;* This routine does not call keyboard routine, as the accurate timing of
;*  500 ms must be generated for counting (high count register is CHARCOU,
;*  low count SHOWCOU). Instead of this, there is the individual routine for
;*  key 1 and key 2 test, and also the countdown timer for automatic power
;*  off (registers TIMOUTL, TIMOUTH).
;* If key 1 is  pressed, mode 1 (analyzer) is entered. If key 2 is
;*  pressed, prescaler value is advanced.
;*  
;* Note: code from label "Loop500A" to comment "; 500 ms timeout" is real
;*  time code. If the number of cycles is changed, then the literals
;*  0f4h+1 and 24h+1 written to CHARCOU and SHOWCOU must be readjusted.
;*  
;* Input variables: PRESC (affects prescaler factor)
;* Output variables: none
;*****************************************************************************


FreqEp				; mode 3: frequency counter
	call	WrParam		; print "Frequency" and "xxMHz/Rxx"
	call	KeysOff		; test both keys off for 34 ms and
				; initialize 8 min auto off sequence
Count500
	movlw	0d2h+REL	; right arrow position
	call	WrComL		; move cursor on right arrow 

	clrf	BIN4+1		; clear next counter byte
	clrf	BIN4+2		; clear next counter byte
	clrf	BIN4+3		; clear next counter byte
	clrf	SCRATCH		; initialize TMR0 overflow detector

	movlw	0f4h+1		; high loop counter for 500 ms
	movwf	CHARCOU		; CHARCOU = hi byte counter
	movlw	24h+1		; 0f424h = .62500 cycles = 1250000 T = 500 ms
	movwf	SHOWCOU		; SHOWCOU = lo byte counter

	movf	PRESC,W		; PRESC = prescaler factor selected
	addlw	20h		; for PRESC 0,1,2,3   w = 20h,21h,22h,23h
	call	ToOption	; here is clrf TMR0 also
Loop500A
	btfss	PORTA,3		; 2   test key 2 status
	goto	GoPresc		; -   and jump to "prescaler change" if hit
Loop500
	movf	TMR0,W		; 1 1 1   the only place where TMR0 is read
	movwf	BIN4		; 1 1 1   rtcc ---> freq0

	rlf	BIN4,W		; 1 1 1   carry <--- TMR0.7
	rlf	SCRATCH,F	; 1 1 1   rotor <--- carry

	movf	SCRATCH,W	; 1 1 1   SCARTCH = TMR0 overflow detector
	andlw	3		; 1 1 1   isolate first 2 bits for edge detect
	xorlw	2		; 1 1 1   00000000 if 1 <--- 0
	
	btfss	STATUS,Z	; 1 2 2 |     skip if TMR0 overflow
	goto	NotOvf1		; 2 - - |     if nz
				;	| 5T any case
	incf	BIN4+1,F	; - 1 1 |     nsb
NotOvf1	btfsc	STATUS,Z	; 2 2 1 |     skip MSB advane if not overflow
	incf	BIN4+2,F	; - - 1 |     msb

	movlw	Head4-1		; 1   initialize "Battery" message
	btfss	PORTA,2		; 2   test key 1 status and...
	goto	Mode4		; -   got shortcut to mode 4 if pressed

	decfsz	SHOWCOU,F	; 1 (2)   lo loop counter
	goto	Loop500A	; 2 (-)   20T total

	decfsz	CHARCOU,F	;   (1)   hi loop counter	
	goto	Loop500		;   (2)   20T total

				; --- 500 ms timeout here
	incf	PRESC,W		; prepare for x2 multiply: PRESC incremented
	movwf	CHARCOU		; CHARCOU = multiply factor counter
	incf	CHARCOU,F	; and prescaler constant incremented again
ShLoop				; BIN4 = BIN4 * 2 total (PRESC+2) times
	bcf	STATUS,C	; clear C to allow clean x2 multiply
	rlf	BIN4, F		; low byte x2 multiply
	rlf	BIN4+1,F	; next byte x2 multiply
	rlf	BIN4+2,F	; next byte x2 multiply
	rlf	BIN4+3,F	; highest byte x2 multiply
		
	decfsz	CHARCOU,F	; CHARCOU = multiply factor counter
	goto	ShLoop		; loop if not yet PRESC*2 times multiplied

	movlw	8ah		; frequency display position
	call	WrComL		; cursor to freq display position
	call	Print8		; print the frequency in 8-digit ASCII

	decfsz	TIMOUTL,F	; TIMOUTL = lo byte auto power off counter
	goto	Count500	; inner loop
	bsf	TIMOUTL,2	; TIMOUTL is initialized to 4 passes instead
				; of 256, 4*256*500ms=512s=8.5min approx

	decfsz	TIMOUTH,F	; TIMOUTH = hi byte auto power off counter
	goto	Count500	; loop if not yet 8.5 min

	goto	Suicide		; 8.5 min timeout - go switch power off
;;*******
;*******  BINARY TO ASCII CONVERSION
;*******

;*****************************************************************************
;* Headline
;* Clears SHOWCOU (Headline2 skips this), Clears LCD, prints right
;*  arrow @ last pos of row 2 and prints message addresed by W+1 at page 0.
;* Terminator is last character with bit 7 set.
;* 
;* Input variables: W+1 addresses string (on page 0) to be printed
;* Output variables:
;*    CHARCOU is decremented by the number of characters printed
;*****************************************************************************

Headline
	clrf	SHOWCOU		; initialize show group counter
Headline2
	movwf	SCRATCH		; move input parameter to SCARTCH
	movlw	.59		; .59 characters to clear
	call	SameAs20	; clear all but right arrow
	goto	GoWrite		; print headline message on LCD

;*****************************************************************************
;* WrParam
;* Prints message "Frequency" in line 1 and parameters for frequency counter
;*  in line 2.
;* Text XXMHz/RYY is printed, where XX is taken from RangeTab, and YY from
;*  PrescTab.
;*  
;* Input variables:
;*    PRESC, affects displayed frequency range and resolution
;* Output variables:
;*    CHARCOU is decremented by the number of characters printed
;*****************************************************************************
;* Print255
;* Entry point Print255 converts 8-bit binary value (<100) in BIN4 to
;*  2-digit ASCII and prints it on LCD, without decimal point. Leading zeros
;*  are printed.
;*  
;* Input variables: W, binary number (0-99) to be converted and printed
;* Output variables:
;*    CHARCOU is decremented by the number of characters printed
;*****************************************************************************
;* Print3
;* Entry point Print3 converts 16-bit binary value (<1000) in BIN4 to 2- or
;*  3-digit ASCII and prints it on LCD. Leading zero is skipped only if value
;*  is <100. Decimal point is printed between tens and ones if FLAG,DP is
;*  set, otherwise decimal point is omited.
;*  
;* Input variables: BIN4 (2 bytes, LSB first, in range 0-999) binary number
;*                       to be converted and printed
;* Output variables:
;*    CHARCOU is decremented by the number of characters printed
;*****************************************************************************
;* PrintBR
;* Entry point PrintBR does the same as PRINT3, but the low byte value is
;*  in W instead in BIN4+0. This is used for baud rate display.
;*  
;* Input variables: W, BIN+1 (2 bytes, LSB in W, MSB in BIN+1, in range
;*  0-999) binary number to be converted and printed
;* Output variables:
;*    CHARCOU is decremented by the number of characters printed
;*****************************************************************************

WrParam				; print xxMHz/Rxx
	movlw	Head3-1		; address of message "Frequency"
	call	Headline2	; print message
	call	Row2		; move cursor to row 2

	movlw	RangeTab	; offset of max frequency range table
	call	Presc255	; print max frequency range

	movlw	TxtHz-1		; address of message "MHz/"
	call	Write		; print message
	movlw	'R'		; "R" stands for "Resolution"
	call	Char		; print "R"

	movlw	PrescTab	; offset of resolution table
Presc255
	addwf	PRESC,W		; add PRESC to offset
	call	PclSub		; read value from table
Print255
	bcf	FLAG,DP		; clear decimal point enable flag
	clrf	BIN4+1		; clear hi byte (allow range 00-99)
PrintBR
	movwf	BIN4		; allow W as input parameter
Print3				; convert BIN4(16) and print 3 decimal digits
	clrf	BIN4+3		; clear hi byte
	clrf	BIN4+2		; clear next byte
	clrf	CMP4+3		; clear hi byte of temporary register
	clrf	CMP4+2		; clear next byte of temporary register
	clrf	CMP4+1		; clear next byte of temporary register

	movlw	.100		; first digit constant
	call	Times		; how many times it goes in BIN4+1 and BIN4?
	btfss	STATUS,Z	; skip printing if it goes zero times
	call	Num		; print digit (hundreds) if W>0

	movlw	.10		; second digit constant
	call	Times		; how many times it goes in BIN4?
	call	Num		; print digit (tens)

	movlw	'.'		; decimal point
	btfsc	FLAG,DP		; test decimal point flag and skip if reset
	call	Char		; print decimal point if DP set
	goto	NumBin4		; print last digit (ones)

;*****************************************************************************
;* Times
;* Counts how many times CMP4 (32-bit value) "goes" in BIN4 (32-bit value).
;* BIN4 is sequentialy subtracted by CMP4 and counter COUNT advanced. When
;*  borrow is detected, BIN4 is restored to the last positiv value (by ADDing
;*  CMP4 again), COUNTer decremented and written to W.
;*  
;* Input variables: CMP4 (32-bit value), BIN4 (32-bit value)
;* Output variables:
;*    BIN4 (32-bit value) modified to mod(CMP4)
;*    W (in range 0...9) = BIN4 (32-bit value) / CMP4 (32-bit value)
;*****************************************************************************

Times
	movwf	CMP4		; place input parameter in CMP4 for comparing
	clrf	COUNT		; clear result counter
GoTD
	incf	COUNT,F		; advance result counter
	call	Sub4		; BIN4=BIN4-CMP4  nc if result <0
	btfsc	STATUS,C	; test did it "go"?
	goto	GoTD		; loop if so
	call	Add4		; BIN4=BIN4+CMP4  c set if ovf
	decf	COUNT,W		; W = how many times CMP4 goes in BIN4 (32bit)
	return			; result in W

;*****************************************************************************
;* Print8
;* This subroutine converts 32-bit value in BIN4 (low byte first), to
;*  8-digit ASCII and prints it on LCD. Leading zeros are printed as blanks.
;* Table DecTab (21 words, must be at page 0 if PCLATH=0) is used in conversion.
;*
;* Input variables: BIN4 (32-bit value, <.100,000,000)
;* Output variables:
;*    CHARCOU is decremented by the number of characters printed
;*****************************************************************************

Print8				; bin2dec conv BIN4(32), print 8 decimal digits
	movlw	DecTab-1	; inici tab ptr
	movwf	SCRATCH		; SCRATCH = tab ptr
	bcf	FLAG,RIPPLE	; zeros initialy printed as blanks, until
				; ...first non-zero appears
Cif7
	clrf	CMP4+3		; clear CMP4+3, it is =0 in all cases
	call	PclSub2		; get constant from table
	movwf	CMP4+2		; load decimal const from table in CMP4+2
	call	PclSub2		; get constant from table
	movwf	CMP4+1		; load decimal const from table in CMP4+1
	call	PclSub2		; get constant from table
	call	Times		; how many times CMP4 goes in BIN4?
	call	NZNum		; print if w>0 or RIPPLE=1, else blank

	movf	SCRATCH,W	; SCRATCH = table pointer
	addlw	.237-DecTab	; test if end of table
	btfss	STATUS,C	; C set if end of table
	goto	Cif7		; if not yet end of table loop (will be 7x)
NumBin4
	movf	BIN4,W		; last digit is in BIN4
	goto	Num		; last digit must be printed unconditionally

;*****************************************************************************
;* Sub4
;* Subtract CMP4 (32-byte value) from 32-bit value in BIN4 (low byte first)  
;* This is performed as adding of negative value of CMP4. Negating is
;*  performed as comlementing and incrementing by 1.
;* Note: Incrementing by 1 is performed on least significant byte only,
;*  without 32-bit extension, for code space saving. This will not cause
;*  error in this case, as the number of all possible values for CMP4+0 is
;*  limited and none of them is equal to 0FFh before incrementing
;*  (all possible values are taken from table DecTab, and are: 0ah, 64h,
;*  0e8h, 10h, 0a0h, 40h and 80h, and their negative values).
;*  However, this is valid if this subroutine is used for decimal
;*  conversion only, and if it is used for some other application,
;*  extension to 32-bit should be added after incrementing.
;*
;* Input variables: BIN4 (32-bit value), CMP4 (32-bit value)
;* Output variables:
;*    BIN4 (32-bit value)
;*    STATUS,C denotes the sign of result: if cleared, output value
;*             is negative (there is borrow)
;*  
;*  Note: Entry points Waist8T and Waist6T are used only by some real-time
;*  routines, in that case the instructions are dummy
;*****************************************************************************

Sub4				; 32-bit sub: BIN4 = BIN4 - CMP4
				; NC if result<0
	call	NegCmp		; negate CMP (32 bits) first
	call	Add4		; add as negative value
NegCmp
	comf	CMP4+0,F	; complement low byte
Waist8T
	comf	CMP4+1,F	; complement next byte
	comf	CMP4+2,F	; complement next byte
Waist6T
	comf	CMP4+3,F	; complement high byte
	incf	CMP4+0,F	; neg = complement + 1
				; (no need to test overflow here, it will
				; ...never reach 0 after incrementing)
	return			; finished

;*****************************************************************************
;* Add4
;* Add CMP4 (32-byte value) to BIN4 (32-byte value). Four-instruction groups
;*  (movf-btfsc-incfsz-addwf) is used istead of non-existing ADD WITH CARRY.
;* Input variables: BIN4 (32-bit value), CMP4 (32-bit value)
;* Output variables: BIN4 (32-bit value), 33th bit in STATUS,C
;*****************************************************************************

Add4				; 32-bit add: BIN4 = BIN4 + CMP4
	movf	CMP4,W		; low byte
	addwf	BIN4,F		; low byte add

	movf	CMP4+1,W	; next byte
	btfsc	STATUS,C	; skip to simple add if C was reset
	incfsz	CMP4+1,W	; add C if it was set
	addwf	BIN4+1,F	; next byte add if NZ

	movf	CMP4+2,W	; next byte
	btfsc	STATUS,C	; skip to simple add if C was reset
	incfsz	CMP4+2,W	; add C if it was set
	addwf	BIN4+2,F	; next byte add if NZ

	movf	CMP4+3,W	; high byte
	btfsc	STATUS,C	; skip to simple add if C was reset
	incfsz	CMP4+3,W	; add C if it was set
	addwf	BIN4+3,F	; high byte add if NZ

	return			; finished

;*****************************************************************************
;* PclSub is used for indirect addressing
;* PclSub1 uses SCRATCH instead of W as input parameter
;* PclSub2 advances pointer SCRATCH before executong
;*  
;* Note: PCLATH=0 in all cases. So all tables pointed by this routine
;*  are on page 0
;*****************************************************************************

PclSub2
	incf	SCRATCH,F	; advance table pointer
PclSub1
	movf	SCRATCH,W	; move table pointer to W
PclSub
	movwf	PCL		; jump to address pointed by PCLATH,W

;*****************************************************************************
;* ClrBuf
;* ClrRam
;* Subroutine ClrBuf clears BUFFER (42 bytes)
;* Entry point ClrRam allows some other start point for claring. It clears
;*  internal RAM from address in W to the location 7Fh.
;*  (locations 50h-7Fh, which do not exist in 16F84, are dummy).
;*
;* Both entry points continue to disabling Enable signal for LCD and
;*  33.8 ms timing loop
;*
;* Input variables:
;*   W is the start address if area to be cleared (ClrRam entry point only)
;* Output variables: none
;*****************************************************************************
  
ClrBuf
	movlw	BUFFER		; get start address of buffer
ClrRam
	movwf	FSR		; FSR = destination pointer for clearing
Zeros
	clrf	INDF		; clear one byte
	incf	FSR,F		; advance dest pointer
	btfss	FSR,7		; test if end of RAM...
	goto	Zeros		; ...if not, loop - else move LEDs

;*****************************************************************************
;*  Entry point DisEna30: Remove enable and discharge signal, and
;*                          refresh LEDs. Then loop 33.8 ms
;*  Entry point Wait30: Loop 33.8 ms
;*  Input variables: none
;*  Output variables: none
;*****************************************************************************

DisEna30
	call	DisEna		; disable discharging output signal
Wait30
	clrf	COUNT		; COUNT = time loop counter, to waist 33.8 ms
GoWait30
	call	loop130		; waist 130 us
	decfsz	COUNT,F		; COUNT = time loop counter
	goto	GoWait30	; loop if not yet 256 passes
	return			; timing over

;*****************************************************************************
;* NZNum
;* Num
;* Print numeric value in W (in range 0...9) on LCD. If FLAG,RIPPLE is
;*  cleared, 0 is printed as blank. If non-zero numeric is printed, it
;*  automatically sets FLAG,RIPPLE.
;* 0 (30h) is printed as capital O (4Fh), for improved readibility, as 0
;*  may easily be substituted by 8 on LCD. This changing 0 to O is not
;*  performed only in ASCII representation of recorded bytes in serial code
;*  receiver.
;* Entry point NUM prints numeric unconditionally, undependently of bit
;*  FLAG,RIPPLE.
;*
;* Input variables:
;*    W (0...9), number to be printed at current cursor position of LCD
;* Output variables:
;*    CHARCOU is decremented by the number of characters printed
;*****************************************************************************

NZNum				; same aso Num, only blank instead of 0
	btfsc	FLAG,RIPPLE	; test if RIPPLE bit set...
	goto	Num		; ...if RIPPLE set, no more blanks
	addlw	0		; set Z flag if W=0
	btfsc	STATUS,Z	; is it = 0 ?
	goto	Blank		; if so, jump to space routine
	bsf	FLAG,RIPPLE	; if >0, clear RIPPLE bit, for no more blanks
Num
	andlw	0fh		; isolate low nibble
	btfsc	STATUS,Z	; is it = 0 ?
	movlw	'O'-30h		; if so, initialize capital O
	addlw	30h		; adjust ASCII for numeric
	goto	Char		; print digit
;;*******
;*******  LCD ROUTINES
;*******

;*****************************************************************************
;*  All these entry ponts of this subroutine are used in program:
;*  
;*  Row2:    Issues command to move cursor to row 2 of LCD, and loops 130 us,
;*             to allow time for LCD controller to execute the command
;*  WrComl:  Issues command in W to the LCD, and loops 130 us, to allow time
;*             to LCD controller to execute the command
;*  loop130: Loops 130 us including call and return
;*  GoLoop:  Loops W*2 us
;*  Loop7:   Same as GoLoop, only 2t shorter (used in sample rate routine)
;*****************************************************************************

Row2
	movlw	0c0h		; command for line 2

WrComL				; issues command in W
	call	WrCom		; write command in LCD
loop130				; * waist 130 us
	clrf	DJNZ		; this is to initialize DJNZ to 40h and...
	bsf	DJNZ,6		; not to disturb W
GoLoop
	goto	$+1		; 2    waist 2 t
Loop7
	decfsz	DJNZ,F		; 1    DJNZ = timing loop counter
	goto	GoLoop		; 2    64x5=320t (128 us)
	return			; 2    finished

;*****************************************************************************
;*  All these entry ponts of this subroutine are used in program:
;*  
;*  CharBl:  print character in W and then blank on LCD and decrement CHARCOU
;*  Blank:   print blank (32h) on LCD and decrement CHARCOU
;*  Char:    print character in W on LCD and decrement CHARCOU
;*  CharNCC: print character in W on LCD without affecting CHARCOU
;*  
;*  Note: CHARCOU is used to print fixed format message on LCD, as the
;*  calling routine will add N-CHARCOU blanks to fill area N characters long
;*  
;*  Input variables:
;*         all entry points except Blank: W = character to be printed
;*  Output variables:
;*         all entry points except CharNCC: CHARCOU is decremented by one
;*****************************************************************************

CharBl				; print char, then blank
	call	Char		; print char first
Blank				; print blank
	movlw	' '		; print blank
Char				; print W
	decf	CHARCOU,F	; decrement character counter
CharNCC				; print W without affecting CHARCOU
	bsf	PORTB,1		; pull RS hi (data register select)
	goto	Skr1		; continue 4-bit mode writing to LCD

;*****************************************************************************
;* PrintBrk
;*  Print message "Break" in row 1, pos 0
;*  
;* Input variables: none
;* Output variables: 
;*    CHARCOU is decremented by the number of characters printed
;*****************************************************************************
;* Write
;* Print message addressed by W+1 in line 1
;* Note: Terminator is last character in string with bit 7 set
;*  
;* Input variables: W points to string (RETLWs) decremented by 1
;* Output variables: 
;*     CHARCOU is decremented by the number of characters printed
;*****************************************************************************

PrintBrk
	call	Row1		; move cursor to row 1
	movlw	BrkMes-1	; message "Break" address-1

Write				; write string addressed by W, terminator 0
	movwf	SCRATCH		; SCRATCH = source pointer
GoWrite				; write string at (SCRATCH+1), terminator 0
	call	PclSub2		; advance pointer and read pointed byte
	addlw	80h		; this is to test if bit 7 was set...
	btfsc	STATUS,C	; ...if so, C will be set
	goto	Char		; last character was with bit 7 set
	andlw	7fh		; restore initial character value
	call	Char		; print one character
	goto	GoWrite		; loop

;*****************************************************************************
;* Wrcom
;*  Write command in W to LCD, then loop 130 us
;* Skr1
;*  Allows data write to LCD, if PORTB,1 is set previously
;* Nibble
;*  Write one nibble (W,0-3) to LCD data bus
;*  
;* Note: all entry ponits are terminated by 130us timing loop, to allow
;*  LCD controller to execute accepted command/data.
;*
;* Input variables: command in W
;* Output variables: none
;*****************************************************************************

WrCom
	bcf	PORTB,1		; rs lo (command)
Skr1
	movwf	DJNZ		; save W in DJNZ for lo nibble writing
	call	Hinib_B		; outputs w,4-7 to PORTB,4-7
	call	EnaLCD		; generate enable signal for hi nibble
Nibble
	swapf	DJNZ,W		; restore initial value of W and swap it
	call	Hinib_B		; outputs w,0-3 to PORTB,4-7
	call	EnaLCD		; generate enable signal for low nibble
	goto	loop130		; waist 130 to allow LCD to crunch command

;*****************************************************************************
;* Generate Enable signal (1200us) for LCD controller, and refresh LEDs.
;* Entry point DisEna: Remove enable and discharge signal, and refresh LEDs.
;*****************************************************************************

EnaLCD
	bsf	PORTB,0		; enable LCD controller
	goto	$+1		; waist 2 cycles, to make signal 1.2us long
DisEna
	bcf	PORTB,0		; terminate enable signal LCD controller

;*****************************************************************************
;* MoveLEDs
;*  Entry point MoveLEDs: transfer from FLAG,LEDP FLAG,LEDH and FLAG,LEDL to
;*                        PORTB,5 PORTB,6 and PORTB,7 to service LEDs.
;*
;* Input variables: 
;*    FLAG, bits LEDP, LEDH, LEDH will affect LED1, LED2, LED3 respectively
;* Output variables: none
;*****************************************************************************
;* Hinib_B
;*  Entry point Hinib_B:  output W,4-7 to 4-bit LCD data bus
;*  
;* Input variables: 
;*    hi nibble of W is copied to LCD data bus
;* Output variables: none
;*****************************************************************************

MoveLESd			; writes states for LEDs L,H,P to PORTB,5-7
	movf	FLAG,W		; FLAG bits 5,6,7 are LED bits
Hinib_B				; outputs w,4-7 to PORTB,4-7
	bcf	PORTB,4		; clear high nibble of PORTB
	bcf	PORTB,5		; clear high nibble of PORTB
	bcf	PORTB,6		; clear high nibble of PORTB
	bcf	PORTB,7		; clear high nibble of PORTB
	andlw	0f0h		; mask for hi nibble of W
	iorwf	PORTB,F		; write hi nibble of W in hi nibble of PORTB
	return			; finished

;*****************************************************************************
;* HexDigit
;* This subroutine prints low nibble of W on LCD as hexadecimal digit. Zero
;*  (30h) is printed as capital O (7Fh)
;*  
;* Input variables: W in range 0...0fh, hex number to be printed
;* Output variables: CHARCOU is decremented by two
;*****************************************************************************

HexDigit			; hex W (lo nibble) to LCD, change 0...
				; ...to capital O
	andlw	0fh		; isolate low nibble of W...
	movwf	BIN4		; ...and put it in BIN4
	addlw	-0ah		; test if input number > 9
	btfss	STATUS,C	; Is it > 9 ?
	goto	NumBin4		; ...if not, just print it as-is
	addlw	.7+3ah		; 3ah...3fh to 'A'...'F' correction
	goto	Char		; print ASCII adjusted hex value a...f

;;*******
;*******  DATA EEPROM
;*******

;*****************************************************************************
;*  This table is located in data eeprom
;*  It contains numerical data for 16 sample frequencies period display
;*  for analyzer. Last 13 bytes are timing constants used by subroutine
;*  GetSlowClk to generate internal timings (three fastest rates need no
;*  constants from the table, as they are treated as special cases)
;*****************************************************************************
;*  ----- TABLE 1 (00h-2Fh): Rate display table for analyzer
;*  
;*  **** First byte: Flags. Bits in this byte have the following functions:
;*  bit 7 = 0: Frequency in Hz
;*        = 1: Frequency in Mhz or Khz
;*  bit 6 = 0: Frequency does not contain decimal point
;*        = 1: Frequency contains decimal point before last digit
;*  bits  5,4: Bits 9 and 8 for frequency display, respectively
;*  bit 3 = 0: Period in us (microseconds)
;*        = 1: Period in ms (miliseconds)
;*  bit 2 = 0: Period does not contain decimal point
;*        = 1: Period contains decimal point before last digit
;*  bits  1,0: Bits 9 and 8 for period display, respectively
;*  **** Second byte: low significant byte for frequency
;*  **** Third byte: low significant byte for period
;*****************************************************************************
;*  ----- TABLE 2 (30h-3Ch): Timing constant table for analyzer
;*  
;*  Timing constant factors for all sample rates generated by subroutine
;*  GetSlowClk (all except 1MHz, 500KHz and 228KHz)
;*****************************************************************************
;*  Note: This is read-only data, so the Data EEPROM must be programmed
;*  before the unit is ready to use. MCU will not affect data EEPROM
;*  contents. If your programmer does not support automatic loading of
;*  Data EEPROM contents from the HEX file, it must be loaded manualy.
;*  This will help in that case (all values are hexadecimal):
;*  
;*  addr 00-07:  88 01 01 98 F4 02 8C E4
;*  addr 08-0f:  2C 88 64 0A 88 32 14 D8
;*  addr 10-17:  80 1A 88 19 28 C8 C0 34
;*  addr 18-1f:  88 0A 64 C8 60 68 C8 30
;*  addr 20-27:  D0 C9 18 A1 80 01 01 14
;*  addr 28-2f:  90 19 00 64 0A 00 28 19
;*  addr 30-37:  01 06 09 10 16 2E 30 64
;*  addr 38-3c:  CC 05 0E 3B 95
;*  
;*  Total bytes used in Data EEPROM: 61 (the last three bytes don't care)
;*****************************************************************************

	org	2100h

;				      constant T/sample	Hz	s     RATE

	de	b'10001000', .1,   .1	; -  	2.5	1M	1u	0
	de	b'10011000', .244, .2	; -  	5	500K	2u	1
	de	b'10001100', .228, .44	; -  	11	228K	4.4u	2
	de	b'10001000', .100, .10	; 1	25	100K   	10u	3
	de	b'10001000', .50,  .20	; 6	50	50K    	20u	4
	de	b'11011000', .128, .26	; 9	65	38.4K  	26u	5
	de	b'10001000', .25,  .40	; 16	100	25K	40u	6
	de	b'11001000', .192, .52	; 22	130	19.2K  	52u	7
	de	b'10001000', .10,  .100	; 46	250	10K	100u	8
	de	b'11001000', .96,  .104	; 48	260	9.6K   	104u	9
	de	b'11001000', .48,  .208	; 100	520	4.8K   	208u	10
	de	b'11001001', .24,  .161	; 204	1040	2.4K   	417u	11
	de	b'10000000', .1,   .1	; 5	2500	1K	1m	12
	de	b'00010100', .144, .25	; 14	6253	400	2.5m	13
	de	b'00000000', .100, .10	; 59	25018	100	10m	14
	de	b'00000000', .40,  .25	; 149	62548	40	25m	15

;	timing constants table

	de	.001, .006, .009, .016, .022, .046, .048
	de	.100, .204, .005, .014, .059, .149

	end
                                                                               
