10 REM"_(C2SLFF4

ingve2 pts0 comments

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...

code line byte address machine c2slff4

Related Articles