--- title: "ProFile with Machine Code" id: 52151 type: "computer_media" slug: "profile-5" url: "http://localhost/computer_media/profile-5/" markdown_url: "http://localhost/computer_media/profile-5.md" published_at: "2023-09-09T18:35:27+00:00" modified_at: "2026-03-30T21:44:48+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2023/09/ProFile-2068-with-MC-j-card.jpg" excerpt: "A full-featured flat-file database with a custom screen editor, machine code search routines, formatted printing, and tape load/save — all in BASIC." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "Thomas B. Woods (company)" slug: "thomas-b-woods" taxonomy: "post_tag" url: "http://localhost/tag/thomas-b-woods/" - name: "TS 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" indiv: - name: "Thomas B. Woods" slug: "thomas-b-woods" taxonomy: "indiv" url: "http://localhost/indiv/thomas-b-woods/" genre: - name: "Database" slug: "database" taxonomy: "genre" url: "http://localhost/type/database/" media_type: "Program" programmers: - name: "Thomas B. Woods" slug: "thomas-b-woods" taxonomy: "indiv" url: "http://localhost/indiv/thomas-b-woods/" download_url: "https://archive.org/download/timex-sinclair-software-archive/ProFile%202068%20with%20MC%20%281984%29%28Woods%2C%20Thomas%29%28US%29%28TS2068%29%28Cassette%29.zip" mediadate: "1985" producer_company: - id: 11390 title: "Thomas B. Woods" type: "company" url: "http://localhost/company/thomas-b-woods/" images: - url: "http://localhost/wp-content/uploads/2023/09/ProFile-2068-with-MC-j-card.jpg" - url: "http://localhost/wp-content/uploads/2023/09/ProFile-2068-with-MC-cassette.jpg" article_media: - id: 45040 title: "Profile +5 for the 2068" type: "article" url: "http://localhost/article/profile-5-for-the-2068/" related_products: - id: 13646 title: "Pro/File 2068" type: "product" url: "http://localhost/product/pro-file-2068-2/" media_type_tags: "Database" --- PRO/FILE 2068 is a text-based flat-file database manager that stores up to 15 lines of 32-character text per record within a single large string variable dimensioned at 28,020 bytes. Records are searched, added, edited, deleted, and sorted using a combination of BASIC control flow and machine code routines called via RANDOMIZE USR at addresses in the 63,000–64,000 range. The full-screen editor (lines 5000–5117) uses ON ERR trapping, SCREEN$ reads, and INKEY$ polling to implement cursor movement, insert/overwrite toggle, line insertion and deletion, and a flashing cursor effect. An AUTOSEARCH mode (lines 5200–5441) can sort records by a user-specified line number field and optionally send formatted output to the printer using LPRINT with a configurable C$ format string. The diagnostic routine at line 8990 allows direct inspection and modification of POKEd memory locations in the machine code workspace. *** ## Program Analysis ### Program Structure The program is organized into several functional regions: 1. **Initialization and main menu** (lines 5–50): Sets up display, dimensions the record buffer `E$(15,32)`, and calls machine code via `RANDOMIZE USR 64268`. 2. **Search dispatch loop** (lines 100–119): Accepts a search command string `X$` and branches to add, load, save, autosearch, or format-change routines. 3. **Search execution and record display** (lines 120–2000): Machine code at `USR 64040` performs the search; `USR 64101` returns a pointer `b`; results are displayed with paged navigation and edit/delete/copy options. 4. **Record modification** (lines 4000–4010): Deletes a record via `USR 63640`, adjusting the free-space pointer `p`. 5. **Full-screen editor** (lines 5000–5117): A complete cursor-addressed text editor for entering or modifying 15-line records. 6. **AUTOSEARCH / sort mode** (lines 5200–5441): Sorts records by a chosen field line, with optional printer output. 7. **Tape load/save** (lines 5500–5510, line 107): Uses `LOAD F$` and `SAVE f$ LINE 1` for named tape operations. 8. **Record commit** (lines 6000–6030): Iterates over all 15 editor lines, calling `USR 63489` to write each to the database string, then calls `USR 63530` to update `p`. 9. **Print format input** (lines 6500–6600): Parses a slash-delimited list of line numbers (or “ALL”) into the format string `C$`. 10. **Formatted output** (lines 7000–7240): Iterates over `C$`, using `CODE C$(X)` as a 1-based index into `E$()` to select which record lines to `LPRINT`. 11. **Diagnostic POKE/PEEK loop** (lines 8990–8998): Allows interactive inspection and patching of the machine code workspace. 12. **Drawing subroutine** (line 9830): Draws a rectangular border around status information using `PLOT`/`DRAW`. 13. **Status display subroutine** (line 9850): Prints free bytes, filename, sort order, and format string. 14. **First-run initialization** (lines 9996–9999): Dimensions the main database string `D$(28020)`, seeds the sentinel record, and prompts to create or load a file. ### Machine Code Integration A substantial portion of the core database logic resides in machine code called from BASIC via `RANDOMIZE USR`. The BASIC side passes data through shared memory locations and system variables rather than through BASIC parameters. Known entry points and their roles are: | Address | Role | | --- | --- | | `64268` | Initialization / bounds check (called at startup and before edit operations) | | `64040` | Execute search using current `X$` | | `64101` | Return current record pointer into `b` | | `63575` | Display next record field line; returns non-zero when more data remains | | `63489` | Write one editor line (`e$(x)`) to the database | | `63530` | Finalize record write; returns updated free pointer for `p` | | `63640` | Delete current record; returns bytes freed (subtracted from `p`) | | `64523` | Advance to next record in sort/autosearch pass; returns new pointer `b` | | `64731` | Initialize autosearch/sort pass | The machine code workspace occupies roughly addresses 63489–64769. The diagnostic loop at lines 8990–8998 iterates over addresses 64468–64769, allowing direct `POKE` patching of machine code parameters at runtime. The sort field is passed by POKEing address `64602` with the chosen line number `s`. ### Database Storage Model The database is held in a single string variable `D$` dimensioned to 28,020 bytes, providing enough space for hundreds of 15-line × 32-character records. The variable `p` serves as a free-space pointer: `LEN D$ - p` (displayed by the status routine at line 9850) gives the number of bytes in use. The sentinel string `"*SEARCH IS COMPLETE*"` is written into the first 20 bytes of `D$` and detected by machine code to signal end-of-file. At startup (line 5), `POKE 64035, PEEK 23670` and `POKE 64036, PEEK 23671` copy the PROG pointer into the machine code workspace, presumably so the routines can locate the BASIC program area. ### Full-Screen Editor (Lines 5000–5117) The editor uses a 15-row × 32-column grid managed by row `l` and column `c` variables, mirrored in the string array `E$(15,32)`. Key techniques include: - **SCREEN$ reads**: The flashing cursor is implemented by reading the character under the cursor with `SCREEN$(l,c)` and reprinting it with `FLASH 1`, avoiding the need to store cursor-position characters separately. - **ON ERR GO TO 5005**: Traps any error (e.g., from `SCREEN$` on an attribute boundary) and returns to the cursor flash loop silently. - **Key dispatch via GO TO 5100+CODE y$**: Control characters (codes 0–15) are dispatched arithmetically. Line 5103 acts as a default (GO TO 5010). Lines 5104–5117 handle specific control codes: cursor up/down/left/right, delete, insert toggle, line feed, line insert, line delete, and the “more commands” overlay. - **Insert/overwrite toggle**: Variable `i` (0 or 1) selects between modes. In insert mode (line 5025), the existing line content is shifted right by one character before printing. - **STOP key detection**: Line 5016 checks for `INKEY$=" STOP "` (the extended-mode STOP token) to close the editor and commit the record. - **Column wrap**: At line 5030, `LET c=(c+1)*(c<31)` and `LET l=l+(c=0)` wrap to the next row when column 31 is exceeded. ### AUTOSEARCH and Formatted Printing AUTOSEARCH mode (entered with command `"AUTO"`) optionally sorts records by a chosen line number field (`s`, stored via `POKE 64602,s`), then iterates through all records in sorted order. The format string `C$` is built in the format parser (lines 6500–6600): each character’s ASCII code (1–15) indexes into `E$()` to select which of the 15 record lines to print. A `CHR$ 0` in `C$` causes a blank `LPRINT` line. This design allows arbitrary reordering and selection of record fields for printed output without any machine code involvement. ### Notable BASIC Idioms - `LET x$=x$` at lines 119 and 5441 is used deliberately: on this platform, assigning a string variable to itself can force a re-evaluation of its storage address, which is required before passing `X$` to a machine code routine that needs a stable pointer. - `GO TO 150+(895*a)` at line 1060 computes the branch target arithmetically: when `a=0` it goes to line 150 (normal search loop), and when `a=1` it goes to line 1045 (autosearch display path). - `GO TO 119+(A*5121)` at line 1090 similarly dispatches between the normal re-search (line 119) and autosearch re-initiation (line 5240) based on the `A` flag. - The `IF ... THEN` (empty THEN clause) construct at lines 1000, 1075, 6005, 7310 is used to call machine code via `USR` in a conditional context: if the USR call returns non-zero, the (empty) THEN branch does nothing and execution falls through; a zero return also falls through. The actual side-effect is the machine code call itself. - `VAL Z$` is used throughout after `INPUT Z$` to convert user-entered numeric strings to numbers, with `ON ERR GO TO 5220` providing validation. ### Bugs and Anomalies - Line 115 checks `x$(LEN x$)=" AND "`, which tests a single character against a 5-character string — this comparison will always be false, making the multi-word `AND` separator warning dead code. The intent was likely to check whether `X$`*ends* with `" AND "`, which would require `x$(LEN x$-4 TO)` or similar. - The `DIM E$(15,32)` at line 1000 re-dimensions the array on every pass through the result display loop, which erases any previously fetched record content. This appears intentional as a way to clear the display buffer before the machine code at `USR 63575` refills it. - Line 5222 uses `ON ERR GO TO 5220` to catch non-numeric input to `VAL Z$`, then at line 5230 uses `ON ERR RESET` to clear the handler — a standard pattern for input validation on this platform. ## Source Code ``` 5 BORDER 0: PAPER 0: RANDOMIZE p: POKE 64035,PEEK 23670: POKE 64036,PEEK 23671: LET a=0: LET Y=0: DIM E$(15,32): CLS : RANDOMIZE USR 64268 20 PRINT AT 1,7; PAPER 3; INK 0;" PRO/FILE 2068 ": PRINT INK 6;AT 3,4;"Separate MULTI-WORD";TAB 4;"command words with the";TAB 4;"token ""AND""" 30 INK 7: PLOT 120,175: GO SUB 9830: INK 5 40 INK 3: PRINT AT 7,0;: GO SUB 9850 50 INK 6: PRINT AT 14,0;" Type ""A"" to ADD files";TAB 4;"""SAVE"" or ""LOAD"" for tape";TAB 4;"""AUTO"" for AUTOSEARCH";TAB 4;"""DEFP"" changes PRINT format" 100 INPUT "SEARCH COMMAND? ";X$ 102 IF x$="DEFP" THEN PRINT AT 14,0;: PLOT 120,70: GO SUB 6500: INK 0: GO SUB 9830: INK 7: PRINT AT 10,7;A$; INK 0;E$(1);E$(1);E$(1): GO TO 50 103 IF x$="AUTO" THEN GO TO 5200 105 INK 5: CLS : IF x$="A" THEN GO TO 5000 106 IF x$="LOAD" THEN GO TO 5500 107 IF x$="SAVE" THEN SAVE f$ LINE 1: GO TO 1 110 IF x$="" THEN GO TO 10 115 IF x$(LEN x$)=" AND " OR LEN X$>32 THEN GO TO 10 119 LET X$=X$ 120 RANDOMIZE USR 64040 150 LET b=USR 64101 1000 PRINT AT 0,0;: DIM E$(15,32): LET e$(1)="": IF USR 63575 THEN 1010 IF a=0 AND e$(1, TO 18)=d$(2 TO 19) AND b<>p THEN CLS : GO TO 150 1020 IF y AND B

PEEK 23627+256*PEEK 23628+6 AND (y$="D" OR y$="E") THEN GO TO 4000 1090 IF y$="R" THEN CLS : LET x$=x$: GO TO 119+(A*5121) 1100 IF y$="C" THEN GO TO 7205 1200 IF y$="DEFP" THEN PRINT AT 20,0,,,,: PRINT AT 16,0;: PLOT 120,50: GO SUB 6500: INK 0: GO SUB 9830: LET y$="M": GO TO 1051 1500 IF y$<>"" AND y$<>"E" AND y$<>"D" THEN LET A=0: LET x$=y$: GO TO 105 2000 GO TO 1050 4000 LET p=p-USR 63640: IF y$="D" THEN LET Y$="": GO TO 1 4010 GO TO 5002 5000 DIM e$(15,32): CLS 5002 LET m=0: LET z=23658: LET i=0: LET l=0: LET c=0 5004 INK 1: PLOT 0,48: PAPER 6: DRAW 255,0: INK 0: PRINT AT 16,0; PAPER 1; INK 7;"CURSOR CONTROLS: ARROWS move cursor one position ENTER: Next line SHIFT 1:Alternate Insert/Over SHIFT 0:Delete character SHIFT 9:More commands ";AT l,c;: LET m=0 5005 INK 6: PAPER 0: FLASH 1: PRINT AT l,c;SCREEN$ (l,c) 5007 ON ERR GO TO 5005 5010 IF INKEY$="" THEN GO TO 5010 5016 IF INKEY$=" STOP " THEN ON ERR RESET : FLASH 0: GO TO 6000 5020 LET y$=INKEY$: FLASH 0: PRINT AT l,c;SCREEN$ (l,c): IF CODE y$<16 THEN GO TO 5100+CODE y$ 5025 IF i THEN LET e$(l+1)=e$(l+1, TO c)+" "+e$(l+1,c+1 TO 31): PRINT AT l,0;e$(l+1) 5030 PRINT AT l,c;y$: LET e$(l+1,c+1)=y$: LET c=(c+1)*(c<31): LET l=l+(c=0): LET l=l*(l<15): FLASH 1: PRINT AT l,c;SCREEN$ (l,c) 5040 PRINT INK 6+i;AT l,c; FLASH 1;SCREEN$ (l,c): FOR X=1 TO 5: NEXT X 5103 GO TO 5010 5104 FOR x=l TO 13: LET e$(x+1)=e$(x+2): PRINT AT x,0;e$(x+1): NEXT x: LET e$(15)="": PRINT e$(15): GO TO 5040 5105 LET e$(l+1,c+1 TO )="": PRINT AT l,0;e$(l+1): GO TO 5040 5106 POKE Z,(PEEK z=0)*8: PRINT AT l,c; INK 7; PAPER 1;CHR$ (60+(PEEK z/4)): PAUSE 20: PRINT AT l,c;e$(l+1,c+1): GO TO 5040 5107 LET i=NOT i: GO TO 5040 5108 LET c=c-(c>0): GO TO 5040 5109 LET c=c+(c<31): GO TO 5040 5110 LET l=l+(l<14): GO TO 5040 5111 LET l=l-(l>0): GO TO 5040 5112 LET e$(l+1)=e$(l+1, TO c)+e$(l+1,c+2 TO 31)+" ": PRINT AT l,0;e$(l+1): GO TO 5108 5113 LET c=0: LET l=l+1: LET l=l-(l>14): GO TO 5040 5114 FOR x=15 TO l+2 STEP -1: LET e$(x)=e$(x-1): PRINT AT x-1,0;e$(x): NEXT x: LET e$(l+1)="": PRINT AT l,0;e$(l+1): GO TO 5040 5115 IF m=0 THEN PRINT BRIGHT 1; PAPER 1;AT 16,0;"Press STOP to close file SHIFT-2 for CAPS lock SHIFT-3 for LINE DELETE SHIFT-4 for LINE ERASE SHIFT-SYMBOL SHIFT: line insert ": LET m=-1: GO TO 5040 5116 IF m=-1 THEN PRINT AT 16,0,,: GO SUB 9850: LET m=1: GO TO 5040 5117 GO TO 5004 5200 LET a=1: PRINT AT 14,0; PAPER 0; INK 7;" PRINT OUT? (Y/N)",,,,,,,: INPUT Y$: LET Y=Y$="Y" 5220 LET Z$="0": PRINT AT 14,0;"TYPE LINE NUMBER BY WHICH FILES WILL BE ORDERED. Type ""0"" if ordering is not required": INPUT Z$: IF z$="" THEN GO TO 1 5222 ON ERR GO TO 5220: IF VAL Z$>=0 AND VAL Z$<=15 THEN GO TO 5230 5225 GO TO 5220 5230 ON ERR RESET : IF VAL z$ THEN LET S=VAL Z$: CLS : GO TO 5240 5235 LET a=0: GO TO 100 5240 RANDOMIZE USR 64731: IF s THEN POKE 64602,s 5441 LET x$="*": LET x$=x$: RANDOMIZE USR 64040: GO TO 7000 5500 PLOT 120,50: GO SUB 9830: PRINT AT 16,1;"HAS THIS FILE BEEN SAVED?(Y/N)": INPUT y$: IF y$<>"Y" THEN GO TO 1 5510 PRINT AT 16,1; PAPER 5; INK 0;"WHAT FILE NAME DO YOU WISH ";AT 17,1;"TO LOAD\::";: INPUT F$: PRINT f$: LOAD F$: GO TO 1 6000 FOR x=1 TO 15 6004 LET e$(x)=e$(x) 6005 IF USR 63489 THEN 6010 NEXT x 6020 LET p=USR 63530 6030 GO TO 1 6500 PRINT INK 7;" ENTER DESIRED FORMAT. Type ALL ";" or line numbers separated by"," ""/"". Use ""0"" to print blank"," lines ": GO SUB 9830 6510 LET C$="": LET n=0: INPUT "format?";a$: LET a$=a$+"/": IF a$(1)="/" THEN GO TO 6510 6515 IF A$="ALL/" THEN FOR X=1 TO 15: LET C$=C$+CHR$ X: NEXT X: LET D=3: GO TO 6600 6520 LET D=1: FOR x=1 TO LEN a$: IF a$(x)<"/" OR a$(x)>"9" THEN GO TO 6510 6525 IF X15 THEN GO TO 6510 6540 NEXT x 6600 RETURN 7000 IF VAL z$=0 THEN GO TO 7202 7005 LET b=USR 64523: IF b

"C" THEN CLS : GO TO 7000 7235 IF Y AND A=0 THEN CLS : GO TO 150 7240 GO TO 1050 7310 PRINT AT 0,0;: LET E$(1)="": IF USR 63575 THEN 7311 RETURN 8990 FOR X=64468 TO 64769 8991 INPUT (X;"=";);Y$ 8992 IF Y$="" THEN GO TO 8997 8993 IF LEN Y$=5 THEN LET X=VAL Y$: PRINT X;"=";PEEK X: GO TO 8991 8994 POKE X,VAL Y$ 8997 PRINT X;"=";PEEK X 8998 NEXT X: STOP 8999 REM 9830 DRAW 134,0: DRAW 0,-48: DRAW -254,0: DRAW 0,48: DRAW 120,0: RETURN 9850 PRINT "OPEN: "; INK 7;LEN d$-p;: PRINT " bytes";TAB 0;: PRINT "FILE: "; INK 7;f$;TAB 0;: PRINT "ORDER: "; INK 7;s;TAB 0;: PRINT "FORMAT:"; INK 7;a$;TAB 0;: RETURN 9996 DIM d$(28020): LET p=20: LET d$( TO 20)="*SEARCH IS COMPLETE*": LET a$="ALL": LET c$="": FOR x=1 TO 15: LET c$=c$+CHR$ x: NEXT x: LET s=0 9997 PRINT AT 19,2; INK 7;"Press""C"" to CREATE a new file or ""L"" to LOAD an existing one": INPUT y$: PRINT AT 19,0;D$(100 TO 164): IF y$="L" THEN GO TO 5510 9998 PRINT AT 19,4; INK 7;"ENTER A NAME FOR THIS FILE": INPUT F$: IF f$="" OR LEN f$>10 THEN GO TO 9998 9999 GO TO 1 ```