;---------------------------------------------------------------------------
; Hardware Random Number Generator on ISA Adapter for the SX18AC
; Aaron Logue, Aug 2002     http://www.cryogenius.com/hardware/isarng/
;
;
; mpasm /x- /l+ /e+ /w0 /p16C58A /aINHX8M /c- isarng.asm
;---------------------------------------------------------------------------
	radix		dec
	include		mydefs.inc

;DEVICE	EQU	TURBO | SYNC | IRC | RC4MHZ | MORETRIM | TRIM9	; Internal RC clock
DEVICE	EQU	TURBO | SYNC | FOSCHI | FOSC2		; External oscillator

MHZ	EQU	20		; Clock rate in Mhz (4, 16, 20, or 50 is best)
JIF	EQU	4		; How many microseconds is one jiffy
PSCALE	EQU	8		; Prescaler ratio (1 if unused)

JIFTIKS	EQU	(MHZ * JIF / PSCALE)	; How many RTCC counts per jiffy
 IF (JIFTIKS == 0)
 ERROR Prescaler may be too high for jiffy granularity.
 ENDIF

JBITS	EQU	(104 / JIF)	; How many jiffies per 9600 baud bit
JBITST	EQU	JBITS / 2	; How many jifs to wait after leading edge
                                ; of start bit to sample middle of bit
JMILLI	EQU	(1000 / JIF)	; How many jiffies per millisecond

 IF (PSCALE == 1)		; Pick a value for OPTION register
OPTREG	EQU	0xC8
 ENDIF
 IF (PSCALE == 2)
OPTREG	EQU	0xC0
 ENDIF
 IF (PSCALE == 4)
OPTREG	EQU	0xC1
 ENDIF
 IF (PSCALE == 8)
OPTREG	EQU	0xC2
 ENDIF
 IF (PSCALE == 16)
OPTREG	EQU	0xC3
 ENDIF

; I/O ports and pin configuration constants
PortA	EQU	05h
PortB	EQU	06h

RNG_PORT	EQU	PortA	; Port to sample noise on
CLR_PORT	EQU	PortA	; Port to check if data read
INT_PORT	EQU	PortA	; Port to trigger interrupt on

RNG_BIT		EQU	0	; RNG input bit
CLR_BIT		EQU	2
INT_BIT		EQU	3

TrisA	EQU	11110101b	; PortA I/O pin configuration  1 = input
TrisB	EQU	00000000b	; PortB I/O pin configuration

; Global registers
ELAPSEDJIF 	EQU	8	; Global, elapsed number of jiffies
LASTRTCC 	EQU	9	; Global, last value of RTCC
ELAPSEDRTCC	EQU	10	; Global, elapsed number of RTCC ticks
TEMP		EQU	11	; Global, general purpose

XMIT_STATE	EQU	12	; Are we transmitting or not?
XMIT_FORMAT	EQU	13	; How many bits per generated sample?

; Banked registers
RNG_JIFS	EQU	16
RNG_SAMPLE	EQU	17	; Stores bit samples
RNG_BYTE	EQU	18	; Stores accumulated random bits
RNG_COUNT	EQU	19	; Counts accumulated bits until we're done
RNG_FINAL	EQU	20	; When done, random bits are stuffed here


	org	0		; Generate code here
	movlw	OPTREG		; Initialize OPTION register
	option

	mode	0xf
  	movlw	TrisA		; Move 0x00 to W to set direction
        tris	PortA		; W --> PortA direction bits

  	movlw	TrisB		; Move 0x00 to W to set direction
        tris	PortB		; W --> PortB direction bits

	; Clear all banked registers
	clrf	FSR
zero_it
	bsf	FSR, 4
	clrf	IND
	incfsz	FSR, f
	goto	zero_it

	clrf	PortA
	clrf	PortB

	clrf	XMIT_STATE	; Default to not transmitting and no data
	movlw	8
	movwf	XMIT_FORMAT	; 8 for accumulating bytes of data
	movwf	RNG_COUNT	; Let RNG_COUNT = XMIT_FORMAT

	clrf	LASTRTCC
	clrf	ELAPSEDRTCC
	clrf	RNG_SAMPLE
	bsf	RNG_SAMPLE, 0	; Set sentinel bit

main_loop
	; -------------------------------------------------------------------
	; Compute elapsed RTCC ticks.  This needs to be called often
	; enough to avoid losing time by wrapping.  1 jiffy costs 20 ticks
	; of detection overhead here.  The prescaler should be set high
	; enough that there's no chance of ever missing a jiffy.
	;
	movf	rtcc, w		; Load current RTCC counter
	movwf	TEMP		; Save an unchanging copy of it
	movf	LASTRTCC, w	; Load previous RTCC counter value
	subwf	TEMP, w		; Compute difference
	addwf	ELAPSEDRTCC, f	; Add difference to elapsed RTCC ticks
	movf	TEMP, w		; Save new previous RTCC counter value
	movwf	LASTRTCC	;  for next elapsed computation
	;
	; Compute elapsed jiffies.  Our clock speed should be high enough
	; and our jiffy period long enough that we don't have very many
	; elapsed jiffies each time through here.  That means that repeated
	; subtraction of the number of RTCC ticks per jiffy (JIFTIKS) from
	; elapsed RTCC ticks (ELAPSEDRTCC) should be more efficient than
	; dividing ELAPSEDRTCC by JIFTIKS.  The more instructions per jiffy,
	; the better, as long as jiffies are still granular enough to be
	; useful.  4 or 8 microseconds per jiffy at 50 Mhz is probably good.
	;
	clrf	ELAPSEDJIF	; Assume no jiffies have elapsed
	movlw	JIFTIKS		; Load number of ELAPSEDRTCC ticks per jiffy
ej_10
	subwf	ELAPSEDRTCC, f	; Subtract JIFTIKS from ELAPSEDRTCC
				; C is 1 if result is positive or zero
	btfss	STATUS, C	; Did we have enough ELAPSEDRTCC for a jiffy?
	goto	ej_20		;   sadly, no
	incf	ELAPSEDJIF, f	;   joyously, yes - we have a jiffy!
	goto	ej_10		; Go see if there's enough for another one
ej_20
	addwf	ELAPSEDRTCC, f	; Restore incomplete-jiffy elapsed RTCC count
	;
	; ELAPSEDJIF is now the number of elapsed jiffies since processes
	; were last called.
	; -------------------------------------------------------------------

	call	rng_sample_process
	call	xmit_random
	goto	main_loop

;---------------------------------------------------------------------------
; rng_sample_process
;
;	The goal of this routine is to sample bits from the hardware
;	RNG and output a stream of unbiased random bits.
;	Each time this function is called, a single sample is taken.
;	When we have accumulated two samples, we compare them, and
;	if they are the same, we throw them away and return.
;	If they are different, we output the value of one sample.
;	Method from Schneier Applied Cryptography 2nd Ed. Page 425.
;	After 8 bits have been accumulated, do something with them.
;---------------------------------------------------------------------------
rng_sample_process
	clrf	FSR		; Set bank 0
	movf	RNG_JIFS, w	; sleep time remaining -> W
	btfsc	STATUS, Z	; skip if we're asleep
	goto	rng_running	; process is awake and running
	movf	ELAPSEDJIF, w	; elapsed jiffies -> W
	btfsc	STATUS, Z	; skip if jiffies have elapsed
	retlw	0		; no jiffies elapsed - keep sleeping
	subwf	RNG_JIFS, f	; Subtract elapsed from remaining sleep time
				; C is 1 if result is positive or zero
	btfss	STATUS, C	; Was result negative?
	goto	rng_running	;   yes, time to wake up (also, we overslept)
	btfsc	STATUS, Z	; Was result zero?
	goto	rng_running	;   yes, time to wake up
	retlw	0		; keep sleeping

rng_running
	clrf	RNG_JIFS
	; nahh, let's sample a bit every time through
	; incf	RNG_JIFS, f	; Delay a little between bitstream samples

	bcf	STATUS, C	; Assume sample bit is 0
	btfss	RNG_PORT, RNG_BIT ; If sample level is high, data is 0
	bsf	STATUS, C	; Sample bit is 1
	rlf	RNG_SAMPLE, f	; Shift sample bit into bit register
	btfss	RNG_SAMPLE, 2	; Have we taken two samples?
	retlw	0		;   no, keep sampling
	;
	; Evaluate most recent sample pair and use only if bits differ
	;
	movf	RNG_SAMPLE, w	; Load samples
	clrf	RNG_SAMPLE
	bsf	RNG_SAMPLE, 0	; Set sentinel bit for next pair
	movwf	TEMP		; Leave second sample in W,0
	rrf	TEMP, f		; Shift first sample to TEMP,0
	xorwf	TEMP, f		; first bit XOR second bit -> TEMP,0
	btfss	TEMP, 0		; Were bits different?
	retlw	0		;   no, keep sampling
	;
	; TEMP,1 is now (1 XOR first sample) so let's use it
	;
	bcf	STATUS, C	; Assume random bit is 0
	btfsc	TEMP, 1		; 
	bsf	STATUS, C	; Random bit is 1
	rlf	RNG_BYTE, f	; Accumulate random bit
	decfsz	RNG_COUNT, f	; Have we accumulated enough bits?
	retlw	0		;   No, keep sampling
	;
	; Do something with accumulated random bits in RNG_BYTE
	;
	movf	XMIT_FORMAT, w
	movwf	RNG_COUNT	; number of bits to gather for next value

	movf	RNG_BYTE, w
	movwf	RNG_FINAL
	bsf	XMIT_STATE, 1	; Queue data for transmission by xmit_random
	clrf	RNG_BYTE
	retlw	0

;---------------------------------------------------------------------------
; xmit_random
;
;---------------------------------------------------------------------------
xmit_random
	;
	; Do nothing if there's a data byte waiting to be picked up
	;
	btfsc	CLR_PORT, CLR_BIT ; skip if data available bit is zero
	retlw	0		; data available bit is set - do nothing
	;
	; Do nothing if there's no accumulated data to transmit
	;
	btfss	XMIT_STATE, 1	; Is there data ready?
	retlw	0		; no, keep waiting
	;
	; We get to generate a byte!
	;
	bcf	XMIT_STATE, 1	; Clear data ready flag so we don't re-send
	movf	RNG_FINAL, w	; Load the data
	movwf	PortB		; Set the output pins

	bsf	INT_PORT, INT_BIT ; raise the data-available line

	; We need to delay at least 100 nanoseconds.  At 20 Mhz, 2 ticks
	; is 100 ns.  At 50 Mhz, 5 ticks is 100 ns.  At 75 Mhz, 8 ticks
	; is 106 ns.
	nop
	nop
	nop

	bcf	INT_PORT, INT_BIT ; lower the data-available line to interrupt

	retlw	0

	end
