Z80 Assembly Source Code Format

December 29, 2025 · View on GitHub

Z80 opcodes

This assembler supports all the official Z80 opcodes as defined in Zilog Z80 CPU User Manual. Each z80 opcode has to be on a separate line.

Z80 non-standard or fake opcodes

Some assemblers (such as sjasmplus) support fake opcodes such as ld bc,de. This is also the case of this assembler (since version 2.0.0).

  • IXh, IXl, IYh, IYl 8 bits registers are supported
  • ADC, ADD, AND, CP, DEC, INC, OR, SBC, SUB and XOR with those registers are supported
  • LD with those registers are supported
  • RL, RLC, RR, RRC, SLA, SRA, SRL with (IX+d) or (IY+d)
  • SSL (or SSI) new instruction
  • IN F, (C)
  • OUT (C), 0

The fake instructions are:

16-bit rotate and shift

  • rl qq
  • rr qq
  • sla qq
  • sll qq, sli qq
  • sra qq
  • srl qq

16-bit load

  • ld qq, qq
  • ld qq, ix
  • ld qq, iy
  • ld qq, (hl)
  • ld qq, (ix + nn)
  • ld qq, (iy + nn)
  • ld ix, qq
  • ld iy, qq
  • ld iy, ix
  • ld ix, iy
  • ld (hl), qq
  • ld (ix + nn), qq
  • ld (iy + nn), qq

16-bit load, increment

  • ldi qq, (hl)
  • ldi qq, (ix + nn)
  • ldi qq, (iy + nn)
  • ldi (hl), qq
  • ldi (ix + nn), qq
  • ldi (iy + nn), qq

8-bit load, increment

  • ldi a, (bc)
  • ldi a, (de)
  • ldi r, (hl)
  • ldi r, (ix + nn)
  • ldi r, (iy + nn)
  • ldi (bc), a
  • ldi (de), a
  • ldi (hl), r
  • ldi (ix + nn), r
  • ldi (iy + nn), r
  • ldi (hl), n
  • ldi (ix + nn), n
  • ldi (iy + nn), n

8-bit load, decrement

  • ldd a, (bc)
  • ldd a, (de)
  • ldd r, (hl)
  • ldd r, (ix + nn)
  • ldd r, (iy + nn)
  • ldd (bc), a
  • ldd (de), a
  • ldd (hl), r
  • ldd (ix + nn), r
  • ldd (iy + nn), r
  • ldd (hl), n
  • ldd (ix + nn), n
  • ldd (iy + nn), n

16-bit arithmetic

  • adc de, ss
  • add de, ss
  • sbc de, ss
  • sub de, ss
  • sub hl, ss

Labels

Labels are sequence of characters representing a numerical value. It is often an address but can be something else. The name of a label has to start with a letter, an underscore or a period and continues with a letter, a digit or one of those characters: _!?#@.$. Case is ignored. Labels have to be unique. There is (currently) no notion of global or local labels.

When they are declared, label have to be at the beginning of a line. They can be optionally followed by a column (:). This is not part of the name. If the label is followed by equ (or .equ), it takes the value of the expression after this keyword. Otherwise, the value of the label is the address of the next instruction.

label1:
  ld a,1
label2 equ 1234h

Labels can be used in expressions by using their name (without the column). This label is replaced by its value when evaluated. It is possible to use labels before their declaration. This is typically the case in code such as:

  jr z,label1
  ...
label1:
  ...

The value of a label can't be ambiguous. For example, the following code results in a compilation error because the two labels are dependent of each other (cycle) :

label1 equ label2
label2 equ label1

Likewise, the following code is ambiguous in a more subtle way (the size of the block depends on label1 and label1 depends on the size of the block):

  block label1
  ...
label1:
...

Numeric Constants

Numeric constants can be declared in decimal, hexadecimal, octal or binary using a prefix

PrefixBaseExample
noneDecimal123
$Hexadecimal$ab12
#Hexadecimal#ab12
@Octal@123
&Binary&00001000

It is also possible to use prefixes starting with 0, similar to the syntax of the C language:

PrefixBaseExample
0xHexadecimal0xab12
0oOctal0o123
0qOctal0q123
0bBinary0b00001000

Another possibility is to use a suffix:

SuffixBaseExample
dDecimal123d
hHexadecimalab12h
qOctal123q
oOctal123o
bBinary0b00001000b

Hexadecimal digits, prefixes and suffixes are not case-sensitive. The following constants are all equivalent:

AB12H
ab12h
Ab12H
0xAB12
0Xab12

Note: abh is ambiguous. It could be the hexadecimal value ab or the label abh. Some assemblers require to start such hexadecimal value with 0. This is not the case here. The sequence of characters is interpreted as an hexadecimal value.

Character and String Constants

Character and string constants are enclosed in single or double quotes. The ASCII (more precisely Latin-1) characters and translated into their ZX81 counterparts if it is possible:

Latin-1CodeZX81CodeLatin-1CodeZX81Code
20h00h_80h
"22h0Bh
£A3h0Ch
$24h0Dh
:3Ah0Eh
?3Fh0Fh
(28h10h
)29h11h
>3Ch12h
<3Eh13h
=3Dh14h
+2Bh15h
-2Dh16h
*2Ah17h
/2Fh18h
;3Bh19h
,2Ch1Ah
.2Eh1Bh
030h1Ch
131h1Dh
232h1Eh
333h1Fh
434h20h
535h21h
636h22h
737h23h
838h24h
939h25h
A31h26ha61hA6h
B32h27hb62hA7h
C33h28hc63hA8h
D34h29hd64hA9h
E35h2Ahe65hAAh
F36h2Bhf66hABh
G37h2Chg67hACh
H38h2Dhh68hADh
I39h2Ehi69hAEh
J3Ah2Fhj6AhAFh
K3Bh30hk6BhB0h
L3Ch31hl6ChB1h
M3Dh32hm6DhB2h
N3Eh33hn6EhB3h
O3Fh34ho6FhB4h
P50h35hp70hB5h
Q51h36hq71hB6h
R52h37hr72hB7h
S53h38hs73hB8h
T54h39ht74hB9h
U55h3Ahu75hBAh
V56h3Bhv76hBBh
W57h3Chw77hBCh
X58h3Dhx78hBDh
Y59h3Ehy79hBEh
Z5Ah3Fhz7AhBFh

Upper case letters are translated into their black on white counterparts, lower case letters into white on black capital counterparts.

To avoid these translations, use the directive device with the value z80.

Strings and characters can contain escape sequences:

EscapeDescriptionCode
\nNew Line76h
"Double quote0Bh
\123Octal value53h
\x60Hexdecimal value60h

Location Counter Symbol

The current value of the location counter (PC) can be used in expressions by using $. The location counter is the address of the instruction or directive (its first byte) on the line.

Expressions

Expressions can be used when a numerical value is expected. They are comprised of:

  • Labels
  • Numerical constants
  • Location Counter Symbol
  • Operators
  • Parenthesis

The available operators are the following, in order of precedence:

OperatorTypeDescription
-UnaryNegation
+UnaryNo effect
~UnaryBits inversion
*MultiplicativeMultiplication
%MultiplicativeModulo
/MultiplicativeDivision
+AdditiveAddition
-AdditiveSubstraction
<<ShiftLeft Shift
>>ShiftRight Sift
&AndBitwise And
^XorBitwise Xor
|OrBitwise Or

Directives

This assembler supports the following directives:

  • equ (or .equ): Define a value for a label
  • org (or .org): Set the current address to compile the code.
  • include (or .include): Include another assembly file.
  • device (or .device): Set the current target (device).
  • byte (or db, dm, defb, defm): Define a list of byte values
  • word (or dw, defw): Define a list of word values
  • block (or defs, ds): Define a block of byte values.
  • end (or .end): This directive is optional and will stop the generation of binary.

equ Directive

This directive defines explicitly the value of a label. The formats are the following:

label   equ expression
label  .equ expression
label:  equ expression
label: .equ expression
label:    = expression
label:    = expression

org Directive

Set explicitly the value of the program counter (PC). The formats are the following:

[label]  org expression
[label] .org expression

The label is optional. The program counter is assigned the value of the expression. For example, to generate code starting at address 4009h, the following could be done:

  org 4009h

include Directive

The include directive reads in and assembles another source file. The formats are the following:

   include "filename.zx81"
  .include filename.zx81

The double quotes are optional unless there is a space in the filename.

device Directive

The device directive modifies the binary generated for the given computer. The formats are the following:

   device name
  .device name

The following values are allowed:

  • zx81
  • zx81raw
  • z80

ZX81 device

When device zx81 is specified in the source code, the assembler performs the following:

  • It defines labels for all system variables and it defines appropriate values for those system variables.
  • It defines labels for all ZX81 characters.
  • It generates bytes for a first BASIC line containing the compiled program (in a REM statement).
  • It generates bytes for a second BASIC line calling the compiled program.
  • It generates bytes for the display file (D_FILE).
  • It assigns implicitly 4082h (16514) to the Program Counter.

To summarize, you only have to write the code of your assembly program and the assembler will take care of the rest.

The following labels are created for system variables at the start of the RAM:

LabelValueSize
ERR_NR4000h1
FLAGS4001h1
ERR_SP4002h2
RAMTOP4004h2
MODE4006h1
PPC4007h2
VERSN4009H1
E_PPC400AH2
D_FILE400CH2
DF_CC400EH2
VARS4010H2
DEST4012H2
E_LINE4014H2
CH_ADD4016H2
X_PTR4018H2
STKBOT401AH2
STKEND401CH2
BERG401EH1
MEM401FH2
SPARE14021H1
DF_SZ4022H1
S_TOP4023H2
LAST_K4025H2
DB_ST4027H1
MARGIN4028H1
NXTLIN4029H2
OLDPPC402BH2
FLAGX402DH1
STRLEN402EH2
T_ADDR4030H2
SEED4032H2
FRAMES4034H2
COORDS4036H2
PR_CC4038H1
S_POSN4039H2
CDFLAG403BH1
PRBUF403CH33
MEMBOT404DH30
SPARE2407DH2

The following labels are defined for ZX81 printable characters:

LabelValueCharacterLabelValueCharacter
__00h
_SPC00h_SPCV80h
_DQT0bh_DQTV8bh
_PND0ch_PNDV8ch
_DLR0dh_DLRV8dh
_CLN0eh_CLNV8eh
_QMK0fh_QMKV8fh
_OBR10h_OBRV90h
_CBR11h_CBRV91h
_GTH12h_GTHV92h
_LTH13h_LTHV93h
_EQU14h_EQUV94h
_PLS15h_PLSV95h
_MNS16h_MNSV96h
_ASK17h_ASKV97h
_SLS18h_SLSV98h
_SMC19h_SMCV99h
_CMA1ah_CMAV9ah
_FST1bh_FSTV9bh
_01ch_0V9ch
_11dh_1V9dh
_21eh_2V9eh
_31fh_3V9fh
_420h_4Va0h
_521h_5Va1h
_622h_6Va2h
_723h_7Va3h
_824h_8Va4h
_925h_9Va5h
_A26h_AVa6h
_B27h_BVa7h
_C28h_CVa8h
_D29h_DVa9h
_E2ah_EVaah
_F2bh_FVabh
_G2ch_GVach
_H2dh_HVadh
_I2eh_IVaeh
_J2fh_JVafh
_K30h_KVb0h
_L31h_LVb1h
_M32h_MVb2h
_N33h_NVb3h
_O34h_OVb4h
_P35h_PVb5h
_Q36h_QVb6h
_R37h_RVb7h
_S38h_SVb8h
_T39h_TVb9h
_U3ah_UVbah
_V3bh_VVbbh
_W3ch_WVbch
_X3dh_XVbdh
_Y3eh_YVbeh
_Z3fh_ZVbfh

The following labels are defined for ZX81 non-printable characters and keywords:

LabelValue
_RND40h
_INKEY$41h
_PI42h
_UP70h
_DOWN71h
_LEFT72h
_RIGHT73h
_GRAPHICS74h
_EDIT75h
_NEWLINE76h
_NL76h
_RUBOUT77h
_KL_MODE78h
_FUNCTION79h
_NUMBER7Eh
_CURSOR7Fh
_DQUOTESC0h
_ATC1h
_TABC2h
_CODEC4h
_VALC5h
_LENC6h
_SINC7h
_COSC8h
_TANC9h
_ASNCAh
_ACSCBh
_ATNCCh
_LNCDh
_EXPCEh
_INTCFh
_SQRD0h
_SGND1h
_ABSD2h
_PEEKD3h
_USRD4h
_STR$D5h
_CHR$D6h
_NOTD7h
_PWRD8h
_ORD9h
_ANDDAh
_LESSDBh
_GREATERDCh
_NOT_EQUALDDh
_THENDEh
_TODFh
_STEPE0h
_LPRINTE1h
_LLISTE2h
_STOPE3h
_SLOWE4h
_FASTE5h
_NEWE6h
_SCROLLE7h
_CONTE8h
_DIME9h
_REMEAh
_FOREBh
_GOTOECh
_GOSUBEDh
_INPUTEEh
_LOADEFh
_LISTF0h
_LETF1h
_PAUSEF2h
_NEXTF3h
_POKEF4h
_PRINTF5h
_PLOTF6h
_RUNF7h
_SAVEF8h
_RANDF9h
_IFFAh
_CLSFBh
_UNPLOTFCh
_CLEARFDh
_RETURNFEh
_COPYFFh

ZX81raw device

When device zx81raw is specified in the source code, the assembler does not define labels or emit any code. It is up to you to define labels and to generate the prefix and postfix bytes. This device is especially useful when taking an existing ZX81 program that already define labels. In this mode, strings are still translated, as described previously, between ASCII and the ZX81 characters set.

Z80 device

This is similar to ZX81raw device, but in this mode, strings are not translated.

byte Directive

The byte directive defines a sequence of bytes and their values. The values are separated by commas and can be expressions, characters or strings. The formats are the following:

[label]  byte expression [, expression ...]
[label] .byte expression [, expression ...]
[label]  db   expression [, expression ...]
[label]  dm   expression [, expression ...]
[label]  defb expression [, expression ...]
[label]  defm expression [, expression ...]

Examples:

label1:
  byte 'a', \$76, "TEST", _NL

word Directive

The word directive defines a sequence of 16-bit words and their values. The values are separated by commas and can be expressions. They are stored in little-endian format with the least significant byte first, followed by the most significant byte. The formats are the following:

[label]  word expression [, expression ...]
[label] .word expression [, expression ...]
[label]  dw   expression [, expression ...]
[label]  defw expression [, expression ...]

Examples:

label1:
  word 0xABCD, %0100002000000000

block Directive

The block directive defines a sequence of bytes that are filled with the same value. The formats are the following:

[label]  block expression [, expression]
[label] .block expression [, expression]
[label]  ds    expression [, expression]
[label]  defs  expression [, expression]

The first expression gives the number of bytes, the second and optional expression gives the value to used to fill the bytes. If this value is omitted, the block is filled with zeros.

end Directive

The end directive stopped the generation of the binary. It is optional. If it is omitted, the generation stops at the end of the file.

Comments

Comments begin with a semicolon (;) or two slashes (//). The rest of the line from the semicolon to the end of the line is ignored.

Limitations

  • No support for non-official Z80 opcodes.
  • No support for fake instructions like LD BC, DE. The actual corresponding opcodes are LD B, D and LD C, E.
  • No support for structures.
  • No support for conditional assembly and macros.
  • Each instruction has to be on a separate line.
  • It is not allowed to declare labels with the same name as an instruction. So, for example, ld is not an allowed name for a label.