BASICally, Its Retro Machine Language

We enjoyed [Beej’s] trip down memory lane looking at a BASIC game, The Wizard’s Castle, written for the Exidy Sorcerer. It appeared in a 1980 magazine that included the title graphic above. It reminded us how, back in those days, we did things with BASIC that you shouldn’t be able to do and it often looks, today, rather cryptic.

In particular, even if you know modern BASIC, these few lines might give you a pause:

10 REM"_(C2SLFF4
40 POKE 260,218: POKE 261,1: T = USR(0): T = PEEK(-2049)
80 Q = RND(-(2*T+1))

Line 10 is a comment, but a strange one. Certainly that doesn’t matter, right? Actually, it is a key part of the action. On line 40, you can see some pokes to write directly to memory and a peek to read some memory value back. The USR function calls some machine language program. You may realize the whole thing is to get some value T to seed the random number generator in line 80.

This leads to a few obvious questions. First, how does USR know what to call? Second, where is the machine language program? The details varied by system, of course, but in this case, the program knows that location 259 has a jump instruction that USR called. So poking an address into 260 and 261 was telling USR where it should go.

But what’s at that address? Keep in mind that an old computer like the Sorcerer didn’t have megabytes of memory being swapped about by an operating system. That means that things tended to be in known places and that BASIC had to be judicious about storing source code.

As was common at the time, a line like “10 PRINT 1+1” would get tokenized. In this case, each line would get a pointer to the next line, a two-byte line number, a single-byte token for “PRINT” and then more bytes to represent the rest of the line. In the case of text in a string or a remark, the bytes were just the text with a zero to terminate the string.

The first line entered would always be at address 469. So? If you consider the format of the REM statement, there will be a pointer at 469 and 470, the line number at 471 and 472, and the REM token at 473. That means the other bytes just get poured into address 474 and beyond.

That might seem like an odd number until you look at the pokes in line 40. Keep in mind that POKE works on bytes, not words. So poking 1 into 261 gives you an address of 256 + whatever is in the low byte, in this case 218. Add 256 and 218, and you get… 474! So USR is going to call that odd string in line 10!

There is more to the detective story, but if you want to know exactly what the REM did, you can read the original post.

29 thoughts on “BASICally, Its Retro Machine Language

  1. Remember POKEing arrays of numbers in sequence to load instructions and data into a location before telling it to run what was there? Pecking in those comma separated values so carefully was part of the game.

  2. back when i needed Photoshop-like effects for a character editor I was writing in BASIC the VIC-20, I resorted to some brief machine-language routines that i got into memory using such tricks. i’m still astounded my 16 year old self pulled this off just by reading technical manuals and experimenting. there was no google or vibe coding back then

    1. There isn’t Google now either. They ruined their search engine so badly ON PURPOSE (go look up the grand jury evidence for emails demanding the search be made worse so more ads could be served). And all the slop filling up the Internet has made whatever you can pull up in a search untrustworthy anyway.

      And ‘vibe coding ‘?
      No such thing.
      The LLM is just returning snippets of other people’s code it was trained on.
      There is no coding being done.
      Just non-attributed theft.

        1. Now Google wants to tell you what you can program and what you can install and run on your computer and what you allowed to do with your OS

          Are they losing money and points on the stock market yet?

        2. Just like YouTube recommendations are either just your subscriptions, or random slop

          Searches have no relavance, and try looking for that old video from over a decade ago you can’t find because you don’t know the search letter for letter

          If they ain’t deleted it

          1. i used to use google to search my website because it worked better than anything i could possibly implement. now it doesn’t work at all, because it seems either the web got too big for it to keep track or it only indexes pages that pay them enshixxifcation tribute

  3. You’d have to be pretty fortuitous for all the bytes of your machine language program to be enterable as non-control ASCII characters. For some machines, that limits you to 95 or fewer codes.

    1. And, having read the article, I understand the original program author wasn’t fortuitous, but rather he forced the issue (by poking in the bytes) and therefore made the program non-reproducible from a print out.

  4. When I was a kid, if you want to play a video game you had to either write it yourself or type it in from a magazine. Often this involved hours typing poke (some peek) commands. Then spend days trying to fix typos, and errors in the original code to get the game to work. Fun times!

    I’m glad the BBC Micro had a built in assembler, that made the write-it-yourself path a lot easier.

  5. Every ZX81 programmer would recognise the technique, because it was the standard way of adding machine code to a BASIC program. Except on a ZX81 you can call a USeR routine (hence USR) at any address, but 16514 was normal as it was the first byte after a REM statement at the beginning of a program (and didn’t move). You would start by running a machine code entry program:

    10 REM ….:….1….:….2….:….3..ETC
    20 LET A=VAL “16514”
    30 LET C=NOT PI
    40 INPUT V
    50 POKE A,V
    60 LET C=C+V
    70 LET A=A+SGN PI
    80 GOTO CODE “C”

    Here there’s a few more ZX81 tricks where I’ve saved memory by using VAL, NOT PI (=0), SGN PI (=1) and CODE “C” (=40). You would then enter the machine code and when it was done, type STOP instead of the next number, then PRINT C to check the checksum was correct.

    Here’s a demo:

    1, 0, 0, [ LD BC,0 ]
    42, 12, 64, [LD HL,(16396) , beginning of display ]
    35, [ INC HL, first printable char]
    LP:
    230, 63, [ AND 63 [ limit to 0..63]]
    119, [ LD (HL),A [ store A in it ]]
    11, [ DEC BC ]
    120, [ LD A,B ]
    177, [ OR C ]
    32, 248, [ JR NZ,LP ]
    201, [ RET]
    STOP [ Shift+A]
    PRINT C should say: 1355.

    Then you’d delete lines 20 to 80 and add:

    20 RAND USR 16514

    Then RUN. It displays characters 63..0 repeatedly at the top left hand corner until 65536 characters have been displayed, but it only takes a couple of seconds.

    1. Yep. There are even tools for this, notably hex2rem en rem2bin, which can be found here: https://github.com/ryangray/zx81-utils

      I assume it also works with the ZX Spectrum.

      The only issue is that it’s hard to type in by hand. Still, magazines did post such programs, which you could type in. Nowadays, we just use a tool that will take a compiled binary, and create a .tap file which you can load in an emulator or stream to your real ZX Spectrum.

      https://boarstone.mcphail.uk/mcphail/spectrum_remload

      https://github.com/semack/zx_tape_player

      1. REM statements are at a much higher address on a ZX Spectrum and machine code doesn’t need to be loaded there anyway, because you can save and load bytes directly into memory ( CLEAR n:LOAD “mcode” CODE n).

        ZX81 REM statements can contain any byte value apart from code 118 (but 64..127 are all problematic). You may know this, but a ZX81 mostly uses the CPU to display the screen. It gets an interrupt at the beginning of the scan line and then the OS jumps to the address of screen memory for that scan (which is always below 32768) + 32768. So, it tries to execute the display.

        The display logic recognises instruction fetches from an address with bit 15 set; allows RAM to read the byte at that location, but then pulls D0..D7 low so the Z80 sees a 0x00, which is a NOP instruction. The logic then combines part of the Z80’s R register and I register (which is output on the refresh cycles) and the character it’s just read to read a byte from ROM which is the character’s scan byte for the current row, which goes into a shift register and sent to the screen.

        This then repeats for the whole line until a character with bit 6 set is read, which will be a byte containing 118. The Display logic allows the CPU to execute that instruction normally – which is a HALT instruction, which pauses the Z80 until the next scan and also puts 0x00 in the shift reg.

        But the upshot is that a 118 byte (or anything from 64 to 127) can mess up the screen, so ZX81 machine code programmers would either avoid those codes (lots of useful register transfers) or tell users only to list a program after the rem statement(s).

  6. If anyone’s interested in sharing and preserving old BASIC programs, I’m working on a website where you can upload these old gems. The idea is to preserve old programs and games along with their history, and even be able to run them directly in the browser. Much love to you all.

    https://screen13.es

  7. The Dancing Demon program for the Radio Shack TRS-80 Model 1 made extensive use of machine code embedded in a BASIC program.

    You loaded it like any other program. If you listed out the program, it was long stretches of gibberish interspersed with standard BASIC commands.

    The “gibberish” was embedded machine code.

    A single line in Model 1 basic was the line number followed by up to 255 bytes. The folks who put the Dancing Demon together generated the machine code, then broke it into sections just shorter than 255 bytes, using JMP commands at the end of each block to skip over the line numbers in memory. The “gibberish” consisted mostly of BASIC keywords. Model 1 BASIC used single byte codes for each keyword. For example, when you entered “PRINT,” it was converted to a single byte code in the program line. This made storage im memory more efficient. When you listed the program, the interpreter expanded the command byte codes to the keyword.

    I once modified a copy of the Dancing Demon to run the same routine for hours. It had a repeat function, but it had only a limited number of repeats. I needed it to run continuously for longer than the maximum number of repeats.

    If I remember correctly (this was back in about 1985, so it’s been a while,) the repeat was in BASIC and the parts in BASIC were all after the machine code blocks so it was easy enough to modify without breaking the machine code.

    https://www.mobygames.com/game/46290/dancing-demon/

    https://www.youtube.com/watch?v=YNT7nVOugTM

    Part of doing “fast” moving graphics in BASIC on the model 1 made use of a similar trick.

    The model 1 used characters to do graphics. Besides the letters, the graphics chip had a set of characters that consisted of 6 white blocks, 2 “pixels” wide and 3 “pixels” high. You could use “SET” and “RESET” to turn any pixel on or off over the entire screen. This worked by calculating the character position on the screen, then changing which character was displayed. It was horribly slow.

    It was faster to PRINT the correct codes for the shape directly.

    PRINT AT 200, CHR$(191)

    Would put a character with all 6 “pixels” on in character position 200 on the screen.

    The intepreter still had to interpret the CHR$ to do it.

    For more speed, you would do this:

    10 A$ = “B”
    20 K = VARPTR(A$)
    30 A = PEEK(K+1) + PEEK(K+2)*256
    40 POKE A, 191

    That put the character 191 directly into a string variable. Once your executed that code, line 10 would change. It would say A$ = “Somegibberish.” You could then delete lines 20-40 and save the program. Line 10 would keep the change.

    Now you could do this:

    PRINT AT 200, A$

    That would put your graphics block on the screen with the best speed BASIC could manage. You could put multiple characters in a single string to draw larger objects.

    Crazy stuff we did way back when.

    The code above may not be 100% correct. It has been over 40 years, and I scratched that together from memory and a quick look through an old manual

  8. One interesting point he misses is why the original author padded with the ‘F4’ token. This maps to 0xC9, which is RET in Z80. So he’s just padding with something that’ll get his function back rather than wander off into the ether.

  9. “You are not expected to understand this”

    :-)

    As I am currently playing with simh and trying to hook up a [simulated] PDP-8 running TSS/8 to a [virtual] 300 baud modem on my Asterisk machine, I’m getting quite a kick out of this.

  10. I seem to remember TRS-BASIC for the Z80-based TRS-80 Model III (the table crusher with 8″ floppy disks) having an actual ‘this is inline Z80 machine code’ command as well as PEEK/POKE/etc. Been too long.

    1. It’s quite likely somebody went wild with the “Report comment” button. That’d put everything in the review queue. As soon as some poor soul gets a chance to look it over, it’ll all be back like it was never gone. It seems to have happened to several recent articles, so it may take a while for it all to be reviewed.

      In keeping with the theme of this article, I hope someone saved the comments on a cassette.

Leave a Reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.