19

How can I obtain the uptime of a Sinclair ZX Spectrum machine (ZX48, ZX128, or Pentagon clone) via their BASIC interpreter? Specifically, the same value used by RANDOMIZE 0 to seed its PRNG. I would assume that this uptime would be the number of cycles the MCU has gone through since boot and so would increment at 3.5 MHz (or some fraction thereof). If it is not possible to obtain through the BASIC interpreter, how would I go about getting it by writing code directly to memory, e.g. with DATA?

The reason I am wondering is because I would like to generate a sequence of random numbers less predictable than that generated by RND. To do that, I need an environmental source of entropy. Inter-keystroke timing is the perfect place to get that entropy (as the ZX has no HWRNG), but only if a high-resolution timer can be sampled at each keystroke. If this is an X/Y question, please let me know.

forest
  • 2,029
  • 12
  • 36
  • I somehow suspect this actually is an xy-question. You seem to want to do the same thing that RANDOMIZE 0 does. The simplest way to do this actually is RANDOMIZE 0 – tofro Feb 23 '18 at 11:01
  • @ilkkachu The question is not asking for any help in inter-event timing. – tofro Feb 23 '18 at 13:14
  • @ilkkachu I don't think so - If you simply wait in a loop for a keystroke, then pick up the frame counter, you have a pretty random seed already. Which is kind of unnecessary on a Spectrum, as the time variation of typical users needed to type in LOAD "" should be enough of a random seed already. Which is another xy-argument. Reproducible seeds from frame counters are a feature of computers that have the capability to auto-start a program. – tofro Feb 23 '18 at 13:22
  • 1
    @tofro, As far as I understood, RANDOMIZE 0 doesn't give you the value of the counter, but uses it directly. So unless you're suggesting reverse engineering the seed of the RNG based on its outputs, I don't think it helps much in getting the value of the actual timer. – ilkkachu Feb 23 '18 at 13:27
  • 1
    @ilkkachu The absolute value of the timer is of no interest to the seed for an RNG - regardless of whether it's the inbuilt or an own one. The important thing is only that it is sufficiently random. And because the FRAMES variable is based on an "alive counter", that should be the case. – tofro Feb 23 '18 at 14:29
  • 1
    If you loop for a keystroke, not relying on a ROM key scanning routine that runs every frame interrupt, you can use BOTH R register and FRAMES counter. Also you can keep your random generator "online", mixing in some entropy every time user presses a key. MItchell-Moore PRNG would fit you well. – lvd Feb 25 '18 at 10:20
  • The absolute value of the counter may be too predictable. Acquiring a sufficient number of inter-keystroke intervals (and combining them appropriately) could get you more entropy. It's an interesting mathematical exercise to work out how many keystrokes are needed to achieve that. – Toby Speight May 30 '19 at 20:34

5 Answers5

26

According to http://www.worldofspectrum.org/ZXBasicManual/zxmanchap25.html, addresses 23672-23674 contain a 24 bit count of 50Hz frame ticks in the UK. I wrote a quick program to print the values, which do indeed seem to start at zero on startup and increment at the right rate, and thus serve as an uptime counter. A simple bit of maths indicates that the counter wraps round roughly once every four days.

pndc
  • 11,222
  • 3
  • 41
  • 64
  • you can combine this with the content of r register which is needed to obtain by assembly and some PEEK .... – Spektre Feb 23 '18 at 08:56
  • 1
    That is, actually, a bit unnecessary. According to the ROM disassembly, a RANDOMIZE 0 does exactly that (seeding the PRNG with FRAMES) – tofro Feb 23 '18 at 10:54
  • @tofro You probably did not read the question thoroughly enough. Forest wants to measure intervals between keystrokes to collect more entropy than is available directly in the FRAMES variable. – pabouk - Ukraine stay strong Feb 23 '18 at 15:42
  • @pabouk In order to do that you simply need to wait for a keystroke and then read FRAMES. But: As the Spectrum doesn't have auto start after power-on, you already do this while waiting for a LOAD "" command. There isn't any more entropy to get than that. – tofro Feb 23 '18 at 16:06
  • @tofro What you describe is a common small-effort procedure. Of course you can collect more entropy than that. To do this you would measure multiple intervals between key-presses and key-releases. In addition to the FRAMES counter you would use the R register. --- To not lose the collected entropy when generating the final seed you cannot simply add the values together. You must use some simple hashing function on the concatenated values. – pabouk - Ukraine stay strong Feb 23 '18 at 16:32
  • @pabouk You don't need to use hashing. Add, sub, mod, and xor will all preserve information. – forest Feb 24 '18 at 02:22
  • @Spektre How would one go about obtaining the value of a register? I suppose using POKE to write the machine code to be executed, have the machine code copy the contents of the R register to a specific memory location, and then use PEEK to retrieve those contents? – forest Feb 26 '18 at 05:32
  • 1
    @forest exactly it was a common technique to have assembly mixed with BASIC sometimes the code was even encoded directly in BASIC code as string or table. Then you call RANDOMIZE USR address and use PEEK address ... – Spektre Feb 26 '18 at 06:48
  • @forest .IIRC the code would look like push ir; pop bc; ld hl,address; ld (hl),c; ret; and may be some push/pops for bc,hl – Spektre Feb 26 '18 at 06:53
10

I would assume that this uptime would be the number of cycles the MCU has gone through since boot and so would increment at 3.5 MHz (or some fraction thereof).

As already mentioned by pndc, the 24 bit counter FRAMES at 5C78h increments (*1) with every frame (*2) displayed and is zeroed at startup. Thus, an overflow happens only after more than 4 days of gameplay :))

The reason I am wondering is because I would like to generate a sequence of random numbers less predictable than that generated by RAND.

FRAMES, or better its lower 16 bits, are already used to initialize the PRNG when RANDOMIZE 0is called, as Tofro pointed out. So there is some randomness, but then again, it's strictly based on the program run, unless the program includes many waiting times for asynchronous external events (like keyboard wait).

To do that, I need an environmental source of entropy. Inter-keystroke timing is the perfect place to get that entropy (as the ZX has no HWRNG), but only if a high-resolution timer can be sampled at each keystroke.

Hmm, there is kind of an high precision timer in every Z80: the refresh (R) Register. It gets incremented in every T3 and T4 clocks of an M1 cycle. That's once per instruction (*3). Only the lower 7 bit are valid, so you may want to collect them if you need more bits. Just make sure there's something un-/less predictable in between.


*1 - Which is an interesting deviation from the ZX81, where a similar counter was decremented

*2 - That's 50 times on European models and 60 on NTSC-Spectrums (sold via mail order in the US) and US-TS2068

*3 - It's a bit more complex, so see this fine Stackoverflow answer for details.

Raffzahn
  • 222,541
  • 22
  • 631
  • 918
3

To do that, you simply initialise the random number generator with 0, like

RANDOMIZE 0

For this specific argument, according to the ROM disassembly, the ZX Spectrum BASIC will pick the value from the lower two bytes of the System variable FRAMES, which is the number of 50Hz interrupts since startup.

In case you want the seed value for your own PRNG, simply PEEK that system variable (05C76H), as pointed out in other answers (But be aware the Spectrum's PRNG isn't actually that bad for an 80ies 8-bit computer and you might be having a hard time writing a better one yourself. The PNRG is actually quite cleverly made without a lot of code because it uses the Arithmetic interpreter of the Spectrum.) You could also RANDOMIZE 0 to make the initial VALUE of RND dependant on the switched-on time and then feed your own, better implementation of a PRNG with the first value obtained by RND.

tofro
  • 34,832
  • 4
  • 89
  • 170
  • Well it's not that bad for very liberal definitions of "random". It repeats only 65536 different values even though the output of RND is larger. – forest Feb 23 '18 at 14:33
  • Which still doesn't disqualify it to be used as a seed for your own, probably better PNRG. – tofro Feb 23 '18 at 14:40
1

Could perform some in(port) commands instead - some ports did not have anything to define the read value, so could return random results. This was very version specific and additional hardware specific too. Not sure what emulators would do.

You could look at the tape port - just play (loud) music at it.

You would watch the keyboard ports (0xfe) directly to see which keys were pressed - loop counter detecting a change is more like your inter-keystroke timing.

10 LET A=0
20 LET B=0
30 LET C=IN (254)
40 IF (B<>C) THEN GOSUB 1000
50 LET A=A+1
60 GOTO 30
1000 PRINT A
1010 LET B=C
1020 RETURN

BASIC is slow; probably need to do that in machine code.

Chenmunka
  • 8,141
  • 3
  • 39
  • 65
Tim Bevan
  • 11
  • 1
  • Welcome to Retrocomputing Stack Exchange. Please read the [tour]. Is that a typo on line 10? – wizzwizz4 Feb 24 '18 at 15:46
  • 3
    Undefined reads aren't actually random, even if they seem rather random. – forest Feb 25 '18 at 01:16
  • @forest Hmm, while I agree in general, the Spectrum might be a special case here. Due the special bus structure it may read data partly defined by the last video data read. Since video is asynchronus to the programm, it may produce hard to predict results. Then again, wigh screen data usually within a certain value range it might as well be worthless again. – Raffzahn Feb 26 '18 at 11:39
  • "just play (loud) music" Not realy. If you want a somewhat equal randomized result, you need to level it out. If at all, best would be 'reading' a deleted tape (white noise) with the sound level up, right where the detector flips. – Raffzahn Feb 26 '18 at 11:42
  • @Raffzahn Do you have any references on that? I know that, in general, undefined reads even of floating gates is often highly predictable despite seeming random. It often takes very, very careful construction and testing of a circuit to allow it to create genuinely random data. It usually requires things like reverse-biased zenner diodes, thermal noise from sensitive floating gates at the breakdown voltage, etc. Simply reading undefined values that are influenced by previous state will not provide actual randomness. – forest Feb 26 '18 at 12:03
  • @forest Take a look at the schematics - the video data lines are are seperated for the processor data lines by a set of resistors. So if the CPU is accessing a non existing port (important, it needs to be realy not decoded to anything), then the read data will be whatever the video logic is reading at that time (or before) on the other side How strong signals are, and what isread depends on timing input circuitry and so on. I haven't tried it, but it is woth a shot. In fact, this sounds like a possible way for a programm to detect if it's running in an emulator or on real hardware :) – Raffzahn Feb 26 '18 at 12:31
  • @forest Building a good random source isn't that hard. The world is full of usable effects. The issue is fine tuning it to work reliable without tuning the randomness - something that is hard to get replicated in large quantities. – Raffzahn Feb 26 '18 at 12:34
  • @Raffzahn In that case it seems like it would be dependent on the data being sampled at unpredictable times. If it were sampled at the maximum rate, the data would be more predictable. One thing I've learned well is that true random sources are very, very hard to come by. Most of the time you can't even program an FPGA to do it! There's a reason techniques like beam splitting are so popular, and even the "poor man's HWRNG" involves a reverse-biased P-N junction right at the breakdown voltage... not something common to see in pre-designed circuits. – forest Feb 26 '18 at 12:35
  • @forest As I sad, it might need some research, but depending on programm and usage it might be woth the time. One doesn't so often come across an idea to distingush emulators form real hardware. And ofc FPGAs aren't a good idea for RNGs - they are the exact oposite of randomness. Hardware random numbers are a domain for classic analogue electronics. – Raffzahn Feb 26 '18 at 12:41
  • @Raffzahn I suppose I'll have to do some research. Unfortunately I have no functioning Spectrum computer at the moment and am stuck with an emulator (albeit a cycle-accurate one). The specific usage is writing a moderately fast cryptosystem (entirely for fun, not for any real high-security requirements). I just think it would be cool to have encrypted tape drives! – forest Feb 26 '18 at 12:44
  • Just flip every other byte with XOR FF and 99.99% of all spectrum users will be kept out :) – Raffzahn Feb 26 '18 at 12:57
0

If RANDOMIZE 0 isn't sufficiently random for your purposes, you could invoke it several times at unpredictable intervals (e.g. using keypress timings as you suggest), then combine the results. You probably want to keep values within the range 0-65535, so that the Spectrum uses exact integers rather than floating-point numbers.

Off-hand (and having not written any ZX Basic for 30 years!), I'd do the combining of partial result r with a new random number something like

LET r = r % 255 * 257 + RND % 256

This allows us to multiply by 257 (which I think is prime), without overflowing the integer range.

Toby Speight
  • 1,611
  • 14
  • 31
  • For what it's worth, Spectrum BASIC doesn't have a modulus operator. You can obviously create it yourself one way or another. – Philip Kendall May 30 '19 at 22:07