Dave's nasmjf Dev Log 15
Created: 2022-07-22
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.
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!