.png)
Sooner or later you will find Einstein code somewhere else: in a book, a magazine listing, a forum post, or somebody's old disk. You will paste it in, and some of it will not assemble.
Usually that does not mean the code is wrong. It means it was written for a different assembler.
Assembly language splits into two halves, and only one of them is standard.
The instructions are the processor's. LD, JP, JR, DEC, ADD,
RET and the rest are Zilog's names for what the Z80 can do. Every Z80
assembler ever written uses them the same way, because they describe the chip.
Code you find will have the same instructions doing the same things.
Everything else is invented by whoever wrote the assembler. How you write a number. What the directives for placing data are called. The rules for labels. How you define a constant. None of that is the processor's business, so every assembler made its own choices, and they disagree.
Which means translating code you find is usually mechanical rather than difficult. Keep the instructions. Adjust the scaffolding around them.
Every one of these means 255:
LD A,255
LD A,0FFh
LD A,$FF
LD A,0xFF
LD A,11111111b
LD A,%11111111
Placing data:
| Directive | What it does |
|---|---|
DEFB, or DB |
One or more bytes |
DEFW, or DW |
A 16-bit value, low byte first |
DEFS n, or DS n |
Reserve n bytes |
DEFM "text" |
Text as bytes |
Text works with single or double quotes: DEFB "hi", DEFB 'hi' and
DEFM "hi" all give the same two bytes.
Naming a constant needs a colon, which many assemblers do not want:
LIVES: EQU 3
LD A,LIVES
Comments run from a semicolon to the end of the line. Labels need a colon, are case-sensitive, and have no length limit - the full rules are in S4.
Some assemblers write hexadecimal with a hash: #FF. This one does not, and
it does not tell you so. The line assembles, reports no error, and produces
something else entirely:
LD A,1 ; assembles to 3E 01 - correct
LD A,#01 ; assembles to 4A - not a load of A at all
4A is a completely unrelated instruction, and you get the same wrong byte
whatever value you put after the hash.
Worse, a stray hash anywhere after the operand does it too:
LD A,1 #note ; assembles to 4A
LD A,1 ;#note ; assembles to 3E 01 - fine, it is inside a comment
So: if you paste in code that writes hex as #FF, change it to 0FFh. And
if a program is behaving in a way that seems impossible, check for a stray
hash. Use semicolons for comments and you are safe - a hash inside a semicolon
comment is harmless.
Most Z80 assemblers let you put two instructions on one line, separated by a colon:
LD A,255 : OUT (8),A
Ours does not, and does not say so. It emits a single byte - 4A, which
is LD C,D and has nothing to do with either instruction - in place of the
four bytes you wrote. The same wrong byte appears whatever the operands, and
the assembly reports success.
LD A,255 ; on two lines: 3E FF D3 08, correct
OUT (8),A
LD A,255 : OUT (8),A ; on one line: 4A
A backslash separator does the same thing.
This is the most likely of all these differences to bite you, because the colon form is common and reads perfectly naturally. A program using it throughout will assemble cleanly and do nothing at all - which is exactly what happens to one well-known Einstein tile-graphics listing: it produces a blank screen and no error.
Put one instruction per line. If you paste in code that uses separators, split every such line before you try to run it.
These two - #FF for hex, and the colon separator - are the only silent
differences known. Everything else on this page fails loudly, which is much
kinder.
The differences above are all about notation - how you write a number, what a directive is called. There is a nastier category: some assemblers accept things that are not Z80 instructions at all, and quietly produce working code for them.
The one you will actually meet is this:
LD HL,DE ; copy DE into HL
There is no such Z80 instruction. The processor has no 16-bit
register-to-register load - not LD HL,DE, not LD DE,HL, not any
combination. But some assemblers accept it as a convenience and emit the two
8-bit loads for you:
LD H,D
LD L,E
Which means Einstein listings in circulation contain LD HL,DE, and on the
assembler they were written for it genuinely worked. It is not a typo and
whoever wrote it was not confused. Our assembler simply does not offer the
shorthand.
When you meet it, you have two replacements:
EX DE,HL ; one byte - but it swaps rather than copies
LD H,D ; two bytes - a true copy, DE left alone
LD L,E
EX DE,HL is usually what you want, and is cheaper than the shorthand it
replaces.
This category is worse than the notation differences because the code looks
right. #FF at least looks unusual enough to check. LD HL,DE looks like
ordinary assembly, sits in a listing that clearly once worked, and fails with
an error blaming something else entirely:
error: unable to resolve reference: DE
The assembler has read DE as the name of a label you never defined. If you
ever see unable to resolve reference naming a register, this is what has
happened.
Work down it before assuming the code is broken:
#FF. Change to 0FFh. Remember this one fails
silently, so check it even when the code assembles.DB, DW and DS all work here, as
do the DEF- forms. Something like .byte or BYTE does not.EQU with no colon. Add one.LD HL,DE. Not a real
instruction; some assemblers accept it anyway. Replace with EX DE,HL if a
swap will do, or LD H,D / LD L,E for a true copy. Watch for the
misleading error - it blames the register as an undefined label.LOOP
but defines loop:, that is your error.The Einstein was never a big enough machine to settle on one toolchain. People used what they had - the assembler that came with the machine, one typed in from a magazine, a cross-assembler on something else entirely. So the code that survives is written in several dialects, and none of them is the "real" one.
That is worth knowing when you read something that does not work. The listing is not necessarily wrong, and neither are you. It was just written for a different tool.