Full Version : I Ching Oracle (GCC)
avr >>GAME & VIDEO PROJECTS >>I Ching Oracle (GCC)


AVR_Admin- 04-16-2006
I Ching Machine

Introduction

In "The Long Dark Tea Time of the Soul" (by Douglas Adams) the main character, Dirk Gently, consults an I Ching oracle in a little electronic box.

There a numerous versions in software of the I Ching for PCs, but due to the fact that true "randomness" is better done in hardware, an I Ching "machine" would be more authentic on an MCU combined with a random generator.

Also, some people have difficulty figuring if its a "moving" or "fixed" line after casting the coins; a bit of hardware logic can solve this and display the oracleŽs answers for them.

This project is to basically take the code and hardware here for the rngbox:

http://www.cryogenius.com/hardware/rng/

and turn it into an I Ching machine.

Logic

A first "throw" of the three coins is, in logic terms, two random binary bits (or a total 4 possibilities):

0 0
0 1
1 0
1 1

The first bit (1 or 0) can be represented as
"Yin" -- -- or "Yang" -----

The second bit (1 or 0) would represent if this first line was "moving" or "not moving".

So this gives the possible four states which are popularly generated by throwing three coins together:

http://en.wikipedia.org/wiki/I_Ching_divin...ree-coin_method


"Looped" 6 six times this would create the first hexagram. Ideally youŽd want the random bits to get cast at excactly the same time, but given that the Tao can probably sort that out, two sequential random bits would do as a first "pair".

Interestingly, to then get the resultant second hexagram simply XOR the two bits:

So a Yin -- -- (say 1) XOR a non moving bit (0) gives "1". So Yin (non moving) stays as Yin.

If a Yin is with a moving bit (1 XOR 1) you get "0" or "Yang" and so on.

The other ports being used for output to LEDŽs / dot matrix display and a push button for "casting"


Implementation:

Each 2 bit pairs are sequentially read into a GPIO port.
The first bit is stored in (lets say) the LSB of register A
The second bit stored somewhere else, LSB register B
The third bit is stored in A, but the next location in the register row of A (call it A2).

This is looped six times so that you end up with 6 bits in A and (as close as you can get to the random bits being in pairs) and 6 bits in B which overlap each other. The LSB of A pairs with LSB of B and so on. When the two register contents are later XORed you end up with
the final hexagram in the result register C.

This looks like:

A: * * X X X X X -X(first bit read in)

B: * * X X X X X -X(second bit read in - i.e. the first bit which was just read into "A" now has its parallel corresponding "moving or not" bit in B, together constituting the first throw)

A * * X X X X X- X(third bit read in) and so on

A XOR B store result in C = second hexagram.

Only first 6 bits (of 8 bit register) are used.


Output:

Simplest would be six L.E.D. s lighting up in a vertical line to represent hexagram 1 - this is essentially the output of register A and the LEDŽs could be rectangle.

The next six LEDŽs next to those could be round points representing if each corresponding line was correspondingly "moving" or not (register B output)

The last would be register C output for the second hexagram, could be displayed on more LEDs but cheaper and less power hungry to simply use existing display.


Timing:

Cool would be to have the hexagrams being generated all the time as the "Tao flows" - you hit a button to cast a momentary reading, then LEDs stop flickering and display first one hexagram then the other when you hit again.


Look up table:

You could further represent the possible 64 hexagrams (generated from the above) as 64 pre stored binary numbers. These then link to 64 "real" BCD numbers, but its not a simple BCD conversion: For example the binary output for register A could be 111111 which would be all yin lines, representing the actual hexagram number "2".

For the matrix see: http://en.wikipedia.org/wiki/I_Ching

The 64 code array could sit in memory as a look up table, for the LED digits (under the LED lines) which light up when a reading is made.

So you hit a button, the oracle shows six lines (as six LEDs) from bottom to top - then the corresponding "moving LED" would light next to any of the lines which are "moving". Underneath there is a two digit LED display showing which hexagram was cast. When the button is pushed again it XORs the lines with the moving ones (if any) and the number changes too.

Or use a dot matrix display using a Max driver like the MAX6952 (see Maxim app. note 1033) for both lines and numbers?


Any comments / suggestions please let me know. I am new at programming but can build hardware ok - if anyone wanted to have a go with me on this IŽd be happy to do a prototype in SMD format eventually.

Further refereneces to randomness:

http://www.robertnz.net/hwrng.htm
http://willware.net:8080/hw-rng.html
http://en.wikipedia.org/wiki/Hardware_rand...umber_generator

greetz
bazthewaz@gmx.de

CODE
;---------------------------------------------------------------------------
; Hardware Random Number Generator with RS-232 interface for the SX18AC
; Aaron Logue, Sep 2002     http://www.cryogenius.com/hardware/rng/
;
; This version is for the standalone RNG box with 7-segment display.
;
; Commands:
; ?  display currently set output format
; ^Q start transmission
; ^S stop transmission
; 1  set output format to ASCII binary (eg: "10010110001011...")
; 4  set output format to ASCII hex    (eg: "692DD3A5981C75...")
; 6  set output format to ASCII base64 (eg: "qE6y8vMSkig7CT...")
; 8  set output format to raw 8-bit binary
;
; mpasm /x- /l+ /e+ /w0 /p16C58A /aINHX8M /c- rng.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

TRUE EQU 1
FALSE EQU 0

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

RX_PORT  EQU PortA
TX_PORT  EQU PortA
RNG_PORT EQU PortA
DP_PORT  EQU PortA
LED_PORT EQU PortB

SERIAL_INVERT EQU TRUE; RS-232 inverted (using a MAX232?)
TX_BIT  EQU 0; RS-232 serial output bit
RX_BIT  EQU 1; RS-232 serial input bit
RNG_BIT  EQU 2; RNG input bit
DP_BIT  EQU 3; Left decimal point

TrisA EQU 11110110b; PortA I/O pin configuration
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

CMD_BYTE EQU 12
XMIT_STATE EQU 13; Are we transmitting or not?
XMIT_FORMAT EQU 14; What format should we send in?

RECENT_VALUE EQU 15; A recent 0-9 random value (for LED)

; Banked registers
RX_JIFS  EQU 16
RX_STATE EQU 17
RX_COUNT EQU 18
RX_CHAR  EQU 19

TX_JIFS  EQU 20
TX_STATE EQU 21
TX_COUNT EQU 22
TX_CHAR  EQU 23

RNG_JIFS EQU 24
RNG_SAMPLE EQU 25; Stores bit samples
RNG_BYTE EQU 26; Stores accumulated random bits
RNG_COUNT EQU 27; Counts accumulated bits until we're done
RNG_FINAL EQU 28; When done, random bits are stuffed here

; 28 to 31 is in bank 0

; 48 to 63 is in bank 1
LED_JIFS EQU 48
LED_STATE EQU 49
LED_COUNT EQU 50; Minor millisecond sleep count
LED_COUNT2 EQU 51; Major millisecond sleep count
LED_DP  EQU 52; Decimal point flip-flop
LED_FLICKER EQU 53; Turn LED off half the time to run cooler
LED_VALUE EQU 54; Remembers what's on display

; Process equates
RX_STARTBIT EQU 0
RX_DATABITS EQU 1
RX_STOPBIT EQU 2

TX_STARTBIT EQU 0
TX_DATABITS EQU 1
TX_STOPBIT EQU 2

CHAR_XON EQU 0x11; ^Q
CHAR_XOFF EQU 0x13; ^S

LED_MINOR EQU 100; 100 * 5 = 500 ms
LED_MAJOR EQU 5
LED_OFF_RATIO EQU 8; Ratio of time LED is off to time it's on


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 4
movwf XMIT_FORMAT; 1, 4, or 8 for ASCII binary, hex, or raw
movwf RNG_COUNT; Let RNG_COUNT = XMIT_FORMAT

clrf LASTRTCC
clrf ELAPSEDRTCC
clrf RNG_SAMPLE
bsf RNG_SAMPLE, 0; Set sentinel bit

;bsf XMIT_STATE, 0; come up transmitting
movlw   255
       movwf   PortB ; come up with LED dark, except for left DP

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 rx_serial_process
call tx_serial_process
call rng_sample_process
call xmit_random
call led_process
goto main_loop


;---------------------------------------------------------------------------
; Asynchronous RS-232 Receiver
;
; States: 0 polling for start bit
;  1 verify center of start bit
;  2 sample center of data bit (8 times)
;  4 verify center of stop bit; parse command
;---------------------------------------------------------------------------
rx_serial_process
clrf FSR ; Set bank 0
movf RX_JIFS, w; sleep time remaining -> W
btfsc STATUS, Z; skip if we're asleep
goto rx_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 RX_JIFS, f; Subtract elapsed from remaining sleep time
  ; C is 1 if result is positive or zero
btfss STATUS, C; Was result negative?
goto rx_overslept;   yes, time to wake up (also, we overslept)
btfsc STATUS, Z; Was result zero?
goto rx_running;   yes, time to wake up
retlw 0 ; keep sleeping
rx_overslept
; RX_JIFS now contains a negative value; the amount that we overslept.
; The next sleeper can add its desired sleep time to this value rather
; than sleeping for a hardcoded value and get reduced sleep.  In this
; way, we "catch up" and our wakeup times will be closer to the center
; of received bits.  Oversleeping should not be an issue at higher
; clock speeds.
; clrf RX_JIFS ; Zero remaining sleep time
rx_running
btfsc RX_STATE, RX_STOPBIT
goto rx_verify_stop
btfsc RX_STATE, RX_DATABITS
goto rx_read_data
btfsc RX_STATE, RX_STARTBIT
goto rx_verify_start
; Poll for beginning of a start bit
rx_poll
IFDEF SERIAL_INVERT
btfsc RX_PORT, RX_BIT; Do we have a start bit?
ELSE
btfss RX_PORT, RX_BIT; Do we have a start bit?
ENDIF
retlw 0 ;   no, keep polling
movlw JBITST ;   yes
movwf RX_JIFS ; Sleep until center of start bit
bsf RX_STATE, RX_STARTBIT; Advance to start bit verify state
retlw 0
; Verify center of start bit
rx_verify_start
IFDEF SERIAL_INVERT
btfsc RX_PORT, RX_BIT; Is the start bit still there?
ELSE
btfss RX_PORT, RX_BIT; Is the start bit still there?
ENDIF
goto rx_reset;   no, keep polling
movlw JBITS ;   yes - sleep until center of first data bit
addwf RX_JIFS, f; Reduce by any time that we overslept
movlw 8
movwf RX_COUNT; Set data bit count to 8
rlf RX_STATE, f; Advance to read data state
retlw 0
; Gather data bits
rx_read_data
bcf STATUS, C; Assume data bit is 0
IFDEF SERIAL_INVERT
btfsc RX_PORT, RX_BIT; If RS-232 voltage level is low, data is 0
ELSE
btfss RX_PORT, RX_BIT; If RS-232 voltage level is high, data is 0
ENDIF
bsf STATUS, C; Data bit is 1
rrf RX_CHAR, f; Shift carry into character register
movlw JBITS ; Sleep until center of next bit
addwf RX_JIFS, f; Reduce by any time that we overslept
decfsz RX_COUNT, f; Have we gathered 8 bits yet?
retlw 0 ;   no, keep gathering
rlf RX_STATE, f; Advance to verify stop bit state
retlw 0
; Verify stop bit
rx_verify_stop
IFDEF SERIAL_INVERT
btfss RX_PORT, RX_BIT; Is the stop bit clear?
ELSE
btfsc RX_PORT, RX_BIT; Is the stop bit clear?
ENDIF
goto rx_reset;   no, throw data away
movf RX_CHAR, w; Received character -> W
call cmd_parse; Do something with received character
; Return to polling
rx_reset
clrf RX_STATE
retlw 0

;---------------------------------------------------------------------------
; Asynchronous RS-232 Transmitter
;
; States: 0 waiting for a character to transmit
;  1 sending start bit
;  2 sending data bits
;  4 sending stop bit
;---------------------------------------------------------------------------
tx_serial_process
clrf FSR ; Set bank 0
movf TX_JIFS, w; sleep time remaining -> W
btfsc STATUS, Z; skip if we're asleep
goto tx_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 TX_JIFS, f; Subtract elapsed from remaining sleep time
  ; C is 1 if result is positive or zero
btfss STATUS, C; Was result negative?
goto tx_running;   yes, time to wake up (also, we overslept)
btfsc STATUS, Z; Was result zero?
goto tx_running;   yes, time to wake up
retlw 0 ; keep sleeping
tx_running
btfsc TX_STATE, TX_STOPBIT
goto tx_send_stop
btfsc TX_STATE, TX_DATABITS
goto tx_send_data
btfss TX_STATE, TX_STARTBIT
retlw 0 ; Nothing to transmit
; Begin transmission
IFDEF SERIAL_INVERT
bcf TX_PORT, TX_BIT; Output the start bit
ELSE
bsf TX_PORT, TX_BIT; Output the start bit
ENDIF
movlw JBITS ; Sleep for one bit interval
movwf TX_JIFS
movlw 8
movwf TX_COUNT; Set data bit count to 8
rlf TX_STATE, f; Advance to transmit data state
retlw 0
tx_send_data
rrf TX_CHAR, f; Rotate LSB into carry
btfsc STATUS, C; Raise or lower the line depending on carry
IFDEF SERIAL_INVERT
bsf TX_PORT, TX_BIT
ELSE
bcf TX_PORT, TX_BIT
ENDIF
btfss STATUS, C
IFDEF SERIAL_INVERT
bcf TX_PORT, TX_BIT
ELSE
bsf TX_PORT, TX_BIT
ENDIF
movlw JBITS ; Leave it there for one bit interval
addwf TX_JIFS, f; Reduce by any time that we overslept
decfsz TX_COUNT, f; Have we transmitted 8 bits?
retlw 0
rlf TX_STATE, f; Advance to stop bit state
retlw 0
tx_send_stop
IFDEF SERIAL_INVERT
bsf TX_PORT, TX_BIT; Output the stop bit
ELSE
bcf TX_PORT, TX_BIT; Output the stop bit
ENDIF
movlw JBITS ; Leave it there for one bit interval
addwf TX_JIFS, f; Reduce by any time that we overslept
clrf TX_STATE
retlw 0

;---------------------------------------------------------------------------
; parse_command
;---------------------------------------------------------------------------
cmd_parse
movwf CMD_BYTE; Store received character in CMD_BYTE
movlw CHAR_XOFF
subwf CMD_BYTE, w
btfsc STATUS, Z
goto cmd_xoff
movlw CHAR_XON
subwf CMD_BYTE, w
btfsc STATUS, Z
goto cmd_xon
movlw '1'
subwf CMD_BYTE, w
btfsc STATUS, Z
goto cmd_binary
movlw '4'
subwf CMD_BYTE, w
btfsc STATUS, Z
goto cmd_hex
movlw '6'
subwf CMD_BYTE, w
btfsc STATUS, Z
goto cmd_base64
movlw '8'
subwf CMD_BYTE, w
btfsc STATUS, Z
goto cmd_raw
movlw '?'
subwf CMD_BYTE, w
btfsc STATUS, Z
goto cmd_query_format
retlw 0
cmd_xoff
bcf XMIT_STATE, 0
retlw 0
cmd_xon
bsf XMIT_STATE, 0
retlw 0
cmd_binary
movlw 1
goto cmd_format
cmd_hex
movlw 4
goto cmd_format
cmd_base64
movlw 6
goto cmd_format
cmd_raw
movlw 8
cmd_format
movwf XMIT_FORMAT
cmd_reset_format
movwf RNG_COUNT; Let RNG_COUNT = 1, 4, 6, or 8 for next value
clrf RNG_BYTE
retlw 0
cmd_query_format
movlw '0'
addwf XMIT_FORMAT, w
movwf TX_CHAR
bsf TX_STATE, TX_STARTBIT; Start the async xmitter
retlw 0

;---------------------------------------------------------------------------
; xmit_random
;
;---------------------------------------------------------------------------
xmit_random
;
; Do nothing if transmitter is currently busy
;
movf TX_STATE, w; transmitter state -> W
btfss STATUS, Z; skip if transmitter idle
retlw 0 ; transmitter is currently busy - do nothing
;
; Do nothing if we're currently x'doff
;
btfss XMIT_STATE, 0; skip if we're not x'doff
retlw 0 ; we're currently x'doff - 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
;
; At last we get to transmit something!
;
bcf XMIT_STATE, 1; Clear data ready flag so we don't re-send
movf RNG_FINAL, w; Load the data
movwf TX_CHAR
bsf TX_STATE, TX_STARTBIT; Start the async xmitter
retlw 0

;---------------------------------------------------------------------------
; led_process
; Every 500 ms, update the 7-segment LED display
;---------------------------------------------------------------------------
led_process
BANKX 1 ; Set bank 1
movf LED_JIFS, w; sleep time remaining -> W
btfsc STATUS, Z; skip if we're asleep
goto led_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 LED_JIFS, f; Subtract elapsed from remaining sleep time
  ; C is 1 if result is positive or zero
btfss STATUS, C; Was result negative?
goto led_running;   yes, time to wake up (also, we overslept)
btfsc STATUS, Z; Was result zero?
goto led_running;   yes, time to wake up
retlw 0 ; keep sleeping

led_running
btfsc LED_STATE, 0
goto led_handler
bsf LED_STATE, 0; We only get here once at startup
movlw LED_OFF_RATIO
movwf LED_FLICKER; Init LED_FLICKER
; Begin sleep for LED_MINOR * LED_MAJOR milliseconds
lr10
movlw LED_MAJOR; Reset counts for full sleep
movwf LED_COUNT2
lr20
movlw LED_MINOR
movwf LED_COUNT
lr30
movlw JMILLI
addwf LED_JIFS, f
retlw 0


;---------------------------------------------------------------------------
; lookup_seg
; This routine converts a number into the corresponding 7-segment LED pattern
; 0 is lit, 1 is dark
;---------------------------------------------------------------------------
lookup_seg
       addwf   PC, f
retlw 10100000b; 0
retlw 10101111b; 1
retlw 10010010b; 2
retlw 10000110b; 3
retlw 10001101b; 4
retlw 11000100b; 5
retlw 11000000b; 6
retlw 10101110b; 7
retlw 10000000b; 8
retlw 10001100b; 9
retlw 10001000b; A
retlw 11000001b; b
retlw 11010011b; c
retlw 10000011b; d
retlw 11010000b; E
retlw 11011000b; F

;---------------------------------------------------------------------------
; 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
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
andlw 7 ; Is format binary or hex or base64?
btfsc   STATUS, Z
goto rng_raw ;   No, format is raw
andlw 2 ; Is format base64?
btfss STATUS, Z
goto rng_base64
movlw '0' ; Load an ASCII '0'
addwf RNG_BYTE, f; Convert to ASCII if in range 0-9
movlw ':'
subwf RNG_BYTE, w; Does result need to be in range A-F?
btfss STATUS, C; C is 1 if result positive or zero
goto rng_raw
movlw 7 ; Bump it up to A-F
rng_offset
addwf RNG_BYTE, f
rng_raw
movf RNG_BYTE, w
rng_done
movwf RNG_FINAL
bsf XMIT_STATE, 1; Queue data for transmission by xmit_random
clrf RNG_BYTE
;
; If (value&F) < 10, then copy it to RECENT_VALUE for possible display
;
movlw 9
movwf TEMP
movf RNG_FINAL, w
andlw 00001111b
subwf TEMP, f
btfsc STATUS, C; C is 0 if result negative
movwf RECENT_VALUE
retlw 0

;
; Convert 6 bits in RNG_BYTE to base64 encoding:
; ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
;
rng_base64
movlw 26
subwf RNG_BYTE, w; Is value in the range 0-25?
btfsc STATUS, C; C is 0 if result negative
goto rng_b64_2
movlw 'A' ; Load an ASCII 'A'
goto rng_offset; Convert 0-25 value to character A-Z
rng_b64_2
movlw 52
subwf RNG_BYTE, w; Is value in the range 26-51?
btfsc STATUS, C; C is 0 if result negative
goto rng_b64_3
movlw 71 ; Load difference between 26 and 97 (ASCII 'a')
goto rng_offset; Convert 26-51 value to character a-z
rng_b64_3
movlw 62
subwf RNG_BYTE, w; Is value in the range 52-61?
btfsc STATUS, C; C is 0 if result negative
goto rng_b64_4
movlw -4 ; Load difference between 52 and 48 (ASCII '0')
goto rng_offset; Convert 52-61 value to character 0-9
rng_b64_4
movlw '+' ; Assume RNG_BYTE is 62
btfsc RNG_BYTE, 0; Distinguish between 62 and 63
movlw '/' ; Oops, it was 63
goto rng_done

;---------------------------------------------------------------------------
led_handler
decfsz LED_FLICKER, f; Time to turn LED on?
goto lh10 ; Not yet - leave them dark

movf LED_VALUE, w; Yes - show 'em our bad bright selves!
movwf LED_PORT
bcf DP_PORT, DP_BIT; Assume left decimal point is dark
btfsc LED_DP, 0; Bit 0 is set if left decimal point is lit
bsf DP_PORT, DP_BIT; Light up left decimal point
movlw LED_OFF_RATIO
movwf LED_FLICKER; Reset dark time
goto lh20
lh10
bsf DP_PORT, DP_BIT; Left decimal point off
movlw 255 ; All LED segments off
movwf LED_PORT
lh20
decfsz LED_COUNT, f; Has minor sleep count hit zero?
goto lr30 ; No, keep sleeping
decfsz LED_COUNT2, f; Has major sleep count hit zero?
goto lr20 ; No, reset minor count and keep sleeping

movf RECENT_VALUE, w
call lookup_seg
movwf TEMP
movlw 127 ; Assume right decimal point is dark
btfss LED_DP, 0; Bit 0 is set if right decimal point is dark
andwf TEMP, f ; Light up right decimal point
movf TEMP, w
movwf LED_VALUE; Remember what we displayed
movwf LED_PORT; Set 7-segment display

bcf DP_PORT, DP_BIT; Assume left decimal point is lit
btfsc LED_DP, 0; Bit 0 is set if left decimal point is lit
bsf DP_PORT, DP_BIT; Shut off left decimal point

comf LED_DP, f; Flip-flop decimal points for next time

goto lr10 ; Reset major and minor sleep counts


;---------------------------------------------------------------------------

end



Forumer™ is Voted #1 Free Forum Hosting provider
Build your own community today with the largest message board hosting company.