SjASMPlus 1.14.2 Documentation [2019-10-03]


1. Introduction
License
What is it?
Main Features
Credits
Feedback
What's new?
2. Where to get and how to use
Packages
Command line
Source file format
3. Labels
Labels
Local labels
@ Labels
Temporary labels
4. Constants, expressions and other features
Numeric constants
Character and string constants
Expressions
Assembly language
Fake instructions
Real device emulation mode
Predefined defines
5. Pseudo-ops (aka Pseudo-instructions, Directives etc)
Simple example of usage
Almost complete list
Conditional assembly
Macros
6. Structures
What is it?
Defining structure
Instructions
Usage of defined structure
Examples
7. Lua scripting
Why?
How to use?
SjASMPlus binded functions
Third-party embedded library(ies)
Example
8. SAVENEX guide
NEX File Format
Detailed description of each SAVENEX command
Examples
9. Source Level Debugging (SLD) data
What is it?
Usage
How to write "non tricky" source
SLD File Format definition (version "0")

Chapter 1. Introduction

License

SjASMPlus is licensed under BSD license.

What is it?

SjASMPlus is Z80 Assembly Language Cross Compiler. It is available for Win32, Linux and FreeBSD (mainly 5.x) systems. It is based on SjASM source code by Sjoerd Mastijn (http://xl2s.tk).

Main Features

  • Full source of assembler available under BSD license, modify and extend as you wish

  • Z80/R800/Z80N/i8080/LR35902 documented and undocumented opcodes support

  • Macro language, defines, array of defines

  • Built-in Lua scripting engine

  • Conditional assembly, block repeating

  • Modules (namespaces), local and temporary labels

  • Source and binary file inclusion, include paths

  • Multi file output, file updating, various types of exports

  • Structures to work easily with structured data in memory

  • Virtual device mode for common machines: ZX 128, ZX Next, … (pseudo op DEVICE)

  • ZX Spectrum specific directives and pseudo ops (SAVESNA, SAVETAP, SAVEHOB, INCHOB, INCTRD…)

  • ZX Spectrum Next specific features and directives (Z80N, 8ki memory paging, SAVENEX)

  • Correctness is assured by Cirrus-CI with 240+ automated tests (that's also 240+ examples of usage!)

  • Fake instructions as LD HL,DE (LD H,D:LD L,E) and more

  • Code inlining through colon (LD A,C:INC A:PUSH AF:IFDEF FX:LD A,D:ENDIF…)

  • Very fast compilation: 1 million lines by 2-3 seconds on modern computer

  • Multiline block comments and user’s messages

Credits

Special thanks to Sjoerd Mastijn, the author of SjASM.

Aprisobal - main programming, documentation, etc.

Thanks to:

  • Kurles/HS/CPU, Alexander Kovalenko, Ped7g - additional programming;

  • Krystian Wlosek <kwlosek(at)gmail.com> - bug fix patches, Linux makefile;

  • Ric Horne <Ric.Hohne@eads-ts.com> - bug fix patches.

  • breeze <breeze@tut.by> - bug fix patches.

  • psndcj <psndcj.tbk@gmail.com> - bug reporting, beta-testing.

  • elfh <elphecy@gmail.com> - bug reporting.

  • bugsy <bugsy@ya.ru> - bug reporting.

  • skrju <sq-@mail.ru> - bug reporting.

  • Tygrys, UB880D, Cizo, mborik, z00m - compilation errors and warnings clean up, makefiles, testing.

  • Antipod, boo_boo, PulkoMandy, Busy, Liniya - bug fix patches, testing.

  • CKirby - SLD export and support in his tools.

Big thanks to all people, who helped on development of the compiler!

Feedback

WWW: https://github.com/z00m128/sjasmplus (newer versions maintained by z00m and others)

WWW: https://sourceforge.net/projects/sjasmplus/ (original Aprisobal's source)

E-Mail: zoom@centrum.sk, my@aprisobal.by

What's new?

WIP - 1.14.3
- fix detection of `.end:` and `.END` labels when `--dirbol` is used
- added export of SLD (Source Level Debugging) data, see also NDS (NextDevSystem)
- docs: added small details about FPOS, SAVETAP, IFUSED
- fix assembling-time reported in linux
3.10.2019 - 1.14.2
- added i8080 mode (--i8080 CLI option) (it's still Z80 Zilog syntax, just limited instruction set)
- added Sharp LR35902 mode (--lr35902 CLI option) (100% syntax compatibility with IDA, 95% bgb)
- new $$label operator to retrieve page of label
- 1.14.0 include-path bugfix reverted, the "." is again automatically added (*did* break projects)
- small improvements/polish/extra-info in docs, INSTALL, README, few new tests added
- cmake script fix of SYSTEM_LUA=ON option, CirrusCI configs added for macOS and FreeBSD
- few fixes of memory leaks, invalid memory access, double free/delete, ...
30.8.2019 - 1.14.1
- refactored SHELLEXEC to use clib "system(..)" on all platforms (also MS VS), minor fixes
- lua example "inc_text" (result of specific request from sjasmplus user)
- listing fixed when Lua was used to emit bytes and also parsed lines of assembly source
- MinGW windows exe prefers "/" file system delimiter ("\" should still work on windows (only))
- lot of small bugfixes and Cirrus CI infrastructure adjustments (windows MinGW build does run full tests)
- MS VS builds stabilized and fixed, should now work mostly on par with MinGW builds (99.5%)
- Using lgtm.com code analysis (did help to find new bugs and memory leaks)
- UnitTest++ framework added for regular C++ unit tests, first few tests added
17.8.2019 - 1.14.0
- INCLUDE bugfix, now searching paths according to original documentation (may break some projects)
- UNDEFINE had undocumented feature of removing also labels, cancelled (was broken beyond repair)
- R800 `MULUB` was producing wrong opcode all those years... fixed
- MODULE names can't contain dot any more! MODULE and ENDMODULE resets non-local label to "_"
- --syntax option: "m" (switch off low-mem access warning) and "M" added, "A" removed
- macro expansion can be inhibited by using "@" in front of instruction
- expression evaluator was not strictly 32 bit (64b binaries could have produced different results than 32b binaries)
- reading memory addresses 0..255 directly emits warning, use "; ok" comment to suppress it.
- several tests added to improve the code coverage: coveralls.io/github/z00m128/sjasmplus
- as tests were added, minor bugs were found and squashed (errors wording, etc)

See CHANGELOG.md for full list of changes.

Chapter 2. Where to get and how to use

Packages

The latest release of this sjasmplus variant is available at https://github.com/z00m128/sjasmplus/releases/latest - there is available zip archive with windows binary (toward bottom of the page), and zip archives with full source code of the sjasmplus.

Win32 package has:

  • sjasmplus.exe - the Win32 executable.

  • examples directory - some examples of use

  • documentation directory - documentation in various formats

You may want to download also the full source package, as it contains more than 240 automated tests used to verify correctness of executable, and these can be often helpful to better understand how specific feature of sjasmplus works, and how to use it effectively.

Linux, Unix, MacOS and BSD version can be built from the full source archive. You can compile it using GCC and included Makefile. There is an option to use CMake for compilation. See INSTALL.md for details.

Windows binaries are compiled with MinGW environment and included Makefile.win file.

You can grab older (up to v1.07) binaries and sources at SourceForge project page: https://sourceforge.net/projects/sjasmplus/

Command line

Usage:

sjasmplus [options] sourcefile(s)

Option flags as follows:

  -h or --help             Help information
  --version                Basic info (with --nologo only raw version string)
  --zxnext[=cspect]        Enable ZX Next Z80 extensions (CSpect emulator has
  extra "exit" DD00 and "break" DD01 fake instructions)
  --i8080                  Limit valid instructions to i8080 only (+ no fakes)
  --lr35902                Sharp LR35902 CPU instructions mode (+ no fakes)
  -i<path> or -I<path> or --inc=<path> ( --inc without "=" to empty the list)
                           Include path (later defined have higher priority)
  --lst[=<filename>]       Save listing to <filename> (<source>.lst is default)
  --lstlab                 Enable label table in listing
  --sym=<filename>         Save symbols list to <filename>
  --exp=<filename>         Save exports to <filename> (see EXPORT pseudo-op)
  --raw=<filename>         Machine code saved also to <filename> (- is STDOUT)
  --sld[=<filename>]       Save Source Level Debugging data to <filename>
                           Default name is: "<first input filename>.sld.txt"
  Note: use OUTPUT,LUA/ENDLUA and other pseudo-ops to control output
 Logging:
  --nologo                 Do not show startup message
  --msg=[all|war|err|none|lst|lstlab]  Stderr messages verbosity ("all" is default)
  Note: "lst" or "lstlab" will turn STDERR into listing file (this will clash with
  `--lst`, use only one of the options) and some diagnostic messages of "all"
  are omitted, as they are not part of listing files.
  --fullpath               Show full path to error file
 Other:
  -D<NAME>[=<value>]       Define <NAME> as <value>
  -                        Reads STDIN as source (no other sourcefile allowed)
  --reversepop             Enable reverse POP order (as in base SjASM version)
  --dirbol                 Enable directives processing from the beginning of line
  --nofakes                Disable fake instructions (obsolete, use --syntax=F)
  --dos866                 Encode from Windows codepage to DOS 866 (Cyrillic)
  --syntax=<...>           Adjust parsing syntax, read details below.

Value for --syntax option may consist of multiple letters, omitting letter for particular feature will use the default setting:

  a      Multi-argument delimiter ",," (default is ",")
  b      Whole expression parentheses are legal for memory access only (default = immediate or memory)
  B      memory access brackets [] required (default = relaxed syntax, [] allowed as extra)
  f F    Fake instructions: warning / disabled (default = enabled)
  i      Case insensitive instructions/directives (default = same case required)
† l L    Keyword labels (registers, instructions, ...): warning / error (default = silent)
  m      Switch off "Accessing low memory" warning globally
  M      Alias "m" and "M" for "(hl)" to cover 8080-like syntax: ADD A,M
  w      Warnings option: report warnings as errors

† work in progress: options "l" and "L" are not implemented yet, following example is then not working correctly either.

I.e. --syntax=faBil will modify parser to process source line

hl:  Ld a,(hl),,de,hl

in a way to produce warnings about keyword "hl" being used for label, about fake instruction being used (ld de,hl) and assemble (hl) as numeric expression, not memory access. Warnings on fake instructions can be suppressed for particular line by adding any end-of-line comment containing string "fake", i.e. "ld de,hl ; fake DE=HL" will assemble without warning. The "F" option is identical to "--nofakes" and preferred.

The recommended setup for new projects is --syntax=abfw which makes syntax less relaxed, so some typos and mistakes are easier to catch, for example:

        OPT reset --syntax=abfw
label:  dw 15
        ld b,(label)
        sub a,b

will produce "error: Illegal instruction (can't access memory): (label)" message for the ld b,(label) and the sub a,b will produce only the sub b instruction (to give the sub multi-argument with syntax option "a" the line would have to be sub a,,b).

The assembler will also read the environment variable SJASMPLUSOPTS (if available), and process its content as part of command line options (before the actual options), so you can pre-configure certain options in your environment, for example:

export SJASMPLUSOPTS="--zxnext=cspect --msg=war"
sjasmplus --lst --lstlab example.asm

will execute the assembling as if command line "sjasmplus --zxnext=cspect --msg=war --lst --lstlab example.asm" was used. Known issue: parser of environment variable delimits each option on any white-space character, so option containing space character will be incorrectly parsed (like "-Ifile-path with space" = fails and there is no way to escape/quote the path in the SJASMPLUSOPTS variable to make it work).

Source file format

Lines in the source file should have the following form:

Label Operator Operand Comment

All fields are optional. Lines without label should start with whitespace. Operators and operands can be inlined with colon:

      Operator Operand:Operator Operand:Operator Operand... Comment

Comments should start with ';' or '//'. Comment blocks start with '/*' and end with '*/' (work in "nested" way, i.e. comment block started inside comment block must be also ended, before main block ends).

Example 2.1. 

; comment
// comment
 ld /* comment */ a,80
/*
 comment /* nested comment block */
*/
 ld /*
 but this won't be ld a,3!
 */ a,3


Some warnings (low memory-access and fake instruction) can be suppressed for particular line by adding end-of-line type of comment starting with "ok":

Example 2.2. 

        OPT --syntax=f  ; warning on accidental fake instructions
        ld  hl,de    ; warning here
        ld  hl,de    ; ok (warning will be suppressed by this comment)


Chapter 3. Labels

Labels

Labels are case-sensitive and may be of any reasonable length, that is: up to about 70 characters. Label definitions should start on the beginning of a line, but don't have to be followed by a colon ':'. Generally labels should start with a letter or a underscore ('_'), the following characters may be chosen from letters, numbers and the following special symbols: '_', '.', '!', '?', '#' and '@'. Note that the '.' has special meaning, as it is used between module names, labels and local labels. The following are all legal and distinct labels:

Kip
KIP
Kip@@
MAIN.loop?

It is possible to use mnemonics, pseudo-ops and register names as labels but it is not advised to do so. Also note that the identifiers defined with the DEFINE pseudo-op use another name space.

Local labels

When there is a module definition all labels (except those starting with a '@') are local to that module. To use a label from outside the module use modulename.labelname, in this example: 'vdp.Cls' Labels starting with a '.' are also local to the previous non-local label.

Example 3.1. docs_examples/s_local_labels.asm

    MODULE main             ; module "main"
Main:                       ; main.Main
        CALL SetScreen      ; SetScreen
        CALL vdp.Cls        ; main.vdp.Cls
.loop:                      ; main.Main.loop
        LD A,(.event)       ; main.Main.event
        CALL ProcessEvent   ; label not found: main.ProcessEvent
        DJNZ .loop          ; main.Main.loop

        MODULE vdp          ; module "main.vdp"
@SetScreen:                 ; SetScreen
.loop:                      ; main.vdp.SetScreen.loop
            RET
Cls:                        ; main.vdp.Cls
.loop:      DJNZ .loop      ; main.vdp.Cls.loop
            RET
        ENDMODULE

Main.event DB 0             ; main.Main.event
    ENDMODULE


@ Labels

Labels starting with a '@' are not touched by the label processing and used 'as-is'. See 'SetScreen' in the previous example code.

Example 3.2. docs_examples/s_at_labels.asm

    MODULE xxx
Label      ; xxx.Label
.Local     ; xxx.Label.Local
@Label     ; Label
.Local     ; xxx.Label.Local => duplicate label error
@Label2    ; Label2
.Local     ; xxx.Label2.Local
@yyy.Local ; yyy.Local
yyy.Local  ; xxx.yyy.Local


Temporary labels

To keep the number of used labels reasonable it is possible to use numbers as labels. These labels can only be used as labels to jump to. To jump to these labels, use the number followed by an 'F' for forward branches or a 'B' for backward branches. Temporary labels should be defined in the same order during every pass of assembling, but they can be used within macro, or repeating blocks (old sjasmplus versions didn't allow usage within macro).

Example 3.3. docs_examples/s_temp_labels.asm

        ADD A,E
        JR NC,1F
        INC D
1       LD E,A
2       LD B,4
        LD A,(DE)
        OUT (152),A
        DJNZ 2B

        MACRO zing
            DUP 2
                JR 1F
1               DJNZ    1B
            EDUP
        ENDM

        .4 zing


Chapter 4. Constants, expressions and other features

Numeric constants

Numeric constants should always start with a digit or $, # or %. The following formats are supported:

12     decimal
12d    decimal
0xc    hexadecimal
$c     hexadecimal
#c     hexadecimal
0ch    hexadecimal
0b1100 binary (v1.12.1)
%1100  binary
1100b  binary
0q14   octal (v1.12.1)
14q    octal
14o    octal

(v1.12.1) Optional single quotes(') may be inserted between the digits as a separator (example: ld a,%11'01'11'00 ). They are ignored by the assembler.

Character and string constants

Character constants are characters surrounded by single quotes. It is possible to use double quotes in some cases, but in general it is better to use single quotes. String constants are characters surrounded by double quotes. When double quotes are used, the following escape sequences are recognized:

\\ 92
\? 63
\' 39
\" 34
\0 0     ; since v1.11
\A 7
\B 8
\D 127
\E 27
\F 12
\N 10
\R 13
\T 9
\V 11

Inside single quotes two quotes after each other are parsed as the apostrophe itself (since v1.11).

Example 4.1. 

    BYTE "stringconstant\n" ; escape sequence assembles to newline
    BYTE 'stringconstant\n' ; \n assembles literally as two bytes: '\', 'n'
    LD HL,'hl'  ; hl = 0x686C = 'l', 'h'
    LD HL,"hl"  ; hl = 0x686C = 'l', 'h'
    LD A,"7"    ; not recommended (but works)
    LD A,'8'    ; recommended
    LD A,'\E'   ; warning + truncating value to 'E' (0x45)
    LD A,'"'    ; A = 0x22
    LD A,"'"    ; A = 0x27
    LD A,''''   ; A = 0x27 ; since v1.11


Expressions

Expressions are evaluated in signed 32 bits in this version of SjASMPlus (unless explicitly specified as unsigned, like ">>>" operator), so intermediate values have range -2147483648 to +2147483647.

'$' represents the current program counter. '$$' represents the current page in the current slot in the real device emulation mode, '$$label' evaluates to number of page where the "label" was defined (only regular labels have meaningful value, labels defined under DISP mode or EQU/DEFL/... will produce irrelevant values) and '{address}' can be used to read WORD from virtual device memory (correct value is read only in last pass of assembling, in early passes the zero value is always returned), '{b address}' reads only BYTE.

It is possible to use parenthesis '(' and ')' to override the precedence of the operators. The following operators may be used in expressions:

!     !x       logical not
~     ~x       complement
+     +x       does "nothing", can be used to make "+(...)" parse as value (not as memory)
-     -x       minus
low   low x    low 8 bits of 16 bit value or lower part of register pair
high  high x   high 8 bits of 16 bit value or higher part of register pair
not   not x    logical not

*     x*y      multiplication
/     x/y      division
%     x%y      modulo
mod   x mod y  modulo

+     x+y      addition
-     x-y      subtraction

<<    x<<y     shift left
>>    x>>y     shift right signed
>>>   x>>>y    shift right unsigned
shl   x shl y  shift left
shr   x shr y  shift right signed

<?    x<?y     minimum
>?    x>?y     maximum

<     x<y      less than
>     x>y      greater than
<=    x<=y     equal or less than
>=    x>=y     equal or greater than

=     x=y      equal
==    x==y     equal
!=    x!=y     not equal

&     x&y      bitwise and
and   x and y  bitwise and

^     x^y      bitwise xor
xor   x xor y  bitwise xor

|     x|y      bitwise or
or    x or y   bitwise or

&&    x&&y     logical and

||    x||y     logical or

$     $        current program counter
$$    $$       current page at program counter (in virtual device mode)
label label    value of label (aka symbol), usually memory address
$$lab $$lab    page of "lab" label (in virtual device mode)
{}    {x}      reads WORD from address x (in virtual device mode, in last pass)
{b}   {b x}    reads BYTE from address x (in virtual device mode, in last pass)

Assembly language

This version only accepts Z80 mnemonics. There are some additions to what I think is standard Z80:

  • '[' and ']' can be used instead of '(' and ')' for indirection. So LD A,[HL] is the same as LD A,(HL) (does not apply to IN/OUT ports, those must use '(...)' form)

  • IN F,(C) and OUT (C),0 and SLL/SLI can be used (warning: on some CPU versions of Z80 the OUT (C),0 is working as OUT (C),255).

  • IXL (or LX, XL), IYL (or LY, YL), IXH (or HX, XH) and IYH (or HY, YH) registers are supported.

  • Can write code throught colon: ORG 100h:LD A,10:LD B,10:SUB B:RET:IFDEF AA:.....

  • JP HL, JP IX and JP IY may be used instead of JP (HL), etc.

  • EX AF,AF or EX AF or EXA may be used instead of EX AF,AF'.

  • R800's MULUB and MULUW are recognised (but won't work on Z80, of course:)

  • Z80N, i8080 and LR35902 modes use the identical Z80 sjasmplus syntax (!), for the correct (and incorrect) syntax examples of extended opcodes, please check the test files: Z80N test 1 test 2 and LR35902 test 1 test 2 (also for the Z80 syntax examples you can check Z80 tests).

  • RLC, RRC, RL, RR, SLA, SRA, SLL (SLI), RES, SET undocumented instructions added.

    SET 4,(IX+4),C ; (aka LD C,SET 4,(IX+4)) is LD C,(IX+4) / SET 4,C / LD (IX+4),C
    RRC (IY),A     ; (aka LD A,RRC (IY+0))   is LD A,(IY)   / RRC A   / LD (IY),A
  • PUSH and POP can take register lists:

    PUSH AF,BC  ; push af / push bc
    POP  AF,BC  ; pop  af / pop  bc
  • and many other instructions support this "multi-argument" syntax:

    LD A,B,B,D,D,H
   /* this is:
     LD A,B
     LD B,D
     LD D,H
   */
   ;or you can write  LD A,B:LD B,D:LD D,H

   ; since v1.13.1 it is possible and *recommended* to change the multi-arg delimiter
   ; into ",,", to avoid some ambiguities with certain instructions.
   OPT --syntax=a
   LD A,B,,B,D,,D,H ; same as example above in default syntax
   SUB A,B,,C  ; = SUB B, SUB C (the default syntax does for SUB A,B two SUBs!)

Fake instructions

Of course the Z80 is only an 8 bit cpu, but sometimes ld hl,de would be nice. SjASMPlus now 'fakes' some instructions like that. This improves the readability of the source, but it might not be the fastest way to get the result. Also possibly some 'new' load instructions do affect the flags in ways you wouldn't expect. You can use option --syntax=f to get warnings when fake instruction is used, to avoid using them by accident. Here's the list:

  rl bc           ; rl c : rl b
  rl de           ; rl e : rl d
  rl hl           ; rl l : rl h
  rr bc           ; rr b : rr c
  rr de           ; rr d : rr e
  rr hl           ; rr h : rr l
  sla bc          ; sla c : rl b
  sla de          ; sla e : rl d
  sla hl          ; add hl,hl
  sll bc          ; sli c : rl b
  sll de          ; sli e : rl d
  sll hl          ; sli l : rl h
  sli bc          ; sli c : rl b
  sli de          ; sli e : rl d
  sli hl          ; sli l : rl h
  sra bc          ; sra b : rr c
  sra de          ; sra d : rr e
  sra hl          ; sra h : rr l
  srl bc          ; srl b : rr c
  srl de          ; srl d : rr e
  srl hl          ; srl h : rr l

  ld bc,bc        ; ld b,b : ld c,c
  ld bc,de        ; ld b,d : ld c,e
  ld bc,hl        ; ld b,h : ld c,l
  ld bc,ix        ; ld b,xh : ld c,xl
  ld bc,iy        ; ld b,yh : ld c,yl
  ld bc,(hl)      ; ld c,(hl) : inc hl : ld b,(hl) : dec hl
  ld bc,(ix+nn)   ; ld c,(ix+nn) : ld b,(ix+nn+1)
  ld bc,(iy+nn)   ; ld c,(iy+nn) : ld b,(iy+nn+1)

  ld de,bc        ; ld d,b : ld e,c
  ld de,de        ; ld d,d : ld e,e
  ld de,hl        ; ld d,h : ld e,l
  ld de,ix        ; ld d,xh : ld e,xl
  ld de,iy        ; ld d,yh : ld e,yl
  ld de,(hl)      ; ld e,(hl) : inc hl : ld d,(hl) : dec hl
  ld de,(ix+nn)   ; ld e,(ix+nn) : ld d,(ix+nn+1)
  ld de,(iy+nn)   ; ld e,(iy+nn) : ld d,(iy+nn+1)

  ld hl,bc        ; ld h,b : ld l,c
  ld hl,de        ; ld h,d : ld l,e
  ld hl,hl        ; ld h,h : ld l,l
  ld hl,ix        ; push ix : pop hl
  ld hl,iy        ; push iy : pop hl
  ld hl,(ix+nn)   ; ld l,(ix+nn) : ld h,(ix+nn+1)
  ld hl,(iy+nn)   ; ld l,(iy+nn) : ld h,(iy+nn+1)

  ld ix,bc        ; ld xh,b : ld xl,c
  ld ix,de        ; ld xh,d : ld xl,e
  ld ix,hl        ; push hl : pop ix
  ld ix,ix        ; ld xh,xh : ld xl,xl
  ld ix,iy        ; push iy : pop ix

  ld iy,bc        ; ld yh,b : ld yl,c
  ld iy,de        ; ld yh,d : ld yl,e
  ld iy,hl        ; push hl : pop iy
  ld iy,ix        ; push ix : pop iy
  ld iy,iy        ; ld yh,yh : ld yl,yl

  ld (hl),bc      ; ld (hl),c : inc hl : ld (hl),b : dec hl
  ld (hl),de      ; ld (hl),e : inc hl : ld (hl),d : dec hl

  ld (ix+nn),bc   ; ld (ix+nn),c : ld (ix+nn+1),b
  ld (ix+nn),de   ; ld (ix+nn),e : ld (ix+nn+1),d
  ld (ix+nn),hl   ; ld (ix+nn),l : ld (ix+nn+1),h

  ld (iy+nn),bc   ; ld (iy+nn),c : ld (iy+nn+1),b
  ld (iy+nn),de   ; ld (iy+nn),e : ld (iy+nn+1),d
  ld (iy+nn),hl   ; ld (iy+nn),l : ld (iy+nn+1),h

  ldi bc,(hl)     ; ld c,(hl) : inc hl : ld b,(hl) : inc hl
  ldi bc,(ix+nn)  ; ld c,(ix+nn) : inc ix : ld b,(ix+nn) : inc ix
  ldi bc,(iy+nn)  ; ld c,(iy+nn) : inc iy : ld b,(iy+nn) : inc iy

  ldi de,(hl)     ; ld e,(hl) : inc hl : ld d,(hl) : inc hl
  ldi de,(ix+nn)  ; ld e,(ix+nn) : inc ix : ld d,(ix+nn) : inc ix
  ldi de,(iy+nn)  ; ld e,(iy+nn) : inc iy : ld d,(iy+nn) : inc iy

  ldi hl,(ix+nn)  ; ld l,(ix+nn) : inc ix : ld h,(ix+nn) : inc ix
  ldi hl,(iy+nn)  ; ld l,(iy+nn) : inc iy : ld h,(iy+nn) : inc iy

  ldi (hl),bc     ; ld (hl),c : inc hl : ld (hl),b : inc hl
  ldi (hl),de     ; ld (hl),e : inc hl : ld (hl),d : inc hl

  ldi (ix+nn),bc  ; ld (ix+nn),c : inc ix : ld (ix+nn),b : inc ix
  ldi (ix+nn),de  ; ld (ix+nn),e : inc ix : ld (ix+nn),d : inc ix
  ldi (ix+nn),hl  ; ld (ix+nn),l : inc ix : ld (ix+nn),h : inc ix

  ldi (iy+nn),bc  ; ld (iy+nn),c : inc iy : ld (iy+nn),b : inc iy
  ldi (iy+nn),de  ; ld (iy+nn),e : inc iy : ld (iy+nn),d : inc iy
  ldi (iy+nn),hl  ; ld (iy+nn),l : inc iy : ld (iy+nn),h : inc iy

  ldi a,(bc)      ; ld a,(bc) : inc bc
  ldi a,(de)      ; ld a,(de) : inc de
  ldi a,(hl)      ; ld a,(hl) : inc hl
  ldi b,(hl)      ; ld b,(hl) : inc hl
  ldi c,(hl)      ; ld c,(hl) : inc hl
  ldi d,(hl)      ; ld d,(hl) : inc hl
  ldi e,(hl)      ; ld e,(hl) : inc hl
  ldi h,(hl)      ; ld h,(hl) : inc hl
  ldi l,(hl)      ; ld l,(hl) : inc hl
  ldi a,(ix+nn)   ; ld a,(ix+nn) : inc ix
  ldi b,(ix+nn)   ; ld b,(ix+nn) : inc ix
  ldi c,(ix+nn)   ; ld c,(ix+nn) : inc ix
  ldi d,(ix+nn)   ; ld d,(ix+nn) : inc ix
  ldi e,(ix+nn)   ; ld e,(ix+nn) : inc ix
  ldi h,(ix+nn)   ; ld h,(ix+nn) : inc ix
  ldi l,(ix+nn)   ; ld l,(ix+nn) : inc ix
  ldi a,(iy+nn)   ; ld a,(iy+nn) : inc iy
  ldi b,(iy+nn)   ; ld b,(iy+nn) : inc iy
  ldi c,(iy+nn)   ; ld c,(iy+nn) : inc iy
  ldi d,(iy+nn)   ; ld d,(iy+nn) : inc iy
  ldi e,(iy+nn)   ; ld e,(iy+nn) : inc iy
  ldi h,(iy+nn)   ; ld h,(iy+nn) : inc iy
  ldi l,(iy+nn)   ; ld l,(iy+nn) : inc iy

  ldd a,(bc)      ; ld a,(bc) : dec bc
  ldd a,(de)      ; ld a,(de) : dec de
  ldd a,(hl)      ; ld a,(hl) : dec hl
  ldd b,(hl)      ; ld b,(hl) : dec hl
  ldd c,(hl)      ; ld c,(hl) : dec hl
  ldd d,(hl)      ; ld d,(hl) : dec hl
  ldd e,(hl)      ; ld e,(hl) : dec hl
  ldd h,(hl)      ; ld h,(hl) : dec hl
  ldd l,(hl)      ; ld l,(hl) : dec hl
  ldd a,(ix+nn)   ; ld a,(ix+nn) : dec ix
  ldd b,(ix+nn)   ; ld b,(ix+nn) : dec ix
  ldd c,(ix+nn)   ; ld c,(ix+nn) : dec ix
  ldd d,(ix+nn)   ; ld d,(ix+nn) : dec ix
  ldd e,(ix+nn)   ; ld e,(ix+nn) : dec ix
  ldd h,(ix+nn)   ; ld h,(ix+nn) : dec ix
  ldd l,(ix+nn)   ; ld l,(ix+nn) : dec ix
  ldd a,(iy+nn)   ; ld a,(iy+nn) : dec iy
  ldd b,(iy+nn)   ; ld b,(iy+nn) : dec iy
  ldd c,(iy+nn)   ; ld c,(iy+nn) : dec iy
  ldd d,(iy+nn)   ; ld d,(iy+nn) : dec iy
  ldd e,(iy+nn)   ; ld e,(iy+nn) : dec iy
  ldd h,(iy+nn)   ; ld h,(iy+nn) : dec iy
  ldd l,(iy+nn)   ; ld l,(iy+nn) : dec iy

  ldi (bc),a      ; ld (bc),a : inc bc
  ldi (de),a      ; ld (de),a : inc de
  ldi (hl),a      ; ld (hl),a : inc hl
  ldi (hl),b      ; ld (hl),b : inc hl
  ldi (hl),c      ; ld (hl),c : inc hl
  ldi (hl),d      ; ld (hl),d : inc hl
  ldi (hl),e      ; ld (hl),e : inc hl
  ldi (hl),h      ; ld (hl),h : inc hl
  ldi (hl),l      ; ld (hl),l : inc hl
  ldi (ix+nn),a   ; ld (ix+nn),a : inc ix
  ldi (ix+nn),b   ; ld (ix+nn),b : inc ix
  ldi (ix+nn),c   ; ld (ix+nn),c : inc ix
  ldi (ix+nn),d   ; ld (ix+nn),d : inc ix
  ldi (ix+nn),e   ; ld (ix+nn),e : inc ix
  ldi (ix+nn),h   ; ld (ix+nn),h : inc ix
  ldi (ix+nn),l   ; ld (ix+nn),l : inc ix
  ldi (iy+nn),a   ; ld (iy+nn),a : inc iy
  ldi (iy+nn),b   ; ld (iy+nn),b : inc iy
  ldi (iy+nn),c   ; ld (iy+nn),c : inc iy
  ldi (iy+nn),d   ; ld (iy+nn),d : inc iy
  ldi (iy+nn),e   ; ld (iy+nn),e : inc iy
  ldi (iy+nn),h   ; ld (iy+nn),h : inc iy
  ldi (iy+nn),l   ; ld (iy+nn),l : inc iy

  ldd (bc),a      ; ld (bc),a : dec bc
  ldd (de),a      ; ld (de),a : dec de
  ldd (hl),a      ; ld (hl),a : dec hl
  ldd (hl),b      ; ld (hl),b : dec hl
  ldd (hl),c      ; ld (hl),c : dec hl
  ldd (hl),d      ; ld (hl),d : dec hl
  ldd (hl),e      ; ld (hl),e : dec hl
  ldd (hl),h      ; ld (hl),h : dec hl
  ldd (hl),l      ; ld (hl),l : dec hl
  ldd (ix+nn),a   ; ld (ix+nn),a : dec ix
  ldd (ix+nn),b   ; ld (ix+nn),b : dec ix
  ldd (ix+nn),c   ; ld (ix+nn),c : dec ix
  ldd (ix+nn),d   ; ld (ix+nn),d : dec ix
  ldd (ix+nn),e   ; ld (ix+nn),e : dec ix
  ldd (ix+nn),h   ; ld (ix+nn),h : dec ix
  ldd (ix+nn),l   ; ld (ix+nn),l : dec ix
  ldd (iy+nn),a   ; ld (iy+nn),a : dec iy
  ldd (iy+nn),b   ; ld (iy+nn),b : dec iy
  ldd (iy+nn),c   ; ld (iy+nn),c : dec iy
  ldd (iy+nn),d   ; ld (iy+nn),d : dec iy
  ldd (iy+nn),e   ; ld (iy+nn),e : dec iy
  ldd (iy+nn),h   ; ld (iy+nn),h : dec iy
  ldd (iy+nn),l   ; ld (iy+nn),l : dec iy

  ldi (hl),mm     ; ld (hl),mm : inc hl
  ldi (ix+nn),mm  ; ld (ix+nn),mm : inc ix
  ldi (iy+nn),mm  ; ld (iy+nn),mm : inc iy

  ldd (hl),mm     ; ld (hl),mm : dec hl
  ldd (ix+nn),mm  ; ld (ix+nn),mm : dec ix
  ldd (iy+nn),mm  ; ld (iy+nn),mm : dec iy

  sub hl,bc       ; or a : sbc hl,bc
  sub hl,de       ; or a : sbc hl,de
  sub hl,hl       ; or a : sbc hl,hl
  sub hl,sp       ; or a : sbc hl,sp

  ; Z80N only
  mul             ; mul de ; to avoid warning specify registers

LDI increases the data pointer after the data access, so LDI A,(HL) is the same as LD A,(HL):INC HL. Likewise, LDD A,(DE) is LD A,(DE):DEC DE.

Real device emulation mode

To enable this mode you must use pseudo-op DEVICE.

In this mode the compiler compiling program to virtual memory (as at MSX's WB-ASS2, ZX-Spectrum's GENS, ZEUS, ALASM etc). After this all you can use new pseudo-ops as SAVEBIN, SAVEDEV SAVEHOB, SAVETRD, SAVETAP, PAGE, SLOT, MMU, LABELSLIST, use special functions in Lua scripts and use operators {address}, {b address} to read WORD/BYTE from the virtual memory.

If only single DEVICE is used in whole source batch, the device becomes "global" and will affect also source ahead of the DEVICE line.

Example 4.2. docs_examples/s_realdevice.asm

    DEVICE ZXSPECTRUM128
    ; in this device the default slot is SLOT 3 with PAGE 0 paged in.

    ORG 32768
StartProg:
    JP $

    DEVICE NONE
    ;do something, if you don't want to corrupt virtual
    ;memory with other code, for example, loader of code.
    ;...code...

    ;return to our virtual device:
    DEVICE ZXSPECTRUM128

    SAVESNA "snapshotname.sna", StartProg


Predefined devices:

NONE

Disable real device emulation mode. By default.

ZXSPECTRUM48

Has 4 slots (0-3) with size 4000h, 4 pages (0-3) with size 4000h. Slot 3 (it from 0C000h) enables to current by default.

ZXSPECTRUM128

Has 8 RAM pages (0-7) with size 4000h. Default slot is 3.

ZXSPECTRUM256

Same as Russian clone Scorption 256. Has 16 RAM pages (0-15) with size 4000h.

ZXSPECTRUM512

Same as Russian clones ATM Turbo 512 and Pentagon 512. Has 32 RAM pages (0-31) with size 4000h.

ZXSPECTRUM1024

Same as Russian clones ATM Turbo 2 and Pentagon 1024 SL. Has 64 RAM pages (0-63) with size 4000h.

ZXSPECTRUMNEXT

ZX Spectrum Next, has 8 slots (0-7) of size 0x2000 and 224 RAM pages (0-223) totalling at 1.75MiB of memory. The default mapping is similar to ZX128, paging in: {14, 15, 10, 11, 4, 5, 0, 1} pages. Default slot is 7 (memory range 0xE000..0xFFFF). All memory is zeroed during initialization.

If you want to see other devices you must write to us. See Feedback chapter.

Predefined defines

SjASMPlus has predefined defines.

_SJASMPLUS = 1

Example 4.3. 

   IFDEF _SJASMPLUS
     ;code for sjasmplus
   ELSE
     ;code for other compiler
   ENDIF


_VERSION = "version"

Example 4.4. 

   IF _VERSION = "1.07"
     ;code for 1.07
   ELSE
     ;code for other version
   ENDIF


_RELEASE = releasenumber

Example 4.5. 

   IF _RELEASE = 1 ; 0 - is stable version
     ;code for Release Candidate 1
   ELSE
     ;code for other version
   ENDIF


_ERRORS = <number>

Number of errors.

_WARNINGS = <number>

Number of warnings.

Chapter 5. Pseudo-ops (aka Pseudo-instructions, Directives etc)

Simple example of usage

     .SOMEPSEUDOOP ;or
     SOMEPSEUDOOP  ;or
     somepseudoop

Almost complete list

.<repeat-count> <single instruction>

Repeat <single instruction> <repeat-count> many times. Doesn't work in the beginning of line. The <repeat-count> must be either simple integer number or expression fully enclosed in parentheses.

Example 5.1. docs_examples/po_dot_repeat.asm

 .3        INC A    ;will be compiled to INC A:INC A:INC A
len        EQU 10
 .(12-len) BYTE 0   ;will be compiled to BYTE 0,0
 .2 .3     RET      ;will be compiled to 6x RET


ABYTE <offset> <bytes>

Defines a byte or a string of bytes. The offset is added to each of the following bytes.

Example 5.2. 

    ABYTE 2 4,9    ; Same as BYTE 6,11
    ABYTE 3 "ABC"  ; Same as BYTE "DEF"


ABYTEC <offset> <bytes>

Defines a byte or a string of bytes, where the last byte of the string will have bit 7 set. The offset is added to each of the following bytes.

Example 5.3. 

    ABYTEC 0 "KIP"        ; Same as BYTE "KI",'P'|128
    ABYTEC 1 "ABC",0,"DE" ; Same as BYTE "BC",'D'|128,1,'E','F'|128


ABYTEZ <offset> <bytes>

Defines a byte or a string of bytes, followed by a zero. The offset is added to each of the following bytes.

Example 5.4. 

    ABYTEZ 0 "KIP"        ; Same as BYTE "KIP",0


ALIGN [<expression equal to 1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384 or 32768>[, <byte>]]

Align advances to nearest address where <new address> modulo <expression> (default 4) equals zero (stays at current address if possible).

If <byte> is specified, memory advanced over is set to it.

Example 5.5. 

    ALIGN         ; => ALIGN 4 - simply align by 4
    ALIGN 2       ; by 2 (preserves value of "device" memory)
    ALIGN 2,0     ; + fills memory with zero


ASSERT <expression>

An 'assertion failed' error is issued if the expression evaluates to zero.

Example 5.6. 

STACKPOINTER=0D500H
    ASSERT END_OF_PROGRAM < STACKPOINTER
END_OF_PROGRAM


BINARY <filename>[,<offset>[,<length>]]

Synonym of INCBIN.

BLOCK <length>[,<fill byte>]

Defines space. Has to be followed by the number of byte to reserve, optionally followed by the value to fill these bytes with.

Example 5.7. 

    BLOCK 500     ; define a block of 500 bytes of zero
    BLOCK 500,0   ; define a block of 500 bytes of zero
    BLOCK 400,-1  ; define a block of 400 bytes of 255


BYTE <bytes>

Defines a byte or a string of bytes. Each value should be between -129 and 256.

Example 5.8. 

    BYTE 0x56
    BYTE 1,-78,'@'
    BYTE "Format C:? ",0h


CSPECTMAP [<filename>]

Useful for ZX-Spectrum Emulator #CSpect by Mike Dailly.

Works only in real device emulation mode. See DEVICE.

Saves labels list in format:

HEXA_16BIT_ADDRESS HEXA_LONG_ADDRESS TYPE LABELNAME

where TYPE is: 00 = regular label, 01 = EQU or struct defined, 02 = DEFL defined.

If no filename is provided, default is created by appending ".map" to source name.

D24

Defines three bytes by 24b constant. Values should be between -16777217 and 16777216.

Example 5.9. 

    D24 0x123456   ; define three bytes 0x56, 0x34, 0x12


DB

Synonym of BYTE.

DC

Same as BYTE, but every last character of a string will have bit 7 set.

Example 5.10. 

    DC "kip" ; same as BYTE "ki",'p'|128


DD

Synonym of DWORD.

DEFARRAY <id> <replacements>

Array of DEFINEs

Example 5.11. docs_examples/po_defarray.asm

    DEFARRAY myarray 10*20,"A",20,</D,40>,50,70
CNT DEFL 0 ;or CNT=0
    DUP 6
    DISPLAY myarray[CNT]
CNT DEFL CNT+1 ;or CNT=CNT+1
    EDUP


DEFARRAY+ <id> <additional replacements>

Appending more DEFINEs to already defined array

Example 5.12. 

    DEFARRAY   myarray 'A', 'B', 'C'
    DEFARRAY+  myarray 'D', 'E'            ; now "myarray" has 5 items
    DUP 3 : DEFARRAY+ myarray '!' : EDUP   ; "DEFARRAYFILL" adding 3x '!'


DEFB

Synonym of BYTE.

DEFD

Synonym of DWORD.

DEFDEVICE <deviceid>

Sorry, not available yet. If you want to see new device in SjASMPlus, please, write us.

DEFG

Synonym of DG.

DEFH

Synonym of DH.

DEFINE <id> <replacement>

The identifier <id> will be replaced with the <replacement>. The replacement could be omitted, in such case it is still possible to check if the identifier was defined with IFDEF or IFNDEF.

Example 5.13. 

    DEFINE str_honderd "Honderd"
    BYTE str_honderd,0             ; BYTE "Honderd",0


<label> DEFL <expression>

Assigns value of <expression> to symbol <label>. New label defined by DEFL is marked internally as "modifiable", allowing to re-assign new values to it with further DEFL statements (you can use also = instead of DEFL).

Example 5.14. 

counter DEFL 0
    DUP 4
        DB 0xAA, counter
counter = counter + 1
    EDUP
; machine code produced: AA 00 AA 01 AA 02 AA 03


DEFM

Synonym of BYTE.

DEFS

Synonym of BLOCK.

DEFW

Synonym of WORD.

DEPHASE

Synonym of ENT.

DEVICE <deviceid>

Enables real device emulation mode by it identifier. If there is only single DEVICE directive in whole source batch, it becomes "global" and the device affects all lines of source, otherwise the DEVICE is applied for lines following it.

Predefined devices' identifiers list:

 NONE ; off real device emulation mode
 ZXSPECTRUM48  ; ZX-Spectrum 48 (4 slots, 4 pages, slot/page size 0x4000, default map: 0, 1, 2, 3)
 ZXSPECTRUM128 ; ZX-Spectrum 128 (like 48 with 8 pages, default map: 7, 5, 2, 0)
 ZXSPECTRUM256 ; e.g. Scorpion 256 (exUSSR clone of ZX-Spectrum 128)
 ZXSPECTRUM512 ; e.g. ATM-Turbo 512 (another clone)
 ZXSPECTRUM1024
 ZXSPECTRUMNEXT ; ZX Spectrum Next (8 slots, 224 pages, slot size 0x2000 = 1.75MiB RAM)
                ; (default pages map: 14, 15, 10, 11, 4, 5, 0, 1) (default slot: 7 (0xE000..0xFFFF))

 ;disable:
   DEVICE NONE
 ;enable:
   DEVICE ZXSPECTRUM128
DG <data encoded in bits>

Data comprises of characters in multiples of eight, each block is converted to a byte value.

A hyphen '-' (also '.' and '_') represents 0 and any other non-whitespace character represents 1. It ignores spaces, use them for formatting if you like. Warning, "DG 10100001" is value 255, because character '0' is not a dash '-'. (since v1.11)

Example 5.15. 

    DG 1-1----1  ; store 161 at the current location
    DG ...# #... .##. .... ; store two bytes: 0x18, 0x60


DH "<data>"[,"<data2>"...]

The data string comprises pairs of hexadecimal digits, each pair is converted to a byte value. You can add spaces between pairs as you like. (since v1.11)

Example 5.16. 

    DH "0123456789ABCDEF"   ; eight bytes #01 #23 ...
    DH "01 23  45 67"       ; four bytes #01 #23 #45 #67


DISP <address>[,<page_number>]

Set the address in which the part of code should work. PHASE and TEXTAREA are synonyms of DISP. ENT is restore current address. UNPHASE, DEPHASE and ENDT are synonyms of ENT. DISP blocks can NOT be nested, and to change the displacement address within current DISP block use the ordinary ORG. When in device mode, you can specify fixed value for "fake" page of emitted instructions and regular labels, but to avoid warning, you must also map-in the target page into the target memory slot (MMU). When no fixed page in DISP is specified, the current mapping of memory pages is used.

Example 5.17. docs_examples/po_disp.asm

    DEVICE ZXSPECTRUM48
SCREEN  EQU $4000
        ORG $8000
        LD HL,BEGIN
        LD DE,SCREEN
        LD BC,ENDOFPROG-BEGIN
        LDIR
        JP SCREEN
BEGIN   DISP SCREEN ;code will compile for address $4000, but to the current ORG
MARKA       DEC A
            HALT
            JP NZ,MARKA
            DI
            HALT
        ENT
ENDOFPROG

    ASSERT $800E == BEGIN && $8015 == ENDOFPROG && $4000 == MARKA
    ASSERT $76 == {B $800F}     ; HALT instruction lands at $800F (BEGIN+1)


DISPLAY <bytes>

This pseudo-op comes from ZX-Spectrum assembler ALASM.

Out to console a string of bytes. Each value should be between -129 and 256. Keys /D, /H and /A set format of output of numbers:

/D - out only in Decimal
/H - out only in Hexadecimal
/A - out both in Hexadecimal and Decimal

Example 5.18. docs_examples/po_display.asm

    ORG 100h
TESTLABEL:
    ;...some code...
    RET
    DISPLAY "--the some program-- by me"
    DISPLAY "TESTLABEL address is:",/A,TESTLABEL
/*
will output to the console strings:
> --the some program-- by me
> TESTLABEL address is:0x100, 256
*/


DM

Synonym of BYTE.

DS

Synonym of BLOCK.

DUP <count>

DUP specifies the number of times to generate the following lines until an EDUP pseudo-op is encountered. DUP can be used in macro's.

Example 5.19. 

    DUP 3
    NOP
    EDUP

/*this will expand to:
    NOP
    NOP
    NOP
*/


DW

Synonym of WORD.

DWORD

Defines a so called doubleword. Values should be between -2147483649 and 4294967296.

Example 5.20. 

    DWORD 4000h,0d000h
    DWORD 4


DZ

Same as BYTE, but an extra zero will be added at the end.

Example 5.21. 

    DZ 1      ; same as BYTE 1,0
    DZ "kip"  ; same as BYTE "kip",0


EMPTYTAP <filenameoftapefile>

Useful only for ZX-Spectrum users

Create the new or truncate existing standard tape file for emulators of ZX-Spectrum. See example of SAVETAP.

EMPTYTRD <filenameoftrdimage>

Useful only for ZX-Spectrum users

Create the empty TRD image for emulators of ZX-Spectrum. See example of SAVETRD.

ENCODING <encoding>

Useful only for non English users

Set the current encoding, i.e. if you set "DOS", SjASMPlus will automatically convert strings from ANSI to DOS-866. Encoding may be "DOS"(DOS-866) or "WIN"(ANSI/Win-1251). Default is "WIN".

Example 5.22. 

    ENCODING "WIN"
    DB "тексттекст" ;will be тексттекст
    ENCODING "DOS"
    DB "тексттекст" ;will be ⥪бв⥪бв


END [<startaddress>]

The assembler will stop at this point. The pseudo-op END does NOT work in the beginning of line (even with --dirbol). The optional argument is used by SAVESNA, SAVETAP and SAVENEX.

ENDLUA

See LUA for more information.

ENDMOD

Synonym of ENDMODULE.

ENDMODULE

To indicate the end of a module (see MODULE).

ENDT

Synonym of ENT.

ENT

Restore current address. See DISP for more information.

<label> EQU <expression>

To give the label a value other than the current program counter. The label should not already exist (you can assign only one value to it). For modifiable labels holding temporary values use DEFL.

Example 5.23. 

Label EQU 3
Kip   EQU 0x23*256 + low $


EXPORT <label>

The named label will be written to the export-file, in the form 'label: EQU value'. This way the export-file can be included in other sources.

Example 5.24. 

DRIE=3
    EXPORT DRIE  ; adds into export file line: "DRIE: EQU 0x00000003"


FPOS <position>

The FPOS directive makes it possible to set the file position to anywhere in the output file. Position value without sign is used as absolute one to be set, position starting with + or - sign will be used as relative position.

In combination with OUTPUT <filename>,r it is possible to update existing files.

Example 5.25. 

; This example will result in a file with a length of one byte:
    BYTE 0
    FPOS 0
    BYTE 1
    END


HEX

Synonym of DH, usually used without quotes around data.

INCBIN <filename>[,<offset>[,<length>]]

To include a binary file into the outputfile. The offset and length are optional. Added in v1.12.1: if negative offset or length is provided, it counts relatively from the end of the file.

Example 5.26. 

    INCBIN "gfx.scc",7        ; include gfx.scc, skip first 7 bytes
    INCBIN "rantab.com",3,256 ; include 256 bytes from offset 3
    INCBIN gfx.scc ,,7        ; 7 bytes from offset 0 (unquoted filename must end with space)
    INCBIN "48.rom",-768,-256 ; include (from 16kiB file) 512 bytes 15616..16127


INCHOB <filename>[,<offset>[,<length>]]

To include a data from a hobeta file into the outputfile. The offset and length are optional.

Example 5.27. 

    INCHOB "gfx.$c",7        ; include gfx.scc, skip first 7 bytes
    INCHOB "sprs.$c",3,256   ; include 256 bytes from offset 3
    INCHOB gfx.$c ,7        ; note the space between the filename and the ',7' here :)


INCLUDE <filename>

To include another sourcefile into the current. Sourcefiles can be nested 20 levels deep. If the file cannot be found in the current directory (the current directory is the directory the current asm file comes from!) the file will be searched for in the directories specified at the commandline. When angle brackets are used, the commandline directories are searched before the current directory.

The directory used to launch the assembling process is automatically added to the list (as if "-i." was added to command line manually) (v1.14.0 and v1.14.1 don't add it, reverted back for v1.14.2). If you want to start with completely empty include-path list, use "--inc" option early (order matters) without the "=" to empty the current list, like: sjasmplus --inc --inc=path1 --inc=path2 file.asm

Example 5.28. 

    INCLUDE <VDP.I>     ; search for file "VDP.I" in the include directories, then in current
    INCLUDE MORE.I      ; search for "MORE.I" in current directory, then in include directories
    INCLUDE "MORE.I"


INCLUDELUA <filename>

To include another LUA script in first pass(!). If the file cannot be found in the current directory (the current directory is the directory the current file comes from) the file will be searched for in the directories specified at the commandline. When angle brackets are used, the commandline directories are searched before the current directory.

Example 5.29. 

    INCLUDELUA <mylibrary1.lua>
    INCLUDELUA mylibrary2.lua
    INCLUDELUA "library_for_zx.lua"


INCTRD <filenameoftrdimage>,<filenameintrdimage>[,<offset>[,<length>]]

To include a file from a TRD image into the outputfile. The offset and length are optional.

Example 5.30. 

    INCTRD "test.trd","mygfx.C" ; include mygfx.C from test.trd
    INCTRD "test.trd","mygfx.C",12 ; include mygfx.C from test.trd, skip first 12 bytes


INSERT <filename>[,<offset>[,<length>]]

INSERT is a synonym of INCBIN. See above.

LABELSLIST <filename>

Useful only for ZX-Spectrum Emulator UNREALSPECCY.

Work only in real device emulation mode. See DEVICE.

Save labels list in format:

NN:ADDRESS LABELNAME

where NN is number of RAM page and ADDRESS is truncated to 0000..3FFF range

Example 5.31. 

    LABELSLIST "x:/somepath/user.l"


LUA [pass]

Using pseudo-ops LUA and ENDLUA you can insert Lua scripts. See more in the chapter "Lua scripting".

Parameter is optional. It may be:

PASS1   -  interpret Lua script in first pass only.
PASS2   -  interpret Lua script in second pass only.
PASS3   -  interpret Lua script in third pass only. By default.
ALLPASS -  interpret Lua script in all passes. It is need, if you generate some Z80 code.

Example 5.32. 

    LUA
-- some comments
        print "Hi, man! This is Lua!"
    ENDLUA
; some code now:
    LUA ALLPASS
        _pl("LABEL LD A,10")
        _pc("RET")
    ENDLUA


MEMORYMAP

Not available yet.

MMU <first slot number> [<last slot number>|<single slot option>], <page number>

Maps memory page(s) to slot(s), similar to SLOT + PAGE combination, but allows to set up whole range of consecutive slots (with consecutive memory pages). Or when only single slot is specified, extra option can be used to extend particular slot functionality. The slot behaviour will stay set in the current DEVICE until reset by another MMU specifying same slot (even as part of range, that will clear the option to "default").

Single slot option (default state is: no error/warning and no wrap = nothing special):

e = error on writing beyond last byte of slot
w = warning on writing beyond last byte of slot
n = wrap address back to start of slot, map next page

Example 5.33. docs_examples/po_mmu.asm

    DEVICE ZXSPECTRUM128 : LABELSLIST "po_mmu.lbl"  ; to check label pages
    MMU 1 3, 5      ; maps slots 1, 2, 3 with pages 5, 6, 7
    ORG 0xBFFF
label1_p6: scf      ; last byte of page 6 (in slot 2)
label2_p7: scf      ; first byte of page 7 (in slot 3)

    MMU 3 e, 0      ; page 0 into slot 3, write beyond slot will cause error
    ORG 0xFFFF
    ld  a,1         ; error: Write outside of memory slot: 65536 (65536 = address outside)

    MMU 3 n, 1      ; page 1 into slot 3, make it wrap + map next page automatically
    ORG 0xFFFF      ; ! also the $ address was truncated by MMU from $10001 to $0001 !
label3_p1: scf      ; last byte of page 1, then wrapping back to 0xC000 with page 2
label4_p2: scf      ; first byte of page 2 at 0xC000


MODULE <name>

Labels has to be unique only whithin the current module (module is added as prefix to them). Also note the use of '@' operator to suppress all this label-processing. Modules can be nested, and module has to be ended by ENDMODULE.

Example 5.34. docs_examples/po_module.asm

    MODULE xxx
Kip:                ; label xxx.Kip
    ld  hl,@Kip     ; global Kip
    ld  hl,@Kop     ; global Kop
    ld  hl,Kop      ; xxx.Kop
Kop:                ; label xxx.Kop
    ld  hl,Kip      ; xxx.Kip
    ld  hl,yyy.Kip  ; yyy.Kip
    ld  hl,nested.Kip   ; xxx.nested.Kip
        MODULE nested
Kip:        ret     ; label xxx.nested.Kip
        ENDMODULE
    ENDMODULE

    MODULE yyy
Kip:    ret         ; label yyy.Kip
@Kop:   ret         ; label Kop (global one, no module prefix)
@xxx.Kop: nop       ; ERROR: duplicate: label xxx.Kop
    ENDMODULE

Kip     ret         ; global label Kip


Since v1.14.0 the module <name> can NOT contain dot character. You can use nested modules to get identical identifier as in older versions, or please rename with underscores/etc:

    ; invalid since v1.14.0
        MODULE older.version
fn1:        ret        ; final label: @older.version.fn1
        ENDMODULE
    ; can be replaced in v1.14.0 with
        MODULE new
            MODULE version
fn1:            ret    ; final label: @new.version.fn1
            ENDMODULE
        ENDMODULE

Since v1.14.0 the MODULE and ENDMODULE also resets the current "non-local" label prefix back to "_":

Kep:    ; "Kep" label (global one), and also works as "non-local" prefix for local labels
    MODULE zzz
.local: ; in v1.14.0 this will be "zzz._.local" label, previously it was "zzz.Kep.local"
Kup:    ; this is "zzz.Kup", but also defines "non-local" prefix as "Kup"
.local  ; this is "zzz.Kup.local"
    ENDMODULE
.local: ; in v1.14.0 this will be "_.local" label, previously it was "Kup.local"

OPT [push] [reset] [listoff] [liston] [<command line options>]

OPT pop

Allows to reset and modify options affecting syntax and parsing (for lines of source following the OPT). The options allowed for OPT are: --nofakes, --syntax, --zxnext, --reversepop and --dirbol.

Ahead of options you can use OPT commands: push, pop, reset, listoff, liston. The "push" command will make OPT to preserve current state of options. The "reset" command will reset OPT-related options to default state. The "listoff" command will suspend the listing for following lines until "liston" command is used (listing availibility is part of the push/pop state, so to "nest" listing-off you can use "OPT push listoff : ... code ... : OPT pop" code sequence.

Then the provided options are applied. The default values are: fake instructions enabled (no warning), multi-argument delimiter is ",", both () and [] brackets can be used to access memory, labels can have any name, ZX Next instructions are OFF, POP with multiple arguments doesn't reverse them and pseudo-ops at beginning of line are OFF (to just reset to these defaults you can use OPT reset).

The "pop" command: the previously preserved state of options is restored (states are preserved in "stack" way, so further OPT with "pop" will restore older states).

Example 5.35. docs_examples/po_opt.asm

    POP bc, hl   ; pops BC first
    OPT push reset --reversepop --syntax=af
    POP bc,,hl   ; pops HL first
    LD  bc,hl    ; warning about Fake instruction
    LD  bc,hl    ; warning supressed by lowercase "fake" in this comment
    OPT reset --syntax=a
    POP bc,,hl   ; pop BC first (--reversepop was reset)
    OPT pop      ; restoring syntax to original state

ORG <address>[,<page_number>]

Set the program counter to a specific address. If the second argument is provided, it will change memory page in the current slot, see PAGE.

Example 5.36. docs_examples/po_org.asm

    ORG 100h ; or 0x100, or $100, or #100

    ; useful macro that padding code
    MACRO PADORG addr
         ; add padding
         IF $ < addr
         BLOCK addr-$
         ENDIF
         ORG addr
    ENDM

    MACRO PADORG2 addr
         ; add padding + display warning
         IF $ > addr
           ; no padding
           DISPLAY /L, "Warning! PADORG failed! ", $, " is more than ", addr
         ELSE
           ; add padding
           BLOCK addr-$
         ENDIF
         ORG addr
    ENDM


OUTEND

Ends generating compiler output to file specified in OUTPUT and resets SIZE value to default "none".

OUTPUT [<filename>[,<mode>]]

With OUTPUT it is possible to create multiple files from one source. All following instructions will be assembled to this file. It will also close (OUTEND) and finalize any previously opened output.

There are three possible output modes: truncate (overwrite existing files, this is the default), rewind (open and execute FPOS 0) and append (open and leave the file pointer at the end of the file).

OUTPUT <filename>,t  ; truncate (default)
OUTPUT <filename>,r  ; rewind
OUTPUT <filename>,a  ; append

Example 5.37. bigfile.asm

    OUTPUT loader.com
    ORG 100H
    INCLUDE loader.asm
    INCLUDE bios.asm

    OUTPUT bigfile.dat
    ORG 4000H
    INCLUDE main.asm
    ORG 8000H
    INCLUDE data.asm
    OUTEND
    INCLUDE next.asm


This will create two files: loader.com and bigfile.dat.

PAGE <number>

Work only in real device emulation mode. See DEVICE.

Set the current memory page to current slot.

Example 5.38. 

    PAGE 7 ;set 7 page
    SAVEBIN "ram7.bin",$C000,$4000 ;- save $4000 begin from $C000 of RAM to file


PHASE

Synonym of DISP.

REPT <count>

Synonym of DUP. There is also synonym ENDR to end REPT block (EDUP works too).

SAVEBIN <filename>,<startadress>,<lengthofcode>

Works only in real device emulation mode. See DEVICE.

Save the block of RAM.

Example 5.39. 

    PAGE 7 ;set 7 page to current slot
    SAVEBIN "ram7.bin",$C000,$4000 ;- save 4000h begin from C000h of RAM to file
    SAVEBIN "ram2.bin",$8000,$3000 ;- save 3000h begin from 8000h of RAM to file


SAVEDEV <filename>,<startPage>,<startOffset>,<length>

Works only in real device emulation mode. See DEVICE.

Like SAVEBIN, saves the block of device RAM.

But it allows lengths over 64ki, and the offset value goes directly into device virtual memory (where pages are allocated consecutively), ignoring current slot "mapping". I.e. page=2,offset=0 will start saving data from page 2 at its beginning, going through pages 3, 4, 5, ... until the requested length of data is saved.

The offset is not limited to page size, i.e. arguments page=1,offset=0x500 are equal to arguments page=0,offset=0x4500 for ZXSPECTRUM128 device (has page size 0x4000).

Example 5.40. 

    DEVICE ZXSPECTRUM128 : SAVEDEV "fullram.bin",0,0,0x20000 ; save full 128kiB

SAVEHOB <filename>,<filename_in_trdos>,<startadress>,<lengthofcode>

Work only in real device emulation mode. See DEVICE.

Save the block of RAM in Hobeta format.

Example 5.41. 

    PAGE 7 ;set 7 page to current slot
    SAVEHOB "ram7.$c","myfile1.C",$C000,$4000 ;- save 4000h begin from C000h of RAM to file
    SAVEHOB "ram2.$c","myfile2.C",$8000,$3000 ;- save 3000h begin from 8000h of RAM to file


SAVENEX <command> <command arguments>

Commands to build NEX file, for details check SAVENEX chapter.

Works only in ZXSPECTRUMNEXT device emulation mode. See DEVICE.

SAVESNA <filename>[,<startadressofprogram>]

Work only in real device emulation mode. See DEVICE.

Save the snapshot for emulators of ZX-Spectrum. (If start address is omitted, the one provided by END is used)

Example 5.42. 

    DEVICE ZXSPECTRUM128
    ORG $8000
START  .... ;something code
    RET
    SAVESNA "game.sna",START ;save snapshot to file game.sna. Start address is START ($8000)


SAVETAP <filename>,BASIC,<fileintapeheader>,<start>,<length>[,<autorunline>[,<lengthwithoutvars>]]

SAVETAP <filename>,CODE,<fileintapeheader>,<start>,<length>[,<customstartaddress>[,<optional3rdparam>]]

SAVETAP <filename>,NUMBERS,<fileintapeheader>,<start>,<length>[,<variableletter(A..Z)>]

SAVETAP <filename>,CHARS,<fileintapeheader>,<start>,<length>[,<variableletter(A..Z)>]

SAVETAP <filename>,HEADLESS,<start>,<length>[,<customblockflag(0..255)>]

Work only in real device emulation mode. See DEVICE.

Append the tape header or block of data to the end of the standard tape file for emulators of ZX-Spectrum.

Example 5.43. 

    DEVICE ZXSPECTRUM48
    ...
    EMPTYTAP "output.tap"

    SAVETAP "output.tap",BASIC,"noAutorun",label,100
    SAVETAP "output.tap",BASIC,"w/Autorun",label,100,9999
    SAVETAP "output.tap",BASIC,"withVars",label,123,9999,100
    SAVETAP "output.tap",CODE,"bank17",screen,6912
    SAVETAP "output.tap",CODE,"screen",demo,length,org
    SAVETAP "output.tap",NUMBERS,"dimArray",label,100      ; a()
    SAVETAP "output.tap",NUMBERS,"othernum",label,200,'b'  ; b()
    SAVETAP "output.tap",CHARS,"charArray",label,300       ; a$()
    SAVETAP "output.tap",CHARS,"nextone",label,400,'m'     ; m$()
    SAVETAP "output.tap",HEADLESS,start,length


SAVETAP <filename>[,<startadressofprogram>]

Work only in real device emulation mode. See DEVICE.

Save the tape file for emulators of ZX-Spectrum as a "snapshot" of almost-whole memory. Generated tape file supports the ZX-Spectrum clones with extended RAM such as ATM Turbo 512, etc. (If start address is omitted, the one provided by END is used)

The stored memory starts at $5E00 (with screen data at $4000 auto-detected and optionally stored into the TAP file). The stored memory is automatically trimmed of zero values at start and end of the region (put non-zero byte markers around region you want to store, if you have zero-ed bytes at beginning or end of your code).

Example 5.44. 

    DEVICE ZXSPECTRUM48
    ORG $7FFF
    DB $01      ; non-zero marker to store the following zero-ed data
DAT DS 1024, 0  ; zero-ed data at $8000 (1024 bytes)
START  ....     ; some code
    RET
    SAVETAP "game.tap", START ; save tape-snapshot to file game.tap. Start address is $8400


SAVETRD <filename_of_trd_image>,[|]<filename_in_trdos>,<address>,<length>[,<autostart_BASIC_line>]

Works only in real device emulation mode. See DEVICE.

Save the device memory into TRD disk image. Adding pipe character "|" ahead of file name will make sjasmplus to delete old file(s) with the same name, before writing the new one => replace-like functionality. If the deleted file did occupy all sectors till the free space position in disc info, sjasmplus will salvage those sectors back and save new file over them (but it will not do full reshuffle/defrag in more complex cases, sjasmplus is just assembler, not full featured TRD images manipulation tool).

Example 5.45. 

    EMPTYTRD "test.trd" ;create empty TRD image
    PAGE 7 ;set 7 page to current slot
    SAVETRD "test.trd","myfile1.C",$C000,$4000 ;- save 4000h begin from C000h of RAM to file to TRD image
    SAVETRD "test.trd","myfile2.C",$8000,$3000 ;- save 3000h begin from 8000h of RAM to file to TRD image
    SAVETRD "test.trd",|"myfile1.C",$B000,$400 ;- replace "myfile1.C" with new file


SHELLEXEC <filename>[,<parameters>]

Execute external program <filename> using optional command line <parameters>.

Example 5.46. 

    OUTPUT "mybin.bin"
    ;some code
    IF ((_ERRORS = 0) && (_WARNINGS = 0))
        SHELLEXEC "x:/somepath/bin2tap.exe mybin.bin mytap.tap"
       ; or SHELLEXEC "x:/somepath/bin2tap.exe","mybin.bin mytap.tap"
    ENDIF


SIZE <filesize in bytes>

If the resulting file is less than the given length, as many zero bytes are added as necessary. See OUTPUT for more.

Example 5.47. 

    SIZE 32768       ; make sure file will be 32K


SLOT <number>

Work only in real device emulation mode. See DEVICE.

Set current slot. Slot's defined by MEMORYMAP pseudo-op. Use pseudo-op PAGE to change page in the current slot.

Example 5.48. 

    DEVICE ZXSPECTRUM128
    SLOT 3 ;from 0C000h to 0FFFFh
    PAGE 1 ;set page 1 to slot 3
    ORG 0C000h
    ;your program here
    PAGE 2
    INCBIN "somegfx.bin"
    ;....


TAPEND

Ends generating compiler output to tape file block specified in TAPOUT.

TAPOUT <filename>[,<flagbyte>]

Appends one tape block at the end of specified file. All following code will be assembled to this tape file block.

Default value of flagbyte is 255.

Example 5.49. bigfile.asm

    EMPTYTAP screen.tap

    TAPOUT screen.tap,0
    DB 3
    DB 'ScreenName'
    DW 6912
    DW 16384
    DW 32768
    TAPEND

    TAPOUT screen.tap
    INCBIN screen.bin
    TAPEND


This will create tap file with the screen.

TEXTAREA <address>

Synonym of DISP.

UNDEFINE <id>

Removes the identifier defined by DEFINE

Example 5.50. 

    DEFINE Release 1

    IFDEF Release
      DISPLAY "Building release version"
    ENDIF

    UNDEFINE Release

    IFNDEF Release
      DISPLAY "It's works!"
    ENDIF

    IFDEF _SJASMPLUS
      DISPLAY "Yes, it's the sjasmplus!"
    ENDIF

    UNDEFINE *  ; undefine all identifiers

    IFNDEF _SJASMPLUS
      DISPLAY "It's not the sjasmplus??"
    ENDIF


UNPHASE

Synonym of ENT.

WORD <words>

Defines a word. Values should be between -32787 and 65536.

Example 5.51. 

    WORD 4000h,0d000h
    WORD 4,"HA"


Conditional assembly

It may be useful to assemble a part or not based on a certain condition.

IF <expression>

If <expression> is non-zero the following lines are assembled until an ELSE or ENDIF.

IFN <expression>

If <expression> is zero the following lines are assembled until an ELSE or ENDIF.

IFDEF <id>

The condition is true if there is an id defined. These are NOT labels.

Example 5.52. 

    IFDEF MSX_LEAN_AND_MEAN
        CALL InitOwnMM
    ELSE
        CALL InitDos2MemMan
    ENDIF


IFNDEF <id>

The condition is true if there isn't an id defined. These are NOT labels.

Example 5.53. 

1   IN A,(0C4H)
    AND 2
    IFNDEF DEBUG
        JR NC,1B
    ENDIF


IFUSED <label>

The condition is true if there is an label used somewhere in the code. You can create libraries of useful functions using IFUSED pseudo-op

Example 5.54. (similar to tests/misc/ifused_test.asm)

    OUTPUT "TEST.OUT"

    CALL LABEL3 ; LABEL3 - yes
    LD A,(LABEL1) ; LABEL1 - yes

    IFUSED LABEL1
LABEL1:
    DB 1
    ENDIF

    IFUSED LABEL2
LABEL2:
    DB 2
    ENDIF

    IFUSED LABEL3
LABEL3:
    DB 3
    ENDIF

    IFUSED LABEL4
LABEL4:
    DB 4
    ENDIF

    LD A,LABEL2 ; LABEL2 - yes

    RET

; Output will contain bytes from LABEL1 to LABEL3 (1, 2, 3), but not contain from LABEL4, because this label is not used.

; Alternative syntax:
LABEL5:
    IFUSED ; sjasmplus will use name of previous label, i.e. LABEL5

    ENDIF
    


Known bug: when code is using label inside module "moduleX", like call labelY, only usage of moduleX.labelY label is noted. Then if you define "labelY" outside of module and hide it inside IFUSED labelY block, the call from module will be unable to find the routine.

Workaround: you can use the global-label operator @: "call @labelY" to trigger usage of the global "labelY", or you can use the alternative IFUSED syntax "labelY: IFUSED" which does not only check condition, but also does define the label. Once the label is defined, the "call labelY" line inside module will find the global variant and mark it as "used" correctly.

IFNUSED <label>

The condition is true if there is an label not used somewhere in the code.

ELSE

See IF. If the condition is not true, the else-part is assembled.

ENDIF

Every IF should be followed by an ENDIF.

Macros

The MACRO pseudo-op defines a macro. It should be followed by the name of the macro, optionally followed by the parameters. The following lines will be stored as the macro-body until an ENDM pseudo-op is encountered. Macro's have to be defined before their use.

Example 5.55. Macro without parameters (docs_examples/s_macros.asm)

  MACRO ADD_HL_A
    ADD A,L
    JR NC,.hup
    INC H
.hup
    LD L,A
  ENDM


Labels in a macro starting with a dot are local to each macro expansion.

Example 5.56. A macro with parameters (docs_examples/s_macros.asm)

  MACRO WAVEOUT reg, data
    LD A,reg
    OUT (7EH),A
    LD A,data
    OUT (7FH),A
  ENDM
; this macro will make
  WAVEOUT 2,17
; expand to:
  LD A,2
  OUT (7EH),A
  LD A,17
  OUT (7FH),A


Example 5.57. Another example (docs_examples/s_macros.asm)

    MACRO LOOP
      IF $-.lus<127
        DJNZ .lus
      ELSE
        DEC B
        JP NZ,.lus
      ENDIF
    ENDM

Main
.lus
    CALL DoALot
    LOOP
; This will expand to:
Main
.lus                  ; Main.lus
    CALL DoALot
    DJNZ .lus         ; Main.lus


Angle brackets can be used when the arguments contain commas.

Example 5.58. Argument in angle brackets (docs_examples/s_macros.asm)

    MACRO UseLess data
      DB data
    ENDM

    UseLess <10,12,13,0>
; expands to:
    DB 10,12,13,0

; use '!' to include '!' and '>' in those strings.

    UseLess <5, 6 !> 3>
; expands to:
    DB 5, 6 > 3

    UseLess <"Kip!!",3>
; expands to:
    DB "Kip!",3


As compatibility convenience to make porting from different assemblers somewhat easier, there is alternative syntax, where the macro name is written at beginning of line (as if label, but MODULE part is NOT applied to macro name).

Example 5.59. Macro name at beginning of line (docs_examples/s_macros.asm)

LabelAsMacroName    MACRO  arg1?, arg2?
                        ld  a,arg1?
                        ld  hl,arg2?
                    ENDM

                LabelAsMacroName 1,$1234
            ; expands to:
                ld a,1 : ld hl,$1234


If some macro over-shadows regular instruction or directive name, the @ character in front of instruction/directive name can be used to inhibit macro expansion.

Example 5.60. Inhibit macro expansion operator (docs_examples/s_macros.asm)

djnz    MACRO   arg1?
            dec c
            jr  nz,arg1?
            @djnz arg1? ; avoid self-reference and use real instruction
        ENDM

1:      djnz    1B      ; macro replacement will be used here
1:      @djnz   1B      ; original djnz instruction here


Chapter 6. Structures

What is it?

Structures can be used to define data structures in memory more easily. The name of the structure is set to the total size of the structure.

Defining structure

A structure definition starts with: STRUCT <name>[,<initial offset>] and ends with ENDS. Structure definitions are local to the current module, but, as with labels, '@' can be used to override this.

Lines between STRUCT and ENDS should have the following format:

membername pseudo-operation operands

All fields are optional. Lines without label should start with whitespace.

When non zero offset is used, it acts as if BLOCK with length equal to offset was defined as first member of structure.

Instructions

Between the STRUCT and ENDS pseudo-instructions the following instructions can be used:

BYTE [<defaultvalue>]

To define a one byte member. The defaultvalue is used when no initialisation value is given when the structure is declared. (DB and DEFB may be used instead of BYTE).

WORD [<defaultvalue>]

To define a two byte member. The defaultvalue is used when no initialisation value is given when the structure is declared. (DW and DEFW may be used instead of WORD).

D24 [<defaultvalue>]

To define a three byte member. The defaultvalue is used when no initialisation value is given when the structure is declared.

DWORD [<defaultvalue>]

To define a four byte member. The defaultvalue is used when no initialisation value is given when the structure is declared. (DD and DEFD may be used instead of DWORD).

BLOCK <length>[,<fillbyte>]]

To define a member of the specified number of bytes. Arguments are set when defining the current structure, and are not part of init values when the structure is later used. ('#', DS and DEFS may be used instead of BLOCK).

(since v1.11) If fillbyte is omitted, the device memory content in the block area is preserved (not zeroed).

ALIGN [<expression>[, <byte>]]

To align the offset. If the expression is omitted, 4 is assumed. ('##' May be used instead of ALIGN).

(since v1.11) If the byte is omitted, device memory content is preserved (not zeroed).

<structure name> [<init values>]

It is possible to nest structures, and give new defaults for the BYTE and WORD members.

Usage of defined structure

[<label>] <struct_name> [<initial values>] will emit full structure into machine code, either using default values from structure definition, or overriding them with explicit value from the <initial values> list. In initial values you can use curly brackets {} to group particular initial values for particular sub-structure, any missing values in particular sub-structure init-list are set up by default values of particular field. See "SDOT" example below or tests/struct asm files for more examples.

<label> <struct_name> = <expression> will only set up <label>.<struct_field> labels starting from designed address provided by expression, but there will be no machine code emitted (and current address "$" will not advance).

Examples

Example 6.1. docs_examples/c_structures.asm

	STRUCT SCOLOR
RED	BYTE 4
GREEN	BYTE 5
BLUE	BYTE 6
	ENDS

This is identical to:

SCOLOR		EQU 3 ; length
SCOLOR.RED	EQU 0 ; offset
SCOLOR.GREEN	EQU 1 ; offset
SCOLOR.BLUE	EQU 2 ; offset

Example 6.2. docs_examples/c_structures.asm

	STRUCT SDOT
X	BYTE
Y	BYTE
C	SCOLOR 0,0,0 ; use new default values
	ENDS

This is identical to:

SDOT		EQU 5 ; length
SDOT.X		EQU 0 ; offset
SDOT.Y		EQU 1 ; offset
SDOT.C		EQU 2 ; offset
SDOT.C.RED	EQU 2 ; offset
SDOT.C.GREEN	EQU 3 ; offset
SDOT.C.BLUE	EQU 4 ; offset

Example 6.3. docs_examples/c_structures.asm

	STRUCT SPOS,4
X	WORD
Y	BYTE
	ALIGN 2
AD	WORD
	ENDS

This is identical to:

SPOS	EQU 10 ; length
SPOS.X	EQU  4 ; offset
SPOS.Y	EQU  6 ; offset
SPOS.AD	EQU  8 ; offset

Example 6.4. docs_examples/c_structures.asm

When a structure is defined it is possible to declare labels with it

COLOR SCOLOR

This is identical to:

COLOR
COLOR.RED   BYTE 4
COLOR.GREEN BYTE 5
COLOR.BLUE  BYTE 6

Note the default values.

Or without label:

COLORTABLE
  SCOLOR 0,0,0
  SCOLOR 1,2,3
  SCOLOR ,2
  ; etc.

This is identical to:

COLORTABLE
  BYTE 0,0,0
  BYTE 1,2,3
  BYTE 4,2,6
  ; etc.

DOT1 SDOT 0,0, 0,0,0     ; or 0,0,0,0,0 or {0,0,{0,0,0}}

Only BYTE and WORD members can be initialised.

The resulting labels can be used as any other label:

  ld b,(ix+SCOLOR.RED)
  ld a,(COLOR.GREEN)
  ld de,COLOR
  ; etc.

Example 6.5. docs_examples/st_usage_example.asm

	STRUCT BIN_FILE_MAP, 256
value1	BYTE
value2	WORD
	ENDS

        ORG  0x8000
binData BIN_FILE_MAP = $        ; set up label values only (no bytes)
        INCBIN "some_data.bin"  ; load the bytes from file instead

        ; using the data through struct definition
        ld  a,(binData.value1)
        ld  hl,(binData.value2)

This is identical to:

BIN_FILE_MAP            EQU 259      ; length
BIN_FILE_MAP.value1     EQU 256      ; offset
BIN_FILE_MAP.value2     EQU 257      ; offset
; labels to access binary data loaded by INCBIN
binData                 EQU 0x8000   ; address
binData.value1          EQU 0x8100   ; address
binData.value2          EQU 0x8101   ; address

Warning

Do not use the offset labels in indirections like:

LD A,(SDOT.X)

This will conflict with futher 'improvements' ;-)

If this is absolutely necessary (why?) use something like this:

LD A,(+SDOT.X)

Chapter 7. Lua scripting

Why?

Why is scripting engine as Lua embedded to the compiler? Answer is simple: It need to add extra features by users. And to whole other Lua is enough small, fast and powerful scripting engine.

How to use?

You must use LUA and ENDLUA pseudo-ops.

Example 7.1. Hello World!

    LUA
        print ("Hello World!")
    ENDLUA


SjASMPlus binded functions

From Lua you can control some variables and use functions of the compiler. Complete list:

[integer] _c("expression")

Calculate expression using calculator of the compiler. Example: val = _c("SOMELABEL+12").

[void] _pc("code")

Parse string of Z80 assembly. Example: _pc("ADD A,B")

[void] _pl("label code")

Parse line of Z80 assembly. Example: _pc("SOMELABEL ADD A,B")

[integer] sj.calc("expression")

See _c

[void] sj.parse_code("code")

See _pc

[void] sj.parse_line("label code")

See _pl

[void] sj.error("message")

Print error message.

[void] sj.warning("message")

Print warning message.

[boolean] sj.file_exists("filename")

Check for file exists.

[string] sj.get_define("name")

Get define value.

[boolean] sj.insert_define("name", "value")

Add new define.

[integer] sj.get_label("name")

Get label address.

[boolean] sj.insert_label("name", address)

Add new label.

[integer] sj.current_address

Variable. Current address.

[integer] sj.error_count

Variable. Count of Errors.

[integer] sj.warning_count

Variable. Count of Warnings.

[void] sj.exit(errorcode)

Shutdown the compiler.

[void] sj.add_byte(byte)

Add byte to output (or to memory) and increase sj.current_address

[void] sj.add_word(word)

Add word to output (or to memory) and twice increase sj.current_address

[integer] sj.get_byte(address)

Get byte from memory. Work only in real device emulation mode.

[integer] sj.get_word(address)

Get word from memory. Work only in real device emulation mode.

[string] sj.get_device()

Return current emulating device's identifier. Returns "NONE" if no emulation mode.

[boolean] sj.set_device("id")

Set current emulating device's identifier. Returns false if no device found.

[boolean] sj.set_page(number)

Set page with number "number" to the current slot. Works as pseudo-op PAGE.

[boolean] sj.set_slot(number)

Set current slot with number "number". Works as pseudo-op SLOT.

[void] sj.shellexec("programname")

See pseudo-op SHELLEXEC.

[void] zx.trdimage_create("filename")

Creates emptry TRD image file.

[void] zx.trdimage_add_file("filename", "somenameC", startaddress, length, autostart, replace)

Save block of memory to TRD image file. Work only in real device emulation mode.

[void] zx.save_snapshot_sna("filename.sna", startaddressofprogram)

Save snapshot of memory in SNA format. Work only in real device emulation mode and only for ZXSPECTRUM48 and ZXSPECTRUM128..

Third-party embedded library(ies)

lpack.c

a Lua library for packing and unpacking binary data

by Luiz Henrique de Figueiredo <lhf(at)tecgraf.puc-rio.br>

The library adds two functions to the string library: string.pack and string.unpack.

pack is called as follows: string.pack(F,x1,x2,...), where F is a string describing how the values x1, x2, ... are to be interpreted and formatted. Each letter in the format string F consumes one of the given values. Only values of type number or string are accepted. pack returns a (binary) string containing the values packed as described in F. The letter codes understood by pack are listed in lpack.c (they are inspired by Perl's codes but are not the same). Numbers following letter codes in F indicate repetitions.

unpack is called as follows: string.unpack(s,F,[init]), where s is a (binary) string containing data packed as if by pack, F is a format string describing what is to be read from s, and the optional init marks where in s to begin reading the values. unpack returns one value per letter in F until F or s is exhausted (the letters codes are the same as for pack, except that numbers following 'A' are interpreted as the number of characters to read into the string, not as repetitions).

The first value returned by unpack is the next unread position in s, which can be used as the init position in a subsequent call to unpack. This allows you to unpack values in a loop or in several steps. If the position returned by unpack is beyond the end of s, then s has been exhausted; any calls to unpack starting beyond the end of s will always return nil values.

List of types for F string:

z

zero-terminated string

p

string preceded by length byte

P

string preceded by length word

a

string preceded by length size_t

A

string

f

float

d

double

n

Lua number

c

char

b

byte = unsigned char

h

short = word

H

unsigned short

i

int

I

unsigned int

l

long

L

unsigned long

<

little endian

>

big endian

=

native endian

Example

Example 7.2. Variables doesn't clear in new passes of the compiler

    LUA PASS1
       v = 1
    ENDLUA

    LUA PASS2
       print (v)
-- out to console: 1
       v++
    ENDLUA

    LUA PASS3
       print (v)
-- out to console: 2
    ENDLUA


Example 7.3. To generate some code you need to generate it in all passes

    LUA ALLPASS
        _pl("ClearScreen LD (.savesp+1),SP")
        _pc("LD SP,16384+6144")
        _pc("LD HL,0")
        for i = 32768, 38912, 2 do
            _pc("PUSH HL")
        end
        _pl(".savesp: LD SP,0")
        _pc("RET")
    ENDLUA


Example 7.4. Declare function and use it

     LUA
         function savetape_mytype(filename, startaddress)
             local fp
             fp = assert(io.open(fname, "wb"))
             for i = 16384, 32767, 4 do
                 assert(fp:write( string.pack("bbbb",
                                sj.get_byte(i),
                                sj.get_byte(i+1),
                                sj.get_byte(i+2),
                                sj.get_byte(i+3)) ))
             end
             assert(fp:flush())
             assert(fp:close())
         end
     ENDLUA

 ;somewhere in your program
     LUA
         savetape_mytype("tapefiles/myprogram.tape", _c("StartGameLabel"))
     ENDLUA


Example 7.5. Simple sample :)

	LUA
-- Function reads number from file <fname>, increases it, creates define "BUILD" with the number and saves the number to <fname>.
-- With this function you can control count of compilations.
	function increase_build(fname)
		local fp
		local build
		fp = assert(io.open(fname, "rb"))
		build = tonumber(fp:read("*all"))
		assert(fp:close())
		if type(build) == "nil" then
		    build = 0
		end
		build = build + 1;
		sj.insert_define("BUILD", build)
		fp = assert(io.open(fname, "wb"))
		assert(fp:write( build ))
		assert(fp:flush())
		assert(fp:close())
	end

-- Before using you must create empty file "build.txt"!
	increase_build("build.txt")

-- Creates define "TIME" with current time
	sj.insert_define("TIME", '"' .. os.date("%Y-%m-%d %H:%M:%S") .. '"')
	ENDLUA

; print to console our time and build number
	DISPLAY "Build time: ", TIME
	DISPLAY "Build number: ", /D, BUILD


Chapter 8. SAVENEX guide

NEX File Format

NEX is binary format for ZX Spectrum Next, aiming to provide simple delivery of software for the platform. For file format details check https://specnext.dev/wiki/NEX_file_format. In short it is header + loading screen + like-snapshot binary and remaining resources appended after.

As such, the SAVENEX commands are available only in ZXSPECTRUMNEXT device emulation mode, see DEVICE.

As the file is designed for self-contained distribution of whole applications/games, its configuration and assembling is a bit too complex for single directive, and the configuration is instead divided into multiple commands, and the assembling goes through multiple stages, so some commands must be used in correct sequence.

While the format technically allows to include multiple screen types data, they are all loaded at the beginning over each other, so it makes sense to provide only single loading screen (sjasmplus enforces that).

Detailed description of each SAVENEX command

SAVENEX OPEN <filename>[,<startAddress>[,<stackAddress>[,<entryBank 0..111>]]]

Opens a NEX file, defines start address, stack address and 16k bank to be mapped at 0xC000 before code is executed (if values are omitted, start address is zero = no start, stack address is 0xFFFE, entryBank is zero).

Only single NEX file can be open at the same time, and to finalize the header content the command CLOSE has to be used (does auto-close if source ends).

Entry bank is number of 16k bank (0..111), not native 8k page, default is zero, i.e. the default memory map is identical to ZX 128 (ROM, RAM banks 5, 2 and 0).

Make sure your new stack has at least tens of bytes available as those will be used already by the NEX loader before executing your entry point (although released back).

SAVENEX CORE <major 0..15>,<minor 0..15>,<subminor 0..255>

Set minimum required Next core version, can be set any time before CLOSE.

SAVENEX CFG <border 0..7>[,<fileHandle 0/1/$4000+>[,<PreserveNextRegs 0/1>[,<2MbRamReq 0/1>]]]

Set border colour (during loading), whether the machine should be set to default state (PreserveNextRegs = 0 = default), if the app requires extended RAM (224 8k pages) and how the file handle of the NEX file should be treated: 0 = default = close, 1 = keep open and pass in BC, $4000..$FFFE = keep open, and write into memory at provided address (after entry bank is paged in). This can be set any time before CLOSE.

SAVENEX BAR <loadBar 0/1>,<barColour 0..255>[,<startDelay 0..255>[,<bankDelay 0..255>]]

Loading-bar related setup ("colour" usage depends on screen mode), can be set any time before CLOSE.

SAVENEX SCREEN L2 [<Page8kNum 0..223>,<offset>[,<palPage8kNum 0..223>,<palOffset>]]

Layer 2 loading screen, can be used between OPEN and first AUTO/BANK command.

Palette consists of 512 bytes (256 palette items from index 0), in 9b colour format: first byte is %RRRGGGBB, second byte is %P000000B (P is priority flag for Layer 2 colours).

Image data are 48kiB block of memory, the loader will use always banks 9..11 to display it (8k pages 18..23), but if you will prepare the data there, it will be also re-saved by AUTO command, so either use other banks, and overwrite them with valid data/code after using the SCREEN command, or reset pages 18..23 to zero after SCREEN.

If no memory address is specified, the pages 18..23 are stored in file, and if no palette address is specified, no-palette flag is set in NEX file.

SAVENEX SCREEN LR [<Page8kNum 0..223>,<offset>[,<palPage8kNum 0..223>,<palOffset>]]

LoRes (128x96) loading screen, can be used between OPEN and first AUTO/BANK command.

Palette is similar to Layer 2 mode, just LoRes mode doesn't have priority bit.

Image data are 12288 bytes memory block - either consecutive block if specific address is provided, or without address the actual bank 5 memory is stored (taking 6144 bytes from address 0x4000 and 6144 bytes from address 0x6000).

SAVENEX SCREEN (SCR|SHC|SHR) [<hiResColour 0..7>]

ULA/Timex modes loading screen, can be used between OPEN and first AUTO/BANK command.

The actual bank 5 memory (pages 10..11) is stored as if the image is displayed, in these modes the palette can't be specified.

SCR is classic ZX 6912 bytes long screen from address 0x4000 (page 10 is used, even if the slot 1 is modified to other page, so you must prepare the image "in place").

SHC and SHR are Timex HiColor (8x1 attribute) and HiRes (512x192 bitmap) modes, prepare data "in place", i.e. 6144 bytes into page 10 and 6144 bytes into page 11 (0x4000 and 0x6000 addresses in default memory setup). For HiRes mode you should specify ink colour (the paper is complement of ink).

SAVENEX BANK <bank 0..111>[,...]

Can be used after OPEN or SCREEN and before CLOSE, but the 16ki banks must be saved in correct order: 5, 2, 0, 1, 3, 4, 6, 7, 8, 9, 10, ..., 111

SAVENEX AUTO [<fromBank 0..111>[,<toBank 0..111>]]

Can be used after OPEN or SCREEN and before CLOSE. The sjasmplus will save every 16k bank containing at least one non-zero byte; detected in the correct order (automatically will save every non-zero 16k bank detected in the correct order (automatically starting from first possible bank after previous BANK/AUTO commands, or from provided "fromBank").

For "fromBank" value use the specified order above in BANK command, i.e. 5, 2, 0, ...

SAVENEX CLOSE [<fileToAppend>]

Can be used after OPEN. The currently open NEX file will be finalized (header adjusted), and optional extra file just appended to the end of NEX file.

Examples

Example 8.1. docs_examples/s_savenex_examples.asm

Creating NEX file which will have Layer2 loading screen (stripes), progress bar, and will enter infinite loop with calling stack (used by IM 1 interrupt handler) visible on the Layer 2 screen.

    DEVICE ZXSPECTRUMNEXT
    ORG $7E00
start:  ei : jr $           ; app code entry point, BC = NEX file handle
    ; Layer2 screen (top 1/3 defined, bottom of it will be used also as "visible" stack)
    ORG $C000 : DUP 64*32 : DB $90,$91,$92,$93,$94,$95,$96,$97 : EDUP

    ; write everything into NEX file
    SAVENEX OPEN "example.nex", start, $FFFE, 9  ; stack will go into Layer2
    SAVENEX CORE 2, 0, 0        ; Next core 2.0.0 required as minimum
    SAVENEX CFG 4, 1            ; green border, file handle in BC
    SAVENEX BAR 1, $E0, 50, 25  ; do load bar, red colour, start/load delays 50/25 frames
    SAVENEX SCREEN L2 0, 0      ; store the data from C000 (page 0, offset 0), no palette
    SAVENEX BANK 5, 100, 101    ; store the 16ki banks 5 (contains the code at 0x7E00), 100, 101
    SAVENEX CLOSE               ; (banks 100 and 101 are added just as example)
		  

Chapter 9. Source Level Debugging (SLD) data

What is it?

SLD data are extra "tracing" data produced during assembling for debuggers and IDEs, similar to "map" files already supported by sjasmplus ( LABELSLIST and CSPECTMAP).

The debugger can read these data, and with non-tricky source producing machine code with correct device memory mapping, the debugger can trace the origins of every instruction back to the original source code line, and display the source instead/along the disassembly view (the "map" files mentioned above provide only list of labels which is usually already super helpful, but can't track the source origins of each instruction).

The original impulse and working patch for this feature came from Chris Kirby, adding the single-instruction-step feature to his development tools: Next Development System (currently in 2019 under heavy development).

Usage

On the sjasmplus side all you need is to add another command line option when starting the assembler.

prompt$ sjasmplus --sld=project.sld.txt file1.asm file2.asm

This will produce along regular files also file `project.sld.txt` containing the tracing data. The file format is text-like, so the content can be viewed in any text editor, but it's supposed to be processed by the debugger.

The SLD data are being exported only for machine code produced within one of the virtual devices (see DEVICE). And the accuracy of the data directly depends on the state of the virtual device at the moment when the particular instruction is assembled, see next section for further advice how to get best tracing data.

If the option `--sld` without explicit filename is used, the first input source filename will be copied and its extension changed to `.sld.txt`, which should work well for single-main file type of projects.

How to write "non tricky" source

For best results (tracing data covering all your instructions and making source-level debugging available all the time):

Generate machine code only with instructions, i.e. while `db 0` in source will produce `nop` instruction, the tracing data for such `nop` will be missing. Only source line containing the "nop" will emit both the zero byte into machine code and tracing data.

Keep the memory map of the virtual device as it will be set at run-time when writing particular code. While using only regular ORG to place the code in desired memory area, keeping only the modified area with memory pages mapped-in is enough to get correct SLD data - but when using DISP directive to generate code in displaced way, you need to map-in correct pages in both memory areas (where the code is currently assembled, and where it is supposed to be operational at run-time - currently impossible if the two areas share the same SLOT area).

To map-in correct pages you can use directives: SLOT, PAGE, MMU and ORG

Only labels defined in regular way get also the memory-page data in SLD file, EQU values are exported without the page information, even if they represent memory address:

regularLabel:   nop             ; current page exported to SLD data
equLabel        EQU   $8000     ; only value without page is exported
varSymbol       =     14        ; =/DEFL labels are omitted completely

Re-using the same memory area (page + address) for two different pieces of code (for example by having multiple routines targeting the same address with DISP, and run-time code uploading the correct variant dynamically at the destination) will highly likely confuse the debugger, producing two source-origins data for the identical address. You may want to avoid this, especially with early version of tools not being able to resolve such ambiguity in reasonable way (the sjasmplus will generate tracing data ok even in this case, but they are tricky to interpret).

If you are using the colons to put multiple instructions on single line, verify the debugger can cope with the tracing data containing full line + column begin/end information, the simple/early tools will likely highlight only whole line of source. For a start keeping single instruction per line may keep things simple.

The multi-arg instructions produce the tracing data only for the first instruction, which will probably cause problems with single-stepping through such code. Rather avoid multi-arg syntax when you are planning to debug the code.

The various code generators written in macros or Lua scripts will produce accurate tracing data in case of macros, and condensed (pointing at "ENDLUA" line) data for Lua generators, but it still depends on the debugger if it can display both top level source code triggering the generator and source lines containing definitions of particular instructions.

SLD File Format definition (version "0")

The SLD data is text-file (sjasmplus is using UNIX-like newlines 0x0A, but parsers should rather cope with any of common EOL scheme). The general format is CSV-like using pipe character as delimiter between fields.

If the first field is empty, the line is one of the special control lines - the second field selects type of control line. If also the second field is empty, the remaining part of line should be ignored (comment line). Currently only one type of "control line" exists, the "SLD.data.version" (third field is integer number):

|SLD.data.version|0
||anything ... comment-like line

The file format version line should be always first line of the SLD data file.

The regular tracing data lines have fixed amount of fields: eight. The seventh field contains single uppercase letter defining type of the line, which affects sub-variants of format for eighth field, but first six fields share the same formatting and meaning across all types of regular lines:

<source file>|<src line>|<definition file>|<def line>|<page>|<value>|<type>|<data>

<source file> - file name of top-level source emitting the instruction/label/device.

<src line> - line and characters in top-level source file, the precise format is "<line>[:<column begin>[:<column end>]]" where first number is line number (starting at 1). The two following numbers delimited by colon are optional, representing the column where the segment starts/ends at the line. The column values are starting from 1 too, the "end" column is pointing beyond the current segment. The columns are in "bytes", i.e. tabulator does increase the column value by +1 only, and the "end" may actually point well beyond "strlen(line)".

<definition file> - file name of the source defining the particular instruction (for example where the instruction inside MACRO was originally defined). If empty, the definition-file name is identical to <source file> (common case for single-source projects).

<def line> - zero when there is no extra "definition" of instruction involved. When non-zero, the format is identical to <src line>, but with regard to the file specified in <definition file> field.

<page> - number of memory page where the <value> address points to, or -1 when value is not memory address.

<value> - any 32 bit integer value (EQU), but when representing memory address, it is full 16 bit value from Z80 address space (the top bits beyond the memory page size contains the number of "slot" where the instruction/label resides).

<type> - single uppercase letter representing type of line, current types: T (instruction Trace), F (Function), D (equ Data), Z (device memory model)

<data> - extra data specific for particular line type. Empty for "T" lines, contains label/symbol name for types "F" and "D". The "D" values usually don't have page defined (equals -1), but the tools should accept also valid page value for "D" type too, just like for "F" lines.

For type "Z" the data field contains string describing the device memory model which is being selected in source and should be applied for following SLD data lines. The memory model string format is:

pages.size:<page size>,pages.count:<page count>,slots.count:<slots count>[,slots.adr:<slot0 adr>,...,<slotLast adr>]
// unsigned <page size> is also any-slot size in current version.
// unsigned <page count> and <slots count> define how many pages/slots there are
// uint16_t <slotX adr> is starting address of slot memory region in Z80 16b addressing

Example 9.1. Example of SLD file

|SLD.data.version|0
|| ZX Spectrum Next device description:
toplevel.asm|59||0|-1|-1|Z|pages.size:8192,pages.count:224,slots.count:8,slots.adr:0,8192,16384,24576,32768,40960,49152,57344
|| label "main" points to 32768, with page 14 mapped-in (defined in toplevel.asm:62)
toplevel.asm|62||0|14|32768|F|main
|| instruction opcode at 32768 (page 14) was created by toplevel.asm:64
toplevel.asm|64||0|14|32768|T|
|| instruction opcode at 32769 (p 14) was created by toplevel.asm:67
|| (but it is a line using macro, the instruction was defined by toplevel.asm:52)
toplevel.asm|67||52|14|32769|T|
|| instruction opcode at 32770 (p 18) was created by toplevel.asm:68:12-24
toplevel.asm|68:12:24||0|18|32770|T|
|| instruction opcode at 32771 (p 18) was created by toplevel.asm:68:24 (till EOL)
toplevel.asm|68:24||0|18|32771|T|
|| "PORT_NUMBER EQU 254" defined at toplevel.asm:69
toplevel.asm|69||0|-1|254|D|PORT_NUMBER
|| label+instruction emitted from toplevel.asm:70, but defined in include.asm:3
toplevel.asm|70|include.asm|3|37|40976|F|0>macro_defined_in_include_asm
toplevel.asm|70|include.asm|3|37|40976|T|