Program lifecycle in BBC BASIC II: what NEW, OLD, CLEAR, RUN and CHAIN inherit and leave behind
Scope. This article covers the BBC BASIC II commands that start, clear, and re-run a program —
NEW,OLD,CLEAR,RUN,CHAIN— and exactly what state each resets versus preserves. Every routine and workspace address is specific to this version and was read from the disassembly (which reassembles byte-identically to the ROM). The expression/parsing machinery these commands hand control to is mapped in parsers and evaluators; the loop stacks they reset are detailed in control flow on five stacks.
The single fact that explains the whole family:
All five commands funnel through one routine,
clear_vars_heap_stack(&BD20), which wipes the dynamic-variable world — every named variable, array, string andPROC/FN— but deliberately preserves the resident integer variables@%andA%–Z%, along withHIMEMandPAGE. That preservation is not an accident: it is the documented channel for passing data into aCHAINed program.
The shared clear
clear_vars_heap_stack (&BD20) does exactly three things:
LOMEM = VARTOP = TOP— the dynamic-variable heap is emptied by resetting its top back to the end of the program.reset_data_and_stacks(&BD3A) — theDATApointer is reset toPAGE(an implicitRESTORE), the BASIC value stack toHIMEM, and theFOR/REPEAT/GOSUBlevel counters to empty.clear_var_table(&BD2F) — zeroes the per-letter variable chain-head table.
Step 3 is the crux, and it is precise about its range. It clears &0480–&04FF only:
.clear_var_table
ldx #&80
lda #0
.clear_var_table_loop
sta var_table_base,x ; var_table_base = &047F, so &0480..&04FF
dex
bne clear_var_table_loop
It starts at &0480 and never descends into &0400–&047F.
The page-4 map: why resident integers survive
That &0480 boundary lines up with the page-4 workspace layout:
| Range | Contents | Cleared by the lifecycle commands? |
|---|---|---|
&0400–&046B |
resint_at (&0400): the resident integers — @% at &0400, then A%–Z% at &0404–&046B (four bytes each) |
No — never touched |
&046C–&047F |
fp_temps (&046C): four 5-byte floating-point scratch temporaries |
No (scratch, not user-visible) |
&0480–&04FF |
var_ptr_table (&0480): the dynamic-variable chain heads (two bytes per initial-character class) |
Yes — zeroed every time |
So @% and A%–Z% live below the cleared region and ride through NEW, OLD, CLEAR, RUN and CHAIN untouched. Because @% is also the PRINT/STR$ number-format control word, the print format carries over too — an easily missed consequence.
The five commands, side by side
| Command | Handler | Program text | Dynamic vars / arrays / strings / PROC·FN |
Resident @%/A%–Z% |
LOMEM |
DATA ptr |
FOR/REPEAT/GOSUB + value stack |
Then |
|---|---|---|---|---|---|---|---|---|
NEW |
start_new_program (&8ADD) |
emptied (CR,&FF at PAGE; recoverable by OLD) |
cleared | preserved | =TOP |
=PAGE |
cleared | TRACE off; → immediate |
OLD |
stmt_old (&8AB6) |
recovered + TOP re-derived |
cleared | preserved | =TOP |
=PAGE |
cleared | → immediate |
CLEAR |
stmt_clear (&928D) |
kept | cleared | preserved | =TOP |
=PAGE |
cleared | → next statement |
RUN |
stmt_run (&BD11) |
kept | cleared | preserved | =TOP |
=PAGE |
cleared | execute from PAGE |
CHAIN |
stmt_chain (&BF2A) |
replaced by the loaded file | cleared | preserved | =TOP |
=PAGE |
cleared | execute from PAGE |
HIMEM and PAGE are preserved by every one of them — clear_vars_heap_stack (&BD20) reads HIMEM to reset the stack but never writes it, and PAGE is where each program lives.
A few per-command details worth knowing:
NEWdoesn't actually erase the program —start_new_program(&8ADD) just writes the empty-program markers (&0D &FF) atPAGE, setsTOP = PAGE+2, and turnsTRACEoff. The old bytes remain, which is exactly whatOLDexploits.OLDremoves the end marker, re-validates the lines withcheck_program(re-derivingTOP), then clears the heap. It only succeeds if the text hasn't been overwritten sinceNEW(by typing a new program,LOAD, orCHAIN).CLEARis the pure case: it runs the shared clear and nothing else — program kept, not executed.RUNisclearthen "execute fromPAGE".CHAINisLOADthenRUN:load_program(&BE62) does anOSFILEload over the old program atPAGEand re-derivesTOP, then falls intorun_clear(&BD14). It clears no variables itself — all the clearing is the sharedRUNpath.TRACEis turned off only byNEW;CLEAR/RUN/CHAINleave the trace flag as it was.
Carried over by every command
- The resident integers
A%–Z%and@%(&0400–&046B) — values and@%'s print-format role. HIMEM, and therefore any memory the program reserved above it (DIM x% Nblocks aboveHIMEM, machine code, etc.).PAGE.
This is why the idiom for CHAIN data-passing works: set A%…Z% (or stash bytes above a lowered HIMEM) before CHAIN, and read them back in the chained program.
Two clears you might not expect
- Editing any program line clears all variables. Inserting or deleting a line goes through
insert_line(&BC8D) and then immediately callsclear_vars_heap_stack(&BD20) (jsrat&90C9). So adding a line in the middle of a debugging session wipes the dynamic variables (the resident integers, again, survive). The out-of-room path raises LINE space after the same clear. - An error does not clear variables — by contrast. The error path calls only
reset_data_and_stacks(&BD3A) (jsrat&B41B), which resets theDATApointer and theFOR/REPEAT/GOSUB/value stacks but leaves every variable intact. So after an error orEscape, your variables are still readable at the prompt, but the loop stacks andDATAposition are gone (this is also whyGOTOing back into a loop after an error misbehaves — see control-flow stacks).
For a compiler
Model the lifecycle as: NEW/OLD/CLEAR/RUN/CHAIN all reset the dynamic-variable environment (heap, arrays, strings, PROC/FN table, LOMEM, DATA position, all stacks) but preserve the resident integers @%/A%–Z%, HIMEM (and reserved memory above it), and PAGE. CHAIN additionally swaps the program text. The resident-integer survival is observable behaviour a faithful implementation must reproduce — programs rely on it both for CHAIN parameter passing and for @% formatting persisting across a RUN.
Cross-references
clear_vars_heap_stack(&BD20) — the shared clear;clear_var_table(&BD2F, zeroes&0480–&04FF) andreset_data_and_stacks(&BD3A).start_new_program(&8ADD) (NEW),stmt_old(&8AB6) (OLD),stmt_clear(&928D) (CLEAR),stmt_run(&BD11)/run_clear(&BD14) (RUN),stmt_chain(&BF2A)/load_program(&BE62) (CHAIN).- Workspace:
resint_at(&0400) (@%,A%–Z%),var_ptr_table(&0480), and the pointersLOMEM(&0000),VARTOP(&0002),HIMEM(&0006),TOP(&0012),PAGE(&0018). - Control flow on five stacks — what
reset_data_and_stacksempties, and the early-exit hazards.
