The Bird Processor is a full-featured word processor implemented primarily in machine code, with a BASIC loader and menu system handling file management, printing, and configuration. The BASIC portion at lines 3–995 uses RAND USR calls into machine code routines at addresses such as 18185, 17994, 16960, 17700, 19193, 18300, 21526, 21540, 21550, and 18265 to perform the core editing, display, and printing functions. Auto-justify is toggled at runtime by POKEing Z80 instruction bytes directly into the machine code at addresses 17232–17253, switching between a CALL instruction (205, then address bytes) and NOP sequences. The review/scroll feature (lines 900–990) navigates through 33-byte-wide text lines stored in memory, using a pair of POKEable address pointers at 17398–17399 to track the current display position. File saving is implemented by temporarily redirecting the system’s program-end and VARS pointers (16404–16405) to save only the word-processor data area before restoring them.
Program Analysis
Program Structure
The program is organized as a menu-driven shell (lines 3–30) that dispatches to numbered suites of BASIC lines, each handling one menu function. The real editing engine lives entirely in machine code embedded in the line 0 REM statement; BASIC acts only as a loader, menu, and configuration layer.
| Line range | Function |
|---|---|
| 3–30 | Startup: USR call to init routine, display auto-justify state, wait for keypress, decode and GOTO menu choice |
| 100–195 | Option 1 – Save program with files (temporary VARS/end-of-program pointer swap) |
| 200–220 | Option 2 – Begin new files (two USR calls then RUN) |
| 300 | Option 3 – Return to BASIC via NEW |
| 400–420 | Option 4 – Help screen (USR call, PAUSE, RUN) |
| 500–510 | Option 5 – Continue with current files |
| 600–610 | Option 6 – Print files |
| 700–790 | Option 7 – Toggle auto-justify on/off via direct POKE into machine code |
| 800–820 | Option 8 – Display free memory (USR returns byte count) |
| 900–990 | Option 9 – Scrollable file review with forward/reverse/stop controls |
| 992–995 | SAVE of the loader itself, then RUN |
Menu Dispatch
The menu selection mechanism at line 30 is a compact single-line dispatch:
GOTO (PEEK VAL "21633"-VAL "28")*VAL "100"
The keypress captured at line 26 via POKE 21633,CODE INKEY$ stores the ASCII code of the chosen digit. Subtracting 28 (which equals the ASCII code of digit “1” minus 1, i.e., 48−28=20… actually 49−28=21… noting the menu choices are 1–9 mapping to GOTOs 100–900) converts the character code to a menu index, then multiplying by 100 produces the correct target line number. The PAUSE/INKEY$ pattern at lines 25–26 is a standard efficient keypress-wait idiom.
Machine Code Integration
All heavy lifting is delegated to Z80 routines stored inside the line 0 REM block. The BASIC layer calls them with RAND USR VAL "address". Using VAL "number" as the argument to USR (and elsewhere) saves tokenized bytes compared to storing numeric literals directly.
| Address | Purpose inferred from context |
|---|---|
| 18185 | Main initialization / entry point |
| 17994 | Post-menu re-entry / editor launch |
| 16960 | Save-mode setup |
| 17700 | New-file initialization (first stage) |
| 19193 | New-file initialization (second stage) |
| 18300 | Help screen display |
| 21526 | Continue-editing entry |
| 21540 | Print-files routine |
| 21550 | Display one 33-column line during review |
| 18265 | Returns free-memory count (used in PRINT USR …) |
Auto-Justify Toggle via Self-Modifying Code
Lines 700–790 implement a toggle for the word-processor’s auto-justify feature by writing Z80 opcodes directly into the machine code at fixed addresses. When turning justify OFF, the code POKEs 35 (Z80 mnemonic: INC L) to address 17232 and zeros to 17233–17234, effectively replacing a CALL nn instruction with NOPs or a harmless single-byte instruction. Turning justify back ON restores 205 (the Z80 CALL opcode) at 17232 and the two address bytes 4, 66 at 17233–17234. Two further bytes at 17252–17253 are also patched, likely controlling a loop counter or another branch.
File Save via Pointer Manipulation
The save routine (lines 150–185) works around the system’s normal SAVE behavior by temporarily replacing the program-end and VARS pointers (system variables at 16404–16405) with values from 16507–16508, which point to the word-processor data area rather than the end of BASIC. This causes the SAVE at line 170 to write only the data files, not the BASIC program itself. The original pointer values are preserved in scratch locations at 21630–21631 and restored afterward.
Scrollable Review (Lines 900–990)
The review mode navigates through 33-byte-wide text records stored sequentially in memory. A pair of POKEable bytes at addresses 17398–17399 form a 16-bit pointer to the currently displayed line. The BASIC code advances or retreats this pointer by 33 bytes per keypress and calls USR 21550 each time to render the line. Boundary checks prevent scrolling past the start (A+35) or end (A+8447, implying a maximum of ~256 lines of 33 bytes) of the file area.
- Key
6– scroll forward one line - Key
7– scroll backward one line - Key
0– return to scroll-direction prompt - Key
9– return to main menu (via RUN)
Notable BASIC Idioms
VAL "number"used pervasively as arguments to GOTO, GOSUB, POKE, PAUSE, and USR — a well-known token-saving technique.NOT PIevaluates to0(since PI is non-zero, NOT PI = 0) and is used wherever a zero byte must be POKEd, saving one byte over the literal0.RUN(without a line number) is used as a lightweight “return to menu” — it re-executes from line 3, reinitializing the display state cleanly.RAND USRrather thanUSRalone discards the return value without needing aLETassignment, except at line 800 wherePRINT USR …exploits the integer return value directly.
Bugs and Anomalies
- Lines 913–917 poll
INKEY$in a tight loop without any debounce or PAUSE, which will cause very rapid repeated triggering on slow keypresses — typical for ZX81-era polling loops but potentially problematic in the review scroller. - The dispatch formula
(PEEK VAL "21633"-VAL "28")*VAL "100"produces GOTO 0 if no key was pressed (INKEY$ returning “” gives CODE “” = 0; 0−28 = −28, ×100 = −2800), which would cause a “2 Variable not found” or similar error. In practice the PAUSE at line 25 ensures a key is always held before line 26 executes, mitigating this.
Content
Source Code
0 REM -▘5WRNDLN SCROLL▝VAL FAST[J]#PEEK CLS LPRINT LN #?S▌3PEEK CLSINT £<= CLS▗ IF THEN▒K INPUT FASTSTR$ # RETURN▝[3][7]▛[7]###7 RETURN#C8 FASTACS B▗▗ADACS =▄#ACS )[3][I]#▞▒#ACS ▘3#<= CLS3K CLS#PEEK CLS( LET LPRINT /STR$ <= CLS3K CLS#?PEEK CLSSGN 0ACS #C[B]AT +4[4]Y▖PEEK CLSLN ▛▝AT 5#RNDQ#▞4FQ ( CLS#ACS COPYMSRNDTAN GOSUB #9RNDACS RNDC,#=C POKE LN [X]▛4 LOAD #▞ #U9RNDW4 IF Y#W[T]C=TAN ACS ""#=C**LN [X]▛KPEEK #ACS COPY▞ #/ STEP GOSUB #9RND#=C SAVE ACS █/ACS LN C#$4 IF *************E#RND: FAST#Y[9][X]K▖#CHR$ ##A DDD)4#; FOR VAL A #ACS +ACS +ACS +▘ #\,, FOR ▘▒ GOSUB [K]AT LPRINT 7£Y4[T]4EXP 5WRND▞ #▖[S]74 IF LN ▙RNDTAN E£RND GOSUB # PLOT #▘/▀ GOSUB [K]E PLOT #▘/▀\,, FOR E0RND77 FASTSTR$ 6#RNDLN #INKEY$ SGN LPRINT 7# RETURN#4 IF 7 FAST GOSUB # LPRINT SQR / CONT 4▞▘5 \,,/) RETURN[6]4▒▘5 [B] GOSUB PI/▌ RETURN[4]COS #7# RETURN#4K FASTY ▘2 GOSUB [S]# RETURN 4"SGN ▘6 \,,7Q█F# RETURN COS F# RETURN#COS RETURN 4 RUN FAST▘ 7▀# RETURN#4 RAND FOR <" LPRINT 7 FASTVAL GOSUB [K]AT LPRINT Q 7$4 IF FOR TAN ▌ RETURN[4]COS #7# RETURN#4K FASTY ▘2U5RNDA # FASTAT DDDDD\,, GOSUB #0RND\,,F# RETURN C IF RETURN#C PLOT 6#RNDTAN Q█ /<>▘▘E£RND7Q█ FASTE£RND:/Q#▞47ACS [Q]( CLS7$4 NEXT Q#LN ▞INKEY$ LPRINT # RETURN[0]4$Q F# RETURN#C IF LEN █#/STR$ RETURN[1]4▛#CHR$ █##[4]INKEY$ RETURN[2]COS RETURN[3]42 FAST#CHR$ █#▘ # RETURN#7▀4 RAND "FF##F GOSUB [S] LPRINT #LEN █#/EXP RETURN[4]4A#CHR$ █# FAST FAST▘ # RETURN#7▀4 RAND " LPRINT ##. GOSUB [K]Y > LPRINT F# RETURN#C IF LEN █#/[8] RETURN[5]4?#CHR$ █#F# RETURN#C IF LEN █#/[)] RETURN[6]4▌LN SAVE #/ PRINT RETURN[7]4▌LN ""#/ GOTO RETURN[8]4?#CHR$ █#7# RETURN#C IF LEN █#/ OR LN ##VAL RETURN**S▛ RETURN LLIST K▀CHR$ W#ACS [L]#7# RETURN 4▒# RETURN#4+7/▒# RETURN#4$LN ▖PI FAST▘5 [B] GOSUB PI/▒ LPRINT #LEN █#AT /[4]▞ 7# RETURN#4 IF F▖# RETURN#C DIM RETURN C PRINT ▌F# RETURN#C THEN RETURN 4 PLOT FASTF# RETURN█C▖ RETURN 44 FASTVAL ▘ # RETURN#7▀4 RAND ""FF##F GOSUB [S]AT ▌C▀ LPRINT /▖ LPRINT LPRINT /[J]# RETURN#4SGN LPRINT /LN # RETURN#7▀4 RAND "FF##F GOSUB [S] LPRINT #LEN █#/EXP RETURN[4]4A#CHR$ █# FAST FAST▘ # RETURN#7▀4 RAND " LPRINT ##. GOSUB [K]Y > LPRINT F# RETURN#C IF LEN █#/[N]##CHR$ █#)5 ; FAST GOSUB #(RND.[B] GOSUB #K▞ LPRINT #LEN █#TAN E£RND GOSUB # PLOT #▘/▀ GOSUB [K]E PLOT #▘5 \,,6 PLOT # GOSUB #£RND▘/▀ GOSUB [K] LPRINT ▘5 [B] GOSUB PI#LEN █#TAN RND< GOSUB #0RND<Y#> GOSUB # PLOT #U5RND<5WRND▘5 GOSUB [K]X4 PRINT ##PIVAL PRINT FASTLN ##E(RND)6 [B] GOSUB # FOR LPRINT FAST[B] GOSUB #K▖ LPRINT LET AT TAN LPRINT LN SAVE ##CHR$ █#)5 GOSUB ##LEN █# LET AT TAN ***************************************************** FAST#CHR$ █#▘5 [B] GOSUB PI GOSUB #£RND[B] GOSUB #S▛;SGN #LEN █#TAN E PLOT # GOSUB #0RND<[B] GOSUB #4▞ LPRINT #LEN █#TAN GOSUB # PLOT #E£RND▘/▀ GOSUB [K]E PLOT #▘5 [B] GOSUB PI6 PLOT # GOSUB #£RND▘/▀ GOSUB [K] LPRINT #LEN █#TAN *************** GOSUB #0RND<Y#> GOSUB # PLOT #U5RND<5WRND▘5 GOSUB [K]X4 PRINT ##PI******************** RETURN THEN4\,,▘▜▀SGN SGN SGN SGN SGN TAN RETURN AND 4\,,▘4▀SGN SGN SGN SGN SGN TAN RETURN#4*#CHR$ █#7# RETURN#4 IF 7#LEN █#SGN SGN SGN SGN SGN ##PI RETURN[9]ATN ## RETURN#40FLN [-]#7 FAST-47# RETURN#C IF Q +4 PRINT LPRINT SGN SGN SGN SGN SGN ##PI RETURN#4"LN /#SGN SGN SGN SGN SGN ##PITAN ****EORNDFY#[8][P]#4▀*/▝#R#6ORNDSQR LN [V]▝ GOSUB #9RND69RND#LEN ▝ GOSUB PIUBRND[O][P]#▞"5VRNDACS ▚4▒ACS #ACS LEN COS ▌ R5BRNDZACS (( RETURN## RETURN RETURN[3]▞3[Q][4]3#PEEK COPYE£RNDACS UNPLOT LN [>]▝ GOSUB #▘▘;Y PRINT LN [P]▝FLN [>]▝U9RND RETURN[3]4▀ LPRINT 7 FAST RETURN UNPLOT 4▌5.INKEY$ Q RETURN RAND TAB CODE #5.INKEY$ QU#CODE #<>5CODE #TAN DIM FASTVAL PRINT E:RND##A DDD▘4#\,, FOR E:RND▘5 \,,▞▒STR$ :▒- # RETURN█4▝ACS IF ACS ▝7$4 NEXT 7# RETURN#4 IF 7#SGN ><( LPRINT LET AT LPRINT TAN #76:RND CLEARPT PRINT VAL FAST GOSUB # PLOT #E£RND▘/▀ GOSUB [K] LPRINT FAST GOSUB #£RND[B] GOSUB PI FOR E PLOT #; FASTAT 5[.]#[B] GOSUB PI FASTAT )[W]#5[.]# GOSUB [S]E0RND7# RETURN#4 IF )5 U5RND#Q#;( CLSE PLOT # GOSUB #£RND▘/▀ GOSUB [K] LPRINT AT LET TAN ▘NOT ▝\,,▞ CLEAR#5ACS [T]ACS [L]ACS [D]\,,#**************5▒# GOSUB #£RND▘/▀ GOSUB [K]TAN ** FAST GOSUB # PLOT #E£RND▘/▀ GOSUB [K] LPRINT FAST GOSUB #£RND[B] GOSUB # GOSUB # PLOT #; FAST)5 ;SGN FAST FAST5[.]#AT [B] GOSUB PI## LPRINT GOSUB [K]E PLOT # GOSUB #£RND▘/▀ GOSUB [K] LPRINT ▞ Y TAN E0RND▘ NEW4\,, FASTF# RETURN#C IF RETURN C PLOT SGN FOR [B] GOSUB # FASTAT TAN *********) STEP #E£RND FOR ▘/▀ GOSUB [K]TAN *******E PLOT #▘5 \,,6 PLOT # GOSUB #£RND▘/▀ GOSUB [K]##PI**********************************************************# HELP SCREEN #KEY FUNCTION #SH1 PRINT UP TO CURSOR POSITION #SH2 RETURN TO MENU #SH3 INSERT A SPACE #SH4 DELETE A SPACE #SH5 MOVE CURSOR LEFT #SH6 MOVE CURSOR UP ONE LINE #SH7 MOVE CURSOR DOWN A LINE #SH8 MOVE CURSOR RIGHT #SH9 PROGRAM A CHARACTER #SH0 ERASE CHARACTER BELOW CURSOR#NLNL SEND CURSOR TO NEXT LINE #NL9 INSERT A LINE #NL8 DELETE A LINE # SPECIAL CHARACTERS #NLW-EXCLAMATION MARK #NLE-UNDERLINE #NLT-NUMBER SIGN #NLR-PERCENT SIGN #NLY-AND SIGN # # # / GOSUB # PLOT #E£RND▘/▀ GOSUB [K]TAN TAN TAN # [T][H][E]█[B][I][R][D]█[P][R][O][C][E][S][S][O][R] # # [C] 1983 CRAIG BIRD # # [M][E][N][U] # #1) SAVE PROGRAM WITH FILES #2) BEGIN NEW FILES #3) RETURN TO BASIC #4) HELP SCREEN #5) CONTINUE WITH CURRENT FILES #6) PRINT FILES #7) TURN OFF AUTO JUSTIFY FEATURE#8) MEMORY LEFT #9) REVIEW FILES # # # # # # # # # LIST LIST LIST LIST ???? COPY COPY COPY COPY LIST LIST LIST LIST LIST LIST LIST LIST LIST LIST LIST LIST ???? LIST LIST LIST LIST COPY COPY COPY COPY LIST LIST LIST LIST [E]#[E]#[E]#[E]# [E]#[E]#[E]#[E]# 88 06#44# ▒YCY\~~Y▒ ( ( WPI▖▒ ▒ ▖▒▒▒▒▖ 4((((4 (▒▖▒( ▖▒(▒▖ Y Y ▒▒Y▒▒ Y =▒Y▒= ▝▖▒(4 ( ((4 ▒▒( // W#####W K#((((# WPI▝WRNDRND# WPI▝£▝PIW ▖£=8#▖▖ YRNDRNDW▝PIW WRNDRND#PIPIW #▝▝▖▒(( WPIPIWPIPIW WPIPIY▝▝W S▖W#U RNDRND#PIPIPI# WPIRNDPIW ▝▝YPIPIPIY WPI#RNDW /84#444 ▝WPIW▝# RNDRND#PIPIPIPI ▒ /▒▒▒0 ▝ ▝▝▝PIW RNDRND####PI /▒▒▒▒▒0 Q#### WPIPIPIPI WPIPIPIW #PIPI#RNDRND W#W▖▞ #6444 WRNDW▝W ((#(((: PIPIPIPIW PIPIPI8/ PIPIPI#8 PI8/8PI PIPIW▝W #▖/4# WPIPI#PIPIPI #PIPI#PIPI# WPIRNDRNDRNDPIW ##PIPIPI## #RNDRND#RNDRND# #RNDRND#RNDRNDRND WPIRNDRND#PIW PIPIPI#PIPIPI Y▒▒▒▒▒Y ▝▝▝▝PIPIW PI#####PI RNDRNDRNDRNDRNDRND# PI##PIPIPIPI PI#####PI WPIPIPIPIPIW #PIPI#RNDRNDRND WPIPIPIPI#W▖#PIPI###PI WPIRNDW▝PIW Y▒▒▒▒▒▒ PIPIPIPIPIPIW PIPIPIPIPI8/ PIPIPIPIPI#8 PIPI8/8PIPI 6=▒▒▒▒▒ #▖▒(4RND# ((((( ( COPY **************************************************************************************************************************************************************************************************************************************************************** COPY RNDRNDRNDRNDRND RND ********5#8▒>9PI (C(C#61 6#666#6 ▘▘▘▘▘▘▘▘ COPY COPY ████████**************************************************************************************** ************************************************WPIPI#PIPIPI #PIPI#PIPI# WPIRNDRNDRNDPIW ##PIPIPI## ******E(RND▞#7# RETURN# Q ( PLOT TAN LN ▞##[(]#********LN ▞##[G]INKEY$ ****E PLOT #▘CHR$ ▝ GOSUB #£RND GOSUB [K]E£RND:-Q#▞47ACS [Q]( CLS7$4 NEXT TAN *************************************************[H]#*5
3 RAND USR VAL "18185"
22 IF PEEK VAL "17232"<>VAL "35" THEN PRINT AT VAL "16",NOT PI;"AUTO JUSTIFY IS [O][N]"
25 PAUSE VAL "9999"
26 POKE 21633,CODE INKEY$
27 RAND USR VAL "17994"
29 CLS
30 GOTO (PEEK VAL "21633"-VAL "28")*VAL "100"
100 RAND USR VAL "16960"
105 CLEAR
110 FAST
130 PRINT " INPUT NAME OF FILE"
140 INPUT A$
150 POKE VAL "21630",PEEK VAL "16404"
160 POKE VAL "21631",PEEK VAL "16405"
162 POKE 16404,PEEK 16507
165 POKE 16405,PEEK 16508
170 SAVE A$
180 POKE 16405,PEEK 21631
185 POKE 16404,PEEK 21630
190 SLOW
195 RUN
200 RAND USR VAL "17700"
215 RAND USR VAL "19193"
220 RUN
300 NEW
400 RAND USR VAL "18300"
410 PAUSE VAL "999"
420 RUN
500 RAND USR VAL "21526"
510 RUN
600 RAND USR VAL "21540"
610 RUN
700 PRINT "AUTO JUSTIFY IS ";
710 IF PEEK VAL "17232"=VAL "35" THEN GOTO VAL "750"
720 PRINT "[O][F][F]"
730 POKE VAL "17232",VAL "35"
735 POKE VAL "17234",NOT PI
740 POKE VAL "17233",NOT PI
743 POKE VAL "17252",VAL "24"
747 POKE VAL "17253",VAL "246"
748 GOTO VAL "410"
750 PRINT "[O][N]"
755 POKE VAL "17232",VAL "205"
760 POKE VAL "17233",VAL "4"
765 POKE VAL "17234",VAL "66"
770 POKE VAL "17252",VAL "6"
780 POKE VAL "17253",NOT PI
790 GOTO VAL "410"
800 PRINT USR VAL "18265";" BYTES ARE LEFT"
820 GOTO VAL "410"
900 PRINT " INPUT START LINE"
901 INPUT B
902 LET A=PEEK VAL "16412"+VAL "256"*PEEK VAL "16413"
903 LET B=A-VAL "5"+B*VAL "33"
904 POKE VAL "17398",B-VAL "256"*INT (B/VAL "256")
905 POKE VAL "17399",INT (B/VAL "256")
908 LET B=PEEK VAL "17398"+VAL "256"*PEEK VAL "17399"
909 POKE VAL "17398",(B+VAL "33")-VAL "256"*INT ((B+VAL "33")/VAL "256")
910 POKE VAL "17399",INT ((B+VAL "33")/VAL "256")
912 PRINT AT VAL "20",VAL "0";"6-FORWARD 7-REVERSE 0- STOP","9-RETURN TO MENU"
913 IF INKEY$ ="6" THEN GOTO VAL "960"
914 IF INKEY$ ="7" THEN GOTO VAL "920"
915 IF INKEY$ ="9" THEN RUN
917 GOTO VAL "912"
920 IF INKEY$ ="6" THEN GOTO VAL "970"
922 IF INKEY$ ="0" THEN GOTO VAL "912"
925 IF (PEEK 17398+256*PEEK 17399)<(A+35) THEN GOTO 970
930 RAND USR VAL "21550"
935 LET B=PEEK 17398+256*PEEK 17399
940 LET B=B-33
945 POKE 17398,B-256*INT (B/256)
950 POKE 17399,INT (B/256)
955 GOTO VAL "920"
960 IF INKEY$ ="0" THEN GOTO VAL "912"
962 IF INKEY$ ="7" THEN GOTO VAL "935"
965 IF (PEEK 17398+256*PEEK 17399)>(A+8447) THEN GOTO 935
967 RAND USR VAL "21550"
970 LET B=PEEK 17398+256*PEEK 17399
975 LET B=B+33
980 POKE 17398,B-256*INT (B/256)
985 POKE 17399,INT (B/256)
990 GOTO VAL "960"
992 SAVE "B[P]"
995 RUN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

