REMIZE is a utility that embeds a binary tape file (such as a SCREEN$ or text editor output) inside a BASIC program as a line 9999 REM statement, allowing the binary data to be transmitted over modem as if it were ordinary BASIC source code. The program operates in two modes selected by the user: “L” loads a bytes-format cassette file and appends its data after a REM token in a newly created line 9999, while “S” saves the contents of an existing line 9999 REM back to tape as a binary file. The machine code, stored in the REM statement at line 9999, uses XROM calls for tape I/O, calls ROM routines to insert space into the BASIC program area, and constructs the line 9999 header (line number, size word, REM token, carriage return) manually in memory. A notable feature is that multiple line 9999 entries can coexist in the program, with the WRITE routine (USR 26860) processing only the first one found; the user can delete each after saving by typing 9999. The code also demonstrates techniques such as using HALT in a loop to count 60 interrupts for a one-second delay, calling the ROM’s error-reporting RST 08 mechanism, and using LDIR to copy a default tape header name.
Program Structure
REMIZE consists of a short BASIC loader (lines 1–40) and a large REM statement at line 9999 that serves as both documentation and the container for machine code. The BASIC portion handles user interaction and mode selection, while all substantive work is performed by machine code invoked via USR calls.
| Line(s) | Purpose |
|---|---|
| 1 | REM containing embedded machine code (disguised in REM token stream) |
| 5 | CLS and program title/copyright display |
| 10 | Prompt user for L (Load) or S (Save) mode; INPUT and validation |
| 20 | Save port 244 state, select Load mode: call USR 26750 (hex 687E), print result |
| 30 | Select Save mode: RANDOMIZE USR 26860 (hex 68EC) |
| 40 | Restore port 244, STOP |
| 9999 | REM: extensive documentation followed by full assembly listing of embedded machine code |
Machine Code Layout
The machine code resides beginning at address 26750 (hex 685B). The assembly listing embedded in the line 9999 REM documents every byte. The code is organized into well-defined subroutines:
CALXat685B: Enables XROM, calls a routine pointed to by HL, saves horizontal select port state.RELXat686F: Restores home ROM, re-enables interrupts — the complement of CALX.LOAHat687E: Entry point for Load mode (USR 26750); reads a 17-byte tape header via XROM R_TAPE, then inserts a new line 9999 into the BASIC program area and loads the tape data bytes into it.SAVTat68EC: Entry point for Save mode (USR 26860); locates line 9999, extracts size and address of data following the REM token, writes a header then data block to tape via XROM W_TAPE.HEDRat68DB: 17-byte tape header buffer (type byte + 10-byte name + size + address + end flag).ELSNat6951: Default 11-byte header name “E.L.SCHOEN”.
XROM Interface
All tape operations go through the XROM rather than directly calling the home ROM tape routines. CALX enables the XROM by setting bit 7 of port FF and setting port F4 to 1, preserving the prior state of F4 in the byte labeled SVF4. RELX reverses this, restoring the saved F4 value and clearing bit 7 of port FF, then issues EI and RET. The XROM R_TAPE entry is at address 00FC and W_TAPE at 0068. The carry flag convention mirrors the home ROM: SCF before the call means Load (not Verify); the return carry flag signals success.
BASIC Program Insertion Technique
The Load routine manually constructs a new BASIC line 9999 in memory. It calls ROM routine at address 12BB to make room in the BASIC program area at the VARS pointer (vars = 5C4B). It then writes the line header fields directly:
- Two bytes for line number 9999 (hex
270F):27h,0Fh. - Two bytes for line length (file size minus overhead).
- One byte
EAh= REM token. - Data bytes loaded from tape fill the body.
- Final byte
0Dh(carriage return) terminates the line.
The size field accounts for the overhead by performing four DEC BC operations before writing the length word, and two more before loading data to subtract the REM token and carriage-return bytes.
Save (Write) Routine Details
The Save entry point calls ROM routine 16D6 with HL = 270Fh (9999) to locate line 9999. If not found, RST 08 with error code 07 is executed to invoke the ROM’s standard error-reporting path (“End of file”). When found, the routine reads the line’s size word and advances past the REM token to obtain the data start address and byte count, stores them in SIZE (68E6) and ADRS (68E8), copies the default header name via LDIR, opens channel K (FDh) via ROM call 1230, prints a message using ROM call 073F, and calls ROM 11CF to wait for a keypress. Then it writes the header block, waits one second using a HALT loop, and writes the data block.
One-Second Delay via HALT
Between writing the tape header and the data block, the code executes a loop:
LD B, 3Ch— load 60 into B.HALT— suspend until the next interrupt (approximately every 17 ms).DJNZback to HALT — repeat 60 times ≈ 1020 ms ≈ 1 second.
This is a clean, CPU-idle delay that does not busy-wait, relying on the regular interrupt cadence.
BASIC Layer Techniques
Line 20 reads and saves the state of port 244 (IN 244), immediately outputs 0 to it, then calls the appropriate machine code routine. Line 40 restores the port state with OUT 244,c. The variable i$ is checked case-insensitively by testing both "L"/"l" and "S"/"s". Note that line 20 references I$ (uppercase) while line 10 assigns to i$ (lowercase); on this platform these are the same variable, so there is no bug.
Multiple Line 9999 Feature
The documentation in the REM deliberately notes that no check is made for a pre-existing line 9999 before the Load routine inserts one. This allows multiple line 9999 entries to accumulate, each carrying a different payload. The Save routine always processes the first one found; subsequent ones can be accessed by deleting the just-saved line (typing 9999 at the command prompt) and repeating RANDOMIZE USR 26860. This is described as intentional design.
Notable Anomalies
Line 1 is a REM whose content appears to be the raw machine code bytes interpreted as BASIC keyword tokens — a common technique for storing machine code in a REM at the start of a program. However, the working machine code documentation in line 9999 places the actual routines starting at address 685Bh (26715), while the BASIC calls reference USR 26750 (687Eh, LOAH) and USR 26860 (68ECh, SAVT). Line 1’s REM content as rendered includes many BASIC keywords, consistent with binary machine code bytes coinciding with keyword token values.
The assembly listing in line 9999 contains one apparent typographical error in the documentation: the DJNZ instruction is rendered as DJNZ,SA1S with a comma, which is non-standard Z80 assembly syntax but does not affect the actual machine code.
The header buffer’s last two bytes are documented as 00 80 (“end header flag=8000H”), but the byte at 68EA is 00 and 68EB is the ADD A,B opcode (80h) — this byte is shared with the start of the SAVT label’s LD HL,270F instruction, suggesting the header overlaps with the beginning of the Save routine. The 68EB: 80h byte serves dual purpose as both the header’s end-flag high byte and the first byte of the SAVT entry is actually at 68EC after the full LD HL,270F sequence, so the two-byte overlap is intentional layout optimization.
Source Code
1 REM NEXT PRINT FLASH COPY THEN COPY OPEN #COPY FLASH POKE 2nh>OPEN #POKE LET DIM PRINT :nhOPEN #POKE FLASH COPY THEN IN OPEN #COPY LET CLS <>INVERSE !FLASH h!DRAW >7 STEP [h STEP oh0FOR *K\GO SUB KNEW hFOR RESTORE STR$ FOR RESTORE OR STEP SQR STR$ LLIST RESTORE +6
5 CLS :PRINT AT 21,0;"REMIZE ©1987 by Edwin L. Schoen"
10 PRINT "Enter L to Load code to create"; TAB 0;" 9999 REM"; TAB 0;,,"or",,,,"Enter S to Save contents of"; TAB 0;"line 9999 REM":INPUT i$:IF i$ <>"L" AND i$ <>"l" AND i$ <>"S" AND i$ <>"s" THEN STOP
20 LET c= IN 244:OUT 244,0:IF I$="L" OR i$="l" THEN LET a= USR 26750:PRINT a:LPRINT a:GO TO 40
30 IF I$="S" OR i$="s" THEN RANDOMIZE USR 26860
40 OUT 244,c:STOP
9999 REM ▜▜REMIZE▗▜▜ This machine code program is for the purpose of adding a text file to the body of a BASIC program so that it may be transmitted over modem to someone else just like a BASIC program, which it really is. It is accomplished by creating a REM statement as line 9999 and following the REM token with the text file as read from cassette tape in "bytes" format. The secondary purpose is to create such a tape text file when given the above mentioned REM line.▜▜ RAND USR 26750 will load the first file found on cassette tape, create a REM statement as line 9999 and append the cassette data to it. RAND USR 26860 will take such a REM statement and save the data following it onto tape for future loading into some text editors.▜▜ The intended steps to utilize this program are as follows. Firstly create a binary tape file (SCREEN$ or output of an enditor). Next load this program intothe 2068. Rewind the tape with the SCREEN$ or text file; press PLAY and then type RANDOMIZE USR 26750. Next SAVE the BASIC program you have just created on any medium. Now LOAD MTERM II or facsimile and LOAD your new BASIC program into it's buffer. The rest is really up to you to use XMODEM or other means of modem transmission. When the receiver gets your BASIC program he should put his tape recorder of RECORD and type RANDOMIZE USR 26860 to SAVE the SCREEN$ or text file to his tape. Also he should keep a copy of the actual BASIC program (which contains the code below). It is good practice to type 9999 before running the LOAD portion of my program since no check is made when LOADing to see if line 9999 already exists. This is a feature, not a bug, which CAN allow you to create several line 9999's for transmission. However, be aware that when using the WRITE (26860) portion of my program only the FIRST 9999 will have it's contents written to tape. If several exist do several RAND USR 26860's each one followed by typing 9999 to delete the line just saved. ▜▜ This program shows how to call ROM routinex in the XROM, how to print to the screen, how to create a BASIC line, how to have the ROM print an ERROR report and how to have the ROM wait for a key to be pressed before proceeding. It also shows how to wait for 60 interrupts to provide a wait of 1 second by use of the HALT instruction. Interrupts occur approximately every 17ms so 60*17=1020ms=1.02seconds. Finally, it shows how to make room (insert) space into the body of a BASIC program.▜▜ My personal motive in giving this program into the public domain is to foster more machine code programming on the TS2068 and TS1000. (Note that all application ROM calls have their counterparts in the TS1000 ROM also.) Previously I have put programs on te NiteOwl to allw scannin te eyboard and finding a variables via ROM calls. Please feel free to use the ideas presented here and the program for non commercial purposes.▜▜▜685B F3 CALX DI Enable XROM; call HL pgm▜685C F5 PUSH AF ▜685D DBFF IN A,(FF)▜685F CBFF SET 7,A Enable XROM▜6861 D2FF OUT (FF),A▜6863 DBF4 IN A,(F4)▜6865 326E68 LD (SVF4),A▜6868 3E01 LD A,01▜686A D3F4 OUT (F4),A▜686C F1 POP AF▜686D E9 JP (HL)▜686E 00 SVF4 NOP Save Horiz Sel status▜686F F5 RELX PUSH AF Restore home ROM & Ints.▜6870 3A6E68 LD A,(SVF4)▜6873 D3F4 OUT (F4),A▜6875 DBFF IN A,(FF)▜6877 CBBF RES 7,A▜6879 D3FF OUT (FF),A▜687B F1 POP AF▜687C FB EI▜687D C9 RET▜687E DD21DB68 LOAH LD IX,HEDR Entry to load tape to 9999▜6882 111100 LD DE,0011 Size of Header=17 bytes▜6885 21FC00 LD HL,00FC Adrs. of XROM R_TAPE▜6888 3E00 LD A,00 Tell XROM it's a Header▜688A 37 SCF Really LOAD not VERIFY▜688B CD5B68 CALL CALX Read the Header▜688E CD6F68 CALL RELX Release XROM▜6891 30EB JR NC,LOAH XROM error: go retry.▜6893 2A4B5C LD HL,(vars) Adrs to insert=Variables▜6896 ED4BE668 LD BC,(SIZE) Prog. size from Header▜689A 110600 LD DE,0006 line no.(2),size(2),REM(1),C/R(1)▜689D EB EX DE,HL▜689E 09 ADD HL,DE Total line size▜689F E5 PUSH HL▜68A0 C1 POP BC▜68A1 EB EX DE,HL HL= adrs of VARS▜68A2 E5 PUSH HL▜68A3 C5 PUSH BC▜68A4 CDBB12 CALL 12BB ROM call to make room▜68A7 C1 POP BC▜68A8 E1 POP HL▜68A9 E5 PUSH HL▜68AA 09 ADD HL,BC Point past new area▜68AB 2B DEC HL Point to C/R loc.▜68AC 360D LD (HL),0D End line with C/R.▜68AE E1 POP HL▜68AF 3627 LD (HL),27 Make line no. 9999▜68B1 23 INC HL▜68B2 360F LD (HL),0F▜68B4 23 INC HL▜68B5 0B DEC BC Form correct line 9999 size▜68B6 0B DEC BC▜68B7 0B DEC BC▜68B8 0B DEC BC▜68B9 71 LD (HL),C▜68BA 23 INC HL▜68BB 70 LD (HL),B▜68BC 23 INC HL▜68BD 36EA LD (HL),EA Add REM to line.▜68BF 23 INC HL▜68C0 0B DEC BC Remove REM & C/R from size▜68C1 0B DEC BC▜68C2 C5 PUSH BC▜68C3 E5 PUSH HL▜68C4 DDE1 LOAD POP IX XROM wants IX=adrs to load to.▜68C6 3EFF LD A,FF Tell XROM load DATA▜68C8 D1 POP DE XROM WANTS DE=bytes to load▜68C9 D5 PUSH DE▜68CA DDE5 PUSH IX▜68CC 37 SCF Tell XROM to Load not Verify▜68CD 21FC00 LD HL,00FC▜68D0 CD5B68 CALL CALX▜68D3 CD6F68 CALL RELX▜68D6 C1 POP BC▜68D7 E1 POP HL▜68D8 30EA JR NC,LOAD Error=go reload data▜68DA C9 RET▜68DB 03 HEDR INC BC Tape header; type=3▜68DC 00 NOP These 10 bytes=ascii name▜68DD 00 NOP▜68DE 00 NOP▜68DF 00 NOP▜68E0 00 NOP▜68E1 00 NOP▜68E2 00 NOP▜68E3 00 NOP▜68E4 00 NOP▜68E5 00 NOP▜68E6 00 SIZE NOP Byte size of file▜68E7 00 NOP▜68E8 00 ADRS NOP Address to load to▜68E9 00 NOP▜68EA 00 NOP end header flag=8000H▜68EB 80 ADD A,B▜68EC 210F27 SAVT LD HL,270F HEX FOR 9999.▜68EF CDD616 CALL 16D6 Rom call for adrs of 9999.▜68F2 2802 JR Z,SFND Found it!▜68F4 CF RST 08 Print End of file if no 9999.▜68F5 07 ERROR #8▜68F6 23 SFND INC HL Found 9999. point to size▜68F7 23 INC HL▜68F8 4E LD C,(HL) Get size of line 9999.▜68F9 23 INC HL▜68FA 46 LD B,(HL)▜68FB 23 INC HL▜68FC 23 INC HL Point past the REM▜68FD 0B DEC BC Lower size by REM & C/R.▜68FE 0B DEC BC▜68FF ED43E668 LD (SIZE),BC▜6903 22E868 LD (ADRS),HL▜6906 E5 PUSH HL▜6907 215169 LD HL,ELSN Point to default header name▜690A 11DB68 LD DE,HEDR▜690D D5 PUSH DE▜690E 010B00 LD BC,000B Type + name = 11 bytes▜6911 EDB0 LDIR Move type & name to header▜6913 3EFD LD A,FD Open channel K▜6915 CD3012 CALL 1230 Rom call to open channel▜6918 AF XOR A Message index 0 ▜6919 11893C LD DE,3C89 Mesg. tbl. adrs.▜691C CD3F07 CALL 073F Print the message▜691F 213C5C LD HL,TV-FLAGS▜6922 CBEE SET 5,(HL) Show must clear screen▜6924 CDCF11 CALL 11CF Wait for key press.▜6927 DDE1 POP IX Adrs of Header ▜6929 111100 LD DE,0011 Size of header= 17 bytes▜692C AF XOR A▜692D 216800 LD HL,0068 XROM W_TAPE address.▜6930 E5 PUSH HL▜6931 CD5B68 CALL CALX Write header to tape▜6934 CD6F68 CALL RELX▜6937 063C LD B,3C Count 60 ints.▜6939 76 SA1S HALT Allow 60 ints to occur▜693A 10FD DJNZ,SA1S▜693C ED5BE668 LD DE,(SIZE)▜6940 3EFF LD A,FF▜6942 E1 POP HL▜6943 DDE1 POP IX▜6945 CD5B68 CALL CALX Write DATA to tape.▜6948 F3 DI Insure against kbd. int.▜6949 CD6F68 CALL RELX Release XROM; enable ints.▜694C C9 RET▜6951 03 ELSN INC BC Default 11-byte header▜6952 45 DEFW "E.L.SCHOEN"▜ ▜▜ Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
