Implementing FORTH
on my 6502 computer
Alexandre Dumont #FORTH2020
@adumont Dec, 11th, 2021
Content
● Introduction
○ Motivation
○ My 6502 breadboard computer
● 6502 basics
○ Registers, ZP, Stack
○ Addressing Modes
● My FORTH Implementation
○ Dictionary, Inner interpreter, Data stack
○ Development methodology
○ Features & current limitations
● Quick Demo
Motivation
● Enjoy!
● Learn something new
○ 6502 assembly
○ FORTH internals
○ Eventually learn some FORTH
● Make my 6502 computer usable
My 6502 computer Clock ACIA
Data bus
● WDC 65c02s
● 1.8432 MHz crystal oscillator (clock)
● 32KB EEPROM, 16KB SRAM 6502 RAM ROM
Addr bus
● 6551 ACIA (serial port) ← User interface
● 6522 VIA (Versatile Interface Adapter) ≈ GPIO
● 20x4 LCD display
● No keyboard, no VGA display
● EEPROM Programmer: VIA
ROM
A computer... in need of some software
● I began coding simple 6502 assembly programs:
○ From simple registers manipulation to writing a “Hello World” on the LCD display
● I developed my own ROM monitor:
○ Read/write to memory (or I/O)
○ Jump to code at a specified address
○ Return to monitor on BRK and then resume
○ Dump and Edit registers
Around that time (April 2021) is when I then discovered the FORTH language
6502 basics*
*Relevant to my FORTH implementation ;-)
6502 Registers
● 8 bits CPU, 16 bits wide address bus
● It can address 64KB of memory space
(RAM+ROM+I/O devices)
● 8 bits registers:
○ A: Accumulator: general purpose, ALU
○ X, Y: indexes, used in addressing modes
○ S: Stack pointer ($01xx)
○ P: processor flags
● PC: program counter (16 bits)
● Zero Page ($00xx): 1 byte address! Can act
as 16 bits registers in complex addressing
modes
6502 hardware stack
● Resides in memory page 1: $0100:$01FF ● Instructions to push/pop 8 bit registers
on/off the stack:
● Register S is a pointer into the stack ○ pha / pla
○ $0100+S is the next available location in the ○ phx / plx, phy / ply (*)
stack memory area ○ php / plp
● The stack is used by the 6502:
○ Calls to subroutines (store return address)
○ Responding to interrupts (store status
register and return address)
* 65c02 specific instructions
6502 addressing modes (1)
● Zero page indexed with X or Y zp,X /zp,Y
Example: LDA $B0,X
Loads content of address $00B0+X ($00B2)
into A.
(Later we’ll see we use this to address our FORTH
data stack)
6502 addressing modes (2)
● Zero page indirect indexed with Y (zp),Y
Example: LDA ($B2),Y
Take what’s in:
$00B2 → low byte: 00
$00B3 → high byte: D1
And forms the base address: $D100
Then loads the byte at $D100+Y into register A.
● Also: Zero page indirect (unindexed) (zp)
Implementing FORTH
(My own implementation)
What did I need to implement a minimal FORTH?
● A minimum set of words ● And to make it interactive:
→ a dictionary ○ An outer interpreter
○ A way to lookup words in the
● A minimal program to be run: dictionary
→ a “thread” ○ A way to compile new words
● A way to read and interpret the
program, ie. move along the thread:
→ the inner interpreter ● Design choices
○ DTC: Direct Threaded Code
● A data stack and a return stack ○ 16 bits cells
Dictionary
ENTER EXIT
Data Stack
● The Data Stack is build using Zero Page. It starts
at the top of ZP (just below the FORTH registers
W, IP, G1 and G2), and grows downwards.
● Accessing the Data Stack is easy using the
Zero Page Indexed with X addressing mode
● 0,X & 1,X → next free cell of Data Stack
● 2,X & 3,X → cell at Top of the Stack
● Pushing a cell on the stack: storing the Low byte
at 0,X, and the high byte at 1,X, and then
decrementing X twice (DEX).
● DROP is simply two inx
Inner Interpreter
● Two Registers:
○ IP: (next) Instruction Pointer
○ W: (current) Word Address
● Routines:
● NEXT
● ENTER (“COLON” in my implementation)
● EXIT (“SEMI” in my implementation)
Inner Interpreter - NEXT
● NEXT does 3 things:
○ (IP) --> W IP: (next) Instruction Pointer
○ IP+2 --> IP W: (current) Word Address
○ JMP (W)
NEXT:
; (IP) --> W
LDA (IP) ← Zero page indirect (unindexed)
STA W
LDY #1
LDA (IP),y ← Zero page indirect indexed with Y
STA W+1
; IP+2 --> IP
CLC
LDA IP
ADC #2
STA IP
BCC @skip
INC IP+1 ENTER EXIT
@skip:
JMP (W) ← Absolute indirect Dictionary representation of “,”
Inner Interpreter - ENTER
W
defword "ENTER",,
; push IP to Return Stack
LDA IP+1 ; HI ← I use the HW stack
PHA
LDA IP ; LO as FORTH return stack
PHA
; W+3 --> IP
; (Code at W was a JMP)
CLC
ENTER EXIT
LDA W
ADC #3 IP ← W+3
STA IP
LDA W+1
ADC #0
STA IP+1
JMP NEXT Jump to NEXT
Inner Interpreter - EXIT
defword "EXIT",,
; POP IP from Return Stack
PLA
← I use the HW stack for
STA IP
PLA FORTH return stack
STA IP+1
; JMP NEXT
JMP NEXT ← Jump to NEXT
(My) Development Methodology
● Iterative: start small and grow incrementally
● First test in emulation:
○ I started with Kowalsky 6502 emulator
○ Then I build my own tools in Python with py65
emulator library
● Then test on hardware:
○ Slow and cumbersome: rom flashing, replace the
rom chip
○ Fragile: lots of cables, and breadboard not ideal
(I should learn how to do a PCB)
● Commit everything on Github
My 6502 emulator for developing Alex FORTH for the Cerberus2080
My FORTH Features (for now)
● : ; EXIT
● JUMP EXEC
● IF ELSE THEN
● DO LOOP +LOOP LEAVE
● BEGIN AGAIN, BEGIN UNTIL, BEGIN WHILE REPEAT
● VARIABLE and Local Variables
● MARKER FORGET
● CREATE DOES>
● Comments \ and ( )
● WORDS, HIDE, REVEAL, HIDDEN, RECURSIVE
Limitations (at the moment)
● Doesn’t adhere to any standard
● Only Integer numbers (no floating points)
● Only Hexadecimal representation (no BASE/conversions)
● No stack overflow/underflow verification. Easy to crash!
● Only one dictionary (no contexts)
● Case sensitive words, all predefined words are capital case
● No mass storage, no block feature, no save/restore
Demo time!
Links & how to contact me
My site: https://adumont.github.io/
My FORTH:
● Alex FORTH for 6502 Breadboard Computer
○ Test it in your browser (py65 emulation)
● Alex FORTH for the Cerberus2080 (6502)
Twitter: @adumont
Thank you!