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
| Range | Entry | Function |
|---|---|---|
| $F801–$F829 | USR 63489 | Store one line of e$ into d$ (trim, $01 terminator, advance p) |
| $F82A–$F845 | USR 63530 | Finalise record: trailing $01 becomes *; return new p |
| $F857–$F880 | USR 63575 | Unpack and display a record into the 15×32 e$ array |
| $F898–$F8B7 | USR 63640 | Delete current record (shift tail down, shrink p) |
| $F8B8–$F92C | — | Tom Woods’ 117-byte ProFile 2068 printer driver |
| $F930–$F959 | USR 63792 | Numeric-field guard for the SUM totals (width at $F936 / POKE 63798) |
| $F9E2–$FA02 | — | Search pattern buffer |
| $FA16–$FA27 | — | Search-engine state slots |
| $FA28–$FA5A | USR 64040 / 64046 | Parse search command into the buffer; return type in SEED |
| $FA5B–$FA62 | — | base helper: HL = start of d$ |
| $FA65–$FB0B | — | AND-only matcher + record-boundary finder ($FAE1) |
| $FB0C–$FB22 | USR 64268 | Commit pass: every $00 separator becomes * |
| $FBC2–$FBD3 | — | Sort state slots |
| $FBD4–$FC0A | — | Insert one record by block move (LDDR) |
| $FC0B–$FCDA | USR 64523 | Insertion-sort placement step (key line set by POKE 64602) |
| $FCDB–$FD01 | USR 64731 | Sort setup |
| $FE4D–$FF83 | USR 65101 | Boolean search engine (AND / OR / NOT) |
| $FF8C–$FFA8 | — | Match 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 ----