Dave's nasmjf Dev Log 15

Created: 2022-07-22

nasmjf home

This is an entry in my developer’s log series written between December 2021 and August 2022 (started project in September). I wrote these as I completed my port of the JONESFORTH assembly language Forth interpreter.

← Previous 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 Next →
    Warning, the examples with variables in this log are
    all wrong. This update explains:

        !!!!!!!!!!!!!!!!!!!! Update !!!!!!!!!!!!!!!!!!!!
        ! In log19.txt, I realize that my variable     !
        ! handling is wrong. Variables should leave    !
        ! their addresses on the stack, not their      !
        ! values! We need FETCH to get the value from  !
        ! the address!                                 !
        !!!!!!!!!!!!!!!!!!!! Update !!!!!!!!!!!!!!!!!!!!

    Now I've ported the primitive memory words.
    Actually, I'd already done FETCH ('@') because it was
    required by INTERPRET.

            !  store value at address
            @  get value from address
           +!  add value to value at address
           -!  subtract value from value at address
           C!  store byte
           C@  fetch byte
         C@C!  copy 1 byte  from source addr to dest addr
        CMOVE  copy n bytes from source addr to dest addr

    So far, it's been easy to figure out how to test new
    words as I add them. But these memory primitives aren't
    so obvious because how do I know where I can read/write
    memory?

    The VARIABLE word which creates new variables won't be
    defined until the second half of JonesFORTH runs...and
    that's defined in FORTH, not asm!

    Thankfully, we have some existing variables which should
    be helpful:

        STATE   compiling (1) or executing (0)
        LATEST  Points to the most recently defined word
        HERE    Points to the next free byte of memory
        S0      The address of the top of the parameter stack
        BASE    The current base for printing and reading numbers

    So, on with the testing, starting with storing and fetching
    from HERE.

HERE .
134537216
5 HERE !
HERE @ .
5
HERE @ HERE @ . .
55

    Now the in-place addition and subtraction.

1 HERE +! HERE @ .
6
2 HERE -! HERE @ .
4

    For the byte-wise operators, I'll set the
    native-sized 4 bytes to all 1s, then it'll be
    clear if we're just storing/fetching the lower
    byte.

0 INVERT HERE ! HERE @ .
65535
0 HERE C! HERE @ .
65280
HERE C@ .
0

    Now to copy a byte from memory to another
    memory location, let's get an interesting
    byte - the 'L' from 'LATEST' in the name
    portion of the word definition:

LATEST .
134522384
LATEST 5 + C@ EMIT
L
LATEST 5 + HERE C@C!
HERE @ .
65356
HERE C@ EMIT
L

    That's neat, but we can do one better by
    copying the entire string. I'll reformat
    the responses to make it even clearer:

LATEST 5 + HERE 6 CMOVE
HERE 0 + @ EMIT L
HERE 1 + @ EMIT A
HERE 2 + @ EMIT T
HERE 3 + @ EMIT E
HERE 4 + @ EMIT S
HERE 5 + @ EMIT T

    Neat!

    Next are return stack manipulation words. Forth
    is surely unique in inviting programmers to monkey
    about with the return stack for storing temporary
    values or perform other clever tricks.

        >R     move value from param stack to return stack
        R>     move value from return stack to param stack
        RSP@   get the actual address RSP points to
        RSP!   set the address RSP points to
        RDROP  move RSP to "pop" value and throw it away

    According to Leo Brodie's Starting Forth, storing stuff
    on the return stack should be safe to use when defining
    a word. Outside of that, it'll likely cause a crash.

: store-add >R + R> ;
1 2 3 store-add . .
33

    That works. Stows the 3 from the param stack on the return
    stack, adds the 1 and 2 from the param stack and puts the
    resulting 3 onto the param stack, restores the 3 from the
    return stack back to the param stack.

    We can look at the address of the return stack pointer (RSP).
    Which is currently equal to the top of the return stack
    memory address, stored in constant R0:

RSP@ .
134530680
R0 .
134530680

    This isn't a very good demonstration of setting RSP since
    I'm setting it to the existing value:

RSP@ RSP!

    Now let's see if we can "drop" the value on the return
    stack:

RSP@ @ .
0
5 >R
RSP@ @ .
5
RDROP
RSP@ @ .
0

    Yup!

    Like the return stack, we can mess directly with the param
    stack with two words:

        DSP@ - fetch address of param ("data") stack pointer
        DSP! - set address of param stack pointer

    Let's view the DSP address:

DSP@ .
3221223824

    And it should go lower when we push a value because it
    grows "upward":

55
DSP@ .

    We can view the value on the stack by fetching it from
    the address:

3221223820
DSP@ @ .
55

    Let's put another value on the stack. DSP will get lower.
    And we can view the new value:

77
DSP@ .
3221223816
DSP@ @ .
77

    Let's try setting DSP "back" to a higher address to point
    to and view the previous value again:

DSP@ 4 + DSP!
DSP@ .
3221223820
DSP@ @ .
55

    Neat!
← Previous 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 Next →