Posts: 31
Threads: 8
Joined: Jul 2018
Reputation:
12
If I write this simple program:
Code: DIM u as UBYTE
10 let u =rnd*40
print u
pause 0
if code inkey$=13 then stop
40 goto 10
a number between 0 and 40 will be shown at every pressing of ENTER.
However, inside of a larger program, this instruction:
Code: DIM x AS UBYTE
...
x = rnd*40
always returns 38 when executed.
Also, the RND of the first example always returns exactly the same sequence when started afresh, at least under emulation (Spectaculator): 9, 34, 15, 7, 16, 25, 13, 37, 7, 24, 19 etc.
In the past I used a simple routine (by John Connolly) to generate random numbers. Here it is, modified to let it return values from 0 to 31:
Code: LD A,R
AND %00011111
LD (address),A
When placed in a ZX-Basic listing as a subroutine, it will always return the first value. The only way to make it work is to assemble it elsewhere and call it from within ZX-BASIC with the usual RANDOMIZE USR call, then assign the PEEK [address] to a variable.
Posts: 1,763
Threads: 55
Joined: Aug 2019
Reputation:
24
Alessandro Wrote:If I write this simple program:
Code: DIM u as UBYTE
10 let u =rnd*40
print u
pause 0
if code inkey$=13 then stop
40 goto 10
a number between 0 and 40 will be shown at every pressing of ENTER.
However, inside of a larger program, this instruction:
Code: DIM x AS UBYTE
...
x = rnd*40
always returns 38 when executed. This might be a bug. Please provide me some code (you can do it privately, if you want). Also keep in mind that memory address 23670 and 23671 (ROM SEED variable) is also used by ZX Basic, so if something is accidentally writing there it will cause the RND sequence to repeat.
Quote:Also, the RND of the first example always returns exactly the same sequence when started afresh, at least under emulation (Spectaculator): 9, 34, 15, 7, 16, 25, 13, 37, 7, 24, 19 etc.
That's to be expected. You have to RANDOMIZE the sequence first. Use RANDOMIZE to have a different sequence each time. Since RANDOMIZE uses the system ROM timer ensure (within an emulator) that the time the program starts is always different. A trick is to ask the user to press a key before randomize, or doing it during the program main menu.
Quote:In the past I used a simple routine (by John Connolly) to generate random numbers. Here it is, modified to let it return values from 0 to 31:
Code: LD A,R
AND %00011111
LD (address),A
When placed in a ZX-Basic listing as a subroutine, it will always return the first value. The only way to make it work is to assemble it elsewhere and call it from within ZX-BASIC with the usual RANDOMIZE USR call, then assign the PEEK [address] to a variable.
When using asm as a function, you should use FASTCALL to inline return a register. Try this:
Code: FUNCTION FASTCALL myRND As Byte
Asm
ld a, r
and 0x1F
End Asm
END FUNCTION
Now call if with myRND (i.e. PRINT myRND).
Please tell me if this helps.
Posts: 31
Threads: 8
Joined: Jul 2018
Reputation:
12
Unfortunately it does not work. I wrote this simple program (prova.bas):
Code: for n = 1 TO 20
print at 2,0; myRND; ",";
next n
stop
FUNCTION FASTCALL myRND As Byte
Asm
ld a, r
and 0x1F
End Asm
END FUNCTION
but at the moment of compiling it, here is the result:
prova.bas:1: warning: Using default implicit type 'float' for 'myRND'
prova.bas:9: identifier 'myRND' is a var, not a function
prova.bas:9: 'myRND' already declared as a var at 1
To make it work, I must insert the Assembly code into a subroutine:
Code: 10 myRND()
print peek 49152; ",";
pause 0
if code inkey$=13 then goto 10
stop
sub myRND()
Asm
ld a, r
and 0x1F
LD (49152),A
End Asm
end sub
This will produce the desired effect.
Posts: 1,763
Threads: 55
Joined: Aug 2019
Reputation:
24
08-08-2018, 09:06 PM
(This post was last modified: 12-30-2020, 05:23 PM by boriel.)
Alessandro Wrote:Unfortunately it does not work. I wrote this simple program (prova.bas):
Code: for n = 1 TO 20
print at 2,0; myRND; ",";
next n
stop
FUNCTION FASTCALL myRND As Byte
Asm
ld a, r
and 0x1F
End Asm
END FUNCTION
but at the moment of compiling it, here is the result:
prova.bas:1: warning: Using default implicit type 'float' for 'myRND'
prova.bas:9: identifier 'myRND' is a var, not a function
prova.bas:9: 'myRND' already declared as a var at 1
You have to declare the function *before* it's being used. Put the function before the code that invokes it; try and tell me.
Posts: 31
Threads: 8
Joined: Jul 2018
Reputation:
12
Thank you, now it works
On a side note, I thought that functions, being a kind of subroutines (at least that's what I read in the Wiki) should be put at the end of the code, as it was normal with subroutines. (I never mastered the use of functions in Sinclair BASIC to be honest.)
Posts: 1,763
Threads: 55
Joined: Aug 2019
Reputation:
24
Alessandro Wrote:Thank you, now it works
On a side note, I thought that functions, being a kind of subroutines (at least that's what I read in the Wiki) should be put at the end of the code, as it was normal with subroutines. (I never mastered the use of functions in Sinclair BASIC to be honest.)
If you need to call a function before implementing it, you can also pre-declare a function (like in C), using DECLARE, followed by the function header:
Code: DECLARE FUNCTION FASTCALL myRND as Byte
for n = 1 TO 20
print at 2,0; myRND; ",";
next n
stop
FUNCTION FASTCALL myRND As Byte
Asm
ld a, r
and 0x1F
End Asm
END FUNCTION
|