@evanh said:
Isn't that just using the system ticks variable, after waiting on human input, to produce a seed? That shouldn't be hard to replicate.
After a P1 reset the system ticks variable is always the same when the program starts executing. Asking for user input adds some randomness to the timing before the system timer is consulted. The P2 has a source of randomness I could use - but the P1 doesn't have such a thing (AFAIK).
PS: The Prop2's startup seed comes from ADC sampling noise I think.
EDIT: Yep ... Ha, it just samples the bitstream direct. 50 x 31 bits = 1550 random bits.
' Seed xoroshiro 128** using delta-sigma ADC bits from calibration mode
'
wrpin ##$00100000,#rx_pin 'put rx pin in adc gio calibration mode
mov x,#50 'ready to seed 50 times with 31 bits
.seed rep #2,#31 'get 31 bits (31*4 clocks = 124/20 = ~6us)
testp #rx_pin wc
rcl y,#1
bith y,#31 'seed via hubset
hubset y
djnz x,#.seed
wrpin #0,#rx_pin 'return rx pin to normal mode
EDIT2: I think Xoroshiro 128** is a typo on Chip's behave. I'm pretty sure that was the original Xoroshiro 128+ algorithm used to make the free-running global generator. Dunno why he typed a double asterisk.
EDIT3: The mark-space ratio of GIO level will be around 20% ones to 80% zeros. There will be aliasing beat patterns with patches of all zeros in the sysclock/4 sampling. Still, no two will be the same.
Right, I thought you were targetting compatibility for those old programs is all.
Using only built-in features of the Prop1 might be tricky alright .... maybe trying spamming a counter's PLL divider to keep it unstable ... but that would need to use a pin for output I suspect ... could momentarily use the comms TX pin if no other was suitable.
EDIT: Ah, not the divider, that's just a post-VCO divider. It's the PLL's input coming from the main PHSx counter that is important for stability of the VCO in the PLL. So spamming lots of changes to FRQx to upset the PLL's reference frequency should then keep the PLL unstable. Just toggling between two target frequencies should be enough.
Then just have to collect the output. It could be done like Chip did in the Prop2 - treat the pin as bitstream directly. Or another way might be take timing measurements with the second counter.
@evanh said:
Using only built-in features of the Prop1 might be tricky alright .... maybe trying spamming a counter's PLL divider to keep it unstable ... but that would need to use a pin for output I suspect ... could momentarily use the comms TX pin if no other was suitable.
EDIT: Ah, not the divider, that's just a post-VCO divider. It's the PLL's input coming from the main PHSx counter that is important for stability of the VCO in the PLL. So spamming lots of changes to FRQx to upset the PLL's reference frequency should then keep the PLL unstable. Just toggling between two target frequencies should be enough.
Huh, yeah, same idea but different implementation. Also somehow is extracting the random bits from the PHSx register. That implies PHSx is in some way integral to the VCO loop inside the PLL. Or is maybe looped back from the PLL output. I don't know enough about the Prop1 to understand the detail.
EDIT: Ahhh, the video out can be driven by a counter. So it's the WAITVID instruction that is sampling the counter's PLL jitter. Chip is just looping that back in with the ADD PHSA,CNT
This is a full release. I was embarrassed not to have known about Chip's RealRandom object so I thought I had better put out a new release to incorporate it. The addition of the new RANDOM plugin is the only significant new feature, and it is only required on the Propeller 1. I use it in Dumbo Basic to eliminate the need to ask for user input when seeding the random number generator.
Here is the relevant extract from the README.TXT:
RELEASE 7.3
New Functionality
-----------------
1. A new random number plugin has been added for the Propeller 1, based on
Chip Gracey's RealRandom.spin. The plugin is enabled by defining the
Catalina symbol RANDOM. For example:
catalina ex_random.c -lci -C RANDOM
Note that a random number generator is built into the Propeller 2, so the
plugin is not required on that platform. The RANDOM plugin occupies a
cog on the Propeller 1. It generates random data continuously, and writes
a new 32 bit random number to the second long in its registry entry
approximately every 100us (at 80Mhz).
C provides a pseudo random number generator called rand() and this must
be seeded using the srand() function, otherwise it will always return
the same sequence of pseudo random numbers.
In previous releases, the getrand() function was a "stopgap" function -
it generated true random data on the Propeller 2, but on the Propeller 1
it only generated pseudo random data, seeded by the system clock. However,
there are good reasons to have both a pseudo random number generator and
a true random number generator, so with the addition of the new Propeller
1 RANDOM plugin, getrand() has been modified to always generate 32 bit
pseudo random numbers seeded by the system clock on both the Propeller 1
and 2, and a new getrealrand() function has been added which returns a 32
bit true random number on both the Propeller 1 and 2 (provided the RANDOM
plugin has been loaded on the Propeller 1). If the RANDOM plugin has not
been loaded (which may be because there is not a spare cog available) on
the Propeller 1 then getrealrand() uses the same technique as getrand().
The new getrealrand() function will generate unique random numbers in
situations where the Propeller 1 previously would not, such as if a
program was automatically started immediately after power up. In such
situations the system clock would be reset, so the previous technique
of seeding a pseudo random number generator with the system clock would
always generate the same sequence of pseudo random numbers.
Here is a summary of the random number routines:
rand(), srand() -> rand() generates pseudo random numbers, in the
range 0 to MAX_RAND (32767), and srand() can
be used to seed the random number generator
getrand() -> getrand() generates 32 bits of pseudo random
data using rand(). On first call it also seeds
the random number generator using srand() and
the current system clock. This avoids the need
to explicitly call srand()
getrealrand() -> getrealrand() returns 32 bits of random data.
On the Propeller 1 this will be true random data
if the RANDOM plugin is loaded, otherwise it
will use the same technique as getrand(). On the
Propeller 2 it always returns true random data.
The program ex_random.c in demos/examples has been updated to demonstrate
both rand() and getrand() (which both generate pseudo random numbers) as
well as the getrealrand() function (which generates true random numbers).
2 Dumbo Basic has been updated to allow the use of the new getrealrand()
function (described in point 1, above). A new RANDOM option has been
added to the RANDOMIZE statement to seed Basic's built in pseudo random
number generator with getrealrand().
Here are all the RANDOMIZE options:
RANDOMIZE -> prompt for the seed
RANDOMIZE TIMER -> use the the current time as the seed
RANDOMIZE <expr> -> use <expr> as the seed
RANDOMIZE RANDOM -> use getrealrand() as the seed (on the P2,
or when compiled with RANDOM on the P1) or
else prompt for the seed (note this is an
extension to GW Basic).
The RND function has been extended to allow for the generation of either
pseudo random or true random numbers. Instead of just RND, use RND(x)
where ...
x > 0 generates the next pseudo random number
x = 0 returns the last random number generated (pseudo or true)
x < 0 generates a true random number (note this is an extension to GW
Basic, where x < 0 reseeds the random number generator - but
that can also be done using the RANDOMIZE statement).
Dumbo Basic now enables the RANDOM plugin by default when built for the
Propeller 1, and the basic demo programs poker.bas, blackjck.bas,
startrek.bas and ut-trek.bas have been updated to use the RANDOM option
of the RANDOMIZE statement instead of the TIMER option, which depended
on variations in user input to generate a random seed.
3. Dumbo Basic's USING clause (used in PRINT, PRINT# and LPRINT statements)
now accepts more GW Basic format options:
+ : a '+' indicates a leading sign will be printed (i.e. '+' or '-')
except as noted for '-' (below). The '+' also counts as another
digit in the output. In GW Basic the '+' must be in the first
position of the USING clause, but in Dumbo Basic it can appear
in any position before any decimal point (the actual position
makes no difference - the sign will always be printed immediately
before the number). Note that (unlike GW Basic) Dumbo Basic always
prints a sign for negative numbers - adding '+' makes it also
include a leading sign for positive numbers.
- : a '-' anywhere in the clause indicates a trailing sign will be
printed for negative numbers. The '-' does NOT count as another
digit in the output - if the number is not negative, a space
will be printed in its place. In GW Basic the '-' must be
in the last position of the USING clause, but in Dumbo Basic
it can be in any position (the actual position makes no
difference - the '-' will always be printed immediately after
the number). If both '+' and '-' are included in the same USING
clause then the '+' is just treated as another digit position.
^^^^ : a '^^^^' anywhere in the clause indicates an exponent of form
'ESNN' will be used, where E is the letter 'E', S is the sign
of the exponent and NN is the 2 digit exponent. The '^'
characters do NOT count as additional digits - they always
indicate an exponent. In GW Basic the '^^^^' must appear at
the end of the USING clause, but in Dumbo Basic it can be in
any 4 consecutive positions. The actual position makes no
difference.
For example, the statements:
PRINT USING "##.#^^^^"; 0.001
PRINT USING "+#.#^^^^"; 0.001
PRINT USING "##.#^^^^"; -0.001
PRINT USING "+#.#^^^^"; -0.001
PRINT USING "+#.#^^^^-"; 0.001
PRINT USING "##.#^^^^-"; -0.001
PRINT USING "+#.#^^^^-"; -0.001
would result in the following output:
1.0E-03
+1.0E-03
-1.0E-03
-1.0E-03
+1.0E-03
1.0E-03-
1.0E-03-
Other Changes.
--------------
1. The pseudo random number generator getrand() was returning only 31 bits
of random data, not 32. Affected the Propeller 1 only.
2. Dumbo Basic was not correctly keeping track of the current position in the
current line of output, which meant using ',' as a separator to move to
the next 14 character print zone did not always end up with the output in
the correct position. Affected Dumbo Basic on the Propeller 1, Propeller 2,
and also on Windows and Linux.
3. All the Dumbo Basic example programs are now UNIX format files, not DOS
format files. This makes them easier to edit using vi on Catalyst, which
expects UNIX format files by default. Affected Dumbo Basic on the Propeller
1, Propeller 2, and also on Windows and Linux.
It's been so long since I added a new Propeller 1 plugin that I made notes as I figured out how to do it all over again. Catalina's registry architecture is well suited to this kind of plugin, so this was possibly the easiest one I have ever added - which makes it ideal to document for future reference:
These instructions show the steps used in creating the new RANDOM plugin and
making it accessible via a new C library function.
Note that these instructions only include details for adding the plugin to
the Propeller 1 default target (which is in the directory 'target\p1'), but
similar steps apply to other other targets that also need to include the new
plugin (such as the embedded target which is in directory 'embedded\p1').
Also note that the details for adding new plugins to Propeller 2 targets are
similar in concept but differ in detail, but are not included because the
RANDOM plugin is only applicable to the Propeller 1.
Step 1. Create the plugin from the existing Spin file
In this case the file target\p1\Catalina_RND_plugin.spin
was created from RealRandom.spin by adding code to register
the plugin on start up, and to write the results back to the
registry instead of a Spin variable. Examine these two files
for more details.
Step 2. Define a constant for the plugin
Plugin constants are defined in the Spin file:
target\p1\Catalina_Common.spin
where the following was added:
LMM_RND = 32 ' Random Number Generator
and also in the C file:
source\lib\include\plugin.h
where the following was added:
#define LMM_RND 32
Step 3. Define a name for the plugin
Plugin names (e.g. used when printing the registry)
are defined in:
source\lib\catalina\plugname.c
where the following was added:
case LMM_RND : return "Random Number Generator";
Step 4. Add the plugin to Extras.spin
Extra plugins are loaded from the file:
target\p1\Extras.spin
where the following was added to the OBJ declarations:
#ifdef RANDOM
RND : "Catalina_RND_Plugin"
#endif
and the following was added to the Setup function:
#ifdef RANDOM
RND.Setup
#endif
and the following was added to the Start function:
#ifdef RANDOM
RND.Start
#endif
Step 5. Define a function to access the plugin
In this case, the function was defined in:
source\lib\include\prop.h
where the following was added:
int getrealrand();
Step 6. Write a function to access the plugin
In this case the function was created in:
source\lib\catalina\getrealrand.c
where the following code was added to locate the plugin in the
registry and (if found) read a new random number from the
appropriate registry entry:
#include <prop.h>
#include <plugin.h>
int getrealrand() {
static int cog = -1;
static request_t *request_block = NULL;
static int last_random = 0;
int new_random = 0;
if (cog == -1) {
cog = _locate_plugin(LMM_RND);
if (cog >= 0) {
request_block = REQUEST_BLOCK(cog);
}
}
if (request_block) {
// RANDOM plugin loaded - wait for a new random
while ((new_random == 0) || (new_random == last_random)) {
new_random = request_block->response;
}
last_random = new_random;
}
else {
// RANDOM not loaded - fall back to getrand()
new_random = getrand();
last_random = new_random;
}
return new_random;
}
Step 7. Modify the relevant Makefile to build the library function
In this case the relevant Makefile is:
source\lib\catalina\Makefile
where the following line was added:
getrealrand.s \
Step 8. Rebuild the C library
In the directory:
source\catalina\lib
execute the following command:
build_all
This is a patch release that must be installed over an existing Catalina 7.3 installation.
Here is the relevant extract from the README.TXT:
RELEASE 7.3.1
New Functionality
-----------------
1. None.
Other Changes.
--------------
1. When the alloca() function was added (release 7.0) it increased the stack
requirements for all programs very slightly, but the stack sizes allocated
for Factories in the Multi-Processing version of Lua was not updated. This
led to various issues but the most obvious was that Lua programs that tried
to use the sbrk function to defragment the C heap would not run correctly.
Affected the Propeller 2 only.
It would be quite feasible to make the random routine into a library only call without the need for another cog. Obviously such an implementation will consume processing time and use the counterA+vid hardware of the cog it runs on, but I'd suspect those will generally not be in use on the primary C cog anyway.
Something I have meant to do on every release, and keep forgetting ...
The two Lua versions of Startrek (star.lua and star-tos.lua) both contain code that deliberately slows down the program output. The intention is to simulate how the game would have appeared on an old slow teletype terminal. Amusing, but a nuisance on a screen. To disable this, use the vi text editor and find the line that says:
@evanh said:
It would be quite feasible to make the random routine into a library only call without the need for another cog. Obviously such an implementation will consume processing time and use the counterA+vid hardware of the cog it runs on, but I'd suspect those will generally not be in use on the primary C cog anyway.
Yes. Also, it would be good enough for most purposes to just generate one truly random number on startup, to use as a seed for the pseudo random number generator. Then re-use the cog.
Either or both of these might make it into a release at some point. But at least Catalina now has an option for a real random number generator on the Propeller 1, which was something that was missing.
Comments
After a P1 reset the system ticks variable is always the same when the program starts executing. Asking for user input adds some randomness to the timing before the system timer is consulted. The P2 has a source of randomness I could use - but the P1 doesn't have such a thing (AFAIK).
Ross.
I'm lost. Is the user input not an option?
PS: The Prop2's startup seed comes from ADC sampling noise I think.
EDIT: Yep ... Ha, it just samples the bitstream direct. 50 x 31 bits = 1550 random bits.
EDIT2: I think Xoroshiro 128** is a typo on Chip's behave. I'm pretty sure that was the original Xoroshiro 128+ algorithm used to make the free-running global generator. Dunno why he typed a double asterisk.
EDIT3: The mark-space ratio of GIO level will be around 20% ones to 80% zeros. There will be aliasing beat patterns with patches of all zeros in the sysclock/4 sampling. Still, no two will be the same.
Yes, that's why I do it.
But I would like not to have to add a line like that to every P1 program that needs random numbers.
EDIT: For instance, what if the program does not otherwise require any user input? How to seed a random number generator on the P1 in that case?
Right, I thought you were targetting compatibility for those old programs is all.
Using only built-in features of the Prop1 might be tricky alright .... maybe trying spamming a counter's PLL divider to keep it unstable ... but that would need to use a pin for output I suspect ... could momentarily use the comms TX pin if no other was suitable.
EDIT: Ah, not the divider, that's just a post-VCO divider. It's the PLL's input coming from the main PHSx counter that is important for stability of the VCO in the PLL. So spamming lots of changes to FRQx to upset the PLL's reference frequency should then keep the PLL unstable. Just toggling between two target frequencies should be enough.
Then just have to collect the output. It could be done like Chip did in the Prop2 - treat the pin as bitstream directly. Or another way might be take timing measurements with the second counter.
This sounds like what Chip's RealRandom object does: https://obex.parallax.com/obex/real-random/
Huh, yeah, same idea but different implementation. Also somehow is extracting the random bits from the PHSx register. That implies PHSx is in some way integral to the VCO loop inside the PLL. Or is maybe looped back from the PLL output. I don't know enough about the Prop1 to understand the detail.
EDIT: Ahhh, the video out can be driven by a counter. So it's the WAITVID instruction that is sampling the counter's PLL jitter. Chip is just looping that back in with the ADD PHSA,CNT
Aha! I didn't know about that object. I'll investigate incorporating it. Thanks.
Ross.
Catalina 7.3 has been released here.
This is a full release. I was embarrassed not to have known about Chip's RealRandom object so I thought I had better put out a new release to incorporate it. The addition of the new RANDOM plugin is the only significant new feature, and it is only required on the Propeller 1. I use it in Dumbo Basic to eliminate the need to ask for user input when seeding the random number generator.
Here is the relevant extract from the README.TXT:
It's been so long since I added a new Propeller 1 plugin that I made notes as I figured out how to do it all over again. Catalina's registry architecture is well suited to this kind of plugin, so this was possibly the easiest one I have ever added - which makes it ideal to document for future reference:
Ross.
Deleting this post as it has been superseded by the Catalina 7.3.1 patch release.
Ross.
Catalina 7.3.1 has been released here.
This is a patch release that must be installed over an existing Catalina 7.3 installation.
Here is the relevant extract from the README.TXT:
It would be quite feasible to make the random routine into a library only call without the need for another cog. Obviously such an implementation will consume processing time and use the counterA+vid hardware of the cog it runs on, but I'd suspect those will generally not be in use on the primary C cog anyway.
Something I have meant to do on every release, and keep forgetting ...
The two Lua versions of Startrek (star.lua and star-tos.lua) both contain code that deliberately slows down the program output. The intention is to simulate how the game would have appeared on an old slow teletype terminal. Amusing, but a nuisance on a screen. To disable this, use the vi text editor and find the line that says:
DisableTeleprint = false
and change it to ...
DisableTeleprint = true
Now, go save the Federation!
Yes. Also, it would be good enough for most purposes to just generate one truly random number on startup, to use as a seed for the pseudo random number generator. Then re-use the cog.
Either or both of these might make it into a release at some point. But at least Catalina now has an option for a real random number generator on the Propeller 1, which was something that was missing.