10 REM"_(C2SLFF4
-->
Beej's Bit Bucket
⚡ Tech and Programming Fun
2026-07-21
10 REM"_(C2SLFF4
Let's dive into The Wizard's<br>Castle (source code)!
10 REM"_(C2SLFF4
That is the first line of the 80s microcomputer BASIC game The Wizard's<br>Castle initially written for the Exidy<br>Sorcerer platform.
It's a REMark statement, a comment in that particular language. 10<br>is the line number, if you're unfamiliar with languages that had such<br>things.
But the interesting part was this "_(C2SLFF4 mess. A typo or garbage?<br>No. It appears verbatim in the source code as originally published in<br>the July, 1980 issue of Recreational<br>Computing.
What the heck is it?
Note: I'm going to jump back and forth between decimal (which is<br>BASIC-friendly) and hex (which is programmer-friendly). Hex will be<br>prefixed with 0x, suffixed with h, or will obviously have digits A-F<br>in it.
Note: Thanks to Josh and Chris, my hacking buddies, for doing tons of<br>the emulator and research work on this. It took hours, but they were<br>good hours. It would have taken me 10x as long without their help.
Suspicions
Here's more of the source in context, but with irrelevant pieces<br>removed and some spaces added:
10 REM"_(C2SLFF4<br>40 POKE 260,218: POKE 261,1: T = USR(0): T = PEEK(-2049)<br>80 Q = RND(-(2*T+1))
In BASIC, a : is a command separator. POKE writes a byte value to<br>the specified memory location. PEEK reads from one. Sorcerer BASIC<br>used signed 16-bit numbers, apparently, so address -2049 is 65536-2049<br>or 0xF7FF in hex. We'll revisit that address later.
Calling the pseudo-random number generator (PRNG) RND() with a<br>negative number seeds it.<br>Old PRNGs liked to be seeded with odd numbers, thus the 2*T+1<br>expression forcing it odd.
And the USR() function calls a machine code routine.
And line 80 is the first use of T after it is initialized from the<br>PEEK().
The proximity of these things makes it seem like they're all related to<br>seeding the PRNG. Sorcerer BASIC didn't have a RANDOMIZE command, so<br>the only options of the day were:
Ask the user to enter a random seed.
Run an increment loop until the user hits a key (or some user-driven<br>thing like that) and use that for the seed.
Get the seed for some kind of randomish-thing from the existing<br>software and/or hardware.
Wizard's Castle didn't do the first two. So it must be the last one.
Could it be that the REM statement has machine code that happens to<br>encode to ASCII text? The Sorcerer did use ASCII encoding.
But it seems nuts that you could get useful Z80 machine code just from<br>ASCII characters. Right?
Nevertheless, this seemed like a reasonable-enough crazy assumption to<br>run with and see what we could find.
The USR() Function
The USR() call is interesting—this calls a machine code function, but<br>the details are system-dependent. But luckily the Internet has tons of<br>technical manuals<br>in various places and we were<br>able to figure it out.
Address 259 has a JP instruction (unconditional jump) to a 16-bit<br>little-endian absolute address.
And the POKEs were for addresses 260 and 261. This was the<br>trampoline<br>for the USR() function. You POKE the address of the start of your<br>machine code into addresses 260 and 261, and then run USR() to call<br>it.
40 POKE 260,218: POKE 261,1: T = USR(0): T = PEEK(-2049)
Reversing them for little-endian, this means we're looking at address<br>(1 which is 474. When we run USR(0), it jumps to the<br>machine code at address 474 that should end in a RET instruction.
The argument for USR() (the 0) is stored as a 4-byte float<br>elsewhere in RAM so the machine code can look at it if it needs to. It<br>turns out not to be relevant for this use case. Also we're assigning<br>the return value from USR() into T, but it's unclear to me what<br>this return value is. However, it is irrelevant as we immediately<br>overwrite T with the next assignment.
So... What's at address 474?
BASIC RAM
The almighty Exidy<br>Sorcerer
By Marcin Wichary, CC BY<br>2.0
When you enter a line of Sorcerer BASIC, the interpreter<br>tokenizes<br>it and replaces commands like PRINT with single byte values:
PRINT is tokenized to 0x97
REM is tokenized to 0xC3
etc.
And then it is stored like a linked<br>list in memory where each<br>row is a "node".
The node format is:
2 bytes of "next" pointer
2 bytes for the line number
Bytes representing the tokenized line of code
1 byte of null terminator (a 0x00 byte)
And the start of this linked list in RAM is at address 469 on the<br>Sorcerer.
Let's see the mystery line again:
10 REM"_(C2SLFF4
That means the node for that line consists of the following addresses<br>and bytes:
469 Low byte of the next pointer<br>470 High byte of the next pointer<br>471 Low byte of the line number<br>472 High byte of the line number<br>473 `REM` token (0xc3)<br>474 First byte of REM text, the `"` character!!
and 474 is where the USR() function gets us! It is literally calling<br>the text of the REM statement as Z80 machine code!
Disassembly, Attempt One
Could it be true? Let's try it out!
Here are the hex values for the ASCII...