Pro/File +5: The Machine-Code Module

Pro/File +5 is Tom Woods‘ database for the TS2068, with later modifications by Robert Fischer. The BASIC program carries the interface — menus, editing, print formatting — and hands the slow work to a 2,046-byte machine-code module loaded at 63488 ($F800). That module does three things that cannot be done quickly in BASIC: search, pack and unpack records, and sort.

The module occupies $F800 to $FFFD, just under the top of RAM; the BASIC reserves it with CLEAR 63487 before loading.

The data model

The whole database is one character array, DIM d$(27379), and the machine code reaches its bytes directly rather than through the interpreter. The helper at $FA5B is LD HL,($5C4B): ADD HL,6 — the VARS pointer plus six, stepping over the array header (name byte, two length bytes, dimension count, two-byte dimension) to the first data byte. It works only because d$ is dimensioned first after CLEAR, so it sits at the start of the variables area.

Records are variable-length. Lines within a record are space-trimmed and separated by a $01 byte; records are separated by * ($2A). A $00 byte is a transient separator used during batch operations and committed to * afterwards. The pointer p holds the offset to the end of used data.

That $00-versus-* split explains the BASIC line that writes CHR$ (42 - A*42): a * normally, a $00 when the auto-flag A is set. The routine at $FB0C, called at the top of the main loop, sweeps the file and turns every $00 back into *.

How the two halves talk

The pointer p reaches the machine code through a side door. RANDOMIZE p stores it in SEED (23670/1), and BASIC then copies SEED into a parameter slot. Results return the same way: the search parser leaves its command-type code in SEED’s low byte, which BASIC reads back as j$.

The second trick appears all over the BASIC as LET X$=X$. A string self-assignment leaves DEST ($5C4D) pointing at the variable and STRLEN ($5C72) holding its length, so the machine code can locate a string by reading two system variables. The search parser and the numeric SUM guard both rely on it.

Memory map

RangeEntryFunction
$F801–$F829USR 63489Store one line of e$ into d$ (trim, $01 terminator, advance p)
$F82A–$F845USR 63530Finalise record: trailing $01 becomes *; return new p
$F857–$F880USR 63575Unpack and display a record into the 15×32 e$ array
$F898–$F8B7USR 63640Delete current record (shift tail down, shrink p)
$F8B8–$F92CTom Woods’ 117-byte ProFile 2068 printer driver
$F930–$F959USR 63792Numeric-field guard for the SUM totals (width at $F936 / POKE 63798)
$F9E2–$FA02Search pattern buffer
$FA16–$FA27Search-engine state slots
$FA28–$FA5AUSR 64040 / 64046Parse search command into the buffer; return type in SEED
$FA5B–$FA62base helper: HL = start of d$
$FA65–$FB0BAND-only matcher + record-boundary finder ($FAE1)
$FB0C–$FB22USR 64268Commit pass: every $00 separator becomes *
$FBC2–$FBD3Sort state slots
$FBD4–$FC0AInsert one record by block move (LDDR)
$FC0B–$FCDAUSR 64523Insertion-sort placement step (key line set by POKE 64602)
$FCDB–$FD01USR 64731Sort setup
$FE4D–$FF83USR 65101Boolean search engine (AND / OR / NOT)
$FF8C–$FFA8Match finaliser

The two search engines

There are two matchers. The interactive search at $FE4D is a full Boolean evaluator: it reads the operator tokens AND ($C6), OR ($C5) and NOT ($C3) embedded in the typed command and combines terms accordingly. The older matcher at $FA65 handles AND only and is called from the AUTO and ordered-operation loop at $FC92. Both carry byte-identical copies of the boundary-finder code ($FAE1 and $FF4D), which suggests the Boolean engine was layered onto the original during a revision.

The printer driver

The block at $F8B8–$F92C is Tom Woods’ 117-byte ProFile 2068 printer driver, carried in as a unit. The first three bytes are escape and state variables; the code handles a ^ escape prefix and expands a carriage return to CR+LF. Nothing in this module calls it — it is reached through the printer channel, installed separately.

Commented disassembly

Addresses and bytes are exact; mnemonics use $ hexadecimal. Long runs of $00 between routines are collapsed to a single padding line.

F800  00                 NOP

; --------------------------------------------------------------------
; USR 63489  ($F801)  -  STORE ONE LINE
; Append the next e$ line to d$: copy 32 chars, strip trailing
; spaces, write a 0x01 line-terminator, advance the pointer p.
; --------------------------------------------------------------------
F801  CD 5B FA           CALL $FA5B              ; base: HL = VARS+6 = first data byte of d$
F804  ED 4B 23 FA        LD BC,($FA23)           ; BC = p (free-space offset, passed in via SEED)
F808  C5                 PUSH BC
F809  09                 ADD HL,BC               ; HL = d$ + p  (current write position)
F80A  ED 5B 4D 5C        LD DE,($5C4D)           ; DE = DEST = address of the e$ line just self-assigned
F80E  EB                 EX DE,HL
F80F  01 20 00           LD BC,$0020             ; copy 32 chars of the line into d$
F812  ED B0              LDIR
F814  1B                 DEC DE                  ; walk back over trailing spaces, counting them in BC
F815  03                 INC BC
F816  1A                 LD A,(DE)
F817  FE 20              CP $20
F819  28 F9              JR Z,$F814
F81B  13                 INC DE
F81C  0B                 DEC BC
F81D  AF                 XOR A
F81E  3C                 INC A                   ; A = 1; store 0x01 line-terminator after trimmed text
F81F  12                 LD (DE),A
F820  3E 21              LD A,$21                ; stored length = 33 - C
F822  91                 SUB C
F823  4F                 LD C,A
F824  E1                 POP HL
F825  09                 ADD HL,BC
F826  22 23 FA           LD ($FA23),HL           ; advance p past the packed line
F829  C9                 RET
F82A  CD 5B FA           CALL $FA5B              ; base: HL = start of d$
F82D  ED 4B 23 FA        LD BC,($FA23)
F831  09                 ADD HL,BC
F832  AF                 XOR A
F833  3C                 INC A
F834  2B                 DEC HL                  ; step back over trailing 0x01 line markers
F835  0B                 DEC BC
F836  BE                 CP (HL)
F837  20 02              JR NZ,$F83B
F839  18 F9              JR $F834
F83B  23                 INC HL
F83C  03                 INC BC
F83D  03                 INC BC
F83E  3E 2A              LD A,$2A                ; A = '*'  (0x2A) record terminator
F840  77                 LD (HL),A               ; overwrite with end-of-record mark
F841  ED 43 23 FA        LD ($FA23),BC           ; return updated p in BC
F845  C9                 RET

F846            ; ---- 17 bytes of $00 padding ----

; --------------------------------------------------------------------
; USR 63575  ($F857)  -  UNPACK + DISPLAY RECORD
; Expand a packed record back into the 15x32 e$ array while
; printing it; 0x01 -> spaces, stop at '*' or 0x00.
; --------------------------------------------------------------------
F857  ED 5B 4D 5C        LD DE,($5C4D)           ; DE = DEST = e$ (unpack target)
F85B  2A 1A FA           LD HL,($FA1A)           ; HL = start of current record
F85E  06 20              LD B,$20                ; B = 32 columns per line
F860  23                 INC HL
F861  7E                 LD A,(HL)
F862  FE 2A              CP $2A                  ; '*' -> end of record
F864  C8                 RET Z
F865  FE 00              CP $00                  ; 0x00 -> end of record
F867  C8                 RET Z
F868  FE 01              CP $01                  ; 0x01 -> end of line, pad to 32 cols
F86A  28 0C              JR Z,$F878
F86C  12                 LD (DE),A               ; store char to e$ ...
F86D  D7                 RST $10                 ; ... and print it (ROM PRINT-A-1)
F86E  13                 INC DE
F86F  10 EF              DJNZ $F860
F871  23                 INC HL
F872  7E                 LD A,(HL)
F873  FE 01              CP $01
F875  28 E7              JR Z,$F85E
F877  C9                 RET
F878  3E 20              LD A,$20                ; fill remainder of the 32-col line with spaces
F87A  12                 LD (DE),A
F87B  D7                 RST $10
F87C  13                 INC DE
F87D  10 F9              DJNZ $F878
F87F  18 DD              JR $F85E

F881  20 20 20 20        DEFB $20,$20,$20,$20   ; "    "

F885            ; ---- 19 bytes of $00 padding ----

; --------------------------------------------------------------------
; USR 63640  ($F898)  -  DELETE CURRENT RECORD
; LDIR the remainder of d$ down over the record; shrink p.
; Returns the number of bytes removed.
; --------------------------------------------------------------------
F898  ED 5B 1A FA        LD DE,($FA1A)           ; DE = record start, HL = record end
F89C  2A 1C FA           LD HL,($FA1C)
F89F  E5                 PUSH HL
F8A0  ED 52              SBC HL,DE               ; BC = record length
F8A2  44                 LD B,H
F8A3  4D                 LD C,L
F8A4  E1                 POP HL
F8A5  C5                 PUSH BC
F8A6  ED 4B 18 FA        LD BC,($FA18)           ; BC = bytes following the record
F8AA  ED B0              LDIR                    ; shift the tail of d$ down over the deleted record
F8AC  C1                 POP BC
F8AD  2A 23 FA           LD HL,($FA23)
F8B0  ED 42              SBC HL,BC               ; shrink p by the record length; return bytes removed in BC
F8B2  03                 INC BC
F8B3  2B                 DEC HL
F8B4  22 23 FA           LD ($FA23),HL
F8B7  C9                 RET

; --------------------------------------------------------------------
; PRINTER DRIVER  ($F8B8-$F92C, 117 bytes)
; Tom Woods' standalone ProFile 2068 printer driver, loaded as a
; block at 63672. First 3 bytes are escape/state variables.
; Handles a $5E escape prefix (send a printer code as two hex
; digits) and expands CR to CR+LF.
; Installed via the printer channel; not called from this module.
; --------------------------------------------------------------------
F8B8  00 00 00           DEFB $00,$00,$00   ; printer escape/state vars

F8BB  FE 5E              CP $5E                  ; $5E = escape lead-in (next chars = a byte as hex)
F8BD  28 11              JR Z,$F8D0
F8BF  4F                 LD C,A
F8C0  3A B9 F8           LD A,($F8B9)
F8C3  FE 5E              CP $5E
F8C5  28 11              JR Z,$F8D8
F8C7  CD 09 20           CALL $2009              ; ROM/printer char-out entry
F8CA  38 40              JR C,$F90C
F8CC  CF                 RST $08
F8CD  0C                 INC C
F8CE  00                 NOP
F8CF  00                 NOP
F8D0  32 B9 F8           LD ($F8B9),A
F8D3  AF                 XOR A
F8D4  32 BA F8           LD ($F8BA),A
F8D7  C9                 RET
F8D8  79                 LD A,C                  ; decode a hex digit of the escape code
F8D9  FE 40              CP $40
F8DB  30 2B              JR NC,$F908
F8DD  D6 30              SUB $30
F8DF  4F                 LD C,A
F8E0  3A BA F8           LD A,($F8BA)
F8E3  FE 00              CP $00
F8E5  28 11              JR Z,$F8F8
F8E7  3A B8 F8           LD A,($F8B8)
F8EA  81                 ADD A,C
F8EB  4F                 LD C,A
F8EC  AF                 XOR A
F8ED  32 B9 F8           LD ($F8B9),A
F8F0  32 BA F8           LD ($F8BA),A
F8F3  32 B8 F8           LD ($F8B8),A
F8F6  18 CF              JR $F8C7
F8F8  79                 LD A,C                  ; shift first hex digit into the high nibble
F8F9  07                 RLCA
F8FA  07                 RLCA
F8FB  07                 RLCA
F8FC  07                 RLCA
F8FD  32 B8 F8           LD ($F8B8),A
F900  3A BA F8           LD A,($F8BA)
F903  3C                 INC A
F904  32 BA F8           LD ($F8BA),A
F907  C9                 RET
F908  D6 37              SUB $37
F90A  18 D3              JR $F8DF
F90C  DB BF              IN A,($BF)              ; poll printer status port
F90E  CB 47              BIT 0,A
F910  20 B5              JR NZ,$F8C7
F912  AF                 XOR A
F913  D3 FB              OUT ($FB),A             ; drive printer data/strobe ports
F915  3D                 DEC A
F916  D3 7B              OUT ($7B),A
F918  D3 FB              OUT ($FB),A
F91A  79                 LD A,C
F91B  D3 7B              OUT ($7B),A
F91D  3E F7              LD A,$F7
F91F  D3 FB              OUT ($FB),A
F921  3E FF              LD A,$FF
F923  D3 FB              OUT ($FB),A
F925  79                 LD A,C                  ; CR -> follow with LF
F926  FE 0D              CP $0D
F928  C0                 RET NZ
F929  0E 0A              LD C,$0A
F92B  18 9A              JR $F8C7

F92D            ; ---- 3 bytes of $00 padding ----

; --------------------------------------------------------------------
; USR 63792  ($F930)  -  NUMERIC-FIELD GUARD
; Returns non-zero if the e$ field is a valid number, so BASIC can
; safely VAL it into the SUM totals. Scan width = byte at F936
; (set by POKE 63798: 16 for SUM1, 32 for the whole field).
; --------------------------------------------------------------------
F930  2A 4D 5C           LD HL,($5C4D)           ; HL = DEST = field to test (e$ slice)
F933  2B                 DEC HL
F934  01 FF 10           LD BC,$10FF             ; B = max width to scan (byte at F936 = POKE 63798); C = 0xFF
F937  23                 INC HL
F938  3E 20              LD A,$20                ; skip leading spaces
F93A  BE                 CP (HL)
F93B  20 06              JR NZ,$F943
F93D  10 F8              DJNZ $F937
F93F  01 00 00           LD BC,$0000             ; not numeric -> return BC = 0
F942  C9                 RET
F943  3E 27              LD A,$27                ; reject anything below '0'-ish ...
F945  BE                 CP (HL)
F946  30 09              JR NC,$F951
F948  3E 39              LD A,$39                ; ... or above '9'
F94A  BE                 CP (HL)
F94B  38 F2              JR C,$F93F
F94D  23                 INC HL
F94E  10 F3              DJNZ $F943
F950  C9                 RET
F951  3E 20              LD A,$20
F953  BE                 CP (HL)
F954  20 E9              JR NZ,$F93F
F956  23                 INC HL
F957  10 FA              DJNZ $F953
F959  C9                 RET

F95A            ; ---- 103 bytes of $00 padding ----

F9C1  61 74 74 68        DEFB $61,$74,$74,$68   ; "atth"

F9C5            ; ---- 29 bytes of $00 padding ----

; --------------------------------------------------------------------
; SEARCH PATTERN BUFFER  ($F9E2)
; Working buffer the parser copies the typed command into,
; terminated with 0x5C. Bytes below are a runtime snapshot.
; --------------------------------------------------------------------
F9E2  5A 5A 5C 5C 20 38 35 2D 38 36 5C 6E 5C 5C 65 C5 77 6F 6F 64 73 C6 33 5C 6F 6F 64 73 C6 6A 6F 65 5C DEFB $5A,$5A,$5C,$5C,$20,$38,$35,$2D,$38,$36,$5C,$6E,$5C,$5C,$65,$C5,$77,$6F,$6F,$64,$73,$C6,$33,$5C,$6F,$6F,$64,$73,$C6,$6A,$6F,$65,$5C   ; "ZZ\ 85-86\n\e.woods.3\oods.joe\"

FA03            ; ---- 19 bytes of $00 padding ----

; --------------------------------------------------------------------
; SEARCH-ENGINE STATE  ($FA16-$FA27)
; 16-bit slots the routines share. Cursor is also visible to BASIC
; as POKE/PEEK 64022/64023 (save/restore for '>' continued search).
; --------------------------------------------------------------------
FA16  1B 87              DEFW $871B   ; cursor (also POKE/PEEK 64022/64023)
FA18  01 00              DEFW $0001   ; bytes_remaining
FA1A  14 87              DEFW $8714   ; rec_start
FA1C  1B 87              DEFW $871B   ; rec_end
FA1E  00 E7              DEFW $E700   ; (spare)
FA20  F9                 DEFB $F9     ; not_flag
FA21  E4 F9              DEFW $F9E4   ; pat_save
FA23  1D 00              DEFW $001D   ; p  (file-end ptr; via SEED)
FA25  E2 F9              DEFW $F9E2   ; pat_ptr -> F9E2
FA27  00                 DEFB $00     ; (spare)

; --------------------------------------------------------------------
; USR 64040  ($FA28)  -  PARSE SEARCH COMMAND
; Copy X$ into the pattern buffer (via DEST/STRLEN) + 0x5C sentinel.
; Returns a command-type code in SEED low byte (BASIC reads it as j$).
; $FA2E is a second entry used to re-seed for '>'/'>=' searches.
; --------------------------------------------------------------------
FA28  2A 23 FA           LD HL,($FA23)           ; ($FA18) = p : whole file is the search span
FA2B  22 18 FA           LD ($FA18),HL
FA2E  21 E2 F9           LD HL,$F9E2             ; ($FA25) -> pattern buffer at F9E2
FA31  22 25 FA           LD ($FA25),HL
FA34  2A 4D 5C           LD HL,($5C4D)           ; HL = DEST = the X$ command variable
FA37  01 04 00           LD BC,$0004
FA3A  09                 ADD HL,BC
FA3B  11 E2 F9           LD DE,$F9E2
FA3E  ED 4B 72 5C        LD BC,($5C72)           ; BC = STRLEN = its length
FA42  ED B0              LDIR                    ; copy command text into the F9E2 buffer
FA44  3E 5C              LD A,$5C                ; append 0x5C sentinel
FA46  12                 LD (DE),A
FA47  C5                 PUSH BC
FA48  E5                 PUSH HL
FA49  CD 5B FA           CALL $FA5B
FA4C  22 16 FA           LD ($FA16),HL
FA4F  E1                 POP HL
FA50  C1                 POP BC
FA51  03                 INC BC
FA52  2B                 DEC HL
FA53  2B                 DEC HL
FA54  BE                 CP (HL)
FA55  C0                 RET NZ
FA56  23                 INC HL
FA57  4E                 LD C,(HL)
FA58  C9                 RET
FA59  00                 NOP
FA5A  00                 NOP

; --------------------------------------------------------------------
; base  ($FA5B)  -  HL = start of d$ data (VARS+6)
; --------------------------------------------------------------------
FA5B  2A 4B 5C           LD HL,($5C4B)           ; base helper: HL = VARS+6
FA5E  01 06 00           LD BC,$0006
FA61  09                 ADD HL,BC
FA62  C9                 RET

FA63            ; ---- 2 bytes of $00 padding ----

; --------------------------------------------------------------------
; AND-ONLY MATCHER  ($FA65)
; Substring search with AND (0xC6) support only. Used by the
; AUTO / ordered-operation loop at $FC92, not interactive search.
; --------------------------------------------------------------------
FA65  2A 16 FA           LD HL,($FA16)           ; HL=cursor, BC=bytes left, DE=pattern ptr
FA68  ED 4B 18 FA        LD BC,($FA18)
FA6C  ED 5B 25 FA        LD DE,($FA25)
FA70  1A                 LD A,(DE)               ; A = first pattern char
FA71  ED B1              CPIR                    ; CPIR: fast scan d$ for first char
FA73  E2 AC FA           JP PO,$FAAC             ; ran past end -> no match
FA76  22 16 FA           LD ($FA16),HL
FA79  ED 43 18 FA        LD ($FA18),BC
FA7D  13                 INC DE
FA7E  1A                 LD A,(DE)
FA7F  FE C6              CP $C6                  ; 0xC6 = AND token
FA81  28 0D              JR Z,$FA90
FA83  FE 5C              CP $5C                  ; 0x5C = end of pattern -> hit
FA85  28 1A              JR Z,$FAA1
FA87  ED A1              CPI                     ; compare next pattern char
FA89  E2 AC FA           JP PO,$FAAC
FA8C  28 EF              JR Z,$FA7D
FA8E  18 D5              JR $FA65
FA90  CD E1 FA           CALL $FAE1
FA93  CD C1 FA           CALL $FAC1
FA96  2A 16 FA           LD HL,($FA16)
FA99  ED 4B 18 FA        LD BC,($FA18)
FA9D  28 DF              JR Z,$FA7E
FA9F  18 C4              JR $FA65
FAA1  CD E1 FA           CALL $FAE1
FAA4  2A 1A FA           LD HL,($FA1A)
FAA7  AF                 XOR A
FAA8  BE                 CP (HL)
FAA9  28 BA              JR Z,$FA65
FAAB  C9                 RET
FAAC  CD 5B FA           CALL $FA5B
FAAF  23                 INC HL
FAB0  CD E1 FA           CALL $FAE1
FAB3  01 12 00           LD BC,$0012             ; back up 0x12 bytes (record-relative)
FAB6  ED 42              SBC HL,BC
FAB8  ED 4B 23 FA        LD BC,($FA23)
FABC  ED 43 18 FA        LD ($FA18),BC
FAC0  C9                 RET
FAC1  2A 1C FA           LD HL,($FA1C)
FAC4  ED 4B 1A FA        LD BC,($FA1A)
FAC8  C5                 PUSH BC
FAC9  ED 42              SBC HL,BC
FACB  E5                 PUSH HL
FACC  C1                 POP BC
FACD  E1                 POP HL
FACE  ED 5B 21 FA        LD DE,($FA21)
FAD2  13                 INC DE
FAD3  1A                 LD A,(DE)
FAD4  FE C6              CP $C6
FAD6  C8                 RET Z
FAD7  FE 5C              CP $5C
FAD9  C8                 RET Z
FADA  ED A1              CPI
FADC  E0                 RET PO
FADD  28 F3              JR Z,$FAD2
FADF  18 ED              JR $FACE
FAE1  E5                 PUSH HL                 ; boundary finder: scan back for '*'/0x00 -> rec_start
FAE2  2B                 DEC HL
FAE3  7E                 LD A,(HL)
FAE4  FE 2A              CP $2A
FAE6  28 04              JR Z,$FAEC
FAE8  FE 00              CP $00
FAEA  20 F6              JR NZ,$FAE2
FAEC  22 1A FA           LD ($FA1A),HL
FAEF  E1                 POP HL
FAF0  7E                 LD A,(HL)               ; scan forward for '*'/0x00 -> rec_end
FAF1  FE 2A              CP $2A
FAF3  28 08              JR Z,$FAFD
FAF5  FE 00              CP $00
FAF7  28 04              JR Z,$FAFD
FAF9  23                 INC HL
FAFA  0B                 DEC BC
FAFB  18 F3              JR $FAF0
FAFD  22 1C FA           LD ($FA1C),HL
FB00  ED 43 18 FA        LD ($FA18),BC
FB04  22 16 FA           LD ($FA16),HL
FB07  ED 53 21 FA        LD ($FA21),DE
FB0B  C9                 RET

; --------------------------------------------------------------------
; USR 64268  ($FB0C)  -  COMMIT SEPARATORS
; Sweep the file converting every transient 0x00 separator to '*'.
; Called at the top of the main loop (BASIC line 4).
; --------------------------------------------------------------------
FB0C  CD 5B FA           CALL $FA5B              ; base: HL = start of d$
FB0F  ED 4B 23 FA        LD BC,($FA23)
FB13  AF                 XOR A                   ; A = 0; CPIR scan for a 0x00 separator
FB14  ED B1              CPIR
FB16  E2 1E FB           JP PO,$FB1E
FB19  2B                 DEC HL
FB1A  36 2A              LD (HL),$2A             ; commit it: 0x00 -> '*'
FB1C  18 F6              JR $FB14
FB1E  ED 4B 23 FA        LD BC,($FA23)           ; return p in BC
FB22  C9                 RET

FB23            ; ---- 159 bytes of $00 padding ----

; --------------------------------------------------------------------
; SORT STATE  ($FBC2-$FBD3)
; 16-bit slots for the insertion sort: data_end, key pointers,
; insertion target, gap length, byte count, step accumulator.
; --------------------------------------------------------------------
FBC2  00 00              DEFW $0000   ; data_end
FBC4  00 00              DEFW $0000   ; key_ptr_a
FBC6  00 00              DEFW $0000   ; key_ptr_b
FBC8  00 00              DEFW $0000   ; (spare)
FBCA  00 00              DEFW $0000   ; insert_target
FBCC  00 00              DEFW $0000   ; insert_dest
FBCE  00 00              DEFW $0000   ; gap_len
FBD0  00 00              DEFW $0000   ; byte_count
FBD2  00 00              DEFW $0000   ; step_accum

; --------------------------------------------------------------------
; INSERT  ($FBD4)  -  open a gap and slot a record in (LDDR)
; --------------------------------------------------------------------
FBD4  D1                 POP DE                  ; INSERT: open a gap with LDDR and drop one record in
FBD5  1B                 DEC DE
FBD6  2A CA FB           LD HL,($FBCA)
FBD9  D5                 PUSH DE
FBDA  ED 53 CC FB        LD ($FBCC),DE
FBDE  EB                 EX DE,HL
FBDF  ED 52              SBC HL,DE
FBE1  23                 INC HL
FBE2  22 CE FB           LD ($FBCE),HL
FBE5  EB                 EX DE,HL
FBE6  D1                 POP DE
FBE7  ED 43 D0 FB        LD ($FBD0),BC
FBEB  1A                 LD A,(DE)
FBEC  E5                 PUSH HL
FBED  62                 LD H,D
FBEE  6B                 LD L,E
FBEF  2B                 DEC HL
FBF0  ED 4B CE FB        LD BC,($FBCE)
FBF4  ED B8              LDDR
FBF6  E1                 POP HL
FBF7  77                 LD (HL),A
FBF8  ED 5B CC FB        LD DE,($FBCC)
FBFC  E5                 PUSH HL
FBFD  2A D0 FB           LD HL,($FBD0)
FC00  01 01 00           LD BC,$0001
FC03  ED 42              SBC HL,BC
FC05  22 D0 FB           LD ($FBD0),HL
FC08  E1                 POP HL
FC09  20 E0              JR NZ,$FBEB

; --------------------------------------------------------------------
; USR 64523  ($FC0B)  -  INSERTION-SORT STEP
; Place the next record into key order. Compares the chosen key
; line byte-by-byte; jumps to INSERT on 'less than'. The key line
; number is the LD B operand at F C5A (POKE 64602).
; --------------------------------------------------------------------
FC0B  CD EE FC           CALL $FCEE              ; one insertion-sort placement step
FC0E  ED 4B D2 FB        LD BC,($FBD2)
FC12  09                 ADD HL,BC
FC13  CD 54 FC           CALL $FC54
FC16  20 0F              JR NZ,$FC27
FC18  ED 53 C4 FB        LD ($FBC4),DE
FC1C  E5                 PUSH HL
FC1D  2A C6 FB           LD HL,($FBC6)
FC20  22 CA FB           LD ($FBCA),HL
FC23  E1                 POP HL
FC24  CD 54 FC           CALL $FC54
FC27  20 69              JR NZ,$FC92
FC29  E5                 PUSH HL
FC2A  2A C4 FB           LD HL,($FBC4)
FC2D  1A                 LD A,(DE)
FC2E  BE                 CP (HL)
FC2F  38 A3              JR C,$FBD4
FC31  20 F0              JR NZ,$FC23
FC33  EB                 EX DE,HL
FC34  23                 INC HL
FC35  13                 INC DE
FC36  3E 01              LD A,$01
FC38  BE                 CP (HL)
FC39  28 12              JR Z,$FC4D
FC3B  3E 2A              LD A,$2A
FC3D  BE                 CP (HL)
FC3E  28 0D              JR Z,$FC4D
FC40  EB                 EX DE,HL
FC41  3E 01              LD A,$01
FC43  BE                 CP (HL)
FC44  28 DD              JR Z,$FC23
FC46  3E 2A              LD A,$2A
FC48  BE                 CP (HL)
FC49  28 D8              JR Z,$FC23
FC4B  18 E0              JR $FC2D
FC4D  1A                 LD A,(DE)
FC4E  BE                 CP (HL)
FC4F  28 D2              JR Z,$FC23
FC51  C3 D4 FB           JP $FBD4
FC54  54                 LD D,H                  ; locate the key line within a record
FC55  5D                 LD E,L
FC56  22 C6 FB           LD ($FBC6),HL
FC59  06 01              LD B,$01                ; B = sort key line number (byte at F C5A = POKE 64602)
FC5B  CD D0 FC           CALL $FCD0
FC5E  30 1B              JR NC,$FC7B
FC60  3E 2A              LD A,$2A
FC62  BE                 CP (HL)
FC63  28 18              JR Z,$FC7D
FC65  3E 01              LD A,$01
FC67  BE                 CP (HL)
FC68  23                 INC HL
FC69  20 F5              JR NZ,$FC60
FC6B  10 20              DJNZ $FC8D
FC6D  2A C6 FB           LD HL,($FBC6)
FC70  01 00 00           LD BC,$0000
FC73  3E 2A              LD A,$2A
FC75  03                 INC BC
FC76  23                 INC HL
FC77  BE                 CP (HL)
FC78  20 FB              JR NZ,$FC75
FC7A  C9                 RET
FC7B  A7                 AND A
FC7C  C9                 RET
FC7D  E5                 PUSH HL
FC7E  D5                 PUSH DE
FC7F  ED 5B C6 FB        LD DE,($FBC6)
FC83  ED 52              SBC HL,DE
FC85  D1                 POP DE
FC86  E1                 POP HL
FC87  23                 INC HL
FC88  28 D6              JR Z,$FC60
FC8A  2B                 DEC HL
FC8B  18 E0              JR $FC6D
FC8D  54                 LD D,H
FC8E  5D                 LD E,L
FC8F  1B                 DEC DE
FC90  18 CE              JR $FC60
FC92  2A CA FB           LD HL,($FBCA)           ; find insertion position for current record
FC95  01 00 00           LD BC,$0000
FC98  3E 2A              LD A,$2A
FC9A  CD D0 FC           CALL $FCD0
FC9D  30 3C              JR NC,$FCDB
FC9F  23                 INC HL
FCA0  03                 INC BC
FCA1  BE                 CP (HL)
FCA2  20 FB              JR NZ,$FC9F
FCA4  ED 5B CA FB        LD DE,($FBCA)
FCA8  ED 53 D0 FB        LD ($FBD0),DE
FCAC  22 CA FB           LD ($FBCA),HL
FCAF  22 C6 FB           LD ($FBC6),HL
FCB2  2A D2 FB           LD HL,($FBD2)
FCB5  09                 ADD HL,BC
FCB6  22 D2 FB           LD ($FBD2),HL
FCB9  CD 65 FA           CALL $FA65              ; test record with the AND matcher
FCBC  A7                 AND A
FCBD  2A 1A FA           LD HL,($FA1A)
FCC0  ED 5B D0 FB        LD DE,($FBD0)
FCC4  ED 52              SBC HL,DE
FCC6  C8                 RET Z
FCC7  A7                 AND A
FCC8  2A 23 FA           LD HL,($FA23)
FCCB  ED 42              SBC HL,BC
FCCD  20 EA              JR NZ,$FCB9
FCCF  C9                 RET
FCD0  E5                 PUSH HL                 ; compare HL against data_end
FCD1  C5                 PUSH BC
FCD2  ED 4B C2 FB        LD BC,($FBC2)
FCD6  ED 42              SBC HL,BC
FCD8  C1                 POP BC
FCD9  E1                 POP HL
FCDA  C9                 RET

; --------------------------------------------------------------------
; USR 64731  ($FCDB)  -  SORT SETUP
; --------------------------------------------------------------------
FCDB  21 1B 00           LD HL,$001B             ; SORT SETUP: init pointers + key offset
FCDE  22 D2 FB           LD ($FBD2),HL
FCE1  E5                 PUSH HL
FCE2  CD EE FC           CALL $FCEE
FCE5  D1                 POP DE
FCE6  19                 ADD HL,DE
FCE7  22 C6 FB           LD ($FBC6),HL
FCEA  22 CA FB           LD ($FBCA),HL
FCED  C9                 RET
FCEE  2A 4B 5C           LD HL,($5C4B)           ; compute start (d$+1) and end (d$+p-1)
FCF1  01 06 00           LD BC,$0006
FCF4  09                 ADD HL,BC
FCF5  E5                 PUSH HL
FCF6  ED 4B 23 FA        LD BC,($FA23)
FCFA  09                 ADD HL,BC
FCFB  2B                 DEC HL
FCFC  22 C2 FB           LD ($FBC2),HL
FCFF  E1                 POP HL
FD00  23                 INC HL
FD01  C9                 RET

FD02            ; ---- 331 bytes of $00 padding ----

; --------------------------------------------------------------------
; USR 65101  ($FE4D)  -  BOOLEAN SEARCH (AND / OR / NOT)
; The interactive search engine. CPIR finds the first character
; fast, CPI checks the rest, and operator tokens branch:
; 0xC6 AND, 0xC5 OR, 0xC3 NOT, 0x5C end-of-pattern.
; On a hit it exposes the record and returns to BASIC.
; --------------------------------------------------------------------
FE4D  2A 16 FA           LD HL,($FA16)           ; HL=cursor, BC=bytes left, DE=pattern ptr
FE50  ED 4B 18 FA        LD BC,($FA18)
FE54  ED 5B 25 FA        LD DE,($FA25)
FE58  1A                 LD A,(DE)               ; A = first pattern char
FE59  ED B1              CPIR                    ; CPIR: fast scan for first char
FE5B  E2 C6 FE           JP PO,$FEC6
FE5E  22 16 FA           LD ($FA16),HL
FE61  ED 43 18 FA        LD ($FA18),BC
FE65  13                 INC DE
FE66  1A                 LD A,(DE)
FE67  FE C6              CP $C6                  ; 0xC6 = AND
FE69  28 15              JR Z,$FE80
FE6B  FE C5              CP $C5                  ; 0xC5 = OR
FE6D  28 41              JR Z,$FEB0
FE6F  FE C3              CP $C3                  ; 0xC3 = NOT
FE71  28 2C              JR Z,$FE9F
FE73  FE 5C              CP $5C                  ; 0x5C = end of pattern
FE75  28 1A              JR Z,$FE91
FE77  ED A1              CPI
FE79  E2 C6 FE           JP PO,$FEC6
FE7C  28 E7              JR Z,$FE65
FE7E  18 CD              JR $FE4D
FE80  CD 4D FF           CALL $FF4D
FE83  CD 2A FF           CALL $FF2A
FE86  2A 16 FA           LD HL,($FA16)
FE89  ED 4B 18 FA        LD BC,($FA18)
FE8D  28 D7              JR Z,$FE66
FE8F  18 BC              JR $FE4D
FE91  CD 4D FF           CALL $FF4D
FE94  2A 1A FA           LD HL,($FA1A)
FE97  AF                 XOR A
FE98  BE                 CP (HL)
FE99  3E 2A              LD A,$2A
FE9B  77                 LD (HL),A
FE9C  28 AF              JR Z,$FE4D
FE9E  C9                 RET
FE9F  CD 4D FF           CALL $FF4D
FEA2  CD 2A FF           CALL $FF2A
FEA5  2A 16 FA           LD HL,($FA16)
FEA8  ED 4B 18 FA        LD BC,($FA18)
FEAC  20 49              JR NZ,$FEF7
FEAE  18 9D              JR $FE4D
FEB0  3E 01              LD A,$01                ; set NOT flag
FEB2  32 20 FA           LD ($FA20),A
FEB5  ED 53 21 FA        LD ($FA21),DE
FEB9  CD 4D FF           CALL $FF4D
FEBC  2A 1A FA           LD HL,($FA1A)
FEBF  AF                 XOR A
FEC0  BE                 CP (HL)
FEC1  28 8A              JR Z,$FE4D
FEC3  AF                 XOR A
FEC4  77                 LD (HL),A
FEC5  C9                 RET
FEC6  3A 20 FA           LD A,($FA20)            ; end of span: act on accumulated AND/OR/NOT state
FEC9  87                 ADD A,A
FECA  20 48              JR NZ,$FF14
FECC  CD 5B FA           CALL $FA5B
FECF  23                 INC HL
FED0  CD 4D FF           CALL $FF4D
FED3  01 1B 00           LD BC,$001B
FED6  A7                 AND A
FED7  ED 42              SBC HL,BC
FED9  18 31              JR $FF0C
FEDB  ED 5B 21 FA        LD DE,($FA21)
FEDF  13                 INC DE
FEE0  ED 53 25 FA        LD ($FA25),DE
FEE4  CD 5B FA           CALL $FA5B
FEE7  22 16 FA           LD ($FA16),HL
FEEA  2A 23 FA           LD HL,($FA23)
FEED  22 18 FA           LD ($FA18),HL
FEF0  AF                 XOR A
FEF1  32 20 FA           LD ($FA20),A
FEF4  C3 4D FE           JP $FE4D
FEF7  13                 INC DE
FEF8  1A                 LD A,(DE)
FEF9  FE C6              CP $C6
FEFB  CA 80 FE           JP Z,$FE80
FEFE  FE C5              CP $C5
FF00  28 AE              JR Z,$FEB0
FF02  FE C3              CP $C3
FF04  28 99              JR Z,$FE9F
FF06  FE 5C              CP $5C
FF08  28 87              JR Z,$FE91
FF0A  18 EB              JR $FEF7
FF0C  ED 4B 23 FA        LD BC,($FA23)
FF10  ED 43 18 FA        LD ($FA18),BC
FF14  13                 INC DE
FF15  1A                 LD A,(DE)
FF16  FE 5C              CP $5C
FF18  CA 8C FF           JP Z,$FF8C
FF1B  FE C5              CP $C5
FF1D  20 F5              JR NZ,$FF14
FF1F  3E 01              LD A,$01
FF21  32 20 FA           LD ($FA20),A
FF24  ED 53 21 FA        LD ($FA21),DE
FF28  18 B1              JR $FEDB
FF2A  2A 1C FA           LD HL,($FA1C)           ; compare remainder of an OR/AND sub-term
FF2D  ED 4B 1A FA        LD BC,($FA1A)
FF31  C5                 PUSH BC
FF32  A7                 AND A
FF33  ED 42              SBC HL,BC
FF35  E5                 PUSH HL
FF36  C1                 POP BC
FF37  E1                 POP HL
FF38  ED 5B 21 FA        LD DE,($FA21)
FF3C  13                 INC DE
FF3D  1A                 LD A,(DE)
FF3E  CD 78 FF           CALL $FF78
FF41  C8                 RET Z
FF42  ED A1              CPI
FF44  EA 49 FF           JP PE,$FF49
FF47  A7                 AND A
FF48  C9                 RET
FF49  28 F1              JR Z,$FF3C
FF4B  18 EB              JR $FF38
FF4D  E5                 PUSH HL                 ; boundary finder (duplicate of FAE1, used by this engine)
FF4E  2B                 DEC HL
FF4F  7E                 LD A,(HL)
FF50  FE 2A              CP $2A
FF52  28 04              JR Z,$FF58
FF54  FE 00              CP $00
FF56  20 F6              JR NZ,$FF4E
FF58  22 1A FA           LD ($FA1A),HL
FF5B  E1                 POP HL
FF5C  7E                 LD A,(HL)
FF5D  FE 2A              CP $2A
FF5F  28 08              JR Z,$FF69
FF61  FE 00              CP $00
FF63  28 04              JR Z,$FF69
FF65  23                 INC HL
FF66  0B                 DEC BC
FF67  18 F3              JR $FF5C
FF69  22 1C FA           LD ($FA1C),HL
FF6C  ED 43 18 FA        LD ($FA18),BC
FF70  22 16 FA           LD ($FA16),HL
FF73  ED 53 21 FA        LD ($FA21),DE
FF77  C9                 RET
FF78  FE C6              CP $C6                  ; is A one of the operator tokens (AND/OR/NOT/end)?
FF7A  C8                 RET Z
FF7B  FE C5              CP $C5
FF7D  C8                 RET Z
FF7E  FE C3              CP $C3
FF80  C8                 RET Z
FF81  FE 5C              CP $5C
FF83  C9                 RET

FF84            ; ---- 8 bytes of $00 padding ----

; --------------------------------------------------------------------
; MATCH FINALISER  ($FF8C)
; --------------------------------------------------------------------
FF8C  CD 5B FA           CALL $FA5B              ; match finalizer: normalise 0x00 -> '*', set rec_start
FF8F  E5                 PUSH HL
FF90  ED 4B 23 FA        LD BC,($FA23)
FF94  AF                 XOR A
FF95  ED B1              CPIR
FF97  E2 9F FF           JP PO,$FF9F
FF9A  2B                 DEC HL
FF9B  36 2A              LD (HL),$2A
FF9D  18 F6              JR $FF95
FF9F  ED 4B 23 FA        LD BC,($FA23)
FFA3  E1                 POP HL
FFA4  00                 NOP
FFA5  22 1A FA           LD ($FA1A),HL
FFA8  C9                 RET

FFA9            ; ---- 85 bytes of $00 padding ----
Scroll to Top