Skip to content

Directives

Failure

Not all directives have their synopsis, explanation, and examples

LIST, NOLIST

Example:

    org 0
    LIST

    ld a, 0
    ; visible in the listing output

    NOLIST

    ld a, 1
    ; Not visible in the listing ouput

ALIGN

Example:

    org 0x1234

        align 256
        assert $ == 0x1300

        align 256
        assert $ == 0x1300

        nop

        align 128, 3
        assert $ == 0x1300 + 128

CONFINED

Confine a memory area of 256 bytes maximum in such a way that it is always possible to navigate in the data by only modifying the low byte address (i.e INC L always works).

CONFINED
  LISTING
ENDCONFINED
;;
; Confined directive is inspired by confine from rasm.
; I guess confined is more  ergonomic has it does not requires to manually specify the size of the confined area

    org 0x0000

    CONFINED
        assert $ == 0
        defs 128, 0xff
    ENDCONFINED

    CONFINED
        assert $ == 256
        defs 200, 0xff
    ENDCONFINED

    CONFINED
        assert $ == 256 + 200
        defs 20, 0xff
    ENDCONFINED

ORG

LIMIT

On the code space ($), not physical space ($$)

Example of code that assembles:

    org 0x100

    limit 0x102
    print {hex}$ : db 1 ; written in 0x100
    print {hex}$ : db 2 ; written in 0x101
    print {hex}$ : db 3 ; written in 0x102
    ;print {hex}$ : db 4 ; written in 0x103 => must fail

Example of code that fails:


PHASE, DEPHASE

; https://k1.spdns.de/Develop/Projects/zasm/Documentation/z71.htm
    org 0x100

label_100
    nop
label_101

    assert $$ == 0x101
    assert $ == 0x101

    phase 0x200

    assert $$ == 0x101
    assert $ == 0x200

label_200
    nop
label_201

    dephase
label_102

    assert label_100 == 0x100
    assert label_101 == 0x101
    assert label_102 == 0x102
    assert label_200 == 0x200
    assert label_201 == 0x201

PROTECT

Synopsis:

PROTECT START, STOP
Description: Mark the memory between START and STOP as protected against write. Assembler fails when writting there.

On the code space ($), not physical space ($$)

Example:

    org 0x4000

    protect 0x8000, 0xbfff

    defb "No memory issue"

RANGE, SECTION

Description: RANGE allows to define named portion of the memory, while SECTION allows to chose the portion of interest.

Example:

; sarcasm inspiration https://www.ecstaticlyrics.com/electronics/Z80/sarcasm/

range $0080, $3FFF, code
range $4000, $7FFF, data

section code
  ld hl, message_1
  call print_message

section data
message_1: db "This is message #1.", $00

section code
  ld hl, message_2
  call print_message

section data
message_2: db "This is message #2.", $00

section code
  print_message: 
    ld a, (hl)
    or a
    ret z
    call 0xbb5a
    inc hl
    jr print_message

    assert section_start("data") ==  0x4000
    assert section_length("data") == 0x4000
    assert section_used("data") == 40

BANK

Description:

When used with no argument, a bank corresponds to a memory area outside of the snapshot. All things read&write in memory are thus uncorrelated to the snapshot. Sections do not apply in a bank.

BANK page is similar to WRITE DIRECT -1 -1 page

Synopsis:

BANK [EXPRESSION]

Example:

    ; Set up a unique value in various banks
    BANK 0xc0
    org 0x4000 + 0
    db 0xc0

    BANK 0xc4
    org 0x4000 + 1
    db 0xc4


    BANK 0xc5
    org 0x4000 + 2
    db 0xc5

    BANK 0xc6
    org 0x4000 + 3
    db 0xc6


    BANK 0xc7
    org 0x4000 + 4
    db 0xc7


    BANKSET 0
    assert memory(0x4000 + 0) == 0xC0 

    BANKSET 1
    assert memory(0x4000 + 2) == 0xC5
    assert memory(0x8000 + 3) == 0xC6
    assert memory(0xC000 + 4) == 0xC7

BANKSET

Synopsis:

BANKSET EXPRESSION

Example:

    BANKSET 0

    org 0x0000
    db 1,2,3,4

    org 0x4000
    db 5,6,7,8

    org 0x8000
    db 9,10,11,12

    org 0xc000
    db 13, 14, 15, 16


    BANKSET 1
    org 0x0000
    db 10,20,30,40

    org 0x4000
    db 50,60,70,80

    org 0x8000
    db 90,100,110,120

    org 0xc000
    db 130, 140, 150, 160


    BANKSET 0
    assert memory(0x0000) == 1
    assert memory(0x4000) == 5
    assert memory(0x8000) == 9
    assert memory(0xc000) == 13

    save "good_bankset_0_0.o", 0x0000, 4
    save "good_bankset_0_1.o", 0x4000, 4
    save "good_bankset_0_2.o", 0x8000, 4
    save "good_bankset_0_3.o", 0xc000, 4

    BANKSET 1
    assert memory(0x0000) == 10
    assert memory(0x4000) == 50
    assert memory(0x8000) == 90
    assert memory(0xc000) == 130

    save "good_bankset_1_0.o", 0x0000, 4
    save "good_bankset_1_1.o", 0x4000, 4
    save "good_bankset_1_2.o", 0x8000, 4
    save "good_bankset_1_3.o", 0xc000, 4

WRITE DIRECT

Description: WRITE DIRECT is a directive from Winape that we have not fully reproduced. It's two first arguments need to be -1.

Example:

    WRITE DIRECT -1, -1, 0xc0
    org 0x4000+0
    db 0xc0

    WRITE DIRECT -1, -1,  0xc4
    org 0x4000+1
    db 0xc4


    WRITE DIRECT -1, -1,  0xc5
    org 0x4000+2
    db 0xc5

    WRITE DIRECT -1, -1,  0xc6
    org 0x4000+3
    db 0xc6


    WRITE DIRECT -1, -1,  0xc7
    org 0x4000+4
    db 0xc7


    BANKSET 0
    assert memory(0x4000 + 0) == 0xC0 

    BANKSET 1
    assert memory(0x4000 + 2) == 0xC5
    assert memory(0x8000 + 3) == 0xC6
    assert memory(0xC000 + 4) == 0xC7

=, SET

Description:

Assign an expression to a label. Assignement can be repeated several times.

Synopsis:

LABEL = EXPRESSION

Example:

label = 1
label =3
.label=2

    assert label == 3


label = 5
    assert label == 5

label <<= 1
    assert label == 10

EQU

Description: Assign an expression to a label. Assignement cannot be repeated several times.

Synopsis:

LABEL = EXPRESSION

Example:

label = 1
label =3
.label=2

    assert label == 3


label = 5
    assert label == 5

label <<= 1
    assert label == 10

MAP

MAP VALUE define a map counter to the required value.#` is used to assign the value to a given label and increment it of the appropriate amount.

Example:

    ; extract stolen here https://github.com/GuillianSeed/MetalGear/blob/master/Variables.asm#L10
            map #c000

GameStatus:     # 1
GameSubstatus:      # 1
ControlConfig:      # 1
                                ; Bit6: 1=Enable music/Player control
TickCounter:        # 1
WaitCounter:        # 1
TickInProgress:     # 1
ControlsTrigger:    # 1
                                ; 5 = Fire2 / M,  4 = Fire / Space, 3 = Right, 2 = Left, 1 = Down, 0 = Up
ControlsHold:       # 1
                                ; 5 = Fire2 / M,  4 = Fire / Space, 3 = Right, 2 = Left, 1 = Down, 0 = Up
Pause_1_F5_2:       # 1
TutorialStatus:     # 1
DemolHoldTime:      # 1
UnusedVar1:     # 1
DemoPlayId:     # 1
DemoDataPointer:    # 2
                                ; Pointer to presaved demo controls
SprShuffleOffset:   # 1


    assert GameStatus = 0xc000
    assert GameSubstatus = (0xc000 + 1)
    assert ControlConfig = (0xc000 + 1 + 1)
    assert TickCounter = (0xc000 + 1 + 1+1)
    assert DemolHoldTime = (0xc000 + 10)
    assert SprShuffleOffset = (0xc000 + 15)

SETN, NEXT

MAP directive is probably easier to use

Example:

 ; http://www.aspisys.com/asm11man.htm

 org 0x100


data set $
    assert data == 0x100

data1 setn  data ; data1 could be modified
data2 next data, 2 ; data2 cannot be modified
data3 next data

    assert data1 == 0x100
    assert data2 == 0x101
    assert data3 == 0x103
    assert data == 0x104

UNDEF

Example:

my_label = 1

    ifndef my_label
        fail "my_label must exist"
    endif

    undef my_label

    ifdef my_label
        fail "my_label must not exist"
    endif

BYTE, TEXT, DB, DEFB, DM, DEFM

Example:

    ; defb tests
    org 0x200

    defb 1, 2, 3, 4
    defb "hello", ' ', "world"
    defb $, $ ; should generate 2 different values
    db "Hello world", 0

WORD, DW, DEFW

DEFS

Example:

    org 0x100


    defs 5
    defs 5, 8, 4
    defs 5, 8, 4, 1

STR

Description: STR encodes string in AMSDOS format (i.e., adds 0x80 to the last char) and stores its bytes in memory.

Example:

    org 0x1000
    defb "hell"
    defb 'o' + 0x80

    org 0x2000
    str "hello"

    org 0x3000
    db "Next one will be more complex"
    db "   \" et voila"
    db " \" et voila"
    db "\" et voila"

    assert memory(0x1000) == memory(0x2000)
    assert memory(0x1001) == memory(0x2001)
    assert memory(0x1002) == memory(0x2002)
    assert memory(0x1003) == memory(0x2003)

CHARSET

Example:

 org 0x100

 charset "abcdefghijklmnopqrstuvwxyz", 0
 charset "AB", 100
 db "aA"
 ASSERT memory(0x100) == 0x00
 ASSERT memory(0x101) == 100

 org 0x200
 charset
 db "aA"
 ASSERT memory(0x200) == 'a'
 ASSERT memory(0x201) == 'A' 

Conditional directives

IF, IFNOT

IFDEF, IFNDEF

Example:

    org 0x1000

    ifndef toto
    assert toto == $
toto
        assert pouet == 0x1002
        dw pouet
    endif
pouet

IFUSED

Example:

    org 0x1000
; 3 passes are needed there

    ifused toto
toto
        ret
    endif

    call toto

; test

Nested conditions

Conditions can be nested.

Example:

    org 0x100

    if 0 == 1
        fail "not reachable"
    else ifdef toto
        fail "not reachable"
    else ifndef toto
        print "reached"
        db 1
    else
        fail "not reachable"
    endif

SWITCH, ENDSWITCH

Example:

    org 0x100

    switch 3
    ; one comment
    case 1
        db 1
        break
    case 3
        db 3
    ; another comment
    case 4
        db 4
        break
    case 5
        db 5
    default
        db 6
    endswitch



        switch 5
    case 1
        db 1
        break
    case 3
        db 3
    case 4
        db 4
        break
    case 5
        db 5
    default
        db 6
    endswitch

Code duplication directives

FOR

FOR <variable> [, EXPRESSION]+
  LISTING
ENDFOR|FEND

Example:

    ; Takes inspiration from BRASS assembler

    for count, 0, 10, 3
        db {count}
    endfor

    for x, 0, 3
        for y, 0, 3
            db {x}*4 + {y}
        fend
    endfor
Corresponds to
        db 0
        db 3
        db 6
        db 9

        db 0*4 + 0
        db 0*4 + 1
        db 0*4 + 2
        db 0*4 + 3

        db 1*4 + 0
        db 1*4 + 1
        db 1*4 + 2
        db 1*4 + 3

        db 2*4 + 0
        db 2*4 + 1
        db 2*4 + 2
        db 2*4 + 3

        db 3*4 + 0
        db 3*4 + 1
        db 3*4 + 2
        db 3*4 + 3

WHILE

CPT=3

    while CPT > 0
        db CPT
CPT=CPT-1
    wend

REPEAT

REPEAT AMOUNT [, COUNTER [, START]] INNER LISTING REND

start
    repeat 3, count
        incbin 'AZERTY{{count}}.TXT'
    rend


    assert char(memory(start+0)) == 'A'
    assert char(memory(start+1)) == 'Z'
    assert char(memory(start+2)) == 'E'
    assert char(memory(start+3)) == 'R'
    assert char(memory(start+4)) == 'T'
    assert char(memory(start+5)) == 'Y'
    assert char(memory(start+6)) == 'U'
    assert char(memory(start+7)) == 'I'
    assert char(memory(start+8)) == 'O'
    assert char(memory(start+9)) == 'P'

    assert char(memory(start+10)) == 'Q'
    assert char(memory(start+11)) == 'S'
    assert char(memory(start+12)) == 'D'
    assert char(memory(start+13)) == 'F'
    assert char(memory(start+14)) == 'G'
    assert char(memory(start+15)) == 'H'
    assert char(memory(start+16)) == 'J'
    assert char(memory(start+17)) == 'K'
    assert char(memory(start+18)) == 'L'
    assert char(memory(start+19)) == 'M'


    assert char(memory(start+20)) == 'W'
    assert char(memory(start+21)) == 'X'
    assert char(memory(start+22)) == 'C'
    assert char(memory(start+23)) == 'V'
    assert char(memory(start+24)) == 'B'
    assert char(memory(start+25)) == 'N'

ITERATE

ITERATE COUNTER, EXPR...
    INNER LISTING
IEND

The expression $i$ is evaluated after having generated the code of expression $i-1$. Take that into account if expressions use $.

Example:

; Glass inspiration http://www.grauw.nl/projects/glass/

    iterate value, 1, 2, 10
        add {value}
        jr nz, @no_inc
            inc c
@no_inc
        call do_stuff
    iend


    iterate value in [11, 12, 110]
        add {value}
        jr nz, @no_inc
            inc c
@no_inc
        call do_stuff
    iend

do_stuff
    ret
Corresponds to:
        add 1
        jr nz, no_inc1
            inc c
no_inc1
        call do_stuff

        add 2
        jr nz, no_inc2
            inc c
no_inc2
        call do_stuff

        add 10
        jr nz, no_inc3
            inc c
no_inc3
        call do_stuff


        add 11
        jr nz, no_inc4
            inc c
no_inc4
        call do_stuff

        add 12
        jr nz, no_inc5
            inc c
no_inc5
        call do_stuff

        add 110
        jr nz, no_inc6
            inc c
no_inc6
        call do_stuff

do_stuff
    ret

Code and data generation directives

MACRO

Example of standard macro:

    ; rasm inspired
    macro LDIXREG register,dep
        if {dep}<-128 || {dep}>127
            push BC
            ld BC,{dep}
            add IX,BC
            ld (IX+0),{register}
            pop BC
        else
            ld (IX+{dep}),{register}
        endif
    mend

    LDIXREG H,200
    LDIXREG L,32

Example of macro using raw arguments:

    macro BUILD_LABEL r#label
{label}_first
    endm


    BUILD_LABEL "HERE"
    BUILD_LABEL "THERE"

    ifndef HERE_first
        fail "macro error"
    endif
    ifndef THERE_first
        fail "macro error"
    endif

    macro BUILD_CODE r#code
        {code}
    endm

START_CODE1
    BUILD_CODE "xor a"
    BUILD_CODE "ld hl, 0xc9fb : ld (0x38), hl"
END_CODE1

START_CODE2
    xor a
    ld hl, 0xc9fb : ld (0x38), hl
END_CODE2

    assert END_CODE2 - START_CODE2 == END_CODE1 - START_CODE1
    assert END_CODE2 - START_CODE2 == 7

    assert memory(START_CODE1) == memory(START_CODE2)
    assert memory(START_CODE1+1) == memory(START_CODE2+1)
    assert memory(START_CODE1+2) == memory(START_CODE2+2)
    assert memory(START_CODE1+3) == memory(START_CODE2+3)
    assert memory(START_CODE1+4) == memory(START_CODE2+4)
    assert memory(START_CODE1+5) == memory(START_CODE2+5)
    assert memory(START_CODE1+6) == memory(START_CODE2+6)

STRUCT

Description: Structures allow to defined data blocs with semantic. In practice, they replace bunches of DEFB, DEFW directives and enforce checks at assembling (you cannot add more data than expected or forget some). If a label is used before the use of a struct, it is necessary to postfix it by :. Otherwise the assembler thinks the label is a macro or structure call.

Synopsis

STRUCT <name>
<filed1> DB|DW|STR|<other struct> [<value>]
...
<filedn> DB|DW|<other struct> [<value>]
ENDSTRUCT



[<label>:] <name> <arg1>, ... , <argn>

Standard example:

    struct color
r db 1
g db 2
b db 3
    endstruct

    struct point
x db 4
y db 5
    endstruct

col0:   color (void)
pt0:    point (void)

col1:   color 'a', 'b', 'c'
pt1:    point 'd', 'e'

    struct colored_point
col     color 10, 20, 30
pt      point 10, 20
    endstruct


    colored_point (void)

Example using default values:

  ;; Define a 3 fields structure
  struct point
xx    db 4
yy    db 5
zz    db 6
  endstruct

  assert point == 3
  assert point.xx == 0
  assert point.yy == 1
  assert point.zz == 2

 point 1, 2 , 3
 point ,,8
 point 9


; force values
; : after label name allows to disambiguate parser that does not try to check if label is a macro (less errors/faster)
my_point1: point 1, 2, 3

; use all default values
my_point2: point (void)

; use default at the end
my_point3: point 1

; use default at the beginning
my_point4: point ,,1


p1: point 1, 2 , 3
p2: point ,,8
p3: point 9




    struct triangle
p1 point 1, 2 , 3
p2 point ,,8
p3 point 9 ; third point
    endstruct

    assert triangle == 9
    assert triangle.p1 == 0
    assert triangle.p2 == 3
    assert triangle.p3 == 6


my_triangle2: triangle [1, 2, 3], [4, 5, 6], [7, 8 , 9]


 if 0


my_triangle1: triangle

my_triangle2: triangle [1, 2, 3], , [7, 8 , 9]
 endif

Data loading and transformation directives

Filenames are stored in a string. These string can do expansion of formulas embedded in {}.

basm embeds some files in its executable, they are access under the name "inner://" :

LZAPU, LZ48, LZ49

Example:

    org 0x100


    ld hl, CS_START
    ld de, 0xc000
    call aplib.depack
    jp $

CS_START
    LZAPU
INNER_START
        defs 100
INNER_STOP
    LZCLOSE
CS_STOP

    assert INNER_STOP - INNER_START == 100
    assert CS_STOP - CS_START < 100

    include "inner://unaplib.asm" namespace "aplib"

INCBIN, BINCLUDE

INCBIN|BINCLUDE "fname" [[, SKIP], AMOUNT]

Fname can be build with variables.

Limitations:

  • File is loaded fully in memory before being sliced depending on arguments.

Example:

here
    incbin "AZERTY.TXT", 2, 3
there

    assert peek(here) == 'E'
    assert peek(here+1) == 'R'

    assert there-here == 3
with AZERTY.TXT containing the text AZERTYUIOPQSDFGHJKLMWXCVBN.

INCLUDE, READ

INCLUDE|READ [ONCE] "<fname>" [AS|MODULE|NAMESPACE "<module>"]

Fname can be build with variables.

Example with once:

    org 0x4000

SIZE1_start
    include once "include_once.asm"
SIZE1_stop

SIZE2_start
    include once "include_once.asm"
SIZE2_stop

    assert (SIZE1_stop - SIZE1_start) != 0
    assert (SIZE2_stop - SIZE2_start) == 0

Example with namespace:

    include "good_labels.asm" namespace "good"

    ifndef good.outer1
        fail "good.outer1 is undefined"
    endif

    ifdef outer1
        fail "outer1 is defined"
    endif

    ifndef good.outer2.inner1
        fail "good.outer2.inner1 is undedined"
    endif

Files prefixed by inner:// are embedded by BASM. Example:

 include "inner://opcodes_first_byte.asm"

 org 0x4000

 db opcode_inc_l
 inc l

 assert memory(0x4000) == memory(0x4001)

In case of conditional assembling, inclusion are only done in the executed branch. This code always assemble as it never includes 'unknonw' file.

truc equ 0:if truc:include'unknown':endif:nop

Data saving and export

EXPORT, NOEXPORT

Example:

    org 0x4000

    NOEXPORT
    EXPORT toto


label1
label2
toto ; only this one is exported

SAVE, WRITE

  • SAVE "<fname>", [[[START], [SIZE]], AMSDOS|BASIC|TAPE]
  • SAVE "<fname>", START, SIZE, DSK, "<fname.dsk>" [, SIDE]
  • SAVE "<fname>", START, SIZE, HFE, "<fname.hfe>" [, SIDE]
  • SAVE "<fname>", START, SIZE, DISC, "<fname.hfe>"|"<dname.dsk>" [, SIDE]

Unimplemented

TAPE option is not coded. Other options are not intensively tested

Example:

    org 0x4000
    run $
FIRST_ADDRESS
    ld hl, txt
loop
    ld a, (hl)
    or a
    jp z, $

    push hl
        call 0xbb5a
    pop hl
    inc hl
    jp loop

txt
.start
    defb "Hello World!"
    defb 0
.stop
LAST_ADDRESS


    save "good_save_whole_inner.bin" ; Save binary without header
    save "hello.bin", FIRST_ADDRESS, LAST_ADDRESS-FIRST_ADDRESS, AMSDOS ; Save a binary with  header
    save "hello.bin", FIRST_ADDRESS, LAST_ADDRESS-FIRST_ADDRESS, DSK, "hello.dsk" ; Save binary with  header INSIDE a dsk

    if BASM_FEATURE_HFE
        save "hello.bin", FIRST_ADDRESS, LAST_ADDRESS-FIRST_ADDRESS, HFE, "hello.hfe" ; Save binary with  header INSIDE a hfe file
    endif

    save "good_save_txt.bin", txt.start, (txt.stop - txt.start) ; save text without header


; cmd line to generate the binary with header
;    basm good_save.asm --binary -o run.bin 
; cmd line to put it in a dsk
;    dskmanager test.dsk format --format data42
;    dskmanager test.dsk add run.bin 

Debug directives

ASSERT

ASSERT BOOLEAN_EXPRESSION [, PRINTABLE_EXPRESSION]*

PRINT

Example:

    print "Hello world"

world = "World"
    print "hello ", world

TICKER

Description: Compute the execution duration of a block of code

Synopsys:

TICKER START variable
 instructions
TICKER STOP

Example 1:

; http://mads.atari8.info/mads_eng.html

    TICKER START count
        WAITNOPS 3
    TICKER STOP

    assert count == 3

    TICKER START count2
        nop
    TICKER STOP

    assert count2 == 1

Example 2:

    TICKER START duration_varying_code
        xor a
        ld b, 1
    TICKER STOP
    assert duration_varying_code == 1 + 2
    UNDEF duration_varying_code


    TICKER START duration_varying_code
        xor a
    TICKER STOP
    assert duration_varying_code == 1 
    UNDEF duration_varying_code

    TICKER START duration_varying_code
    TICKER STOP
    assert duration_varying_code == 0
    UNDEF duration_varying_code


    assert duration(xor a) == 1
    ;assert duration(xor a : xor a) == 2 ; Does not compile yet Could be a good idea


    TICKER START duration_varying_code
        WAITNOPS 64
    TICKER STOP
    assert duration_varying_code == 64
    UNDEF duration_varying_code


    TICKER START duration_stable_code
        TICKER START duration_varying_code
            out (c), c
        TICKER STOP
        WAITNOPS 64 - duration_varying_code
    TICKER STOP
    assert duration_stable_code == 64
    UNDEF duration_varying_code


    MACRO BUILD_STABLE_CODE duration, r#code
        TICKER START .my_count
            {code}
        TICKER STOP
        ASSERT {duration} >= .my_count
        WAITNOPS {duration}-.my_count

        IFDEF DEBUG_EXPECTED_DURATION
            ASSERT .my_count == DEBUG_EXPECTED_DURATION
        ENDIF
        UNDEF .my_count
    ENDM

    DEBUG_EXPECTED_DURATION = 2
    BUILD_STABLE_CODE 64, "xor a : xor a"

WAITNOPS

Generate a list of instructions that do not modify any registers or memory but is executed with the expected amount of nops. (Currently it is synonym of NOP, but as soon as someone wants to provide clever rules to use less bytes, I'll implement them)

LOCOMOTIVE

    LOCOMOTIVE start
10 REM Basic loader of binary exec
20 REM yeah !!
30 call {start}
    ENDLOCOMOTIVE

start
        ld hl, txt
.loop
        ld a, (hl)
        or a : jr z, .end
        call #bb5a
        inc hl
        jr .loop
.end
        jp $

txt
    db "Hello world", 0

    print "LOADER START IN ", {hex}start
    save "LOADER.BAS",,,BASIC

SNASET