Skip to content

Commit 7940db0

Browse files
committed
added nuclear random numbers
1 parent 7ba2a97 commit 7940db0

File tree

6 files changed

+355
-0
lines changed

6 files changed

+355
-0
lines changed

Diff for: nuclear_random/README.md

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# NUCLEAR RANDOM NUMBERS DEMO
2+
The code in @main.asm is a program written in assembly language for the Commodore 64 computer. Its purpose is to generate truly random bytes using the decay events of a radioactive isotope as a source of entropy.
3+
4+
The program does not take any direct input from the user. Instead, it relies on the decay events of a radioactive isotope, which are detected by a Geiger counter connected to the Commodore 64's joystick port. When a decay event occurs, it triggers a pulse that simulates a button press on the joystick.
5+
6+
The output of the program is an array of 255 random bytes, stored in memory starting at address $2000 (8192 decimal). These random bytes are generated by sampling the least significant bit (LSB) of a free-running timer (Timer B) whenever a decay event is detected.
7+
8+
Here's how the program achieves its purpose:
9+
10+
The program sets up Timer B to count CPU clock cycles and starts it running.
11+
It then enters a loop where it waits for a decay event to occur, which is detected by checking for a simulated button press on the joystick port.
12+
When a decay event is detected, the program reads the LSB of Timer B and shifts it into a temporary byte.
13+
After 8 bits have been shifted into the temporary byte, it is stored in the random_bytes array, and the process starts over for the next byte.
14+
This process is repeated until all 255 bytes in the random_bytes array have been filled.
15+
The key logic flow and data transformation happening in the program are:
16+
17+
The program continuously monitors the joystick port for a simulated button press, which indicates a decay event.
18+
When a decay event is detected, the LSB of Timer B is read and shifted into a temporary byte.
19+
After 8 bits have been shifted into the temporary byte, it is stored in the random_bytes array, and the process starts over for the next byte.
20+
This process is repeated until all 255 bytes in the random_bytes array have been filled.
21+
The program uses a free-running timer (Timer B) to avoid relying on system entropy, which could potentially be predictable or influenced by external factors. By sampling the timer's value at the precise moment of a decay event, which is a truly random quantum event, the program ensures that the generated bytes are truly random and unpredictable.

Diff for: nuclear_random/main.asm

+170
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
// The idea is that we have a free running timer that is incremented by the CPU clock.
2+
// When a decay happens the joystick button is pressed by a transistor connected between fire button and ground.
3+
// As the button is pressed the LSB of the timer B is read and shifted into a byte when 8 bits are filled we have
4+
// a truly random byte generated
5+
// we actually generate 255 bytes in total, so we can do some analysis on the distribution of the bytes
6+
// those random bytes start at $2000 (8192)
7+
// Since we use a free running timer that we read when a decay happens, we avoid system entropy.
8+
.const CONT_TIMER_B = $DC0F
9+
.const LSB_TIMER_B = $DC06
10+
.const CURRENT_SCANLINE = $D012
11+
.const JOYSTICK_2 = $DC00
12+
.const SCREEN_MEM = $0400
13+
14+
BasicUpstart2(main)
15+
16+
*=$1000
17+
main:
18+
jsr setup
19+
jsr start_timer_b
20+
21+
loop:
22+
jsr print_bit_pattern
23+
24+
// the detector has a 10ms pulse so we wait max 40ms for the next decay, so we don't flood the whole byte with same bit
25+
jsr wait_for_raster_line // give a 20 ms wait
26+
jsr wait_for_raster_line // give a 20 ms wait
27+
28+
jsr wait_for_decay_pulse // wait till the isotope decays
29+
30+
jsr mask_lowest_bit //mask the lowest bit of timer B and if 1 shift without add if 0 shift with add of 1
31+
beq !shift_add_1+ // if 0 shift left and add one
32+
33+
jsr shift_add_nothing // if 1 shift left and don't add one
34+
beq !back_to_basic+ // if sub routine set Z flag then exit to basic
35+
36+
jmp loop // read the next pulse
37+
38+
!shift_add_1:
39+
jsr shift_add_1 // shift left and add 1 to the intermediate result in random_result byte that offset x points to
40+
beq !back_to_basic+ // if subroutine set Z flag then exit to basic
41+
42+
jmp loop // read next bit
43+
44+
//Exit to basic gracefully
45+
!back_to_basic:
46+
sei
47+
lda #$37
48+
sta $01
49+
cli
50+
jsr $ff8a
51+
jsr $ff81
52+
jsr $ff84
53+
rts
54+
55+
56+
//
57+
//SUBROUTINES
58+
//
59+
60+
//sets up the counters
61+
setup:
62+
lda CONT_TIMER_B // load the configuration of TIMER B
63+
and #%1000101 // configure to count clock cycles
64+
ora #%0000000 // start the timer
65+
sta CONT_TIMER_B // store the timer config and start
66+
ldx #$00 // offset ponter into random_bytes array
67+
ldy #$00 // ofset pointer to bit in the random_byte pointed to by random_bytes+x
68+
rts
69+
70+
// start the timer B again
71+
start_timer_b:
72+
ora #%00000001 //start timer B again
73+
sta CONT_TIMER_B //set timer B register
74+
rts
75+
76+
// this waits for raster line ff to give a bit of a delay
77+
wait_for_raster_line:
78+
!wait:
79+
lda CURRENT_SCANLINE // read current rasterline
80+
cmp $ff // if current rasterline is not ff than wait
81+
bne !wait-
82+
rts
83+
84+
// wait for the joystick button (which is triggered by the geiger counter when a isotope decays)
85+
wait_for_decay_pulse:
86+
!await_pulse:
87+
//poll joystick button, a button press is a tick from the geiger counter
88+
lda JOYSTICK_2 // read joystick 2
89+
and #$10 // check button pressed
90+
bne !await_pulse- // no new decay measured than wait
91+
rts
92+
93+
// mask the lowerst bit of timer b LSB value
94+
mask_lowest_bit:
95+
lda LSB_TIMER_B // read lsb of timer B
96+
sta SCREEN_MEM+1 // show the low byte timer B on screen
97+
and #$1 // mask lowest bit to see if the timer B count is odd or eveb
98+
rts
99+
100+
// shift the value in random_bytes+x left one position
101+
shift_add_nothing:
102+
lda random_bytes,X // load the current random byte (X is the offset into the 255 random bytes)
103+
asl // if even just shift result left without adding one to the result
104+
jsr prep_for_next_bit // set everything up for the next bit (or next byte)
105+
rts
106+
107+
// shidt the value in random_bytes+x left one position and set the lowest bit to 1
108+
shift_add_1:
109+
lda random_bytes,X // load the current random byte (X is the offset into the 255 random bytes)
110+
asl // shift the temporary random result left
111+
ora #$01 // add one to the result
112+
jsr prep_for_next_bit // set everything up for the next bit (or next byte)
113+
rts
114+
115+
//prepare the offset to y for the next bit, if y is 8 then set to 0 and increment x pointer
116+
prep_for_next_bit:
117+
sta random_bytes,X //store the temporary random byte back into the array
118+
iny //increment the bit counter
119+
cpy #$08 //if bit counter is 8 then reset bit counter and increment array counter to fill next byte
120+
beq !reset_y+ //if y==8 then reset
121+
rts
122+
!reset_y:
123+
jsr reset_y // reset y to 0
124+
rts
125+
126+
reset_y:
127+
lda random_bytes,X // get the whole random byte from offset x
128+
sta SCREEN_MEM // show the random byte on screen
129+
ldy #$00 // reset the bit counter to 0
130+
inx // since y was 8, it means we should also increment x to point to the next random byte
131+
rts
132+
133+
print_bit_pattern:
134+
// store a and y to the stack
135+
pha
136+
tya
137+
pha
138+
139+
ldy #%000000001 // load bit pattern to #%000000001
140+
sty bit_pattern // store bit pattern
141+
ror bit_pattern // set bit pattern to the one in the carry, so the next ror it will be 100000000
142+
ldy #8 // 8 bits to print
143+
144+
!loop:
145+
ror bit_pattern // shift bit to mask, to the right
146+
lda random_bytes, X // load the random byte from the array
147+
and bit_pattern // mask the random byte with the bit pattern
148+
beq !draw_zero+ // if the result is 0 then draw a zero
149+
150+
lda #49 // draw a 1
151+
jmp !draw_bit+ // draw the actual 1 bit to the screen
152+
153+
!draw_zero:
154+
lda #48 // draw a 0
155+
156+
!draw_bit:
157+
sta SCREEN_MEM+2,y // draw the bit to the screen starting from 402 with offset y
158+
dey // decrement the bit counter
159+
bne !loop- // if bit counter is not 0 then loop again
160+
161+
// restore a and y from the stack
162+
pla
163+
tay
164+
pla
165+
rts
166+
167+
*=$2000 "random bytes"
168+
// array that holds the 255 random bytes that were generated
169+
random_bytes: .fill 255,00
170+
bit_pattern: .byte 0

Diff for: nuclear_random/main.dbg

+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
<C64debugger version="1.0">
2+
<Sources values="INDEX,FILE">
3+
0,KickAss.jar:/include/autoinclude.asm
4+
1,/Users/rdoetjes/develop/nuclear_random/main.asm
5+
</Sources>
6+
7+
<Segment name="Default" dest="" values="START,END,FILE_IDX,LINE1,COL1,LINE2,COL2">
8+
<Block name="Basic">
9+
$0801,$0802,0,56,2,56,6
10+
$0803,$0804,0,57,5,57,9
11+
$0805,$0805,0,58,5,58,9
12+
$0806,$0809,0,59,2,59,6
13+
$080a,$080a,0,60,2,60,6
14+
$080b,$080c,0,62,5,62,9
15+
</Block>
16+
<Block name="Unnamed">
17+
$1000,$1002,1,11,5,11,7
18+
$1003,$1005,1,12,5,12,7
19+
$1006,$1008,1,15,5,15,7
20+
$1009,$100b,1,18,5,18,7
21+
$100c,$100e,1,19,5,19,7
22+
$100f,$1011,1,20,5,20,7
23+
$1012,$1014,1,22,5,22,7
24+
$1015,$1017,1,24,5,24,7
25+
$1018,$1019,1,25,5,25,7
26+
$101a,$101c,1,27,5,27,7
27+
$101d,$101e,1,28,5,28,7
28+
$101f,$1021,1,30,5,30,7
29+
$1022,$1024,1,33,5,33,7
30+
$1025,$1026,1,34,5,34,7
31+
$1027,$1029,1,36,5,36,7
32+
$102a,$102a,1,40,5,40,7
33+
$102b,$102c,1,41,5,41,7
34+
$102d,$102e,1,42,5,42,7
35+
$102f,$102f,1,43,5,43,7
36+
$1030,$1032,1,44,5,44,7
37+
$1033,$1035,1,45,5,45,7
38+
$1036,$1038,1,46,5,46,7
39+
$1039,$1039,1,47,5,47,7
40+
$103a,$103c,1,56,5,56,7
41+
$103d,$103e,1,57,5,57,7
42+
$103f,$1040,1,58,5,58,7
43+
$1041,$1043,1,59,5,59,7
44+
$1044,$1045,1,60,5,60,7
45+
$1046,$1047,1,61,5,61,7
46+
$1048,$1048,1,62,5,62,7
47+
$1049,$104a,1,66,5,66,7
48+
$104b,$104d,1,67,5,67,7
49+
$104e,$104e,1,68,5,68,7
50+
$104f,$1051,1,73,5,73,7
51+
$1052,$1053,1,74,5,74,7
52+
$1054,$1055,1,75,5,75,7
53+
$1056,$1056,1,76,5,76,7
54+
$1057,$1059,1,82,5,82,7
55+
$105a,$105b,1,83,5,83,7
56+
$105c,$105d,1,84,5,84,7
57+
$105e,$105e,1,85,5,85,7
58+
$105f,$1061,1,89,5,89,7
59+
$1062,$1064,1,90,5,90,7
60+
$1065,$1066,1,91,5,91,7
61+
$1067,$1067,1,92,5,92,7
62+
$1068,$106a,1,96,5,96,7
63+
$106b,$106b,1,97,5,97,7
64+
$106c,$106e,1,98,5,98,7
65+
$106f,$106f,1,99,5,99,7
66+
$1070,$1072,1,103,5,103,7
67+
$1073,$1073,1,104,5,104,7
68+
$1074,$1075,1,105,5,105,7
69+
$1076,$1078,1,106,5,106,7
70+
$1079,$1079,1,107,5,107,7
71+
$107a,$107c,1,111,5,111,7
72+
$107d,$107d,1,112,5,112,7
73+
$107e,$107f,1,113,5,113,7
74+
$1080,$1081,1,114,5,114,7
75+
$1082,$1082,1,115,5,115,7
76+
$1083,$1085,1,117,5,117,7
77+
$1086,$1086,1,118,5,118,7
78+
$1087,$1089,1,121,5,121,7
79+
$108a,$108c,1,122,5,122,7
80+
$108d,$108e,1,123,5,123,7
81+
$108f,$108f,1,124,5,124,7
82+
$1090,$1090,1,125,5,125,7
83+
$1091,$1091,1,129,5,129,7
84+
$1092,$1092,1,130,5,130,7
85+
$1093,$1093,1,131,5,131,7
86+
$1094,$1095,1,133,5,133,7
87+
$1096,$1098,1,134,5,134,7
88+
$1099,$109b,1,135,5,135,7
89+
$109c,$109d,1,136,5,136,7
90+
$109e,$10a0,1,139,5,139,7
91+
$10a1,$10a3,1,140,5,140,7
92+
$10a4,$10a6,1,141,5,141,7
93+
$10a7,$10a8,1,142,5,142,7
94+
$10a9,$10aa,1,144,5,144,7
95+
$10ab,$10ad,1,145,5,145,7
96+
$10ae,$10af,1,148,5,148,7
97+
$10b0,$10b2,1,151,5,151,7
98+
$10b3,$10b3,1,152,5,152,7
99+
$10b4,$10b5,1,153,5,153,7
100+
$10b6,$10b6,1,156,5,156,7
101+
$10b7,$10b7,1,157,5,157,7
102+
$10b8,$10b8,1,158,5,158,7
103+
$10b9,$10b9,1,159,5,159,7
104+
</Block>
105+
<Block name="random bytes">
106+
$2000,$20fe,1,163,15,163,19
107+
$20ff,$20ff,1,164,14,164,18
108+
</Block>
109+
</Segment>
110+
111+
<Labels values="SEGMENT,ADDRESS,NAME,START,END,FILE_IDX,LINE1,COL1,LINE2,COL2">
112+
Default,$1000,main,1,10,1,10,5
113+
Default,$1006,loop,1,14,1,14,5
114+
Default,$103a,setup,1,55,1,55,6
115+
Default,$1049,start_timer_b,1,65,1,65,14
116+
Default,$104f,wait_for_raster_line,1,71,1,71,21
117+
Default,$1057,wait_for_decay_pulse,1,79,1,79,21
118+
Default,$105f,mask_lowest_bit,1,88,1,88,16
119+
Default,$1068,shift_add_nothing,1,95,1,95,18
120+
Default,$1070,shift_add_1,1,102,1,102,12
121+
Default,$107a,prep_for_next_bit,1,110,1,110,18
122+
Default,$1087,reset_y,1,120,1,120,8
123+
Default,$1091,print_bit_pattern,1,127,1,127,18
124+
Default,$2000,random_bytes,1,163,1,163,13
125+
Default,$20ff,bit_pattern,1,164,1,164,12
126+
Default,$080b,upstartEnd,0,61,1,61,11
127+
</Labels>
128+
129+
<Breakpoints values="SEGMENT,ADDRESS,ARGUMENT">
130+
</Breakpoints>
131+
132+
<Watchpoints values="SEGMENT,ADDRESS1,ADDRESS2,ARGUMENT">
133+
</Watchpoints>
134+
135+
</C64debugger>

Diff for: nuclear_random/main.prg

6.25 KB
Binary file not shown.

Diff for: nuclear_random/main.sym

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
.label wait_for_raster_line=$104f
2+
.label shift_add_1=$1070
3+
.label bit_pattern=$20ff
4+
.label wait_for_decay_pulse=$1057
5+
.label main=$1000
6+
.label reset_y=$1087
7+
.label start_timer_b=$1049
8+
.label shift_add_nothing=$1068
9+
.label loop=$1006
10+
.label mask_lowest_bit=$105f
11+
.label setup=$103a
12+
.label prep_for_next_bit=$107a
13+
.label random_bytes=$2000
14+
.label print_bit_pattern=$1091

Diff for: nuclear_random/main.vs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
al C:104f .wait_for_raster_line
2+
al C:1070 .shift_add_1
3+
al C:20ff .bit_pattern
4+
al C:1057 .wait_for_decay_pulse
5+
al C:80b .upstartEnd
6+
al C:1000 .main
7+
al C:1087 .reset_y
8+
al C:1049 .start_timer_b
9+
al C:1068 .shift_add_nothing
10+
al C:1006 .loop
11+
al C:105f .mask_lowest_bit
12+
al C:103a .setup
13+
al C:107a .prep_for_next_bit
14+
al C:2000 .random_bytes
15+
al C:1091 .print_bit_pattern

0 commit comments

Comments
 (0)