17

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.

  • 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
  • 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
22

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.

  • 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 8: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 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
  • 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 6:48
9

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.

  • Would the Russian Pentagon clone have the behavior of the European models or the NTSC Spectrums? – forest Feb 23 '18 at 14:30
  • 2
    European, as the Russian TV system also works by 50 (half) Framesper second. – Raffzahn Feb 23 '18 at 18:12
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.

  • 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.

  • 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 1: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
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.

  • 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 at 22:07
  • Thanks @Philip - I did say it was a long time ago! – Toby Speight May 31 at 7:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.