Page 1 of 2

Randomize and rotate left.

Posted: Mon Jan 08, 2018 8:00 pm
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

Re: Randomize and rotate left.

Posted: Mon Jan 08, 2018 8:25 pm
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.

Re: Randomize and rotate left.

Posted: Mon Jan 08, 2018 8:36 pm
by Seven.FFF
Have a play around with this one. It generates high quality bytes pretty fast.

Re: Randomize and rotate left.

Posted: Mon Jan 08, 2018 8:38 pm
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.

Re: Randomize and rotate left.

Posted: Mon Jan 08, 2018 8:44 pm
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.

Re: Randomize and rotate left.

Posted: Mon Jan 08, 2018 8:50 pm
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).

Re: Randomize and rotate left.

Posted: Mon Jan 08, 2018 8:53 pm
by Seven.FFF
lol, complementary not complimentary!

Re: Randomize and rotate left.

Posted: Tue Jan 09, 2018 7:36 am
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.

Re: Randomize and rotate left.

Posted: Tue Jan 09, 2018 7:45 am
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-)

Re: Randomize and rotate left.

Posted: Tue Jan 09, 2018 9:11 am
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.

Re: Randomize and rotate left.

Posted: Tue Jan 09, 2018 10:22 am
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

Re: Randomize and rotate left.

Posted: Tue Jan 09, 2018 10:40 am
by Joefish
That gives you a number range of 1024 bytes. The attribute memory is only 768 bytes long.

Re: Randomize and rotate left.

Posted: Tue Jan 09, 2018 10:47 am
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.

Re: Randomize and rotate left.

Posted: Tue Jan 09, 2018 11:42 am
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.

Re: Randomize and rotate left.

Posted: Tue Jan 09, 2018 12:12 pm
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.

Re: Randomize and rotate left.

Posted: Tue Jan 09, 2018 12:32 pm
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.

Re: Randomize and rotate left.

Posted: Tue Jan 09, 2018 1:02 pm
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.

Re: Randomize and rotate left.

Posted: Tue Jan 09, 2018 1:06 pm
by Seven.FFF
oops never mind

Re: Randomize and rotate left.

Posted: Tue Jan 09, 2018 1:14 pm
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:

Re: Randomize and rotate left.

Posted: Tue Jan 09, 2018 1:30 pm
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.

Re: Randomize and rotate left.

Posted: Tue Jan 09, 2018 4:36 pm
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!

Re: Randomize and rotate left.

Posted: Tue Jan 09, 2018 5:00 pm
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.

Re: Randomize and rotate left.

Posted: Tue Jan 09, 2018 5:12 pm
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

Re: Randomize and rotate left.

Posted: Tue Jan 09, 2018 5:18 pm
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.

Re: Randomize and rotate left.

Posted: Tue Jan 09, 2018 5:22 pm
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", $