This program is a machine code loader and editor for the ZX81/TS1000, providing a 12-option menu for working with raw memory. Features include writing hex-encoded bytes to arbitrary addresses, listing memory contents as hexadecimal, editing individual bytes, moving and copying memory blocks, deleting (zeroing) ranges, running routines via RAND USR, adjusting RAMTOP by directly POKEing system variables 16388/16389, and searching memory for a specific byte value. Hex input and output are handled using a custom encoding where hex nibbles are represented as characters offset by 28 from their ASCII code values (since ZX81 character codes begin at 28 for ‘0’). The move function uses a 300-element DIM array as a buffer and supports both upward and downward displacement.
Program Analysis
Program Structure
The program is organized as a main menu dispatcher followed by numbered subroutine blocks. Lines 2–31 handle the menu display and dispatch; all functional routines begin at line 200 or higher and return to the menu via GOTO 2. The REM at line 1 is a ruler of digit characters used for column alignment during development.
| Lines | Function |
|---|---|
| 2–31 | Menu display and dispatch |
| 200–290 | Write code (hex input) |
| 300–397 | List code (hex output) |
| 400–470 | Save program to tape |
| 500–590 | Run machine code via RAND USR |
| 600–696 | Edit a single byte |
| 700–791 | Delete (zero) a memory range |
| 800–870 | Move a block of code (up or down) |
| 900–960 | Adjust RAMTOP |
| 1000–1080 | Copy code to address 16514 |
| 1100–1198 | Copy code to arbitrary address |
| 1200–1217 | Search memory for a byte value |
| 9000–9010 | Auto-save and auto-run stub |
Hex Encoding Scheme
All hex input and output uses a character-offset encoding. On the ZX81/TS1000, the character code for '0' is 28, so the digit characters '0'–'9' map to codes 28–37 and the hex letters 'A'–'F' map to codes 37–42 (since ‘A’ follows the digits in the ZX81 character set). A two-character hex pair is decoded as:
(CODE A$-28)*16 + (CODE A$(2)-28)
The same arithmetic is reversed for display: Q=INT(P/16) gives the high nibble and NIC=((P/16)-Q)*16 gives the low nibble, then CHR$(Q+28)+CHR$(NIC+28) prints the hex digits. The variable name NIC is notable as a named variable rather than a single letter, unusual for ZX81 BASIC space economy.
Write Code Routine (Lines 200–290)
The write routine accepts a multi-character string at a time and processes it two characters per iteration. The loop strips consumed characters using A$(3 TO ), allowing the user to type a run of hex pairs before pressing ENTER. Entering "S" terminates entry and returns to the menu. A blank input loops back to re-prompt, so the user can enter pairs across multiple INPUT statements, advancing the address pointer N after each byte.
Move Routine (Lines 800–870)
The move function allocates a DIM D(300) array as a staging buffer, limiting moves to 299 bytes. It reads bytes from the source range into D(), then writes them to the destination. The direction ("UP" or "DOWN") determines whether the destination is BEG+DIS or BEG-DIS, and the vacated source region is zeroed after the copy. This two-pass approach avoids overlap corruption for non-overlapping moves, but overlapping moves of more than one byte in the same direction may still corrupt data depending on offset.
RAMTOP Adjustment (Lines 900–960)
The RAMTOP system variable is stored as a 16-bit little-endian value at addresses 16388 and 16389. The routine splits the user-supplied value NE into its low byte (NE-256*INT(NE/256)) and high byte (INT(NE/256)) and POKEs them directly. This is the standard ZX81 technique for reserving space above RAMTOP for machine code that survives NEW.
“Move Code Down to Save It” (Lines 1000–1080)
Option 9 copies a user-specified address range to a fixed destination of 16514, which is the start of the ZX81 display file in a standard 1K RAM configuration. This is intended to relocate machine code to a predictable low address before saving. The label in the menu says “MOVE CODE DOWN TO SAVE IT,” but the routine is actually a copy (the source is not zeroed).
Notable Techniques
- Inverse-video text in PRINT statements (lines 201–203, 570, 1215) is rendered using the
%Xescape convention, making banners stand out without cursor positioning tricks. - The delay loop at lines 530–540 (
FOR I=1 TO 20: NEXT I) before running machine code gives a brief pause and performs a finalCLSbeforeRAND USRat line 560. - The auto-run stub at lines 9000–9010 uses
SAVE "1000"(whereis an inverse character acting as the auto-run flag byte) followed byRUN, the standard ZX81 auto-run save idiom. - The search routine (lines 1200–1217) accepts hex input in the same two-character encoding used throughout, keeping the interface consistent.
Bugs and Anomalies
- The move-down zeroing loop at lines 866–868 uses
FOR J=(END-DIS) TO ENDrather thanFOR J=(BEG-DIS) TO (BEG); this zeros the wrong region (end of destination rather than the vacated source tail), leaving old bytes in place. - The variable name
ENDat line 816 shadows the BASIC keywordEND(not used in ZX81 BASIC, but the tokeniser may handle it unpredictably on some variants). - The listing routine (lines 300–397) reassigns
Q(already used as the high-nibble integer) at line 395 to hold the user’s Y/N response string, mixing numeric and string use of the base name — however, sinceQ$andQare distinct variables in BASIC this is not an error. - Option 8 (“SAVE SPACE ABOVE RAMTOP”) adjusts RAMTOP but does not perform any actual SAVE; the user must separately invoke option 11 to save.
Content
Source Code
1 REM 111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999000000000011111111112222222222333333333344444444445555555555666666666677777777778888888888999999999900000000001111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990000000000
2 CLS
3 PRINT "T/S 1000 MACH CODE LOADER/EDITOR"
4 PRINT AT 2,0;"DO YOU WISH TO...",,,
5 PRINT " 1)...WRITE CODE?"
6 PRINT " 2)...LIST CODE?"
7 PRINT " 3)...EDIT CODE?"
8 PRINT " 4)...MOVE CODE?"
9 PRINT " 5)...COPY CODE?"
10 PRINT " 6)...DELETE CODE?"
11 PRINT " 7)...RUN CODE?"
12 PRINT " 8)...SAVE SPACE ABOVE RAMTOP?"
13 PRINT " 9)...MOVE CODE DOWN TO SAVE IT?"
14 PRINT "10)...SEARCH FOR A GIVEN BYTE?"
15 PRINT "11)...SAVE PROGRAM AND CODE?"
16 PRINT "12)...QUIT PROGRAM?",,,
17 PRINT "ENTER NBR. OF SELECTION."
18 INPUT A
19 IF A<1 OR A>12 THEN GOTO 18
20 IF A=1 THEN GOTO 200
21 IF A=2 THEN GOTO 300
22 IF A=3 THEN GOTO 600
23 IF A=4 THEN GOTO 800
24 IF A=5 THEN GOTO 1100
25 IF A=6 THEN GOTO 700
26 IF A=7 THEN GOTO 500
27 IF A=8 THEN GOTO 900
28 IF A=9 THEN GOTO 1000
29 IF A=10 THEN GOTO 1200
30 IF A=11 THEN GOTO 400
31 IF A=12 THEN STOP
200 CLS
201 PRINT ,,"%E%N%T%E%R% %A%D%D%R%E%S%S% %I%N% %D%E%C%I%M%A%L% % % % % % % % "
202 PRINT "%E%N%T%E%R% %C%O%D%E% %I%N% %H%E%X%A%D%E%C%I%M%A%L% % % % % % % "
203 PRINT "%E%N%D% %C%O%D%E% %B%Y% %E%N%T%E%R%I%N%G% %"%S%"% % % % % % % % "
210 PRINT ,,"ADDRESS TO WRITE TO:";
220 INPUT N
225 PRINT N
230 LET A$=""
240 IF A$="" THEN INPUT A$
250 IF A$="S" THEN GOTO 2
260 POKE N,((CODE A$-28)*16+(CODE A$(2)-28))
270 LET N=N+1
280 LET A$=A$(3 TO )
290 GOTO 240
300 CLS
302 PRINT "START ADDRESS: ";
310 INPUT W
320 PRINT W
330 PRINT "END ADDRESS: ";
340 INPUT Y
350 PRINT Y
360 FOR M=W TO Y
370 LET P=PEEK M
380 LET Q=INT (P/16)
390 LET NIC=((P/16)-Q)*16
391 PRINT M;" ";CHR$ (Q+28)+CHR$ (NIC+28)
392 NEXT M
393 PRINT
394 PRINT "ANY MORE LISTING? (Y/N)"
395 INPUT Q$
396 IF Q$<>"Y" THEN GOTO 2
397 GOTO 300
400 CLS
410 PRINT "SAVE AS: ";
420 INPUT S$
430 PRINT S$
440 PRINT "START TAPE AND PRESS ENTER"
450 INPUT Q$
460 SAVE S$
470 GOTO 2
500 CLS
501 PRINT "ADDRESS OF ROUTINE: ";
510 INPUT ADR
520 PRINT ADR
525 CLS
530 FOR I=1 TO 20
540 NEXT I
550 CLS
560 RAND USR ADR
570 PRINT AT 20,0;"%T%O% %R%E%T%U%R%N% %T%O% %M%E%N%U% %P%R%E%S%S% %E%N%T%E%R% % % "
580 INPUT Q$
590 GOTO 2
600 CLS
610 PRINT "ADDRESS OF BYTE: ";
620 INPUT BY
630 PRINT BY
632 PRINT "PRESENT VALUE: ";
633 LET P=PEEK BY
634 LET Q=INT (P/16)
635 LET NIC=((P/16)-Q)*16
636 PRINT CHR$ (Q+28)+CHR$ (NIC+28)
640 PRINT "CHANGE TO: ";
650 INPUT C$
660 PRINT C$
670 POKE BY,((CODE C$-28)*16+(CODE C$(2)-28))
673 FOR J=1 TO 15
674 NEXT J
680 PRINT "ANY MORE EDITING? (Y/N)"
690 INPUT Q$
695 IF Q$<>"Y" THEN GOTO 2
696 GOTO 600
700 CLS
710 PRINT "START ADDRESS: ";
720 INPUT ST
730 PRINT ST
740 PRINT "END ADDRESS: ";
750 INPUT EN
760 PRINT EN
770 FOR J=ST TO EN
780 POKE J,0
790 NEXT J
791 GOTO 2
800 DIM D(300)
801 CLS
802 LET TOT=1
805 PRINT "MOVE ""UP"" OR ""DOWN""?"
806 INPUT M$
807 PRINT M$
809 PRINT
810 PRINT "START ADDRESS: ";
811 INPUT BEG
813 PRINT BEG
814 PRINT "END ADDRESS OF CODE: ";
816 INPUT END
817 PRINT END
818 IF END-BEG>299 THEN PRINT "YOU MAY ONLY MOVE 299 BYTES."
819 IF END-BEG>299 THEN GOTO 800
820 PRINT "DISPLACEMENT: ";
821 INPUT DIS
822 PRINT DIS
823 FOR J=BEG TO END
824 LET D(TOT)=PEEK J
825 LET TOT=TOT+1
827 NEXT J
829 IF M$="DOWN" THEN GOTO 860
835 LET TOT=1
836 FOR J=(BEG+DIS) TO (END+DIS)
837 POKE J,D(TOT)
838 LET TOT=TOT+1
839 NEXT J
840 FOR J=BEG TO (BEG+DIS-1)
841 POKE J,0
850 NEXT J
851 GOTO 2
860 LET TOT=1
862 FOR J=(BEG-DIS) TO (END-DIS)
863 POKE J,D(TOT)
864 LET TOT=TOT+1
865 NEXT J
866 FOR J=(END-DIS) TO END
867 POKE J,0
868 NEXT J
870 GOTO 2
900 CLS
910 PRINT "NEW RAMTOP IS: ";
920 INPUT NE
930 PRINT NE
940 POKE 16388,NE-256*INT (NE/256)
950 POKE 16389,INT (NE/256)
960 GOTO 2
1000 CLS
1005 PRINT "THE CODE WILL BE COPIED INTO"
1006 PRINT "MEMORY STARTING AT 16514."
1010 PRINT "START ADDRESS: ";
1020 INPUT ST
1025 PRINT ST
1030 PRINT "END ADDRESS: ";
1035 INPUT EN
1040 PRINT EN
1045 LET SA=16514
1050 FOR J=ST TO EN
1055 POKE SA,PEEK J
1060 LET SA=SA+1
1070 NEXT J
1080 GOTO 2
1100 CLS
1110 PRINT "START ADDRESS OF CODE"
1120 PRINT "THAT IS TO BE COPIED: ";
1130 INPUT ST
1140 PRINT ST
1150 PRINT "END ADDRESS: ";
1160 INPUT EN
1170 PRINT EN
1180 PRINT "ADDRESS TO BE COPIED TO: ";
1190 INPUT C
1191 PRINT C
1193 LET N=0
1194 FOR J=ST TO EN
1195 POKE (N+C),PEEK J
1196 LET N=N+1
1197 NEXT J
1198 GOTO 2
1200 CLS
1202 PRINT "START ADDRESS FOR SEARCH: ";
1203 INPUT A
1204 PRINT A
1205 PRINT "END ADDRESS FOR SEARCH: ";
1206 INPUT E
1207 PRINT E
1208 PRINT "BYTE TO BE SEARCHED FOR: ";
1209 INPUT Q$
1210 PRINT Q$
1211 LET N=((CODE Q$-28)*16+(CODE Q$(2)-28))
1212 FOR J=A TO E
1213 IF PEEK J=N THEN PRINT J
1214 NEXT J
1215 PRINT "%P%R%E%S%S% %E%N%T%E%R% %T%O% %R%E%T%U%R%N% %T%O% %M%E%N%U% % % "
1216 INPUT Q$
1217 GOTO 2
9000 SAVE "1000%3"
9010 RUN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
