Randomize and rotate left.

The place for codemasters or beginners to talk about programming any language for the Spectrum.
Nomad
Manic Miner
Posts: 600
Joined: Thu Dec 28, 2017 12:38 pm

Randomize and rotate left.

Post by Nomad »

So as a test I wanted to use the rom randomize routine to generate a random screen address to change a colour of a byte of video ram.

Code: Select all

org 50000

MAIN:
	call RANDPOS
	call DRAWSQUARE

DRAWSQUARE:		; this subroutine writes a value to video ram
	ld a, 32	; loads the value 32 (green) into accumulator
	ld hl,22528	; loads the value 22528 (start of videoram) into HL register pair
	ld (hl), a	; loads the value of a (32) into the memory location pointed to by register pair HL (22528)
	ret

RANDPOS:
	ld b,5		; as a test i put a dummy value into b register
	ld a,b		; b gets loaded into accumulator
	or c		; c is negated??? 
	call $1E5A	; Not sure if I am accessing the function at the right point..
	ret		;return

end 50000
Right so as you can see, the RANDPOS is not updating the drawsquare function yet. Because I am not sure if I need to call 1E4F or 1E5A, what value ranges and what registers need to be loaded before the function is called.

From reading the disassembly I think its BC, and that I really only need to worry about B. But what value range is the subroutine expecting. I can see it calls other functions if it is fed a 0 value. So I am assuming its looking for an integer in B..

I am a bit confused by the rom disassembly.

What I want to do is get the output from random, rotate/shift the output till its in the vram range, then load that value into HL in the drawsquare subroutine.

I can crack on once I can figure out how to call the randomize rom routine. Sorry to ask such a simple question but this one had me stumped.

The thing is I am sure that the output is going to be a value less than 1 and a floating point value. So it leads me to believe that it must be loading the result to a register pair or a memory location..

Code: Select all

10 LET X=RND
20 PRINT X
30 GOTO 10
Image
AndyC
Dynamite Dan
Posts: 1388
Joined: Mon Nov 13, 2017 5:12 am

Re: Randomize and rotate left.

Post by AndyC »

The routines at $1e4f/$135a are the equivalent of RANDOMIZE, not RND, so they're just setting the seed for the random number generator (hence doing something different for a value of 0, since RANDOMIZE 0 bases the seed on the current FRAMES counter).

The actual ROM RND routine is a fair bit more complicated because it's designed to generate floating point values and requires a fair bit of understanding of the ROM calculator stack. You could probably find an easier to use (but not necessarily going to be easy to understand) routine for generating random numbers or if it's not that important that it's particularly random (and not called too often), just read the current value of the R register.
User avatar
Seven.FFF
Manic Miner
Posts: 736
Joined: Sat Nov 25, 2017 10:50 pm
Location: USA

Re: Randomize and rotate left.

Post by Seven.FFF »

Have a play around with this one. It generates high quality bytes pretty fast.
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
NXtel NXTP ESP Update ESP Reset CSpect Plugins
User avatar
R-Tape
Site Admin
Posts: 6353
Joined: Thu Nov 09, 2017 11:46 am

Re: Randomize and rotate left.

Post by R-Tape »

I don't understand the math (maths?) behind it, but I can thoroughly recommend Patrik Rak's XOR Shift RN Generator. It's a short fast routine that gives good numbers in practice. Call the routine and it returns a random 0-255 in A, HL and DE corrupted (I say because I used to forget and it caused me no end of problems!). I find the ROM RND often resulted in predictable sequences.

Code: Select all

;PATRIK RAK's XOR SHIFT RND generator
rnd     ld  hl,nn   ; yw -> zt
        ld  de,nn   ; xz -> yw
        ld  (rnd+4),hl  ; x = y, z = w
        ld  a,l         ; w = w ^ ( w << 3 )
        add a,a
        add a,a
        add a,a
        xor l
        ld  l,a
        ld  a,d         ; t = x ^ (x << 1)
        add a,a
        xor d
        ld  h,a
        rra             ; t = t ^ (t >> 1) ^ w
        xor h
        xor l
        ld  h,e         ; y = z
        ld  l,a         ; w = t
        ld  (rnd+1),hl
        ret
You need to seed it with some peeseudonumbers at the beginning of your program though, same as RANDOMIZE. I use something like this:

Code: Select all

	call waitkey
	ld a,(23560)	;get last ASCII pressed
	ld (rnd+1),a	;set the first seed
	ld hl,(23672)	;get the FRAMES counter
	ld (rnd+4),hl	;set the second seed
EDIT - I missed SevenFFF's post suggesting the same one.
User avatar
Seven.FFF
Manic Miner
Posts: 736
Joined: Sat Nov 25, 2017 10:50 pm
Location: USA

Re: Randomize and rotate left.

Post by Seven.FFF »

I've been fiddling around with this thing the last few days, trying to generate a scrolling starfield by reading bytes from the ROM.

Everytime you hit space it picks a new sequence of 96 ROM bytes to generate the stars from - the space reshuffle uses Patrik Rak's CMWC routine to pick the start of the sequence, but the rest of it is just procedurally generated. I really like the way it comes up with drifts of stars that look like constellations.
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
NXtel NXTP ESP Update ESP Reset CSpect Plugins
User avatar
Seven.FFF
Manic Miner
Posts: 736
Joined: Sat Nov 25, 2017 10:50 pm
Location: USA

Re: Randomize and rotate left.

Post by Seven.FFF »

R-Tape wrote: Mon Jan 08, 2018 8:38 pm EDIT - I missed SevenFFF's post suggesting the same one.
Yes indeedy, Patrik has two. The XOR Shift is slightly faster, and the Complimentary Multiply With Carry has an incredibly long period and a balanced bitstream (i.e the high bits aren't less random than the low bits, which is a common weakpoint in algorithms).
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
NXtel NXTP ESP Update ESP Reset CSpect Plugins
User avatar
Seven.FFF
Manic Miner
Posts: 736
Joined: Sat Nov 25, 2017 10:50 pm
Location: USA

Re: Randomize and rotate left.

Post by Seven.FFF »

lol, complementary not complimentary!
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
NXtel NXTP ESP Update ESP Reset CSpect Plugins
Nomad
Manic Miner
Posts: 600
Joined: Thu Dec 28, 2017 12:38 pm

Re: Randomize and rotate left.

Post by Nomad »

I got the Patrik Rak code to work from the git hub, the man has the distinction of managing to use no English to comment the code. Interesting choice. It might as well be magic for all the sense it makes lol.

In fairness there is a thread detailing the code here..

https://www.worldofspectrum.org/forums/ ... edirect/p1

seems a lot of theory talking and code optimisation.

So D is the register I have to seed?
What is the output?

The github example has no visible output (no printing of values or anything..) I guess that is a trivial thing to do but it require you know what register/memory location the output is getting sent to..

The thread details all the various optimisations, and maths theory choices that went into the code but still at the end nobody has actually said where the output goes...

I get the feeling somewhere there is a pinhead like character shouting

https://www.youtube.com/watch?v=h7-yCkXGs3Q

'this isn't for your eyes!'

I don't really understand the maths behind this, or the assembly so its a bit of a enigma.

Image

But by the excitement it generated on WOS no doubt it must be a superior method to the spectrum ROM.

Question - is calling the rnd rom more complicated than this? It seems I need a statistics degree to understand the argument they were having about distributions in the WOS thread.

I just want to pass the rom a seed, some boundary parameters for the random number generated and get a value back in some register pair or a memory location.

It is surprising that the very first type in from the input series could have such hidden depth when you try and do something similar in assembly. I guess that is a great example of the iceberg analogy for assembly.

Code: Select all

10 LET X=RND
20 PRINT X
30 GOTO 10
So first line is just a matter of assigning a register pair to a random output from a rom call (I would have though), after supplying some seed value before the call.

The print routine would just use the rom call as being lazy.

the final line is just a jump to the start of the program. trivial.

The issue seems to be around the random function in basic...

The R register used for random number, In fairness this works in a limited way. However results are restricted to the first few lines of the screen memory (well when I ran it). Now with some more work I could probably fudge some way to have an even distribution across the whole of the screen ram range but.. I was not convinced it was really random enough.
Last edited by Nomad on Tue Jan 09, 2018 8:19 am, edited 7 times in total.
Nomad
Manic Miner
Posts: 600
Joined: Thu Dec 28, 2017 12:38 pm

Re: Randomize and rotate left.

Post by Nomad »

Seven.FFF wrote: Mon Jan 08, 2018 8:44 pm I've been fiddling around with this thing the last few days, trying to generate a scrolling starfield by reading bytes from the ROM.

Everytime you hit space it picks a new sequence of 96 ROM bytes to generate the stars from - the space reshuffle uses Patrik Rak's CMWC routine to pick the start of the sequence, but the rest of it is just procedurally generated. I really like the way it comes up with drifts of stars that look like constellations.
Nice star fields, so you are seeding the routine from the user input. That will overcome the issue with the emulators and randomness that seems to crop up now and again with the games.

Thanks for the practical example! 8-)
AndyC
Dynamite Dan
Posts: 1388
Joined: Mon Nov 13, 2017 5:12 am

Re: Randomize and rotate left.

Post by AndyC »

Nomad wrote: Tue Jan 09, 2018 7:36 am Question - is calling the rnd rom more complicated than this? It seems I need a statistics degree to understand the argument they were having about distributions in the WOS thread.

I just want to pass the rom a seed, some boundary parameters for the random number generated and get a value back in some register pair or a memory location.
Substantially. The ROM routine just generates a five byte floating point number between 0 and 1 and places it on the calculator stack. Which means that to get a number in a given range, you need to understand the ROM calculator and it's stack, as well as knowing how to convert the floating point result back into an integer value. And on top of that it's really slow.

This is one of the harder things coming from BASIC, I think. Simple commands there can be fairly advanced assembly language. It's significantly easier go write a sprite routine than a random number generator, for example.
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2640
Joined: Mon Nov 13, 2017 3:16 pm

Re: Randomize and rotate left.

Post by Ast A. Moore »

And once you have your 16-bit “random” number, here’s how you can trim the excess to fit the range of the screen attributes area:

Code: Select all

;assuming the MSB of the “random” number is stored in H

	ld a,h	;load A with H for “questioning”
	and 3	;the MSB of attr area is in the $58–5b range,
		;in binary this means only the three least significant
		;bits change, so isolate them
	or $58	;now OR the result with $58; this will give you
		;a “random” number between $58 and 5b
	ld h,a	;back into H
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.
User avatar
Joefish
Rick Dangerous
Posts: 2042
Joined: Tue Nov 14, 2017 10:26 am

Re: Randomize and rotate left.

Post by Joefish »

That gives you a number range of 1024 bytes. The attribute memory is only 768 bytes long.
Hikaru
Microbot
Posts: 100
Joined: Mon Nov 13, 2017 1:42 pm
Location: Russia
Contact:

Re: Randomize and rotate left.

Post by Hikaru »

Code: Select all

	ld a,h
	and 3
	cp 3
	adc #57
	ld h,a
But that makes it 'less random' because 2 -> #5A and 3 -> #5A.
Inactive account
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2640
Joined: Mon Nov 13, 2017 3:16 pm

Re: Randomize and rotate left.

Post by Ast A. Moore »

Joefish wrote: Tue Jan 09, 2018 10:40 am That gives you a number range of 1024 bytes. The attribute memory is only 768 bytes long.
D’oy.
Last edited by Ast A. Moore on Tue Jan 09, 2018 12:51 pm, edited 1 time in total.
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.
User avatar
Joefish
Rick Dangerous
Posts: 2042
Joined: Tue Nov 14, 2017 10:26 am

Re: Randomize and rotate left.

Post by Joefish »

Two methods:
1. If the number is too high, ask for another.

2. AND 3 in the top byte to get 0-1023. Then multiply by 3, and divide by 4. 3/4 of 1024 is 768. But you have to do this on the full 10 bits.

You can do the divide by 4 in two separate shifts downward, to save having to shift upwards to do the *3 step.
So shift it all down by 1 bit (divide by 2), add to the original (multiply by 3) and shift the answer down another bit.
User avatar
Seven.FFF
Manic Miner
Posts: 736
Joined: Sat Nov 25, 2017 10:50 pm
Location: USA

Re: Randomize and rotate left.

Post by Seven.FFF »

AND the high byte with 3 then OR it with 88, so only the two lowest bits are random. Leave the low byte fully random in all eight bits.That’ll give you an actual attribute address in the correct range.

You could subtract $5B from the high byte to get back to a 0..767 offset if you needed it.
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
NXtel NXTP ESP Update ESP Reset CSpect Plugins
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2640
Joined: Mon Nov 13, 2017 3:16 pm

Re: Randomize and rotate left.

Post by Ast A. Moore »

Seven.FFF wrote: Tue Jan 09, 2018 12:32 pm AND the high byte with 3 then OR it with 88, so only the two lowest bits are random. Leave the low byte fully random in all eight bits.That’ll give you an actual attribute address in the correct range.
That’s what I suggested originally, but it won’t work, because $5b is already too high. We need to limit the MSB to the $58–5a range, and that’s a tad tricky, as A is an even number.

AND with 3, CP with 3. If equal or greater than, then request a new number. Else, OR with $58 and proceed. Kind of clunky.
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.
User avatar
Seven.FFF
Manic Miner
Posts: 736
Joined: Sat Nov 25, 2017 10:50 pm
Location: USA

Re: Randomize and rotate left.

Post by Seven.FFF »

oops never mind
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
NXtel NXTP ESP Update ESP Reset CSpect Plugins
User avatar
Ast A. Moore
Rick Dangerous
Posts: 2640
Joined: Mon Nov 13, 2017 3:16 pm

Re: Randomize and rotate left.

Post by Ast A. Moore »

Seven.FFF wrote: Tue Jan 09, 2018 1:06 pm oops never mind
My sentiment exactly. Bloody reality always messes up with the way we want things to be. :evil:
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.
User avatar
Seven.FFF
Manic Miner
Posts: 736
Joined: Sat Nov 25, 2017 10:50 pm
Location: USA

Re: Randomize and rotate left.

Post by Seven.FFF »

Yes! I was coding this last night, making my stars coloured. It works there without overrunning, because I’m turning a random pixel address into an attribute address, so it’s already properly constrained.
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
NXtel NXTP ESP Update ESP Reset CSpect Plugins
User avatar
Seven.FFF
Manic Miner
Posts: 736
Joined: Sat Nov 25, 2017 10:50 pm
Location: USA

Re: Randomize and rotate left.

Post by Seven.FFF »

Both Patrik Rak's RNG's work in are used in a similar way. You can poke your own seed into part of its seed table/value(s), like R-Tape said. For the CMWC on github the seed table is ten bytes, so you could do:

Code: Select all

FRAMES equ $5C78 ; 24-bit frame counter sysvar at $5C78, $5C79 and $5C7A, gets incremented 50 times a second by the ROM in interrupt mode 1
ld hl, (FRAMES)  ; lowest 16 bits of frame counter (0..65535)
ld (table), hl   ; first two bytes of CMWC seed table
So if hl read $MMNN from FRAMES, the seed table would then be:

Code: Select all

table   db   $NN,$MM,82,97,120,111,102,116,20,15
The random value is returned in a (0..255) for both RNGs.

The CMWC changes several bytes in its seed table every time you call it, as the initial seed propagates through all ten bytes (from left to right, as we're viewing it here). So even if you reseeded the first pair of bytes with exactly the same values, you wouldn't get the same sequence of numbers returned in a.

Generally, though, you would only seed this table once. A really good way to do this is to have an initial screen (menu, etc) that requires a keypress in order to continue. As long as FRAMES is incremented during this keypress wait, which it is in the standard interrupt mode 1, you'd get a different seed value every time.

You can make this more robust by having two keypress loops. The first one loops until all keys are released, the second one loops until a key is pressed. That stops somebody holding a key down as soon as the emulator loads. (Emulators are harder to work with, because double-clicking on a tap to start most emulators takes exactly the same length of time every time you do it, so FRAMES will have the same frame count every time. That doesn't tend to happen on real hardware, cos you faff around typing LOAD "" etc, which takes a different length of time each time you do it.)

If you need random numbers on your menu as well, it gets harder to achieve!
Last edited by Seven.FFF on Tue Jan 09, 2018 5:28 pm, edited 1 time in total.
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
NXtel NXTP ESP Update ESP Reset CSpect Plugins
Nomad
Manic Miner
Posts: 600
Joined: Thu Dec 28, 2017 12:38 pm

Re: Randomize and rotate left.

Post by Nomad »

Thanks for the input on the fast randomization subroutines. will crack on with that tomorrow when I have more time to work through the examples.

Its great to have your input Ast A. Moore, joefish, Hikaru, AndyC, R-tape. I would be still very much lost without you guys helping out and last but not least shout out to Seven.FFF for the great explanation and examples. Tomorrow will put it to good use. 8-)

On a related note - the R register calls are said to produce random/unpredictable results. Did anyone chart the output?

I want to chart the distribution from the R register calls, is there a way I can write the output to a data file? From what I have seen R register returns a value of 0-~127 But I cant be sure until I can collate more data.

Code: Select all

; This program loads a value into memory location 51000

org 50000

MAIN:
ld a,r 				; loads the content of register r into register a
ld (51000),a		; puts content of a into memory location 51000
ret 				; return
end 50000			; end program
then just did the print loop in basic. (I was lazy..)

I put a program into a loop but it only ran for a few mins, I would like to make a much more comprehensive test. The recorder part could be in basic its no big deal the main thing is i want to get enough samples to plot the distribution.
User avatar
Seven.FFF
Manic Miner
Posts: 736
Joined: Sat Nov 25, 2017 10:50 pm
Location: USA

Re: Randomize and rotate left.

Post by Seven.FFF »

r increments by one with every opcode (or by two for instructions prefixed with $DD, $FD, $ED and $CB), and restarts at 0 after 127.

No nice distribution, as you were hoping! It's rubbish for getting a sequence of numbers, but not bad for getting a single number at an arbitrary point in time.

This prints r repeatedly in Zeus, although be warned it hangs! Too much stuff going to the console window too quickly. Demonstrates the point though - the values literally just count up. You have to tick Enable Data Breakpoints to enable the printing.

Code: Select all

zeusemulate             "48K"                           ; Tell the Zeus emulator to be a 48K Spectrum
Zeus_PC                 = $8000                         ; Tell the Zeus emulator where to start running code at
org                     $8000                           ; Tell the Zeus assembler where to place the code
                        ei
Loop:
                        zeusdatabreakpoint 1, "zeusprint(1, r)", $
                        jp Loop
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
NXtel NXTP ESP Update ESP Reset CSpect Plugins
User avatar
Seven.FFF
Manic Miner
Posts: 736
Joined: Sat Nov 25, 2017 10:50 pm
Location: USA

Re: Randomize and rotate left.

Post by Seven.FFF »

Changing it to this stops Zeus hanging, by breaking every time (the extra ", 1" at the end):

Code: Select all

zeusdatabreakpoint 1, "zeusprint(1, r), 1", $
You have to click Run or type F8 to restart again after breaking.
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
NXtel NXTP ESP Update ESP Reset CSpect Plugins
User avatar
Seven.FFF
Manic Miner
Posts: 736
Joined: Sat Nov 25, 2017 10:50 pm
Location: USA

Re: Randomize and rotate left.

Post by Seven.FFF »

Perhaps even more useful. The "1" first parameter of zeusprint() prints r every time, and the "r=127" condition only breaks when r is 127. It's effectively two separate breakpoint expressions chained together. The value of the last chained expression decides whether to break.

Code: Select all

zeusdatabreakpoint 1, "zeusprint(1, r), r=127", $
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
NXtel NXTP ESP Update ESP Reset CSpect Plugins
Post Reply