--- title: "Machine Code Loader/Editor" id: 56935 type: "computer_media" slug: "machine-code-loader-editor" url: "http://localhost/computer_media/machine-code-loader-editor/" markdown_url: "http://localhost/computer_media/machine-code-loader-editor.md" published_at: "2024-10-02T00:47:19+00:00" modified_at: "2026-04-03T07:58:47+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/09/3MachCd.png" excerpt: "A complete ZX81 machine code toolkit offering hex entry, memory listing, block move/copy, RAMTOP adjustment, byte search, and RAND USR execution from a single menu-driven program." 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 1000" slug: "ts1000" taxonomy: "post_tag" url: "http://localhost/tag/ts1000/" model: - name: "Timex/Sinclair 1000" slug: "ts-1000" taxonomy: "model" url: "http://localhost/model/ts-1000/" genre: - name: "Programming" slug: "programming" taxonomy: "genre" url: "http://localhost/type/programming/" - name: "Utility" slug: "utility" taxonomy: "genre" url: "http://localhost/type/utility/" media_contents: - id: 56732 title: "Timex Sinclair Public Domain Library Tape 1001" type: "computer_media" url: "http://localhost/computer_media/timex-sinclair-public-domain-library-tape-1001/" media_type: "Program" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/09/3MachCd.png" media_type_tags: "Programming, Utility" --- # Machine Code Loader/Editor 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 `%X` escape 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 final `CLS` before `RAND USR` at line 560. - The auto-run stub at lines 9000–9010 uses `SAVE "1000\3"` (where `\3` is an inverse character acting as the auto-run flag byte) followed by `RUN`, 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 END` rather than `FOR 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 `END` at line 816 shadows the BASIC keyword `END` (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, since `Q$` and `Q` are 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. ## 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 ```