├── .gitignore ├── Editor.md ├── LICENSE ├── README.md ├── block-003.fth ├── block-004.fth ├── block-005.fth ├── block-006.fth ├── block-007.fth ├── block-008.fth ├── block-010.fth ├── block-020.fth ├── block-021.fth ├── block-022.fth ├── block-023.fth ├── block-024.fth ├── block-025.fth ├── block-026.fth ├── block-049.fth ├── block-050.fth ├── block-051.fth ├── block-200.fth ├── block-201.fth ├── block-202.fth ├── c4.c ├── c4.h ├── c4.sln ├── c4.vcxproj ├── editor.c ├── file-pc.c ├── makefile ├── sys-load.c └── system.c /.gitignore: -------------------------------------------------------------------------------- 1 | c4 2 | c4-32 3 | /block-99?.fth 4 | /x64 5 | /Debug 6 | /Release 7 | /c4.vcxproj.user 8 | .vscode 9 | /enc_temp_folder/ 10 | -------------------------------------------------------------------------------- /Editor.md: -------------------------------------------------------------------------------- 1 | # The c4 editor 2 | 3 | The c4 editor is somewhat similar to a stripped-down version of VI.
4 | Enable the editor using `#define EDITOR` (in c4.h)
5 | The current block being edited is in 'block@'.
6 | Use '23 edit' to edit block #23.
7 | Use 'ed' to re-edit the last edited block.
8 | While in the editor, use ':!5 block!' to switch to another block.
9 | 10 | ## Modes 11 | There are 4 modes in the editor: 12 | - NORMAL 13 | - INSERT 14 | - REPLACE 15 | - COMMAND 16 | 17 | ### These control keys are active in all modes: 18 | 19 | | Key | Action | 20 | | :-- | :-- | 21 | | [ctrl]+a | Insert/replace tag: COMPILE | 22 | | [ctrl]+b | Insert/replace tag: DEFINE | 23 | | [ctrl]+c | Insert/replace tag: INTERP | 24 | | [ctrl]+d | Insert/replace tag: COMMENT | 25 | | [ctrl]+e | Send the current line to the outer interpreter | 26 | | [ctrl]+h | Left 1 char (and delete it if in INSERT mode) | 27 | | [ctrl]+i | Right 8 chars (also [tab]) | 28 | | [shift]+tab | Left 8 chars | 29 | | [ctrl]+j | Down 1 line | 30 | | [ctrl]+k | Up 1 line | 31 | | [ctrl]+l | Right 1 char | 32 | | [ctrl]+q | Left 8 chars | 33 | | [ctrl]+s | Save the block to disk | 34 | | [ctrl]+Home | Goto the beginning of the first line | 35 | | [ctrl]+End | Goto the beginning of the last line | 36 | | [ctrl]+x | Delete the char under the cursor | 37 | | [esc-esc] | Goto NORMAL mode | 38 | 39 | ### NORMAL mode 40 | 41 | | Key | Action| 42 | | :-- | :-- | 43 | | $ | Goto the end of the line | 44 | | _ | Goto the beginning of the line | 45 | | [CR] | Goto the beginning of the next line | 46 | | [SP] | Right 1 char | 47 | | 1 | Set tag: COMPILE | 48 | | 2 | Set tag: DEFINE | 49 | | 3 | Set tag: INTERP | 50 | | 4 | Set tag: COMMENT | 51 | | : | Change to COMMAND mode | 52 | | + | Save the current block and goto the next block | 53 | | - | Save the current block and goto the previous block | 54 | | / | Specify search string, jump to next occurrence | 55 | | # | Redraw the screen | 56 | | a | Append: move right 1 char and change to INSERT mode (same as 'li') | 57 | | A | Append: goto the end of the line and change to INSERT mode (same as '$i') | 58 | | b | Insert a BLANK/SPACE into the current line | 59 | | B | Insert a BLANK/SPACE to the end of the block | 60 | | c | Change: Delete the current char and change to INSERT mode (same as 'xi') | 61 | | C | Change: Delete to the end of the line and change to INSERT mode (same as 'd$i') | 62 | | d. | Delete the char under the cursor (same as 'x') | 63 | | d$ | Delete to the end of the line | 64 | | dd | Copy the current line into the YANK buffer and delete the line | 65 | | dw | Delete the current word | 66 | | D | Delete to the end of the line (same as 'd$') | 67 | | g | Goto the top-left of the screen | 68 | | G | Goto the bottom-left of the screen | 69 | | h | Left 1 char | 70 | | i | Change to INSERT mode | 71 | | I | Goto the beginning of the line and change to INSERT mode (same as '_i') | 72 | | j | Down 1 line | 73 | | J | Join the current and next lines together | 74 | | k | Up 1 line | 75 | | l | Right 1 char | 76 | | m | Down 4 lines | 77 | | M | Up 4 lines | 78 | | n | Jump to next occurrence of search string | 79 | | N | Jump to previous occurrence of search string | 80 | | o | Insert an empty line BELOW the current line and change to INSERT mode | 81 | | O | Insert an empty line ABOVE the current line and change to INSERT mode | 82 | | p | Paste the YANK buffer into a new line BELOW the current line | 83 | | P | Paste the YANK buffer into a new line ABOVE the current line | 84 | | q | Right 4 chars | 85 | | Q | Left 4 chars | 86 | | r | Replace the char under the cursor with the next key pressed (if printable) | 87 | | R | Change to REPLACE mode | 88 | | S | Switch to last edited block | 89 | | w | Move right to the beginning of the next word | 90 | | W | Move left to the beginning of the previous word | 91 | | x | Delete the char under the cursor to the end of the line | 92 | | X | Delete the char to the left of the cursor (same as 'hx') | 93 | | Y | Copy the current line into the YANK buffer | 94 | | Z | Delete the char under the cursor to the end of the block | 95 | 96 | ### INSERT mode 97 | 98 | In INSERT mode, all printable characters are inserted into the line. 99 | - [Enter] inserts a new line at the cursor. 100 | - [Backspace] moves left and deletes that character. 101 | 102 | ### REPLACE mode 103 | 104 | In REPLACE mode, all printable characters are placed into the line. 105 | - [Enter] moves to the beginning of the next line. 106 | - [Backspace] moves left and does not delete that character. 107 | 108 | ### COMMAND mode 109 | 110 | COMMAND mode is invoked when pressing ':' in NORMAL mode. 111 | 112 | | Command | Action| 113 | | :-- | :-- | 114 | | rl | ReLoad: discard all changes and reload the current block | 115 | | w | Write the current block if it has changed | 116 | | w! | Write the current block, even if it has NOT changed | 117 | | wq | Write the current block and quit (same as ':w' ':q') | 118 | | q | Quit, if the current block has NOT changed | 119 | | q! | Quit, even if the current block has changed | 120 | | ![any] | Send [any] to the c4 outer interpreter | 121 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Chris Curl 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # c4: A portable Forth system inspired by ColorForth and Tachyon 2 | 3 | ## ColorForth's influence on c4 4 | c4 supports control characters in the whitespace that change the state.
5 | c4 has 'A', 'B' and 'T' stacks, inspired by ColorForth's 'a' register.
6 | c4 has 4 states: INTERPRET, COMPILE, DEFINE, AND COMMENT,
7 | c4 also supports the standard state-change words.
8 | 9 | | Ascii | Word | State | Description| 10 | |:-- |:-- |:-- |:-- | 11 | | $01 | ] | 1 | Compile | 12 | | $02 | : | 2 | Define | 13 | | $03 | [ | 3 | Interpret/execute/immediate | 14 | | $04 | | 4 | Comment | 15 | | | ( | 4 | Comment, save current state | 16 | | | ) | | End comment, restores saved state | 17 | 18 | **NOTE**: In the DEFINE state, c4 changes the state to COMPILE after adding the next word.
19 | **NOTE**: Unlike ColorForth, ';' compiles EXIT and then changes the state to INTERPRET.
20 | 21 | ## Tachyon's influence on c4 22 | In c4, a program is a sequence of WORD-CODEs.
23 | A WORD-CODE is a 16-bit unsigned number (a WORD).
24 | Primitives are assigned numbers sequentially from 0 to **BYE**.
25 | If a WORD-CODE is less than or equal to **BYE**, it is a primitive.
26 | If the top 3 bits are set, it is a 13-bit unsigned literal, 0-$1FFF.
27 | If it is between **BYE**, and $E000, it is the code address of a word to execute.
28 | 29 | ## c4's built-in block editor 30 | c4 has a built-in editor. See **Editor.md** for details.
31 | The editor can be excluded from c4 by undefining **EDITOR** in c4.h.
32 | It is built-in so that the editor is available when running c4 from any folder.
33 | In c4, the size if a block is 3072 bytes (3x1024).
34 | The editor is 32 lines, 96 columns, and has a VI like feel.
35 | 36 | ## Building c4 37 | ### Windows 38 | - There is a Visual Studio solution file, c4.sln (either 32- or 64-bit) 39 | ### Linux and other similar systems 40 | - There is a makefile. 41 | - The default architecture is 32-bits. That is faster on my systems. 42 | - 32-bit: use 'make' 43 | - 64-bit: use 'ARCH=64 make' 44 | 45 | ## c4 memory usage 46 | c4 provides a single memory area with size 'mem-sz' (see c4.h, MEM_SZ). 47 | - It is divided into 3 areas as follows **[CODE][VARS][Dictionary]**. 48 | - The **CODE** area is an aray of WORD-CODEs starting at the beginning of the memory. 49 | - The **VARS** area is defined to begin at address **&memory[CODE_SLOTS*WC_SZ]**. 50 | - The **Dictionary** starts at the end and grows downward. 51 | - The size of the CODE area is 'code-sz' (see c4.h, CODE_SLOTS). 52 | - `here` is an offset into the **CODE** area, the next slot to be allocated. 53 | - `last` is the address of the most recently created word. 54 | - `vhere` is the address of the first free byte the **VARS** area. 55 | - Use `->memory` to turn an offset into an address into the memory area. 56 | - Use `->code` to turn an offset into an address of a WORD-CODE. 57 | - **NOTE**: CODE slots 0-25 (`0 wc@` .. `25 wc@`) are reserved for c4 system values. 58 | - **NOTE**: CODE slots 26-BYE (`26 wc@` .. ` wc@`) are unused by c4. 59 | - **NOTE**: These are free for the application to use as desired. 60 | - **NOTE**: Use `wc@` and `wc!` to get and set WORD-CODE values in the **CODE** area. 61 | 62 | | WORD | STACK | DESCRIPTION | 63 | |:-- |:-- |:-- | 64 | | memory | (--A) | A: starting address of the c4 memory | 65 | | vars | (--A) | A: starting address of the VARS area | 66 | | mem-sz | (--N) | N: size in BYTEs of the c4 memory | 67 | | code-sz | (--N) | N: size in WORD-CODEs of the c4 CODE area | 68 | | dstk-sz | (--N) | N: size in CELLs of the DATA and RETURN stacks | 69 | | tstk-sz | (--N) | N: size in CELLs of the A and T stacks | 70 | | wc-sz | (--N) | N: size in BYTEs of a WORD-CODE | 71 | | de-sz | (--N) | N: size in BYTEs of a dictionary entry | 72 | | (dsp) | (--N) | N: CODE slot for the data stack pointer | 73 | | (rsp) | (--N) | N: CODE slot for the return stack pointer | 74 | | (lsp) | (--N) | N: CODE slot for the loop stack pointer | 75 | | (tsp) | (--N) | N: CODE slot for the T stack pointer | 76 | | (asp) | (--N) | N: CODE slot for the A stack pointer | 77 | | (bsp) | (--N) | N: CODE slot for the B stack pointer | 78 | | (here) | (--N) | N: CODE slot for the HERE variable | 79 | | (vhere) | (--A) | A: address of the VHERE variable | 80 | | (last) | (--A) | A: address of the LAST variable | 81 | | base | (--N) | N: CODE slot for the BASE variable | 82 | | state | (--N) | N: CODE slot for the STATE variable | 83 | | (block) | (--N) | N: CODE slot for the BLOCK variable | 84 | 85 | ## c4 Strings 86 | Strings in c4 are NULL-terminated with no count byte.
87 | 88 | ## Format specifiers in `ftype` and `."` 89 | Similar to the printf() function in C, c4 supports formatted output using '%'.
90 | For example `: ascii ( n-- ) dup dup dup ." char %c, decimal #%d, binary: %%%b, hex: $%x%n" ;`. 91 | 92 | | Format | Stack | Description | 93 | |:-- |:-- |:-- | 94 | | %b | (N--) | Print TOS in base 2. | 95 | | %c | (N--) | EMIT TOS. | 96 | | %d | (N--) | Print TOS in base 10. | 97 | | %e | (--) | EMIT `escape` (#27). | 98 | | %i | (N--) | Print TOS in the current base. | 99 | | %n | (--) | Print CR/LF (13/10). | 100 | | %q | (--) | EMIT a `double-quote` (#34). | 101 | | %s | (A--) | Print TOS as a string (formatted). | 102 | | %S | (A--) | Print TOS as a string (unformatted). | 103 | | %x | (N--) | Print TOS in base 16. | 104 | | %B | (--) | Change foreground to Blue | 105 | | %G | (--) | Change foreground to Green | 106 | | %P | (--) | Change foreground to Purple | 107 | | %R | (--) | Change foreground to Reg | 108 | | %W | (--) | Change foreground to White | 109 | | %Y | (--) | Change foreground to Yellow | 110 | | %[x] | (--) | EMIT [x]. | 111 | 112 | ## The A, B and T stacks 113 | c4 includes A, B and T stacks.
114 | These are similar to ColorForth's operations for 'a', but in c4, they are stacks.
115 | The size of the stacks is 'tstk-sz' (see c4.h, TSTK_SZ).
116 | Note that there are also additional words for the return stack.
117 | 118 | | WORD | STACK | DESCRIPTION | 119 | |:-- |:-- |:-- | 120 | | >a | (N--) | Push N onto the A stack. | 121 | | a! | (N--) | Set A-TOS to N. | 122 | | a@ | (--N) | N: copy of A-TOS. | 123 | | a@+ | (--N) | N: copy of A-TOS, then increment A-TOS. | 124 | | a@- | (--N) | N: copy of A-TOS, then decrement A-TOS. | 125 | | a> | (--N) | Pop N from the A stack. | 126 | | adrop | (--) | Drop A-TOS. | 127 | 128 | ## Inline words 129 | In c4, an "INLINE" word is similar to a macro. When compiling a word that is INLINE, c4 copies the contents of the word (up to, but not including the first EXIT) to the target, as opposed to compiling a CALL to the word. This improves performance, but uses extra space. 130 | 131 | **Note that if a word might have an embedded 7 (EXIT) in its implementation (eg - a word in an address for example), then it should not be marked as INLINE.** 132 | 133 | ## Temporary words 134 | c4 provides 10 temporary words, 't0' thru 't9'. 135 | - Defining a temporary word does not add an entry to the dictionary. 136 | - Temporary words are intended to be helpful in factoring code. 137 | - Their names are case-sensitive (t0-t9, not T0-T9). 138 | - A temporary word can be redefined as often as desired. 139 | - When redefined, code references to the previous definition are unchanged. 140 | - t0 thru t5 are 'normal', t6 thru t9 are 'inline'. 141 | 142 | ## c4 WORD-CODE primitives 143 | The primitives: 144 | 145 | | WORD | STACK | DESCRIPTION | 146 | |:-- |:-- |:-- | 147 | | : | (--) ST=2 | Set STATE to DEFINE (RED) | 148 | | ; | (--) ST=3 | Compile EXIT and set STATE to INTERPRET (YELLOW) | 149 | | [ | (--) ST=3 | Set STATE to INTERPRET (YELLOW) | 150 | | ] | (--) ST=1 | Set STATE to COMPILE (GREEN) | 151 | | ( | (--) ST=4 | Remember STATE. Set STATE to COMMENT (WHITE) | 152 | | ) | (--) ST=? | Set STATE to previous state | 153 | | (lit) | (--WC) | WC: WORD-CODE for the LIT primitive | 154 | | (jmp) | (--WC) | WC: WORD-CODE for the JMP primitive | 155 | | (jmpz) | (--WC) | WC: WORD-CODE for the JMPZ primitive | 156 | | (jmpnz) | (--WC) | WC: WORD-CODE for the JMPNZ primitive | 157 | | (njmpz) | (--WC) | WC: WORD-CODE for the NJMPZ primitive | 158 | | (njmpnz) | (--WC) | WC: WORD-CODE for the NJMPNZ primitive | 159 | | (exit) | (--WC) | WC: WORD-CODE for the EXIT primitive | 160 | | exit | (--) | EXIT word | 161 | | dup | (N--N N) | Duplicate TOS (Top-Of-Stack) | 162 | | swap | (X Y--Y X) | Swap TOS and NOS (Next-On-Stack) | 163 | | drop | (N--) | Drop TOS | 164 | | over | (N X--N X N) | Push NOS | 165 | | @ | (A--N) | N: the CELL at absolute address A | 166 | | c@ | (A--C) | C: the CHAR at absolute address A | 167 | | w@ | (A--W) | W: the 16-bit WORD at absolute address A | 168 | | d@ | (A--D) | D: the 32-bit DWORD at absolute address A | 169 | | wc@ | (N--WC) | WC: the WORD-CODE in CODE slot N | 170 | | ! | (N A--) | Store CELL N to absolute address A | 171 | | c! | (C A--) | Store CHAR C to absolute address A | 172 | | w! | (W A--) | Store 16-bit WORD W to absolute address A | 173 | | d! | (D A--) | Store 32-bit DWORD D to absolute address A | 174 | | wc! | (WC N--) | Store WORD-CODE WC to CODE slot N | 175 | | + | (X Y--N) | N: X + Y | 176 | | - | (X Y--N) | N: X - Y | 177 | | * | (X Y--N) | N: X * Y | 178 | | / | (X Y--N) | N: X / Y (integer division) | 179 | | /mod | (X Y--M Q) | M: X modulo Y, Q: quotient of X / Y | 180 | | 1+ | (X--Y) | Increment TOS | 181 | | 1- | (X--Y) | Decrement TOS | 182 | | +! | (N A--) | Add N to the CELL at A | 183 | | < | (X Y--F) | F: 1 if (X < Y), else 0 | 184 | | = | (X Y--F) | F: 1 if (X = Y), else 0 | 185 | | > | (X Y--F) | F: 1 if (X > Y), else 0 | 186 | | 0= | (N--F) | F: 1 if (N=0), else 0 | 187 | | and | (X Y--N) | N: X AND Y | 188 | | or | (X Y--N) | N: X OR Y | 189 | | xor | (X Y--N) | N: X XOR Y | 190 | | com | (X--Y) | Y: X with all bits flipped (complement) | 191 | | for | (CNT--) | Begin FOR loop with bounds 0 and CNT-1. | 192 | | i | (--I) | I: Current FOR loop index. | 193 | | next | (--) | Increment I. If I >= CNT, exit, else start loop again. | 194 | | unloop | (--) | Unwind the loop stack. **DOES NOT EXIT THE LOOP**. | 195 | | >r | (N--) | Push N onto the return stack | 196 | | r! | (N--) | Set R-TOS to N | 197 | | r@ | (--N) | N: copy of R-TOS | 198 | | r@+ | (--N) | N: copy of R-TOS, then increment it | 199 | | r@- | (--N) | N: copy of R-TOS, then decrement it | 200 | | r> | (--N) | Pop N from the return stack | 201 | | rdrop | (--) | Drop R-TOS | 202 | | >t | (N--) | Push N onto the T stack | 203 | | t! | (N--) | Set T-TOS to N | 204 | | t@ | (--N) | N: copy of T-TOS | 205 | | t@+ | (--N) | N: copy of T-TOS, then increment T-TOS | 206 | | t@- | (--N) | N: copy of T-TOS, then decrement T-TOS | 207 | | t> | (--N) | Pop N from the T stack | 208 | | tdrop | (--) | Drop T-TOS | 209 | | >a | (N--) | Push N onto the A stack | 210 | | a! | (N--) | Set A-TOS to N | 211 | | a@ | (--N) | N: copy of A-TOS | 212 | | a@+ | (--N) | N: copy of A-TOS, then increment A-TOS | 213 | | a@- | (--N) | N: copy of A-TOS, then decrement A-TOS | 214 | | a> | (--N) | Pop N from the A stack | 215 | | adrop | (--) | Drop A-TOS | 216 | | >b | (N--) | Push N onto the B stack | 217 | | b! | (N--) | Set B-TOS to N | 218 | | b@ | (--N) | N: copy of B-TOS | 219 | | b@+ | (--N) | N: copy of B-TOS, then increment B-TOS | 220 | | b@- | (--N) | N: copy of B-TOS, then decrement B-TOS | 221 | | b> | (--N) | Pop N from the B stack | 222 | | bdrop | (--) | Drop B-TOS | 223 | | emit | (C--) | Output char C | 224 | | lit, | (N--) | Compile a push of number N | 225 | | next-wd | (--A L) | L: length of the next word (A) from the input stream | 226 | | | | - If L=0, then A is an empty string (end of input) | 227 | | immediate | (--) | Mark the last created word as IMMEDIATE | 228 | | inline | (--) | Mark the last created word as INLINE | 229 | | outer | (S--) | Send string S to the c4 outer interpreter | 230 | | addword | (--) | Add the next word to the dictionary | 231 | | timer | (--N) | N: Current time | 232 | | see X | (--) | Output the definition of word X | 233 | | ztype | (S--) | Print string at S (unformatted) | 234 | | ftype | (S--) | Print string at S (formatted) | 235 | | s-cpy | (D S--D) | Copy string S to D | 236 | | s-find | (B L S--N) | Find string L in B starting at char S. N = -1 if not found | 237 | | s-eq | (D S--F) | F: 1 if string S is equal to D (case sensitive) | 238 | | s-eqi | (D S--F) | F: 1 if string S is equal to D (NOT case sensitive) | 239 | | s-len | (S--N) | N: Length of string S | 240 | | z" str" | (--) | - COMPILE: generate code to push address of `str` (vhere) | 241 | | | (--A) | - RUN: A=address of `str` | 242 | | | (--A) | - INTERPRET: A=address of `str` (only 1 string supported) | 243 | | ." msg" | (--) | - COMPILE: execute `z"`, compile `ftype` | 244 | | | (--) | - RUN: perform `ftype` on `msg` | 245 | | | (--) | - INTERPRET: output `msg` using `ftype` | 246 | | find | (--XT A) | XT: Execution Token, A: Dict Entry address (0 0 if not found) | 247 | | loaded? | (XT A--) | Stops current load if A <> 0 (see `find`) | 248 | | fopen | (NM MD--FH) | NM: File Name, MD: Mode, FH: File Handle (0 if error/not found) | 249 | | fclose | (FH--) | FH: File Handle to close | 250 | | fdelete | (NM--) | NM: File Name to delete | 251 | | fread | (A N FH--X) | A: Buffer, N: Size, FH: File Handle, X: num chars read | 252 | | fwrite | (A N FH--X) | A: Buffer, N: Size, FH: File Handle, X: num chars written | 253 | | fgets | (A N FH--X) | A: Buffer, N: Size, X: num chars read (0 if EOF/Error) | 254 | | include X | (--) | Load file named X (X: next word) | 255 | | load | (N--) | N: Block number to load (file named "block-NNN.fth") | 256 | | load-next | (N--) | Close the current block and load block N next | 257 | | system | (S--) | PC ONLY: S: String to send to `system()` | 258 | | bye | (--) | PC ONLY: Exit c4 | 259 | 260 | ## c4 default words 261 | If _SYS_LOAD_ is not defined in file c4.h, load block-000.fth. 262 | Else, load the words in function `sys_load()` in file sys-load.c.
263 | For details, or to add or change the default words, modify that function. 264 | -------------------------------------------------------------------------------- /block-003.fth: -------------------------------------------------------------------------------- 1 | ( Block 3 - Strings ) 2 | 3 | find fill loaded? 4 | 5 | p1vhere $100 + ; p2p1 $100 + ; 6 | 7 | fill (dst cnt ch--)>t swap >a for t@ c!a+ nextatdrop ; 8 | move (src dst n--) >r >t >a r> for a@ @ t@ ! a@ cell+ a! t@ cell+ t! nextatdrop ; 9 | cmove (src dst n--) >r >t >a r> for c@a+ c!t+ nextatdrop ; 10 | cmove>(src dst n--) >r r@ + 1- >t r@ + 1- >a r> for c@a- c!t- nextatdrop ; 11 | 12 | s-trunc(dst--dst) 0 over c! ; 13 | s-end (str-end) dup s-len + ; 14 | s-cat (dst src--dst)over s-end swap s-cpy drop ; 15 | s-catc (dst ch--dst) over s-end w! ; 16 | s-catn (dst n--dst) <# #s #> s-cat ; 17 | s-scpy (src dst--dst)swap s-cpy ; 18 | s-scat (src dst--dst)swap s-cat ; 19 | s-scatc(ch dst--dst) swap s-catc ; 20 | s-scatn(n dst--dst) swap s-catn ; 21 | s-rtrim(str--str) >r r@ s-end 1- >t 22 |  begint@ r@ < c@t 32 > oriftdrop r> exitthen 23 | 0 c!t-again; 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /block-004.fth: -------------------------------------------------------------------------------- 1 | ( Block 4: case / case! / end-cases / switch ) 2 | 3 | ( NOTE: you can't use z" or ." when using 'case!' ) 4 | ( Any case for #0 has to be the 1st case in the table ) 5 | 6 | find switch loaded? 7 | 8 | case ( N-- )v, find drop v, ; 9 | case!( N-- )v, here v, 1 state wc! ; 10 | end-cases0 v, 0 v, ; 11 | switch( N TBL-- )>t >abegin 12 |  a@ t@ @ =ift@ cell+ @ >r atdrop exitthen 13 |  t@ 2 cells + t! 14 |  t@ @if0atdrop exitthen 15 |  again; 16 | 17 | ( Example usage ... ) 18 | find switch loaded?( keep the example from being loaded ) 19 | 20 | case-a." (case a)" ; 21 | case-b." (case b)" ; 22 | 23 | vhere const cases 24 | 'a' case case-a 25 | 'b' case! 123 . case-a space case-b 789 . ; 26 | end-cases 27 | 28 | 'a' cases switch cr 29 | 'b' cases switch cr 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /block-005.fth: -------------------------------------------------------------------------------- 1 | ( Block 5 - Screen ) 2 | 3 | find cls loaded? 4 | 5 | cur-on (--) ." %e[?25h" ; 6 | cur-off (--) ." %e[?25l" ; 7 | cur-block(--) ." %e[2 q" ; 8 | cur-bar (--) ." %e[5 q" ; 9 | ->cr (r c--) ." %e[%d;%dH" ; 10 | ->rc (c r--) swap ->cr ; 11 | cls (--) ." %e[2J" 1 dup ->rc ; 12 | clr-eol (--) ." %e[0K" ; 13 | color (bg fg--)." %e[%d;%dm" ; 14 | fg (fg--) ." %e[38;5;%dm" ; 15 | 16 | white 255 fg ; red 203 fg ; 17 | green  40 fg ; yellow226 fg ; 18 | blue  63 fg ; purple201 fg ; 19 | cyan 117 fg ; grey 250 fg ; 20 | 21 | colors(f t--)over - 1+ for 22 | dup i + dup fg ." (color-%d)" i 10 modif0crthen 23 | next drop white ; 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /block-006.fth: -------------------------------------------------------------------------------- 1 | ( Block 6 - Some utility words ) 2 | 3 | find dump loaded? 4 | 5 | .nw(num width--)>r <# r> 1- for # next #s #> ztype ; 6 | .22 .nw ; .33 .nw ; .44 .nw ; .88 .nw ; 7 | .NWB(num width base--)base@ >t base! .nw t> base! ; 8 | .hex(num--)#2 #16 .NWB ; .hex4 (num--)#4 #16 .NWB ; .hex8(num--)#8 #16 .NWB ; 9 | .bin(num--)#8 #2 .NWB ; .bin16(num--)#16 #2 .NWB ; 10 | spaces(n--)for space next ; 11 | a-emit(ch--)dup 32 < over 126 > orifdrop '.'thenemit ; 12 | a-dump(addr--)$10 for dup c@ a-emit 1+ next drop ; 13 | dump(addr num--)swap >a 0 >t 14 |  for t@+if0a@ cr .hex8 ':' emit spacethenc@a+ .hex space 15 |  t@ $10 =if3 spaces 0 t! a@ $10 - a-dumpthen 16 |  next atdrop ; 17 | lshift(n count--n')for 2* next ; 18 | rshift(n count--n')for 2/ next ; 19 | align(a--a')4 over 3 and - 3 and + ; 20 | */(n a b--n')>t * t> / ; Used for scaling a number 21 | 22 | The 13/17/5 XOR shift pseudo-random number generator 23 | val seed (val) (t0) 24 | seed!(n--)(t0) ! ; timer seed! 25 | rand(--n)seed dup $1000 * xor dup $10000 / xor dup $20 * xor dup seed! ; 26 | rand-mod(max--n)rand abs swap mod ; 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /block-007.fth: -------------------------------------------------------------------------------- 1 | ( Block 7 - Registers ) 2 | 3 | find s9 loaded? 4 | 5 | val r0 (val) t0 s0t0 ! ; 6 | val r1 (val) t0 s1t0 ! ; 7 | val r2 (val) t0 s2t0 ! ; 8 | val r3 (val) t0 s3t0 ! ; 9 | val r4 (val) t0 s4t0 ! ; 10 | val r5 (val) t0 s5t0 ! ; 11 | val r6 (val) t0 s6t0 ! ; 12 | val r7 (val) t0 s7t0 ! ; 13 | val r8 (val) t0 s8t0 ! ; 14 | val r9 (val) t0 s9t0 ! ; 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /block-008.fth: -------------------------------------------------------------------------------- 1 | Encode and decode TEXT in a bitmap (*.BMP) file 2 | 3 | Usage: to encode: z" bmp-file" ->bmp z" txt-file" ->txt encode 4 |  to decode: z" bmp-file" ->bmp z" txt-file" ->file decode ->stdout 5 | 6 | 64 var txt-fn cell var txt-sz max-txt 40 1024 * ; max-txt var txt 7 | 64 var bmp-fn cell var bmp-sz max-bmp1024 1024 * ; max-bmp var bmp 8 | 9 | NOTES: t is txt addr, a is the txt byte, b is the next bitmap byte 10 | ->txt(fn--)txt-fn s-scpy drop ; 11 | ->bmp(fn--)bmp-fn s-scpy drop ; 12 | read-file(addr max fn--sz)fopen-rb ?dupif02drop 0 exitthen 13 | >a a@ fread a> fclose ; 14 | write-file(addr sz fn--)fopen-wb ?dupif02drop exitthen 15 | >a a@ fwrite drop a> fclose ; 16 | skip-hdr(bmp--addr)dup $0a + d@ + ; 17 | read-txt(--)txt max-txt txt-fn read-file txt-sz ! ; 18 | read-bmp(--)bmp max-bmp bmp-fn read-file bmp-sz ! ; 19 | write-txt(--)txt txt-sz @ txt-fn write-file; 20 | write-bmp(--)bmp bmp-sz @ bmp-fn write-file ; 21 | encode-1(--)a@ 2 /mod a! c@b $FE and + c!b+ ; 22 | encode-8(b--)a! 8 for encode-1 next ; 23 | do-encode(--)bmp skip-hdr b! txt t! txt-sz @ for c@t+ encode-8 next 0 encode-8 ; 24 | decode-1(--)8 for c@b+ 1 and >t next 0 a! 8 for a@ 2* t> + a! next ; 25 | decode-all(--)decode-1 a@ifa@ emit decode-all exitthen; 26 | do-decode(--)bmp skip-hdr b! decode-all ; 27 | .files(--)txt-sz @ txt-fn bmp-sz @ bmp-fn ." bmp: %s (%d)%ntxt: %s (%d)%n" ; 28 | init(--)txt max-txt 0 fill bmp max-bmp 0 fill ; 29 | encode(--)init read-txt read-bmp .files do-encode write-bmp ; 30 | decode(--)init read-bmp do-decode; 31 | 32 | 33 | -------------------------------------------------------------------------------- /block-010.fth: -------------------------------------------------------------------------------- 1 | ( Block 10 - Variables x/y ) 2 | 3 | find x! loaded? 4 | 5 | val x@ (val) t0 x! t0 ! ; 6 | x+ x@ 1+ x! ; x- x@ 1- x! ; 7 | x@+x@ dup 1+ x! ; x@-x@ dup 1- x! ; 8 | !x x@ c! ; @x x@ c@ ; 9 | !x+x+ c! ; !x-x@- c! ; 10 | x>tx@ >t ; t>xt> x! ; 11 | 12 | val y@ (val) t0 y! t0 ! ; 13 | y+ y@ 1+ y! ; y- y@ 1- y! ; 14 | y@+y@ dup 1+ y! ; y@-y@ dup 1- y! ; 15 | !y y@ c! ; @y y@ c@ ; 16 | !y+y@+ c! ; !y-y@- c! ; 17 | y>ty@ >t ; t>yt> y! ; 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /block-020.fth: -------------------------------------------------------------------------------- 1 | (Blocks 20:26 - A simple block editor) 2 | 3 |  3 load(strings) 4 |  4 load(case/switch) 5 |  5 load(screen) 6 | 10 load(vars) 7 | 8 | 32 const rows 100 const cols rows cols * const block-sz 9 | block-sz var block block-sz var work 10 | rows 1- const max-row cols 1- const max-col 11 | 12 | blk 40 wc@ ;  >blk 40 wc! ; 13 | row 43 wc@ ;  >row 43 wc! ;  row++row 1+ >row ; 14 | col 44 wc@ ;  >col 44 wc! ;  col++col 1+ >col ; 15 | ed-mode 45 wc@ ;  >ed-mode 45 wc! ; 16 | show? 46 wc@ ;  show! 1 46 wc! ;  shown 0 46 wc! ; 17 | dirty? 47 wc@ ;  dirtyshow! 1 47 wc! ;  clean 0 47 wc! ; 18 | >row/col(r c--)>col >row ; 19 | >pos(r c--a)swap cols * + block + ; 20 | rc>pos(--a)row col >pos ; 21 | nt-line(r--) 0 swap max-col >pos c! ; 22 | nt-linesrows for i nt-line next ; 23 | ->cur col 2+ row 2+ ->cr cur-on ; 24 | ->foot 1 rows 3 + ->cr ; 25 | ->cmd ->foot cr clr-eol ; 26 | norm-row(x--) row + 0 max max-row min >row ; 27 | norm-col(x--) col + 0 max max-col 1- min >col ; 28 | mv (r c--)norm-col norm-row row nt-line ; 29 | mv-lt0 0 1- mv ; mv-rt0 1 mv ; mv-up0 1- 0 mv ; mv-dn1 0 mv ; 30 | mv-home0 >col ; mv-home! 0 0 >row/col ; 31 | 32 | 21 26 thru 33 | -------------------------------------------------------------------------------- /block-021.fth: -------------------------------------------------------------------------------- 1 | block-fn( --fn )p2 z" block-" s-cpy blk <# # # #s #> s-cat z" .fth" s-cat ; 2 | t1( ch-- )dup 10 =ifdrop row++ 0 >col rc>pos t! exitthendup 4 >if32 maxthenc!t+ ; 3 | t2atdrop nt-lines ; 4 | work->blockwork >a block >t 0 dup >row/col 5 |  begin c@a+ ?dupif0t2 exitthent1 row rows < while t2 ; 6 | clear-block( addr-- )block-sz 32 fill ; 7 | rl( reload block )work clear-block block clear-block 8 |  block-fn fopen-rb ?dupif 9 |  >t work block-sz t@ fread drop t> fclose 10 |  thenwork->block clean show! 0 dup >row/col ; 11 | ed!( blk-- )>blk rl ; 12 | normal-mode 0 ; insert-mode 1 ; replace-mode 2 ;  quit-mode99 ; 13 | normal-mode? normal-mode ed-mode = ; normal-mode! normal-mode >ed-mode ; 14 | insert-mode? insert-mode ed-mode = ; insert-mode! insert-mode >ed-mode ; 15 | replace-mode?replace-mode ed-mode = ; replace-mode!replace-mode >ed-mode ; 16 | quit? quit-mode ed-mode = ; q! quit-mode >ed-mode ; 17 | .modespace normal-mode?if." -normal-" exitthen 18 |  red insert-mode?if." -insert-"then 19 |  replace-mode?if." -replace-"then 20 |  white ; 21 | insert-toggle 22 |  normal-mode?ifinsert-mode! exitthen 23 |  insert-mode?ifreplace-mode! exitthen 24 |  normal-mode! ; 25 | ks( -- )yellow ." %n(press a key ...)" white key drop cls show! ; 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /block-022.fth: -------------------------------------------------------------------------------- 1 | printable?( ch--ch 1 | 0 )dup 32 126 btwiif1 else drop 0then; 2 | put-char( ch-- )rc>pos c! dirty mv-rt show! ; 3 | replace-char( ch-- )printable? if put-char then ; 4 | replace-one ( -- ) cur-off red '?' emit key white replace-char ; 5 | insert-char ( ch-- )printable?if0exitthen 6 |  >a rc>pos >r row max-col >pos >t 7 |  begint@ 1- c@ c!t- t@ r@ >while 8 |  a> r> c! tdrop mv-rt dirty ; 9 | delete-char( -- )rc>pos >t row max-col >pos >r 10 |  begint@ 1+ c@ c!t+ t@ r@ <while 11 |  32 r> 1- c! dirty tdrop ; 12 | delete-prevmv-lt delete-char ; 13 | eol-offset( row--offset )>t max-col >a 14 |  begint@ a@ >pos c@ 32 >ifa@ 1+ atdrop exitthena@-while0 atdrop ; 15 | mv-end( -- )row eol-offset >col ; 16 | clear-line( r-- )0 >pos max-col for 32 over c! 1+ next 0 swap c! dirty ; 17 | clear-eol ( r c-- )max-col over - >t >pos t> for 32 over c! 1+ next drop dirty ; 18 | insert-line( -- ) row max-row <if 19 |  row 0 >pos >t t@ t@ cols + max-row 0 >pos t> - cmove> 20 |  then row clear-line dirty ; 21 | delete-line( -- ) row max-row <if 22 |  row 0 >pos >t t@ cols + >r rows 0 >pos r@ - >a r> t> a> cmove 23 |  then max-row clear-line dirty ; 24 | insert-row( r-- )row swap >row insert-line >row ; 25 | delete-row( r-- )row swap >row delete-line >row ; 26 | join-lines( -- )row max-row >=ifexitthen 27 |  p1 row 0 >pos s-cpy s-rtrim 32 s-catc 28 |  row 1+ 0 >pos s-cat cols 1- + 0 swap c! 29 |  row 0 >pos p1 s-cpy drop 30 |  row 1+ delete-row ; 31 | open-line( flg-- )ifmv-dntheninsert-line insert-mode! mv-home ; 32 | 33 | -------------------------------------------------------------------------------- /block-023.fth: -------------------------------------------------------------------------------- 1 | #256 #71 or const key-home ( 27 91 72 ) 2 | #256 #72 or const key-up ( 27 91 65 ) 3 | #256 #73 or const key-pgup ( 27 91 53 126 ) 4 | #256 #75 or const key-left ( 27 91 68 ) 5 | #256 #77 or const key-right( 27 91 67 ) 6 | #256 #79 or const key-end ( 27 91 70 ) 7 | #256 #80 or const key-down ( 27 91 66 ) 8 | #256 #81 or const key-pgdn ( 27 91 54 126 ) 9 | #256 #82 or const key-ins ( 27 91 50 126 ) 10 | #256 #83 or const key-del ( 27 91 51 126 ) 11 | #256 #119 or const key-chome( 27 91 49 59 53 72 ) 12 | #256 #117 or const key-cend ( 27 91 49 59 53 70 ) 13 | 14 | win-key( --k )key #256 or ; 15 | vk2( --k )key 126 <>if27 exitthen 16 |  a@ 50 =ifkey-ins exitthen 17 |  a@ 51 =ifkey-del exitthen 18 |  a@ 53 =ifkey-pgup exitthen 19 |  a@ 54 =ifkey-pgdn exitthen 27 ; 20 | vk1( --k )key a! 21 |  a@ 65 =ifkey-up exitthen 22 |  a@ 66 =ifkey-down exitthen 23 |  a@ 67 =ifkey-right exitthen 24 |  a@ 68 =ifkey-left exitthen 25 |  a@ 70 =ifkey-end exitthen 26 |  a@ 72 =ifkey-home exitthen 27 |  a@ 50 55 btwiifvk2 exitthen 27 ; 28 | vt-key( --k )key 91 =ifvk1 exitthen27 ; 29 | vkey( --k )key 30 |  dup 224 =ifdrop win-key exitthen 31 |  dup 27 =ifdrop vt-key exitthen; 32 | 33 | -------------------------------------------------------------------------------- /block-024.fth: -------------------------------------------------------------------------------- 1 | cols var cmd-buf cols var yank-buf 2 | ->file(fh--)(output-fp) ! ; ->stdout0 ->file ; 3 | write-row p1 i 0 >pos s-cpy s-rtrim ztype 10 emit ; 4 | write-blockrows for write-row next ; 5 | w(write the block)dirty?ifblock-fn fopen-wb ?dup 6 | if>t t@ ->file write-block t> fclose clean ->stdoutthen then; 7 | bs8 emit ; 8 | del-ch( -- )y@ x@ <ifx- 0 !x bs space bsthen; 9 | app-ch( ch-- )!x+ 0 !x emit ; 10 | clr-bufy@ x! 0 !x ; 11 | t50 >a x>t y>t ; t4t>y t>x adrop ; 12 | accept( str-- )t5 y! clr-buf 13 | beginkey a! 14 |  a@ 13 = ift4 exitthen 15 |  a@ 27 = a@ 3 = or ifclr-buf t4 exitthen 16 |  a@ 8 = a@ 127 = or ifdel-chthen 17 |  a@ printable?ifa@ app-chthen 18 | again; 19 | q(quit)dirty?if." (use q! to quit without saving)" exitthenq! ; 20 | wq(write and quit)w q! ; 21 | ed-exec(A--)->cmd 3 state wc! outer ; 22 | do-cmd ->cmd ':' emit cur-on cmd-buf accept cmd-buf ed-exec ; 23 | yank-line yank-buf row 0 >pos s-cpy drop ; 24 | put-line insert-line row 0 >pos yank-buf s-cpy drop dirty ; 25 | next-blk w blk 1- 0 max ed! ; prev-blk w blk 1+ ed! ; 26 | mv-tab-l 0 -8 mv ; mv-tab-r 0 8 mv ; 27 | mv-cr 1 -99 mv ; mv-end! max-row 0 >row/col ; 28 | yank/del yank-line delete-line ; 29 | exec-line row 0 >pos ed-exec ; 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /block-025.fth: -------------------------------------------------------------------------------- 1 | vhere const ctrl-cases 2 |  key-up case mv-up key-down case mv-dn 127 case mv-lt 3 |  key-left case mv-lt key-right case mv-rt 8 case mv-lt 4 |  key-end case mv-end key-cend case mv-end! 9 case mv-tab-r 5 |  key-home case mv-home key-chome case mv-home! 17 case mv-tab-l 6 |  key-pgup case next-blk key-pgdn case prev-blk 13 case mv-cr 7 |  key-ins case insert-toggle key-del case delete-char 24 case delete-prev 8 |  5 case exec-line 19 case w  27 case normal-mode! 9 |  1 case!1 put-char ; 2 case!2 put-char ; 3 case!3 put-char ; 4 case!4 put-char ; 10 | end-cases 11 | 12 | vhere const ed-cases 13 |  'k' case mv-up 'h' case mv-lt 'j' case mv-dn 'l' case mv-rt 14 |  32 case mv-rt '_' case mv-home 'q' case mv-tab-r 'Q' case mv-tab-l 15 |  'R' case replace-mode! 'r' case replace-one '$' case mv-end 16 |  'i' case insert-mode! 'b' case!32 insert-char mv-lt ; 17 |  ':' case do-cmd '!' case!cmd-buf ed-exec ; 'D' case yank/del 18 |  'x' case delete-char 'X' case delete-prev 19 |  'J' case join-lines 'Y' case yank-line 'E' case exec-line 20 |  'p' case put-line 'P' case!mv-dn put-line ; 'A' case!mv-end insert-mode! ; 21 |  '-' case next-blk '+' case prev-blk 'C' case!row col clear-eol ; 22 |  'o' case!1 open-line ; 'O' case!0 open-line ; '#' case!cur-block cls show! ; 23 |  '1' case!1 put-char ; '2' case!2 put-char ; 24 |  '3' case!3 put-char ; '4' case!4 put-char ; 25 | end-cases 26 | 27 | :ed-key( ch-- )dup 32 126 btwiif0ctrl-cases switch exitthen 28 |  insert-mode? ifinsert-char exitthen 29 |  replace-mode?ifreplace-char exitthen 30 |  ed-cases switch ; 31 | 32 | 33 | -------------------------------------------------------------------------------- /block-026.fth: -------------------------------------------------------------------------------- 1 | ( Editor main loop ) 2 | 3 | ( ANSI colors ... COMPILE DEFINE INTERPRET COMMENT ) 4 |  vhere const ed-colors 40 vc, 203 vc, 226 vc, 255 vc, 5 | 6 | ed-col@(state--color)-if1- ed-colors + c@then; 7 | ed-col!(color state--)1- ed-colors + c! ; 8 | t1(--)green cols 1+ for '-' emit next cr ; 9 | t2(--)green '|' emit ; 10 | footer(--)white ->foot blk ." -Block %d- " 11 |  bl dirty?ifdrop '*'thenemit 12 |  col 1+ row 1+ ." (%d,%d) " .mode rc>pos c@ ." (%d)" 13 |  cmd-buf ." (cmd: %S)" clr-eol ; 14 | ed-emit(ch--)dup 5 <ifdup ed-col@ fgthen32 max emit ; 15 | ed-type(a--)white >a cols 1- for c@a+ ed-emit next adrop ; 16 | show (--)cur-off 1 1 ->rc t1 rows for t2 i 0 >pos ed-type t2 cr next t1 shown ; 17 | ?show(--)show?ifshowthenfooter ; 18 | ed-init(--)0 dup >row/col normal-mode! clean cur-block ; 19 | ed-loop(--)begin?show ->cur vkey cur-off ed-key quit?until->cmd cur-on ; 20 | ed(--)ed-init rl cls ed-loop ; 21 | edit(blk--)>blk ed ; 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /block-049.fth: -------------------------------------------------------------------------------- 1 | ( Block 49 ) 2 | 3 | 50 load 4 | 5 | cell var cp 6 | 7 | rlforget 49 load ; 8 | 9 | ?spc@32 >ifspacethen; 10 | ((1 33 wc! a@ 1+ c@ '(' =ifa+then'(' emit a@ ?sp ; 11 | ))0 33 wc! ." ) "; 12 | cm?33 wc@ ; 13 | ?cmcm?if))then; 14 | .\'\' emit ; 15 | cc(t)space t@ 10 =if?cm .\ cr exitthenspace 16 | t@ 2 =if?cm ." : "then 17 | t@ 1 =if?cmthen 18 | t@ 3 =if?cmthen 19 | t@ 4 =if((then 20 | ; 21 | .qt? (c--)dup '\' =if.\then; 22 | .bsl?(c--)dup '"' =if.\then; 23 | .ch (c-- ).qt? .bsl? emit ; 24 | wk (c--)>t t@ 31 >ift> .ch exitthencc tdrop ; 25 | lp (--)c@a-ifwk a+ lp exitthendrop ; 26 | ini (blk--)blk-buf a! ; 27 | doit (blk--)ini lp ; 28 | go(--)num-blocks for i doit next ; 29 | 30 | cr cr 49 ini lp 31 | 32 | 33 | -------------------------------------------------------------------------------- /block-050.fth: -------------------------------------------------------------------------------- 1 | ( Block 50 - Blocks ) 2 | 3 | find blocks loaded? 4 | 5 | 3 load(strings) 6 | 7 | 341 blocks of 3k is close to 1 meg 8 | block-sz 3072 ;inline 9 | num-blocks 341 ;inline 10 | 11 | block-sz num-blocks * var blocks 12 | num-blocks var fl(clean/dirty flags) 13 | 14 | blk-norm(n--blk)0 maxnum-blocks 1- lit,min ; 15 | blk-flg! (blk flg--)swap blk-norm fl + c! ; 16 | blk-dirty!(blk--)1 blk-flg! ; 17 | blk-clean!(blk--)0 blk-flg! ; 18 | blk-dirty?(blk--flg)blk-norm fl + c@ ; 19 | blk-buf(blk--buf)blk-norm block-sz * blocks + ; 20 | 21 | 32 var fn(file-name) 22 | blk-fn(blk--fn)>r fn z" block-" s-cpy r> 23 | <# # # #s #> s-cat z" .fth" s-cat ; 24 | 25 | blk-clear(blk--)dup blk-clean! blk-buf block-sz 0 fill ; 26 | blk-clear-all(--)num-blocks for i blk-clear next ; 27 | 28 | 51 load-next 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /block-051.fth: -------------------------------------------------------------------------------- 1 | ( Block 51 - More Blocks ) 2 | 3 | find blk-init loaded? 4 | 5 | blk-read(blk--)blk-norm >t t@ blk-fn fopen-rb 6 | ?dupif0t> blk-clear exitthen( fn ." %n-r/%S-" ) 7 | >a t> blk-buf block-sz a@ fread drop a> fclose ; 8 | blk-read-all(--)num-blocks for i blk-read next ; 9 | 10 | blk-write(blk--)blk-norm >a a@ blk-dirty?if0adrop exitthen 11 | a@ blk-buf >t a@ blk-fn fopen-wb ?dup fn ." %n-w/%S-" 12 | if>r t@ block-sz r@ fwrite . r> fclosethenatdrop ; 13 | blk-flush(--)num-blocks for i blk-write next ; 14 | 15 | blk-rm(blk--)dup blk-fn fdelete blk-clear ; 16 | blk-cp-buf(buf blk--)dup blk-dirty! blk-buf block-sz cmove ; 17 | blk-cp(from to--)>t blk-buf t> blk-cp-buf blk-flush ; 18 | blk-mv(from to--)over swap blk-cp blk-rm blk-flush ; 19 | blk-ins(n to--)1+ >t >a 20 |  begint@- t@ swap blk-mv t@ a@ >whiletdrop a> blk-rm blk-flush ; 21 | 22 | blk-load(blk--)blk-buf outer ; 23 | blk-init(--)blk-clear-all blk-read-all ; 24 | 25 | blk-init 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /block-200.fth: -------------------------------------------------------------------------------- 1 | ( benchmarks / tests ) 2 | 3 | find bm1 loaded? 4 | test-if'n' swapifdrop 'y'thenemit '.' emit ; 5 | if-elseifz" yes" else z" no"thenztype '.' emit ; 6 | cr 0 test-if 1 test-if see test-if cr 7 | cr 0 if-else 1 if-else see if-else cr 8 | ..dup . ; 9 | [[cr 5 for i . ." -- " 4 for i . next ." -- " next]] 10 | [[cr 10 begin .. 1- dup while drop]] 11 | [[cr 0 begin .. 1+ dup 10 = until drop]] 12 | elapsedtimer swap - ." (%d usec)" ; mil1000 dup * * ; 13 | t01 drop ; t11 drop t0 ; 14 | fib1- dup 2 < if drop 1 exit then dup fib swap 1- fib + ; 15 | bm1cr dup ." bm1: empty loop: (%d)" timer swap for next elapsed ; 16 | bm2cr dup ." bm2: decrement loop: (%d)" timer swap begin 1- -while drop elapsed ; 17 | bm3cr dup ." bm3: call loop: (%d)" timer swap for t0 next elapsed ; 18 | bm4cr dup ." bm4: 2 call loop: (%d)" timer swap for t1 next elapsed ; 19 | bm5cr dup ." bm5: fib (%d) ... " timer swap fib . elapsed ; 20 | bmk1000 mil bm1 ; 21 | go 250 mil dup dup bmk bm2 bm3 bm4 38 bm5 cr ; 22 | go 23 | val xxx (val) (xxx) 1234 (xxx) ! 24 | see xxx cr cr 25 | xxx ." -xxx created, (%d)-%n" 26 | xxx const yyy 27 | yyy ." -yyy created, (%d)-" cr 28 | see yyy cr 29 | [[xxx yyy = ." val/const: "if." PASS" exitthen." FAIL!"]] 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /block-201.fth: -------------------------------------------------------------------------------- 1 | ( Mandelbrot set ) 2 | 3 | 7 load( registers ) 4 | 5 | val k (val) t0 k!t0 ! ; 6 | val x (val) t0 x!t0 ! ; 7 | val y (val) t0 y!t0 ! ; 8 | val z (val) t0 z!t0 ! ; 9 | 10 | sqr/ dup * z / ; inline 11 | calc r1 sqr/ s3 r2 sqr/ s4 12 | r3 r4 + k >if1 exitthen 13 | r1 r2 * 100 / y + s2 14 | r3 r4 - x + s1 15 | r5 1+ s5 r5 z > ; 16 | 17 | l-loop 0 s1 0 s2 0 s5begincalcuntil; 18 | m-loop l-loop r5 40 + dup '~' >ifdrop blthenemit ; 19 | x-loop -490 x! 95 for m-loop x 8 + x! next cr ; 20 | y-loop -340 y! 35 for x-loop y 20 + y! next ; 21 | mbrot cls ." The Mandelbrot set%n" 1000000 k! 200 z! y-loop ; 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /block-202.fth: -------------------------------------------------------------------------------- 1 | ( Conway's Game Of Life ) 2 | 3 | rows(--n)75 ; cols(--n)150 ; 4 | grid-sz rows cols * const grid-sz 5 | grid grid-sz var grid 6 | work grid-sz var work 7 | start grid cols + 1+ const start 8 | end work cols - 2 - const end 9 | states 10 var states states z" ?* " s-cpy drop 10 | nb(addr--addr+1)dup c@ 1+ over c! 1+ ; 11 | neighbors(addr--)cols - 1- nb nb nb cols + 3 - nb 1+ nb cols + 3 - nb nb nb drop ; 12 | ?alive(--)c@a '*' =ifa@ grid - work + neighborsthen; 13 | live?(--c)c@b states + c@ dup '?' =ifdrop c@athen; 14 | prep(a--a1 c)100 + >t t@ c@t 0 c!t tdrop ; 15 | .row(r--)cols * grid + 10 + dup prep >t >t ztype t> t> swap c! ; 16 | disp(--)1 1 ->cr 35 for i 10 + .row cr next ; 17 | grid->work(--)start a!begin ?alive a@+ end < while; 18 | work->grid(--)work b! grid a! grid-sz for live? c!a+ 0 c!b+ next ; 19 | one-gen(--)grid->work work->grid disp ; 20 | gens(gens--)for one-gen ?keyifkey drop unloop exitthennext ; 21 | init(--)grid a! grid-sz for 100 rand-mod 70 > 10 * 32 + c!a+ next ;32+10=42 ('*') 22 | life(gens--)cur-off dupif0drop 500thencls init gens cur-on ; 23 | lifes(n--)for 1000 life.s key dropnext ; 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /c4.c: -------------------------------------------------------------------------------- 1 | #include "c4.h" 2 | 3 | #define NCASE goto next; case 4 | #define BCASE break; case 5 | #define dsp code[DSPA] 6 | #define rsp code[RSPA] 7 | #define lsp code[LSPA] 8 | #define tsp code[TSPA] 9 | #define asp code[ASPA] 10 | #define bsp code[BSPA] 11 | #define here code[HA] 12 | #define base code[BA] 13 | #define state code[SA] 14 | #define TOS dstk[dsp] 15 | #define NOS dstk[dsp-1] 16 | #define L0 lstk[lsp] 17 | #define L1 lstk[lsp-1] 18 | #define L2 lstk[lsp-2] 19 | 20 | byte memory[MEM_SZ+1]; 21 | wc_t *code = (wc_t*)&memory[0]; 22 | cell dstk[STK_SZ+1], rstk[STK_SZ+1], lstk[LSTK_SZ+1]; 23 | cell tstk[TSTK_SZ+1], astk[TSTK_SZ+1], bstk[TSTK_SZ+1], vhere; 24 | char wd[32], *toIn; 25 | DE_T tmpWords[10], *last; 26 | 27 | #define PRIMS_BASE \ 28 | X(EXIT, "exit", 0, if (0", 0, t=pop(); TOS = (TOS > t); ) \ 54 | X(EQ0, "0=", 0, TOS = (TOS == 0) ? 1 : 0; ) \ 55 | X(AND, "and", 0, t=pop(); TOS &= t; ) \ 56 | X(OR, "or", 0, t=pop(); TOS |= t; ) \ 57 | X(XOR, "xor", 0, t=pop(); TOS ^= t; ) \ 58 | X(COM, "com", 0, TOS = ~TOS; ) \ 59 | X(FOR, "for", 0, lsp+=3; L2=pc; L0=0; L1=pop(); ) \ 60 | X(INDEX, "i", 0, push(L0); ) \ 61 | X(NEXT, "next", 0, if (++L0r", 0, rpush(pop()); ) \ 64 | X(RSTO, "r!", 0, rstk[rsp] = pop(); ) \ 65 | X(RAT, "r@", 0, push(rstk[rsp]); ) \ 66 | X(RATI, "r@+", 0, push(rstk[rsp]++); ) \ 67 | X(RATD, "r@-", 0, push(rstk[rsp]--); ) \ 68 | X(RFROM, "r>", 0, push(rpop()); ) \ 69 | X(RDROP, "rdrop", 0, rpop(); ) \ 70 | X(TTO, ">t", 0, t=pop(); if (tsp < TSTK_SZ) { tstk[++tsp]=t; }; ) \ 71 | X(TSTO, "t!", 0, tstk[tsp] = pop(); ) \ 72 | X(TAT, "t@", 0, push(tstk[tsp]); ) \ 73 | X(TATI, "t@+", 0, push(tstk[tsp]++); ) \ 74 | X(TATD, "t@-", 0, push(tstk[tsp]--); ) \ 75 | X(TFROM, "t>", 0, push((0 < tsp) ? tstk[tsp--] : 0); ) \ 76 | X(TDROP, "tdrop", 0, if (0 < tsp) { tsp--; } ) \ 77 | X(TOA, ">a", 0, t=pop(); if (asp < TSTK_SZ) { astk[++asp] = t; } ) \ 78 | X(ASET, "a!", 0, astk[asp]=pop(); ) \ 79 | X(AGET, "a@", 0, push(astk[asp]); ) \ 80 | X(AGETI, "a@+", 0, push(astk[asp]++); ) \ 81 | X(AGETD, "a@-", 0, push(astk[asp]--); ) \ 82 | X(AFROM, "a>", 0, push((0 < asp) ? astk[asp--] : 0); ) \ 83 | X(ADROP, "adrop", 0, if (0 < asp) { asp--; } ) \ 84 | X(TOB, ">b", 0, t=pop(); if (bsp < TSTK_SZ) { bstk[++bsp] = t; } ) \ 85 | X(BSET, "b!", 0, bstk[bsp]=pop(); ) \ 86 | X(BGET, "b@", 0, push(bstk[bsp]); ) \ 87 | X(BGETI, "b@+", 0, push(bstk[bsp]++); ) \ 88 | X(BGETD, "b@-", 0, push(bstk[bsp]--); ) \ 89 | X(BFROM, "b>", 0, push((0 < bsp) ? bstk[bsp--] : 0); ) \ 90 | X(BDROP, "bdrop", 0, if (0 < bsp) { bsp--; } ) \ 91 | X(EMIT, "emit", 0, emit((char)pop()); ) \ 92 | X(KEY, "key", 0, push(key()); ) \ 93 | X(QKEY, "?key", 0, push(qKey()); ) \ 94 | X(SEMI, ";", 1, comma(EXIT); state=INTERP; ) \ 95 | X(LITC, "lit,", 0, t=pop(); compileNum(t); ) \ 96 | X(NEXTWD, "next-wd", 0, push((cell)wd); push(nextWord()); ) \ 97 | X(IMMED, "immediate", 0, { last->fl=_IMMED; } ) \ 98 | X(INLINE, "inline", 0, makeInline(); ) \ 99 | X(OUTER, "outer", 0, outer((char*)pop()); ) \ 100 | X(ADDWORD, "addword", 0, addWord(0); ) \ 101 | X(CLK, "timer", 0, push(timer()); ) \ 102 | X(SEE, "see", 0, doSee(); ) \ 103 | X(ZTYPE, "ztype", 0, zType((char*)pop()); ) \ 104 | X(FTYPE, "ftype", 0, fType((char*)pop()); ) \ 105 | X(SCPY, "s-cpy", 0, t=pop(); strCpy((char*)TOS, (char*)t); ) \ 106 | X(SEQ, "s-eq", 0, t=pop(); TOS = strEq((char*)TOS, (char*)t); ) \ 107 | X(SEQI, "s-eqi", 0, t=pop(); TOS = strEqI((char*)TOS, (char*)t); ) \ 108 | X(SLEN, "s-len", 0, TOS = strLen((char*)TOS); ) \ 109 | X(SFIND, "s-find", 0, t=pop(); n=pop(); TOS = strFind((char*)TOS, (char*)n, t); ) \ 110 | X(ZQUOTE, "z\"", 1, quote(); ) \ 111 | X(DOTQT, ".\"", 1, quote(); (state==COMPILE) ? comma(FTYPE) : fType((char*)pop()); ) \ 112 | X(FIND, "find", 0, { DE_T *dp=findWord(0); push(dp?dp->xt:0); push((cell)dp); } ) 113 | 114 | #define PRIMS_FILE \ 115 | X(LOADED, "loaded?", 0, t=pop(); pop(); if (t) { fileClose(inputFp); inputFp=filePop(); } ) \ 116 | X(FLOPEN, "fopen", 0, t=pop(); n=pop(); push(fileOpen((char*)n, (char*)t)); ) \ 117 | X(FLCLOSE, "fclose", 0, t=pop(); fileClose(t); ) \ 118 | X(FLDEL, "fdelete", 0, t=pop(); fileDelete((char*)t); ) \ 119 | X(FLREAD, "fread", 0, t=pop(); n=pop(); TOS = fileRead((char*)TOS, (int)n, t); ) \ 120 | X(FLWRITE, "fwrite", 0, t=pop(); n=pop(); TOS = fileWrite((char*)TOS, (int)n, t); ) \ 121 | X(FLGETS, "fgets", 0, t=pop(); n=pop(); TOS = fileGets((char*)TOS, (int)n, t); ) \ 122 | X(INCL, "include", 0, t=nextWord(); if (t) fileLoad(wd); ) \ 123 | X(LOAD, "load", 0, t=pop(); blockLoad((int)t); ) \ 124 | X(NXTBLK, "load-next", 0, t=pop(); blockLoadNext((int)t); ) \ 125 | X(EDITBLK, "edit", 0, editBlock(pop()); ) 126 | 127 | #define PRIMS_SYSTEM \ 128 | X(SYSTEM, "system", 0, t=pop(); ttyMode(0); system((char*)t); ) \ 129 | X(BYE, "bye", 0, ttyMode(0); exit(0); ) 130 | 131 | #define X(op, name, imm, cod) op, 132 | 133 | enum _PRIM { 134 | STOP, LIT, JMP, JMPZ, NJMPZ, JMPNZ, NJMPNZ, PRIMS_BASE PRIMS_FILE PRIMS_SYSTEM 135 | }; 136 | 137 | #undef X 138 | #define X(op, name, imm, code) { name, op, imm, 0 }, 139 | 140 | PRIM_T prims[] = { PRIMS_BASE PRIMS_FILE PRIMS_SYSTEM {0, 0, 0, 0}}; 141 | 142 | void push(cell x) { if (dsp < STK_SZ) { dstk[++dsp] = x; } } 143 | cell pop() { return (0fl=_INLINE; } 158 | void ok() { if (state==0) { state=INTERP; } zType((state==INTERP) ? " ok\r\n" : "... "); } 159 | int lower(const char c) { return btwi(c, 'A', 'Z') ? c + 32 : c; } 160 | int strLen(const char *s) { int l = 0; while (s[l]) { l++; } return l; } 161 | 162 | int strEqI(const char *s, const char *d) { 163 | while (lower(*s) == lower(*d)) { if (*s == 0) { return 1; } s++; d++; } 164 | return 0; 165 | } 166 | 167 | int strEq(const char *s, const char *d) { 168 | while (*s == *d) { if (*s == 0) { return 1; } s++; d++; } 169 | return 0; 170 | } 171 | 172 | void strCpy(char *d, const char *s) { 173 | while (*s) { *(d++) = *(s++); } 174 | *(d) = 0; 175 | } 176 | 177 | int startsWith(const char *buf, const char *lookFor) { 178 | while (*lookFor) { 179 | if (*(lookFor++) != *(buf++)) { return 0; } 180 | } 181 | return 1; 182 | } 183 | 184 | int strFind(const char *buf, const char *lookFor, int start) { 185 | if (strLen(buf) <= start) { return -1; } 186 | while (buf[start]) { 187 | if (startsWith(&buf[start], lookFor)) { return start; } 188 | ++start; 189 | } 190 | return -1; 191 | } 192 | 193 | int nextWord() { 194 | int len = 0, ch; 195 | while (btwi(*toIn, 1, 32)) { 196 | ch = *(toIn++); 197 | if (btwi(ch,COMPILE,COMMENT)) { state = ch; } 198 | } 199 | while (btwi(*toIn, 33, 126)) { wd[len++] = *(toIn++); } 200 | wd[len] = 0; 201 | return len; 202 | } 203 | 204 | int isTempWord(const char *w) { 205 | return ((w[0]=='t') && btwi(w[1],'0','9') && (w[2]==0)) ? 1 : 0; 206 | } 207 | 208 | DE_T *addWord(const char *w) { 209 | if (!w) { nextWord(); w = wd; } 210 | if (NAME_LEN < strLen(wd)) { zTypeF("\n-len:%s-\n", wd); wd[NAME_LEN]=0; } 211 | if (isTempWord(w)) { 212 | tmpWords[w[1]-'0'].xt = here; 213 | return &tmpWords[w[1]-'0']; 214 | } 215 | int ln = strLen(w); 216 | --last; 217 | last->xt = here; 218 | last->fl = 0; 219 | last->ln = ln; 220 | strCpy(last->nm, w); 221 | // zTypeF("\n-add:%d,[%s],(%d)-\n", last, last->nm, last->xt); 222 | return last; 223 | } 224 | 225 | DE_T *findWord(const char *w) { 226 | if (!w) { nextWord(); w = wd; } 227 | if (isTempWord(w)) { return &tmpWords[w[1]-'0']; } 228 | cell len = strLen(w); 229 | DE_T *dp = last; 230 | while ((byte*)dp < &memory[MEM_SZ]) { 231 | if ((len == dp->ln) && strEqI(dp->nm, w)) { return dp; } 232 | ++dp; 233 | } 234 | return (DE_T*)0; 235 | } 236 | 237 | cell findXT(int xt) { 238 | DE_T *dp = last; 239 | while ((byte*)dp < &memory[MEM_SZ]) { 240 | if (dp->xt == xt) { return (cell)dp; } 241 | ++dp; 242 | } 243 | return 0; 244 | } 245 | 246 | void doSee() { 247 | DE_T *dp = findWord(0), *lastWord = last; 248 | if (!dp) { zTypeF("-nf:%s-", wd); return; } 249 | if (dp->xt <= BYE) { zTypeF("%s is a primitive (#%ld/$%lX).\r\n", wd, dp->xt, dp->xt); return; } 250 | cell x = (cell)dp; 251 | wc_t i = dp->xt, stop = (lastWord < dp) ? (dp-1)->xt : here; 252 | zTypeF("\r\n%04lX: %s (%04lX to %04lX)", (long)x, dp->nm, (long)dp->xt, (long)stop-1); 253 | while (i < stop) { 254 | wc_t op = code[i++]; 255 | zTypeF("\r\n%04X: %04X\t", i-1, op); 256 | if (op & NUM_BITS) { op &= NUM_MASK; zTypeF("num #%ld ($%lX)", op, op); continue; } 257 | x = code[i]; 258 | switch (op) { 259 | case STOP: zType("stop"); i++; 260 | BCASE LIT: x = fetchCell((cell)&code[i]); 261 | zTypeF("lit #%zd ($%zX)", (size_t)x, (size_t)x); 262 | i += (CELL_SZ/WC_SZ); 263 | BCASE JMP: zTypeF("jmp $%04lX", (long)x); i++; 264 | BCASE JMPZ: zTypeF("jmpz $%04lX (IF)", (long)x); i++; 265 | BCASE NJMPZ: zTypeF("njmpz $%04lX (-IF)", (long)x); i++; 266 | BCASE JMPNZ: zTypeF("jmpnz $%04lX (WHILE)", (long)x); i++; 267 | BCASE NJMPNZ: zTypeF("njmpnz $%04lX (-WHILE)", (long)x); i++; break; 268 | default: x = findXT(op); 269 | zType(x ? ((DE_T*)x)->nm : "??"); 270 | } 271 | } 272 | } 273 | 274 | void iToA(cell n, cell b) { 275 | if (n<0) { emit('-'); n = -n; } 276 | if (b<=n) { iToA(n/b, b); } 277 | n %= b; if (9xt; 377 | code[18] = STOP; 378 | inner(17); 379 | } 380 | 381 | void compileWord(DE_T *de) { 382 | if (de->fl & _IMMED) { executeWord(de); } 383 | else if (de->fl & _INLINE) { 384 | wc_t x = de->xt; 385 | do { comma(code[x++]); } while (code[x]!=EXIT); 386 | } else { comma(de->xt); } 387 | } 388 | 389 | int isStateChange(const char *wd) { 390 | static int prevState = INTERP; 391 | if (prevState == COMMENT) { prevState = INTERP; } 392 | if (strEq(wd, ")")) { return changeState(prevState); } 393 | if (state==COMMENT) { return 0; } 394 | if (strEq(wd,":")) { return changeState(DEFINE); } 395 | if (strEq(wd,"[")) { return changeState(INTERP); } 396 | if (strEq(wd,"]")) { return changeState(COMPILE); } 397 | if (strEq(wd,"(")) { 398 | if (state!=COMMENT) { prevState=state; } 399 | return changeState(COMMENT); 400 | } 401 | return 0; 402 | } 403 | 404 | void outer(const char *ln) { 405 | // zTypeF("-outer:%s-\n",ln); 406 | char *curIn = toIn; 407 | toIn = (char*)ln; 408 | while (nextWord()) { 409 | // zTypeF("-word:(%s,%d)-",wd,state); 410 | if (isStateChange(wd)) { continue; } 411 | if (state == COMMENT) { continue; } 412 | if (state == DEFINE) { addWord(wd); state = COMPILE; continue; } 413 | if (isNum(wd, base)) { 414 | if (state == COMPILE) { compileNum(pop()); } 415 | continue; 416 | } 417 | DE_T *de = findWord(wd); 418 | if (de) { 419 | if (state == COMPILE) { compileWord(de); } 420 | else { executeWord(de); } 421 | continue; 422 | } 423 | zTypeF("-%s?-", wd); 424 | if (inputFp) { zTypeF(" at\r\n\t%s", ln); } 425 | state = INTERP; 426 | while (inputFp) { fileClose(inputFp); inputFp = filePop(); } 427 | break; 428 | } 429 | toIn = curIn; 430 | } 431 | 432 | void outerF(const char *fmt, ...) { 433 | char buf[128]; 434 | va_list args; 435 | va_start(args, fmt); 436 | vsnprintf(buf, 128, fmt, args); 437 | va_end(args); 438 | outer(buf); 439 | } 440 | 441 | void zTypeF(const char *fmt, ...) { 442 | char buf[128]; 443 | va_list args; 444 | va_start(args, fmt); 445 | vsnprintf(buf, 128, fmt, args); 446 | va_end(args); 447 | zType(buf); 448 | } 449 | 450 | void defNum(const char *name, cell val) { 451 | DE_T *dp = addWord(name); 452 | compileNum(val); 453 | comma(EXIT); 454 | if (btwi(val,0,NUM_MASK)) { dp->fl = _INLINE; } 455 | } 456 | 457 | void baseSys() { 458 | for (int i = 0; prims[i].name; i++) { 459 | DE_T *w = addWord(prims[i].name); 460 | w->xt = prims[i].op; 461 | w->fl = prims[i].fl; 462 | } 463 | defNum("mem-sz", MEM_SZ); 464 | defNum("code-sz", CODE_SLOTS); 465 | defNum("de-sz", sizeof(DE_T)); 466 | defNum("dstk-sz", STK_SZ+1); 467 | defNum("tstk-sz", TSTK_SZ+1); 468 | defNum("wc-sz", WC_SZ); 469 | defNum("(dsp)", DSPA); 470 | defNum("(rsp)", RSPA); 471 | defNum("(lsp)", LSPA); 472 | defNum("(tsp)", TSPA); 473 | defNum("(asp)", ASPA); 474 | defNum("(bsp)", BSPA); 475 | defNum("(block)", BLKA); 476 | defNum("dstk", (cell)&dstk[0]); 477 | defNum("rstk", (cell)&rstk[0]); 478 | defNum("tstk", (cell)&tstk[0]); 479 | defNum("astk", (cell)&astk[0]); 480 | defNum("bstk", (cell)&bstk[0]); 481 | defNum("memory", (cell)&memory[0]); 482 | defNum("vars", vhere); 483 | defNum(">in", (cell)&toIn); 484 | defNum("wd", (cell)&wd[0]); 485 | defNum("(vhere)", (cell)&vhere); 486 | defNum("(output-fp)", (cell)&outputFp); 487 | defNum("(last)", (cell)&last); 488 | defNum("version", VERSION); 489 | defNum("(lit)", LIT); 490 | defNum("(jmp)", JMP); 491 | defNum("(jmpz)", JMPZ); 492 | defNum("(njmpz)", NJMPZ); 493 | defNum("(jmpnz)", JMPNZ); 494 | defNum("(njmpnz)", NJMPNZ); 495 | defNum("(exit)", EXIT); 496 | defNum("(here)", HA); 497 | defNum("base", BA); 498 | defNum("state", SA); 499 | defNum("cell", CELL_SZ); 500 | sys_load(); 501 | } 502 | 503 | void c4Init() { 504 | code = (wc_t*)&memory[0]; 505 | here = BYE+1; 506 | last = (DE_T*)&memory[MEM_SZ]; 507 | base = 10; 508 | state = INTERP; 509 | vhere = (cell)&memory[CODE_SLOTS*WC_SZ]; 510 | for (int i=6; i<=9; i++) { tmpWords[i].fl = _INLINE; } 511 | fileInit(); 512 | baseSys(); 513 | } 514 | -------------------------------------------------------------------------------- /c4.h: -------------------------------------------------------------------------------- 1 | #ifndef __C4_H__ 2 | 3 | #define __C4_H__ 4 | 5 | #define VERSION 20250725 6 | 7 | #ifdef _MSC_VER 8 | #define _CRT_SECURE_NO_WARNINGS 9 | #define IS_WINDOWS 1 10 | #else 11 | #define IS_LINUX 1 12 | #endif 13 | 14 | #define MEM_SZ 16*1024*1024 15 | #define CODE_SLOTS 0xE000 16 | #define STK_SZ 63 // Both data and return stacks 17 | #define LSTK_SZ 60 // 3 cells per entry 18 | #define TSTK_SZ 63 // A, B and T stacks 19 | #define FSTK_SZ 15 // File stack 20 | #define NAME_LEN 19 // DE-SZ = 2+1+1+LEN+1 21 | #define _SYS_LOAD_ 22 | #define EDITOR 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #define btwi(n,l,h) ((l<=n) && (n<=h)) 31 | #define _IMMED 1 32 | #define _INLINE 2 33 | 34 | #define WC_T uint16_t 35 | #define WC_SZ 2 36 | #define NUM_BITS 0xE000 37 | #define NUM_MASK 0x1FFF 38 | 39 | #if INTPTR_MAX > INT32_MAX 40 | #define CELL_T int64_t 41 | #define CELL_SZ 8 42 | #else 43 | #define CELL_T int32_t 44 | #define CELL_SZ 4 45 | #endif 46 | 47 | enum { COMPILE=1, DEFINE=2, INTERP=3, COMMENT=4 }; 48 | enum { DSPA=0, RSPA, LSPA, TSPA, ASPA, BSPA, HA, BA, SA, BLKA }; 49 | 50 | typedef CELL_T cell; 51 | typedef WC_T wc_t; 52 | typedef uint8_t byte; 53 | typedef struct { wc_t xt; byte fl, ln; char nm[NAME_LEN+1]; } DE_T; 54 | typedef struct { const char *name; wc_t op; byte fl; byte pad; } PRIM_T; 55 | 56 | // These are defined by c4.c 57 | extern void push(cell x); 58 | extern cell pop(); 59 | extern void storeWC(wc_t a, wc_t v); 60 | extern wc_t fetchWC(wc_t a); 61 | extern void strCpy(char *d, const char *s); 62 | extern int strFind(const char *buf, const char *lookFor, int start); 63 | extern int strEq(const char *d, const char *s); 64 | extern int strEqI(const char *d, const char *s); 65 | extern int strLen(const char *s); 66 | extern int lower(const char c); 67 | extern void zTypeF(const char *fmt, ...); 68 | extern int changeState(int x); 69 | extern void inner(wc_t start); 70 | extern void outer(const char *src); 71 | extern void outerF(const char *fmt, ...); 72 | extern void c4Init(); 73 | extern void ok(); 74 | 75 | // These are in the editor 76 | extern void FG(int fg); 77 | extern void Blue(); 78 | extern void Green(); 79 | extern void Purple(); 80 | extern void Red(); 81 | extern void White(); 82 | extern void Yellow(); 83 | 84 | // c4.c needs these to be defined 85 | extern cell inputFp, outputFp; 86 | extern void zType(const char *str); 87 | extern void emit(const char ch); 88 | extern void ttyMode(int isRaw); 89 | extern int key(); 90 | extern int qKey(); 91 | extern cell timer(); 92 | extern void fileInit(); 93 | extern void filePush(cell fh); 94 | extern cell filePop(); 95 | extern cell fileOpen(const char *name, const char *mode); 96 | extern void fileClose(cell fh); 97 | extern void fileDelete(const char *name); 98 | extern cell fileRead(char *buf, int sz, cell fh); 99 | extern cell fileWrite(char *buf, int sz, cell fh); 100 | extern int fileGets(char *buf, int sz, cell fh); 101 | extern void fileLoad(const char *name); 102 | extern char *blockFn(int blk); 103 | extern void blockLoad(int blk); 104 | extern void blockLoadNext(int blk); 105 | extern void sys_load(); 106 | extern void editBlock(cell blk); 107 | 108 | #endif // __C4_H__ 109 | -------------------------------------------------------------------------------- /c4.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31112.23 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "c4", "c4.vcxproj", "{BDD1C19E-3EEC-4DDF-AD67-406628BACCCF}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {BDD1C19E-3EEC-4DDF-AD67-406628BACCCF}.Debug|x64.ActiveCfg = Debug|x64 17 | {BDD1C19E-3EEC-4DDF-AD67-406628BACCCF}.Debug|x64.Build.0 = Debug|x64 18 | {BDD1C19E-3EEC-4DDF-AD67-406628BACCCF}.Debug|x86.ActiveCfg = Debug|Win32 19 | {BDD1C19E-3EEC-4DDF-AD67-406628BACCCF}.Debug|x86.Build.0 = Debug|Win32 20 | {BDD1C19E-3EEC-4DDF-AD67-406628BACCCF}.Release|x64.ActiveCfg = Release|x64 21 | {BDD1C19E-3EEC-4DDF-AD67-406628BACCCF}.Release|x64.Build.0 = Release|x64 22 | {BDD1C19E-3EEC-4DDF-AD67-406628BACCCF}.Release|x86.ActiveCfg = Release|Win32 23 | {BDD1C19E-3EEC-4DDF-AD67-406628BACCCF}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {C2304CB4-4ACA-4A41-9669-EACD091DD0F1} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /c4.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {bdd1c19e-3eec-4ddf-ad67-406628bacccf} 25 | stable 26 | 10.0 27 | c4 28 | 29 | 30 | 31 | Application 32 | true 33 | v143 34 | MultiByte 35 | 36 | 37 | Application 38 | false 39 | v143 40 | true 41 | MultiByte 42 | 43 | 44 | Application 45 | true 46 | v143 47 | Unicode 48 | 49 | 50 | Application 51 | false 52 | v143 53 | true 54 | Unicode 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | true 76 | 77 | 78 | false 79 | 80 | 81 | true 82 | 83 | 84 | false 85 | 86 | 87 | 88 | Level3 89 | true 90 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 91 | true 92 | Speed 93 | 94 | 95 | Console 96 | true 97 | 98 | 99 | 100 | 101 | Level3 102 | true 103 | true 104 | true 105 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 106 | true 107 | Speed 108 | AssemblyAndSourceCode 109 | Full 110 | 111 | 112 | Console 113 | true 114 | true 115 | true 116 | 117 | 118 | 119 | 120 | Level3 121 | true 122 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 123 | true 124 | 125 | 126 | Console 127 | true 128 | 129 | 130 | 131 | 132 | Level3 133 | true 134 | true 135 | true 136 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 137 | true 138 | 139 | 140 | Console 141 | true 142 | true 143 | true 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | false 178 | CppCode 179 | false 180 | 181 | 182 | 183 | 184 | 185 | -------------------------------------------------------------------------------- /editor.c: -------------------------------------------------------------------------------- 1 | // editor.cpp - A simple block editor 2 | 3 | #include "c4.h" 4 | 5 | void FG(int fg) { zTypeF("\x1B[38;5;%dm", fg); } 6 | void Blue() { FG(38); } 7 | void Green() { FG(40); } 8 | void Purple() { FG(213); } 9 | void Red() { FG(203); } 10 | void White() { FG(231); } 11 | void Yellow() { FG(226); } 12 | void BG(int c) { zTypeF("\x1B[48;5;%dm", c); } 13 | 14 | #ifndef EDITOR 15 | void editBlock(cell blk) { zType("-no edit-"); } 16 | #else 17 | 18 | #define NUM_LINES 32 19 | #define NUM_COLS 96 20 | #define BLOCK_SZ (NUM_LINES*NUM_COLS) 21 | #define MAX_LINE (NUM_LINES-1) 22 | #define MAX_COL (NUM_COLS-1) 23 | #define EDCH(r,c) edBuf[((r)*NUM_COLS)+(c)] 24 | #define DIRTY isDirty=1; isShow=1 25 | #define CLEAN(show) isDirty=0; isShow=show 26 | #define BCASE break; case 27 | #define RCASE return; case 28 | #define EOL_CHAR 13 29 | 30 | #ifndef MAX 31 | #define MIN(a,b) ((a)<(b))?(a):(b) 32 | #define MAX(a,b) ((a)>(b))?(a):(b) 33 | #endif 34 | 35 | enum { NORMAL=1, INSERT, REPLACE, QUIT }; 36 | enum { Up=7240, Dn=7248, Rt=7245, Lt=7243, Home=7239, PgUp=7241, PgDn=7249, 37 | End=7247, Ins=7250, Del=7251, CHome=7287, CEnd=7285, 38 | STab=12333, F1=0xF01, F5=0xF05, F6=0xF06, F7=0xF07 39 | }; 40 | 41 | static int line, off, edMode, isDirty, isShow, block, lastBlock; 42 | static char edBuf[BLOCK_SZ], yanked[NUM_COLS+1], findBuf[32]; 43 | 44 | static void GotoXY(int x, int y) { zTypeF("\x1B[%d;%dH", y, x); } 45 | static void CLS() { zType("\x1B[2J"); GotoXY(1, 1); } 46 | static void ClearEOL() { zType("\x1B[K"); } 47 | static void CursorBlock() { zType("\x1B[2 q"); } 48 | static void CursorOn() { zType("\x1B[?25h"); } 49 | static void CursorOff() { zType("\x1B[?25l"); } 50 | static void showCursor() { GotoXY(off+2, line+2); CursorOn(); CursorBlock(); } 51 | static void topBottom() { for (int i=0; i<=(NUM_COLS/2); i++) { zType("--"); } } 52 | static void toFooter() { GotoXY(1, NUM_LINES+3); } 53 | static void toCmd() { GotoXY(1, NUM_LINES+4); } 54 | static void normalMode() { edMode=NORMAL; } 55 | static void insertMode() { edMode=INSERT; } 56 | static void replaceMode() { edMode=REPLACE; } 57 | static void toggleInsert() { (edMode==INSERT) ? normalMode() : insertMode(); } 58 | static void setBlock(int blk) { block=MAX(MIN(blk,999),0); storeWC(BLKA, (wc_t)block); } 59 | static int winKey() { return (224 << 5) ^ key(); } 60 | static int winFKey() { return 0xF00 + key() - 58; } 61 | 62 | // VT key mapping, after , '[' 63 | #define NUM_VTK 16 64 | static int vks[NUM_VTK][7] = { 65 | { 0, 49, 53, 126, 999, F5 }, 66 | { 0, 49, 55, 126, 999, F6 }, 67 | { 0, 49, 56, 126, 999, F7 }, 68 | { 0, 49, 59, 53, 72, 999, CHome }, 69 | { 0, 49, 59, 53, 70, 999, CEnd }, 70 | { 0, 50, 126, 999, Ins }, 71 | { 0, 51, 126, 999, Del }, 72 | { 0, 53, 126, 999, PgUp }, 73 | { 0, 54, 126, 999, PgDn }, 74 | { 0, 65, 999, Up }, 75 | { 0, 66, 999, Dn }, 76 | { 0, 67, 999, Rt }, 77 | { 0, 68, 999, Lt }, 78 | { 0, 70, 999, End }, 79 | { 0, 72, 999, Home }, 80 | { 0, 90, 999, STab }, 81 | }; 82 | 83 | static int vtKey() { 84 | if (key() != '[') { return 27; } 85 | int ndx = 0, k, m; 86 | for (int i=0; i 32)) { ++off; } 129 | while ((off < MAX_COL) & (EDCH(line,off) < 33)) { ++off; } 130 | } else { 131 | while ((0 < off) & (EDCH(line,off-1) < 33)) { --off; } 132 | while ((0 < off) & (EDCH(line,off-1) > 32)) { --off; } 133 | } 134 | } 135 | 136 | static void showState(char ch) { 137 | static int lastState = INTERP; 138 | if (ch == -1) { lastState = INTERP; return; } 139 | int cols[4] = { 40, 203, 226, 231 }; 140 | if (ch == 0) { ch = (lastState) ? lastState : INTERP; } 141 | if (btwi(ch,1,4)) { FG(cols[ch-1]); lastState = ch; } 142 | } 143 | 144 | static void gotoEOL() { 145 | off = MAX_COL; 146 | if (EDCH(line, off) > 32) { return; } 147 | while (off && (EDCH(line, off-1) < 33)) { --off; } 148 | } 149 | 150 | static void copyLine(char *from, char *to, int nullTerm) { 151 | for (int c=0; c 32) { deleteChar(0); } 215 | for (int i=0; i<20; i++) { if (EDCH(line,off)<33) { deleteChar(0); } } 216 | } 217 | 218 | static void clrToEOL(int l, int o) { 219 | while (ol; r--) { 248 | char *f = &EDCH(r-1, 0); 249 | char *t = &EDCH(r, 0); 250 | for (int c=0; c 389 | mvLeft(); if (edMode == INSERT) { deleteChar(0); } 390 | return; 391 | } 392 | switch (c) { 393 | case 1: doInsertReplace(c); // COMPLE 394 | RCASE 2: doInsertReplace(c); // DEFINE 395 | RCASE 3: doInsertReplace(c); // INTERP 396 | RCASE 4: doInsertReplace(c); // COMMENT 397 | RCASE 5: execLine(line); // Execute current line 398 | RCASE 9: mv(0, 8); // 399 | RCASE 10: mvDown(); // 400 | RCASE 11: mvUp(); // 401 | RCASE 12: mvRight(); // 402 | RCASE 17: mv(0, -8); // 403 | RCASE 24: edDelX('.'); // 404 | RCASE 20: edSvBlk(0); // 405 | RCASE 27: normalMode(); // 406 | RCASE Up: mvUp(); // Up 407 | RCASE Lt: mvLeft(); // Left 408 | RCASE Rt: mvRight(); // Right 409 | RCASE Dn: mvDown(); // Down 410 | RCASE Home: mv(0, -NUM_COLS); // Home 411 | RCASE End: gotoEOL(); // End 412 | RCASE PgUp: gotoBlock(block-1); // PgUp 413 | RCASE PgDn: gotoBlock(block+1); // PgDn 414 | RCASE Del: edDelX('.'); // Delete 415 | RCASE Ins: toggleInsert(); // Insert 416 | RCASE CHome: mv(-NUM_LINES, -NUM_COLS); // -Home 417 | RCASE CEnd: mv(NUM_LINES, -NUM_COLS); // -End 418 | RCASE STab: mv(0, -8); // 419 | RCASE F1: toCmd(); zType("-See Editor.md-"); // F1 420 | } 421 | } 422 | 423 | static int processEditorChar(int c) { 424 | CursorOff(); 425 | if (!btwi(c,32,126)) { doCTL(c); return 1; } 426 | if (btwi(edMode,INSERT,REPLACE)) { return doInsertReplace((char)c); } 427 | 428 | switch (c) { 429 | BCASE ' ': mvRight(); 430 | BCASE '#': CLS(); isShow=1; 431 | BCASE '$': gotoEOL(); 432 | BCASE '_': mv(0,-NUM_COLS); 433 | BCASE '1': replaceChar(1,1,0); // COMPILE 434 | BCASE '2': replaceChar(2,1,0); // DEFINE 435 | BCASE '3': replaceChar(3,1,0); // INTERP 436 | BCASE '4': replaceChar(4,1,0); // COMMENT 437 | BCASE '+': gotoBlock(block+1); // Next block 438 | BCASE '-': gotoBlock(block-1); // Prev block 439 | BCASE '/': cmdFind(); 440 | BCASE ':': edCommand(); 441 | BCASE 'a': mvRight(); insertMode(); 442 | BCASE 'A': gotoEOL(); insertMode(); 443 | BCASE 'b': insertSpace(0); 444 | BCASE 'B': insertSpace(1); 445 | BCASE 'c': edDelX('.'); insertMode(); 446 | BCASE 'C': edDelX('$'); insertMode(); 447 | BCASE 'd': edDelX(0); 448 | BCASE 'D': edDelX('$'); 449 | BCASE 'g': mv(-NUM_LINES,-NUM_COLS); 450 | BCASE 'G': mv(NUM_LINES,-NUM_COLS); 451 | BCASE 'h': mvLeft(); 452 | BCASE 'i': insertMode(); 453 | BCASE 'I': mv(0, -NUM_COLS); insertMode(); 454 | BCASE 'j': mvDown(); 455 | BCASE 'J': joinLines(); 456 | BCASE 'k': mvUp(); 457 | BCASE 'l': mvRight(); 458 | BCASE 'm': mv(4,0); 459 | BCASE 'M': mv(-4,0); 460 | BCASE 'n': doFind(1); 461 | BCASE 'N': doFind(0); 462 | BCASE 'o': mvNextLine(); insertLine(line, -1); insertMode(); 463 | BCASE 'O': insertLine(line, -1); insertMode(); 464 | BCASE 'P': insertLine(line, -1); putLine(line); 465 | BCASE 'p': mvNextLine(); insertLine(line, -1); putLine(line); 466 | BCASE 'Q': mv(0,-4); 467 | BCASE 'q': mv(0,4); 468 | BCASE 'r': replace1(); 469 | BCASE 'R': replaceMode(); 470 | BCASE 'S': gotoBlock(lastBlock); 471 | BCASE 'w': moveWord(1); 472 | BCASE 'W': moveWord(0); 473 | BCASE 'x': edDelX(c); 474 | BCASE 'X': edDelX(c); 475 | BCASE 'Y': yankLine(line, yanked); 476 | BCASE 'Z': edDelX(c); 477 | } 478 | return 1; 479 | } 480 | 481 | static void showFind() { 482 | if (findBuf[0] == 0) { return; } 483 | FG(255); BG(19); 484 | for (int r=0; rin @ c! ; immediate \ 12 | : ->memory memory + ; \ 13 | : ->code dup + ->memory ; \ 14 | : here (here) wc@ ; \ 15 | : last (last) @ ; \ 16 | : base@ base wc@ ; \ 17 | : base! base wc! ; \ 18 | : block@ (block) wc@ ; \ 19 | : block! (block) wc! ; \ 20 | : vhere (vhere) @ ; \ 21 | : allot (vhere) +! ; \ 22 | : 0sp 0 (dsp) wc! ; \ 23 | : 0rsp 0 (rsp) wc! ; \ 24 | : , here dup 1+ (here) wc! wc! ; \ 25 | : v, vhere ! cell allot ; \ 26 | : vc, vhere c! 1 allot ; \ 27 | : const ( n-- ) addword lit, (exit) , ; \ 28 | : var ( n-- ) vhere const allot ; \ 29 | : val -1 const ; \ 30 | : (val) last w@ 1+ ->code const ; \ 31 | : create vhere addword vhere lit, ; \ 32 | : does> (jmp) , r> , ; \ 33 | : begin here ; immediate \ 34 | : again (jmp) , , ; immediate \ 35 | : while (jmpnz) , , ; immediate \ 36 | : until (jmpz) , , ; immediate \ 37 | : -while (njmpnz) , , ; immediate \ 38 | : -until (njmpz) , , ; immediate \ 39 | : -if (njmpz) , here 0 , ; immediate \ 40 | : if (jmpz) , here 0 , ; immediate \ 41 | : if0 (jmpnz) , here 0 , ; immediate \ 42 | : else (jmp) , here swap 0 , here swap wc! ; immediate \ 43 | : then here swap wc! ; immediate \ 44 | : hex $10 base! ; \ 45 | : binary %10 base! ; \ 46 | : decimal #10 base! ; \ 47 | : ?dup -if dup then ; \ 48 | : nip swap drop ; : tuck swap over ; \ 49 | : 2dup over over ; : 2drop drop drop ; \ 50 | : rot >r swap r> swap ; : -rot swap >r swap r> ; \ 51 | : 0< 0 < ; : 0> 0 > ; \ 52 | : <= > 0= ; : >= < 0= ; : <> = 0= ; \ 53 | : 2+ 1+ 1+ ; inline \ 54 | : 2* dup + ; inline \ 55 | : 2/ 2 / ; inline \ 56 | : cells cell * ; inline \ 57 | : cell+ cell + ; inline \ 58 | : min ( a b--c ) 2dup > if swap then drop ; \ 59 | : max ( a b--c ) 2dup < if swap then drop ; \ 60 | : btwi ( n l h--f ) >r over > swap r> > or 0= ; \ 61 | : negate com 1+ ; \ 62 | : abs dup 0< if negate then ; \ 63 | : mod /mod drop ; \ 64 | : execute ( a-- ) >r ; \ 65 | : atdrop adrop tdrop ; \ 66 | : a+ a@+ drop ; inline : a- a@- drop ; inline \ 67 | : c@a a@ c@ ; inline : c!a a@ c! ; inline \ 68 | : c@a+ a@+ c@ ; inline : c!a+ a@+ c! ; inline \ 69 | : c@a- a@- c@ ; inline : c!a- a@- c! ; inline \ 70 | : b+ b@+ drop ; inline : b- b@- drop ; inline \ 71 | : c@b b@ c@ ; inline : c!b b@ c! ; inline \ 72 | : c@b+ b@+ c@ ; inline : c!b+ b@+ c! ; inline \ 73 | : c@b- b@- c@ ; inline : c!b- b@- c! ; inline \ 74 | : t+ t@+ drop ; inline : t- t@- drop ; inline \ 75 | : c@t t@ c@ ; inline : c!t t@ c! ; inline \ 76 | : c@t+ t@+ c@ ; inline : c!t+ t@+ c! ; inline \ 77 | : c@t- t@- c@ ; inline : c!t- t@- c! ; inline \ 78 | 100 var pad \ 79 | : <# ( n1--n2 ) pad 99 + >t 0 t@ c! dup 0< >a abs ; \ 80 | : #c ( c-- ) t- t@ c! ; \ 81 | : #. ( -- ) '.' #c ; \ 82 | : #n ( n-- ) dup 9 > if 7 + then '0' + #c ; \ 83 | : # ( n1--n2 ) base@ /mod swap #n ; \ 84 | : #s ( n-- ) begin # -while ; \ 85 | : #> ( --str ) drop a> if '-' #c then t> ; \ 86 | : (.) <# #s #> ztype ; \ 87 | : . (.) 32 emit ; \ 88 | : bl 32 ; inline : space 32 emit ; \ 89 | : cr 13 emit 10 emit ; \ 90 | : tab 9 emit ; \ 91 | : .version version <# # # #. # # #. #s #> ztype ; \ 92 | : ? @ . ; \ 93 | : ed block@ edit ; : ed! block! ; inline \ 94 | : .s '(' emit space (dsp) wc@ 1- ?dup \ 95 | if for i 1+ cells dstk + @ . next then ')' emit ; \ 96 | : [[ vhere >t here >t 1 state wc! ; \ 97 | : ]] (exit) , 0 state wc! t@ (here) wc! t> >r t> (vhere) ! ; immediate \ 98 | mem-sz ->memory const mem-end \ 99 | : ->xt w@ ; inline \ 100 | : ->flags wc-sz + c@ ; \ 101 | : ->len wc-sz + 1+ c@ ; \ 102 | : ->name wc-sz + 2+ ; \ 103 | : words last >a 0 >t 0 >r \ 104 | begin \ 105 | a@ ->name ztype r@ 1+ r! \ 106 | a@ ->len dup 7 > t@ + t! 14 > t@ + t! \ 107 | t@+ 9 > if cr 0 t! else tab then \ 108 | a@ de-sz + a! a@ mem-end < \ 109 | while tdrop adrop r> .\" (%d words)\" ; \ 110 | : words-n ( n-- ) 0 >a last swap for \ 111 | dup ->name ztype tab a@+ 9 > if cr 0 a! then de-sz + \ 112 | next drop adrop ; \ 113 | cell var t0 cell var t1 \ 114 | : marker here 20 wc! last t0 ! vhere t1 ! ; \ 115 | : forget 20 wc@ (here) wc! t0 @ (last) ! t1 @ (vhere) ! ; \ 116 | : fgl last dup de-sz + (last) ! ->xt (here) wc! ; \ 117 | : fopen-rt ( fn--fh|0 ) z\" rt\" fopen ; \ 118 | : fopen-rb ( fn--fh|0 ) z\" rb\" fopen ; \ 119 | : fopen-wb ( fn--fh|0 ) z\" wb\" fopen ; \ 120 | : ->file ( fn-- ) fopen-wb (output-fp) ! ; \ 121 | : ->stdout ( -- ) (output-fp) @ ?dup if fclose 0 (output-fp) ! then ; \ 122 | : thru ( f t-- ) begin dup load 1- over over > until drop drop ; \ 123 | marker \ 124 | "); 125 | } 126 | #endif // _SYS_LOAD_ 127 | -------------------------------------------------------------------------------- /system.c: -------------------------------------------------------------------------------- 1 | #include "c4.h" 2 | 3 | #ifdef IS_WINDOWS 4 | #include 5 | int qKey() { return _kbhit(); } 6 | int key() { return _getch(); } 7 | void ttyMode(int isRaw) {} 8 | #endif 9 | 10 | #ifdef IS_LINUX 11 | #include 12 | #include 13 | #include 14 | 15 | void ttyMode(int isRaw) { 16 | static struct termios origt, rawt; 17 | static int curMode = -1; 18 | if (curMode == -1) { 19 | curMode = 0; 20 | tcgetattr( STDIN_FILENO, &origt); 21 | cfmakeraw(&rawt); 22 | } 23 | if (isRaw != curMode) { 24 | if (isRaw) { 25 | tcsetattr( STDIN_FILENO, TCSANOW, &rawt); 26 | } else { 27 | tcsetattr( STDIN_FILENO, TCSANOW, &origt); 28 | } 29 | curMode = isRaw; 30 | } 31 | } 32 | int qKey() { 33 | struct timeval tv; 34 | fd_set rdfs; 35 | ttyMode(1); 36 | tv.tv_sec = 0; 37 | tv.tv_usec = 0; 38 | FD_ZERO(&rdfs); 39 | FD_SET(STDIN_FILENO, &rdfs); 40 | select(STDIN_FILENO+1, &rdfs, NULL, NULL, &tv); 41 | int x = FD_ISSET(STDIN_FILENO, &rdfs); 42 | return x; 43 | } 44 | int key() { 45 | ttyMode(1); 46 | int x = fgetc(stdin); 47 | return x; 48 | } 49 | #endif // IS_LINUX 50 | 51 | cell timer() { return (cell)clock(); } 52 | void zType(const char *str) { fputs(str, outputFp ? (FILE*)outputFp : stdout); } 53 | void emit(const char ch) { fputc(ch, outputFp ? (FILE*)outputFp : stdout); } 54 | 55 | // REP - Read/Execute/Print (no Loop) 56 | void REP() { 57 | char tib[128]; 58 | if (inputFp == 0) { 59 | ttyMode(0); 60 | if (fetchWC(SA) == COMMENT) { changeState(INTERP); } 61 | ok(); 62 | } 63 | if (fileGets(tib, sizeof(tib), inputFp)) { 64 | outer(tib); 65 | return; 66 | } 67 | if (inputFp == 0) { exit(0); } 68 | fileClose(inputFp); 69 | inputFp = filePop(); 70 | } 71 | 72 | void loadArgument(const char *arg) { 73 | char fn[32]; 74 | strCpy(fn, arg); 75 | cell tmp = fileOpen(fn, "rb"); 76 | if (tmp) { 77 | if (inputFp) { filePush(tmp); } 78 | else { inputFp = tmp; } 79 | } 80 | } 81 | 82 | int main(int argc, char *argv[]) { 83 | c4Init(); 84 | if (argc > 1) { loadArgument(argv[1]); } 85 | else { loadArgument("block-999.fth"); } 86 | while (1) { REP(); }; 87 | return 0; 88 | } 89 | --------------------------------------------------------------------------------