Core compliance gaps
Epic 10 is all about mopping up the remaining bits of missing functionality that stop us claiming 100% compliance with the ANS Forth Core wordset.
The main omissions are:
- double-precision integers and their operators
- the traditional “pictured numeric output” system for printing formatted numbers
EVALUATEandENVIRONMENT?and a couple of other stragglers
Once pictured numbers are available, we can re-implement all the current
baked-in number formatting routines (., .S etc) on top of it: so there
shouldn’t be an appreciable increase in overall code size.
Planning
Nothing too remarkable, just the usual cycle of /bmad-create-story, /mad-dev-story and
/bmad-code-review finishing off with a retro /bmad-bmm-retrospective.
Double precision arithmetic
Here are some basic examples of double precision integers in action:

Note that the . denotes a double-precision integer and has nothing to do
with “floating point” or the decimal point. This is a classic Forth trap
for the unwary. Witness:

Here are a few more related words that were created in this sprint:

We also get a set of useful multiply routines:

M* takes two signed single precision values and multiplies them together
to give a double precision signed result. UM* takes two unsigned single
precision values and multiplies them to give a double precision unsigned
result. Finally D* takes two signed double precision values and multiplies
them to give a double precision signed result: watch for overflow with this one!
Finally in this section we have some useful division routines:

Why so many? Well, UM/MOD is an easy one, it divides an unsigned double
by an unsigned single and returns an unsigned qotient and an unsigned remainder.
Once we introduce signedness, things get interesting. SM/REM is symmetric or
truncating division, which rounds towards zero (truncating the decimal part) and
the remainder has the same sign as the dividend (the number that is being divided).
The “symmetric” description comes from the fact that -7 / 3 and 7 / -3 yield
the same magnitude quotient with opposite signs.
This is the standard / operator in C, C++, Java, JavaScript etc.
FM/MOD gives you floored division of a signed double by a signed single.
This rounds down to the nearest integer in the direction of negative infinity.
The remainder always takes the sign of the divisor. You might be familiar with
this sort of division from Python’s // operator.
Pictured numeric output
Pictured numeric output is Forth’s traditional technique for displaying formatted numeric output. It operates on double precision signed integers, and builds the ASCII representation digit-by-digit from right to left.
The important things to remember:
- pictured numeric output always uses double precision ints
- pictured numeric output always uses unsigned ints
You start a conversion with <#, then you get the least significant digit
with #. Then the next significant digit with #, and so on. At any point
you can say #s which means “as many #s as are left to finish converting
the number”. You can use HOLD to insert other characters at appropriate
junctures.

You can use SIGN to print a negative sign if needed, although it’s behaviour
is a little weird: it takes a (single precision signed) integer from the top-of-stack
and it it is negative it prints a minus sign. So you need to have this value
on the stack before you start number conversion, and you need to prefix your
SIGN word with a ROT word, like so:

You may be thinking “hold on, what’s that ROT in there for?!” - I certainly did.
The answer is, at each # step the interpreter is doing ( ud-lo ud-hi -- quot-lo quot-hi ) so there’s
always a working quotient on the stack until we emit #> - so at the point that we
emit SIGN (which must come before #>) the top three entries on the stack are -1 0 0 -
our original -1 and two cells for the remaining double precision quotient (which is
zero). So the ROT brings the -1 to the top-of-stack so SIGN can consume it.
We can actually see this in action:

TUCK DABS ... ROT SIGN is a common idom in Forth to display a signed double precision
number, and S>D TUCK DABS ... ROT is another for single precision signed numbers.

*/ and */MOD
*/ takes three single-precision numbers on the stack (n1 n2 n3 -- n4) - it multiples
n1 by n2 giving a douple precision result, then divides that by n3 to give the
quotient n4.
*/MOD is similar but it also returns the remainder (n1 n2 n3 -- n4 n5).

EVALUATE
EVALUATE evaluates Forth code from a string exactly as if you had tyed it in.
It’s the equivalent to eval() from other languages.

ENVIRONMENT?
The ENVIRONMENT word lets you query certain environmental constants in the system.
There are a number of different queries you can make, each identified by name.
The name precedes the ENVIRONMENT call and is in the usual Forth (c-addr u --)
(start address, count) convention. You’ll get back a true or a false in TOS to indicate
whether ENVIRONMENT? knows what you are talking about: a TRUE value is accompanied by
other pertinent information in the rest of the stack.
Here are the queries AntForth supports:
| Name | Type | Constant? | Meaning |
|---|---|---|---|
/COUNTED-STRING |
n | yes | max size of a counted string, in chars |
/HOLD |
n | yes | Size of pictured numeric output string in chars |
/PAD |
n | yes | size of scratch area pointed to by PAD |
ADDRESS-UNIT-BITS |
n | yes | size of one address unit, in bits |
FLOORED |
flag | yes | true if floored division is the default |
MAX-CHAR |
u | yes | maximum value of any character |
MAX-D |
d | yes | largest usable signed double integer |
MAX-N |
n | yes | largest usable signed integer |
MAX-U |
u | yes | largest usable unsigned integer |
MAX-UD |
ud | yes | largest usable unsigned double integer |
RETURN-STACK-CELLS |
n | yes | maximum size of the return stack, in cells |
STACK-CELLS |
n | yes | maximum size of the data stack, in cells |
AntForth also supports wordset queries from the 1994 ANS Forth standard, although the user should note that the 2012 standard makes these as obsolete:
| Name | Type | Constant? | Meaning |
|---|---|---|---|
/CORE |
flag | no | True if complete Core wordset is present |
/CORE-EXT |
flag | no | True if Core extensions wordset is present |
Other values may become available as AntForth supports them.