In C4, a program is a sequence of WORD-CODEs.
A WORD-CODE is a 32-bit unsigned number.
Primitives are assigned numbers sequentially from 0 to BYE
.
If a WORD-CODE is less than or equal to BYE
, it is a primitive.
If the top 3 bits are set ($Exxxxxxx), it is a 29-bit unsigned literal.
If it is between BYE
, and $E0000000, it is the code address of a word to execute.
A CELL
is either 32-bits or 64-bits, depending on the target system.
- Linux 32-bit (-m32): a CELL is 32-bits.
- Linux 64-bit (-m64): a CELL is 64-bits.
- Windows 32-bit (x86): a CELL is 32-bits.
- Windows 64-bit (x64): a CELL is 64-bits.
C4 provides two memory areas:
- The CODE area can store up to $1FFFFFFF 32-bit WORD-CODEs. (see
code-sz
).- NOTE: CODE slots 0-25 (
0 wc@ .. 25 wc@
) are reserved for C4 system values. - NOTE: CODE slots 26-75 (
26 wc@
..75 wc@
) are unused by C4. - NOTE: These are free for the application to use as desired.
- NOTE: Use
wc@
andwc!
to get and set WORD-CODE values in the code area. here
is an offset into the code area.
- NOTE: CODE slots 0-25 (
- The VARS area can store up to CELL bytes (see
vars-sz
).vhere
is the address of the first free byte the vars area.last
is an offset into the vars area.
- Use
->code
and->vars
to turn an offset into an address.
WORD | STACK | DESCRIPTION |
---|---|---|
(dsp) | (--N) | CODE slot for the data stack pointer |
(rsp) | (--N) | CODE slot for the return stack pointer |
(lsp) | (--N) | CODE slot for the loop stack pointer |
(tsp) | (--N) | CODE slot for the T stack pointer |
(asp) | (--N) | CODE slot for the A stack pointer |
(here) | (--N) | CODE slot for the HERE variable |
(last) | (--N) | CODE slot for the LAST variable |
base | (--N) | CODE slot for the BASE variable |
state | (--N) | CODE slot for the STATE variable |
Strings in C4 are NULL-terminated strings with no count byte (ztype).
Similar to the printf() function in C, C4 supports formatted output using '%'.
For example : ascii dup dup dup ." char %c, decimal #%d, binary: %%%b, hex: $%x%n" ;
.
Format | Stack | Description |
---|---|---|
%b | (N--) | Print TOS in base 2. |
%c | (N--) | EMIT TOS. |
%d | (N--) | Print TOS in base 10. |
%e | (--) | EMIT escape (#27). |
%i | (N--) | Print TOS in the current base. |
%n | (--) | Print CR/LF (13/10). |
%q | (--) | EMIT " (#34). |
%s | (A--) | Print TOS as a string (formatted). |
%S | (A--) | Print TOS as a string (unformatted). |
%x | (N--) | Print TOS in base 16. |
%[x] | (--) | EMIT [x]. |
C4 includes an A stack.
This is somewhat similar to MachineForth's operations for 'a', but in C4, it is a stack.
The size of the A stack is configurable (see tstk-sz
).
WORD | STACK | DESCRIPTION |
---|---|---|
>a |
(N--) | Push N onto the A stack. |
a! |
(N--) | Set A-TOS to N. |
a@ |
(--N) | N: copy of A-TOS. |
a@+ |
(--N) | N: copy of A-TOS, then increment A-TOS. |
a@- |
(--N) | N: copy of A-TOS, then decrement A-TOS. |
a> |
(--N) | Pop N from the A stack. |
adrop | ( -- ) | Drop A-TOS |
C4 includes a T stack, with same ops as the A stack.
Note that there are also additional words for the return stack.
WORD | STACK | DESCRIPTION |
---|---|---|
>t |
(N--) | Push N onto the T stack. |
t! |
(N--) | Set T-TOS to N. |
t@ |
(--N) | N: copy of T-TOS. |
t@+ |
(--N) | N: copy of T-TOS, then increment T-TOS. |
t@- |
(--N) | N: copy of T-TOS, then decrement T-TOS. |
t> |
(--N) | Pop N from the T stack. |
tdrop | ( -- ) | Drop T-TOS |
Stack effect notation conventions:
TERM | DESCRIPTION |
---|---|
SZ/NM/MD | String, uncounted, NULL terminated |
A | Address |
C | Number, 8-bits |
WC | WORD-CODE, 32-bits |
N/X/Y | Number, CELL sized |
F | Flag: 0 mean0 false, <>0 means true |
R | Register number |
FH | File handle: 0 means no file |
I | For loop index counter |
The primitives:
WORD | STACK | DESCRIPTION |
---|---|---|
(lit) | (--WC) | WC: WORD-CODE for LIT primitive |
(jmp) | (--WC) | WC: WORD-CODE for JMP primitive |
(jmpz) | (--WC) | WC: WORD-CODE for JMPZ primitive |
(jmpnz) | (--WC) | WC: WORD-CODE for JMPNZ primitive |
(njmpz) | (--WC) | WC: WORD-CODE for NJMPZ primitive |
(njmpnz) | (--WC) | WC: WORD-CODE for NJMPNZ primitive |
(exit) | (--WC) | WC: WORD-CODE for EXIT primitive |
exit | (--) | EXIT word |
dup | (X--X X) | Duplicate TOS (Top-Of-Stack) |
swap | (X Y--Y X) | Swap TOS and NOS (Next-On-Stack) |
drop | (N--) | Drop TOS |
over | (N X--N X N) | Push NOS |
@ | (A--N) | N: the CELL at absolute address A |
c@ | (A--C) | C: the CHAR at absolute address A |
d@ | (A--D) | D: the DWORD at absolute address A |
wc@ | (N--WC) | WC: the WORD-CODE in CODE slot N |
! | (N A--) | Store CELL N to absolute address A |
c! | (C A--) | Store CHAR C to absolute address A |
d! | (D A--) | Store DWORD D to absolute address A |
wc! | (WC N--) | Store WORD-CODE WC to CODE slot N |
+ | (X Y--N) | N: X + Y |
- | (X Y--N) | N: X - Y |
* | (X Y--N) | N: X * Y |
/ | (X Y--N) | N: X / Y (integer division) |
/mod | (X Y--M Q) | M: X modulo Y, Q: quotient of X / Y |
1+ | (X--Y) | Increment TOS |
1- | (X--Y) | Decrement TOS |
< | (X Y--F) | F: 1 if (X < Y), else 0 |
= | (X Y--F) | F: 1 if (X = Y), else 0 |
> | (X Y--F) | F: 1 if (X > Y), else 0 |
0= | (N--F) | F: 1 if (N=0), else 0 |
and | (X Y--N) | N: X AND Y |
or | (X Y--N) | N: X OR Y |
xor | (X Y--N) | N: X XOR Y |
com | (X--Y) | Y: X with all bits flipped (complement) |
for | (N--) | Begin FOR loop with bounds 0 and N-1. |
i | (--I) | N: Current FOR loop index. |
next | (--) | Increment I. If I >= N, exit, else start loop again. |
>r | (N--) | Push N onto the return stack |
r! | (N--) | Set R-TOS to N |
r@ | (--N) | N: copy of R-TOS |
r@+ | (--N) | N: copy of R-TOS, then increment it |
r@- | (--N) | N: copy of R-TOS, then decrement it |
r> | (--N) | Pop N from the return stack |
rdrop | (--) | Drop R-TOS |
>t | (N--) | Push N onto the T stack |
t! | (N--) | Set T-TOS to N |
t@ | (--N) | N: copy of T-TOS |
t@+ | (--N) | N: copy of T-TOS, then increment T-TOS |
t@- | (--N) | N: copy of T-TOS, then decrement T-TOS |
t> | (--N) | Pop N from the T stack |
tdrop | (--) | Drop T-TOS |
>a | (N--) | Push N onto the A stack |
a! | (N--) | Set A-TOS to N |
a@ | (--N) | N: copy of A-TOS |
a@+ | (--N) | N: copy of A-TOS, then increment A-TOS |
a@- | (--N) | N: copy of A-TOS, then decrement A-TOS |
a> | (--N) | Pop N from the A stack |
adrop | (--) | Drop A-TOS |
emit | (C--) | Output char C |
: | (--) | Create a new word, set STATE=1 |
; | (--) | Compile EXIT, set STATE=0 |
lit, | (N--) | Compile a push of number N |
next-wd | (--L) | L: length of the next word from the input stream |
immediate | (--) | Mark the last created word as IMMEDIATE |
inline | (--) | Mark the last created word as INLINE |
outer | (S--) | Send string S to the C4 outer interpreter |
addword | (--) | Add the next word to the dictionary |
timer | (--N) | N: Current time |
see X | (--) | Output the definition of word X |
ztype | (S--) | Print string at S (unformatted) |
ftype | (S--) | Print string at S (formatted) |
s-cpy | (D S--D) | Copy string S to D |
s-eq | (D S--F) | F: 1 if string S is equal to D (case sensitive) |
s-eqi | (D S--F) | F: 1 if string S is equal to D (NOT case sensitive) |
s-len | (S--N) | N: Length of string S |
z" | (--) | -COMPILE: Create string S to next " |
(--S) | -RUN: push address S of string | |
." | (--) | -COMPILE: execute z" , compile ftype |
(--) | -RUN: ftype on string |
|
find | (--XT A) | XT: Execution Token, A: Dict Entry address (0 0 if not found) |
loaded? | (XT A--) | Stops current load if A <> 0 (see find ) |
fopen | (NM MD--FH) | NM: File Name, MD: Mode, FH: File Handle (0 if error/not found) |
fclose | (FH--) | FH: File Handle to close |
fdelete | (NM--) | NM: File Name to delete |
fread | (A N FH--X) | A: Buffer, N: Size, FH: File Handle, X: num chars read |
fwrite | (A N FH--X) | A: Buffer, N: Size, FH: File Handle, X: num chars written |
fgets | (A N FH--X) | A: Buffer, N: Size, X: num chars read (0 if EOF/Error) |
include X | (--) | Load file named X (X: next word) |
load | (N--) | N: Block number to load (file named "block-NNN.fth") |
load-next | (N--) | Close the current block and load block N next |
system | (S--) | PC ONLY: S: String to send to system() |
bye | (--) | PC ONLY: Exit C4 |
Default words are defined in function sys_load()
in file sys-load.c.
For details, or to add or change the default words, modify that function.