--- title: "Bas2Txt" id: 70763 type: "computer_media" slug: "bas2txt" url: "http://localhost/computer_media/bas2txt/" markdown_url: "http://localhost/computer_media/bas2txt.md" published_at: "2026-08-23T15:08:16+00:00" modified_at: "2026-08-23T16:55:31+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/08/bas2text.png" alt: "Bas2Txt screen" excerpt: "A clever hex-string loader pokes Z80 machine code into high RAM to silently convert any BASIC program into a saveable ASCII text file." 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: "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/" genre: - name: "Utility" slug: "utility" taxonomy: "genre" url: "http://localhost/type/utility/" media_type: "Program" download_url: "https://archive.org/download/timex-sinclair-software-archive/Bas2Txt%20%28198x%29%28-%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2026/08/bas2text.png" alt: "Bas2Txt screen" media_type_tags: "Utility" --- # Bas2Txt Bas2Txt is a machine code utility that converts a BASIC program stored in memory into a plain ASCII text file. The BASIC loader (lines 10–170) encodes a Z80 machine code routine as a long hexadecimal string, which is decoded byte-by-byte via POKE into RAM starting at address 64900. Two entry points exist: USR 64909 relocates the BASIC area, and USR 65042 performs the actual translation, writing the program out as a SAVE-compatible text file. The hex-decode loop at line 9030 converts each pair of ASCII hex digits into a byte using a compact expression that handles both digit (0–9) and uppercase letter (A–F) characters without branching on IF. Comments in the REM statements (lines 9050–9054) document the multi-step workflow: load the code, run it, then load the resulting ASCII file back. *** ### Program Structure The program is organized into three logical sections: 1. **Lines 10–170:** Build a long hex-encoded machine code string in `A$` through concatenation across multiple lines. 2. **Lines 1010–9040:** Decode the hex string and POKE each byte into RAM starting at address 64900. 3. **Lines 9050–9054:** REM statements documenting the two-phase usage workflow. ### Hex String Construction The machine code is split across lines 10–170 as uppercase hex pairs stored in `A$`. Each line appends additional hex data using `LET A$=A$+"..."`. This approach sidesteps any single-line length limit and allows the full binary payload to be embedded in BASIC source. The total encoded data spans roughly 175+ bytes of Z80 code. ### Hex Decode Loop (Line 9030) The decode loop at lines 1020–9040 iterates over `A$` two characters at a time. The core POKE expression is: `(CODE A$(I)-(48 AND CODE A$(I)<58)-(55 AND CODE A$(I)>64))*16 + CODE A$(I+1)-(48 AND CODE A$(I+1)<58)-(55 AND CODE A$(I+1)>64)` This converts a hex digit character to its numeric value branchlessly: - For digits `'0'–'9'` (ASCII 48–57): subtracts 48, yielding 0–9. - For letters `'A'–'F'` (ASCII 65–70): subtracts 55, yielding 10–15. - The high nibble is multiplied by 16 before adding the low nibble. The `AND` keyword is used here as a conditional mask (its Sinclair BASIC boolean behavior returning 0 or the left operand), not as a bitwise operator — a characteristic idiom for branchless conditional arithmetic. ### Memory Layout and Entry Points | Address | Purpose | | --- | --- | | 64900 | Start of machine code block (CLEAR target) | | 64909 | First entry point: relocates the BASIC area in memory | | 65042 | Second entry point: translates BASIC to ASCII text | The `CLEAR 64899` at line 1 sets the top of BASIC RAM just below the code block, protecting the poked bytes from being overwritten by BASIC’s variable storage. ### Machine Code Functionality The embedded Z80 routine implements the actual BASIC-to-text conversion. Readable ASCII strings are embedded directly in the hex data; decoding the payload reveals messages such as `"BASIC has been moved."`, `"PRESS ANY KEY TO CLEAR MEMORY."`, and the save prompt `'To save BASIC as text file: SAVE "name" CODE 28416,'`. The routine uses ROM calls (indicated by addresses such as `CD B6 FF`, `CD C9 FF`, etc.) to perform output and interact with the BASIC system. ### Workflow and Usage The REM statements (lines 9050–9054) document a multi-step process: 1. Run the BASIC loader to poke the machine code into RAM. 2. Execute `PRINT USR 64909` to relocate BASIC. 3. Load the target BASIC program into the now-relocated area. 4. Execute `PRINT USR 65042` to translate and save the program as a text file. 5. The saved file can be loaded back and will be in ASCII format. ### Notable Techniques - The branchless hex-digit decode in line 9030 avoids `IF` statements, keeping the loop tight and efficient. - Splitting the hex string across multiple `LET A$=A$+"..."` lines works around line-length constraints while keeping the loader self-contained. - The `CLEAR` statement in line 1 is integral to the program’s function, not merely a safety measure — it reserves high RAM for the machine code before any variables are allocated. - Line numbering is non-uniform (10–170 for data, 1010 and 9030–9040 for the decode loop), suggesting the program was edited over time with new sections inserted. - The note in line 9054 warns that line 1’s `CLEAR` address may need to be adjusted to `768` depending on the system configuration, indicating the tool was designed with some portability in mind. ## Source Code ``` 1 CLEAR 64899 10 LET a$="000000000000000000ED4B535C2A4B5CED422323E5C1ED4389FDED5BB25CED5387FD2A4B5CEDB813ED5385FD11D8FD013A00" 20 LET A$=A$+"CD3C20CD8E027BFEFF20F87AFEFF20F3CD8E027BFEFF28F811006FED53B25CCDB711160000424153494320686173206265656E206D6F7665642E20" 30 LET A$=A$+"160200505245535320414E59204B455920544F20434C454152204D454D4F52592E" 40 LET A$=A$+"2AB25C228BFD2184FD36002A85FD56235E232323E5E5C12A87FDAFED42D2AEFEE11176FE013700" 50 LET A$=A$+"CD3C202A8BFD11006FED522B111027CD69FE11E803CD69FE116400CD69FE110A00CD69FE110100CD69FE3EFD" 60 LET A$=A$+"CD0116C9AF3CED52380218F919C62FD7C9160000546F207361766520424153494320617320746578742066696C653A0D0D5341564520226E616D652220434F444520" 100 LET A$=A$+"32383431362C00EB1E2001E803CDB6FF016400CDB6FF010A00CDB6FF7DFE202802C630CDC9FFE17E23E5" 110 LET A$=A$+"FE22200AF53A84FDEE013284FDF1FE0D2817FE0E2009E1010500ED4AE518DDFE203002" 120 LET A$=A$+"18D7FE7B302AF53A84FDCB8F3284FDF1FE0DCC19FFFE3ACC19FFCDC9FFFE0D20B8E1C320FEF53A84FDCB973284FDF1C9" 130 LET A$=A$+"FE80301DFE7C280FFE7E280B2184FDCB4620C3CB5620BFD61FCD77FFCD6EFF1888" 140 LET A$=A$+"FE9030043E2018ADFEA53004D64F18A5FEEA200AF53A84FDCBD73284FDF1D6A5CD77FFCD6EFF" 150 LET A$=A$+"C3CDFE3A84FDCBCF3284FDC9119500F5CD410C380C3A84FDCB4F20053E20CDA5FF1AE67FCDA5FF" 160 LET A$=A$+"1A138730F5D1FE482803FE82D87AFE03D83E20D5D9CDC9FFD9D1C97BFEFF200E3E00180AAFED423C30FB093D28EDFE202805" 170 LET A$=A$+"C63011FF00E52A8BFD7723228BFDE1C9" 1010 LET ADDRESS=64900: LET C=0 1020 FOR I=1 TO LEN A$-1 STEP 2 9030 POKE (ADDRESS+C),(CODE A$(I)-(48 AND CODE A$(I)<58)-(55 AND CODE A$(I)>64))*16+CODE A$(I+1)-(48 AND CODE A$(I+1)<58)-(55 AND CODE A$(I+1)>64) 9035 LET C=C+1 9040 NEXT I 9050 REM CLEAR 64900-LOAD"BAS2TXTBYT",64900 9051 REM PRINT USR 64909 MOVES BASIC--THEN PRINT USR 65042 TO TRANSLATE 9052 REM SAVE program that was loaded after code was loaded and run as directed 9053 REM LOAD back in program area it will be in ASCII 9054 REM Please check line #1 it may be 768 also last few lines for corrections. 9999 SAVE "BAS2TXT" LINE 1 ```