Random between other things...

So first of all, I figured out why everything broke with 9 words. (and no, the front did not fell off) It turns I don't know C (nothing new) and I thought that 'sizeof' returns size in bits, not bytes. I'm actually surprised it even worked until the fix to be honest. Well, I still don't know C I guess, but I'm getting closer.

"So what now?" you ask? It's quite simple, you see... I just run the current version on the gforth-tetris code, see where it crashes, fix a bug or implement a feature and repeat until it runs!

So first of all, turns out the first line of gforth-tetris is:

require random.fs

This means that I have to do two things.

Require shenanigans

Sow what do I mean by "work like this"? Well, just be able to have system libraries stored at specific locations and search them in addition to file directory.

First of all, 'REQUIRE' uses 'INCLUDE', which uses 'INCLUDED', so let's start there. It turns out it opens a file which returns a file handle and an error, that is later checked. I can use this by placing a '?DO' loop between the opened file and check. This loop will go through the prefixes and if there is an error on the stack, it will try opening the file with current prefix instead.

OK, another problem, I need the actual prefixes. Well, two problems, as one needs to be in the user home directory, for which I need the shell commands I implemented in later files.

First things first: how do I even make string array? You see, When you use 'S"' outside of compilation, it stores to the 'PAD' buffer which is used for temporary string storage. Now when I think about this, I could probably just 'ALLOT' and 'MOVE', but I decided to (ab)use FORTH a bit more than that...

3 constant (include-prefixes-elems)
create (include-prefixes) (include-prefixes-elems) cells allot

\ for some reason addresses change later on, so I will store exec tokens

:noname s" /usr/local/share/exforth/" ;
(include-prefixes) !

\ will be filled later in ex_shell.fth
:noname s" " ; (include-prefixes) 1 cells + !

:noname s" ../fth/ex_local_share/" ;
(include-prefixes) 2 cells + !

In case you don't want to read a bunch of FORTH without syntax highlighting: This code stores not strings, but anonymous words that return the strings. I know, someone would probably crucify me for this, but I like it.

This also means, that I can later redefine the second word like so:

:noname ( -- c-addr u )
  s\" echo \"$HOME/.local/share/exforth/\"" sh-get 1- ; \ newline
(include-prefixes) 1 cells + !

Just as Moore intended...

This is how the loop looks like:

( c-addr u1 fileid1 ior1 -- c-addr u1 fileid2 ior2 )
(include-prefixes-elems) 0 ?do
  dup if
    drop drop
 
    (include-prefixes) i cells + @ execute
    dup >r dup >r
    pad swap move
 
    2dup pad r> + swap move
    pad over r> +
    r/o open-file
  then
loop

The random stuff

So now the actual random implementation. Gforth 'random.fs' exports 4 words:

Long story short, I used the Linear Congruential algorithm to make the following code:

\ Gforth compatible random number implementation
\ 0BSD

variable seed

: SEED! ( n -- ) seed ! ;
: SEED-INIT ( -- ) ntime drop seed ! ;

\ https://en.wikipedia.org/wiki/Linear_congruential_generator
: RND ( -- n )
  \ Musl values
  #6364136223846793005 seed @ * 1+ \ natural overflow as modulus
  dup seed ! ;
: RANDOM ( n -- 0..n-1 ) rnd swap mod abs ;

seed-init

It took me way too long to realise that I forgot the '@' after 'seed', but I'm slowly getting better at this.

I also had to implement my own 'ntime', but I just used '<time.h>'. When at it, I also implemented other time-based words: 'UTIME' and 'TIME&DATE'. (fun-fact: This is the only standard word that has the '&' symbol in it's name. Gforth adds second one: 'TIME&DATE&TZ')

That's it. I guess this entry should be called "require-with-prefix" or something, but implementing random just sounds more exiting. Technically still not a click-bait tho.

Floats?

Well, technically I also added few words for struct definitions, but that is not worth talking about.

But yea, It turns out I used 'FVALUE' in my tetris at some point. 'FVALUE' itself is not that hard:

: FVALUE ( n <name> )
  create
    f,
  does>
    f@
;

Except that there was no 'F,'

: F, here float allot f! ;

But 'FVALUE' is paired with the same 'TO' as 'VALUE'. This means that 'TO' needs to determine which one it is writing to. One suggestion by forth-standard.org is:

VARIABLE %var
: TO 1 %var ! ;

: FVALUE ( F: r -- ) ( "<spaces>name" -- )
   CREATE F,
   DOES> %var @ IF F! ELSE F@ THEN
         0 %var ! ;

: VALUE ( x "<spaces>name" -- )
   CREATE ,
   DOES> %var @ IF ! ELSE @ THEN
         0 %var ! ;

This has two problems tho. First is that I don't want to do 'IF' every time I want to read from value, and second, more important reason, 'TO' in pforth also does some other stuff, some of which are related to local variables. I really don't want to change all that, so I decided for a different approach.

: FVALUE ( n <name> )
  create
    1 , f,
  does>
    cell + f@
;

I just store extra value, based on which I decide what to do. If it is local variable is determined beforehand, so that does not break. It does break backwards compatibility however, as the previous implementation was also able to store to variables, but that is non-standard, so I kinda don't care... But I would still like to make it in a different way. I don't know of any tho, so let's keep this method for now.

If you ask why is the type first, floats can technically have different size from cells. This would still break other things I rely on, but I like to take it into account where possible.

-LOOP

I also implemented this I guess. It turns out '-LOOP' and '-1 * +LOOP' are not equivalent. '-LOOP' does not go all the way to limit. Turns out loops are mostly implemented in C, so I just made it here. Not that hard actually.

I found that all control structures are later wrapped in 'fth/smart_if.fth'. This allows them to be used directly in interactive mode without wrapping it all in a word. Good addition if you ask me, so I also added '-LOOP' there.

What is next?

'SF!' it seems. That sounds like fun...