1

I am trying to find a way to automatically set the random seed in Microsoft Z80 Basic 4.7b on an RC 2014, as one would in Sinclair BASIC with RAND or RANDOMIZE.

I have got as far as understanding that you can call RND with a negative number to reset the seed, but I have not worked out how to make that negative number a random one other than asking the user.

Is there a convenient way of getting a random number to feed into RND as a negative number to have a different starting point each time a program is run?

It turns out that this particular BASIC on the RC 2014 is compatible with NASCOM BASIC, and INKEY$ is apparently not implemented - it is not listed as a reserved word whereas STR$, LEFT$ and others are, so a loop with INKEY$ will not work there.

I would also be interested in knowing a general approach with Microsoft BASIC, so I could use it on my Soviet BK machine too. I'm pretty sure INKEY$ is implemented there.

5
  • 1
    System specific - need to read something that has at least some sort of randomness and use it to produce a seed. If the system maintains some sort of cycle count or clock that runs since it was started that would be one possible source. Commented Oct 21 at 4:01
  • 1
    So what things you have available? Can you read the Z80 register with the DRAM refresh counter? Does the system have some kind of system timer tick interrupt counter available? If there is a CRT controller, does it have any flags available on status where it is drawing the screen? Is there a button or keyboard or other input available, so you can e.g. wait counting loops until key is pressed? Commented Oct 22 at 6:09
  • So the answer to your question is the r register, which at least loops from 0 to 255, which is sufficiently random for me. Commented Oct 22 at 16:41
  • 1
    @harlandski No, the refresh counter is 7-bit so it loops from 0 to 127. Commented Oct 23 at 18:17
  • Good catch, I'll update my answer accordingly Commented Oct 24 at 2:02

3 Answers 3

3

The truth is: A digital computer cannot produce a truly random number. What you get back from RND is a series of values that may look random, but isn't.

Modern computers use a source of (analogue) white noise to create randomness.

Microsoft Basic doesn't have that luxury. You need to rely on external input to obtain randomness. That external input can be the computer's clock (if it has one), for example, or a counting loop that has to be interrupted by the user pressing a key. (That is one reason for program title pictures in games).

100 REM Display title screen
110 a$ = INKEY$() : IF LEN (A$) <> 0 THEN 110 : REM eat away stray keypresses
120 PRINT "Press any key to continue"
130 seed% = 1234
140 seed% = seed% + 1
150 a$ = INKEY$() : IF LEN (a$) = 0 THEN 140
160 REM RANDOMIZE seed% : REM use if your BASIC has it, otherwise the following lines
170 IF seed% < 0 THEN 190
180 seed% = -seed%
190 x = RND (seed%)

This increments the integer variable seed% as long as the user doesn't press a key and uses the result as a seed for the random number generator.

9
  • 1
    I understand that it is not truly random, but I just want something to put the seed to something different each time. Have you tried your code on an RC 2014? I've a feeling some of the commands and syntax here don't work on it, but I'll give it a try when I can. For example I'm pretty sure there's no RANDOMIZE command, when I tried it I got syntax error. Commented Oct 21 at 10:42
  • I don't own an RC2014. The code is in accordance with the MS-BASIC manuals I could find. If your BASIC doesn't know RANDOMIZE, you need to call RND with the negative seed you want Commented Oct 21 at 11:42
  • 1
    PRINT LEN(INKEY$) gives "", and I've done various other tests to convince myself that INKEY$ is not working properly on my setup for some reason. Commented Oct 22 at 14:15
  • 1
    I've got some confirmation that INKEY$ just isn't implemented in the BASIC version I have, so that explains why it is always just "" - it is considered to be just an unassigned string label. Commented Oct 22 at 14:33
  • 2
    For the record, we should quote Johnny van Neumann - "Anyone who considers arithmetical methods of producing random numbers is, of course, in a state of sin". Commented Oct 23 at 0:26
2

At least your system has one sort of source of randomness, the 7-bit DRAM refresh counter address, if there is no other source of variable timing or waiting of time events between keyboard presses is possible.

1

So while tofro's answer should work, one way or the other INKEY$ is not working properly on my current setup.

I have found a solution which several people have hinted at, which is to find some kind of counter in memory which can be read to give something like a random seed.

I found the exact solution here, but I have simplified things as I am not using an emulator, and I don't need all the PRINTed commentary. Basically the routine takes the value of register r which is counting CPU cycles from 0 to 127, not using bit 8. It uses the ROM ABPASS routine, which passes the contents of AB back to the USR call.

The simplified assembly language goes like this:

ABPASS     = 0x117D

.org    0xF800

usr:

    ld      a, r           

    ld      b, a

    xor     a               ; clear a as you only want a one byte number

    jp      ABPASS

And this can be executed in BASIC like this, with the seed used to then "roll a d6".

20 REM == poke at 0xF800 ==
30 let mb=&HF800
100 REM == Poking in the 'random' seed program ==
110 read op
120 if op = 999 then goto 200
130 poke mb, op
140 let mb = mb + 1
150 goto 110
200 REM == JP start address (c3 00 f8) jp f800 ==
210 mb = &H8048 
220 poke mb, &HC3
230 poke mb+1, &H00
240 poke mb+2, &HF8
260 seed = usr(0)
270 x = rnd(-seed)    
280 print int(rnd(1)*6)+1
290 end
9000 REM == program ==
9001 DATA 237, 95, 71, 175, 195, 125, 17
9003 DATA 999

This produces different results after a hard reset, and is adequately random for my purposes.

Having corrected some syntax errors from here I have also made a simpler routine using BASIC only which generates a random seed as INKEY$ would be able to if it were implemented:

10 OUT 128,22
20 PRINT "Roll a d6! (Hit any key)"
30 COUNT = -1
40 IF (INP(128) AND 1) = 0 THEN 100
50 I = INP(129)
60 OUT 128,150
70 S = RND (COUNT)
80 PRINT INT(RND (1)*6)+1
90 END
100 COUNT = COUNT - 1
110 GOTO 40

I is the ASCII value of the key(s) pressed, so that is a pretty direct way of doing INKEY$ in Nascom BASIC.

5
  • 1
    Well, nice. you've found a solution - But you should definitely dive deeper into the INKEY$ problem, as that is such an ubiquitous command that it will continue to haunt you. Commented Oct 22 at 14:29
  • Yeah, I think I have - someone confirmed that INKEY$ is not implemented in this version of BASIC. I take it that the BASIC I have is a clone of NASCOM BASIC. Commented Oct 22 at 14:39
  • I'm looking in the NASCOM BASIC manual. STR$, CHR$, LEFT$, RIGHT$ and MID$ are reserved words, but INEKY$ is not listed... Commented Oct 22 at 14:52
  • 1
    Well you're not on a NASCOM. But that omission ws actually realized and the NASCOM Basic manual has an INKEY$ replacement under "useful routines" relatively far to the end. Of course, that will not work on your system. Commented Oct 22 at 16:12
  • 1
    So here is a minimal implementation of INKEY$ for Nascom BASIC: 10 REM Inkey$ for Nascom BASIC 20 OUT 128,22 30 PRINT "Hit any key" 40 IF (INP(128) AND 1) = 0 THEN 40 50 X = INP(129) 60 OUT 128,150 70 PRINT "You pressed "; CHR$(X) 80 END Commented Oct 23 at 16:12

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.