add starting forth notes, page on netbpm and a reading page

This commit is contained in:
opfez 2021-08-13 01:01:49 +02:00
parent 980b4b5697
commit 0c9637d898
14 changed files with 1050 additions and 5 deletions

View File

@ -0,0 +1,21 @@
( 1 )
( -- )
: gift ." bookends" ;
( -- )
: giver ." John" ;
( -- )
: thanks cr ." Dear " giver ." ," cr
." thanks for the " gift ." . " ;
( 2 )
( n -- m )
: ten.less 10 - ;
( 3 )
( thanks still uses the old definition of giver. )

View File

@ -0,0 +1,37 @@
( Problems, page 305 )
( skipping 1 and 2 )
: rat ." Rat" ;
: ox ." Ox" ;
: tiger ." Tiger" ;
: rabbit ." Rabbit" ;
: dragon ." Dragon" ;
: snake ." Snake" ;
: horse ." Horse" ;
: ram ." Ram" ;
: monkey ." Monkey" ;
: cock ." Cock" ;
: boar ." Boar" ;
create animals
' rat ,
' ox ,
' tiger ,
' rabbit ,
' dragon ,
' snake ,
' horse ,
' ram ,
' monkey ,
' cock ,
' boar ,
: .animal 8 * animals + @ execute ;
: (juneeshee) 8 + 12 mod .animal ;
variable year-of-birth
: juneeshee cr ." Enter your year of birth: "
year-of-birth
4 over c!
dup 1+ 4 expect number drop cr (juneeshee) ;
( Skipping 4 )
( And 5. I don't know how block works on modern machines. )

View File

@ -0,0 +1,16 @@
( Problems, page 333 )
( Skipping 1, don't have blocks )
: based. create ,
does> base @ >r @ base ! . r> base ! ;
: plural create ,
does> @ swap ?dup if 0 do dup execute loop then ;
: tourne [compile] do ; immediate
: retourne [compile] loop ; immediate
: ascii 32 word 1+ c@ [compile] literal ; immediate
( Skipping 6, I have no idea how to deal with the input stream and it sounds
like pain. )

View File

@ -0,0 +1,71 @@
( Quizzie 2-a, page 55 )
( 1 )
a b + c *
( 2 )
a 3 * b - 4 / c +
( 3 )
a b * 200 /
( 4 )
n 1 + n /
( 5 )
7 x * 5 + x *
( 6 )
( [a-b]/[b+a] )
( 7 )
( a / 10b )
( Quizzie 2-b, page 59 )
( 1 is already given)
( c a b -- result )
: 2b2 4 * - 6 / + ;
( a b -- result )
: 2b3 8 * / ;
( a b -- result )
: 2b4 * 200 / ;
( a a -- result )
: 2b5 2 * 3 + * ;
( or )
( a -- result )
: 2b5 dup 2 * 3 + * ;
( c a b )
: 2b6 - swap / ;
( Quizzie 2-c, page 69 )
( a b c -- c b a )
: 2c1 swap rot ;
( a b -- a b a )
: 2c2 swap dup rot rot ;
( a b c -- c a b )
: <rot rot rot ;
: 2c3 <rot ;
( n -- result )
: 2c4 dup 1 + swap / ;
( x -- result )
: 2c5 dup 7 * 5 + * ;
( for this one, i start by factoring out the a first, so the expression becomes:
a[9a - b]
)
( a b -- result )
: 2c6 swap dup 9 * rot - * ;
( looks like I reimplemented rot by accident. The book's solution is cleaner imo. )

View File

@ -0,0 +1,4 @@
( Problems, page 123 )
: sign.test dup 0= if ." ZERO"
else dup 0< if ." NEGATIVE"
else ." POSITIVE" then then ;

View File

@ -0,0 +1,16 @@
( Problems, page 143 )
: <rot rot rot ;
( a b c -- n )
: 5-1 <rot * swap / negate ;
( 6 70 123 45 -- )
: 5-2 max max max . ;
: f>c 32 - 10 18 */ ;
: c>f 18 10 */ 32 + ;
: c>k 273 + ;
: k>c 273 - ;
: k>f k>c c>f ;
: f>k f>c c>k ;

View File

@ -0,0 +1,25 @@
( Problems, page 163 )
: stars ?dup if 0 do 42 emit loop then ;
: box cr 0 do dup stars cr loop drop ;
: \stars cr 0 do i spaces 10 stars cr loop ;
: /stars dup cr 0 do dup i - 1- spaces 10 stars cr loop ;
: /stars begin cr dup 1- spaces 10 stars 1- dup 0= until ;
: upside-down-triangle 0 9 do cr i 10 - abs spaces i 2* 1+ stars -1 +loop ;
: triangle 10 0 do cr i 10 - abs spaces i 2* 1+ stars loop ;
: diamond triangle upside-down-triangle ;
: diamonds 0 do diamond loop quit ;
: ** ( base exponent -- exponential-value )
over >r
1 do j * loop r> drop ;

View File

@ -0,0 +1,16 @@
( Problems, page 200 )
( I'm no beginner! )
: binary 2 base ! ;
: 7-6 17 0 do
cr
decimal
." Decimal" i 3 .r 3 spaces
hex
." Hex" i 3 .r 3 spaces
binary
." Binary" i 6 .r 3 spaces
loop decimal ;
( 8 - ugh i won't bother )

View File

@ -0,0 +1,56 @@
( Problems, page 230 )
variable pies
0 pies !
: bake-pie 1 pies +! ;
: eat-pie pies @ if -1 pies +! ." Thank you!" else
." What pie?" then ;
variable frozen-pies
0 frozen-pies !
: freeze-pies pies @ frozen-pies !
0 pies ! ;
: .base base @ decimal . ;
( I looked at the answer for problem 3, didn't know you could embed code between
the <# #> tags!
)
create pencil-types 0 , 0 , 0 , 0 ,
: red 0 ; : blue 1 ; : green 2 ; : orange 3 ;
: pencils 8 * pencil-types + ;
( Usage: <val> <colour> pencils ! - sets the pencil colour value
<colour> pencils ? - prints the pencil colour value
)
: stars 0 do 42 emit loop ;
create values 1 , 52 , 13 , 20 , 18 , 14 , 8 , 6 , 2 ,
: plot 9 0 do
cr i 1+ . space values i 8 * + @ stars
loop ;
create board 1 allot
: reset board 9 0 fill ;
: line cr 9 0 do 45 emit loop cr ;
: bar ." | " ;
: sqr board + ;
: .box ( i -- )
sqr c@ dup 0= if 2 spaces else
dup 1 = if ." X " else
." O " then then drop ;
: show-board ( adr -- )
cr 9 0 do
i if i 3 mod 0= if line else bar then then
i .box
loop cr quit ;
( v oops!! This is really important!! )
: x! board + 1 swap c! show-board ;
: o! board + -1 swap c! show-board ;
reset

View File

@ -0,0 +1,29 @@
( Problems, page 269 )
( This is problem 6, chapter 2 )
: homicide 20 + ;
: arson 10 + ;
: bookmaking 2 + ;
: tax-evasion 5 + ;
: convicted-of 0 ;
: will-serve . ." years" ;
: counts ( n -- n )
0 ' execute * + ;
here
pad here - .
\ => 176
( The difference between "date ." and "' date ." and the same for the base
example is that the latter will print the address to the code that will push
the value onto the stack, while the former will push the address to the
variable to the stack. I think? )
: greet ." Hello!" ;
: count cr 10 0 do i . space loop ;
: stars cr 10 0 do 42 emit loop ;
: do-nothing ;
create exec-array ' greet , ' count , ' stars , ' do-nothing ,
: do-something ( i -- )
1- 8 * exec-array + @ execute ;

View File

@ -0,0 +1,723 @@
#+Starting Forth
This file contains my notes reading the book Starting Forth by Leo Brodie. In
this directory are also several files containing Forth source code. These are my
solutions to the problems found in the book.
* Chapter 1
** Some operations defined in this chapter
- spaces - ( n -- ) - prints n spaces ('15 spaces' will print 15 spaces to the
screen).
- emit - ( c -- ) - prints a number as an ascii character to the screen.
- : xxx yyy ; - ( -- ) - define new words like this: : star 42 emit ;
- cr - ( -- ) - print a carriage return to the screen (newline).
- ." <text> " - ( -- ) - print the string <text> to the screen.
- . - ( n -- ) - pop a number off the stack and print it.
- Forth features arithmetic with +, -, / and *.
** The dictionary
Forth has a "dictionary" where all the word definitions are stored (words =
functions). When you create a new word with : and ;, the definition is stored
in the dictionary under the given name.
** The text interpreter
When a word is entered, Forth will "activate a word called INTERPRET" which
will look up that word in the dictionary. If it finds a definition, it will
pass it onto another word called "EXECUTE" which will perform the
action. Otherwise, it will give it to the numbers-runner called "NUMBER" which
will push the word onto the stack if it is indeed a number. Otherwise, the
interpreter will throw an error. It's interesting that all the parts of the
compilation and interpretation process are described as simple words that are
part of the Forth system, I wonder if these are redefinable.
** Stack notation
Stack notation is used to communicate the effects a function has on the stack.
The basic form is like this: ( before -- after )
The left side indicates what should be on the stack before you execute a word,
the right side indicates the things that will be on the stack afterwards.
The stack notation for +, for example, is ( n1 n2 -- sum ).
The rightmost item on the left side is the top of the stack (in the previous
example, this means that n2 is on top of the stack).
** General thoughts
The end of the chapter features a review of terminology and the words defined
so far.
* Chapter 2
** General notes
This chapter is mostly about stack based arithmetic and a few stack
operations. These are documented tersely and well in the book, so I won't
write about them here.
The non-desktructive stack print (.s) is already provided with gforth, but
that definition looks very inviting. I'm excited for when I can understand it.
I also find it's a bit difficult to decide on the proper stack layout for a
function. I guess it will come for practice (or pracicality; when you chain
multiple words together, I'm guessing there will become a preferred stack
order).
I find it weird to use /; I often forget which argument is the dividend and
which is the divisor. - is more straight forward, weirdly.
** "Problems"
1. The difference between 'dup dup' and '2dup' is that the former will produce
two copies of the value on top of the stack, while the latter will produce
a copy of both the second value of the stack as well as the first.
2. : 4reverse swap 2swap swap ;
3. : 3dup dup 2over rot ;
4. I start by factoring the expression like so:
+ ab + c = a(a + b) + c
Then, the answer is trivial.
#+begin_src forth
( c a b -- result )
: answer over + * + ;
#+end_src
5.
#+begin_src forth
( a b -- result )
: answer 2dup - rot rot + / ;
#+end_src
6.
#+begin_src forth
: homicide 20 + ;
: arson 10 + ;
: bookmaking 2 + ;
: tax-evasion 5 + ;
: convicted-of 0 ;
: will-serve . ." years" ;
#+end_src
7.
#+begin_src forth
( eggs -- )
: egg.cartons 6 /mod
cr . ." carton(s), with " . ." left-over egg(s).";
#+end_src
* Chapter 3
** General notes
The chapter starts off by talking about how redefining words doesn't actually
erase the previous definition, but adds a new entry at the "back of the
dictionary". This means you can use FORGET to remove the newest version of the
word and get back the old definition. I learned this when I made the forth
interpreter for Pansy Linux; the dictionary is a reversed linked list with
this very property.
The rest of the chapter describes the Forth file system as well as the
editor. Neither of these are available on modern systems, but it might be fun
to re-read this chapter for ideas if I ever want to create a virtual machine
operating system.
** Forget
As it turns out, gforth doesn't have forget! I'm switching implementation...
Looks like most implementations don't implement forget. pforth is the only one
I found so far... I guess I'll use that then?
NOTE: Forget not only erases the newest version of the word provided, it also
removes all other words defined after that word.
** The block system
The book describes how you can interface with files from the Forth
system. Source files can be stored in "blocks" of 16 lines with 64 characters
on each line (1024 bytes). To load, say, block 12, you type '12 load' at the
interpreter.
This sounds very neat and facilitates Forth as a system in addition to just a
programming language (and it sounds pretty aesthetic~. I know CollapseOS
adopted the block model from Forth).
** Style
There's some style tips here as well:
1. Separate the name of a word definition from the contents by three spaces.
2. Break definitions up into phrases, separated by double spaces.
3. If the definition takes more than one line, indent all but the first line.
4. Don't put more than one definition on a single line unless the definitions
are very short and logically related.
** Handy word (just one!)
depth ( -- n ) - Places the number of items on the stack onto the stack.
* Chapter 4
** General notes
This chapter is about branching (if/else). It starts with number comparison
and the basic 'if' form which is like follows:
#+begin_src forth
if <consequent> then <rest of the program>
#+end_src
The part after 'then' will always be executed. Special comparison words like =
will push a true value (which is -1 by the way) on the stack, which will be
popped off by the conditional check.
If can also take an else branch:
#+begin_src forth
if <consequent> else <alternative> then <rest of the program>
#+end_src
The if form can only be used in a word definition, for some reason. Weird.
Not reverses the boolean on the stack.
If's can be nested, but you'll end up having to close off each if with a
respective then. This leads to weird situations ilke
if <something> else
if <something else> else
...
then then then then then ;
Turns out, any value other than 0 counts as true (like in C).
This means the logical or operator in Forth is simply +! Ah, but what if the
values are each other's inverse? If you add -1 and 1 you get 0, which is the
wrong answer. Because of this, Forth has the or operator which works like
you'd expect.
** New words
*** Words with built-in if
?dup - dup only if the argument is non-zero.
abort - abort execution and return to the interpreter, clearing the stack in
the process.
The book mentions ?stack for checking stack underflows, but it doesn't exist
in my implementation (fforth). I guess it can be implemented with depth and 0=.
*** Logical operators
(self explanatory)
and
or
*** Comparing numbers
(these are self explanatory)
**** Two arguments
=
<
>
**** One argument
0= - this one is actually equivalent to not, but may be used for readability.
0<
0>
** "Problems"
1. Note - My implementation uses -1 as true, instead of 1 as the book.
1. 1 (not not 1 = -1)
2. 0 (not not 0 = 0)
3. 1 (not not 200 = not 0 = -1)
2. Huh?
3.
#+begin_src forth
: card 17 > if ." Alcoholic beverages permitted" else ." Under age" then ;
#+end_src
4.
#+begin_src forth
: sign.test dup 0= if ." ZERO" else
dup 0< if ." NEGATIVE" else
." POSITIVE" then then drop ;
#+end_src
5. Simply wrapping the loop in an if statement works!
#+begin_src forth
: stars dup if 0 do 42 emit loop then ;
#+end_src
6. Probably not a good solution, but it works well.
#+begin_src forth
( n low-limit hi-limit -- )
: within rot ( l h n )
swap ( l n h )
over ( l n h n )
swap ( l n n h )
< ( l n t/f )
rot rot ( t/f l n )
<=
and
;
( The version provided by the book is much nicer )
: <rot rot rot ;
: within <rot over > not <rot > and ;
( But my implementation doesn't have not )
#+end_src
7.
#+begin_src forth
: guess 2dup = if ." CORRECT" drop else
2dup < if ." TOO HIGH" else
." TOO LOW" then then drop ;
#+end_src
8.
#+begin_src forth
: n= over = ;
: speller dup if
dup 0< if ." NEGATIVE " then
abs 1 n= if ." ONE" else
2 n= if ." TWO" else
3 n= if ." THREE" else
4 n= if ." FOUR" else
." OUT OF RANGE" then then then then
else ." ZERO" then drop ;
#+end_src
9.
#+begin_src forth
( TODO )
#+end_src
* Chapter 5
** General notes
This chapter starts off by introducing a few new arithmetic words and the
return stack along with a few words for manipulating this. The return stack
looks really powerful for easing the stack manipulation bit of Forth.
One thing to note is that the return stack must be empty before the end of a
word definition.
The chapter also describes how to deal with floating point numbers in
Forth. Don't. Instead, use fixed-point numbers but externally represent them
as floating points. Generally, instead of thinking of expressions like
(x / y) * z, translate them into (x * z) / y for greater accuracy (which uses
the */ word in Forth). In general, one can use the expression 3 2 */ to mean
rational numbers (3/2 in this case) which you can multiply some other
fixed-point number.
** New words
*** Arithmetic
These are self explanatory.
1+
1-
2+
2-
2*
2/
*** Miscellaneous math
abs ( n -- |n| )
negate ( n -- -n )
min ( n1 n2 -- n-min )
max ( n1 n2 -- n-max )
*** Return stack manipulation
>R ( n -- ) - Pops the first value off the stack and pushes it to the
parameter stack.
R> ( -- n ) - The opposite
I ( -- n ) - Copies the first item off the return stack
I' ( -- n ) - --"-- second --"--
J ( -- n ) - --"-- third --"--
*** Scaling operators
*/ ( x y z -- x*y/z ) - This is a word which uses a 32-bit integer as the
intermediate result of mutliplying x by y. This makes it better to use for
scaling than simply * and / alone.
*/mod ( x y z -- remainder ) - The same as /*, but returns the remainder
rather than the quotient.
* Chapter 6
** General thoughts
This chapter is all about looping!
** Handy hint
Typing in an unrecognised word will clear the stack since the interpreter
will call abort.
Making quit the last word of a word definition will silence the "ok."
** Looping constructs
*** Definite loops -- do...loop
This is your basic for loop in other languages. The syntax is the following:
limit index do ... loop
So you put the limit first, then the starting index, then do, the body and
finally loop.
The do loop works by first pushing the limit and the index onto the return
stack and incrementing the index until it is the same as the limit. This
means you can use the i word to get a copy of the index at any given moment
in the loop!
Since we can nest loops, the words i and j make a bit more sense. You can
use j to access the index of the outer loop and i to access the index of the
inner one.
for (int j = 0; j < 10; j++)
for (int i = 0; i < 10; i++)
...
Looks familiar?
+loop can make the index go up by a specific increment. It pops a value off
the stack and increments the index by that number.
Do loops terminate when (going up) the index reaches or passes the limit and
(when going down) it passes the limit. A do loop always executes at least
once.
One can use the word leave to set the limit to equal the index, so that the
loop will immediately terminate on the next time loop is executed. This can
be useful for early exits.
*** Indefinite loops
Indefinite loops are like a do while loop, they loop if a condition is true
by the end of the body execution.
The basic indefinite loop is on the form:
begin xxx f until
Where xxx is the body of the loop and f is the boolean flag indicating if
the loop should, well, loop.
A simple infinite loop can then be defined like so:
begin xxx 0 until
There's another form of the loop as well, with syntax like this:
begin xxx f while yyy repeat
This one's weird. It performs xxx, then checks for a boolean true. If it's
true, it performs yyy and repeats again. The begin...until loop will loop on
the opposite condition. That is, it will only loop if the condition is
false.
* Chapter 7
** General notes
This chapter is about number representations, like unsigned, double width,
hexadecimal etc.
Turns out that the and and or words defined earlier are actually bitwise
operators. They work fine as logical and/or regardless.
** Double length numbers
To push a double length number onto the stack, use punctuation in the number.
Punctuation works with , and . and a few other characters.
So 4.000.000.000.000.000.000.000 pushes that number as two cells.
** Number formatting
One can create number formatting words with the special words <# and #>. The
book has good sections on this, I won't bother trying to word them myself
here. NOTE! These only work on unsigned, double length words. To make them
work on single length words, put a 0 on the stack before calling it.
This is a sort of DSL, kinda like format in Common Lisp. I wonder if there
are more of these in Forth, and how easy it is to make one...
** New words
*** Unsigned number manipulation
The numbers on the stack are both unsigned and signed at the same time. The
programmer decides what representation they are by the operations performed
on them. Most arithmetic operations have an unsigned equivalent:
u. ( u -- ) Prints the unsigned number u.
u*, u/mod, u< does what you'd expect.
do ... /loop is your normal loop, but with unsigned index, limit and
increment. It takes an increment value, like +loop.
*** Changing the base
One can change the base of the program with the words hex (hexadecimal),
octal and decimal.
You can also change the base to whatever you please with 'n base !'. That
means you can declare : binary 2 base ! ; and see numbers in binary form!
That's pretty cool.
*** Double length numbers
d. ( d -- ) - Prints a signed, double length number.
d+, d-, dnegate, dabs, dmax, dmin, d=, d0=, d<, du< do what you'd expect.
d.r ( d width -- ) - Print the signed 32 bit number, right justified within
the field width.
* Chapter 8
** General notes
This chapter is called "Variables, constants and arrays". My normie
programming brain thinks these are good things, and will hopefully make the
code a bit more readable?
** Variables
You declare a variable like so:
variable <var>
One can set the variable like so:
<value> <var> !
This means that the earlier base changing trick (<num> base !) is actually
just changing a global "base" variable!
! is pronounced "store".
@ is the opposite of !, and is used for getting the values stored in a
variable. It's pronounced "fetch" and works thusly:
<variable> @
Printing the value of a variable (<var> @ .) is so common that Forth has a
separate word for it, ?.
Since variables are accessed at run time, it's good to store often changed
values there instead of in words since words that use those words would have
to be recompiled to work with the new definition. Variable declaration is
similar to word declaration, but it compiles a new word which when called
pushes its value address onto the stack. That's how <value> <var> ! works, it
pushes a value, pushes an address, and stores that value into that address.
** Constants
Constants are declared like this:
<value> constant <name>
The value can be retrieved by executing the constant name.
** Arrays
Arrays are simply variables with extra memory. What does that mean? To make
an array called "arr", you do this:
variable arr 8 allot
The "8 allot" part means that we want arr to contain 8 additional bytes, that
is, 1 extra cell on modern machines.
The new fields can be accessed with simple pointer arithmetic.
2 arr 8 + ! - Sets the second element of the array to 2 (on a 64-bit machine).
You can make an array with initial elements using create.
create xs 1 , 2 , 3 ,
will create an array called xs with 1, 2 and 3 as its initial elements. They
are each a cell big. Create creates an array which can be manipulated as
normal (the book has a notice for polyFORTH, but I don't think it's
relevant).
** Byte arrays
In addition to arrays, Forth also has byte arrays which operate on bytes
instead of cells. I think this one's probably better for portability since
you don't have to consider the cell size on different systems when using
it. You also don't have to double the index of an array since each value
correspond to a single byte.
Byte arrays can also be created with initial values, like you can for normal
arrays with create. The difference is that you use c, instead of ,.
create bytes 1 c, 2 c, 3 c,
** New words
!, @ and ? are described in the variables section.
+! ( n adr -- ) - Increments the value at adr with n.
Constant and variable operations can be prefixed with a 2 to mean a
double-length variable/constant.
fill ( adr n b -- ) - Fill n bytes of memory beginning at the address with
value b.
erase ( adr n -- ) - Like '<adr> <n> 0 fill'
c! and c@ are like ! and @ but for byte values.
* Chapter 9
** General notes
This looks to be a very interesting chapter. It's all about how Forth works
internally, which is extremely captivating.
In the section "vectored execution" the book goes over a powerful
strategy. Since the ' word gives the address of a word, you can store it in a
variable. Then, later, you can execute this word with <var> @ execute. This
means you can have a word have different meanings in different parts of the
program! This makes it possible to compile words and place them into
variables, which bypasses the need for recompiling every word that uses that
word.
** Dictionary entries
Dictionary entries are described in the book. They have the following
structure:
Name
Link (to previous word, the dictionary is a backwards linked list)
Code pointer
Parameter field
The code pointer is what makes variables, constants and colon definitions
different. Variables have a code pointer pointing to code which pushes the
variable's address onto the stack, and constants have a similar one but
pushes a value rather than an address. For other words, it points to machine
code to be executed when the word is invoked.
The parameter field contains data, and the size of it depends on the type of
variable, constant or colond definition. Normal variables/constants have a 1
cell sized parameter field, 2constant/2variable declares a constant/variable
with a 2 cell sized parameter field and colon definitions can have
arbitrarily long parameter fields. The address provided by tick and used by
execute is the parameter field address (pfa).
The name and the link is called the 'header' and the code pointer and the
parameter field is called the 'body'.
In colon defined words, the parameter field contaiins the addresses of the
words that comprise the defintion. These addresses are called code field
addresses.
** How colon defined words work
At the end of any colon defined word sits a call to exit. When executing a
word, the interpreter will first push the current interpreter pointer to the
return stack. Exit will pop this address and return to it. Therefore, you can
change where a colon defined word will return to by manipulating the return
stack directly! This also means that you can use exit as an 'early return' to
a word.
** The pad
The pad is some memory location a fixed place after here which is used for
text processing. Since it starts a fixed point after here, it moves as
definitions are added.
** The parameter stack
The parameter stack (remember, this is the thing referred to as just "the
stack") resides far above the pad in memory. The stack is in reality just
memory with a pointer that changes as we push and pop items off it. The stack
grows downwards, that is it begins at high memory and grows towards low
memory. The stack pointer memory value can be fetched with 's. The current
top value of the stack is therefore the same as 's @ (which is the same as
dup!). The point right before the bottom of the stack is pointed to by s0
(which doesn't seem to be defined in gforth!). 's, s0 and h are not required
by the standard, which explains why they're not available in gforth.
** Input buffer
The text retrieved from the command line interface is stored in the input
message buffer, which is located just above the stack and grows upwards.
** Return stack
Above the input buffer is the return stack. It works the same as the
parameter stack, but doesn't have words like 's and s0 defined for it.
** User variables
Variables available to the user (like base) is stored above the return
stack. User variables are different from the ones defined by the user (with
variable). They're kept in an array called the "user table". This was
probably more relevant back in the day with multi-user systems, but each user
has their own user table, meaning that if one person changes a base, it won't
affect others.
** Vocabularies
Vocabularies work as namespaces for word definitions. The book describes
three vocabularies, the standard Forth vocabulary, editor (not present on
modern systems) and assembly (which allows for making words in the computers
assembly language??). Now I want to know how to use this assembly
vocabulary. Vocabularies are just a part of the dictionary, but words of
particular vocabularies only point to words of the same vocabulary in the
dictionary. The current vocabulary is called the 'context' (it can be
accessed with the word context, but it's just an address). The current
vocabulary new words are linked to is found in the variable current. To
change current, use the word definitions (which will take the current context
and put it in current).
** New words
' ( -- adr ) - Takes the next word of the input stream and pushes the address onto the
stack. Since ' takes the *next word in the input stream* you can do stuff
like
: set ' <var> ! ;
And invoke it like "set <word>". The input stream is visible, even to words.
dump ( adr n -- ) - Prints the first n bytes of data found at adr.
['] - Takes the next word inside the definition and puts the address on the
stack. It ignores the input stream, which will be handy.
h ( -- adr ) - Returns the current address in the dictionary. This increases
as more words are defined.
here ( -- n ) - The same as h @. You can use this to see how much memory
something takes by first performing here, defining something and comparing
the new value of here to the one on the stack.
, is explained to be the same as 'here ! 2 allot'
user - define a new user variable.
* Chapter 10
** General notes
Ah, a practical chapter (it's about i/o).
It starts off with some (now) irrelevant information about blocks.
It's a difficult chapter to read, since half the information is in this way
irrelevant.
** "New" words (some old)
*** Output
emit ( c -- ) - Prints a value on the stack as an ascii character (using the
lower byte).
type ( adr n -- ) - Prints n bytes starting at adr. Used for printing
strings.
*** Input
key ( -- c ) - Waits for a key to be pressed, then pushes that ascii value
onto the stack. NOTE: This works pretty bad in emacs since it doesn't
support terminal codes that well.
expect ( adr u -- ) - Reads u bytes as a string and stores it at adr.
word ( c -- adr ) - Read a word from the input, using c as a delimiter. The
address returned has the count as the first value and the string follows.
text ( c -- ) - Reads a string using c as a delimiter, but places the string
in the pad. Text is non-standard and doesn't exist in gforth.
query ( -- ) - Expect, but read 80 chars and store them in the input message
buffer. The input has to be valid character.
*** Memory manipulation
move ( adr1 adr2 u -- ) - Copies u cells from adr1 to adr2.
cmove ( adr1 adr2 u -- ) - Like move, but for bytes instead of cells.
*** String operations
count ( adr -- adr+1 u ) - Takes the address of a string and returns the
length of the string (u) as well as the incremented address.
-text ( adr1 u adr2 -- f ) - Compares two strings of length u (adr1 and
adr2).
* Chapter 11
** General notes
What is this chapter about? Metaprogramming? The chapter name is "extending
the compiler" which makes my Lisp brain tingle.
** Compile time and run time
One of the first things the chapter tackles is the difference between run
time and compile time. You see, some words have both run time behaviour and
compile time behaviour. These types of words fall in one of two categories;
defining words and compiling words.
Constant is a defining word, as it takes an identifier at compile time and
exhibits run time behaviour in that, every time you call that word, you get
a value on the stack.
A compiling word is a word used inside a colon definition which does
different stuff when the word is defined from what is shown at run time. For
example, ." will at compile time embed the string into the word's parameter
field, but at run time print the string. Other examples include if and loop.
The first example of metaprogramming uses create and does>. I'm not really
seeing the power yet, and it still looks a bit limiting.
The next section covers how to change the behaviour of the colon
compiler. Now we're talking! Any word can be made immediate (word that will
be executed at definition time) by simply placing the immediate word behind a
definition.
: say-hello ." Hello " ; immediaten
Now, if this word is placed in a colon definition, it will execute at compile
time rather than run time. It can still be executed interactively.
** New words
does> ( -- pfa ) - Marks the end of the compile time behaviour in a definition and
the start of the run time behaviour it will exhibit. It pushes the pfa of the
word defined onto the stack.
compile ( -- ) - Used in immediate definitions. Places the following words
address inside the definition, making it possible to describe run time
behaviour. Not available in gforth.
[compile] ( -- ) - Used in definitions to move the compile time behaviour of
an immediate word to run time.
literal Compile time ( n -- ) Run time ( -- n ) - Compiles a literal value on
the stack by embedding the run time code for pushing the value onto the stack
and the value itself inside a definition.
[ ( -- ) - Stop compilation and start executing words.
] ( -- ) - Start compilation mode again. These words can be used in
conjuction with literal to do i.e. arithmetic at compile time.

View File

@ -23,11 +23,6 @@ Underneath is a code block.
(frobnicate x))
``
This can also be written as
``(define (frob x)
(forbnicate x))``
You can inline ``code`` blocks.
> Some profound text someone said once. This is a blockquote. It can occupy

25
site-src/netbpm.wtn Normal file
View File

@ -0,0 +1,25 @@
* Netbpm formats
The Netbpm formats are a series of very simple image formats. It is divided
into three types, with two variants each. The first variant stores its data in
human readable ASCII format, while the other stores the data in a binary (raw)
format.
The first of the three different types are the Portable BitMap format which is
able to store 1-bit images. The second is the Portable GrayMap format, with the
ability to store gray scale images with ranges from 0-255 or 0-65535. The last
format is the Portable PixMap format which stores a range from 0-255 for each
RGB channel.
The formats have a similar structure:
``
P3 # Magic value. P1 corresponds to BitMap, P2 to GrayMap, and P3 to PixMap.
# P4-P6 are for the binary variants.
800 600 # Width and height of the image.
255 # The maximum value for each colour. This marks the end of the header.
# The rest of the file is the actual image data.
255 0 0
255 255 255
...
``

11
site-src/reading.wtn Normal file
View File

@ -0,0 +1,11 @@
* Reading
Various texts I've read.
** 2021
_LAMBDA: The Ultimate Imperative_, Guy Lewis Steele Jr. and Gerald Jay Sussman
_The Colour of Magic_, Terry Pratchett
_{../resources/txt/starting-forth/ Starting Forth}_, Leo Brodie