about summary refs log tree commit diff stats
path: root/forth
diff options
context:
space:
mode:
Diffstat (limited to 'forth')
-rw-r--r--forth/factorial.forth11
-rw-r--r--forth/foreforthfourth/README.md420
-rw-r--r--forth/foreforthfourth/debug-string2.js32
-rw-r--r--forth/foreforthfourth/forth-documented.js1076
-rw-r--r--forth/foreforthfourth/forth.js1973
-rw-r--r--forth/foreforthfourth/index.html381
-rw-r--r--forth/foreforthfourth/test-advanced.js94
-rw-r--r--forth/foreforthfourth/test-cross-stack-complete.js373
-rw-r--r--forth/foreforthfourth/test-forth.js77
-rw-r--r--forth/foreforthfourth/test-help-full.js33
-rw-r--r--forth/foreforthfourth/test-help.js52
-rw-r--r--forth/guesser.fth100
-rw-r--r--forth/pf_ref.md1213
-rw-r--r--forth/pf_tut.md1345
14 files changed, 7180 insertions, 0 deletions
diff --git a/forth/factorial.forth b/forth/factorial.forth
new file mode 100644
index 0000000..359a642
--- /dev/null
+++ b/forth/factorial.forth
@@ -0,0 +1,11 @@
+( n -- n! )
+: FACTORIAL
+    \ If n is 0, the loop won't run and the initial 1 is returned.
+    1 SWAP            \ Put initial result 1 on stack, ( 1 n )
+    1+ 1              \ Setup loop bounds, ( 1 n+1 1 )
+    DO
+        I * \ Multiply accumulator by loop index
+    LOOP ;
+
+5 FACTORIAL .
+10 FACTORIAL . 
diff --git a/forth/foreforthfourth/README.md b/forth/foreforthfourth/README.md
new file mode 100644
index 0000000..29c3b5e
--- /dev/null
+++ b/forth/foreforthfourth/README.md
@@ -0,0 +1,420 @@
+# 4-Stack Toy Forth Interpreter
+
+A pure functional implementation of a toy Forth interpreter with a unique 4-stack architecture, written in JavaScript.
+
+## Architecture
+
+This Forth interpreter features **4 separate stacks** that users can juggle between, unlike traditional single-stack Forth implementations:
+
+- **Stack 1 (Red)** - Default stack for most operations
+- **Stack 2 (Teal)** - Secondary stack for data organization
+- **Stack 3 (Blue)** - Tertiary stack for complex operations
+- **Stack 4 (Yellow)** - Quaternary stack for additional data
+
+## Features
+
+### Core Forth Operations
+- **Stack Manipulation**: `dup`, `swap`, `drop`, `2dup`, `2drop`, `over`, `rot`, `-rot`
+- **Arithmetic**: `+`, `-`, `*`, `/`, `mod`
+- **Comparison**: `=`, `<`, `>`, `and`, `or`, `not`
+- **Math Utilities**: `abs`, `negate`, `min`, `max`
+- **Stack Inspection**: `.s` (non-destructive), `depth`
+- **String Operations**: `." ... "` (print), `s" ... "` (push), `strlen`, `strcat`, `char+`
+- **Control Flow**: `if ... then`, `if ... else ... then`, `begin ... until`
+- **Help System**: `help` (comprehensive help), `doc <word>` (word documentation), `words` (word list)
+
+### Multi-Stack Operations
+- **Stack Focus System**: `focus.red`, `focus.teal`, `focus.blue`, `focus.yellow` (or `focus.1`, `focus.2`, `focus.3`, `focus.4`)
+- **Move Operations**: `move.red`, `move.teal`, `move.blue`, `move.yellow` (or `move.1`, `move.2`, `move.3`, `move.4`)
+- **Pop Operations**: `pop.red`, `pop.teal`, `pop.blue`, `pop.yellow` (or `pop.1`, `pop.2`, `pop.3`, `pop.4`)
+- **Copy Operations**: `copy.red`, `copy.teal`, `copy.blue`, `copy.yellow` (or `copy.1`, `copy.2`, `copy.3`, `copy.4`)
+- **Move Operations**: `move` (interactive stack-to-stack movement), `move.red`, `move.teal`, `move.blue`, `move.yellow` (or `move.1`, `move.2`, `move.3`, `move.4`)
+- **Clear Operations**: `clear.all` (clear all stacks), `clear.focused` (clear focused stack)
+- **Cross-Stack Operations**: `dup.stacks`, `over.stacks`, `swap.stacks`, `nip.stacks`, `tuck.stacks`, `rot.stacks`, `2dup.stacks`, `2over.stacks`, `2swap.stacks`
+
+### Word Definition System
+- **Define Words**: `: name ... ;` syntax
+- **List Words**: `words` command shows all available words
+- **User Dictionary**: Persistent storage of custom words
+
+## Project Structure
+
+```
+foreforthfourth/
+├── index.html          # Web interface
+├── forth.js            # Core Forth interpreter (standalone)
+├── test-forth.js       # Test suite for the interpreter
+└── README.md           # This file
+```
+
+## Testing
+
+### Run Tests
+```bash
+node test-forth.js
+```
+
+### Complete Test Suite
+We have comprehensive test coverage including:
+- **Core Operations**: Stack manipulation, arithmetic, comparison, logic
+- **Focus System**: All focus commands and stack operations on different stacks
+- **String Operations**: String literals, manipulation, and type checking
+- **Control Flow**: Conditional execution and loops
+- **Multi-Stack**: Operations across all 4 stacks with focus system
+- **Error Handling**: Enhanced error messages and edge cases
+- **Help System**: Help commands and word documentation
+- **Word Definition**: User-defined words and compilation
+- **Enhanced Features**: Clear operations, move operations, focus persistence
+
+### Test Results
+- **Total Tests**: 16 comprehensive test cases
+- **Success Rate**: 100% ✅
+- **Coverage**: Complete feature coverage with edge case testing
+
+## Web Interface
+
+Open `index.html` in a web browser to use the interactive Forth interpreter with:
+- Visual representation of all 4 stacks
+- Real-time command execution
+- Output history
+- Responsive design for mobile and desktop
+
+## Usage Examples
+
+### Basic Stack Operations
+```forth
+5 3 2 .s          # Push numbers and show stack
+dup over          # Duplicate top, copy second over top
+2dup              # Duplicate top two items
+```
+
+### Arithmetic
+```forth
+10 3 /            # Integer division (result: 3)
+10 3 mod          # Modulo (result: 1)
+-5 abs            # Absolute value (result: 5)
+5 negate          # Negate (result: -5)
+```
+
+### Multi-Stack Juggling
+```forth
+5 3 2             # Push to red stack
+move.teal         # Move top of red to teal stack
+move.blue         # Move top of red to blue stack
+```
+
+### Stack Focus System
+The interpreter now supports operating on any of the 4 stacks using a focus system:
+
+```forth
+focus.red         # Set focus to Red stack (Stack 1)
+focus.teal        # Set focus to Teal stack (Stack 2)  
+focus.blue        # Set focus to Blue stack (Stack 3)
+focus.yellow      # Set focus to Yellow stack (Stack 4)
+
+# Number aliases also work:
+focus.1           # Same as focus.red
+focus.2           # Same as focus.teal
+focus.3           # Same as focus.blue
+focus.4           # Same as focus.yellow
+
+focus.show        # Show which stack is currently focused
+```
+
+**All stack operations** (dup, swap, drop, +, -, *, /, etc.) now work on the **focused stack** instead of just the Red stack. This makes the multi-stack architecture truly powerful!
+
+**Number and Color Aliases**: All focus, push, and pop commands support both color names and numbers:
+```forth
+# Focus commands
+focus.1            # Same as focus.red
+focus.2            # Same as focus.teal
+focus.3            # Same as focus.blue
+focus.4            # Same as focus.yellow
+
+# Move commands
+move.1             # Same as move.red
+move.2             # Same as move.teal
+move.3             # Same as move.blue
+move.4             # Same as move.yellow
+
+# Copy commands
+copy.1             # Same as copy.red
+copy.2             # Same as copy.teal
+copy.3             # Same as copy.blue
+copy.4             # Same as copy.yellow
+
+# Pop commands
+pop.1              # Same as pop.red
+pop.2              # Same as pop.teal
+pop.3              # Same as pop.blue
+pop.4              # Same as pop.yellow
+```
+
+### Enhanced Clear Operations
+```forth
+clear.all         # Clear all stacks (same as clear)
+clear.focused     # Clear only the currently focused stack
+
+# Example workflow:
+focus.teal        # Focus on Teal stack
+15 20 25          # Add items to Teal stack
+clear.focused     # Clear only Teal stack
+focus.red         # Switch to Red stack
+5 10              # Add items to Red stack
+clear.focused     # Clear only Red stack
+```
+
+### Cross-Stack Operations
+The interpreter now provides comprehensive cross-stack manipulation using familiar Forth words with `.stacks` suffix:
+
+#### **Basic Cross-Stack Operations**
+```forth
+# Duplicate top item to another stack
+focus.red
+15                 # Add item to Red stack
+dup.stacks         # Start dup.stacks operation
+2                   # Enter target stack (Teal)
+# Result: 15 is duplicated to Teal stack
+
+# Copy second item to another stack
+focus.blue
+10 20 30           # Add items to Blue stack
+over.stacks        # Start over.stacks operation
+1                   # Enter target stack (Red)
+# Result: 20 is copied to Red stack
+
+# Swap top items between stacks
+focus.red
+5 10               # Add items to Red stack
+swap.stacks        # Start swap.stacks operation
+3                   # Enter target stack (Blue)
+# Result: Top items are swapped between Red and Blue stacks
+```
+
+#### **Advanced Cross-Stack Operations**
+```forth
+# Move second item to another stack (remove from source)
+focus.teal
+100 200 300        # Add items to Teal stack
+nip.stacks         # Start nip.stacks operation
+4                   # Enter target stack (Yellow)
+# Result: 200 moved to Yellow stack, 100 and 300 remain on Teal
+
+# Tuck top item under second item on another stack
+focus.red
+5 10               # Add items to Red stack
+tuck.stacks        # Start tuck.stacks operation
+2                   # Enter target stack (Teal)
+# Result: 5 tucked under 10 on Teal stack
+
+# Rotate top 3 items between stacks
+focus.blue
+1 2 3              # Add items to Blue stack
+rot.stacks         # Start rot.stacks operation
+1                   # Enter target stack (Red)
+# Result: Top 3 items rotated between Blue and Red stacks
+
+# Duplicate top 2 items to another stack
+focus.yellow
+50 60              # Add items to Yellow stack
+2dup.stacks        # Start 2dup.stacks operation
+3                   # Enter target stack (Blue)
+# Result: 50 and 60 duplicated to Blue stack
+
+# Copy second pair of items to another stack
+focus.red
+10 20 30 40        # Add items to Red stack
+2over.stacks       # Start 2over.stacks operation
+2                   # Enter target stack (Teal)
+# Result: 20 and 30 copied to Teal stack
+
+# Swap top 2 pairs between stacks
+focus.teal
+1 2 3 4            # Add items to Teal stack
+2swap.stacks       # Start 2swap.stacks operation
+1                   # Enter target stack (Red)
+# Result: Top 2 pairs swapped between Teal and Red stacks
+```
+
+#### **Cross-Stack Operation Workflow**
+All cross-stack operations follow this pattern:
+1. **Set focus** to the source stack
+2. **Add items** to the source stack
+3. **Execute operation** (e.g., `dup.stacks`)
+4. **Enter target stack** number (1-4) when prompted
+5. **Operation completes** automatically
+
+This provides **true multi-stack power** while maintaining familiar Forth semantics!
+
+### Move Operations (No Duplication)
+The interpreter provides several ways to **move** items between stacks without duplication:
+
+#### **Interactive Move Command**
+The `move` command is a **two-step interactive operation** that moves the top item from one stack to another:
+
+```forth
+move    # Start move operation
+1       # Source stack (Red/Stack 1)
+3       # Destination stack (Blue/Stack 3)
+# Result: Top item moved from Red to Blue stack
+```
+
+**Workflow:**
+1. Type `move` to start the operation
+2. Enter the **source stack number** (1-4)
+3. Enter the **destination stack number** (1-4)
+4. The item is **removed** from source and **added** to destination
+
+#### **Move Commands with Focus System**
+Use `move.` commands to move items from the focused stack to a specific target stack:
+
+```forth
+focus.red         # Focus on Red stack (1)
+42                # Add item to Red stack
+move.3            # Move top item to Blue stack (3)
+# Result: 42 moved from Red to Blue stack
+
+focus.teal        # Focus on Teal stack (2)
+100               # Add item to Teal stack
+move.yellow       # Move top item to Yellow stack (4)
+# Result: 100 moved from Teal to Yellow stack
+```
+
+#### **Number and Color Aliases**
+All move and copy commands support both number and color naming:
+
+```forth
+# Move commands (remove from source)
+move.1            # Move to Red stack (1)
+move.2            # Move to Teal stack (2)
+move.3            # Move to Blue stack (3)
+move.4            # Move to Yellow stack (4)
+
+# Copy commands (keep in source)
+copy.1            # Copy to Red stack (1)
+copy.2            # Copy to Teal stack (2)
+copy.3            # Copy to Blue stack (3)
+copy.4            # Copy to Yellow stack (4)
+
+# Color aliases
+move.red          # Move to Red stack (1)
+move.teal         # Move to Teal stack (2)
+move.blue         # Move to Blue stack (3)
+move.yellow       # Move to Yellow stack (4)
+
+copy.red          # Copy to Red stack (1)
+copy.teal         # Copy to Teal stack (2)
+copy.blue         # Copy to Blue stack (3)
+copy.yellow       # Copy to Yellow stack (4)
+```
+
+#### **Comparison: Move vs Copy Operations**
+
+| Operation | Effect | Duplication | Use Case |
+|-----------|--------|-------------|----------|
+| `move` | **Moves** item from source to destination | ❌ No | Relocate items between stacks |
+| `move.{stack}` | **Moves** item from focused stack to target | ❌ No | Move from focused stack to specific stack |
+| `copy.{stack}` | **Copies** item from focused stack to target | ✅ Yes | Keep item on source, copy to target |
+| `dup.stacks` | **Copies** item from focused stack to target | ✅ Yes | Keep item on source, copy to target |
+| `over.stacks` | **Copies** second item from focused stack to target | ✅ Yes | Copy second item without affecting top |
+
+#### **Quick Reference: All Move and Copy Operations**
+
+| Command | From | To | Effect |
+|---------|------|----|---------|
+| `move` + source + dest | Any stack | Any stack | Move top item between specified stacks |
+| `move.red` / `move.1` | Focused stack | Red stack (1) | Move top item to Red stack |
+| `move.teal` / `move.2` | Focused stack | Teal stack (2) | Move top item to Teal stack |
+| `move.blue` / `move.3` | Focused stack | Blue stack (3) | Move top item to Blue stack |
+| `move.yellow` / `move.4` | Focused stack | Yellow stack (4) | Move top item to Yellow stack |
+| `copy.red` / `copy.1` | Focused stack | Red stack (1) | Copy top item to Red stack |
+| `copy.teal` / `copy.2` | Focused stack | Teal stack (2) | Copy top item to Teal stack |
+| `copy.blue` / `copy.3` | Focused stack | Blue stack (3) | Copy top item to Blue stack |
+| `copy.yellow` / `copy.4` | Focused stack | Yellow stack (4) | Copy top item to Yellow stack |
+
+#### **Complete Move Example**
+```forth
+# Setup: Add items to different stacks
+focus.red
+42                # Red stack: [42]
+focus.teal
+100               # Teal stack: [100]
+focus.blue
+200               # Blue stack: [200]
+
+# Move items between stacks
+focus.red
+move.2            # Move 42 from Red to Teal
+# Red stack: [], Teal stack: [100, 42]
+
+focus.teal
+move.3            # Move 100 from Teal to Blue
+# Teal stack: [42], Blue stack: [200, 100]
+
+# Use interactive move for complex operations
+move              # Start move operation
+3                 # Source: Blue stack (3)
+1                 # Destination: Red stack (1)
+# Result: 200 moved from Blue to Red stack
+# Red stack: [200], Blue stack: [100]
+```
+
+### Word Definition
+```forth
+: double dup + ;   # Define 'double' word
+5 double          # Use the word (result: 10)
+```
+
+### Help System
+```forth
+help              # Show comprehensive help for all words
+s" dup" doc      # Show detailed documentation for 'dup'
+words             # List all available words
+```
+
+### Comparison and Logic
+```forth
+5 3 >             # 5 > 3 (result: -1 for true)
+5 3 <             # 5 < 3 (result: 0 for false)
+5 3 > not         # NOT (5 > 3) (result: 0)
+```
+
+## Design Principles
+
+### Pure Functional
+- **Immutable State**: All state updates return new state objects
+- **No Side Effects**: Functions are pure and predictable
+- **Functional Composition**: Operations compose naturally
+
+### 4-Stack Architecture
+- **Stack Independence**: Each stack operates independently
+- **Flexible Data Flow**: Move data between stacks as needed
+- **Organized Workflows**: Use different stacks for different purposes
+
+### ANS Forth Compatibility
+- **Standard Words**: Implements core ANS Forth words
+- **Familiar Syntax**: Standard Forth syntax and semantics
+- **Extensible**: Easy to add new words and functionality
+- **Enhanced Error Messages**: Helpful, actionable error messages with stack context and solutions
+
+## Current Status
+
+### **Fully Implemented Features**
+- **Control Flow**: `IF ... THEN`, `IF ... ELSE ... THEN`, `BEGIN ... UNTIL` constructs
+- **String Operations**: String literals (`."` and `s"`), manipulation (`strlen`, `strcat`, `char+`, `type`, `count`)
+- **Stack Focus System**: Operate on any of the 4 stacks using focus commands
+- **Enhanced Error Messages**: Helpful, actionable error messages with stack context
+- **Help System**: Comprehensive help (`help`) and word documentation (`doc`)
+- **Multi-Stack Operations**: Full support for all 4 stacks with focus system
+- **Enhanced Clear Operations**: `clear.all` and `clear.focused` commands
+- **Move Operations**: Interactive `move` command and `move.{stack}` commands for moving items between stacks
+- **Copy Operations**: `copy.{stack}` commands for copying items between stacks without removal
+- **Number Aliases**: All focus, move, copy, and pop commands support both color names and numbers (1-4)
+- **Math Utilities**: `abs`, `negate`, `min`, `max` operations
+
+### **Advanced Capabilities**
+- **Universal Stack Operations**: All built-in words work on any focused stack
+- **Dual Naming System**: Both color names and numbers work for all commands
+- **Professional Error Handling**: Context-aware error messages with solutions
+- **Visual Focus Indicators**: UI shows which stack is currently focused
+- **Complete Test Coverage**: 100% test coverage of all features
\ No newline at end of file
diff --git a/forth/foreforthfourth/debug-string2.js b/forth/foreforthfourth/debug-string2.js
new file mode 100644
index 0000000..01a42aa
--- /dev/null
+++ b/forth/foreforthfourth/debug-string2.js
@@ -0,0 +1,32 @@
+// Debug string literals with actual command format
+const ForthInterpreter = require('./forth.js');
+
+console.log('🔍 Debugging String Literals - Command Format\n');
+
+let state = ForthInterpreter.createInitialState();
+
+console.log('Testing: ." Hello World"');
+state = ForthInterpreter.parseAndExecute(state, '." Hello World"');
+console.log('Result:', {
+    stringMode: state.stringMode,
+    currentString: state.currentString,
+    stacks: state.stacks[0]
+});
+
+console.log('\nTesting: ." Hello"');
+state = ForthInterpreter.createInitialState();
+state = ForthInterpreter.parseAndExecute(state, '." Hello"');
+console.log('Result:', {
+    stringMode: state.stringMode,
+    currentString: state.currentString,
+    stacks: state.stacks[0]
+});
+
+console.log('\nTesting: ." Test"');
+state = ForthInterpreter.createInitialState();
+state = ForthInterpreter.parseAndExecute(state, '." Test"');
+console.log('Result:', {
+    stringMode: state.stringMode,
+    currentString: state.currentString,
+    stacks: state.stacks[0]
+});
diff --git a/forth/foreforthfourth/forth-documented.js b/forth/foreforthfourth/forth-documented.js
new file mode 100644
index 0000000..56ccc17
--- /dev/null
+++ b/forth/foreforthfourth/forth-documented.js
@@ -0,0 +1,1076 @@
+// Pure functional approach to Forth interpreter state
+const createInitialState = () => ({
+    stacks: [[], [], [], []],
+    dictionary: new Map(),
+    output: [],
+    compilingWord: null,
+    compilingDefinition: [],
+    stringMode: false,
+    currentString: '',
+    stringPushMode: false,
+    skipMode: false,
+    skipCount: 0,
+    loopStart: null,
+    loopBack: false
+});
+
+// Pure function to update state
+const updateState = (state, updates) => ({
+    ...state,
+    ...updates
+});
+
+// Stack operations
+const pushToStack = (stacks, stackIndex, value) => {
+    const newStacks = stacks.map((stack, i) => 
+        i === stackIndex ? [...stack, value] : stack
+    );
+    return newStacks;
+};
+
+const popFromStack = (stacks, stackIndex) => {
+    const newStacks = stacks.map((stack, i) => 
+        i === stackIndex ? stack.slice(0, -1) : stack
+    );
+    const value = stacks[stackIndex][stacks[stackIndex].length - 1];
+    return { stacks: newStacks, value };
+};
+
+const moveBetweenStacks = (stacks, fromStack, toStack) => {
+    if (stacks[fromStack].length === 0) return { stacks, value: null };
+    const { stacks: newStacks, value } = popFromStack(stacks, fromStack);
+    return { stacks: pushToStack(newStacks, toStack, value), value };
+};
+
+const popAndPrint = (state, stackIndex) => {
+    if (state.stacks[stackIndex].length === 0) {
+        return updateState(state, { output: [...state.output, `Stack ${stackIndex + 1} is empty`] });
+    }
+    const { stacks, value } = popFromStack(state.stacks, stackIndex);
+    return updateState(state, {
+        stacks,
+        output: [...state.output, `Stack ${stackIndex + 1}: ${value}`]
+    });
+};
+
+// Built-in words with documentation
+const builtinWords = {
+    // Stack manipulation for stack 1 (default)
+    'dup': {
+        fn: (state) => {
+            if (state.stacks[0].length === 0) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on dup'] });
+            }
+            const top = state.stacks[0][state.stacks[0].length - 1];
+            return updateState(state, {
+                stacks: pushToStack(state.stacks, 0, top)
+            });
+        },
+        doc: 'Duplicate the top item on the stack',
+        stack: '( x -- x x )'
+    },
+
+    'swap': {
+        fn: (state) => {
+            if (state.stacks[0].length < 2) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on swap'] });
+            }
+            const newStacks = state.stacks.map((stack, i) => [...stack]);
+            const a = newStacks[0].pop();
+            const b = newStacks[0].pop();
+            newStacks[0].push(a);
+            newStacks[0].push(b);
+            return updateState(state, { stacks: newStacks });
+        },
+        doc: 'Exchange the top two items on the stack',
+        stack: '( x1 x2 -- x2 x1 )'
+    },
+
+    'drop': {
+        fn: (state) => {
+            if (state.stacks[0].length === 0) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on drop'] });
+            }
+            const { stacks } = popFromStack(state.stacks, 0);
+            return updateState(state, { stacks });
+        },
+        doc: 'Remove the top item from the stack',
+        stack: '( x -- )'
+    },
+
+    '2drop': {
+        fn: (state) => {
+            if (state.stacks[0].length < 2) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on 2drop'] });
+            }
+            const { stacks: stacks1 } = popFromStack(state.stacks, 0);
+            const { stacks } = popFromStack(stacks1, 0);
+            return updateState(state, { stacks });
+        },
+        doc: 'Remove the top two items from the stack',
+        stack: '( x1 x2 -- )'
+    },
+
+    'over': {
+        fn: (state) => {
+            if (state.stacks[0].length < 2) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on over'] });
+            }
+            const second = state.stacks[0][state.stacks[0].length - 2];
+            return updateState(state, {
+                stacks: pushToStack(state.stacks, 0, second)
+            });
+        },
+        doc: 'Copy the second item on the stack to the top',
+        stack: '( x1 x2 -- x1 x2 x1 )'
+    },
+
+    '2dup': {
+        fn: (state) => {
+            if (state.stacks[0].length < 2) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on 2dup'] });
+            }
+            const top = state.stacks[0][state.stacks[0].length - 1];
+            const second = state.stacks[0][state.stacks[0].length - 2];
+            const newStacks = pushToStack(state.stacks, 0, second);
+            return updateState(state, {
+                stacks: pushToStack(newStacks, 0, top)
+            });
+        },
+        doc: 'Duplicate the top two items on the stack',
+        stack: '( x1 x2 -- x1 x2 x1 x2 )'
+    },
+
+    'rot': {
+        fn: (state) => {
+            if (state.stacks[0].length < 3) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on rot'] });
+            }
+            const newStacks = state.stacks.map((stack, i) => [...stack]);
+            const a = newStacks[0].pop();
+            const b = newStacks[0].pop();
+            const c = newStacks[0].pop();
+            newStacks[0].push(b);
+            newStacks[0].push(a);
+            newStacks[0].push(c);
+            return updateState(state, { stacks: newStacks });
+        },
+        doc: 'Rotate the top three items on the stack',
+        stack: '( x1 x2 x3 -- x2 x3 x1 )'
+    },
+
+    '-rot': {
+        fn: (state) => {
+            if (state.stacks[0].length < 3) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on -rot'] });
+            }
+            const newStacks = state.stacks.map((stack, i) => [...stack]);
+            const a = newStacks[0].pop();
+            const b = newStacks[0].pop();
+            const c = newStacks[0].pop();
+            newStacks[0].push(a);
+            newStacks[0].push(c);
+            newStacks[0].push(b);
+            return updateState(state, { stacks: newStacks });
+        },
+        doc: 'Rotate the top three items on the stack (reverse of rot)',
+        stack: '( x1 x2 x3 -- x3 x1 x2 )'
+    },
+
+    // Arithmetic operations on stack 1
+    '+': {
+        fn: (state) => {
+            if (state.stacks[0].length < 2) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on +'] });
+            }
+            const { stacks: stacks1, value: b } = popFromStack(state.stacks, 0);
+            const { stacks: stacks2, value: a } = popFromStack(stacks1, 0);
+            return updateState(state, {
+                stacks: pushToStack(stacks2, 0, a + b)
+            });
+        },
+        doc: 'Add the top two numbers on the stack',
+        stack: '( n1 n2 -- n3 )'
+    },
+
+    '-': {
+        fn: (state) => {
+            if (state.stacks[0].length < 2) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on -'] });
+            }
+            const { stacks: stacks1, value: b } = popFromStack(state.stacks, 0);
+            const { stacks: stacks2, value: a } = popFromStack(stacks1, 0);
+            return updateState(state, {
+                stacks: pushToStack(stacks2, 0, a - b)
+            });
+        },
+        doc: 'Subtract the top number from the second number on the stack',
+        stack: '( n1 n2 -- n3 )'
+    },
+
+    '*': {
+        fn: (state) => {
+            if (state.stacks[0].length < 2) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on *'] });
+            }
+            const { stacks: stacks1, value: b } = popFromStack(state.stacks, 0);
+            const { stacks: stacks2, value: a } = popFromStack(stacks1, 0);
+            return updateState(state, {
+                stacks: pushToStack(stacks2, 0, a * b)
+            });
+        },
+        doc: 'Multiply the top two numbers on the stack',
+        stack: '( n1 n2 -- n3 )'
+    },
+
+    '/': {
+        fn: (state) => {
+            if (state.stacks[0].length < 2) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on /'] });
+            }
+            const { stacks: stacks1, value: b } = popFromStack(state.stacks, 0);
+            const { stacks: stacks2, value: a } = popFromStack(stacks1, 0);
+            if (b === 0) {
+                return updateState(state, { 
+                    stacks: pushToStack(pushToStack(stacks2, 0, a), 0, b),
+                    output: [...state.output, 'Error: Division by zero'] 
+                });
+            }
+            return updateState(state, {
+                stacks: pushToStack(stacks2, 0, Math.floor(a / b))
+            });
+        },
+        doc: 'Divide the second number by the top number on the stack (integer division)',
+        stack: '( n1 n2 -- n3 )'
+    },
+
+    'mod': {
+        fn: (state) => {
+            if (state.stacks[0].length < 2) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on mod'] });
+            }
+            const { stacks: stacks1, value: b } = popFromStack(state.stacks, 0);
+            const { stacks: stacks2, value: a } = popFromStack(stacks1, 0);
+            if (b === 0) {
+                return updateState(state, { 
+                    stacks: pushToStack(pushToStack(stacks2, 0, a), 0, b),
+                    output: [...state.output, 'Error: Modulo by zero'] 
+                });
+            }
+            return updateState(state, {
+                stacks: pushToStack(stacks2, 0, a % b)
+            });
+        },
+        doc: 'Return the remainder of dividing the second number by the top number',
+        stack: '( n1 n2 -- n3 )'
+    },
+
+    'abs': {
+        fn: (state) => {
+            if (state.stacks[0].length === 0) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on abs'] });
+            }
+            const { stacks, value } = popFromStack(state.stacks, 0);
+            return updateState(state, {
+                stacks: pushToStack(stacks, 0, Math.abs(value))
+            });
+        },
+        doc: 'Return the absolute value of the top number on the stack',
+        stack: '( n -- |n| )'
+    },
+
+    'min': {
+        fn: (state) => {
+            if (state.stacks[0].length < 2) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on min'] });
+            }
+            const { stacks: stacks1, value: b } = popFromStack(state.stacks, 0);
+            const { stacks: stacks2, value: a } = popFromStack(stacks1, 0);
+            return updateState(state, {
+                stacks: pushToStack(stacks2, 0, Math.min(a, b))
+            });
+        },
+        doc: 'Return the smaller of the top two numbers on the stack',
+        stack: '( n1 n2 -- n3 )'
+    },
+
+    'max': {
+        fn: (state) => {
+            if (state.stacks[0].length < 2) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on max'] });
+            }
+            const { stacks: stacks1, value: b } = popFromStack(state.stacks, 0);
+            const { stacks: stacks2, value: a } = popFromStack(stacks1, 0);
+            return updateState(state, {
+                stacks: pushToStack(stacks2, 0, Math.max(a, b))
+            });
+        },
+        doc: 'Return the larger of the top two numbers on the stack',
+        stack: '( n1 n2 -- n3 )'
+    },
+
+    // Comparison and logic
+    '=': {
+        fn: (state) => {
+            if (state.stacks[0].length < 2) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on ='] });
+            }
+            const { stacks: stacks1, value: b } = popFromStack(state.stacks, 0);
+            const { stacks: stacks2, value: a } = popFromStack(stacks1, 0);
+            return updateState(state, {
+                stacks: pushToStack(stacks2, 0, a === b ? -1 : 0)
+            });
+        },
+        doc: 'Return true (-1) if the top two numbers are equal, false (0) otherwise',
+        stack: '( n1 n2 -- flag )'
+    },
+
+    '<': {
+        fn: (state) => {
+            if (state.stacks[0].length < 2) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on <'] });
+            }
+            const { stacks: stacks1, value: b } = popFromStack(state.stacks, 0);
+            const { stacks: stacks2, value: a } = popFromStack(stacks1, 0);
+            return updateState(state, {
+                stacks: pushToStack(stacks2, 0, a < b ? -1 : 0)
+            });
+        },
+        doc: 'Return true (-1) if the second number is less than the top number, false (0) otherwise',
+        stack: '( n1 n2 -- flag )'
+    },
+
+    '>': {
+        fn: (state) => {
+            if (state.stacks[0].length < 2) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on >'] });
+            }
+            const { stacks: stacks1, value: b } = popFromStack(state.stacks, 0);
+            const { stacks: stacks2, value: a } = popFromStack(stacks1, 0);
+            return updateState(state, {
+                stacks: pushToStack(stacks2, 0, a > b ? -1 : 0)
+            });
+        },
+        doc: 'Return true (-1) if the second number is greater than the top number, false (0) otherwise',
+        stack: '( n1 n2 -- flag )'
+    },
+
+    'and': {
+        fn: (state) => {
+            if (state.stacks[0].length < 2) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on and'] });
+            }
+            const { stacks: stacks1, value: b } = popFromStack(state.stacks, 0);
+            const { stacks: stacks2, value: a } = popFromStack(stacks1, 0);
+            return updateState(state, {
+                stacks: pushToStack(stacks2, 0, a && b ? -1 : 0)
+            });
+        },
+        doc: 'Return true (-1) if both top two values are true, false (0) otherwise',
+        stack: '( x1 x2 -- flag )'
+    },
+
+    'or': {
+        fn: (state) => {
+            if (state.stacks[0].length < 2) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on or'] });
+            }
+            const { stacks: stacks1, value: b } = popFromStack(state.stacks, 0);
+            const { stacks: stacks2, value: a } = popFromStack(stacks1, 0);
+            return updateState(state, {
+                stacks: pushToStack(stacks2, 0, a || b ? -1 : 0)
+            });
+        },
+        doc: 'Return true (-1) if either of the top two values is true, false (0) otherwise',
+        stack: '( x1 x2 -- flag )'
+    },
+
+    'not': {
+        fn: (state) => {
+            if (state.stacks[0].length === 0) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on not'] });
+            }
+            const { stacks, value } = popFromStack(state.stacks, 0);
+            return updateState(state, {
+                stacks: pushToStack(stacks, 0, value ? 0 : -1)
+            });
+        },
+        doc: 'Return true (-1) if the top value is false, false (0) if it is true',
+        stack: '( x -- flag )'
+    },
+
+    // Stack inspection
+    '.s': {
+        fn: (state) => {
+            const stackStr = state.stacks[0].length === 0 ? 'empty' : state.stacks[0].join(' ');
+            return updateState(state, {
+                output: [...state.output, `Stack 1 (red): ${stackStr}`]
+            });
+        },
+        doc: 'Display the contents of the red stack (non-destructive)',
+        stack: '( -- )'
+    },
+
+    'depth': {
+        fn: (state) => {
+            return updateState(state, {
+                stacks: pushToStack(state.stacks, 0, state.stacks[0].length)
+            });
+        },
+        doc: 'Push the number of items on the red stack',
+        stack: '( -- n )'
+    },
+
+    '.': {
+        fn: (state) => {
+            if (state.stacks[0].length === 0) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on .'] });
+            }
+            const { stacks, value } = popFromStack(state.stacks, 0);
+            return updateState(state, {
+                stacks,
+                output: [...state.output, value.toString()]
+            });
+        },
+        doc: 'Pop and print the top item from the red stack',
+        stack: '( x -- )'
+    },
+
+    // Multi-stack operations
+    'push.red': {
+        fn: (state) => {
+            if (state.stacks[0].length === 0) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on push.red'] });
+            }
+            const { stacks, value } = popFromStack(state.stacks, 0);
+            return updateState(state, {
+                stacks: pushToStack(stacks, 0, value)
+            });
+        },
+        doc: 'Move top item from red stack to red stack (no-op, for consistency)',
+        stack: '( x -- x )'
+    },
+
+    'push.teal': {
+        fn: (state) => {
+            if (state.stacks[0].length === 0) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on push.teal'] });
+            }
+            const { stacks, value } = popFromStack(state.stacks, 0);
+            return updateState(state, {
+                stacks: pushToStack(stacks, 1, value)
+            });
+        },
+        doc: 'Move top item from red stack to teal stack',
+        stack: '( x -- )'
+    },
+
+    'push.blue': {
+        fn: (state) => {
+            if (state.stacks[0].length === 0) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on push.blue'] });
+            }
+            const { stacks, value } = popFromStack(state.stacks, 0);
+            return updateState(state, {
+                stacks: pushToStack(stacks, 2, value)
+            });
+        },
+        doc: 'Move top item from red stack to blue stack',
+        stack: '( x -- )'
+    },
+
+    'push.yellow': {
+        fn: (state) => {
+            if (state.stacks[0].length === 0) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on push.yellow'] });
+            }
+            const { stacks, value } = popFromStack(state.stacks, 0);
+            return updateState(state, {
+                stacks: pushToStack(stacks, 3, value)
+            });
+        },
+        doc: 'Move top item from red stack to yellow stack',
+        stack: '( x -- )'
+    },
+
+    'pop.red': {
+        fn: (state) => popAndPrint(state, 0),
+        doc: 'Pop and print top item from red stack',
+        stack: '( x -- )'
+    },
+
+    'pop.teal': {
+        fn: (state) => popAndPrint(state, 1),
+        doc: 'Pop and print top item from teal stack',
+        stack: '( x -- )'
+    },
+
+    'pop.blue': {
+        fn: (state) => popAndPrint(state, 2),
+        doc: 'Pop and print top item from blue stack',
+        stack: '( x -- )'
+    },
+
+    'pop.yellow': {
+        fn: (state) => popAndPrint(state, 3),
+        doc: 'Pop and print top item from yellow stack',
+        stack: '( x -- )'
+    },
+
+    // Utility words
+    'clear': {
+        fn: (state) => updateState(state, {
+            stacks: [[], [], [], []],
+            output: [...state.output, 'All stacks cleared']
+        }),
+        doc: 'Clear all four stacks',
+        stack: '( -- )'
+    },
+
+    // String operations
+    '."': {
+        fn: (state) => {
+            return updateState(state, {
+                stringMode: true,
+                currentString: '',
+                stringPushMode: false
+            });
+        },
+        doc: 'Begin a string literal that will be printed to output',
+        stack: '( -- )'
+    },
+
+    's"': {
+        fn: (state) => {
+            return updateState(state, {
+                stringMode: true,
+                currentString: '',
+                stringPushMode: true
+            });
+        },
+        doc: 'Begin a string literal that will be pushed to the stack',
+        stack: '( -- )'
+    },
+
+    'type': {
+        fn: (state) => {
+            if (state.stacks[0].length < 2) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on type'] });
+            }
+            const { stacks: stacks1, value: length } = popFromStack(state.stacks, 0);
+            const { stacks: stacks2, value: string } = popFromStack(stacks1, 0);
+            return updateState(state, {
+                stacks: stacks2,
+                output: [...state.output, string.toString()]
+            });
+        },
+        doc: 'Print a string from the stack (takes length and string address)',
+        stack: '( addr len -- )'
+    },
+
+    'count': {
+        fn: (state) => {
+            if (state.stacks[0].length === 0) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on count'] });
+            }
+            const { stacks, value } = popFromStack(state.stacks, 0);
+            if (typeof value === 'string') {
+                const newStacks = pushToStack(stacks, 0, value.length);
+                return updateState(state, {
+                    stacks: pushToStack(newStacks, 0, value)
+                });
+            } else {
+                const newStacks = pushToStack(stacks, 0, 0);
+                return updateState(state, {
+                    stacks: pushToStack(newStacks, 0, '')
+                });
+            }
+        },
+        doc: 'Extract string info from counted string (returns length and address)',
+        stack: '( c-addr -- c-addr u )'
+    },
+
+    'char+': {
+        fn: (state) => {
+            if (state.stacks[0].length < 2) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on char+'] });
+            }
+            const { stacks: stacks1, value: offset } = popFromStack(state.stacks, 0);
+            const { stacks: stacks2, value: string } = popFromStack(stacks1, 0);
+            if (typeof string === 'string') {
+                return updateState(state, {
+                    stacks: pushToStack(stacks2, 0, string + String.fromCharCode(offset))
+                });
+            } else {
+                return updateState(state, {
+                    stacks: pushToStack(stacks2, 0, string),
+                    output: [...state.output, 'Error: char+ requires string on stack']
+                });
+            }
+        },
+        doc: 'Add a character to a string using ASCII offset',
+        stack: '( c-addr1 char -- c-addr2 )'
+    },
+
+    'strlen': {
+        fn: (state) => {
+            if (state.stacks[0].length === 0) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on strlen'] });
+            }
+            const { stacks, value } = popFromStack(state.stacks, 0);
+            if (typeof value === 'string') {
+                return updateState(state, {
+                    stacks: pushToStack(stacks, 0, value.length)
+                });
+            } else {
+                return updateState(state, {
+                    stacks: pushToStack(stacks, 0, 0),
+                    output: [...state.output, 'Error: strlen requires string on stack']
+                });
+            }
+        },
+        doc: 'Get the length of a string on the stack',
+        stack: '( str -- len )'
+    },
+
+    'strcat': {
+        fn: (state) => {
+            if (state.stacks[0].length < 2) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on strcat'] });
+            }
+            const { stacks: stacks1, value: str2 } = popFromStack(state.stacks, 0);
+            const { stacks: stacks2, value: str1 } = popFromStack(stacks1, 0);
+            if (typeof str1 === 'string' && typeof str2 === 'string') {
+                return updateState(state, {
+                    stacks: pushToStack(stacks2, 0, str1 + str2)
+                });
+            } else {
+                return updateState(state, {
+                    stacks: pushToStack(pushToStack(stacks2, 0, str1), 0, str2),
+                    output: [...state.output, `Error: strcat requires two strings, got ${typeof str1} and ${typeof str2}`]
+                });
+            }
+        },
+        doc: 'Concatenate two strings from the stack',
+        stack: '( str1 str2 -- str3 )'
+    },
+
+    // Control flow
+    'if': {
+        fn: (state) => {
+            if (state.stacks[0].length === 0) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on if'] });
+            }
+            const { stacks, value } = popFromStack(state.stacks, 0);
+            if (value === 0) {
+                // Skip until THEN or ELSE
+                return updateState(state, {
+                    stacks,
+                    skipMode: true,
+                    skipCount: 0
+                });
+            }
+            return updateState(state, {
+                stacks
+            });
+        },
+        doc: 'Begin conditional execution - if top of stack is false (0), skip to THEN',
+        stack: '( flag -- )'
+    },
+
+    'else': {
+        fn: (state) => {
+            if (state.skipMode) {
+                return updateState(state, {
+                    skipCount: state.skipCount + 1
+                });
+            }
+            // Skip until THEN
+            return updateState(state, {
+                skipMode: true,
+                skipCount: 0
+            });
+        },
+        doc: 'Begin alternative branch in conditional execution',
+        stack: '( -- )'
+    },
+
+    'then': {
+        fn: (state) => {
+            if (state.skipMode && state.skipCount > 0) {
+                return updateState(state, {
+                    skipCount: state.skipCount - 1
+                });
+            } else if (state.skipMode) {
+                return updateState(state, {
+                    skipMode: false,
+                    skipCount: 0
+                });
+            }
+            return state;
+        },
+        doc: 'End conditional execution block',
+        stack: '( -- )'
+    },
+
+    'begin': {
+        fn: (state) => {
+            return updateState(state, {
+                loopStart: state.output.length
+            });
+        },
+        doc: 'Mark the beginning of a loop',
+        stack: '( -- )'
+    },
+
+    'until': {
+        fn: (state) => {
+            if (state.stacks[0].length === 0) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on until'] });
+            }
+            const { stacks, value } = popFromStack(state.stacks, 0);
+            if (value === 0) {
+                // Loop back to BEGIN
+                return updateState(state, {
+                    stacks,
+                    loopBack: true
+                });
+            }
+            return updateState(state, {
+                stacks
+            });
+        },
+        doc: 'End a loop - if top of stack is false (0), loop back to BEGIN',
+        stack: '( flag -- )'
+    },
+
+    // Help and documentation
+    'help': {
+        fn: (state) => {
+            const builtinWordNames = Object.keys(builtinWords);
+            const userWords = Array.from(state.dictionary.keys());
+            const allWords = [...builtinWordNames, ...userWords];
+            
+            const builtinList = builtinWordNames.map(name => {
+                const word = builtinWords[name];
+                return `${name} ${word.stack} - ${word.doc}`;
+            }).join('\n');
+            
+            const userList = userWords.length > 0 ? userWords.join(' ') : 'none';
+            
+            return updateState(state, {
+                output: [
+                    ...state.output,
+                    '=== 4-Stack Forth Interpreter Help ===',
+                    '',
+                    'Built-in words:',
+                    builtinList,
+                    '',
+                    'User defined words: ' + userList,
+                    '',
+                    'Total words: ' + allWords.length,
+                    '',
+                    'Use "doc <word>" to get detailed help for a specific word',
+                    'Use "words" to see just the word names'
+                ]
+            });
+        },
+        doc: 'Display comprehensive help information for all available words',
+        stack: '( -- )'
+    },
+
+    'doc': {
+        fn: (state) => {
+            if (state.stacks[0].length === 0) {
+                return updateState(state, { output: [...state.output, 'Error: Stack underflow on doc'] });
+            }
+            const { stacks, value: wordName } = popFromStack(state.stacks, 0);
+            
+            // Check built-in words first
+            if (builtinWords[wordName]) {
+                const word = builtinWords[wordName];
+                return updateState(state, {
+                    stacks,
+                    output: [
+                        ...state.output,
+                        `=== ${wordName} ===`,
+                        `Stack effect: ${word.stack}`,
+                        `Description: ${word.doc}`,
+                        `Type: Built-in word`
+                    ]
+                });
+            }
+            
+            // Check user-defined words
+            if (state.dictionary.has(wordName)) {
+                const definition = state.dictionary.get(wordName);
+                return updateState(state, {
+                    stacks,
+                    output: [
+                        ...state.output,
+                        `=== ${wordName} ===`,
+                        `Definition: ${definition.join(' ')}`,
+                        `Type: User-defined word`
+                    ]
+                });
+            }
+            
+            return updateState(state, {
+                stacks,
+                output: [...state.output, `Word '${wordName}' not found`]
+            });
+        },
+        doc: 'Display documentation for a specific word',
+        stack: '( "word" -- )'
+    },
+
+    'words': {
+        fn: (state) => {
+            const builtinWordNames = Object.keys(builtinWords);
+            const userWords = Array.from(state.dictionary.keys());
+            const allWords = [...builtinWordNames, ...userWords];
+            
+            const builtinList = builtinWordNames.join(' ');
+            const userList = userWords.length > 0 ? userWords.join(' ') : 'none';
+            
+            return updateState(state, {
+                output: [
+                    ...state.output,
+                    'Built-in words: ' + builtinList,
+                    'User defined words: ' + userList,
+                    'Total words: ' + allWords.length
+                ]
+            });
+        },
+        doc: 'List all available words (built-in and user-defined)',
+        stack: '( -- )'
+    }
+};
+
+// Parse and execute Forth input
+const parseAndExecute = (state, input) => {
+    const tokens = input.trim().split(/\s+/).filter(token => token.length > 0);
+    return tokens.reduce(executeToken, state);
+};
+
+// Execute a single token
+const executeToken = (state, token) => {
+    // Handle string mode
+    if (state.stringMode) {
+        // Check if this token contains the closing quote
+        const quoteIndex = token.indexOf('"');
+        if (quoteIndex !== -1) {
+            // Token contains closing quote
+            const beforeQuote = token.substring(0, quoteIndex);
+            const afterQuote = token.substring(quoteIndex + 1);
+            
+            // Add the part before the quote to the string
+            const finalString = state.currentString + (state.currentString ? ' ' : '') + beforeQuote;
+            
+            // End string mode and handle based on mode
+            let newState;
+            if (state.stringPushMode) {
+                // Push mode: add string to stack
+                newState = updateState(state, {
+                    stringMode: false,
+                    currentString: '',
+                    stringPushMode: false,
+                    stacks: pushToStack(state.stacks, 0, finalString)
+                });
+            } else {
+                // Print mode: add to output
+                newState = updateState(state, {
+                    stringMode: false,
+                    currentString: '',
+                    stringPushMode: false,
+                    output: [...state.output, finalString]
+                });
+            }
+            
+            // If there's content after the quote, process it
+            if (afterQuote.trim()) {
+                newState = ForthInterpreter.parseAndExecute(newState, afterQuote);
+            }
+            
+            return newState;
+        } else {
+            // Add to current string
+            return updateState(state, {
+                currentString: state.currentString + (state.currentString ? ' ' : '') + token
+            });
+        }
+    }
+
+    // Handle skip mode (for control flow)
+    if (state.skipMode) {
+        if (token === 'if' || token === 'begin') {
+            return updateState(state, {
+                skipCount: state.skipCount + 1
+            });
+        } else if (token === 'then' || token === 'until') {
+            if (state.skipCount > 0) {
+                return updateState(state, {
+                    skipCount: state.skipCount - 1
+                });
+            } else {
+                return updateState(state, {
+                    skipMode: false,
+                    skipCount: 0
+                });
+            }
+        } else if (token === 'else') {
+            if (state.skipCount === 0) {
+                // Switch to skipping ELSE branch
+                return updateState(state, {
+                    skipMode: true,
+                    skipCount: 0
+                });
+            }
+        }
+        // Skip this token
+        return state;
+    }
+
+    // Handle move operation state machine
+    if (state.moveInProgress) {
+        if (state.moveFromStack === null) {
+            // Expecting source stack number
+            const from = parseInt(token);
+            if (isNaN(from) || from < 1 || from > 4) {
+                return updateState(state, {
+                    moveInProgress: false,
+                    moveFromStack: null,
+                    output: [...state.output, 'Error: Invalid source stack. Must be 1-4']
+                });
+            }
+            if (state.stacks[from - 1].length === 0) {
+                return updateState(state, {
+                    moveInProgress: false,
+                    moveFromStack: null,
+                    output: [...state.output, `Error: Stack ${from} is empty`]
+                });
+            }
+            return updateState(state, {
+                moveInProgress: true,
+                moveFromStack: from - 1, // Convert to 0-based index
+                output: [...state.output, `Moving from stack ${from}. Enter destination stack (1-4):`]
+            });
+        } else {
+            // Expecting destination stack number
+            const to = parseInt(token);
+            if (isNaN(to) || to < 1 || to > 4) {
+                return updateState(state, {
+                    moveInProgress: false,
+                    moveFromStack: null,
+                    output: [...state.output, 'Error: Invalid destination stack. Must be 1-4']
+                });
+            }
+            const toIndex = to - 1; // Convert to 0-based index
+            const fromIndex = state.moveFromStack;
+            
+            // Reset move state
+            const newState = updateState(state, {
+                moveInProgress: false,
+                moveFromStack: null
+            });
+            
+            // Perform the move
+            const { stacks, value } = popFromStack(newState.stacks, fromIndex);
+            return updateState(newState, {
+                stacks: pushToStack(stacks, toIndex, value),
+                output: [...newState.output, `Moved ${value} from stack ${fromIndex + 1} to stack ${toIndex + 1}`]
+            });
+        }
+    }
+
+    // Handle word definition compilation
+    if (state.compilingWord !== null) {
+        if (token === ';') {
+            const newDictionary = new Map(state.dictionary);
+            newDictionary.set(state.compilingWord, [...state.compilingDefinition]);
+            return updateState(state, {
+                dictionary: newDictionary,
+                compilingWord: null,
+                compilingDefinition: [],
+                output: [...state.output, `Word '${state.compilingWord}' defined`]
+            });
+        }
+        
+        // If we're expecting a name, capture it
+        if (state.compilingWord === 'EXPECTING_NAME') {
+            return updateState(state, {
+                compilingWord: token,
+                compilingDefinition: []
+            });
+        }
+        
+        // Otherwise, add to definition
+        return updateState(state, {
+            compilingDefinition: [...state.compilingDefinition, token]
+        });
+    }
+
+    // Handle word definition start
+    if (token === ':') {
+        return updateState(state, {
+            compilingWord: 'EXPECTING_NAME',
+            compilingDefinition: []
+        });
+    }
+
+    // Check if it's a built-in word
+    if (builtinWords[token]) {
+        return builtinWords[token].fn(state);
+    }
+
+    // Check if it's a user-defined word
+    if (state.dictionary.has(token)) {
+        const definition = state.dictionary.get(token);
+        return definition.reduce(executeToken, state);
+    }
+
+    // Check if it's a number
+    const num = parseFloat(token);
+    if (!isNaN(num)) {
+        return updateState(state, {
+            stacks: pushToStack(state.stacks, 0, num)
+        });
+    }
+
+    // Check if it's a move command
+    if (token === 'move') {
+        return updateState(state, {
+            moveInProgress: true,
+            moveFromStack: null
+        });
+    }
+
+    // Unknown token
+    return updateState(state, {
+        output: [...state.output, `Error: Unknown word '${token}'`]
+    });
+};
+
+// Export for use in other modules or testing
+if (typeof module !== 'undefined' && module.exports) {
+    module.exports = {
+        createInitialState,
+        parseAndExecute,
+        executeToken,
+        builtinWords,
+        updateState,
+        pushToStack,
+        popFromStack
+    };
+} else if (typeof window !== 'undefined') {
+    // Browser environment
+    window.ForthInterpreter = {
+        createInitialState,
+        parseAndExecute,
+        executeToken,
+        builtinWords,
+        updateState,
+        pushToStack,
+        popFromStack
+    };
+}
diff --git a/forth/foreforthfourth/forth.js b/forth/foreforthfourth/forth.js
new file mode 100644
index 0000000..af133ea
--- /dev/null
+++ b/forth/foreforthfourth/forth.js
@@ -0,0 +1,1973 @@
+// Pure functional approach to Forth interpreter state
+const createInitialState = () => ({
+    stacks: [[], [], [], []],
+    dictionary: new Map(),
+    output: [],
+    compilingWord: null,
+    compilingDefinition: [],
+    stringMode: false,
+    currentString: '',
+    stringPushMode: false,
+    skipMode: false,
+    skipCount: 0,
+    loopStart: null,
+    loopBack: false,
+    focusedStack: 0,        // New: 0=Red, 1=Teal, 2=Blue, 3=Yellow
+    moveInProgress: false,  // For move operation
+    moveFromStack: null,    // For move operation
+    crossStackInProgress: false,  // For cross-stack operations
+    crossStackOperation: null,    // Type of cross-stack operation
+    crossStackData: null         // Data for cross-stack operation
+});
+
+// Pure function to update state
+const updateState = (state, updates) => ({
+    ...state,
+    ...updates
+});
+
+// Stack operations
+const pushToStack = (stacks, stackIndex, value) => {
+    const newStacks = stacks.map((stack, i) => 
+        i === stackIndex ? [...stack, value] : stack
+    );
+    return newStacks;
+};
+
+const popFromStack = (stacks, stackIndex) => {
+    const newStacks = stacks.map((stack, i) => 
+        i === stackIndex ? stack.slice(0, -1) : stack
+    );
+    const value = stacks[stackIndex][stacks[stackIndex].length - 1];
+    return { stacks: newStacks, value };
+};
+
+const moveBetweenStacks = (stacks, fromStack, toStack) => {
+    if (stacks[fromStack].length === 0) return { stacks, value: null };
+    const { stacks: newStacks, value } = popFromStack(stacks, fromStack);
+    return { stacks: pushToStack(newStacks, toStack, value), value };
+};
+
+const popAndPrint = (state, stackIndex) => {
+    if (state.stacks[stackIndex].length === 0) {
+        return updateState(state, { output: [...state.output, `Stack ${stackIndex + 1} is empty`] });
+    }
+    const { stacks, value } = popFromStack(state.stacks, stackIndex);
+    return updateState(state, {
+        stacks,
+        output: [...state.output, `Stack ${stackIndex + 1}: ${value}`]
+    });
+};
+
+// Helper function to get focused stack name
+const getFocusedStackName = (focusedStack) => {
+    const stackNames = ['Red (1)', 'Teal (2)', 'Blue (3)', 'Yellow (4)'];
+    return stackNames[focusedStack];
+};
+
+// Helper function to execute cross-stack operations
+const executeCrossStackOperation = (state, sourceIndex, targetIndex) => {
+    const operation = state.crossStackOperation;
+    const data = state.crossStackData;
+    
+    switch (operation) {
+        case 'dup':
+            return updateState(state, {
+                stacks: pushToStack(state.stacks, targetIndex, data.top),
+                output: [...state.output, `Duplicated ${data.top} from ${getFocusedStackName(sourceIndex)} to ${getFocusedStackName(targetIndex)}`]
+            });
+            
+        case 'over':
+            return updateState(state, {
+                stacks: pushToStack(state.stacks, targetIndex, data.second),
+                output: [...state.output, `Copied ${data.second} from ${getFocusedStackName(sourceIndex)} to ${getFocusedStackName(targetIndex)}`]
+            });
+            
+        case 'swap':
+            // Create new stacks array
+            const newStacks = state.stacks.map((stack, i) => [...stack]);
+            
+            // Get top 2 items from source stack (don't remove)
+            const sourceTop = newStacks[sourceIndex][newStacks[sourceIndex].length - 1];
+            const sourceSecond = newStacks[sourceIndex][newStacks[sourceIndex].length - 2];
+            
+            // Add source items to target stack in order: top, second
+            newStacks[targetIndex].push(sourceTop);
+            newStacks[targetIndex].push(sourceSecond);
+            
+            // Source stack remains unchanged (no popping/pushing)
+            
+            return updateState(state, {
+                stacks: newStacks,
+                output: [...state.output, `Copied top 2 items from ${getFocusedStackName(sourceIndex)} to ${getFocusedStackName(targetIndex)}`]
+            });
+            
+        case 'nip':
+            // Create new stacks array
+            const nipStacks = state.stacks.map((stack, i) => [...stack]);
+            
+            // Remove second item from source stack
+            nipStacks[sourceIndex].splice(-2, 1);
+            
+            // Add second item to target stack
+            nipStacks[targetIndex].push(data.second);
+            
+            return updateState(state, {
+                stacks: nipStacks,
+                output: [...state.output, `Moved second item ${data.second} from ${getFocusedStackName(sourceIndex)} to ${getFocusedStackName(targetIndex)}`]
+            });
+            
+        case 'tuck':
+            // Create new stacks array
+            const tuckStacks = state.stacks.map((stack, i) => [...stack]);
+            
+            // Remove top item from source stack
+            const tuckTop = tuckStacks[sourceIndex].pop();
+            
+            // Add items to target stack in tuck order: top, second, top
+            tuckStacks[targetIndex].push(tuckTop);
+            tuckStacks[targetIndex].push(data.second);
+            tuckStacks[targetIndex].push(tuckTop);
+            
+            // Don't add top item back to source stack - tuck removes it
+            
+            return updateState(state, {
+                stacks: tuckStacks,
+                output: [...state.output, `Tucked ${tuckTop} under ${data.second} on ${getFocusedStackName(targetIndex)}`]
+            });
+            
+        case 'rot':
+            // Create new stacks array
+            const rotStacks = state.stacks.map((stack, i) => [...stack]);
+            
+            // Remove top 3 items from source stack
+            // For stack [1, 2, 3], top=3, second=2, third=1
+            const rotTop = rotStacks[sourceIndex].pop();      // 3
+            const rotSecond = rotStacks[sourceIndex].pop();   // 2
+            const rotThird = rotStacks[sourceIndex].pop();    // 1
+            
+            // Add 3 items to target stack in rotated order: third, first, second
+            rotStacks[targetIndex].push(rotThird);
+            rotStacks[targetIndex].push(rotTop);
+            rotStacks[targetIndex].push(rotSecond);
+            
+            // Add 3 items back to source stack in rotated order: third, first, second
+            rotStacks[sourceIndex].push(rotThird);
+            rotStacks[sourceIndex].push(rotTop);
+            rotStacks[sourceIndex].push(rotSecond);
+            
+            return updateState(state, {
+                stacks: rotStacks,
+                output: [...state.output, `Rotated top 3 items between ${getFocusedStackName(sourceIndex)} and ${getFocusedStackName(targetIndex)}`]
+            });
+            
+        case '2dup':
+            // Create new stacks array
+            const dup2Stacks = state.stacks.map((stack, i) => [...stack]);
+            
+            // Get top 2 items from source stack (don't remove)
+            const dup2Top = dup2Stacks[sourceIndex][dup2Stacks[sourceIndex].length - 1];
+            const dup2Second = dup2Stacks[sourceIndex][dup2Stacks[sourceIndex].length - 2];
+            
+            // Add 2 items to target stack (preserve order: second, top)
+            dup2Stacks[targetIndex].push(dup2Second);
+            dup2Stacks[targetIndex].push(dup2Top);
+            
+            // Source stack remains unchanged (no popping/pushing)
+            
+            return updateState(state, {
+                stacks: dup2Stacks,
+                output: [...state.output, `Duplicated top 2 items from ${getFocusedStackName(sourceIndex)} to ${getFocusedStackName(targetIndex)}`]
+            });
+            
+        case '2over':
+            // Create new stacks array
+            const over2Stacks = state.stacks.map((stack, i) => [...stack]);
+            
+            // Get second pair of items from source stack (don't remove)
+            // For stack [10, 20, 30, 40], second pair is [20, 30]
+            const over2Second = over2Stacks[sourceIndex][over2Stacks[sourceIndex].length - 3];
+            const over2Third = over2Stacks[sourceIndex][over2Stacks[sourceIndex].length - 2];
+            
+            // Add 2 items to target stack (preserve order: third, second)
+            over2Stacks[targetIndex].push(over2Third);
+            over2Stacks[targetIndex].push(over2Second);
+            
+            return updateState(state, {
+                stacks: over2Stacks,
+                output: [...state.output, `Copied second pair of items from ${getFocusedStackName(sourceIndex)} to ${getFocusedStackName(targetIndex)}`]
+            });
+            
+        case '2swap':
+            // Create new stacks array
+            const swap2Stacks = state.stacks.map((stack, i) => [...stack]);
+            
+            // Get top 4 items from source stack (don't remove)
+            const sourceItems = [
+                swap2Stacks[sourceIndex][swap2Stacks[sourceIndex].length - 1], // top
+                swap2Stacks[sourceIndex][swap2Stacks[sourceIndex].length - 2], // second
+                swap2Stacks[sourceIndex][swap2Stacks[sourceIndex].length - 3], // third
+                swap2Stacks[sourceIndex][swap2Stacks[sourceIndex].length - 4]  // fourth
+            ];
+            
+            // Add source items to target stack in order: fourth, third, second, top
+            swap2Stacks[targetIndex].push(sourceItems[3]); // fourth
+            swap2Stacks[targetIndex].push(sourceItems[2]); // third
+            swap2Stacks[targetIndex].push(sourceItems[1]); // second
+            swap2Stacks[targetIndex].push(sourceItems[0]); // top
+            
+            // Source stack remains unchanged (no popping/pushing)
+            
+            return updateState(state, {
+                stacks: swap2Stacks,
+                output: [...state.output, `Copied top 2 pairs of items from ${getFocusedStackName(sourceIndex)} to ${getFocusedStackName(targetIndex)}`]
+            });
+            
+        default:
+            return updateState(state, {
+                output: [...state.output, `Error: Unknown cross-stack operation: ${operation}`]
+            });
+    }
+};
+
+// Built-in words with documentation
+const builtinWords = {
+    // Stack focus commands
+    'focus.red': {
+        fn: (state) => updateState(state, { 
+            focusedStack: 0,
+            output: [...state.output, 'Focus set to Red stack (Stack 1)']
+        }),
+        doc: 'Set focus to Red stack (Stack 1)',
+        stack: '( -- )'
+    },
+    'focus.1': {
+        fn: (state) => updateState(state, { 
+            focusedStack: 0,
+            output: [...state.output, 'Focus set to Red stack (Stack 1)']
+        }),
+        doc: 'Set focus to Red stack (Stack 1)',
+        stack: '( -- )'
+    },
+    'focus.teal': {
+        fn: (state) => updateState(state, { 
+            focusedStack: 1,
+            output: [...state.output, 'Focus set to Teal stack (Stack 2)']
+        }),
+        doc: 'Set focus to Teal stack (Stack 2)',
+        stack: '( -- )'
+    },
+    'focus.2': {
+        fn: (state) => updateState(state, { 
+            focusedStack: 1,
+            output: [...state.output, 'Focus set to Teal stack (Stack 2)']
+        }),
+        doc: 'Set focus to Teal stack (Stack 2)',
+        stack: '( -- )'
+    },
+    'focus.blue': {
+        fn: (state) => updateState(state, { 
+            focusedStack: 2,
+            output: [...state.output, 'Focus set to Blue stack (Stack 3)']
+        }),
+        doc: 'Set focus to Blue stack (Stack 3)',
+        stack: '( -- )'
+    },
+    'focus.3': {
+        fn: (state) => updateState(state, { 
+            focusedStack: 2,
+            output: [...state.output, 'Focus set to Blue stack (Stack 3)']
+        }),
+        doc: 'Set focus to Blue stack (Stack 3)',
+        stack: '( -- )'
+    },
+    'focus.yellow': {
+        fn: (state) => updateState(state, { 
+            focusedStack: 3,
+            output: [...state.output, 'Focus set to Yellow stack (Stack 4)']
+        }),
+        doc: 'Set focus to Yellow stack (Stack 4)',
+        stack: '( -- )'
+    },
+    'focus.4': {
+        fn: (state) => updateState(state, { 
+            focusedStack: 3,
+            output: [...state.output, 'Focus set to Yellow stack (Stack 4)']
+        }),
+        doc: 'Set focus to Yellow stack (Stack 4)',
+        stack: '( -- )'
+    },
+    'focus.show': {
+        fn: (state) => {
+            const stackNames = ['Red (1)', 'Teal (2)', 'Blue (3)', 'Yellow (4)'];
+            return updateState(state, { 
+                output: [...state.output, `Currently focused on: ${stackNames[state.focusedStack]}`]
+            });
+        },
+        doc: 'Show which stack is currently focused',
+        stack: '( -- )'
+    },
+    
+    // Stack manipulation for focused stack
+    'dup': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                const stackNames = ['Red (1)', 'Teal (2)', 'Blue (3)', 'Yellow (4)'];
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on dup - ${stackNames[state.focusedStack]} is empty. Use numbers or strings to add items first.`] 
+                });
+            }
+            const top = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 1];
+            return updateState(state, {
+                stacks: pushToStack(state.stacks, state.focusedStack, top)
+            });
+        },
+        doc: 'Duplicate the top item on the stack',
+        stack: '( x -- x x )'
+    },
+
+    'swap': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 2) {
+                const stackNames = ['Red (1)', 'Teal (2)', 'Blue (3)', 'Yellow (4)'];
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on swap - ${stackNames[state.focusedStack]} needs 2 items, but has ${state.stacks[state.focusedStack].length}. Add more items first.`] 
+                });
+            }
+            const newStacks = state.stacks.map((stack, i) => [...stack]);
+            const a = newStacks[state.focusedStack].pop();
+            const b = newStacks[state.focusedStack].pop();
+            newStacks[state.focusedStack].push(a);
+            newStacks[state.focusedStack].push(b);
+            return updateState(state, { stacks: newStacks });
+        },
+        doc: 'Exchange the top two items on the stack',
+        stack: '( x1 x2 -- x2 x1 )'
+    },
+
+    'drop': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on drop - ${getFocusedStackName(state.focusedStack)} is empty. Nothing to remove.`] 
+                });
+            }
+            const { stacks } = popFromStack(state.stacks, state.focusedStack);
+            return updateState(state, { stacks });
+        },
+        doc: 'Remove the top item from the stack',
+        stack: '( x -- )'
+    },
+
+    '2drop': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 2) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on 2drop - Stack 1 (Red) needs 2 items, but has ${state.stacks[state.focusedStack].length}. Add more items first.`] 
+                });
+            }
+            const { stacks: stacks1 } = popFromStack(state.stacks, state.focusedStack);
+            const { stacks } = popFromStack(stacks1, 0);
+            return updateState(state, { stacks });
+        },
+        doc: 'Remove the top two items from the stack',
+        stack: '( x1 x2 -- )'
+    },
+
+    'over': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 2) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on over - Stack 1 (Red) needs 2 items, but has ${state.stacks[state.focusedStack].length}. Add more items first.`] 
+                });
+            }
+            const second = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 2];
+            return updateState(state, {
+                stacks: pushToStack(state.stacks, state.focusedStack, second)
+            });
+        },
+        doc: 'Copy the second item on the stack to the top',
+        stack: '( x1 x2 -- x1 x2 x1 )'
+    },
+
+    '2dup': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 2) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on 2dup - Stack 1 (Red) needs 2 items, but has ${state.stacks[state.focusedStack].length}. Add more items first.`] 
+                });
+            }
+            const top = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 1];
+            const second = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 2];
+            const newStacks = pushToStack(state.stacks, state.focusedStack, second);
+            return updateState(state, {
+                stacks: pushToStack(newStacks, state.focusedStack, top)
+            });
+        },
+        doc: 'Duplicate the top two items on the stack',
+        stack: '( x1 x2 -- x1 x2 x1 x2 )'
+    },
+
+    'rot': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 3) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on rot - Stack 1 (Red) needs 3 items, but has ${state.stacks[state.focusedStack].length}. Add more items first.`] 
+                });
+            }
+            const newStacks = state.stacks.map((stack, i) => [...stack]);
+            const a = newStacks[state.focusedStack].pop();
+            const b = newStacks[state.focusedStack].pop();
+            const c = newStacks[state.focusedStack].pop();
+            newStacks[state.focusedStack].push(b);
+            newStacks[state.focusedStack].push(a);
+            newStacks[state.focusedStack].push(c);
+            return updateState(state, { stacks: newStacks });
+        },
+        doc: 'Rotate the top three items on the stack',
+        stack: '( x1 x2 x3 -- x2 x3 x1 )'
+    },
+
+    '-rot': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 3) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on -rot - Stack 1 (Red) needs 3 items, but has ${state.stacks[state.focusedStack].length}. Add more items first.`] 
+                });
+            }
+            const newStacks = state.stacks.map((stack, i) => [...stack]);
+            const a = newStacks[state.focusedStack].pop();
+            const b = newStacks[state.focusedStack].pop();
+            const c = newStacks[state.focusedStack].pop();
+            newStacks[state.focusedStack].push(a);
+            newStacks[state.focusedStack].push(c);
+            newStacks[state.focusedStack].push(b);
+            return updateState(state, { stacks: newStacks });
+        },
+        doc: 'Rotate the top three items on the stack (reverse of rot)',
+        stack: '( x1 x2 x3 -- x3 x1 x2 )'
+    },
+
+    // Cross-stack operations
+    'dup.stacks': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on dup.stacks - ${getFocusedStackName(state.focusedStack)} is empty. Add an item first.`] 
+                });
+            }
+            return updateState(state, {
+                crossStackInProgress: true,
+                crossStackOperation: 'dup',
+                crossStackData: { top: state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 1] },
+                output: [...state.output, `Enter destination stack (1-4) to duplicate to:`]
+            });
+        },
+        doc: 'Duplicate top item from focused stack to another stack',
+        stack: '( x -- x x ) (interactive)'
+    },
+
+    'over.stacks': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 2) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on over.stacks - ${getFocusedStackName(state.focusedStack)} needs 2 items, but has ${state.stacks[state.focusedStack].length}. Add more items first.`] 
+                });
+            }
+            const second = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 2];
+            return updateState(state, {
+                crossStackInProgress: true,
+                crossStackOperation: 'over',
+                crossStackData: { second },
+                output: [...state.output, `Enter destination stack (1-4) to copy second item to:`]
+            });
+        },
+        doc: 'Copy second item from focused stack to another stack',
+        stack: '( x1 x2 -- x1 x2 x1 ) (interactive)'
+    },
+
+    'swap.stacks': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 2) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on swap.stacks - ${getFocusedStackName(state.focusedStack)} needs 2 items, but has ${state.stacks[state.focusedStack].length}. Add more items first.`] 
+                });
+            }
+            const top = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 1];
+            const second = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 2];
+            return updateState(state, {
+                crossStackInProgress: true,
+                crossStackOperation: 'swap',
+                crossStackData: { top, second },
+                output: [...state.output, `Enter target stack (1-4) to swap with:`]
+            });
+        },
+        doc: 'Swap top items between focused stack and another stack',
+        stack: '( x1 x2 -- x2 x1 ) (interactive)'
+    },
+
+    'nip.stacks': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 2) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on nip.stacks - ${getFocusedStackName(state.focusedStack)} needs 2 items, but has ${state.stacks[state.focusedStack].length}. Add more items first.`] 
+                });
+            }
+            const top = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 1];
+            const second = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 2];
+            return updateState(state, {
+                crossStackInProgress: true,
+                crossStackOperation: 'nip',
+                crossStackData: { top, second },
+                output: [...state.output, `Enter destination stack (1-4) to move second item to:`]
+            });
+        },
+        doc: 'Move second item from focused stack to another stack (remove from source)',
+        stack: '( x1 x2 -- x1 ) (interactive)'
+    },
+
+    'tuck.stacks': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 2) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on tuck.stacks - ${getFocusedStackName(state.focusedStack)} needs 2 items, but has ${state.stacks[state.focusedStack].length}. Add more items first.`] 
+                });
+            }
+            const top = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 1];
+            const second = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 2];
+            return updateState(state, {
+                crossStackInProgress: true,
+                crossStackOperation: 'tuck',
+                crossStackData: { top, second },
+                output: [...state.output, `Enter destination stack (1-4) to tuck top item under second item:`]
+            });
+        },
+        doc: 'Tuck top item under second item on another stack',
+        stack: '( x1 x2 -- x2 x1 x2 ) (interactive)'
+    },
+
+    'rot.stacks': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 3) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on rot.stacks - ${getFocusedStackName(state.focusedStack)} needs 3 items, but has ${state.stacks[state.focusedStack].length}. Add more items first.`] 
+                });
+            }
+            const first = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 3];
+            const second = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 2];
+            const third = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 1];
+            return updateState(state, {
+                crossStackInProgress: true,
+                crossStackOperation: 'rot',
+                crossStackData: { first, second, third },
+                output: [...state.output, `Enter destination stack (1-4) to rotate with:`]
+            });
+        },
+        doc: 'Rotate top 3 items between focused stack and another stack',
+        stack: '( x1 x2 x3 -- x2 x3 x1 ) (interactive)'
+    },
+
+    '2dup.stacks': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 2) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on 2dup.stacks - ${getFocusedStackName(state.focusedStack)} needs 2 items, but has ${state.stacks[state.focusedStack].length}. Add more items first.`] 
+                });
+            }
+            const top = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 1];
+            const second = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 2];
+            return updateState(state, {
+                crossStackInProgress: true,
+                crossStackOperation: '2dup',
+                crossStackData: { top, second },
+                output: [...state.output, `Enter destination stack (1-4) to duplicate top 2 items to:`]
+            });
+        },
+        doc: 'Duplicate top 2 items from focused stack to another stack',
+        stack: '( x1 x2 -- x1 x2 x1 x2 ) (interactive)'
+    },
+
+    '2over.stacks': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 4) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on 2over.stacks - ${getFocusedStackName(state.focusedStack)} needs 4 items, but has ${state.stacks[state.focusedStack].length}. Add more items first.`] 
+                });
+            }
+            return updateState(state, {
+                crossStackInProgress: true,
+                crossStackOperation: '2over',
+                crossStackData: {},
+                output: [...state.output, `Enter destination stack (1-4) to copy second pair of items to:`]
+            });
+        },
+        doc: 'Copy second pair of items from focused stack to another stack',
+        stack: '( x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2 ) (interactive)'
+    },
+
+    '2swap.stacks': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 4) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on 2swap.stacks - ${getFocusedStackName(state.focusedStack)} needs 4 items, but has ${state.stacks[state.focusedStack].length}. Add more items first.`] 
+                });
+            }
+            const first = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 4];
+            const second = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 3];
+            const third = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 2];
+            const fourth = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 1];
+            return updateState(state, {
+                crossStackInProgress: true,
+                crossStackOperation: '2swap',
+                crossStackData: { first, second, third, fourth },
+                output: [...state.output, `Enter destination stack (1-4) to swap top 2 pairs with:`]
+            });
+        },
+        doc: 'Swap top 2 pairs of items between focused stack and another stack',
+        stack: '( x1 x2 x3 x4 -- x3 x4 x1 x2 ) (interactive)'
+    },
+
+    // Arithmetic operations on stack 1
+    '+': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 2) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on + - ${getFocusedStackName(state.focusedStack)} needs 2 numbers, but has ${state.stacks[state.focusedStack].length}. Add more numbers first.`] 
+                });
+            }
+            const { stacks: stacks1, value: b } = popFromStack(state.stacks, state.focusedStack);
+            const { stacks: stacks2, value: a } = popFromStack(stacks1, state.focusedStack);
+            return updateState(state, {
+                stacks: pushToStack(stacks2, state.focusedStack, a + b)
+            });
+        },
+        doc: 'Add the top two numbers on the stack',
+        stack: '( n1 n2 -- n3 )'
+    },
+
+    '-': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 2) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on - - Stack 1 (Red) needs 2 numbers, but has ${state.stacks[state.focusedStack].length}. Add more numbers first.`] 
+                });
+            }
+            const { stacks: stacks1, value: b } = popFromStack(state.stacks, state.focusedStack);
+            const { stacks: stacks2, value: a } = popFromStack(stacks1, state.focusedStack);
+            return updateState(state, {
+                stacks: pushToStack(stacks2, state.focusedStack, a - b)
+            });
+        },
+        doc: 'Subtract the top number from the second number on the stack',
+        stack: '( n1 n2 -- n3 )'
+    },
+
+    '*': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 2) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on * - Stack 1 (Red) needs 2 numbers, but has ${state.stacks[state.focusedStack].length}. Add more numbers first.`] 
+                });
+            }
+            const { stacks: stacks1, value: b } = popFromStack(state.stacks, state.focusedStack);
+            const { stacks: stacks2, value: a } = popFromStack(stacks1, state.focusedStack);
+            return updateState(state, {
+                stacks: pushToStack(stacks2, state.focusedStack, a * b)
+            });
+        },
+        doc: 'Multiply the top two numbers on the stack',
+        stack: '( n1 n2 -- n3 )'
+    },
+
+    '/': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 2) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on / - Stack 1 (Red) needs 2 numbers, but has ${state.stacks[state.focusedStack].length}. Add more numbers first.`] 
+                });
+            }
+            const { stacks: stacks1, value: b } = popFromStack(state.stacks, state.focusedStack);
+            const { stacks: stacks2, value: a } = popFromStack(stacks1, state.focusedStack);
+            if (b === 0) {
+                return updateState(state, { 
+                    stacks: pushToStack(pushToStack(stacks2, state.focusedStack, a), state.focusedStack, b),
+                    output: [...state.output, 'Error: Division by zero - Cannot divide by zero. Check your divisor before dividing.'] 
+                });
+            }
+            return updateState(state, {
+                stacks: pushToStack(stacks2, state.focusedStack, Math.floor(a / b))
+            });
+        },
+        doc: 'Divide the second number by the top number on the stack (integer division)',
+        stack: '( n1 n2 -- n3 )'
+    },
+
+    'mod': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 2) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on mod - Stack 1 (Red) needs 2 numbers, but has ${state.stacks[state.focusedStack].length}. Add more numbers first.`] 
+                });
+            }
+            const { stacks: stacks1, value: b } = popFromStack(state.stacks, state.focusedStack);
+            const { stacks: stacks2, value: a } = popFromStack(stacks1, state.focusedStack);
+            if (b === 0) {
+                return updateState(state, { 
+                    stacks: pushToStack(pushToStack(stacks2, state.focusedStack, a), state.focusedStack, b),
+                    output: [...state.output, 'Error: Modulo by zero - Cannot calculate remainder when dividing by zero. Check your divisor first.'] 
+                });
+            }
+            return updateState(state, {
+                stacks: pushToStack(stacks2, state.focusedStack, a % b)
+            });
+        },
+        doc: 'Return the remainder of dividing the second number by the top number',
+        stack: '( n1 n2 -- n3 )'
+    },
+
+    'abs': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on abs - Stack 1 (Red) is empty. Add a number first.`] 
+                });
+            }
+            const { stacks, value } = popFromStack(state.stacks, state.focusedStack);
+            return updateState(state, {
+                stacks: pushToStack(stacks, state.focusedStack, Math.abs(value))
+            });
+        },
+        doc: 'Return the absolute value of the top number on the stack',
+        stack: '( n -- |n| )'
+    },
+
+    'negate': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on negate - ${getFocusedStackName(state.focusedStack)} is empty. Add a number first.`] 
+                });
+            }
+            const { stacks, value } = popFromStack(state.stacks, state.focusedStack);
+            return updateState(state, {
+                stacks: pushToStack(stacks, state.focusedStack, -value)
+            });
+        },
+        doc: 'Return the negative of the top number on the stack',
+        stack: '( n -- -n )'
+    },
+
+    'min': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 2) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on min - Stack 1 (Red) needs 2 numbers, but has ${state.stacks[state.focusedStack].length}. Add more numbers first.`] 
+                });
+            }
+            const { stacks: stacks1, value: b } = popFromStack(state.stacks, state.focusedStack);
+            const { stacks: stacks2, value: a } = popFromStack(stacks1, state.focusedStack);
+            return updateState(state, {
+                stacks: pushToStack(stacks2, state.focusedStack, Math.min(a, b))
+            });
+        },
+        doc: 'Return the smaller of the top two numbers on the stack',
+        stack: '( n1 n2 -- n3 )'
+    },
+
+    'max': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 2) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on max - Stack 1 (Red) needs 2 numbers, but has ${state.stacks[state.focusedStack].length}. Add more numbers first.`] 
+                });
+            }
+            const { stacks: stacks1, value: b } = popFromStack(state.stacks, state.focusedStack);
+            const { stacks: stacks2, value: a } = popFromStack(stacks1, state.focusedStack);
+            return updateState(state, {
+                stacks: pushToStack(stacks2, state.focusedStack, Math.max(a, b))
+            });
+        },
+        doc: 'Return the larger of the top two numbers on the stack',
+        stack: '( n1 n2 -- n3 )'
+    },
+
+    // Comparison and logic
+    '=': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 2) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on = - Stack 1 (Red) needs 2 values, but has ${state.stacks[state.focusedStack].length}. Add more values first.`] 
+                });
+            }
+            const { stacks: stacks1, value: b } = popFromStack(state.stacks, state.focusedStack);
+            const { stacks: stacks2, value: a } = popFromStack(stacks1, state.focusedStack);
+            return updateState(state, {
+                stacks: pushToStack(stacks2, state.focusedStack, a === b ? -1 : 0)
+            });
+        },
+        doc: 'Return true (-1) if the top two numbers are equal, false (0) otherwise',
+        stack: '( n1 n2 -- flag )'
+    },
+
+    '<': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 2) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on < - Stack 1 (Red) needs 2 values, but has ${state.stacks[state.focusedStack].length}. Add more values first.`] 
+                });
+            }
+            const { stacks: stacks1, value: b } = popFromStack(state.stacks, state.focusedStack);
+            const { stacks: stacks2, value: a } = popFromStack(stacks1, state.focusedStack);
+            return updateState(state, {
+                stacks: pushToStack(stacks2, state.focusedStack, a < b ? -1 : 0)
+            });
+        },
+        doc: 'Return true (-1) if the second number is less than the top number, false (0) otherwise',
+        stack: '( n1 n2 -- flag )'
+    },
+
+    '>': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 2) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on > - Stack 1 (Red) needs 2 values, but has ${state.stacks[state.focusedStack].length}. Add more values first.`] 
+                });
+            }
+            const { stacks: stacks1, value: b } = popFromStack(state.stacks, state.focusedStack);
+            const { stacks: stacks2, value: a } = popFromStack(stacks1, state.focusedStack);
+            return updateState(state, {
+                stacks: pushToStack(stacks2, state.focusedStack, a > b ? -1 : 0)
+            });
+        },
+        doc: 'Return true (-1) if the second number is greater than the top number, false (0) otherwise',
+        stack: '( n1 n2 -- flag )'
+    },
+
+    'and': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 2) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on and - Stack 1 (Red) needs 2 values, but has ${state.stacks[state.focusedStack].length}. Add more values first.`] 
+                });
+            }
+            const { stacks: stacks1, value: b } = popFromStack(state.stacks, state.focusedStack);
+            const { stacks: stacks2, value: a } = popFromStack(stacks1, state.focusedStack);
+            return updateState(state, {
+                stacks: pushToStack(stacks2, state.focusedStack, a && b ? -1 : 0)
+            });
+        },
+        doc: 'Return true (-1) if both top two values are true, false (0) otherwise',
+        stack: '( x1 x2 -- flag )'
+    },
+
+    'or': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 2) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on or - Stack 1 (Red) needs 2 values, but has ${state.stacks[state.focusedStack].length}. Add more values first.`] 
+                });
+            }
+            const { stacks: stacks1, value: b } = popFromStack(state.stacks, state.focusedStack);
+            const { stacks: stacks2, value: a } = popFromStack(stacks1, state.focusedStack);
+            return updateState(state, {
+                stacks: pushToStack(stacks2, state.focusedStack, a || b ? -1 : 0)
+            });
+        },
+        doc: 'Return true (-1) if either of the top two values is true, false (0) otherwise',
+        stack: '( x1 x2 -- flag )'
+    },
+
+    'not': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on not - Stack 1 (Red) is empty. Add a value first.`] 
+                });
+            }
+            const { stacks, value } = popFromStack(state.stacks, state.focusedStack);
+            return updateState(state, {
+                stacks: pushToStack(stacks, state.focusedStack, value ? 0 : -1)
+            });
+        },
+        doc: 'Return true (-1) if the top value is false, false (0) if it is true',
+        stack: '( x -- flag )'
+    },
+
+    // Stack inspection
+    '.s': {
+        fn: (state) => {
+            const stackStr = state.stacks[state.focusedStack].length === 0 ? 'empty' : state.stacks[0].join(' ');
+            return updateState(state, {
+                output: [...state.output, `Stack 1 (red): ${stackStr}`]
+            });
+        },
+        doc: 'Display the contents of the red stack (non-destructive)',
+        stack: '( -- )'
+    },
+
+    'depth': {
+        fn: (state) => {
+            return updateState(state, {
+                stacks: pushToStack(state.stacks, state.focusedStack, state.stacks[state.focusedStack].length)
+            });
+        },
+        doc: 'Push the number of items on the red stack',
+        stack: '( -- n )'
+    },
+
+    '.': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on . - Stack 1 (Red) is empty. Nothing to print.`] 
+                });
+            }
+            const { stacks, value } = popFromStack(state.stacks, state.focusedStack);
+            return updateState(state, {
+                stacks,
+                output: [...state.output, value.toString()]
+            });
+        },
+        doc: 'Pop and print the top item from the red stack',
+        stack: '( x -- )'
+    },
+
+    // Multi-stack operations
+    'move.red': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on move.red - ${getFocusedStackName(state.focusedStack)} is empty. Nothing to move.`] 
+                });
+            }
+            const { stacks, value } = popFromStack(state.stacks, state.focusedStack);
+            return updateState(state, {
+                stacks: pushToStack(stacks, 0, value)
+            });
+        },
+        doc: 'Move top item from focused stack to red stack (removes from focused stack)',
+        stack: '( x -- )'
+    },
+
+    'move.1': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on move.1 - ${getFocusedStackName(state.focusedStack)} is empty. Nothing to move.`] 
+                });
+            }
+            const { stacks, value } = popFromStack(state.stacks, state.focusedStack);
+            return updateState(state, {
+                stacks: pushToStack(stacks, 0, value)
+            });
+        },
+        doc: 'Move top item from focused stack to red stack (Stack 1) (removes from focused stack)',
+        stack: '( x -- )'
+    },
+
+    'move.teal': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on move.teal - ${getFocusedStackName(state.focusedStack)} is empty. Nothing to move.`] 
+                });
+            }
+            const { stacks, value } = popFromStack(state.stacks, state.focusedStack);
+            return updateState(state, {
+                stacks: pushToStack(stacks, 1, value)
+            });
+        },
+        doc: 'Move top item from focused stack to teal stack (removes from focused stack)',
+        stack: '( x -- )'
+    },
+
+    'move.2': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on move.2 - ${getFocusedStackName(state.focusedStack)} is empty. Nothing to move.`] 
+                });
+            }
+            const { stacks, value } = popFromStack(state.stacks, state.focusedStack);
+            return updateState(state, {
+                stacks: pushToStack(stacks, 1, value)
+            });
+        },
+        doc: 'Move top item from focused stack to teal stack (Stack 2) (removes from focused stack)',
+        stack: '( x -- )'
+    },
+
+    'move.blue': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on move.blue - ${getFocusedStackName(state.focusedStack)} is empty. Nothing to move.`] 
+                });
+            }
+            const { stacks, value } = popFromStack(state.stacks, state.focusedStack);
+            return updateState(state, {
+                stacks: pushToStack(stacks, 2, value)
+            });
+        },
+        doc: 'Move top item from focused stack to blue stack (removes from focused stack)',
+        stack: '( x -- )'
+    },
+
+    'move.3': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on move.3 - ${getFocusedStackName(state.focusedStack)} is empty. Nothing to move.`] 
+                });
+            }
+            const { stacks, value } = popFromStack(state.stacks, state.focusedStack);
+            return updateState(state, {
+                stacks: pushToStack(stacks, 2, value)
+            });
+        },
+        doc: 'Move top item from focused stack to blue stack (Stack 3) (removes from focused stack)',
+        stack: '( x -- )'
+    },
+
+    'move.yellow': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on move.yellow - ${getFocusedStackName(state.focusedStack)} is empty. Nothing to move.`] 
+                });
+            }
+            const { stacks, value } = popFromStack(state.stacks, state.focusedStack);
+            return updateState(state, {
+                stacks: pushToStack(stacks, 3, value)
+            });
+        },
+        doc: 'Move top item from focused stack to yellow stack (removes from focused stack)',
+        stack: '( x -- )'
+    },
+
+    'move.4': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on move.4 - ${getFocusedStackName(state.focusedStack)} is empty. Nothing to move.`] 
+                });
+            }
+            const { stacks, value } = popFromStack(state.stacks, state.focusedStack);
+            return updateState(state, {
+                stacks: pushToStack(stacks, 3, value)
+            });
+        },
+        doc: 'Move top item from focused stack to yellow stack (Stack 4) (removes from focused stack)',
+        stack: '( x -- )'
+    },
+
+    // Copy operations (keep item in source stack)
+    'copy.red': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on copy.red - ${getFocusedStackName(state.focusedStack)} is empty. Nothing to copy.`] 
+                });
+            }
+            const top = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 1];
+            return updateState(state, {
+                stacks: pushToStack(state.stacks, 0, top)
+            });
+        },
+        doc: 'Copy top item from focused stack to red stack (keeps item on focused stack)',
+        stack: '( x -- x )'
+    },
+
+    'copy.1': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on copy.1 - ${getFocusedStackName(state.focusedStack)} is empty. Nothing to copy.`] 
+                });
+            }
+            const top = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 1];
+            return updateState(state, {
+                stacks: pushToStack(state.stacks, 0, top)
+            });
+        },
+        doc: 'Copy top item from focused stack to red stack (Stack 1) (keeps item on focused stack)',
+        stack: '( x -- x )'
+    },
+
+    'copy.teal': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on copy.teal - ${getFocusedStackName(state.focusedStack)} is empty. Nothing to copy.`] 
+                });
+            }
+            const top = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 1];
+            return updateState(state, {
+                stacks: pushToStack(state.stacks, 1, top)
+            });
+        },
+        doc: 'Copy top item from focused stack to teal stack (keeps item on focused stack)',
+        stack: '( x -- x )'
+    },
+
+    'copy.2': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on copy.2 - ${getFocusedStackName(state.focusedStack)} is empty. Nothing to copy.`] 
+                });
+            }
+            const top = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 1];
+            return updateState(state, {
+                stacks: pushToStack(state.stacks, 1, top)
+            });
+        },
+        doc: 'Copy top item from focused stack to teal stack (Stack 2) (keeps item on focused stack)',
+        stack: '( x -- x )'
+    },
+
+    'copy.blue': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on copy.blue - ${getFocusedStackName(state.focusedStack)} is empty. Nothing to copy.`] 
+                });
+            }
+            const top = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 1];
+            return updateState(state, {
+                stacks: pushToStack(state.stacks, 2, top)
+            });
+        },
+        doc: 'Copy top item from focused stack to blue stack (keeps item on focused stack)',
+        stack: '( x -- x )'
+    },
+
+    'copy.3': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on copy.3 - ${getFocusedStackName(state.focusedStack)} is empty. Nothing to copy.`] 
+                });
+            }
+            const top = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 1];
+            return updateState(state, {
+                stacks: pushToStack(state.stacks, 2, top)
+            });
+        },
+        doc: 'Copy top item from focused stack to blue stack (Stack 3) (keeps item on focused stack)',
+        stack: '( x -- x )'
+    },
+
+    'copy.yellow': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on copy.yellow - ${getFocusedStackName(state.focusedStack)} is empty. Nothing to copy.`] 
+                });
+            }
+            const top = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 1];
+            return updateState(state, {
+                stacks: pushToStack(state.stacks, 3, top)
+            });
+        },
+        doc: 'Copy top item from focused stack to yellow stack (keeps item on focused stack)',
+        stack: '( x -- x )'
+    },
+
+    'copy.4': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on copy.4 - ${getFocusedStackName(state.focusedStack)} is empty. Nothing to copy.`] 
+                });
+            }
+            const top = state.stacks[state.focusedStack][state.stacks[state.focusedStack].length - 1];
+            return updateState(state, {
+                stacks: pushToStack(state.stacks, 3, top)
+            });
+        },
+        doc: 'Copy top item from focused stack to yellow stack (Stack 4) (keeps item on focused stack)',
+        stack: '( x -- x )'
+    },
+
+    'pop.red': {
+        fn: (state) => popAndPrint(state, 0),
+        doc: 'Pop and print top item from red stack',
+        stack: '( x -- )'
+    },
+
+    'pop.1': {
+        fn: (state) => popAndPrint(state, 0),
+        doc: 'Pop and print top item from red stack (Stack 1)',
+        stack: '( x -- )'
+    },
+
+    'pop.teal': {
+        fn: (state) => popAndPrint(state, 1),
+        doc: 'Pop and print top item from teal stack',
+        stack: '( x -- )'
+    },
+
+    'pop.2': {
+        fn: (state) => popAndPrint(state, 1),
+        doc: 'Pop and print top item from teal stack (Stack 2)',
+        stack: '( x -- )'
+    },
+
+    'pop.blue': {
+        fn: (state) => popAndPrint(state, 2),
+        doc: 'Pop and print top item from blue stack',
+        stack: '( x -- )'
+    },
+
+    'pop.3': {
+        fn: (state) => popAndPrint(state, 2),
+        doc: 'Pop and print top item from blue stack (Stack 3)',
+        stack: '( x -- )'
+    },
+
+    'pop.yellow': {
+        fn: (state) => popAndPrint(state, 3),
+        doc: 'Pop and print top item from yellow stack',
+        stack: '( x -- )'
+    },
+
+    'pop.4': {
+        fn: (state) => popAndPrint(state, 3),
+        doc: 'Pop and print top item from yellow stack (Stack 4)',
+        stack: '( x -- )'
+    },
+
+    // Move operations
+    'move': {
+        fn: (state) => {
+            return updateState(state, {
+                moveInProgress: true,
+                moveFromStack: null,
+                output: [...state.output, 'Enter source stack (1-4) to move from:']
+            });
+        },
+        doc: 'Move top item from one stack to another (interactive: source, then destination)',
+        stack: '( -- ) (interactive)'
+    },
+
+    // Utility words
+    'clear': {
+        fn: (state) => updateState(state, {
+            stacks: [[], [], [], []],
+            output: [...state.output, 'All stacks cleared']
+        }),
+        doc: 'Clear all four stacks',
+        stack: '( -- )'
+    },
+    
+    'clear.all': {
+        fn: (state) => updateState(state, {
+            stacks: [[], [], [], []],
+            output: [...state.output, 'All stacks cleared']
+        }),
+        doc: 'Clear all four stacks (alias for clear)',
+        stack: '( -- )'
+    },
+    
+    'clear.focused': {
+        fn: (state) => {
+            const stackNames = ['Red (1)', 'Teal (2)', 'Blue (3)', 'Yellow (4)'];
+            const newStacks = state.stacks.map((stack, i) => 
+                i === state.focusedStack ? [] : stack
+            );
+            return updateState(state, {
+                stacks: newStacks,
+                output: [...state.output, `${stackNames[state.focusedStack]} cleared`]
+            });
+        },
+        doc: 'Clear only the currently focused stack',
+        stack: '( -- )'
+    },
+
+    // String operations
+    '."': {
+        fn: (state) => {
+            return updateState(state, {
+                stringMode: true,
+                currentString: '',
+                stringPushMode: false
+            });
+        },
+        doc: 'Begin a string literal that will be printed to output',
+        stack: '( -- )'
+    },
+
+    's"': {
+        fn: (state) => {
+            return updateState(state, {
+                stringMode: true,
+                currentString: '',
+                stringPushMode: true
+            });
+        },
+        doc: 'Begin a string literal that will be pushed to the stack',
+        stack: '( -- )'
+    },
+
+    'type': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 2) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on type - Stack 1 (Red) needs 2 items (address and length), but has ${state.stacks[state.focusedStack].length}. Add more items first.`] 
+                });
+            }
+            const { stacks: stacks1, value: length } = popFromStack(state.stacks, state.focusedStack);
+            const { stacks: stacks2, value: string } = popFromStack(stacks1, 0);
+            return updateState(state, {
+                stacks: stacks2,
+                output: [...state.output, string.toString()]
+            });
+        },
+        doc: 'Print a string from the stack (takes length and string address)',
+        stack: '( addr len -- )'
+    },
+
+    'count': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on count - Stack 1 (Red) is empty. Add a string first.`] 
+                });
+            }
+            const { stacks, value } = popFromStack(state.stacks, state.focusedStack);
+            if (typeof value === 'string') {
+                const newStacks = pushToStack(stacks, state.focusedStack, value.length);
+                return updateState(state, {
+                    stacks: pushToStack(newStacks, state.focusedStack, value)
+                });
+            } else {
+                const newStacks = pushToStack(stacks, state.focusedStack, 0);
+                return updateState(state, {
+                    stacks: pushToStack(newStacks, state.focusedStack, '')
+                });
+            }
+        },
+        doc: 'Extract string info from counted string (returns length and address)',
+        stack: '( c-addr -- c-addr u )'
+    },
+
+    'char+': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 2) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on char+ - Stack 1 (Red) needs 2 items (string and offset), but has ${state.stacks[state.focusedStack].length}. Add more items first.`] 
+                });
+            }
+            const { stacks: stacks1, value: offset } = popFromStack(state.stacks, state.focusedStack);
+            const { stacks: stacks2, value: string } = popFromStack(stacks1, 0);
+            if (typeof string === 'string') {
+                return updateState(state, {
+                    stacks: pushToStack(stacks2, state.focusedStack, string + String.fromCharCode(offset))
+                });
+            } else {
+                return updateState(state, {
+                    stacks: pushToStack(pushToStack(stacks2, state.focusedStack, string), 0, offset),
+                    output: [...state.output, `Error: char+ requires string on stack - Got ${typeof string}, expected string. Use s" to create strings.`]
+                });
+            }
+        },
+        doc: 'Add a character to a string using ASCII offset',
+        stack: '( c-addr1 char -- c-addr2 )'
+    },
+
+    'strlen': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on strlen - Stack 1 (Red) is empty. Add a string first.`] 
+                });
+            }
+            const { stacks, value } = popFromStack(state.stacks, state.focusedStack);
+            if (typeof value === 'string') {
+                return updateState(state, {
+                    stacks: pushToStack(stacks, state.focusedStack, value.length)
+                });
+            } else {
+                return updateState(state, {
+                    stacks: pushToStack(stacks, state.focusedStack, 0),
+                    output: [...state.output, `Error: strlen requires string on stack - Got ${typeof value}, expected string. Use s" to create strings.`]
+                });
+            }
+        },
+        doc: 'Get the length of a string on the stack',
+        stack: '( str -- len )'
+    },
+
+    'strcat': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length < 2) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on strcat - Stack 1 (Red) needs 2 strings, but has ${state.stacks[state.focusedStack].length}. Add more strings first.`] 
+                });
+            }
+            const { stacks: stacks1, value: str2 } = popFromStack(state.stacks, state.focusedStack);
+            const { stacks: stacks2, value: str1 } = popFromStack(stacks1, 0);
+            if (typeof str1 === 'string' && typeof str2 === 'string') {
+                return updateState(state, {
+                    stacks: pushToStack(stacks2, state.focusedStack, str1 + str2)
+                });
+            } else {
+                return updateState(state, {
+                    stacks: pushToStack(pushToStack(stacks2, state.focusedStack, str1), 0, str2),
+                    output: [...state.output, `Error: strcat requires two strings - Got ${typeof str1} and ${typeof str2}. Use s" to create strings.`]
+                });
+            }
+        },
+        doc: 'Concatenate two strings from the stack',
+        stack: '( str1 str2 -- str3 )'
+    },
+
+    // Control flow
+    'if': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on if - Stack 1 (Red) is empty. Add a condition value first.`] 
+                });
+            }
+            const { stacks, value } = popFromStack(state.stacks, state.focusedStack);
+            if (value === 0) {
+                // Skip until THEN or ELSE
+                return updateState(state, {
+                    stacks,
+                    skipMode: true,
+                    skipCount: 0
+                });
+            }
+            return updateState(state, {
+                stacks
+            });
+        },
+        doc: 'Begin conditional execution - if top of stack is false (0), skip to THEN',
+        stack: '( flag -- )'
+    },
+
+    'else': {
+        fn: (state) => {
+            if (state.skipMode) {
+                return updateState(state, {
+                    skipCount: state.skipCount + 1
+                });
+            }
+            // Skip until THEN
+            return updateState(state, {
+                skipMode: true,
+                skipCount: 0
+            });
+        },
+        doc: 'Begin alternative branch in conditional execution',
+        stack: '( -- )'
+    },
+
+    'then': {
+        fn: (state) => {
+            if (state.skipMode && state.skipCount > 0) {
+                return updateState(state, {
+                    skipCount: state.skipCount - 1
+                });
+            } else if (state.skipMode) {
+                return updateState(state, {
+                    skipMode: false,
+                    skipCount: 0
+                });
+            }
+            return state;
+        },
+        doc: 'End conditional execution block',
+        stack: '( -- )'
+    },
+
+    'begin': {
+        fn: (state) => {
+            return updateState(state, {
+                loopStart: state.output.length
+            });
+        },
+        doc: 'Mark the beginning of a loop',
+        stack: '( -- )'
+    },
+
+    'until': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on until - Stack 1 (Red) is empty. Add a condition value first.`] 
+                });
+            }
+            const { stacks, value } = popFromStack(state.stacks, state.focusedStack);
+            if (value === 0) {
+                // Loop back to BEGIN
+                return updateState(state, {
+                    stacks,
+                    loopBack: true
+                });
+            }
+            return updateState(state, {
+                stacks
+            });
+        },
+        doc: 'End a loop - if top of stack is false (0), loop back to BEGIN',
+        stack: '( flag -- )'
+    },
+
+    // Help and documentation
+    'help': {
+        fn: (state) => {
+            const builtinWordNames = Object.keys(builtinWords);
+            const userWords = Array.from(state.dictionary.keys());
+            const allWords = [...builtinWordNames, ...userWords];
+            
+            const builtinList = builtinWordNames.map(name => {
+                const word = builtinWords[name];
+                return `${name} ${word.stack} - ${word.doc}`;
+            });
+            
+            const userList = userWords.length > 0 ? userWords.join(' ') : 'none';
+            
+            return updateState(state, {
+                output: [
+                    ...state.output,
+                    '=== 4-Stack Forth Interpreter Help ===',
+                    '',
+                    'Built-in words:',
+                    ...builtinList,
+                    '',
+                    'User defined words: ' + userList,
+                    '',
+                    'Total words: ' + allWords.length,
+                    '',
+                    'Use "doc <word>" to get detailed help for a specific word',
+                    'Use "words" to see just the word names'
+                ]
+            });
+        },
+        doc: 'Display comprehensive help information for all available words',
+        stack: '( -- )'
+    },
+
+    'doc': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on doc - Stack 1 (Red) is empty. Add a word name first (e.g., s" dup" doc).`] 
+                });
+            }
+            const { stacks, value: wordName } = popFromStack(state.stacks, state.focusedStack);
+            
+            // Check built-in words first
+            if (builtinWords[wordName]) {
+                const word = builtinWords[wordName];
+                return updateState(state, {
+                    stacks,
+                    output: [
+                        ...state.output,
+                        `=== ${wordName} ===`,
+                        `Stack effect: ${word.stack}`,
+                        `Description: ${word.doc}`,
+                        `Type: Built-in word`
+                    ]
+                });
+            }
+            
+            // Check user-defined words
+            if (state.dictionary.has(wordName)) {
+                const definition = state.dictionary.get(wordName);
+                return updateState(state, {
+                    stacks,
+                    output: [
+                        ...state.output,
+                        `=== ${wordName} ===`,
+                        `Definition: ${definition.join(' ')}`,
+                        `Type: User-defined word`
+                    ]
+                });
+            }
+            
+            return updateState(state, {
+                stacks,
+                output: [...state.output, `Word '${wordName}' not found`]
+            });
+        },
+        doc: 'Display documentation for a specific word',
+        stack: '( "word" -- )'
+    },
+
+    'words': {
+        fn: (state) => {
+            const builtinWordNames = Object.keys(builtinWords);
+            const userWords = Array.from(state.dictionary.keys());
+            const allWords = [...builtinWordNames, ...userWords];
+            
+            const builtinList = builtinWordNames.join(' ');
+            const userList = userWords.length > 0 ? userWords.join(' ') : 'none';
+            
+            return updateState(state, {
+                output: [
+                    ...state.output,
+                    'Built-in words: ' + builtinList,
+                    'User defined words: ' + userWords.join(' '),
+                    'Total words: ' + allWords.length
+                ]
+            });
+        },
+        doc: 'List all available words (built-in and user-defined)',
+        stack: '( -- )'
+    }
+};
+
+// Help and documentation commands (defined outside builtinWords to avoid circular reference)
+const helpCommands = {
+    'help': {
+        fn: (state) => {
+            const builtinWordNames = Object.keys(builtinWords);
+            const userWords = Array.from(state.dictionary.keys());
+            const allWords = [...builtinWordNames, ...userWords];
+            
+            const builtinList = builtinWordNames.map(name => {
+                const word = builtinWords[name];
+                return `${name} ${word.stack} - ${word.doc}`;
+            });
+            
+            const userList = userWords.length > 0 ? userWords.join(' ') : 'none';
+            
+            return updateState(state, {
+                output: [
+                    ...state.output,
+                    '=== 4-Stack Forth Interpreter Help ===',
+                    '',
+                    'Built-in words:',
+                    ...builtinList,
+                    '',
+                    'User defined words: ' + userList,
+                    '',
+                    'Total words: ' + allWords.length,
+                    '',
+                    'Use "doc <word>" to get detailed help for a specific word',
+                    'Use "words" to see just the word names'
+                ]
+            });
+        },
+        doc: 'Display comprehensive help information for all available words',
+        stack: '( -- )'
+    },
+
+    'doc': {
+        fn: (state) => {
+            if (state.stacks[state.focusedStack].length === 0) {
+                return updateState(state, { 
+                    output: [...state.output, `Error: Stack underflow on doc - Stack 1 (Red) is empty. Add a word name first (e.g., s" dup" doc).`] 
+                });
+            }
+            const { stacks, value: wordName } = popFromStack(state.stacks, state.focusedStack);
+            
+            // Check built-in words first
+            if (builtinWords[wordName]) {
+                const word = builtinWords[wordName];
+                return updateState(state, {
+                    stacks,
+                    output: [
+                        ...state.output,
+                        `=== ${wordName} ===`,
+                        `Stack effect: ${word.stack}`,
+                        `Description: ${word.doc}`,
+                        `Type: Built-in word`
+                    ]
+                });
+            }
+            
+            // Check user-defined words
+            if (state.dictionary.has(wordName)) {
+                const definition = state.dictionary.get(wordName);
+                return updateState(state, {
+                    stacks,
+                    output: [
+                        ...state.output,
+                        `=== ${wordName} ===`,
+                        `Definition: ${definition.join(' ')}`,
+                        `Type: User-defined word`
+                    ]
+                });
+            }
+            
+            return updateState(state, {
+                stacks,
+                output: [...state.output, `Word '${wordName}' not found`]
+            });
+        },
+        doc: 'Display documentation for a specific word',
+        stack: '( "word" -- )'
+    }
+};
+
+// Parse and execute Forth input
+const parseAndExecute = (state, input) => {
+    const tokens = input.trim().split(/\s+/).filter(token => token.length > 0);
+    return tokens.reduce(executeToken, state);
+};
+
+// Execute a single token
+const executeToken = (state, token) => {
+    // Handle string mode
+    if (state.stringMode) {
+        // Check if this token contains the closing quote
+        const quoteIndex = token.indexOf('"');
+        if (quoteIndex !== -1) {
+            // Token contains closing quote
+            const beforeQuote = token.substring(0, quoteIndex);
+            const afterQuote = token.substring(quoteIndex + 1);
+            
+            // Add the part before the quote to the string
+            const finalString = state.currentString + (state.currentString ? ' ' : '') + beforeQuote;
+            
+            // End string mode and handle based on mode
+            let newState;
+            if (state.stringPushMode) {
+                // Push mode: add string to stack
+                newState = updateState(state, {
+                    stringMode: false,
+                    currentString: '',
+                    stringPushMode: false,
+                    stacks: pushToStack(state.stacks, state.focusedStack, finalString)
+                });
+            } else {
+                // Print mode: add to output
+                newState = updateState(state, {
+                    stringMode: false,
+                    currentString: '',
+                    stringPushMode: false,
+                    output: [...state.output, finalString]
+                });
+            }
+            
+            // If there's content after the quote, process it
+            if (afterQuote.trim()) {
+                newState = ForthInterpreter.parseAndExecute(newState, afterQuote);
+            }
+            
+            return newState;
+        } else {
+            // Add to current string
+            return updateState(state, {
+                currentString: state.currentString + (state.currentString ? ' ' : '') + token
+            });
+        }
+    }
+
+    // Handle skip mode (for control flow)
+    if (state.skipMode) {
+        if (token === 'if' || token === 'begin') {
+            return updateState(state, {
+                skipCount: state.skipCount + 1
+            });
+        } else if (token === 'then' || token === 'until') {
+            if (state.skipCount > 0) {
+                return updateState(state, {
+                    skipCount: state.skipCount - 1
+                });
+            } else {
+                return updateState(state, {
+                    skipMode: false,
+                    skipCount: 0
+                });
+            }
+        } else if (token === 'else') {
+            if (state.skipCount === 0) {
+                // Switch to skipping ELSE branch
+                return updateState(state, {
+                    skipMode: true,
+                    skipCount: 0
+                });
+            }
+        }
+        // Skip this token
+        return state;
+    }
+
+    // Handle move operation state machine
+    if (state.moveInProgress) {
+        if (state.moveFromStack === null) {
+            // Expecting source stack number
+            const from = parseInt(token);
+            if (isNaN(from) || from < 1 || from > 4) {
+                return updateState(state, {
+                    moveInProgress: false,
+                    moveFromStack: null,
+                    output: [...state.output, `Error: Invalid source stack ${from} - Must be 1, 2, 3, or 4.`]
+                });
+            }
+            if (state.stacks[from - 1].length === 0) {
+                return updateState(state, {
+                    moveInProgress: false,
+                    moveFromStack: null,
+                    output: [...state.output, `Error: Stack ${from} is empty - Nothing to move. Add items to stack ${from} first.`]
+                });
+            }
+            return updateState(state, {
+                moveInProgress: true,
+                moveFromStack: from - 1, // Convert to 0-based index
+                output: [...state.output, `Moving from stack ${from}. Enter destination stack (1-4):`]
+            });
+        } else {
+            // Expecting destination stack number
+            const to = parseInt(token);
+            if (isNaN(to) || to < 1 || to > 4) {
+                return updateState(state, {
+                    moveInProgress: false,
+                    moveFromStack: null,
+                    output: [...state.output, `Error: Invalid destination stack ${to} - Must be 1, 2, 3, or 4.`]
+                });
+            }
+            const toIndex = to - 1; // Convert to 0-based index
+            const fromIndex = state.moveFromStack;
+            
+            // Reset move state
+            const newState = updateState(state, {
+                moveInProgress: false,
+                moveFromStack: null
+            });
+            
+            // Perform the move
+            const { stacks, value } = popFromStack(newState.stacks, fromIndex);
+            return updateState(newState, {
+                stacks: pushToStack(stacks, toIndex, value),
+                output: [...newState.output, `Moved ${value} from stack ${fromIndex + 1} to stack ${toIndex + 1}`]
+            });
+        }
+    }
+
+    // Handle cross-stack operations state machine
+    if (state.crossStackInProgress) {
+        if (state.crossStackOperation === null) {
+            // This shouldn't happen, but handle gracefully
+            return updateState(state, {
+                crossStackInProgress: false,
+                crossStackOperation: null,
+                crossStackData: null,
+                output: [...state.output, 'Error: Cross-stack operation state corrupted']
+            });
+        }
+        
+        // Expecting target stack number
+        const target = parseInt(token);
+        if (isNaN(target) || target < 1 || target > 4) {
+            return updateState(state, {
+                crossStackInProgress: false,
+                crossStackOperation: null,
+                crossStackData: null,
+                output: [...state.output, `Error: Invalid target stack ${target} - Must be 1, 2, 3, or 4.`]
+            });
+        }
+        
+        const targetIndex = target - 1; // Convert to 0-based index
+        const sourceIndex = state.focusedStack;
+        
+        // Execute the cross-stack operation (don't clear state yet)
+        const result = executeCrossStackOperation(state, sourceIndex, targetIndex);
+        
+        // Clear cross-stack state after execution
+        return updateState(result, {
+            crossStackInProgress: false,
+            crossStackOperation: null,
+            crossStackData: null
+        });
+    }
+
+    // Handle word definition compilation
+    if (state.compilingWord !== null) {
+        if (token === ';') {
+            const newDictionary = new Map(state.dictionary);
+            newDictionary.set(state.compilingWord, [...state.compilingDefinition]);
+            return updateState(state, {
+                dictionary: newDictionary,
+                compilingWord: null,
+                compilingDefinition: [],
+                output: [...state.output, `Word '${state.compilingWord}' defined`]
+            });
+        }
+        
+        // If we're expecting a name, capture it
+        if (state.compilingWord === 'EXPECTING_NAME') {
+            return updateState(state, {
+                compilingWord: token,
+                compilingDefinition: []
+            });
+        }
+        
+        // Otherwise, add to definition
+        return updateState(state, {
+            compilingDefinition: [...state.compilingDefinition, token]
+        });
+    }
+
+    // Handle word definition start
+    if (token === ':') {
+        return updateState(state, {
+            compilingWord: 'EXPECTING_NAME',
+            compilingDefinition: []
+        });
+    }
+
+    // Check if it's a built-in word
+    if (builtinWords[token]) {
+        return builtinWords[token].fn(state);
+    }
+
+    // Check if it's a user-defined word
+    if (state.dictionary.has(token)) {
+        const definition = state.dictionary.get(token);
+        return definition.reduce(executeToken, state);
+    }
+
+    // Check if it's a number
+    const num = parseFloat(token);
+    if (!isNaN(num)) {
+        return updateState(state, {
+            stacks: pushToStack(state.stacks, state.focusedStack, num)
+        });
+    }
+
+
+
+    // Check if it's a cross-stack operation
+    if (token.endsWith('.stacks')) {
+        const baseOperation = token.replace('.stacks', '');
+        if (builtinWords[token]) {
+            return builtinWords[token].fn(state);
+        }
+    }
+
+    // Unknown token
+    return updateState(state, {
+        output: [...state.output, `Error: Unknown word '${token}' - Use 'help' to see all available words, or 'doc <word>' for specific help.`]
+    });
+};
+
+// Export for use in other modules or testing
+if (typeof module !== 'undefined' && module.exports) {
+    module.exports = {
+        createInitialState,
+        parseAndExecute,
+        executeToken,
+        builtinWords,
+        updateState,
+        pushToStack,
+        popFromStack
+    };
+} else if (typeof window !== 'undefined') {
+    // Browser environment
+    window.ForthInterpreter = {
+        createInitialState,
+        parseAndExecute,
+        executeToken,
+        builtinWords,
+        updateState,
+        pushToStack,
+        popFromStack
+    };
+}
diff --git a/forth/foreforthfourth/index.html b/forth/foreforthfourth/index.html
new file mode 100644
index 0000000..a4400f3
--- /dev/null
+++ b/forth/foreforthfourth/index.html
@@ -0,0 +1,381 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>4-Stack Forth</title>
+    <style>
+        body {
+            font-family: 'Courier New', monospace;
+            margin: 0;
+            padding: 10px;
+            background-color: #000;
+            color: #fff;
+            min-height: 100vh;
+            display: flex;
+            flex-direction: column;
+        }
+        
+        .container {
+            max-width: 100%;
+            flex: 1;
+            display: flex;
+            flex-direction: column;
+        }
+        
+        h1 {
+            font-size: 18px;
+            margin: 0 0 10px 0;
+            text-align: center;
+        }
+        
+        .help {
+            background-color: #111;
+            border: 1px solid #333;
+            padding: 10px;
+            margin-bottom: 10px;
+            font-size: 12px;
+            line-height: 1.3;
+        }
+        
+        .help h2 {
+            margin: 0 0 5px 0;
+            font-size: 13px;
+        }
+        
+        .help p {
+            margin: 0;
+        }
+        
+        .stacks-container {
+            display: grid;
+            grid-template-columns: repeat(4, 1fr);
+            gap: 8px;
+            margin-bottom: 10px;
+            flex: 1;
+            min-height: 200px;
+        }
+        
+        .stack {
+            border: 2px solid;
+            padding: 8px;
+            display: flex;
+            flex-direction: column;
+            background-color: #111;
+            position: relative;
+            transition: all 0.3s ease;
+        }
+        
+        .stack.focused {
+            border-width: 4px;
+            box-shadow: 0 0 15px rgba(255, 255, 255, 0.3);
+            transform: scale(1.02);
+        }
+        
+        .stack-1 { border-color: #ff6b6b; }
+        .stack-2 { border-color: #4ecdc4; }
+        .stack-3 { border-color: #45b7d1; }
+        .stack-4 { border-color: #f9ca24; }
+        
+        .stack h3 {
+            margin: 0 0 8px 0;
+            text-align: center;
+            font-size: 12px;
+            color: #fff;
+        }
+        
+        .stack-items {
+            flex: 1;
+            display: flex;
+            flex-direction: column-reverse;
+            gap: 2px;
+            align-items: stretch;
+        }
+        
+        .stack-item {
+            background-color: #222;
+            padding: 4px 6px;
+            text-align: center;
+            border: 1px solid #555;
+            font-size: 12px;
+            word-break: break-all;
+        }
+        
+        .input-section {
+            margin-bottom: 10px;
+        }
+        
+        .input-container {
+            display: flex;
+            gap: 5px;
+            margin-bottom: 8px;
+        }
+        
+        #forth-input {
+            flex: 1;
+            padding: 8px;
+            font-family: 'Courier New', monospace;
+            font-size: 14px;
+            border: 1px solid #555;
+            background-color: #111;
+            color: #fff;
+        }
+        
+        #forth-input:focus {
+            outline: none;
+            border-color: #fff;
+        }
+        
+        button {
+            padding: 8px 12px;
+            font-family: 'Courier New', monospace;
+            font-size: 12px;
+            border: 1px solid #fff;
+            background-color: #000;
+            color: #fff;
+            cursor: pointer;
+        }
+        
+        button:hover, button:active {
+            background-color: #fff;
+            color: #000;
+        }
+        
+        .output {
+            background-color: #111;
+            border: 1px solid #555;
+            padding: 8px;
+            height: 300px;
+            overflow-y: auto;
+            font-family: 'Courier New', monospace;
+            font-size: 12px;
+            line-height: 1.3;
+        }
+        
+        /* Semantic section styling */
+        section {
+            margin-bottom: 10px;
+        }
+        
+        section h2 {
+            margin: 0 0 8px 0;
+            font-size: 14px;
+            color: #fff;
+        }
+        
+        .output-section h2 {
+            margin-bottom: 5px;
+        }
+        
+        .error {
+            color: #fff;
+        }
+        
+        .success {
+            color: #fff;
+        }
+        
+        /* Screen reader only content */
+        .sr-only {
+            position: absolute;
+            width: 1px;
+            height: 1px;
+            padding: 0;
+            margin: -1px;
+            overflow: hidden;
+            clip: rect(0, 0, 0, 0);
+            white-space: nowrap;
+            border: 0;
+        }
+        
+        /* Mobile optimizations */
+        @media (max-width: 768px) {
+            body { padding: 5px; }
+            
+            h1 { font-size: 16px; }
+            
+            .help { 
+                font-size: 11px; 
+                padding: 8px;
+            }
+            
+            .help h2 {
+                font-size: 12px;
+            }
+            
+            .stacks-container {
+                gap: 4px;
+                min-height: 150px;
+            }
+            
+            .stack {
+                padding: 6px;
+            }
+            
+            .stack h3 {
+                font-size: 11px;
+                margin-bottom: 6px;
+            }
+            
+            .stack-item {
+                font-size: 11px;
+                padding: 3px 4px;
+            }
+            
+            #forth-input {
+                font-size: 16px; /* Prevents zoom on iOS */
+                padding: 6px;
+            }
+            
+            button {
+                font-size: 11px;
+                padding: 6px 10px;
+            }
+            
+            .output {
+                height: 80px;
+                font-size: 11px;
+                padding: 6px;
+            }
+        }
+        
+        @media (max-width: 480px) {
+            .stacks-container {
+                grid-template-columns: repeat(2, 1fr);
+                grid-template-rows: repeat(2, 1fr);
+            }
+            
+            .input-container {
+                flex-direction: column;
+            }
+            
+            button {
+                width: 100%;
+            }
+        }
+    </style>
+</head>
+<body>
+    <header>
+        <h1>4-Stack Forth</h1>
+    </header>
+    
+    <main class="container">
+        <section class="help" aria-label="Quick help">
+            <h2>Quick Help</h2>
+            <p>Type <strong>help</strong> to see all available words with documentation and stack effects. Add a word to the stack like <code>s" dup"</code> and then run <code>doc</code> to see documentation for that word.</p>
+            <p><strong>Stack Focus:</strong> Use <code>focus.red</code>, <code>focus.teal</code>, <code>focus.blue</code>, or <code>focus.yellow</code> (or <code>focus.1</code>, <code>focus.2</code>, <code>focus.3</code>, <code>focus.4</code>) to switch which stack operations target. Currently focused: <span id="current-focus">Red (1)</span></p>
+        </section>
+        
+        <section class="stacks-container" aria-label="Data stacks">
+            <h2 class="sr-only">Data Stacks</h2>
+            <div class="stack stack-1" role="region" aria-label="Stack 1 (Red)" id="stack-1">
+                <h3>Stack 1 (Red)</h3>
+                <div class="stack-items" id="stack-1-items" aria-live="polite"></div>
+            </div>
+            <div class="stack stack-2" role="region" aria-label="Stack 2 (Teal)" id="stack-2">
+                <h3>Stack 2 (Teal)</h3>
+                <div class="stack-items" id="stack-2-items" aria-live="polite"></div>
+            </div>
+            <div class="stack stack-3" role="region" aria-label="Stack 3 (Blue)" id="stack-3">
+                <h3>Stack 3 (Blue)</h3>
+                <div class="stack-items" id="stack-3-items" aria-live="polite"></div>
+            </div>
+            <div class="stack stack-4" role="region" aria-label="Stack 4 (Yellow)" id="stack-4">
+                <h3>Stack 4 (Yellow)</h3>
+                <div class="stack-items" id="stack-4-items" aria-live="polite"></div>
+            </div>
+        </section>
+        
+        <section class="input-section" aria-label="Command input">
+            <h2 class="sr-only">Command Input</h2>
+            <div class="input-container">
+                <label for="forth-input" class="sr-only">Forth command input</label>
+                <input type="text" id="forth-input" placeholder="Enter Forth commands here..." aria-describedby="input-help" />
+                <button type="button" onclick="executeForth()" aria-label="Execute command">Run</button>
+                <button type="button" onclick="clearAll()" aria-label="Clear all stacks">Clear</button>
+            </div>
+            <div id="input-help" class="sr-only">Press Enter to execute commands</div>
+        </section>
+        
+        <section class="output-section" aria-label="Command output">
+            <h2 class="sr-only">Output</h2>
+            <div class="output" id="output" role="log" aria-live="polite" aria-label="Forth interpreter output"></div>
+        </section>
+    </main>
+
+    <script src="forth.js"></script>
+    <script>
+        let forthState = ForthInterpreter.createInitialState();
+
+
+
+        // Update visual display
+        const updateDisplay = () => {
+            // Update stacks
+            forthState.stacks.forEach((stack, index) => {
+                const container = document.getElementById(`stack-${index + 1}-items`);
+                container.innerHTML = '';
+                stack.forEach(item => {
+                    const div = document.createElement('div');
+                    div.className = 'stack-item';
+                    div.textContent = item.toString();
+                    container.appendChild(div);
+                });
+            });
+            
+            // Update focus indicator
+            document.querySelectorAll('.stack').forEach((stack, index) => {
+                if (index === forthState.focusedStack) {
+                    stack.classList.add('focused');
+                } else {
+                    stack.classList.remove('focused');
+                }
+            });
+            
+            // Update focus text
+            const focusNames = ['Red (1)', 'Teal (2)', 'Blue (3)', 'Yellow (4)'];
+            document.getElementById('current-focus').textContent = focusNames[forthState.focusedStack];
+
+            // Update output
+            const outputElement = document.getElementById('output');
+            outputElement.innerHTML = forthState.output
+                .slice(-100) // Show last 100 messages for better help display
+                .map(msg => {
+                    const className = msg.startsWith('Error:') ? 'error' : 'success';
+                    return `<div class="${className}">${msg}</div>`;
+                })
+                .join('');
+            outputElement.scrollTop = outputElement.scrollHeight;
+        };
+
+        // Execute Forth commands
+        const executeForth = () => {
+            const input = document.getElementById('forth-input');
+            const command = input.value.trim();
+            
+            if (command) {
+                forthState = ForthInterpreter.parseAndExecute(forthState, command);
+                updateDisplay();
+                input.value = '';
+            }
+        };
+
+        // Clear all stacks
+        const clearAll = () => {
+            forthState = ForthInterpreter.createInitialState();
+            updateDisplay();
+        };
+
+        // Handle Enter key in input
+        document.getElementById('forth-input').addEventListener('keypress', (e) => {
+            if (e.key === 'Enter') {
+                executeForth();
+            }
+        });
+
+        // Initialize display
+        updateDisplay();
+    </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/forth/foreforthfourth/test-advanced.js b/forth/foreforthfourth/test-advanced.js
new file mode 100644
index 0000000..330fd81
--- /dev/null
+++ b/forth/foreforthfourth/test-advanced.js
@@ -0,0 +1,94 @@
+// Advanced test file for string operations and control flow
+// Run with: node test-advanced.js
+
+const ForthInterpreter = require('./forth.js');
+
+console.log('🧪 Testing Advanced 4-Stack Forth Features\n');
+
+// Test 1: String literals
+console.log('Test 1: String literals');
+let state = ForthInterpreter.createInitialState();
+state = ForthInterpreter.parseAndExecute(state, '." Hello World"');
+console.log('Stack 1 after string literal:', state.stacks[0]);
+console.log('Expected: ["Hello World"]\n');
+
+// Test 2: String operations
+console.log('Test 2: String operations');
+state = ForthInterpreter.parseAndExecute(state, 'dup strlen');
+console.log('Stack 1 after strlen:', state.stacks[0]);
+console.log('Expected: ["Hello World", 11]\n');
+
+// Test 3: String concatenation
+console.log('Test 3: String concatenation');
+state = ForthInterpreter.parseAndExecute(state, '."  from Forth" strcat');
+console.log('Stack 1 after strcat:', state.stacks[0]);
+console.log('Expected: ["Hello World from Forth"]\n');
+
+// Test 4: Basic control flow - IF THEN
+console.log('Test 4: Basic control flow - IF THEN');
+state = ForthInterpreter.parseAndExecute(state, '5 3 > if ." Greater" then');
+console.log('Output:', state.output[state.output.length - 1]);
+console.log('Expected: "Greater"\n');
+
+// Test 5: Control flow with false condition
+console.log('Test 5: Control flow with false condition');
+state = ForthInterpreter.parseAndExecute(state, '3 5 > if ." Greater" then');
+console.log('Output:', state.output[state.output.length - 1]);
+console.log('Expected: No output (condition was false)\n');
+
+// Test 6: IF ELSE THEN
+console.log('Test 6: IF ELSE THEN');
+state = ForthInterpreter.parseAndExecute(state, '5 3 > if ." Greater" else ." Less or Equal" then');
+console.log('Output:', state.output[state.output.length - 1]);
+console.log('Expected: "Greater"\n');
+
+// Test 7: IF ELSE THEN with false condition
+console.log('Test 7: IF ELSE THEN with false condition');
+state = ForthInterpreter.parseAndExecute(state, '3 5 > if ." Greater" else ." Less or Equal" then');
+console.log('Output:', state.output[state.output.length - 1]);
+console.log('Expected: "Less or Equal"\n');
+
+// Test 8: BEGIN UNTIL loop
+console.log('Test 8: BEGIN UNTIL loop');
+state = ForthInterpreter.parseAndExecute(state, '5 begin dup ." Loop " 1 - dup 0 = until drop');
+console.log('Output:', state.output.slice(-5));
+console.log('Expected: 5 loop iterations\n');
+
+// Test 9: Complex control flow
+console.log('Test 9: Complex control flow');
+state = ForthInterpreter.parseAndExecute(state, '10 begin dup 0 > if dup ." Count: " . 1 - else drop 0 then dup 0 = until');
+console.log('Output:', state.output.slice(-10));
+console.log('Expected: Countdown from 10 to 1\n');
+
+// Test 10: String manipulation with control flow
+console.log('Test 10: String manipulation with control flow');
+state = ForthInterpreter.parseAndExecute(state, '." Test" dup strlen 5 > if ." Long string" else ." Short string" then');
+console.log('Output:', state.output.slice(-3));
+console.log('Expected: "Test", "Short string"\n');
+
+// Test 11: Nested control flow
+console.log('Test 11: Nested control flow');
+state = ForthInterpreter.parseAndExecute(state, '7 dup 5 > if dup 10 > if ." Very large" else ." Large" then else dup 3 > if ." Medium" else ." Small" then then');
+console.log('Output:', state.output[state.output.length - 1]);
+console.log('Expected: "Large"\n');
+
+// Test 12: String operations with numbers
+console.log('Test 12: String operations with numbers');
+state = ForthInterpreter.parseAndExecute(state, '." Hello" 32 char+');
+console.log('Stack 1 after char+:', state.stacks[0]);
+console.log('Expected: ["Hello "]\n');
+
+console.log('✅ All advanced tests completed!');
+console.log('\nFinal state:');
+console.log('Stack 1:', state.stacks[0]);
+console.log('Stack 2:', state.stacks[1]);
+console.log('Stack 3:', state.stacks[2]);
+console.log('Stack 4:', state.stacks[3]);
+console.log('Dictionary size:', state.dictionary.size);
+console.log('Output messages:', state.output.length);
+
+// Show some example outputs
+console.log('\nSample outputs:');
+state.output.slice(-5).forEach((msg, i) => {
+    console.log(`${i + 1}. ${msg}`);
+});
diff --git a/forth/foreforthfourth/test-cross-stack-complete.js b/forth/foreforthfourth/test-cross-stack-complete.js
new file mode 100644
index 0000000..22f7a16
--- /dev/null
+++ b/forth/foreforthfourth/test-cross-stack-complete.js
@@ -0,0 +1,373 @@
+const ForthInterpreter = require('./forth.js');
+
+console.log('🧪 Comprehensive Cross-Stack Operations Test Suite\n');
+
+let state = ForthInterpreter.createInitialState();
+let testCount = 0;
+let passCount = 0;
+
+// Test helper function
+const test = (name, testFn) => {
+    testCount++;
+    try {
+        testFn();
+        console.log(`✅ ${name}`);
+        passCount++;
+    } catch (error) {
+        console.log(`❌ ${name}: ${error.message}`);
+    }
+};
+
+// Test 1: Basic Cross-Stack Operations
+test('dup.stacks - Basic Functionality', () => {
+    state = ForthInterpreter.createInitialState();
+    
+    // Set up source stack
+    state = ForthInterpreter.parseAndExecute(state, 'focus.red');
+    state = ForthInterpreter.parseAndExecute(state, '42');
+    
+    // Execute dup.stacks
+    state = ForthInterpreter.parseAndExecute(state, 'dup.stacks');
+    if (!state.crossStackInProgress) throw new Error('Should set cross-stack mode');
+    if (state.crossStackOperation !== 'dup') throw new Error('Should set operation to dup');
+    
+    // Specify target stack
+    state = ForthInterpreter.parseAndExecute(state, '2'); // Target Teal stack
+    if (state.crossStackInProgress) throw new Error('Should clear cross-stack mode');
+    
+    // Verify results
+    if (state.stacks[0].length !== 1) throw new Error('Source stack should still have 1 item');
+    if (state.stacks[1].length !== 1) throw new Error('Target stack should have 1 item');
+    if (state.stacks[0][state.stacks[0].length - 1] !== 42) throw new Error('Source stack should still have 42');
+    if (state.stacks[1][state.stacks[1].length - 1] !== 42) throw new Error('Target stack should have 42');
+});
+
+test('over.stacks - Copy Second Item', () => {
+    state = ForthInterpreter.createInitialState();
+    
+    // Set up source stack with multiple items
+    state = ForthInterpreter.parseAndExecute(state, 'focus.blue');
+    state = ForthInterpreter.parseAndExecute(state, '10');
+    state = ForthInterpreter.parseAndExecute(state, '20');
+    state = ForthInterpreter.parseAndExecute(state, '30');
+    
+    // Execute over.stacks
+    state = ForthInterpreter.parseAndExecute(state, 'over.stacks');
+    if (!state.crossStackInProgress) throw new Error('Should set cross-stack mode');
+    
+    // Specify target stack
+    state = ForthInterpreter.parseAndExecute(state, '1'); // Target Red stack
+    if (state.crossStackInProgress) throw new Error('Should clear cross-stack mode');
+    
+    // Verify results
+    if (state.stacks[2].length !== 3) throw new Error('Source stack should still have 3 items');
+    if (state.stacks[0].length !== 1) throw new Error('Target stack should have 1 item');
+    if (state.stacks[0][state.stacks[0].length - 1] !== 20) throw new Error('Target stack should have second item (20)');
+});
+
+test('swap.stacks - Swap Top Items', () => {
+    state = ForthInterpreter.createInitialState();
+    
+    // Set up source stack
+    state = ForthInterpreter.parseAndExecute(state, 'focus.yellow');
+    state = ForthInterpreter.parseAndExecute(state, '100');
+    state = ForthInterpreter.parseAndExecute(state, '200');
+    
+    // Execute swap.stacks
+    state = ForthInterpreter.parseAndExecute(state, 'swap.stacks');
+    if (!state.crossStackInProgress) throw new Error('Should set cross-stack mode');
+    
+    // Specify target stack
+    state = ForthInterpreter.parseAndExecute(state, '3'); // Target Blue stack
+    if (state.crossStackInProgress) throw new Error('Should clear cross-stack mode');
+    
+    // Verify results
+    if (state.stacks[3].length !== 2) throw new Error('Source stack should still have 2 items');
+    if (state.stacks[2].length !== 2) throw new Error('Target stack should have 2 items');
+    if (state.stacks[3][state.stacks[3].length - 1] !== 200) throw new Error('Source stack top should be 200');
+    if (state.stacks[3][state.stacks[3].length - 2] !== 100) throw new Error('Source stack second should be 100');
+    if (state.stacks[2][state.stacks[2].length - 1] !== 100) throw new Error('Target stack top should be 100');
+    if (state.stacks[2][state.stacks[2].length - 2] !== 200) throw new Error('Target stack second should be 200');
+});
+
+test('nip.stacks - Move Second Item', () => {
+    state = ForthInterpreter.createInitialState();
+    
+    // Set up source stack
+    state = ForthInterpreter.parseAndExecute(state, 'focus.teal');
+    state = ForthInterpreter.parseAndExecute(state, '50');
+    state = ForthInterpreter.parseAndExecute(state, '60');
+    
+    // Execute nip.stacks
+    state = ForthInterpreter.parseAndExecute(state, 'nip.stacks');
+    if (!state.crossStackInProgress) throw new Error('Should set cross-stack mode');
+    
+    // Specify target stack
+    state = ForthInterpreter.parseAndExecute(state, '4'); // Target Yellow stack
+    if (state.crossStackInProgress) throw new Error('Should clear cross-stack mode');
+    
+    // Verify results
+    if (state.stacks[1].length !== 1) throw new Error('Source stack should have 1 item after nip');
+    if (state.stacks[3].length !== 1) throw new Error('Target stack should have 1 item');
+    if (state.stacks[1][state.stacks[1].length - 1] !== 60) throw new Error('Source stack should keep top item (60)');
+    if (state.stacks[3][state.stacks[3].length - 1] !== 50) throw new Error('Target stack should have second item (50)');
+});
+
+test('tuck.stacks - Tuck Operation', () => {
+    state = ForthInterpreter.createInitialState();
+    
+    // Set up source stack
+    state = ForthInterpreter.parseAndExecute(state, 'focus.red');
+    state = ForthInterpreter.parseAndExecute(state, '7');
+    state = ForthInterpreter.parseAndExecute(state, '8');
+    
+    // Execute tuck.stacks
+    state = ForthInterpreter.parseAndExecute(state, 'tuck.stacks');
+    if (!state.crossStackInProgress) throw new Error('Should set cross-stack mode');
+    
+    // Specify target stack
+    state = ForthInterpreter.parseAndExecute(state, '2'); // Target Teal stack
+    if (state.crossStackInProgress) throw new Error('Should clear cross-stack mode');
+    
+    // Verify results
+    if (state.stacks[0].length !== 1) throw new Error('Source stack should have 1 item after tuck');
+    if (state.stacks[1].length !== 3) throw new Error('Target stack should have 3 items');
+    if (state.stacks[0][state.stacks[0].length - 1] !== 7) throw new Error('Source stack should keep top item (7)');
+    if (state.stacks[1][state.stacks[1].length - 1] !== 8) throw new Error('Target stack should have 8 at top');
+    if (state.stacks[1][state.stacks[1].length - 2] !== 7) throw new Error('Target stack should have 7 in middle');
+    if (state.stacks[1][state.stacks[1].length - 3] !== 8) throw new Error('Target stack should have 8 at bottom');
+});
+
+test('rot.stacks - Rotate Top 3 Items', () => {
+    state = ForthInterpreter.createInitialState();
+    
+    // Set up source stack
+    state = ForthInterpreter.parseAndExecute(state, 'focus.blue');
+    state = ForthInterpreter.parseAndExecute(state, '1');
+    state = ForthInterpreter.parseAndExecute(state, '2');
+    state = ForthInterpreter.parseAndExecute(state, '3');
+    
+    // Execute rot.stacks
+    state = ForthInterpreter.parseAndExecute(state, 'rot.stacks');
+    if (!state.crossStackInProgress) throw new Error('Should set cross-stack mode');
+    
+    // Specify target stack
+    state = ForthInterpreter.parseAndExecute(state, '1'); // Target Red stack
+    if (state.crossStackInProgress) throw new Error('Should clear cross-stack mode');
+    
+    // Verify results
+    if (state.stacks[2].length !== 3) throw new Error('Source stack should still have 3 items');
+    if (state.stacks[0].length !== 3) throw new Error('Target stack should have 3 items');
+    // Source stack should be rotated: [1, 3, 2] (top=2, second=3, third=1)
+    if (state.stacks[2][state.stacks[2].length - 1] !== 2) throw new Error('Source stack top should be 2');
+    if (state.stacks[2][state.stacks[2].length - 2] !== 3) throw new Error('Source stack second should be 3');
+    if (state.stacks[2][state.stacks[2].length - 3] !== 1) throw new Error('Source stack third should be 1');
+    // Target stack should have rotated items: [1, 3, 2] (top=2, second=3, third=1)
+    if (state.stacks[0][state.stacks[0].length - 1] !== 2) throw new Error('Target stack top should be 2');
+    if (state.stacks[0][state.stacks[0].length - 2] !== 3) throw new Error('Target stack second should be 3');
+    if (state.stacks[0][state.stacks[0].length - 3] !== 1) throw new Error('Target stack third should be 1');
+});
+
+test('2dup.stacks - Duplicate Top 2 Items', () => {
+    state = ForthInterpreter.createInitialState();
+    
+    // Set up source stack
+    state = ForthInterpreter.parseAndExecute(state, 'focus.yellow');
+    state = ForthInterpreter.parseAndExecute(state, '25');
+    state = ForthInterpreter.parseAndExecute(state, '35');
+    
+    // Execute 2dup.stacks
+    state = ForthInterpreter.parseAndExecute(state, '2dup.stacks');
+    if (!state.crossStackInProgress) throw new Error('Should set cross-stack mode');
+    
+    // Specify target stack
+    state = ForthInterpreter.parseAndExecute(state, '3'); // Target Blue stack
+    if (state.crossStackInProgress) throw new Error('Should clear cross-stack mode');
+    
+    // Verify results
+    if (state.stacks[3].length !== 2) throw new Error('Source stack should still have 2 items');
+    if (state.stacks[2].length !== 2) throw new Error('Target stack should have 2 items');
+    if (state.stacks[3][state.stacks[3].length - 1] !== 35) throw new Error('Source stack top should be 35');
+    if (state.stacks[3][state.stacks[3].length - 2] !== 25) throw new Error('Source stack second should be 25');
+    if (state.stacks[2][state.stacks[2].length - 1] !== 35) throw new Error('Target stack top should be 35');
+    if (state.stacks[2][state.stacks[2].length - 2] !== 25) throw new Error('Target stack second should be 25');
+});
+
+test('2over.stacks - Copy Second Pair', () => {
+    state = ForthInterpreter.createInitialState();
+    
+    // Set up source stack with 4 items
+    state = ForthInterpreter.parseAndExecute(state, 'focus.red');
+    state = ForthInterpreter.parseAndExecute(state, '10');
+    state = ForthInterpreter.parseAndExecute(state, '20');
+    state = ForthInterpreter.parseAndExecute(state, '30');
+    state = ForthInterpreter.parseAndExecute(state, '40');
+    
+    // Execute 2over.stacks
+    state = ForthInterpreter.parseAndExecute(state, '2over.stacks');
+    if (!state.crossStackInProgress) throw new Error('Should set cross-stack mode');
+    
+    // Specify target stack
+    state = ForthInterpreter.parseAndExecute(state, '2'); // Target Teal stack
+    if (state.crossStackInProgress) throw new Error('Should clear cross-stack mode');
+    
+    // Verify results
+    if (state.stacks[0].length !== 4) throw new Error('Source stack should still have 4 items');
+    if (state.stacks[1].length !== 2) throw new Error('Target stack should have 2 items');
+    if (state.stacks[1][state.stacks[1].length - 1] !== 20) throw new Error('Target stack top should be 20');
+    if (state.stacks[1][state.stacks[1].length - 2] !== 30) throw new Error('Target stack second should be 30');
+});
+
+test('2swap.stacks - Swap Top 2 Pairs', () => {
+    state = ForthInterpreter.createInitialState();
+    
+    // Set up source stack with 4 items
+    state = ForthInterpreter.parseAndExecute(state, 'focus.teal');
+    state = ForthInterpreter.parseAndExecute(state, '1');
+    state = ForthInterpreter.parseAndExecute(state, '2');
+    state = ForthInterpreter.parseAndExecute(state, '3');
+    state = ForthInterpreter.parseAndExecute(state, '4');
+    
+    // Execute 2swap.stacks
+    state = ForthInterpreter.parseAndExecute(state, '2swap.stacks');
+    if (!state.crossStackInProgress) throw new Error('Should set cross-stack mode');
+    
+    // Specify target stack
+    state = ForthInterpreter.parseAndExecute(state, '1'); // Target Red stack
+    if (state.crossStackInProgress) throw new Error('Should clear cross-stack mode');
+    
+    // Verify results
+    if (state.stacks[1].length !== 4) throw new Error('Source stack should still have 4 items');
+    if (state.stacks[0].length !== 4) throw new Error('Target stack should have 4 items');
+    // Source stack should be: [1, 2, 3, 4] (top=4, second=3, third=2, fourth=1)
+    if (state.stacks[1][state.stacks[1].length - 1] !== 4) throw new Error('Source stack top should be 4');
+    if (state.stacks[1][state.stacks[1].length - 2] !== 3) throw new Error('Source stack second should be 3');
+    if (state.stacks[1][state.stacks[1].length - 3] !== 2) throw new Error('Source stack third should be 2');
+    if (state.stacks[1][state.stacks[1].length - 4] !== 1) throw new Error('Source stack fourth should be 1');
+    // Target stack should be: [1, 2, 3, 4] (top=4, second=3, third=2, fourth=1)
+    if (state.stacks[0][state.stacks[0].length - 1] !== 4) throw new Error('Target stack top should be 4');
+    if (state.stacks[0][state.stacks[0].length - 2] !== 3) throw new Error('Target stack second should be 3');
+    if (state.stacks[0][state.stacks[0].length - 3] !== 2) throw new Error('Target stack third should be 2');
+    if (state.stacks[0][state.stacks[0].length - 4] !== 1) throw new Error('Target stack fourth should be 1');
+});
+
+// Test 2: Error Handling
+test('Error Handling - Stack Underflow', () => {
+    state = ForthInterpreter.createInitialState();
+    
+    // Try dup.stacks on empty stack
+    state = ForthInterpreter.parseAndExecute(state, 'focus.red');
+    state = ForthInterpreter.parseAndExecute(state, 'dup.stacks');
+    
+    const lastOutput = state.output[state.output.length - 1];
+    if (!lastOutput.includes('Error: Stack underflow')) {
+        throw new Error('Should show stack underflow error');
+    }
+});
+
+test('Error Handling - Invalid Target Stack', () => {
+    state = ForthInterpreter.createInitialState();
+    
+    // Set up valid operation
+    state = ForthInterpreter.parseAndExecute(state, 'focus.red');
+    state = ForthInterpreter.parseAndExecute(state, '42');
+    state = ForthInterpreter.parseAndExecute(state, 'dup.stacks');
+    
+    // Try invalid target stack
+    state = ForthInterpreter.parseAndExecute(state, '5'); // Invalid stack number
+    
+    const lastOutput = state.output[state.output.length - 1];
+    if (!lastOutput.includes('Error: Invalid target stack')) {
+        throw new Error('Should show invalid target stack error');
+    }
+    if (state.crossStackInProgress) {
+        throw new Error('Should clear cross-stack mode on error');
+    }
+});
+
+// Test 3: Edge Cases
+test('Edge Cases - Single Item Operations', () => {
+    state = ForthInterpreter.createInitialState();
+    
+    // Test over.stacks with only 1 item
+    state = ForthInterpreter.parseAndExecute(state, 'focus.blue');
+    state = ForthInterpreter.parseAndExecute(state, '100');
+    state = ForthInterpreter.parseAndExecute(state, 'over.stacks');
+    
+    const lastOutput = state.output[state.output.length - 1];
+    if (!lastOutput.includes('Error: Stack underflow')) {
+        throw new Error('Should show stack underflow error for over.stacks with 1 item');
+    }
+});
+
+test('Edge Cases - Focus Persistence', () => {
+    state = ForthInterpreter.createInitialState();
+    
+    // Set focus and perform operation
+    state = ForthInterpreter.parseAndExecute(state, 'focus.yellow');
+    const originalFocus = state.focusedStack;
+    
+    state = ForthInterpreter.parseAndExecute(state, '50');
+    state = ForthInterpreter.parseAndExecute(state, 'dup.stacks');
+    state = ForthInterpreter.parseAndExecute(state, '1');
+    
+    if (state.focusedStack !== originalFocus) {
+        throw new Error('Focus should persist through cross-stack operations');
+    }
+});
+
+// Test 4: Complex Workflows
+test('Complex Workflows - Multi-Operation Chain', () => {
+    state = ForthInterpreter.createInitialState();
+    
+    // Set up multiple stacks
+    state = ForthInterpreter.parseAndExecute(state, 'focus.red');
+    state = ForthInterpreter.parseAndExecute(state, '100');
+    state = ForthInterpreter.parseAndExecute(state, '200');
+    
+    state = ForthInterpreter.parseAndExecute(state, 'focus.teal');
+    state = ForthInterpreter.parseAndExecute(state, '300');
+    
+    // Chain operations
+    state = ForthInterpreter.parseAndExecute(state, 'dup.stacks');
+    state = ForthInterpreter.parseAndExecute(state, '1'); // Target Red
+    
+    state = ForthInterpreter.parseAndExecute(state, 'focus.red');
+    state = ForthInterpreter.parseAndExecute(state, 'over.stacks');
+    state = ForthInterpreter.parseAndExecute(state, '3'); // Target Blue
+    
+    // Verify final state
+    if (state.stacks[0].length !== 3) throw new Error('Red stack should have 3 items');
+    if (state.stacks[1].length !== 1) throw new Error('Teal stack should have 1 item');
+    if (state.stacks[2].length !== 1) throw new Error('Blue stack should have 1 item');
+});
+
+// Test 5: State Management
+test('State Management - Cross-Stack Mode Reset', () => {
+    state = ForthInterpreter.createInitialState();
+    
+    // Start operation
+    state = ForthInterpreter.parseAndExecute(state, 'focus.red');
+    state = ForthInterpreter.parseAndExecute(state, '42');
+    state = ForthInterpreter.parseAndExecute(state, 'dup.stacks');
+    
+    if (!state.crossStackInProgress) throw new Error('Should set cross-stack mode');
+    
+    // Complete operation
+    state = ForthInterpreter.parseAndExecute(state, '2');
+    
+    if (state.crossStackInProgress) throw new Error('Should clear cross-stack mode');
+    if (state.crossStackOperation !== null) throw new Error('Should clear operation');
+    if (state.crossStackData !== null) throw new Error('Should clear data');
+});
+
+console.log(`\n📊 Test Results: ${passCount}/${testCount} tests passed`);
+console.log(`🎯 Success Rate: ${((passCount / testCount) * 100).toFixed(1)}%`);
+
+if (passCount === testCount) {
+    console.log('\n🎉 All cross-stack operation tests passed!');
+} else {
+    console.log('\n⚠️  Some tests failed. Please review the implementation.');
+}
+
+console.log('\n🚀 Cross-stack operations test suite complete!');
diff --git a/forth/foreforthfourth/test-forth.js b/forth/foreforthfourth/test-forth.js
new file mode 100644
index 0000000..54f9963
--- /dev/null
+++ b/forth/foreforthfourth/test-forth.js
@@ -0,0 +1,77 @@
+// Simple test file for the Forth interpreter
+// Run with: node test-forth.js
+
+const ForthInterpreter = require('./forth.js');
+
+console.log('🧪 Testing 4-Stack Toy Forth Interpreter\n');
+
+// Test 1: Basic number pushing
+console.log('Test 1: Basic number pushing');
+let state = ForthInterpreter.createInitialState();
+state = ForthInterpreter.parseAndExecute(state, '5 3 2');
+console.log('Stack 1 after "5 3 2":', state.stacks[0]);
+console.log('Expected: [5, 3, 2]\n');
+
+// Test 2: Basic arithmetic
+console.log('Test 2: Basic arithmetic');
+state = ForthInterpreter.parseAndExecute(state, '+');
+console.log('Stack 1 after "+":', state.stacks[0]);
+console.log('Expected: [5, 5] (3+2=5)\n');
+
+// Test 3: Stack manipulation
+console.log('Test 3: Stack manipulation');
+state = ForthInterpreter.parseAndExecute(state, 'dup over');
+console.log('Stack 1 after "dup over":', state.stacks[0]);
+console.log('Expected: [5, 5, 5, 5] (dup then over)\n');
+
+// Test 4: Stack inspection
+console.log('Test 4: Stack inspection');
+state = ForthInterpreter.parseAndExecute(state, '.s');
+console.log('Output:', state.output[state.output.length - 1]);
+console.log('Expected: <4> 5 5 5 5\n');
+
+// Test 5: Comparison operators
+console.log('Test 5: Comparison operators');
+state = ForthInterpreter.parseAndExecute(state, '5 3 >');
+console.log('Stack 1 after "5 3 >":', state.stacks[0]);
+console.log('Expected: [5, 5, 5, 5, -1] (5 > 3 = true = -1)\n');
+
+// Test 6: Word definition
+console.log('Test 6: Word definition');
+state = ForthInterpreter.parseAndExecute(state, ': double dup + ;');
+console.log('Output:', state.output[state.output.length - 1]);
+console.log('Expected: Word \'double\' defined\n');
+
+// Test 7: Using defined word
+console.log('Test 7: Using defined word');
+state = ForthInterpreter.parseAndExecute(state, 'double');
+console.log('Stack 1 after "double":', state.stacks[0]);
+console.log('Expected: [5, 5, 5, 5, -1, 10] (double of 5 = 10)\n');
+
+// Test 8: List all words
+console.log('Test 8: List all words');
+state = ForthInterpreter.parseAndExecute(state, 'words');
+console.log('Output:', state.output.slice(-3));
+console.log('Expected: Built-in words, User defined words, Total words count\n');
+
+// Test 9: Stack juggling
+console.log('Test 9: Stack juggling');
+state = ForthInterpreter.parseAndExecute(state, 'push.teal');
+console.log('Stack 1 after "push.teal":', state.stacks[0]);
+console.log('Stack 2 after "push.teal":', state.stacks[1]);
+console.log('Expected: Stack 1: [5, 5, 5, 5, -1], Stack 2: [10]\n');
+
+// Test 10: Error handling
+console.log('Test 10: Error handling');
+state = ForthInterpreter.parseAndExecute(state, 'drop drop drop drop drop drop drop drop drop drop drop');
+console.log('Output:', state.output[state.output.length - 1]);
+console.log('Expected: Error: Stack underflow on drop\n');
+
+console.log('✅ All tests completed!');
+console.log('\nFinal state:');
+console.log('Stack 1:', state.stacks[0]);
+console.log('Stack 2:', state.stacks[1]);
+console.log('Stack 3:', state.stacks[2]);
+console.log('Stack 4:', state.stacks[3]);
+console.log('Dictionary size:', state.dictionary.size);
+console.log('Output messages:', state.output.length);
diff --git a/forth/foreforthfourth/test-help-full.js b/forth/foreforthfourth/test-help-full.js
new file mode 100644
index 0000000..bf257ec
--- /dev/null
+++ b/forth/foreforthfourth/test-help-full.js
@@ -0,0 +1,33 @@
+// Test to see the complete help output
+const ForthInterpreter = require('./forth.js');
+
+console.log('🔍 Testing Complete Help Output\n');
+
+let state = ForthInterpreter.createInitialState();
+
+// Run help command
+console.log('Running help command...');
+state = ForthInterpreter.parseAndExecute(state, 'help');
+
+console.log('\n=== COMPLETE HELP OUTPUT ===');
+state.output.forEach((line, i) => {
+    console.log(`${i + 1}: ${line}`);
+});
+
+console.log('\n=== ANALYSIS ===');
+console.log('Total output lines:', state.output.length);
+
+// Count built-in words in help output
+const helpLines = state.output.filter(line => line.includes(' - '));
+console.log('Lines with word documentation:', helpLines.length);
+
+// Check if specific words are present
+const expectedWords = ['dup', 'swap', 'drop', '+', '-', '*', '/', 'mod', 'if', 'then', 'begin', 'until'];
+expectedWords.forEach(word => {
+    const found = helpLines.some(line => line.startsWith(word));
+    console.log(`${word}: ${found ? '✅' : '❌'}`);
+});
+
+// Show first few documented words
+console.log('\nFirst 10 documented words:');
+helpLines.slice(0, 10).forEach(line => console.log(line));
diff --git a/forth/foreforthfourth/test-help.js b/forth/foreforthfourth/test-help.js
new file mode 100644
index 0000000..b3aa28e
--- /dev/null
+++ b/forth/foreforthfourth/test-help.js
@@ -0,0 +1,52 @@
+// Test the new help system
+const ForthInterpreter = require('./forth.js');
+
+console.log('🧪 Testing Help System\n');
+
+let state = ForthInterpreter.createInitialState();
+
+// Test 1: Basic help command
+console.log('Test 1: Basic help command');
+state = ForthInterpreter.parseAndExecute(state, 'help');
+console.log('Help output (first 10 lines):');
+state.output.slice(-10).forEach((line, i) => {
+    console.log(`${i + 1}. ${line}`);
+});
+
+// Test 2: Document specific word
+console.log('\nTest 2: Document specific word');
+state = ForthInterpreter.parseAndExecute(state, 's" dup"');
+state = ForthInterpreter.parseAndExecute(state, 'doc');
+console.log('Doc output:');
+state.output.slice(-5).forEach((line, i) => {
+    console.log(`${i + 1}. ${line}`);
+});
+
+// Test 3: Document another word
+console.log('\nTest 3: Document another word');
+state = ForthInterpreter.parseAndExecute(state, 's" +"');
+state = ForthInterpreter.parseAndExecute(state, 'doc');
+console.log('Doc output:');
+state.output.slice(-5).forEach((line, i) => {
+    console.log(`${i + 1}. ${line}`);
+});
+
+// Test 4: Document non-existent word
+console.log('\nTest 4: Document non-existent word');
+state = ForthInterpreter.parseAndExecute(state, 's" nonexistent"');
+state = ForthInterpreter.parseAndExecute(state, 'doc');
+console.log('Doc output:');
+state.output.slice(-3).forEach((line, i) => {
+    console.log(`${i + 1}. ${line}`);
+});
+
+// Test 5: Words command
+console.log('\nTest 5: Words command');
+state = ForthInterpreter.parseAndExecute(state, 'words');
+console.log('Words output:');
+state.output.slice(-3).forEach((line, i) => {
+    console.log(`${i + 1}. ${line}`);
+});
+
+console.log('\n✅ Help system tests completed!');
+console.log('Total output lines:', state.output.length);
diff --git a/forth/guesser.fth b/forth/guesser.fth
new file mode 100644
index 0000000..01d84a0
--- /dev/null
+++ b/forth/guesser.fth
@@ -0,0 +1,100 @@
+\ --- Number Guessing Game for pForth ---
+
+\ First, we need some helper words for reliable numeric input.
+\ These are based on the pForth tutorial.
+( -- $addr )
+: INPUT$ ( A word to get a line of text from the user )
+    PAD 1+      ( addr --- , leave room on the scratchpad for a byte count )
+    80 ACCEPT   ( addr maxbytes -- numbytes , get up to 80 chars )
+    PAD C!      ( numbytes --- , store the count in the first byte )
+    PAD         ( -- $addr , leave the address of the counted string )
+;
+
+( -- n true | false )
+: INPUT# ( Read a line and convert it to a number )
+    INPUT$      ( -- $addr )
+    NUMBER?     ( $addr -- d_num true | false , convert string to double )
+    IF
+        SWAP DROP TRUE  ( d_num true -- n true , drop high part of double )
+    ELSE
+        FALSE           ( -- false )
+    THEN
+;
+
+\ --- Game Logic ---
+
+VARIABLE SECRET#  \ A place to store the secret number.
+
+( -- )
+: INIT-SECRET# ( Generate and store a random number )
+    501 CHOOSE  ( -- rand , CHOOSE gives a number from 0 to n-1 )
+    SECRET# !   ( rand -- , store it in our variable )
+;
+
+( -- n )
+: GET-GUESS ( Loop until the user enters a valid number )
+    BEGIN
+        CR ." Your guess (0-500)? "
+        INPUT#      ( -- n true | false )
+    UNTIL           ( loops until flag is true )
+;
+
+( guess -- correct? )
+: CHECK-GUESS ( Compares guess to the secret number, gives a hint )
+    SECRET# @       ( guess -- guess secret# )
+    2DUP =          ( guess secret# -- guess secret# flag )
+    IF              ( guess is equal to secret# )
+        ." You got it!" CR
+        2DROP TRUE  \ --> Make sure this 2DROP is here.
+    ELSE            ( guess is not equal )
+        2DUP <      ( guess secret# -- guess secret# flag )
+        IF
+            ." Too low!" CR
+            2DROP FALSE \ --> And this one.
+        ELSE
+            ." Too high!" CR
+            2DROP FALSE \ --> And this one.
+        THEN
+    THEN
+;
+
+( -- )
+: SEED-BY-WAITING ( Uses user's reaction time to seed the PRNG )
+    CR ." Press any key to begin..."
+    BEGIN
+        501 CHOOSE DROP  \ "Burn" a random number from the sequence
+        ?TERMINAL        \ Check if a key has been pressed
+    UNTIL
+    CR
+;
+
+( -- )
+: GUESSING-GAME ( The main word to run the game )
+    SEED-BY-WAITING \ Call our new interactive seeder
+    INIT-SECRET#
+    CR ." I'm thinking of a number between 0 and 500."
+    CR ." You have 5 chances to guess it."
+
+    FALSE           ( -- user-won? , place a 'false' flag on the stack )
+
+    5 0 DO          ( loop 5 times, from 0 to 4 )
+        CR 5 I - . ." guesses left."
+        GET-GUESS
+        CHECK-GUESS ( guess -- correct? )
+        IF          ( a correct guess was made )
+            DROP TRUE ( replace the 'user-won?' flag with 'true' )
+            LEAVE     ( and exit the loop immediately )
+        THEN
+    LOOP
+
+    ( The 'user-won?' flag is now on top of the stack )
+    IF
+        CR ." Congratulations!" CR
+    ELSE
+        CR ." Sorry, you ran out of guesses."
+        CR ." The number was " SECRET# @ . CR
+    THEN
+;
+
+GUESSING-GAME   \ This line executes the game word we just defined.
+BYE             \ This tells pForth to exit when the game is over.
diff --git a/forth/pf_ref.md b/forth/pf_ref.md
new file mode 100644
index 0000000..92ebbb6
--- /dev/null
+++ b/forth/pf_ref.md
@@ -0,0 +1,1213 @@
+::::::::::::: {#container}
+:::::: {#header}
+::: {#leftheader}
+[![](/images/softsynth_logo.png){width="200" height="100"
+border="0"}](/)
+:::
+
+::: {#rightheader}
+:::
+
+::: {#midheader}
+# SoftSynth
+
+## \... music and computers \...
+:::
+
+\
+::::::
+
+:::: {#leftside}
+::: {#leftside_inner}
+- [Home](/index.php)
+- [Products](/products.php)
+- [JSyn](/jsyn/index.php)
+- [Syntona](/syntona/index.php)
+- [pForth](/pforth/index.php)
+- [Music](/music/index.php)
+- [Info](/info/index.php)
+- [News](/news/index.php)
+- [Links](/links/index.php)
+- [Contact Us](/contacts.php)
+- [About Us](/aboutus.php)
+:::
+::::
+
+:::: {#rightside}
+::: {#rightside_inner}
+### Projects
+
+  ---------------------------------------------------------------------------------------------------
+  [JSyn](/jsyn/) - modular synthesis API for Java.
+  [JMSL](https://www.algomusic.com/jmsl/){target="_blank"} - Java Music Specification Language
+  [PortAudio](https://www.portaudio.com/){target="_blank"} - cross platform audio I/O API for \'C\'
+  ---------------------------------------------------------------------------------------------------
+:::
+::::
+
+::: {#content}
+[pForth](/pforth/index.php)
+ : [GitHub](https://github.com/philburk/pforth/)
+ \| [Tutorial](/pforth/pf_tut.php)  \| [Reference]{.current_link}
+ \| [Links](/forthlinks.php)
+
+------------------------------------------------------------------------
+
+# pForth Reference Manual
+
+------------------------------------------------------------------------
+
+### pForth - a Portable ANSI style Forth written in ANSI \'C\'.
+
+### **Last updated: July 21, 2016 V23**
+
+by Phil Burk with Larry Polansky, David Rosenboom. Special thanks to
+contributors Darren Gibbs, Herb Maeder, Gary Arakaki, Mike Haas.
+
+Back to [pForth Home Page](../pforth)
+
+## LEGAL NOTICE
+
+The pForth software code is dedicated to the public domain, and any
+third party may reproduce, distribute and modify the pForth software
+code or any derivative works thereof without any compensation or
+license. The pForth software code is provided on an \"as is\" basis
+without any warranty of any kind, including, without limitation, the
+implied warranties of merchantability and fitness for a particular
+purpose and their equivalents under the laws of any jurisdiction.
+
+------------------------------------------------------------------------
+
+## Table of Contents
+
+- [What is pForth?](#what-is)
+- [Compiling pForth for your System](#Compiling-pForth-System)
+  - [Description of Source Files](#Description-Files)
+- [Running pForth](#Running-pForth)
+- [ANSI Compliance](#ANSI-Compliance)
+- [pForth Special Features](#pForth-Features)
+  - [Compiling from a File - INCLUDE](#Compiling-File)
+  - [Saving Precompiled Dictionaries](#Saving-Dictionaries)
+  - [Creating Turnkey Applications](#Turnkey-Apps)
+  - [Recompiling Code - ANEW INCLUDE?](#Recompiling-Code)
+  - [Customising Forget with \[FORGET\]](#Customising-FORGET)
+  - [Smart Conditionals](#Smart-Conditionals)
+  - [Development Tools](#Development-Tools)
+    - [WORDS.LIKE](#WORDS.LIKE)
+    - [FILE?](#FILEQ)
+    - [SEE](#SEE)
+    - [Single Step Trace and Debug](#single-step-trace)
+  - [Conditional Compilation - \[IF\] \[ELSE\]
+    \[THEN\]](#Conditional-Compilation)
+  - [Miscellaneous Handy Words](#Miscellaneous-Words)
+  - [Local Variables { foo \-- }](#Local-Variables)
+  - [\'C\' like Structures. :STRUCT](#C-Structures)
+  - [Vectorred execution - DEFER](#Vectorred-Execution)
+  - [Floating Point](#Floating-Point)
+- [pForth Design](#pForth-Design)
+  - [\'C\' kernel](#C-kernel)
+  - [Dictionary Structures](#Dictionary-Structures)
+- [Compiling pForth](#Compiling-pForth)
+  - [Compiler Options](#Compiler-Options)
+  - [Building pForth on Supported Hosts](#Building-pForth-Hosts)
+  - [Compiling for Embedded Systems](#Compiling-Embedded)
+  - [Linking with Custom \'C\' Functions](#Link-Custom-C)
+  - [Testing your Compiled pForth](#Testing-pForth)
+
+------------------------------------------------------------------------
+
+## []{#what-is}What is pForth?
+
+PForth is an ANSI style Forth designed to be portable across many
+platforms. The \'P\' in pForth stands for \"Portable\". PForth is based
+on a Forth kernel written in ANSI standard \'C\'.
+
+### What is Forth?
+
+Forth is a stack based language invented by astronomer Charles Moore for
+controlling telescopes. Forth is an interactive language. You can enter
+commands at the keyboard and have them be immediately executed, similar
+to BASIC or LISP. Forth has a dictionary of words that can be executed
+or used to construct new words that are then added to the dictionary.
+Forth words operate on a data stack that contains numbers and addresses.
+
+To learn more about Forth, see the [Forth Tutorial](pf_tut.php).
+
+### The Origins of pForth
+
+PForth began as a JSR threaded 68000 Forth called HForth that was used
+to support [HMSL](/hmsl/), the Hierarchical Music Specification
+Language. HMSL was a music experimentation language developed by Phil
+Burk, Larry Polansky and David Rosenboom while working at the Mills
+College Center for Contemporary Music. Phil moved from Mills to the 3DO
+Company where he ported the Forth kernel to \'C\'. It was used
+extensively at 3DO as a tool for verifying ASIC design and for bringing
+up new hardware platforms. At 3DO, the Forth had to run on many systems
+including SUN, SGI, Macintosh, PC, Amiga, the 3DO ARM based Opera
+system, and the 3DO PowerPC based M2 system.
+
+### pForth Design Goals
+
+PForth has been designed with portability as the primary design goal. As
+a result, pForth avoids any fancy UNIX calls. pForth also avoids using
+any clever and original ways of constructing the Forth dictionary. It
+just compiles its kernel from ANSI compatible \'C\' code then loads ANS
+compatible Forth code to build the dictionary. Very boring but very
+likely to work on almost any platform.
+
+The dictionary files that can be saved from pForth are almost host
+independent. They can be compiled on one processor, and then run on
+another processor. as long as the endian-ness is the same. In other
+words, dictionaries built on a PC will only work on a PC. Dictionaries
+built on almost any other computer will work on almost any other
+computer.
+
+PForth can be used to bring up minimal hardware systems that have very
+few system services implemented. It is possible to compile pForth for
+systems that only support routines to send and receive a single
+character. If malloc() and free() are not available, equivalent
+functions are available in standard \'C\' code. If file I/O is not
+available, the dictionary can be saved as a static data array in \'C\'
+source format on a host system. The dictionary in \'C\' source form is
+then compiled with a custom pForth kernel to avoid having to read the
+dictionary from disk.
+
+------------------------------------------------------------------------
+
+## []{#Compiling-pForth-System}Compiling pForth for your System
+
+Up-to-date instructions on compiling, possibly with comments from the
+community, may be found at:
+
+> [https://github.com/philburk/pforth/wiki/Compiling-on-Unix](https://github.com/philburk/pforth/wiki/Compiling-on-Unix){target="_blank"}
+
+The process of building pForth involves several steps. This process is
+typically handled automatically by the Makefile or IDE Project.
+
+1.  Compile the \'C\' based pForth kernel called \"pforth\" or
+    \"pforth.exe\".
+2.  Execute \"pforth\" with the -i option to build the dictionary from
+    scratch. Compile the \"system.fth\" file which will add all the top
+    level Forth words. This can be done in one command by entering
+    \"pforth -i system.fth\".
+3.  Save the compiled dictionary as \"pforth.dic\".
+4.  The next time you run pforth, the precompiled pforth.dic file will
+    be loaded automatically.
+
+### Unix and Max OS X
+
+A Makefile has been provided that should work on most Unix based
+platforms.
+
+1.  cd to \"platforms/unix\" folder.
+2.  Enter: make all
+3.  Enter: ./pforth
+
+Note that the platforms folder used to be called build.
+
+### []{#Description-Files}Description of Source Files
+
+#### Forth Source in /fth/
+
+    ansilocs.fth    = support for ANSI (LOCAL) word
+    c_struct.fth    = 'C' like data structures
+    case.fth        = CASE OF ENDOF ENDCASE
+    catch.fth       = CATCH and THROW
+    condcomp.fth    = [IF] [ELSE] [THEN] conditional compiler
+    filefind.fth    = FILE?
+    floats.fth      = floating point support
+    forget.fth      = FORGET [FORGET] IF.FORGOTTEN
+    loadp4th.fth    = loads basic dictionary
+    locals.fth      = { } style locals using (LOCAL)
+    math.fth        = misc math words
+    member.fth      = additional 'C' like data structure support
+    misc1.fth       = miscellaneous words
+    misc2.fth       = miscellaneous words
+    numberio.fth    = formatted numeric input/output
+    private.fth     = hide low level words
+    quit.fth        = QUIT EVALUATE INTERPRET in high level
+    smart_if.fth    = allows conditionals outside colon definition
+    see.fth         = Forth "disassembler".  Eg.  SEE SPACES
+    strings.fth     = string support
+    system.fth      = bootstraps pForth dictionary
+    trace.fth       = single step trace for debugging
+
+#### \'C\' Source in /csrc/
+
+    pfcompil.c  = pForth compiler support
+    pfcustom.c  = example of 'C' functions callable from pForth
+    pfinnrfp.h  = float extensions to interpreter
+    pforth.h    = include this in app that embeds pForth
+    pf_cglue.c  = glue for pForth calling 'C'
+    pf_clib.c   = replacement routines for 'C' stdlib
+    pf_core.c   = primary words called from 'C' app that embeds pForth
+    pf_float.h  = defines PF_FLOAT, and the floating point math functions such as fp_sin
+    pf_inner.c  = inner interpreter
+    pf_guts.h   = primary include file, define structures
+    pf_io.c     = input/output
+    pf_main.c   = basic application for standalone pForth
+    pf_mem.c    = optional malloc() implementation
+    pf_save.c   = save and load dictionaries
+    pf_text.c   = string tools, error message text
+    pf_words.c  = miscellaneous pForth words implemented
+
+------------------------------------------------------------------------
+
+## []{#Running-pForth}Running pForth
+
+PForth can be run from a shell or by double clicking on its icon,
+depending on the system you are using. The execution options for pForth
+are described assuming that you are running it from a shell.
+
+Usage:
+
+- pforth [-i] [-dDictionaryFilename] [SourceFilename]
+
+-i
+
+Initialize pForth by building dictionary from scratch. Used when
+building pForth or when debugging pForth on new systems.
+

+
+-dDictionaryFilename
+
+Specify a custom dictionary to be loaded in place of the default
+\"pforth.dic\". For example:
+
+- - pforth -dgame.dic
+
+SourceFilename
+
+A Forth source file can be automatically compiled by passing its name to
+pForth. This is useful when using Forth as an assembler or for automated
+hardware testing. Remember that the source file can compile code and
+execute it all in the same file.
+
+#### Quick Verification of pForth
+
+To verify that PForth is working, enter:
+
+- 3 4 + .
+
+It should print \"7 ok\". Now enter:
+
+- WORDS
+
+You should see a long list of all the words in the pForth dictionary.
+Don\'t worry. You won\'t need to learn all of these.  More tests are
+described in the README.txt file.
+
+If you want to learn how to program in Forth, try our
+[tutorial](pf_tut.php).
+
+------------------------------------------------------------------------
+
+## []{#ANSI-Compliance}ANSI Compliance
+
+This Forth is intended to be ANS compatible. I will not claim that it is
+compatible until more people bang on it. If you find areas where it
+deviates from the standard, please let me know.
+
+Word sets supported include:
+
+- FLOAT
+- LOCAL with support for { lv1 lv2 \| lv3 \-- } style locals
+- EXCEPTION but standard throw codes not implemented
+- FILE ACCESS
+- MEMORY ALLOCATION
+
+Here are the areas that I know are not compatible:
+
+The ENVIRONMENT queries are not implemented.
+
+Word sets NOT supported include:
+
+- BLOCK - a matter of religion
+- SEARCH ORDER
+- PROGRAMMING TOOLS - only has .S ? DUMP WORDS BYE
+- STRING - only has CMOVE CMOVE\> COMPARE
+- DOUBLE NUMBER - but cell is 32 bits
+
+------------------------------------------------------------------------
+
+## []{#pForth-Features}pForth Special Features
+
+These features are not part of the ANS standard for Forth.  They have
+been added to assist developers.
+
+### []{#Compiling-File}Compiling from a File
+
+Use INCLUDE to compile source code from a file:
+
+- INCLUDE filename
+
+You can nest calls to INCLUDE. INCLUDE simply redirects Forth to takes
+its input from the file instead of the keyboard so you can place any
+legal Forth code in the source code file.
+
+### []{#Saving-Dictionaries}Saving Precompiled Dictionaries
+
+Use SAVE-FORTH save your precompiled code to a file. To save the current
+dictionary to a file called \"custom.dic\", enter:
+
+- c" custom.dic" SAVE-FORTH
+
+You can then leave pForth and use your custom dictionary by entering:
+
+- pforth -dcustom.dic
+
+On icon based systems, you may wish to name your custom dictionary
+\"pforth.dic\" so that it will be loaded automatically.
+
+Be careful that you do not leave absolute addresses stored in the
+dictionary because they will not work when you reload pForth at a
+different address. Use A! to store an address in a variable in a
+relocatable form and A@ to get it back if you need to.
+
+- VARIABLE DATA-PTR
+      CREATE DATA 100 ALLOT
+      DATA DATA-PTR !    \ storing absolute address!  BAD
+      DATA DATA-PTR A!   \ storing relocatable address!  GOOD
+      DATA-PTR A@        \ fetch relocatable address
+
+### []{#Turnkey-Apps}Creating Turnkey Applications
+
+Use TURNKEY to save a dictionary with a word that will run
+automatically. The headers (names) will be discarded to save space in
+the dictionary. Suppose you have defined a word called MYAPP to prints
+the ASCII code when you press a key on the keyboard.
+
+- : MYAPP ( -- , print key codes )
+          BEGIN ." #" key dup ascii q = not
+          WHILE . cr REPEAT ;
+
+Save a dictionary named \"turnkey.dic\" that will run MYAPP. Other names
+are OK.
+
+- c" turnkey.dic"  ' MYAPP  TURNKEY
+
+Run the app. Press some letters to see the code. Then press \'q\' to
+exit.
+
+- ./pforth -dturnkey.dic
+
+### []{#Recompiling-Code}Recompiling Code - ANEW INCLUDE?
+
+When you are testing a file full of code, you will probably recompile
+many times. You will probably want to FORGET the old code before loading
+the new code. You could put a line at the beginning of your file like
+this:
+
+- FORGET XXXX-MINE     : XXXX-MINE ;
+
+This would automatically FORGET for you every time you load.
+Unfortunately, you must define XXXX-MINE before you can ever load this
+file. We have a word that will automatically define a word for you the
+first time, then FORGET and redefine it each time after that. It is
+called ANEW and can be found at the beginning of most Forth source
+files. We use a prefix of TASK- followed by the filename just to be
+consistent. This TASK-name word is handy when working with INCLUDE? as
+well. Here is an example:
+
+- \ Start of file
+      INCLUDE? TASK-MYTHING.FTH MYTHING.FTH
+      ANEW TASK-THISFILE.FTH
+      \ the rest of the file follows...
+
+Notice that the INCLUDE? comes before the call to ANEW so that we don\'t
+FORGET MYTHING.FTH every time we recompile.
+
+FORGET allows you to get rid of code that you have already compiled.
+This is an unusual feature in a programming language. It is very
+convenient in Forth but can cause problems. Most problems with FORGET
+involve leaving addresses that point to the forgotten code that are not
+themselves forgotten. This can occur if you set a deferred system word
+to your word then FORGET your word. The system word which is below your
+word in the dictionary is pointing up to code that no longer exists. It
+will probably crash if called. (See discussion of DEFER below.) Another
+problem is if your code allocates memory, opens files, or opens windows.
+If your code is forgotten you may have no way to free or close these
+thing. You could also have a problems if you add addresses from your
+code to a table that is below your code. This might be a jump table or
+data table.
+
+Since this is a common problem we have provided a tool for handling it.
+If you have some code that you know could potentially cause a problem if
+forgotten, then write a cleanup word that will eliminate the problem.
+This word could UNdefer words, free memory, etc. Then tell the system to
+call this word if the code is forgotten. Here is how:
+
+- : MY.CLEANUP  ( -- , do whatever )
+          MY-MEM @ FREE DROP
+          0 MY-MEM !
+      ;
+      IF.FORGOTTEN  MY.CLEANUP
+
+IF.FORGOTTEN creates a linked list node containing your CFA that is
+checked by FORGET. Any nodes that end up above HERE (the Forth pointer
+to the top of the dictionary) after FORGET is done are executed.
+
+### []{#Customising-FORGET}Customising FORGET with \[FORGET\]
+
+Sometimes, you may need to extend the way that FORGET works. FORGET is
+not deferred, however, because that could cause some real problems.
+Instead, you can define a new version of \[FORGET\] which is searched
+for and executed by FORGET. You MUST call \[FORGET\] from your program
+or FORGET will not actually FORGET. Here is an example.
+
+- : [FORGET]  ( -- , my version )
+          ." Change things around!" CR
+          [FORGET]  ( must be called )
+          ." Now put them back!" CR
+      ;
+      : FOO ." Hello!" ;
+      FORGET FOO  ( Will print "Change things around!", etc.)
+
+This is recommended over redefining FORGET because words like ANEW that
+call FORGET will now pick up your changes.
+
+### []{#Smart-Conditionals}Smart Conditionals
+
+In pForth, you can use IF THEN DO LOOP and other conditionals outside of
+colon definitions. PForth will switch temporarily into the compile
+state, then automatically execute the conditional code. (Thank you Mitch
+Bradley) For example, just enter this at the keyboard.
+
+- 10 0 DO I . LOOP
+
+### []{#Development-Tools}Development Tools
+
+#### []{#WORDS.LIKE}WORDS.LIKE
+
+If you cannot remember the exact name of a word, you can use WORDS.LIKE
+to search the dictionary for all words that contain a substring. For an
+example, enter:
+
+- WORDS.LIKE   FOR
+      WORDS.LIKE   EMIT
+
+#### []{#FILEQ}FILE?
+
+You can use FILE? to find out what file a word was compiled from. If a
+word was defined in multiple files then it will list each file. The
+execution token of each definition of the word is listed on the same
+line.
+
+- FILE? IF
+      FILE? AUTO.INIT
+
+#### []{#SEE}SEE
+
+You can use SEE to \"disassemble\" a word in the pForth dictionary. SEE
+will attempt to print out Forth source in a form that is similar to the
+source code. SEE will give you some idea of how the word was defined but
+is not perfect. Certain compiler words, like BEGIN and LITERAL, are
+difficult to disassemble and may not print properly. For an example,
+enter:
+
+- SEE SPACES
+      SEE WORDS
+
+#### []{#single-step-trace}Single Step Trace and Debug
+
+It is often useful to proceed step by step through your code when
+debugging.  PForth provides a simple single step trace facility for this
+purpose.  Here is an example of using TRACE to debug a simple program. 
+Enter the following program:\

+
+- : SQUARE ( n -- n**2 )
+          DUP  *
+      ;
+      : TSQ  ( n -- , test square )
+          ." Square of "   DUP   .
+          ." is "   SQUARE   .   CR
+      ;
+
+Even though this program should work, let\'s pretend it doesn\'t and try
+to debug it.  Enter:
+
+- 7  TRACE  TSQ
+
+You should see:
+
+- 7 trace tsq
+      <<  TSQ +0           <10:1> 7             ||  (.")  Square of "          >>    ok
+
+The \"TSQ +0\" means that you are about to execute code at an offset of
+\"+0\" from the beginning of TSQ.  The \<10:1\> means that we are in
+base 10, and that there is 1 item on the stack, which is shown to be
+\"7\". The (.\") is the word that is about to be executed.  (.\") is the
+word that is compiled when use use .\".  Now to single step, enter:
+
+- s
+
+You should see:
+
+- Square of
+      <<  TSQ +16          <10:1> 7             ||  DUP                         >>    ok
+
+The \"Square os\" was printed by (.\"). We can step multiple times using
+the \"sm\" command. Enter:
+
+- 3 sm
+
+You should see:
+
+- <<  TSQ +20          <10:2> 7 7           ||  .                         >> 7 
+      <<  TSQ +24          <10:1> 7             ||  (.")  is "                >> is 
+      <<  TSQ +32          <10:1> 7             ||  SQUARE                    >>    ok
+
+The \"7\" after the \"\>\>\" was printed by the . word. If we entered
+\"s\", we would step over the SQUARE word. If we want to dive down into
+SQUARE, we can enter:
+
+- sd
+
+You should see:
+
+- <<  SQUARE +0        <10:1> 7             ||    DUP                     >>    ok
+
+To step once in SQUARE, enter:
+
+- s
+
+You should see:
+
+- <<  SQUARE +4        <10:2> 7 7           ||    *                        >>    ok
+
+To go to the end of the current word, enter:
+
+- g
+
+You should see:
+
+- <<  SQUARE +8        <10:1> 49            ||    EXIT                      >> 
+      <<  TSQ +36          <10:1> 49            ||  .                           >>    ok
+
+EXIT is compiled at the end of every Forth word. For more information on
+TRACE, enter TRACE.HELP:
+
+- TRACE  ( i*x <name> -- , setup trace for Forth word )
+      S      ( -- , step over )
+      SM     ( many -- , step over many times )
+      SD     ( -- , step down )
+      G      ( -- , go to end of word )
+      GD     ( n -- , go down N levels from current level,
+                      stop at end of this level )
+
+### []{#Conditional-Compilation}Conditional Compilation \[IF\] \[ELSE\] \[THEN\]
+
+PForth supports conditional compilation words similar to \'C\'\'s #if,
+#else, and #endif.
+
+\[IF\] ( flag \-- , if true, skip to \[ELSE\] or \[THEN\] )
+
+\[ELSE\] ( \-- , skip to \[THEN\] )
+
+\[THEN\] ( \-- , noop, used to terminate \[IF\] and \[ELSE\] section )
+
+For example:
+
+- TRUE constant USE_FRENCH
+
+      USE_FRENCH  [IF]
+        : WELCOME  ." Bienvenue!" cr ;
+      [ELSE]
+        : WELCOME  ." Welcome!" cr ;
+      [THEN]
+
+Here is how to conditionally compile within a colon definition by using
+\[ and \].
+
+- : DOIT  ( -- )
+          START.REACTOR
+          IF
+              [ USE_FRENCH [IF] ] ." Zut alors!"
+              [ [ELSE] ] ." Uh oh!"
+              [THEN]
+          THEN cr
+      ;
+
+### []{#Miscellaneous-Words}Miscellaneous Handy Words
+
+.HEX ( n \-- , print N as hex number )
+
+CHOOSE ( n \-- rand , select random number between 0 and N-1 )
+
+MAP ( \-- , print dictionary information )
+
+### []{#Local-Variables}Local Variables { foo \--}
+
+In a complicated Forth word it is sometimes hard to keep track of where
+things are on the stack. If you find you are doing a lot of stack
+operations like DUP SWAP ROT PICK etc. then you may want to use local
+variables. They can greatly simplify your code. You can declare local
+variables for a word using a syntax similar to the stack diagram. These
+variables will only be accessible within that word. Thus they are
+\"local\" as opposed to \"global\" like regular variables. Local
+variables are self-fetching. They automatically put their values on the
+stack when you give their name. You don\'t need to @ the contents. Local
+variables do not take up space in the dictionary. They reside on the
+return stack where space is made for them as needed. Words written with
+them can be reentrant and recursive.
+
+Consider a word that calculates the difference of two squares, Here are
+two ways of writing the same word.
+
+- : DIFF.SQUARES ( A B -- A*A-B*B ) 
+          DUP * 
+          SWAP DUP * 
+          SWAP - 
+      ; 
+        ( or ) 
+      : DIFF.SQUARES { A B -- A*A-B*B } 
+          A A * 
+          B B * - 
+      ; 
+      3 2 DIFF.SQUARES  ( would return 5 )
+
+In the second definition of DIFF.SQUARES the curly bracket \'{\' told
+the compiler to start declaring local variables. Two locals were
+defined, A and B. The names could be as long as regular Forth words if
+desired. The \"\--\" marked the end of the local variable list. When the
+word is executed, the values will automatically be pulled from the stack
+and placed in the local variables. When a local variable is executed it
+places its value on the stack instead of its address. This is called
+self-fetching. Since there is no address, you may wonder how you can
+store into a local variable. There is a special operator for local
+variables that does a store. It looks like -\> and is pronounced \"to\".
+
+Local variables need not be passed on the stack. You can declare a local
+variable by placing it after a \"vertical bar\" ( \| )character. These
+are automatically set to zero when created. Here is a simple example
+that uses -\> and \| in a word:
+
+- : SHOW2*  
+              { loc1 | unvar --  , 1 regular, 1 uninitialized }
+              LOC1  2*  ->  UNVAR 
+                      (set unver to 2*LOC1 )
+              UNVAR   .   ( print UNVAR )
+      ;
+      3 SHOW2*   ( pass only 1 parameter, prints 6 )
+
+Since local variable often used as counters or accumulators, we have a
+special operator for adding to a local variable It is +-\> which is
+pronounced \"plus to\". These next two lines are functionally equivalent
+but the second line is faster and smaller:
+
+- ACCUM   10 +   -> ACCUM
+      10 +-> ACCUM
+
+If you name a local variable the same as a Forth word in the dictionary,
+eg. INDEX or COUNT, you will be given a warning message. The local
+variable will still work but one could easily get confused so we warn
+you about this. Other errors that can occur include, missing a closing
+\'}\', missing \'\--\', or having too many local variables.
+
+### []{#C-Structures}\'C\' like Structures. :STRUCT
+
+You can define \'C\' like data structures in pForth using :STRUCT. For
+example:
+
+- :STRUCT  SONG
+          LONG     SONG_NUMNOTES  \ define 32 bit structure member named SONG_NUMNOTES
+          SHORT    SONG_SECONDS   \ define 16 bit structure member
+          BYTE     SONG_QUALITY   \ define 8 bit member
+          LONG     SONG_NUMBYTES  \ auto aligns after SHORT or BYTE
+          RPTR     SONG_DATA      \ relocatable pointer to data
+      ;STRUCT
+
+      SONG  HAPPY   \ define a song structure called happy
+
+      400  HAPPY  S!  SONG_NUMNOTES  \ set number of notes to 400
+      17   HAPPY  S!  SONG_SECONDS   \ S! works with all size members
+
+      CREATE  SONG-DATA  23 , 17 , 19 , 27 ,
+      SONG-DATA  HAPPY S! SONG_DATA  \ store pointer in relocatable form
+
+      HAPPY  DST  SONG    \ dump HAPPY as a SONG structure
+
+      HAPPY   S@  SONG_NUMNOTES .  \ fetch numnotes and print
+
+See the file \"c_struct.fth\" for more information.
+
+### []{#Vectorred-Execution}Vectorred Execution - DEFER
+
+Using DEFER for vectored words. In Forth and other languages you can
+save the address of a function in a variable. You can later fetch from
+that variable and execute the function it points to.This is called
+vectored execution. PForth provides a tool that simplifies this process.
+You can define a word using DEFER. This word will contain the execution
+token of another Forth function. When you execute the deferred word, it
+will execute the function it points to. By changing the contents of this
+deferred word, you can change what it will do. There are several words
+that support this process.
+
+DEFER ( \<name\> \-- , define a deferred word )
+
+IS ( CFA \<name\> \-- , set the function for a deferred word )
+
+WHAT\'S ( \<name\> \-- CFA , return the CFA set by IS )
+
+Simple way to see the name of what\'s in a deferred word:
+
+- - WHAT'S EMIT >NAME ID.
+
+should print name of current word that\'s in EMIT.
+
+Here is an example that uses a deferred word.
+
+- DEFER PRINTIT
+      ' . IS PRINTIT   ( make PRINTIT use . )
+      8 3 + PRINTIT
+
+      : COUNTUP  ( -- , call deferred word )
+              ." Hit RETURN to stop!" CR
+              0 ( first value )
+              BEGIN 1+ DUP PRINTIT CR
+                      ?TERMINAL
+              UNTIL
+      ;
+      COUNTUP  ( uses simple . )
+
+      : FANCY.PRINT  ( N -- , print in DECIMAL and HEX)
+              DUP ." DECIMAL = " .
+              ." , HEX = " .HEX
+      ;
+      ' FANCY.PRINT  IS PRINTIT  ( change printit )
+      WHAT'S PRINTIT >NAME ID. ( shows use of WHAT'S )
+      8 3 + PRINTIT
+      COUNTUP  ( notice that it now uses FANCY.PRINT )
+
+Many words in the system have been defined using DEFER which means that
+we can change how they work without recompiling the entire system. Here
+is a partial list of those words
+
+- ABORT EMIT NUMBER?
+
+#### Potential Problems with Defer
+
+Deferred words are very handy to use, however, you must be careful with
+them. One problem that can occur is if you initialize a deferred system
+more than once. In the below example, suppose we called STUTTER twice.
+The first time we would save the original EMIT vector in OLD-EMIT and
+put in a new one. The second time we called it we would take our new
+function from EMIT and save it in OLD-EMIT overwriting what we had saved
+previously. Thus we would lose the original vector for EMIT . You can
+avoid this if you check to see whether you have already done the defer.
+Here\'s an example of this technique.
+
+- DEFER OLD-EMIT
+      ' QUIT  IS OLD-EMIT  ( set to known value )
+      : EEMMIITT  ( char --- , our fun EMIT )
+          DUP OLD-EMIT OLD-EMIT
+      ; 
+      : STUTTER   ( --- )
+          WHAT'S OLD-EMIT  'C QUIT =  ( still the same? )
+          IF  ( this must be the first time )
+              WHAT'S EMIT  ( get the current value of EMIT )  
+              IS OLD-EMIT  ( save this value in OLD-EMIT )  
+              'C EEMMIITT IS EMIT
+          ELSE ."  Attempt to STUTTER twice!" CR
+          THEN
+      ; 
+      : STOP-IT!  ( --- )
+          WHAT'S OLD-EMIT ' QUIT =
+          IF  ." STUTTER not installed!" CR
+
+          ELSE  WHAT'S OLD-EMIT IS EMIT
+              'C QUIT IS OLD-EMIT  
+                      ( reset to show termination )
+          THEN
+      ;
+
+In the above example, we could call STUTTER or STOP-IT! as many times as
+we want and still be safe.
+
+Suppose you forget your word that EMIT now calls. As you compile new
+code you will overwrite the code that EMIT calls and it will crash
+miserably. You must reset any deferred words that call your code before
+you FORGET your code. The easiest way to do this is to use the word
+IF.FORGOTTEN to specify a cleanup word to be called if you ever FORGET
+the code in question. In the above example using EMIT , we could have
+said:
+
+- IF.FORGOTTEN STOP-IT!
+
+### []{#Floating-Point}Floating Point
+
+PForth supports the FLOAT word set and much of the FLOATEXT word set as
+a compile time option.  You can select single or double precision as the
+default by changing the typedef of PF_FLOAT.
+
+PForth has several options for floating point output.
+
+FS. ( r -f- , prints in scientific/exponential format )
+
+FE. ( r -f- , prints in engineering format, exponent if multiple of 3  )
+
+FG. ( r -f- , prints in normal or exponential format depending on size )
+
+F. ( r -f- , as defined by the standard )
+

+
+Here is an example of output from each word for a number ranging from
+large to very small.
+
+         FS.             FE.            FG.           F.
+    1.234000e+12     1.234000e+12     1.234e+12     1234000000000. 
+    1.234000e+11     123.4000e+09     1.234e+11     123400000000. 
+    1.234000e+10     12.34000e+09     1.234e+10     12340000000. 
+    1.234000e+09     1.234000e+09     1.234e+09     1234000000. 
+    1.234000e+08     123.4000e+06     1.234e+08     123400000. 
+    1.234000e+07     12.34000e+06     1.234e+07     12340000. 
+    1.234000e+06     1.234000e+06     1234000.     1234000. 
+    1.234000e+05     123.4000e+03     123400.     123400.0 
+    1.234000e+04     12.34000e+03     12340.     12340.00 
+    1.234000e+03     1.234000e+03     1234.     1234.000 
+    1.234000e+02     123.4000e+00     123.4     123.4000 
+    1.234000e+01     12.34000e+00     12.34     12.34000 
+    1.234000e+00     1.234000e+00     1.234     1.234000 
+    1.234000e-01     123.4000e-03     0.1234     0.1234000 
+    1.234000e-02     12.34000e-03     0.01234     0.0123400 
+    1.234000e-03     1.234000e-03     0.001234     0.0012340 
+    1.234000e-04     123.4000e-06     0.0001234     0.0001234 
+    1.234000e-05     12.34000e-06     1.234e-05     0.0000123 
+    1.234000e-06     1.234000e-06     1.234e-06     0.0000012 
+    1.234000e-07     123.4000e-09     1.234e-07     0.0000001 
+    1.234000e-08     12.34000e-09     1.234e-08     0.0000000 
+    1.234000e-09     1.234000e-09     1.234e-09     0.0000000 
+    1.234000e-10     123.4000e-12     1.234e-10     0.0000000 
+    1.234000e-11     12.34000e-12     1.234e-11     0.0000000
+
+    1.234568e+12     1.234568e+12     1.234568e+12     1234567890000. 
+    1.234568e+11     123.4568e+09     1.234568e+11     123456789000. 
+    1.234568e+10     12.34568e+09     1.234568e+10     12345678900. 
+    1.234568e+09     1.234568e+09     1.234568e+09     1234567890. 
+    1.234568e+08     123.4568e+06     1.234568e+08     123456789. 
+    1.234568e+07     12.34568e+06     1.234568e+07     12345679. 
+    1.234568e+06     1.234568e+06     1234568.     1234568. 
+    1.234568e+05     123.4568e+03     123456.8     123456.8 
+    1.234568e+04     12.34568e+03     12345.68     12345.68 
+    1.234568e+03     1.234568e+03     1234.568     1234.568 
+    1.234568e+02     123.4568e+00     123.4568     123.4568 
+    1.234568e+01     12.34568e+00     12.34568     12.34568 
+    1.234568e+00     1.234568e+00     1.234568     1.234568 
+    1.234568e-01     123.4568e-03     0.1234568     0.1234568 
+    1.234568e-02     12.34568e-03     0.01234568     0.0123456 
+    1.234568e-03     1.234568e-03     0.001234568     0.0012345 
+    1.234568e-04     123.4568e-06     0.0001234568     0.0001234 
+    1.234568e-05     12.34568e-06     1.234568e-05     0.0000123 
+    1.234568e-06     1.234568e-06     1.234568e-06     0.0000012 
+    1.234568e-07     123.4568e-09     1.234568e-07     0.0000001 
+    1.234568e-08     12.34568e-09     1.234568e-08     0.0000000 
+    1.234568e-09     1.234568e-09     1.234568e-09     0.0000000 
+    1.234568e-10     123.4568e-12     1.234568e-10     0.0000000 
+    1.234568e-11     12.34568e-12     1.234568e-11     0.0000000
+
+## []{#pForth-Design}pForth Design
+
+### []{#C-kernel}\'C\' kernel
+
+The pForth kernel is written in \'C\' for portability. The inner
+interpreter is implemented in the function ExecuteToken() which is in
+pf_inner.c.
+
+- void pfExecuteToken( ExecToken XT );
+
+It is passed an execution token the same as EXECUTE would accept. It
+handles threading of secondaries and also has a large switch() case
+statement to interpret primitives. It is in one huge routine to take
+advantage of register variables, and to reduce calling overhead.
+Hopefully, your compiler will optimise the switch() statement into a
+jump table so it will run fast.
+
+### []{#Dictionary-Structures}Dictionary Structures
+
+This Forth supports multiple dictionaries. Each dictionary consists of a
+header segment and a seperate code segment. The header segment contains
+link fields and names. The code segment contains tokens and data. The
+headers, as well as some entire dictionaries such as the compiler
+support words, can be discarded when creating a stand-alone app.
+
+\[NOT IMPLEMENTED\] Dictionaries can be split so that the compile time
+words can be placed above the main dictionary. Thus they can use the
+same relative addressing but be discarded when turnkeying.
+
+Execution tokens are either an index of a primitive ( n \<
+NUM_PRIMITIVES), or the offset of a secondary in the code segment. ( n
+\>= NUM_PRIMITIVES )
+
+The NAME HEADER portion of the dictionary contains a structure for each
+named word in the dictionary. It contains the following fields:
+
+- bytes
+      4 Link Field = relative address of previous name header
+      4 Code Pointer = relative address of corresponding code
+      n Name Field = name as counted string Headers are quad byte aligned.
+
+The CODE portion of the dictionary consists of the following structures:
+
+#### Primitive
+
+No Forth code. \'C\' code in \"pf_inner.c\".
+
+#### Secondary
+
+- 4*n Parameter Field containing execution tokens
+      4 ID_NEXT = 0 terminates secondary
+
+#### CREATE DOES\>
+
+- 4 ID_CREATE_P token
+      4 Token for optional DOES> code, OR ID_NEXT = 0
+      4 ID_NEXT = 0
+      n Body = arbitrary data
+
+#### Deferred Word
+
+- 4 ID_DEFER_P same action as ID_NOOP, identifies deferred words
+      4 Execution Token of word to execute.
+      4 ID_NEXT = 0
+
+#### Call to custom \'C\' function.
+
+- 4 ID_CALL_C
+      4 Pack C Call Info Bits
+
+  - 0-15 = Function Index Bits
+        16-23 = FunctionTable Index (Unused) Bits
+        24-30 = NumParams Bit
+        31 = 1 if function returns value
+
+  <!-- -->
+
+      4 ID_NEXT = 0
+
+------------------------------------------------------------------------
+
+## []{#Compiling-pForth}Compiling pForth
+
+A makefile is supplied that will help you compile pForth for your
+environment. You can customize the build by setting various compiler
+options.
+
+### []{#Compiler-Options}Compiler Options
+
+There are several versions of PForth that can be built. By default, the
+full kernel will be built. For custom builds, define the following
+options in the Makefile before compiling the \'C\' code:
+
+PF_DEFAULT_DICTIONARY=\"filename\"
+
+> Specify a dictionary to use in place of the default \"pforth.dic\",
+> for example \"/usr/lib/pforth/pforth.dic\".
+
+PF_NO_INIT
+
+- Don\'t compile the code used to initially build the dictionary. This
+  can be used to save space if you already have a prebuilt dictionary.
+
+PF_NO_SHELL
+
+- Don\'t compile the outer interpreter and Forth compiler. This can be
+  used with Cloned dictionaries.
+
+PF_NO_MALLOC
+
+- Replace malloc() and free() function with pForth\'s own version. See
+  pf_mem.c for more details.
+
+PF_USER_MALLOC=\'\"filename.h\"\'
+
+- Replace malloc() and free() function with users custom version. See
+  pf_mem.h for details.
+
+PF_MEM_POOL_SIZE=numbytes
+
+- Size of array in bytes used by pForth custom allocator.
+
+PF_NO_GLOBAL_INIT
+
+- Define this if you want pForth to not rely on initialization of global
+  variables by the loader. This may be required for some embedded
+  systems that may not have a fully functioning loader.  Take a look in
+  \"pfcustom.c\" for an example of its use.
+
+PF_USER_INC1=\'\"filename.h\"\'
+
+- File to include BEFORE other include files. Generally set to host
+  dependent files such as \"pf_mac.h\".
+
+PF_USER_INC2=\'\"filename.h\"\'
+
+- File to include AFTER other include files. Generally used to #undef
+  and re#define symbols. See \"pf_win32.h\" for an example.
+
+PF_NO_CLIB
+
+- Replace \'C\' lib calls like toupper and memcpy with pForth\'s own
+  version. This is useful for embedded systems.
+
+PF_USER_CLIB=\'\"filename.h\"\'
+
+- Rreplace \'C\' lib calls like toupper and memcpy with users custom
+  version. See pf_clib.h for details.
+
+PF_NO_FILEIO
+
+- System does not support standard file I/O so stub it out. Setting this
+  flag will automatically set PF_STATIC_DIC.
+
+PF_USER_CHARIO=\'\"filename.h\"\'
+
+- Replace stdio terminal calls like getchar() and putchar() with users
+  custom version. See pf_io.h for details.
+
+PF_USER_FILEIO=\'\"filename.h\"\'
+
+- Replace stdio file calls like fopen and fread with users custom
+  version. See pf_io.h for details.
+
+PF_USER_FLOAT=\'\"filename.h\"\'
+
+- Replace floating point math calls like sin and pow with users custom
+  version. Also defines PF_FLOAT.
+
+PF_USER_INIT=MyInit()
+
+- Call a user defined initialization function that returns a negative
+  error code if it fails.
+
+PF_USER_TERM=MyTerm()
+
+- Call a user defined void termination function.
+
+PF_STATIC_DIC
+
+- Compile in static dictionary instead of loading dictionary. from file.
+  Use \"utils/savedicd.fth\" to save a dictionary as \'C\' source code
+  in a file called \"pfdicdat.h\".
+
+PF_SUPPORT_FP
+
+- Compile ANSI floating point support.
+
+### []{#Building-pForth-Hosts}Building pForth on Supported Hosts
+
+To build on UNIX, do nothing, system will default to \"pf_unix.h\".
+
+To build on Macintosh:
+
+- -DPF_USER_INC1='"pf_mac.h"'
+
+To build on PCs:
+
+- -DPF_USER_INC2='"pf_win32.h"'
+
+To build a system that only runs turnkey or cloned binaries:
+
+- -DPF_NO_INIT -DPF_NO_SHELL
+
+### []{#Compiling-Embedded}Compiling for Embedded Systems
+
+You may want to create a version of pForth that can be run on a small
+system that does not support file I/O. This is useful when bringing up
+new computer systems. On UNIX systems, you can use the supplied gmake
+target. Simply enter:
+
+- gmake pfemb
+
+For other systems, here are the steps to create an embedded pForth.
+
+1.  Determine whether your target system has a different endian-ness
+    than your host system.  If the address of a long word is the address
+    of the most significant byte, then it is \"big endian\". Examples of
+    big endian processors are Sparc, Motorola 680x0 and PowerPC60x.  If
+    the address of a long word is the address of the least significant
+    byte, then it is \"Little Endian\". Examples of little endian
+    processors are Intel 8088 and derivatives such as the Intel Pentium,
+    X86. ARM processors can be configured as either big or little
+    endian.
+2.  If your target system has a different endian-ness than your host
+    system, then you must compile a version of pForth for your host that
+    matches the target.  Rebuild pForth with either PF_BIG_ENDIAN_DIC or
+    PF_LITTLE_ENDIAN_DIC defined.  You will need to rebuild pforth.dic
+    as well as the executable Forth.  If you do not specify one of these
+    variables, then the dictionary will match the native endian-ness of
+    the host processor.
+3.  Execute pForth. Notice the message regarding the endian-ness of the
+    dictionary.
+4.  Compile your custom Forth words on the host development system.
+5.  Compile the pForth utulity \"utils/savedicd.fth\".
+6.  Enter in pForth: SDAD
+7.  SDAD will generate a file called \"pfdicdat.h\" that contains your
+    dictionary in source code form.
+8.  Rewrite the character primitives sdTerminalOut(), sdTerminalIn() and
+    sdTerminalFlush() defined in pf_io.h to use your new computers
+    communications port.
+9.  Write a \"user_chario.h\" file based on the API defined in
+    \"pf_io.h\".
+10. Compile a new version of pForth for your target machine with the
+    following options:
+    1.  -DPF_NO_INIT -DPF_NO_MALLOC -DPF_NO_FILEIO \
+            -DPF_USER_CHARIO="user_chario.h" \
+            -DPF_NO_CLIB -DPF_STATIC_DIC
+11. The file \"pfdicdat.h\" will be compiled into this executable and
+    your dictionary will thus be included in the pForth executable as a
+    static array.
+12. Burn a ROM with your new pForth and run it on your target machine.
+13. If you compiled a version of pForth with different endian-ness than
+    your host system, do not use it for daily operation because it will
+    be much slower than a native version.
+
+### []{#Link-Custom-C}Linking with Custom \'C\' Functions
+
+You can call the pForth interpreter as an embedded tool in a \'C\'
+application. For an example of this, see the file pf_main.c. This
+application does nothing but load the dictionary and call the pForth
+interpreter.
+
+You can call \'C\' from pForth by adding your own custom \'C\' functions
+to a dispatch table, and then adding Forth words to the dictionary that
+call those functions. See the file \"pfcustom.c\" for more information.
+
+### []{#Testing-pForth}Testing your Compiled pForth
+
+Once you have compiled pForth, you can test it using the small
+verification suite we provide.  The first test you should run was
+written by John Hayes at John Hopkins University.  Enter:
+
+- pforth
+      include tester.fth
+      include coretest.fth
+      bye
+
+The output will be self explanatory.  There are also a number of tests
+that I have added that print the number of successes and failures.
+Enter:
+
+- pforth t_corex.fth
+      pforth t_locals.fth
+      pforth t_strings.fth
+      pforth t_floats.ft
+
+Note that t_corex.fth reveals an expected error because SAVE-INPUT is
+not fully implemented. (FIXME)\
+
+------------------------------------------------------------------------
+
+\
+PForth source code is freely available and is in the public domain.
+
+Back to [pForth Home Page](../pforth)\
+:::
+
+::: {#footer}
+\(C\) 1997-2015 Mobileer Inc - All Rights Reserved - [Contact
+Us](/contacts.php)
+:::
+:::::::::::::
diff --git a/forth/pf_tut.md b/forth/pf_tut.md
new file mode 100644
index 0000000..5b4d608
--- /dev/null
+++ b/forth/pf_tut.md
@@ -0,0 +1,1345 @@
+::::::::::::: {#container}
+:::::: {#header}
+::: {#leftheader}
+[![](/images/softsynth_logo.png){width="200" height="100"
+border="0"}](/)
+:::
+
+::: {#rightheader}
+:::
+
+::: {#midheader}
+# SoftSynth
+
+## \... music and computers \...
+:::
+
+\
+::::::
+
+:::: {#leftside}
+::: {#leftside_inner}
+- [Home](/index.php)
+- [Products](/products.php)
+- [JSyn](/jsyn/index.php)
+- [Syntona](/syntona/index.php)
+- [pForth](/pforth/index.php)
+- [Music](/music/index.php)
+- [Info](/info/index.php)
+- [News](/news/index.php)
+- [Links](/links/index.php)
+- [Contact Us](/contacts.php)
+- [About Us](/aboutus.php)
+:::
+::::
+
+:::: {#rightside}
+::: {#rightside_inner}
+### Projects
+
+  ---------------------------------------------------------------------------------------------------
+  [JSyn](/jsyn/) - modular synthesis API for Java.
+  [JMSL](https://www.algomusic.com/jmsl/){target="_blank"} - Java Music Specification Language
+  [PortAudio](https://www.portaudio.com/){target="_blank"} - cross platform audio I/O API for \'C\'
+  ---------------------------------------------------------------------------------------------------
+:::
+::::
+
+::: {#content}
+[pForth](/pforth/index.php)
+ : [GitHub](https://github.com/philburk/pforth/)
+ \| [Tutorial]{.current_link}  \| [Reference](/pforth/pf_ref.php)
+ \| [Links](/forthlinks.php)
+
+------------------------------------------------------------------------
+
+# Forth Tutorial
+
+------------------------------------------------------------------------
+
+Translations:
+[Chinese](http://vision.twbbs.org/%7Eletoh/forth/pf_tuttw.html){target="_blank"}
+by
+[Letoh](http://vision.twbbs.org/%7Eletoh/blog/?page_id=169){target="_blank"}
+
+by [Phil Burk](http://www.softsynth.com/philburk.html) of
+[SoftSynth.com](http://www.softsynth.com)
+
+## Table of Contents
+
+- [Forth Syntax](#Forth%20Syntax)
+- [Stack Manipulation](#The%20Stack)
+- [Arithmetic](#Arithmetic)
+- [Defining a New Word](#Defining%20a%20New%20Word)
+- [More Arithmetic](#More%20Arithmetic)
+  - [Arithmetic Overflow](#Arithmetic%20Overflow)
+  - [Convert Algebraic Expressions to
+    Forth](#Convert%20Algebraic%20Expressions%20to%20Forth)
+- [Character Input and Output](#Character%20Input%20and%20Output)
+- [Compiling from Files](#Compiling%20from%20Files)
+- [Variables](#Variables)
+- [Constants](#Constants)
+- [Logical Operators](#Logical%20Operators)
+- [Conditionals - IF ELSE THEN
+  CASE](#Conditionals%20-%20IF%20ELSE%20THEN%20CASE)
+- [Loops](#Loops)
+- [Text Input and Output](#Text%20Input%20and%20Output)
+- [Changing Numeric Base](#Changing%20Numeric%20Base)
+- [Answers to Problems](#Answers%20to%20Problems)
+
+The intent of this tutorial is to provide a series of experiments that
+will introduce you to the major concepts of Forth. It is only a starting
+point. Feel free to deviate from the sequences I provide. A free form
+investigation that is based on your curiosity is probably the best way
+to learn any language. Forth is especially well adapted to this type of
+learning.
+
+This tutorial is written for the PForth implementation of the ANS Forth
+standard. I have tried to restrict this tutorial to words that are part
+of the ANS standard but some PForth specific words may have crept in.
+
+In the tutorials, I will print the things you need to type in upper
+case, and indent them. You can enter them in upper or lower case. At the
+end of each line, press the RETURN (or ENTER) key; this causes Forth to
+interpret what you\'ve entered.
+
+## []{#Forth Syntax}Forth Syntax
+
+Forth has one of the simplest syntaxes of any computer language. The
+syntax can be stated as follows, \"**Forth code is a bunch of words with
+spaces between them.**\" This is even simpler than English! Each *word*
+is equivalent to a function or subroutine in a language like \'C\'. They
+are executed in the order they appear in the code. The following
+statement, for example, could appear in a Forth program:
+
+-  WAKE.UP EAT.BREAKFAST WORK EAT.DINNER PLAY SLEEP
+
+Notice that WAKE.UP has a dot between the WAKE and UP. The dot has no
+particular meaning to the Forth compiler. I simply used a dot to connect
+the two words together to make one word. Forth word names can have any
+combination of letters, numbers, or punctuation. We will encounter words
+with names like:
+
+-  ." #S SWAP ! @ ACCEPT . *
+
+They are all called *words*. The word **\$%%-GL7OP** is a legal Forth
+name, although not a very good one. It is up to the programmer to name
+words in a sensible manner.
+
+Now it is time to run your Forth and begin experimenting. Please consult
+the manual for your Forth for instructions on how to run it.
+
+## []{#The Stack}Stack Manipulation
+
+The Forth language is based on the concept of a *stack*. Imagine a stack
+of blocks with numbers on them. You can add or remove numbers from the
+top of the stack. You can also rearrange the order of the numbers. Forth
+uses several stacks. The *DataStack* is the one used for passing data
+between Forth words so we will concentrate our attention there. The
+*Return Stack* is another Forth stack that is primarily for internal
+system use. In this tutorial, when we refer to the \"stack,\" we will be
+referring to the Data Stack.
+
+The stack is initially empty. To put some numbers on the stack, enter:
+
+- 23 7 9182
+
+Let\'s now print the number on top of the stack using the Forth word \'
+**.** \', which is pronounced \" dot \". This is a hard word to write
+about in a manual because it is a single period.
+
+Enter: **. **
+
+You should see the last number you entered, 9182 , printed. Forth has a
+very handy word for showing you what\'s on the stack. It is **.S** ,
+which is pronounced \"dot S\". The name was constructed from \"dot\" for
+print, and \"S\" for stack. (PForth will automatically print the stack
+after every line if the TRACE-STACK variable is set to TRUE.) If you
+enter:
+
+- .S
+
+you will see your numbers in a list. The number at the far right is the
+one on top of the stack.
+
+You will notice that the 9182 is not on the stack. The word \' . \'
+removes the number on top of the stack before printing it. In contrast,
+\' .S \' leaves the stack untouched.
+
+We have a way of documenting the effect of words on the stack with a
+*stack diagram*. A stack diagram is contained in parentheses. In Forth,
+the parentheses indicate a comment. In the examples that follow, you do
+not need to type in the comments. When you are programming, of course,
+we encourage the use of comments and stack diagrams to make your code
+more readable. In this manual, we often indicate stack diagrams in
+**bold text** like the one that follows. Do not type these in. The stack
+diagram for a word like \' . \' would be:
+
+**`. ( N -- , print number on top of stack )`**
+
+The symbols to the left of \-- describe the parameters that a word
+expects to process. In this example, N stands for any integer number. To
+the right of \--, up to the comma, is a description of the stack
+parameters when the word is finished, in this case there are none
+because \'dot\' \"eats\" the N that was passed in. (Note that the stack
+descriptions are not necessary, but they are a great help when learning
+other peoples programs.)
+
+The text following the comma is an English description of the word. You
+will note that after the \-- , N is gone. You may be concerned about the
+fact that there were other numbers on the stack, namely 23 and 7 . The
+stack diagram, however, only describes the portion of the stack that is
+affected by the word. For a more detailed description of the stack
+diagrams, there is a special section on them in this manual right before
+the main glossary section.
+
+Between examples, you will probably want to clear the stack. If you
+enter **0SP**, pronounced \"zero S P\", then the stack will be cleared.
+
+Since the stack is central to Forth, it is important to be able to alter
+the stack easily. Let\'s look at some more words that manipulate the
+stack. Enter:
+
+- 0SP .S \ That's a 'zero' 0, not an 'oh' O.
+      777 DUP .S
+
+You will notice that there are two copies of 777 on the stack. The word
+**DUP** duplicates the top item on the stack. This is useful when you
+want to use the number on top of the stack and still have a copy. The
+stack diagram for DUP would be:
+
+**`DUP ( n -- n n , DUPlicate top of stack )`**
+
+Another useful word, is **SWAP**. Enter:
+
+- 0SP 
+      23 7 .S 
+      SWAP .S 
+      SWAP .S
+
+The stack diagram for SWAP would be:
+
+**`SWAP ( a b -- b a , swap top two items on stack )`**
+
+Now enter:
+
+- OVER .S
+      OVER .S
+
+The word **OVER** causes a copy of the second item on the stack to
+leapfrog over the first. It\'s stack diagram would be:
+
+**`OVER ( a b -- a b a , copy second item on stack )`**
+
+Here is another commonly used Forth word:
+
+**`DROP ( a -- , remove item from the stack )`**
+
+Can you guess what we will see if we enter:
+
+- 0SP 11 22 .S
+      DROP .S
+
+Another handy word for manipulating the stack is **ROT**. Enter:
+
+- 0SP
+      11 22 33 44 .S
+      ROT .S
+
+The stack diagram for ROT is, therefore:
+
+**`ROT ( a b c -- b c a , ROTate third item to top ) `**
+
+You have now learned the more important stack manipulation words. You
+will see these in almost every Forth program. I should caution you that
+if you see too many stack manipulation words being used in your code
+then you may want to reexamine and perhaps reorganize your code. You
+will often find that you can avoid excessive stack manipulations by
+using *local or global VARIABLES* which will be discussed later.
+
+If you want to grab any arbitrary item on the stack, use **PICK** . Try
+entering:
+
+- 0SP
+      14 13 12 11 10
+      3 PICK . ( prints 13 )
+      0 PICK . ( prints 10 )
+      4 PICK .
+
+PICK makes a copy of the Nth item on the stack. The numbering starts
+with zero, therefore:
+
+- `0 PICK is equivalent to DUP`\
+  `1 PICK is equivalent to OVER`
+
+**`PICK ( ... v3 v2 v1 v0 N -- ... v3 v2 v1 v0 vN ) `**
+
+(Warning. The Forth-79 and FIG Forth standards differ from the ANS and
+Forth \'83 standard in that their PICK numbering starts with one, not
+zero.)
+
+I have included the stack diagrams for some other useful stack
+manipulation words. Try experimenting with them by putting numbers on
+the stack and calling them to get a feel for what they do. Again, the
+text in parentheses is just a comment and need not be entered.
+
+**`DROP ( n -- , remove top of stack ) `**
+
+**`?DUP ( n -- n n | 0 , duplicate only if non-zero, '|' means OR ) `**
+
+**`-ROT ( a b c -- c a b , rotate top to third position ) `**
+
+**`2SWAP ( a b c d -- c d a b , swap pairs ) `**
+
+**`2OVER ( a b c d -- a b c d a b , leapfrog pair ) `**
+
+**`2DUP ( a b -- a b a b , duplicate pair ) `**
+
+**`2DROP ( a b -- , remove pair ) `**
+
+**`NIP ( a b -- b , remove second item from stack ) `**
+
+**`TUCK ( a b -- b a b , copy top item to third position ) `**
+
+### []{#Problems - Stack}Problems:
+
+Start each problem by entering:
+
+- 0SP 11 22 33
+
+Then use the stack manipulation words you have learned to end up with
+the following numbers on the stack:
+
+- 1) 11 33 22 22
+
+      2) 22 33
+
+      3) 22 33 11 11 22
+
+      4) 11 33 22 33 11
+
+      5) 33 11 22 11 22
+
+[Answers to the problems](#Answers%20to%20Problems) can be found at the
+end of this tutorial.
+
+## []{#Arithmetic}Arithmetic
+
+Great joy can be derived from simply moving numbers around on a stack.
+Eventually, however, you\'ll want to do something useful with them. This
+section describes how to perform arithmetic operations in Forth.
+
+The Forth arithmetic operators work on the numbers currently on top of
+the stack. If you want to add the top two numbers together, use the
+Forth word **+** , pronounced \"plus\". Enter:
+
+- 2 3 + .
+      2 3 + 10 + .
+
+This style of expressing arithmetic operations is called *Reverse Polish
+Notation,* or *RPN*. It will already be familiar to those of you with HP
+calculators. In the following examples, I have put the algebraic
+equivalent representation in a comment.
+
+Some other arithmetic operators are **- \* /** . Enter:
+
+- 30 5 - . ( 25=30-5 )
+      30 5 / . ( 6=30/5 )
+      30 5 * . ( 150=30*5 )
+      30 5 + 7 / . \ 5=(30+5)/7
+
+Some combinations of operations are very common and have been coded in
+assembly language for speed. For example, **2\*** is short for 2 \* .
+You should use these whenever possible to increase the speed of your
+program. These include:
+
+- 1+ 1- 2+ 2- 2* 2/
+
+Try entering:
+
+- 10 1- .
+      7 2* 1+ . ( 15=7*2+1 )
+
+One thing that you should be aware of is that when you are doing
+division with integers using / , the remainder is lost. Enter:
+
+- 15 5 / .
+      17 5 / .
+
+This is true in all languages on all computers. Later we will examine
+**/MOD** and **MOD** which do give the remainder.
+
+## []{#Defining a New Word}Defining a New Word
+
+It\'s now time to write a *small program* in Forth. You can do this by
+defining a new word that is a combination of words we have already
+learned. Let\'s define and test a new word that takes the average of two
+numbers.
+
+We will make use of two new words, **:** ( \"colon\"), and **;** (
+\"semicolon\") . These words start and end a typical *Forth definition*.
+Enter:
+
+- : AVERAGE ( a b -- avg ) + 2/ ;
+
+Congratulations. You have just written a Forth program. Let\'s look more
+closely at what just happened. The colon told Forth to add a new word to
+its list of words. This list is called the Forth dictionary. The name of
+the new word will be whatever name follows the colon. Any Forth words
+entered after the name will be compiled into the new word. This
+continues until the semicolon is reached which finishes the definition.
+
+Let\'s test this word by entering:
+
+- 10 20 AVERAGE . ( should print 15 )
+
+Once a word has been defined, it can be used to define more words.
+Let\'s write a word that tests our word.. Enter:
+
+- : TEST ( --) 50 60 AVERAGE . ;
+      TEST
+
+Try combining some of the words you have learned into new Forth
+definitions of your choice. If you promise not to be overwhelmed, you
+can get a list of the words that are available for programming by
+entering:
+
+- WORDS
+
+Don\'t worry, only a small fraction of these will be used directly in
+your programs.
+
+## []{#More Arithmetic}More Arithmetic
+
+When you need to know the remainder of a divide operation. /MOD will
+return the remainder as well as the quotient. the word MOD will only
+return the remainder. Enter:
+
+- 0SP
+      53 10 /MOD .S
+      0SP
+      7 5 MOD .S
+
+Two other handy words are **MIN** and **MAX** . They accept two numbers
+and return the MINimum or MAXimum value respectively. Try entering the
+following:
+
+- 56 34 MAX .
+      56 34 MIN .
+      -17 0 MIN .
+
+Some other useful words are:
+
+**`ABS ( n -- abs(n) , absolute value of n ) `**
+
+**`NEGATE ( n -- -n , negate value, faster then -1 * ) `**
+
+**`LSHIFT ( n c -- n<<c , left shift of n ) `**
+
+**`RSHIFT ( n c -- n>>c , logical right shift of n ) `**
+
+**`ARSHIFT ( n c -- n>>c ) , arithmetic right shift of n ) `**
+
+ARSHIFT or LSHIFT can be used if you have to multiply quickly by a power
+of 2 . A right shift is like doing a divide by 2. This is often faster
+than doing a regular multiply or divide. Try entering:
+
+- : 256* 8 LSHIFT ;
+      3 256* .
+
+### []{#Arithmetic Overflow}Arithmetic Overflow
+
+If you are having problems with your calculation overflowing the 32-bit
+precision of the stack, then you can use **\*/** . This produces an
+intermediate result that is 64 bits long. Try the following three
+methods of doing the same calculation. Only the one using \*/ will yield
+the correct answer, 5197799.
+
+- 34867312 99154 * 665134 / .
+      34867312 665134 / 99154 * .
+      34867312 99154 665134 */ .
+
+#### []{#Convert Algebraic Expressions to Forth}Convert Algebraic Expressions to Forth
+
+How do we express complex algebraic expressions in Forth? For example:
+20 + (3 \* 4)
+
+To convert this to Forth you must order the operations in the order of
+evaluation. In Forth, therefore, this would look like:
+
+- 3 4 * 20 +
+
+Evaluation proceeds from left to right in Forth so there is no
+ambiguity. Compare the following algebraic expressions and their Forth
+equivalents: (Do **not** enter these!)
+
+- (100+50)/2 ==> 100 50 + 2/
+      ((2*7) + (13*5)) ==> 2 7 * 13 5 * +
+
+If any of these expressions puzzle you, try entering them one word at a
+time, while viewing the stack with .S .
+
+### []{#Problems - Square}Problems:
+
+Convert the following algebraic expressions to their equivalent Forth
+expressions. (Do **not** enter these because they are not Forth code!)
+
+- (12 * ( 20 - 17 ))
+
+      (1 - ( 4 * (-18) / 6) )
+
+      ( 6 * 13 ) - ( 4 * 2 * 7 )
+
+Use the words you have learned to write these new words:
+
+- SQUARE ( N -- N*N , calculate square )
+
+      DIFF.SQUARES ( A B -- A*A-B*B , difference of squares )
+
+      AVERAGE4 ( A B C D -- [A+B+C+D]/4 )
+
+      HMS>SECONDS ( HOURS MINUTES SECONDS -- TOTAL-SECONDS , convert )
+
+[Answers to the problems](#Answers%20to%20Problems) can be found at the
+end of this tutorial.
+
+## []{#Character Input and Output}Character Input and Output
+
+The numbers on top of the stack can represent anything. The top number
+might be how many blue whales are left on Earth or your weight in
+kilograms. It can also be an ASCII character. Try entering the
+following:
+
+- 72 EMIT 105 EMIT
+
+You should see the word \"Hi\" appear before the OK. The 72 is an ASCII
+\'H\' and 105 is an \'i\'. EMIT takes the number on the stack and
+outputs it as a character. If you want to find the ASCII value for any
+character, you can use the word ASCII . Enter:
+
+- CHAR W .
+      CHAR % DUP . EMIT
+      CHAR A DUP .
+      32 + EMIT
+
+Here is a complete [ASCII chart](http://www.asciitable.com/).
+
+Notice that the word CHAR is a bit unusual because its input comes not
+from the stack, but from the following text. In a stack diagram, we
+represent that by putting the input in angle brackets, \<input\>. Here
+is the stack diagram for CHAR.
+
+**`CHAR ( <char> -- char , get ASCII value of a character ) `**
+
+Using EMIT to output character strings would be very tedious. Luckily
+there is a better way. Enter:
+
+- : TOFU ." Yummy bean curd!" ;
+      TOFU
+
+The word **.\"** , pronounced \"dot quote\", will take everything up to
+the next quotation mark and print it to the screen. Make sure you leave
+a space after the first quotation mark. When you want to have text begin
+on a new line, you can issue a carriage return using the word **CR** .
+Enter:
+
+- : SPROUTS ." Miniature vegetables." ;
+      : MENU
+          CR TOFU CR SPROUTS CR
+      ;
+      MENU
+
+You can emit a blank space with **SPACE** . A number of spaces can be
+output with SPACES . Enter:
+
+- CR TOFU SPROUTS
+      CR TOFU SPACE SPROUTS
+      CR 10 SPACES TOFU CR 20 SPACES SPROUTS
+
+For character input, Forth uses the word **KEY** which corresponds to
+the word EMIT for output. KEY waits for the user to press a key then
+leaves its value on the stack. Try the following.
+
+- : TESTKEY ( -- )
+          ." Hit a key: " KEY CR
+          ." That = " . CR
+      ;
+      TESTKEY
+
+\[Note: On some computers, the input if buffered so you will need to hit
+the ENTER key after typing your character.\]
+
+**`EMIT ( char -- , output character ) `**
+
+**`KEY ( -- char , input character ) `**
+
+**`SPACE ( -- , output a space ) `**
+
+**`SPACES ( n -- , output n spaces ) `**
+
+**`CHAR ( <char> -- char , convert to ASCII ) `**
+
+**`CR ( -- , start new line , carriage return ) `**
+
+**`." ( -- , output " delimited text ) `**
+
+##   []{#Compiling from Files}Compiling from Files
+
+PForth can read read from ordinary text files so you can use any editor
+that you wish to write your programs.
+
+### Sample Program
+
+Enter into your file, the following code.
+
+- \ Sample Forth Code
+      \ Author: your name
+
+      : SQUARE ( n -- n*n , square number )
+          DUP *
+      ;
+
+      : TEST.SQUARE ( -- )
+          CR ." 7 squared = "
+          7 SQUARE . CR
+      ;
+
+Now save the file to disk.
+
+The text following the **\\** character is treated as a comment. This
+would be a REM statement in BASIC or a /\*\-\--\*/ in \'C\'. The text in
+parentheses is also a comment.
+
+### Using INCLUDE
+
+\"INCLUDE\" in Forth means to compile from a file.
+
+You can compile this file using the INCLUDE command. If you saved your
+file as WORK:SAMPLE, then compile it by entering:
+
+- INCLUDE SAMPLE.FTH
+
+Forth will compile your file and tell you how many bytes it has added to
+the dictionary. To test your word, enter:
+
+- TEST.SQUARE
+
+Your two words, SQUARE and TEST.SQUARE are now in the Forth dictionary.
+We can now do something that is very unusual in a programming language.
+We can \"uncompile\" the code by telling Forth to **FORGET** it. Enter:
+
+- FORGET SQUARE
+
+This removes SQUARE and everything that follows it, ie. TEST.SQUARE,
+from the dictionary. If you now try to execute TEST.SQUARE it won\'t be
+found.
+
+Now let\'s make some changes to our file and reload it. Go back into the
+editor and make the following changes: (1) Change TEST.SQUARE to use 15
+instead of 7 then (2) Add this line right before the definition of
+SQUARE:
+
+- ANEW TASK-SAMPLE.FTH
+
+Now Save your changes and go back to the Forth window.
+
+You\'re probably wondering what the line starting with **ANEW** was for.
+ANEW is always used at the beginning of a file. It defines a special
+marker word in the dictionary before the code. The word typically has
+\"TASK-\" as a prefix followed by the name of the file. When you
+ReInclude a file, ANEW will automatically FORGET the old code starting
+after the ANEW statement. This allows you to Include a file over and
+over again without having to manually FORGET the first word. If the code
+was not forgotten, the dictionary would eventually fill up.
+
+If you have a big project that needs lots of files, you can have a file
+that will load all the files you need. Sometimes you need some code to
+be loaded that may already be loaded. The word **INCLUDE?** will only
+load code if it isn\'t already in the dictionary. In this next example,
+I assume the file is on the volume WORK: and called SAMPLE. If not,
+please substitute the actual name. Enter:
+
+- FORGET TASK-SAMPLE.FTH
+      INCLUDE? SQUARE WORK:SAMPLE
+      INCLUDE? SQUARE WORK:SAMPLE
+
+Only the first INCLUDE? will result in the file being loaded.
+
+## []{#Variables}Variables
+
+Forth does not rely as heavily on the use of variables as other compiled
+languages. This is because values normally reside on the stack. There
+are situations, of course, where variables are required. To create a
+variable, use the word **VARIABLE** as follows:
+
+- VARIABLE MY-VAR
+
+This created a variable named MY-VAR . A space in memory is now reserved
+to hold its 32-bit value. The word VARIABLE is what\'s known as a
+\"defining word\" since it creates new words in the dictionary. Now
+enter:
+
+- MY-VAR .
+
+The number you see is the address, or location, of the memory that was
+reserved for MY-VAR. To store data into memory you use the word **!** ,
+pronounced \"store\". It looks like an exclamation point, but to a Forth
+programmer it is the way to write 32-bit data to memory. To read the
+value contained in memory at a given address, use the Forth word **@** ,
+pronounced \"fetch\". Try entering the following:
+
+- 513 MY-VAR !
+      MY-VAR @ .
+
+This sets the variable MY-VAR to 513 , then reads the value back and
+prints it. The stack diagrams for these words follows:
+
+**`@ ( address -- value , FETCH value FROM address in memory ) `**
+
+**`! ( value address -- , STORE value TO address in memory )`**
+
+**`VARIABLE ( <name> -- , define a 4 byte memory storage location)`**
+
+A handy word for checking the value of a variable is **?** , pronounced
+\"question\". Try entering:
+
+- MY-VAR ?
+
+If ? wasn\'t defined, we could define it as:
+
+- : ? ( address -- , look at variable )
+          @ .
+      ;
+
+Imagine you are writing a game and you want to keep track of the highest
+score. You could keep the highest score in a variable. When you reported
+a new score, you could check it aginst the highest score. Try entering
+this code in a file as described in the previous section:
+
+- VARIABLE HIGH-SCORE
+
+      : REPORT.SCORE ( score -- , print out score )
+          DUP CR ." Your Score = " . CR
+          HIGH-SCORE @ MAX ( calculate new high )
+          DUP ." Highest Score = " . CR
+          HIGH-SCORE ! ( update variable )
+      ;
+
+Save the file to disk, then compile this code using the INCLUDE word.
+Test your word as follows:
+
+- 123 REPORT.SCORE
+      9845 REPORT.SCORE
+      534 REPORT.SCORE
+
+The Forth words @ and ! work on 32-bit quantities. Some Forths are
+\"16-bit\" Forths. They fetch and store 16-bit quantities. Forth has
+some words that will work on 8 and 16-bit values. C@ and C! work
+characters which are usually for 8-bit bytes. The \'C\' stands for
+\"Character\" since ASCII characters are 8-bit numbers. Use W@ and W!
+for 16-bit \"Words.\"
+
+Another useful word is **+!** , pronounced \"plus store.\" It adds a
+value to a 32-bit value in memory. Try:
+
+- 20 MY-VAR !
+      5 MY-VAR +!
+      MY-VAR @ .
+
+Forth also provides some other words that are similar to VARIABLE. Look
+in the glossary for VALUE and ARRAY. Also look at the section on
+\"[local variables](pf_ref.php#Local%20Variables%20%7B%20foo%20--%7D?)\"
+which are variables which only exist on the stack while a Forth word is
+executing.
+
+*A word of warning about fetching and storing to memory*: You have now
+learned enough about Forth to be dangerous. The operation of a computer
+is based on having the right numbers in the right place in memory. You
+now know how to write new numbers to any place in memory. Since an
+address is just a number, you could, but shouldn\'t, enter:
+
+- 73 253000 ! ( Do NOT do this. )
+
+The 253000 would be treated as an address and you would set that memory
+location to 73. I have no idea what will happen after that, maybe
+nothing. This would be like firing a rifle through the walls of your
+apartment building. You don\'t know who or what you are going to hit.
+Since you share memory with other programs including the operating
+system, you could easily cause the computer to behave strangely, even
+crash. Don\'t let this bother you too much, however. Crashing a
+computer, unlike crashing a car, does not hurt the computer. You just
+have to reboot. The worst that could happen is that if you crash while
+the computer is writing to a disk, you could lose a file. That\'s why we
+make backups. This same potential problem exists in any powerful
+language, not just Forth. This might be less likely in BASIC, however,
+because BASIC protects you from a lot of things, including the danger of
+writing powerful programs.
+
+Another way to get into trouble is to do what\'s called an \"odd address
+memory access.\" The 68000 processor arranges words and longwords, 16
+and 32 bit numbers, on even addresses. If you do a **@** or **!** , or
+**W@** or **W!** , to an odd address, the 68000 processor will take
+exception to this and try to abort.
+
+Forth gives you some protection from this by trapping this exception and
+returning you to the OK prompt. If you really need to access data on an
+odd address, check out the words **ODD@** and **ODD!** in the glossary.
+**C@** and **C!** work fine on both odd and even addresses.
+
+## []{#Constants}Constants
+
+If you have a number that is appearing often in your program, we
+recommend that you define it as a \"constant.\" Enter:
+
+- 128 CONSTANT MAX_CHARS
+      MAX_CHARS .
+
+We just defined a word called MAX_CHARS that returns the value on the
+stack when it was defined. It cannot be changed unless you edit the
+program and recompile. Using **CONSTANT** can improve the readability of
+your programs and reduce some bugs. Imagine if you refer to the number
+128 very often in your program, say 8 times. Then you decide to change
+this number to 256. If you globally change 128 to 256 you might change
+something you didn\'t intend to. If you change it by hand you might miss
+one, especially if your program occupies more than one file. Using
+CONSTANT will make it easy to change. The code that results is equally
+as fast and small as putting the numbers in directly. I recommend
+defining a constant for almost any number.
+
+## []{#Logical Operators}Logical Operators
+
+These next two sections are concerned with decision making. This first
+section deals with answering questions like \"Is this value too large?\"
+or \"Does the guess match the answer?\". The answers to questions like
+these are either TRUE or FALSE. Forth uses a 0 to represent **FALSE**
+and a -1 to represent **TRUE**. TRUE and FALSE have been capitalized
+because they have been defined as Forth constants. Try entering:
+
+- 23 71 = .
+      18 18 = .
+
+You will notice that the first line printed a 0, or FALSE, and the
+second line a -1, or TRUE. The equal sign in Forth is used as a
+question, not a statement. It asks whether the top two items on the
+stack are equal. It does not set them equal. There are other questions
+that you can ask. Enter:
+
+- 23 198 < .
+      23 198 > .
+      254 15 > .
+
+In California, the drinking age for alcohol is 21. You could write a
+simple word now to help bartenders. Enter:
+
+- : DRINK? ( age -- flag , can this person drink? )
+          20 >
+      ;
+
+      20 DRINK? .
+      21 DRINK? .
+      43 DRINK? .
+
+The word FLAG in the stack diagram above refers to a logical value.
+
+Forth provides special words for comparing a number to 0. They are
+**0=** **0\>** and **0\<** . Using 0\> is faster than calling 0 and \>
+separately. Enter:
+
+- `23 0> . ( print -1 )`\
+  `-23 0> . ( print 0 )`\
+  `23 0= . ( print 0 )`
+
+For more complex decisions, you can use the *Boolean* operators **OR** ,
+**AND** , and **NOT** . OR returns a TRUE if either one or both of the
+top two stack items are true.
+
+- TRUE TRUE OR .
+      TRUE FALSE OR .
+      FALSE FALSE OR .
+
+AND only returns a TRUE if both of them are true.
+
+- TRUE TRUE AND .
+      TRUE FALSE AND .
+
+NOT reverses the value of the flag on the stack. Enter:
+
+- TRUE .
+      TRUE NOT .
+
+Logical operators can be combined.
+
+- 56 3 > 56 123 < AND .
+      23 45 = 23 23 = OR .
+
+Here are stack diagrams for some of these words. See the glossary for a
+more complete list.
+
+**`< ( a b -- flag , flag is true if A is less than B )`**
+
+**`> ( a b -- flag , flag is true if A is greater than B )`**
+
+**`= ( a b -- flag , flag is true if A is equal to B )`**
+
+**`0= ( a -- flag , true if a equals zero )`**
+
+**`OR ( a b -- a||b , perform logical OR of bits in A and B )`**
+
+**`AND ( a b -- a&b , perform logical AND of bits in A and B )`**
+
+**`NOT ( flag -- opposite-flag , true if false, false if true )`**
+
+### []{#Problems - Logical}Problems:
+
+1\) Write a word called LOWERCASE? that returns TRUE if the number on
+top of the stack is an ASCII lowercase character. An ASCII \'a\' is 97 .
+An ASCII \'z\' is 122 . Test using the characters \" A \` a q z { \".
+
+- CHAR A LOWERCASE? . ( should print 0 )
+      CHAR a LOWERCASE? . ( should print -1 )
+
+[Answers to the problems](#Answers%20to%20Problems) can be found at the
+end of this tutorial.
+
+## []{#Conditionals - IF ELSE THEN CASE}Conditionals - IF ELSE THEN CASE
+
+You will now use the TRUE and FALSE flags you learned to generate in the
+last section. The \"flow of control\" words accept flags from the stack,
+and then possibly \"branch\" depending on the value. Enter the following
+code.
+
+- : .L ( flag -- , print logical value )
+          IF ." True value on stack!"
+          ELSE ." False value on stack!"
+          THEN
+      ;
+
+      0 .L
+      FALSE .L
+      TRUE .L
+      23 7 < .L
+
+You can see that when a TRUE was on the stack, the first part got
+executed. If a FALSE was on the stack, then the first part was skipped,
+and the second part was executed. One thing you will find interesting is
+that if you enter:
+
+- 23 .L
+
+the value on the stack will be treated as true. The flow of control
+words consider any value that does not equal zero to be TRUE.
+
+The **ELSE** word is optional in the **IF\...THEN** construct. Try the
+following:
+
+- : BIGBUCKS? ( amount -- )
+          1000 >
+          IF ." That's TOO expensive!"
+          THEN
+      ;
+
+      531 BIGBUCKS?
+      1021 BIGBUCKS?
+
+Many Forths also support a **CASE** statement similar to switch() in
+\'C\'. Enter:
+
+- : TESTCASE ( N -- , respond appropriately )
+          CASE
+              0 OF ." Just a zero!" ENDOF
+              1 OF ." All is ONE!" ENDOF
+              2 OF WORDS ENDOF
+              DUP . ." Invalid Input!"
+          ENDCASE CR
+      ;
+
+      0 TESTCASE
+      1 TESTCASE
+      5 TESTCASE
+
+See CASE in the glossary for more information.
+
+### []{#Problems - Conditionals}Problems:
+
+1\) Write a word called DEDUCT that subtracts a value from a variable
+containing your checking account balance. Assume the balance is in
+dollars. Print the balance. Print a warning if the balance is negative.
+
+- VARIABLE ACCOUNT
+
+      : DEDUCT ( n -- , subtract N from balance )
+          ????????????????????????????????? ( you fill this in )
+      ;
+
+      300 ACCOUNT ! ( initial funds )
+      40 DEDUCT ( prints 260 )
+      200 DEDUCT ( print 60 )
+      100 DEDUCT ( print -40 and give warning! )
+
+[Answers to the problems](#Answers%20to%20Problems) can be found at the
+end of this tutorial.
+
+## []{#Loops}Loops
+
+Another useful pair of words is **BEGIN\...UNTIL** . These are used to
+loop until a given condition is true. Try this:
+
+- : COUNTDOWN  ( N -- )
+          BEGIN
+              DUP . CR       ( print number on top of stack )
+              1-  DUP  0<    ( loop until we go negative )
+          UNTIL
+      ;
+
+      16 COUNTDOWN
+
+This word will count down from N to zero.
+
+If you know how many times you want a loop to execute, you can use the
+**DO\...LOOP** construct. Enter:
+
+- : SPELL
+          ." ba"
+          4 0 DO
+              ." na"
+          LOOP
+      ;
+
+This will print \"ba\" followed by four occurrences of \"na\". The
+ending value is placed on the stack before the beginning value. Be
+careful that you don\'t pass the values in reverse. Forth will go \"the
+long way around\" which could take awhile. The reason for this order is
+to make it easier to pass the loop count into a word on the stack.
+Consider the following word for doing character graphics. Enter:
+
+- : PLOT# ( n -- )
+          0 DO
+              [CHAR] - EMIT
+          LOOP CR
+      ;
+
+      CR 9 PLOT# 37 PLOT#
+
+If you want to access the loop counter you can use the word I . Here is
+a simple word that dumps numbers and their associated ASCII characters.
+
+- : .ASCII ( end start -- , dump characters )
+          DO
+              CR I . I EMIT
+          LOOP CR
+      ;
+
+      80 64 .ASCII
+
+If you want to leave a DO LOOP before it finishes, you can use the word
+**LEAVE**. Enter:
+
+- : TEST.LEAVE  ( -- , show use of leave )
+          100 0
+          DO
+              I . CR  \ print loop index
+              I 20 >  \ is I over 20
+              IF
+                  LEAVE
+              THEN
+          LOOP
+      ;
+      TEST.LEAVE  \ will print 0 to 20
+
+Please consult the manual to learn about the following words **+LOOP**
+and **RETURN** . FIXME
+
+Another useful looping construct is the **BEGIN WHILE REPEAT** loop.
+This allows you to make a test each time through the loop before you
+actually do something. The word WHILE will continue looping if the flag
+on the stack is True. Enter:
+
+- : SUM.OF.N ( N -- SUM[N] , calculate sum of N integers )
+          0  \ starting value of SUM
+          BEGIN
+              OVER 0>   \ Is N greater than zero?
+          WHILE
+              OVER +  \ add N to sum
+              SWAP 1- SWAP  \ decrement N
+          REPEAT
+          SWAP DROP  \ get rid on N
+      ;
+
+      4 SUM.OF.N    \ prints 10   ( 1+2+3+4 )
+
+### []{#Problems - Loops}Problems:
+
+1\) Rewrite SUM.OF.N using a DO LOOP.
+
+2\) Rewrite SUM.OF.N using BEGIN UNTIL.
+
+3\) For bonus points, write SUM.OF.N without using any looping or
+conditional construct!
+
+[Answers to the problems](#Answers%20to%20Problems) can be found at the
+end of this tutorial.
+
+## []{#Text Input and Output}Text Input and Output
+
+You learned earlier how to do single character I/O. This section
+concentrates on using strings of characters. You can embed a text string
+in your program using S\". Note that you must follow the S\" by one
+space. The text string is terminated by an ending \" .Enter:
+
+- : TEST S" Hello world!" ;
+      TEST .S
+
+Note that TEST leaves two numbers on the stack. The first number is the
+address of the first character. The second number is the number of
+characters in the string. You can print the characters of the string as
+follows.
+
+- TEST DROP       \ get rid of number of characters
+      DUP C@ EMIT     \ prints first character, 'H'
+      CHAR+ DUP C@ EMIT  \ prints second character, 'e'
+      \ and so on
+
+CHAR+ advances the address to the next character. You can print the
+entire string using TYPE.
+
+- TEST  TYPE
+      TEST  2/  TYPE   \ print half of string
+
+It would be nice if we could simply use a single address to describe a
+string and not have to pass the number of characters around. \'C\' does
+this by putting a zero at the end of the string to show when it ends.
+Forth has a different solution. A text string in Forth consists of a
+character count in the first byte, followed immediately by the
+characters themselves. This type of character string can be created
+using the Forth word C\" , pronounced \'c quote\'. Enter:
+
+- : T2 C" Greetings Fred" ;
+      T2 .
+
+The number that was printed was the address of the start of the string.
+It should be a byte that contains the number of characters. Now enter:
+
+- T2 C@ .
+
+You should see a 14 printed. Remember that C@ fetches one character/byte
+at the address on the stack. You can convert a counted Forth string to
+an address and count using COUNT.
+
+- T2 COUNT .S
+      TYPE
+
+The word **COUNT** extracts the number of characters and their starting
+address. COUNT will only work with strings of less than 256 characters,
+since 255 is the largest number that can be stored in the count byte.
+TYPE will, however, work with longer strings since the length is on the
+stack. Their stack diagrams follow:
+
+**`CHAR+ ( address -- address' , add the size of one character )`**
+
+**`COUNT ( $addr -- addr #bytes , extract string information ) `**
+
+**`TYPE ( addr #bytes -- , output characters at addr )`**
+
+The \$addr is the address of a count byte. The dollar sign is often used
+to mark words that relate to strings.
+
+You can easily input a string using the word **ACCEPT**. (You may want
+to put these upcoming examples in a file since they are very handy.) The
+word **ACCEPT** receives characters from the keyboard and places them at
+any specified address. **ACCEPT** takes input characters until a maximum
+is reached or an end of line character is entered. **ACCEPT** returns
+the number of characters entered. You can write a word for entering
+text. Enter:
+
+- : INPUT$ ( -- $addr )
+          PAD  1+ ( leave room for byte count )
+          127 ACCEPT ( recieve a maximum of 127 chars )
+          PAD C! ( set byte count )
+          PAD ( return address of string )
+      ;
+
+      INPUT$ COUNT TYPE
+
+Enter a string which should then be echoed. You could use this in a
+program that writes form letters.
+
+- : FORM.LETTER ( -- )
+          ." Enter customer's name." CR
+          INPUT$
+          CR ." Dear " DUP COUNT TYPE CR
+          ." Your cup that says " COUNT TYPE
+          ." is in the mail!" CR
+      ;
+
+**`ACCEPT ( addr maxbytes -- numbytes , input text, save at address ) `**
+
+You can use your word INPUT\$ to write a word that will read a number
+from the keyboard. Enter:
+
+- : INPUT# ( -- N true | false )
+          INPUT$ ( get string )
+          NUMBER? ( convert to a string if valid )
+          IF DROP TRUE ( get rid of high cell )
+          ELSE FALSE
+          THEN
+      ;
+
+This word will return a single-precision number and a TRUE, or it will
+just return FALSE. The word **NUMBER?** returns a double precision
+number if the input string contains a valid number. Double precision
+numbers are 64-bit so we DROP the top 32 bits to get a single-precision
+32 bit number.
+
+## []{#Changing Numeric Base}Changing Numeric Base
+
+For day-to-day life, the numbering system we use is decimal, or \"base
+10.\" That means each digit get multiplied by a power of 10. Thus a
+number like 527 is equal to (5\*100 + 2\*10 + 7\*1). The use of 10 for
+the numeric base is a completely arbitrary decision. It no doubt has
+something to do with the fact that most people have 10 fingers
+(including thumbs). The Babylonians used base 60, which is where we got
+saddled with the concept of 60 minutes in an hour. Computer hardware
+uses base 2, or \"binary\". The computer number \"1101\" is equal to
+(1\*8 + 1\*4 + 0\*2 + 1\*1). If you add these up, you get 8+4+1=13 . The
+binary number \"10\" is (1\*2 + 0\*1), or 2. Likewise the numeric string
+\"10\" in any base N is N.
+
+Forth makes it very easy to explore different numeric bases because it
+can work in any base. Try entering the following:
+
+- DECIMAL 6 BINARY .
+      1 1 + .
+      1101 DECIMAL .
+
+Another useful numeric base is *hexadecimal*. which is base 16. One
+problem with bases over 10 is that our normal numbering system only has
+digits 0 to 9. For hex numbers we use the letters A to F for the digits
+10 to 15. Thus the hex number \"3E7\" is equal to (3\*256 + 14\*16 +
+7\*1). Try entering:
+
+- DECIMAL 12 HEX .  \ print C
+      DECIMAL 12 256 *   7 16 * +  10 + .S
+      DUP BINARY .
+      HEX .
+
+A variable called **BASE** is used to keep track of the current numeric
+base. The words **HEX** , **DECIMAL** , and **BINARY** work by changing
+this variable. You can change the base to anything you want. Try:
+
+- 7 BASE !
+      6 1 + .
+      BASE @ . \ surprise!
+
+You are now in base 7 . When you fetched and printed the value of BASE,
+it said \"10\" because 7, in base 7, is \"10\".
+
+PForth defines a word called .HEX that prints a number as hexadecimal
+regardless of the current base.
+
+- DECIMAL 14 .HEX
+
+You could define a word like .HEX for any base. What is needed is a way
+to temporarily set the base while a number is printed, then restore it
+when we are through. Try the following word:
+
+- : .BIN ( N -- , print N in Binary )
+          BASE @ ( save current base )
+          2 BASE ! ( set to binary )
+          SWAP . ( print number )
+          BASE ! ( restore base )
+      ;
+
+      DECIMAL
+      13 .BIN
+      13 .
+
+## []{#Answers to Problems}Answers to Problems
+
+If your answer doesn\'t exactly match these but it works, don\'t fret.
+In Forth, there are usually many ways to the same thing.
+
+### [Stack Manipulations](#Problems%20-%20Stack)
+
+- 1) SWAP DUP
+      2) ROT DROP
+      3) ROT DUP 3 PICK
+      4) SWAP OVER 3 PICK
+      5) -ROT 2DUP
+
+### [Arithmetic](#Problems%20-%20Square)
+
+- (12 * (20 - 17)) ==> 20 17 - 12 *
+      (1 - (4 * (-18) / 6)) ==> 1 4 -18 * 6 / -
+      (6 * 13) - (4 * 2 * 7) ==> 6 13 * 4 2 * 7 * -
+
+      : SQUARE ( N -- N*N ) 
+          DUP *
+      ;
+
+      : DIFF.SQUARES ( A B -- A*A-B*B )
+          SWAP SQUARE 
+          SWAP SQUARE - 
+      ;
+
+      : AVERAGE4 ( A B C D -- [A+B+C+D]/4 )
+          + + + ( add'em up )
+          4 /
+      ;
+
+      : HMS>SECONDS ( HOURS MINUTES SECONDS -- TOTAL-SECONDS )
+          -ROT SWAP ( -- seconds minutes hours )
+          60 * + ( -- seconds total-minutes )
+          60 * + ( -- seconds )
+      ;
+
+### [Logical Operators](#Problems%20-%20Logical)
+
+- : LOWERCASE? ( CHAR -- FLAG , true if lowercase )
+          DUP 123 <
+          SWAP 96 > AND
+      ;
+
+### [Conditionals](#Problems%20-%20Conditionals)
+
+- : DEDUCT ( n -- , subtract from account )
+          ACCOUNT @ ( -- n acc )
+          SWAP - DUP ACCOUNT ! ( -- acc' , update variable )
+          ." Balance = $" DUP . CR ( -- acc' )
+          0< ( are we broke? )
+          IF ." Warning!! Your account is overdrawn!" CR
+          THEN
+      ;
+
+### [`Loops`](#Problems%20-%20Loops)
+
+- : SUM.OF.N.1 ( N -- SUM[N] )
+          0 SWAP \ starting value of SUM
+          1+ 0 \ set indices for DO LOOP
+          ?DO \ safer than DO if N=0
+              I +
+          LOOP
+      ;
+
+      : SUM.OF.N.2 ( N -- SUM[N] )
+          0 \ starting value of SUM
+          BEGIN ( -- N' SUM )
+              OVER +
+              SWAP 1- SWAP
+              OVER 0<
+          UNTIL
+          SWAP DROP
+      ;
+
+      : SUM.OF.N.3 ( NUM -- SUM[N] , Gauss' method )
+          DUP 1+   \ SUM(N) = N*(N+1)/2
+          * 2/
+      ;
+
+Back to [pForth Home Page](../pforth)\
+:::
+
+::: {#footer}
+\(C\) 1997-2015 Mobileer Inc - All Rights Reserved - [Contact
+Us](/contacts.php)
+:::
+:::::::::::::