The Bird Processor

Developer(s): Craig Bird
Date: 1984
Type: Cassette
Platform(s): TS 1000

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 rangeFunction
3–30Startup: USR call to init routine, display auto-justify state, wait for keypress, decode and GOTO menu choice
100–195Option 1 – Save program with files (temporary VARS/end-of-program pointer swap)
200–220Option 2 – Begin new files (two USR calls then RUN)
300Option 3 – Return to BASIC via NEW
400–420Option 4 – Help screen (USR call, PAUSE, RUN)
500–510Option 5 – Continue with current files
600–610Option 6 – Print files
700–790Option 7 – Toggle auto-justify on/off via direct POKE into machine code
800–820Option 8 – Display free memory (USR returns byte count)
900–990Option 9 – Scrollable file review with forward/reverse/stop controls
992–995SAVE 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.

AddressPurpose inferred from context
18185Main initialization / entry point
17994Post-menu re-entry / editor launch
16960Save-mode setup
17700New-file initialization (first stage)
19193New-file initialization (second stage)
18300Help screen display
21526Continue-editing entry
21540Print-files routine
21550Display one 33-column line during review
18265Returns 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 PI evaluates to 0 (since PI is non-zero, NOT PI = 0) and is used wherever a zero byte must be POKEd, saving one byte over the literal 0.
  • 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 USR rather than USR alone discards the return value without needing a LET assignment, except at line 800 where PRINT 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

Appears On

Related Products

Upper and lower case text on the Sinclair or TS2040 printer; programmable characters.

Related Articles

Related Content

Image Gallery

The Bird Processor

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.

Scroll to Top