RASM v0.96


roudoudou Z80 assembler

Contents

Introduction
Features
Installation
Compilation
   - Linux Compilation
   - Windows Compilation
   - DOS Compilation
   - Morphos Compilation
Behaviour
command line
   - File options
   - EDSK file export
   - Snapshot file export
   - Symbol options
   - Dependencies options
   - Compatibility options
   - Dev options (internal use)
Format
   - General
   - Comments
   - Literal values
   - Allowed characters
   - Files
   - Labels
   - Proximity labels
   - Local labels
   - Tags specific to labels or structures
      {BANK}
      {PAGE}
      {PAGESET}
      {SIZEOF}
   - Syntax coloring in VIM
Expressions
   - Calculation operators
   - Operators priorities
Variables
   - Statics
   - Dynamics
Memory directives
   - ALIGN
   - BANK
   - BANKSET
   - LIMIT
   - ORG
   - PROTECT
   - WRITE DIRECT
Data directives
   - CHARSET
   - DEFB
   - DEFW
   - DEFI
   - DEFR
   - DEFS
   - EQU
   - STR
   - STRUCT
Import and compression directives
   - INCLUDE
   - INCBIN
   - INCL48, INCL49, INCLZ4, INCZX7, INCEXO
   - LZ48, LZ49, LZ4, LZX7, LZEXO
   - LZCLOSE
Conditionnal code directives
   - ASSERT
   - IF IFNOT THEN ELSEIF ELSE ENDIF
   - IFDEF, IFNDEF
   - IFUSED, IFNUSED
   - MACRO
   - PRINT
   - FAIL
   - REPEAT, REND, UNTIL
   - SWITCH, CASE, DEFAULT, BREAK, ENDSWITCH
   - STOP
   - WHILE, WEND
File output directives
   - AMSDOS
   - BREAKPOINT
   - BUILDCPR
   - BUILDSNA
   - RUN
   - SAVE
   - SETCPC
   - SETCRTC
Useless directives
   - LIST, NOLIST, LET
Instruction set
Limitations
Introduction
During the making of my first big demo CRTC³, available assemblers were too slow and too limited. The demo has 250'000 words (out of comments), 35'000 labels and 60'000 expressions. I needed a damn fast assembler with cartridge management, banking facilities and integrated crunched sections (to avoid import/export and multi-pass).

18 years ago i wrote a small assembler. I didn't use it back in the days cause it was very limited. Nevertheless, i took up the mono-pass concept of it.

Rasm uses proven concepts (merkel trees, many caches, grouped allocs) and a linear conception because recursivity slow down anything. The performances in real conditions are really fast, especially with huge projects.

Nowadays Rasm is used on big projects:
 - Ghost'n Goblins by Golem13
 - Arkos Tracker II by Targhan (embedded in the software)


Rasm is under MIT licence.
Fonctionnalities
Installation
Rasm is a standalone executable, there is no installation.
Compilation
Linux compilation
cc rasm_v096.c -O2 -lm -lrt -march=native
mv a.out rasm
strip rasm
Windows compilation
cl.exe rasm_v096.c -O2
Compilation DOS or Windows 32 bits with Watcom
# do not use -s or -ox option, this will cause a fatal stack issue wcl386.exe rasm_v096.c -6r -6s -fp6 -d0 -k4000000 -obmiler
MorphOS compilation
ppc-morphos-gcc-5 -O2 -c -o rasm rasm_v096.c
strip rasm
Rasm behaviour
The goal of Rasm is to be easy to use. He will make default filenames, autoselect a cartridge or snapshot file when dedicated ROM directives are used.

Rasm allow any ORG in the same memory space but it will trigger an error if two ORG zones overlap. For each new ORG, Rasm will do the check.

If you need to assemble two programs in the same adressing range you may use the second parameter of ORG to relocate the code elsewhere or create a new memory space with directive BANK

Rasm has many error messages to help fixing wrong syntax.

Rasm pre-processor remove useless spaces and comments. A first check is perform on valid characters and quotes. Some Maxam operators are translated to their C equivalent.

When using relative file reference, the root directory to the relative path is that of the current file.

Command line
Usage: rasm.exe <file to assemble> [options]

Example: rasm.exe myfile.asm
Filenames options
-o <file radix> set a common name for each output file, disregarding his type (.bin, .cpr, .sym). The default value is “rasmoutput”
Example:
rasm.exe test -o grouik -s
Pre-processing [test.asm]
Assembling
Write binary file grouik.bin (25 bytes)
Write symbol file grouik.sym (10 bytes)
-ob <binary filename> set the full filename for automatic binary output.
-oc <cartridge filename> set the full filename for cartridge output.
-oi <snapshot filename> set the full filename for snapshot output.
-os <symbol filename> set the full filename for symbol output.
-ok <breakpoint filename> set the full filename for breakpoint export.
-I<include directory> set the include directory. Multiple -I options is possible.
-no disable file output.
EDSK file option
-eo   overwrite file in the EDSK if it exists.
Snapshot file option
-v2 Export a snapshot version 2 (default is version 3)
-sb Export breakpoints in snapshot file (Winape and ACE emulator format), only with snapshot version 3+
-ss Export symbols in snapshot file (Winape and ACE emulator format), only with snapshot version 3+
Symbol options
-s export symbols in Rasm format
Example of outputed file:
LABEL1 #0 B0
LABEL2 #1 B0
LABEL3 #2 B0
LABEL4 #4 B0
-sp export symbols in Pasmo format
Example of outputed file:
LABEL1 EQU 00000H
LABEL2 EQU 00001H
LABEL3 EQU 00002H
LABEL4 EQU 00004H
-sw export symbols in Winape format
Example of outputed file:
LABEL1 #0
LABEL2 #1
LABEL3 #2
LABEL4 #4
-sl additionnal option to export also local labels.
-sv additionnal option to export also variables.
-sq additionnal option to export also EQU aliases.
-sa meta option meaning -sl -sv -sq
-ss export symbols in snapshot
-eb export breakpoints
-l <label file> import symbols in Rasm, Pasmo or Winape format
Note: You may import as many symbol file as you want using multiple times -l option
rasm.exe test -l import1.sym -l import2.sym -l import3.sym
Note 1: Arnold emulator is Rasm and Pasmo compliant for labels.
Note 2: Winape emulator cannot handle long labels.
Dependencies options
-depend=make
Export all dependencies in a single line (for makefile usage)

-depend=list
Export all dependencies, one per line (for other usage)

If there is a binary output filename set (-ob option), it will be outputed in first position.
Compatibility options
            
-m Maxam compatibility
  • 16 bits unsigned integer calculations with wrong rounding
  • comparisons using single = sign
  • simplified operator priorities (see charts)
-ass AS80 compatibility
  • 32 bits integer calculations with wrong rounding
  • DEFB,DEFW,DEFI or DEFR directives with more than one parameter use first outputed byte as reference adress. Not the current outputed byte. That's why AS80 does not generate the same thing when using two DEFB instead of one with two parameters when using $ reference in it.
  • macro parameters are not protected by {}
  • MACRO directive must be used after the name of the macro
-uz UZ80 compatibility
  • 32 bits integer calculations with wrong rounding
  • macro parameters are not protected by {}
  • MACRO directive must be used after the name of the macro
Dev options
-v verbose mode, display stats
-d verbose detailed pre-processing
-a verbose detailed assembling
-n display third parties licences
Source code format
Generalities
Assembler is not COBOL, it's useless to use identation with Rasm, except to be pretty. A ":" separator is not required between labels and instructions. As a direct consequence you cannot use a reserved word (directive, register, Z80 instruction) as a label.

As you may declare two labels without separator, i encourage you to use the optionnal "(void)" parameter with macros declared without parameter (see macro chapter)

Windows and Unix files are both supported.

Rasm is not case sensitive. Don't be surprise to see your code in upper case in error messages.

A label is NOT an alias, and NOT a variable. Rasm knows the difference!

- a label refer to a memory adress (an adress and sometimes a memory page if the workspace is in cartridge/snapshot space)
- an alias (EQU) is a text, which encountered, is replaced by another one. Known variables are translated during first declaration
- a variable is global to the whole source, his value must be initialised and may be modified anytime
Comments
Rasm uses the semicolon to start a comment. Every appending chars will be ignored until carriage return.

Note: There is no multi-lines comments.
Literal values
Rasm accepts the following values:
  • Decimal if the value begins with a digit.
  • Binary if the value begins with %, 0b or ends with b.
  • Octal if the value begins with @.
  • Hexadecimal if the value begins with #, $, 0x or ends with h.
  • ASCII value with a single char between quotes.
  • Value of a label or a variable if it begins with a letter or @ followed by a letter.
  • $ symbol used alone means current instruction adress. When using DEFB, DEFW, DEFI, DEFR ou DEFS, the current adress is that of the current instruction. DEF* directives will output the very same data if you use multiple DEF or one DEF with multiple arguments.
All Rasm calculations are done with double precision floating point accumulator. A correct rounding is done in the end for integer needs.

Beware of the & char, reserved for AND operator.
Allowed chars
Between quotes, all characters are allowed, at your own risks for special conversions. Outside quotes, you can use letters, digits, the dot, arobas, parenthesis, dollar, operators, pipe, power, percent, sharp, paragraphs, chevrons and also both quotes types.

Quoted strings may contains escaped chars:
  • \t

  • \n

  • \r

  • \f

  • \v

  • \b

  • \0
  • -> except with PRINT directive
    Files
    RASM is using UNIX model for file management. Windows path are converted on the fly. If you use only relative paths and filenames Windows can handle, you may use your sources with Rasm either with Linux or Windows without modification.

    Relative path rule is simple: Every relative path take root of the file where it was read.
    Labels
    Inside a loop (REPEAT/WHILE/UNTIL) or inside a macro, you may use local labels the same way as Winape assembler does, prefixing labels with '@'.

    You may use a label value with a directive (ORG for example) only if the label declaration is before the directive.

    Every label beginning with BRK or @BRK will generate a BREAKPOINT.

    In DAMS mode, you may prefix labels with a dot but the call to this label will be done without the dot. DAMS mode will disable proximity labels.

    Local labels
    Local labels must be declared inside a loop or a repeat. Those labels are invisibles out of the loop scope.

    Local labels are declared with prefix '@'.

    Usage of local label in a loop:
    repeat 16
    add hl,bc
    jr nc,@no_overflow
    dec hl
    @no_overflow
    rend
    Proximity label
    Proximity labels may be used anywhere. The prefix is the dot '.'

    Proximity label sample code:
    routine1
    add hl,bc
    jr nc,.no_overflow
    dec hl
    .no_overflow

    routine2
    add hl,bc
    jr nc,.no_overflow
    dec hl
    .no_overflow

    routine3
    xor a
    ld hl,routine1.no_overflow ; retrieve proximity label of routine1
    ld de,routine2.no_overflow ; of routine2
    sbc hl,de
    Specifics tags for labels or structures
    {BANK}
    Using {BANK} prefix before a label (example: {BANK}mylabel ) get the BANK number where is located the label, instead of his absolute adress.

    Example:
    Bank 0
    ld a,{bank}mysub ; will be assembled LD A,1
    call connect_bank
    jp mysub
    bank 1
    defb 'coucou'
    mysub
    jr $
    {PAGE}
    Using {PAGE} prefix before a label (example: {PAGE}mylabel ) get a Gate array style of the BANK number where is located the label, instead of the label adress.

    If you are using BANKSET directive to select 4 banks in a 64K set, then the gate array value is composed of the set number and the 2 most significant bits of the label adress.

    Example:
    Bank 0
    ld a,{page}mysub ; will be assembled LD A,#C5
    ld b,#7F
    out (c),a
    jp mysub
    bank 5
    defb 'coucou'
    mysub
    jr $
    {PAGESET}
    Using {PAGESET} prefix before a label (example: {PAGESET}mylabel ) get a Gate array style of the BANKSET number where is located the label, instead of the label adress.

    Example:
    bank 0
    ld a,{pageset}mysub ; will be assembled LD A,#C2
    ld b,#7F
    out (c),a ; as all the RAM is switching the code is supposed to be in ROM, or in a proper place
    jp mysub
    bank 5
    defb 'coucou'
    mysub
    jr $
    {SIZEOF}
    Using {SIZEOF} prefix before a structure name get the size of it.

    See structure chapter here.
    Syntax color with VIM
    • If you already own a syntax color file, just add the following lines to the file .vim/syntax/z80.vim
    • Or you may download the whole file here

    " rasm/winape directives
    syn keyword z80PreProc charset bank write save include incbin incl48 incl49
    syn keyword z80PreProc macro mend switch case break while wend repeat until
    syn keyword z80PreProc buildcpr amsdos lz48 lz49 lzclose protect
    syn keyword z80PreProc direct brk let print stop nolist str
    syn keyword z80PreProc defr dr defi bankset page pageset sizeof endm struct endstruct ends
    syn keyword z80PreProc incexo lzexo lzx7 inczx7 buildsna setcrtc setcpc assert print
    syn keyword z80Reg lix liy hix hiy
    Expressions
    Calculation operators
    Rasm is using a simplified calculation engine with multiple priorities (like C language). Here is the list of supported operations:
    • * multiply
    • / divide
    • + addition
    • - soustraction
    • & or AND for logical and
    • | or OR for logical or
    • ^ or XOR for logical xor
    • % or MOD for modulo
    • && for boolean and
    • || for boolean or
    • << multiply by a power of two
    • >> divide by a power of two
    • hi() get upper 8 bits of a word
    • lo() get lower 8 bits of a word
    • sin() sinus
    • cos() cosinus
    • asin() d'arc-sinus
    • acos() d'arc-cosinus
    • atan() d'arc-tangente
    • int() float to integer conversion
    • floor() convert a float to the closest lower integer value
    • abs() absolute value
    • ln() logarithme népérien
    • log10() logarithme base 10
    • exp() exponentielle
    • sqrt() square root
    • == égalité (ou un seul = en mode Maxam)
    • != ou <> différent de
    • <= less or equal
    • >= greater or equal
    • < lower
    • > greater
    Operators priorities
    Lower is the prevalence, higher is the execution priority.
    Operators Rasm Prevalence Maxam Prevalence
    (   ) 0 0
    !   1 464
    *   /   % 2 464
    +   - 3 464
    <<   >> 4 464
    <   <=   ==   =>   >   != 5 664
    &   AND 6 464
    |   OR 7 464
    ^   XOR 8 464
    && 9 6128
    || 10 6128
    Variables
    Static variables or alias
    You may create an unlimited number of alias with directive EQU. Alias cannot be modified (use variables if you need to).
    Dynamic variables
    Rasm is able to handle an unlimited number of variables.

    Syntax:
    myvar=5
    LET myvar=5 ; Winape compatible
    Variables are used for numeric values only.

    Examples:
    dep=0
    repeat 16
    ld (ix+dep),a
    dep=dep+8
    rend
    ang=0
    repeat 256
    defb 127*sin(ang)
    ang=ang+360/256
    rend
    A directive is not a functiob. That is a keyword which must be (séparé) with at least one space char.

    Correct syntax: ASSERT (4*myvar)
    Wrong syntax: ASSERT(4*myvar)
    Directives mémoires
    ALIGN <boundary>[,fill]
    If you want to enforce that the current code adress is aligned to a value. Si l'adresse d'écriture du code en cours n'est pas un multiple de la valeur d'alignement, on augmente cette dernière afin que ça soit le cas. Cette instruction ne produit pas d'octet sur la sortie. Cependant, si un ALIGN est réalisé entre deux portions de code, ce sont des zéros qui seront en mémoire car les espaces de travail sont initialisés avec la valeur zéro.

    Exemple:
    ALIGN 2 ; align code on even adress
    ALIGN 256 ; align code on high byte
    BANK [ROM page number, RAM page number]
    Select a ROM number (cartridge) or a RAM slot (snapshot). When using BANK, cartridge is selected if there is no previous BUILDSNA directive.

    Values range from 0 to 31. In snapshot mode the values range from 0 to 35 (64K base memory + 512K extended memory).

    Used without parameter, the BANK directive open a new memory workspace.
    BANKSET <64K bloc number>
    BANKSET directive select a set of 4 pages in a row. With snapshot v3, there is 9 memory sets, indexed from 0 to 8.

    You may use BANK and BANKSET in a source but you cannot select the same memory space. A check will trigger an error if you try to.

    Using this directive enables snapshot output (like BUILDSNA does).
    LIMIT <adress boundary>
    Enforce a lower limit when assembling. Default limit is 65536 but you may need to change this value.

    If you want to protect a zone, take a look at the directive PROTECT.
    ORG <logical adress>[,physical adress]
    Change assembling adress. You may write the code to another adress than the logical adress with the second parameter.

    Assembling to an absolute adress:
    ORG #8000
    defw $
    ; bytecode output
    #8000: #00,#80

    Assembling to another adress than where the code will be written:
    ORG #8000,#1000
    defw $
    ; bytecode output
    #1000: #00,#80

    Get back physical adress after changing logical adress:
    ORG #8000,#1000
    defw $ ; This instruction is logically in #8000 but will be written in #1000
    ORG $ ; ORG do not care of logical adress, we are in #1002 after the previous DEFW
    defw $ ; current adress is #1002
    ; bytecode output
    #1000: #00,#80,#02,#10
    PROTECT <starting adress>,<ending adress>
    Protect the memory zone from writes.
    WRITE DIRECT <lower rom>[,<higher rom>[,<RAM gate array>]]
    The purpose of this directive is the Winape compatibility. Prefer usage of BANK or BANKSET directives.

    Using lower ROM (from 0 et 7) or higher ROM (from 0 et 31) is like BANK usage.

    Using only RAM gate array (disabling ROM with -1 like Winape does) a new memory workspace is created, like using BANK directive without parameter.
    Data declaration directives
    CHARSET 'string',<value> | <code>,<value> | <start>,<end>,<value>
    The directive allow quoted char values to be changed. There is 4 usages for this directive:
    • 'string',<value> → First char of the string will get the value <value>. Then the next char <value>+1 and so on until the end of the string.
    • <code>,<value> → Set to the ASCII char <code> the value <value>.
    • <start>,<end>,<value> → Set to the ASCII char from <start> to <end> an incremental value starting from <value>.
    • Without parameter → Reset all ASCII char to default.
    This directive is Winape compatible.


    Redefine char:
    CHARSET 'T','t' ; 'T' chars will be outputed as 't'
    defb 'tT'
    ; bytecode output
    #0000: #74 #74

    Redefine non consecutives chars:
    CHARSET 'turndiskheo ',0
    defb 'there is no turndisk'
    ; bytecode output
    #0000: #00,#08,#09,#02,#0B,#05,#06,#0B,#03,#0A,#0B,#00,#01,#02,#03,#04,#05,#06,#07

    Redefine consecutives chars:
    CHARSET 'A','Z','a' ; Change all uppercases to their respective lowercases
    defb 'abcdeABCDE'
    ; bytecode output
    #0000: #61,#62,#63,#64,#65,#61,#62,#63,#64,#65
    DB, DEFB, DM, DEFM <value>[,<value>,...]
    This directive handle one or more parameters and output bytes regarding of thoses parameters. The value may be a literal value, a formula (the result will be rounded), or a string where each char will output a byte. If CHARSET directive were used, the outputed bytes respect the CHARSET redefinition.

    Example:
    defb 'r'-'a'+'A',oudoudou',#FF,lo($)
    The current adress ($) is the one from outputed byte (this is not the case in AS80 mode, see compatibility options).
    DEFW, DW <value>[,<value>,...]
    This directive handle one or more parameters and output words (two bytes) regarding thoses parameters. Values may be literal value, formula, single char but not a string!

    Example:
    defw mylabel1,mylabel2,'a'+#100

    DEFI <value>[,<value>,...]
    This directive handle one or more parameters and output integers (four bytes) regarding thoses parameters. Values may be literal value, formula, single char but not a string!

    DEFR, DR <real number>[,<real number>,...]
    This directive handle one or more parameters and output AMSTRAD firmware compatible real numbers (5 bytes)

    Example:
    defr 5/12,7/3
    DEFS, DS <repetition>[,<value>,[<repetition>,...]
    This directive outputs a byte and repeat <repetition> times. If the byte to output is not set, then the directive will output zeroes. If the repetition value is zero then no byte will be outputed. You may declare multiples repetition sequences with only one DEFS.

    Examples:
    defs 5,8,4,1 ; output=#08,#08,#08,#08,#08,#01,#01,#01,#01
    defs 5,8,4 ; output=#08,#08,#08,#08,#08,#00,#00,#00,#00
    defs 5 ; output=#00,#00,#00,#00,#00
    <alias> EQU <replacement string>
    Create an alias. When assembling, each alias will be replaced by the replacement string previously defined. There is an infinite recursivity check done for each alias declaration.

    Example:
    tab1 EQU #8000
    tab2 EQU tab1+#100

    ld hl,tab2
    STR 'string1'[,'string2'...]
    Almost same directive as DEFB, except the very last outputed char will have his 7th bit forced to 1.

    Both lines will output the same bytes:
    defb 'roudoudo','u'+128
    str 'roudoudou'
    STRUCT <prototype name>[,<variable name>]
    Z80 processor is able to manage structures thanks to his indexed instructions (with IX and IY registers). Structures may be used anywhere.

    Example of structure creation
    struct st1
    ch1 defw 0
    ch2 defb 0
    endstruct
    ; structure st1 created with two fields ch1 and ch2. No byte outputed.
    ; Imbricated structures
    struct metast1
    struct st1 pr1
    struct st1 pr2
    endstruct
    ; structure metast1 created with 2 sub-structures st1 called pr1 et pr2

    Recommended usage to get a structure size is to use the prefix {SIZEOF}. Like Vasm, you can get the structure size using his prototype name but it is not a recommended usage.
    LD HL,{SIZEOF}metast1
    LD HL,metast1

    STRUCT directive called with 2 parameters will create a structure in memory, based on prototype. In the example below, it will create a metast1 structure type, called mymeta.

    struct metast1 mymeta

    Example of retrieving fields offsets using prototype name:
    LD A,(IX+metast1.pr2.ch1) ; offset because prototype used

    Example of retrieving fields absolute adress using declared structure name:
    LD HL,mymeta.pr2.ch1
    LD A,(HL)
    Crunch and import directives
    INCLUDE, READ 'file to read'
    Read a textfile in place of the directive. The root of the relative path is the location of the file containing the include directive. An absolute path discard the relative path.

    There is no recursivity limit, be aware of what you are doing.
    INCBIN 'file to read'[,offset[,size[,extended offset[,OFF]]]]
    Read a binary file. Binary data will go straight to memory space. Additional parameters are Winape INCBIN compatibles. The first offset had no limitation (like Winape) so for a pure Rasm usage, do not bother with extended offset.

    You may use a negative offset, relative to the end of the file.

    You may use a negative size, relative to the fullsize of the file, minus the offset. A size of -10 will read all the file except 10 latest byets.

    A zero size will read all the file.

    'OFF' parameter: You may want to read binary data in order to initialise a memory space, then assemble code on it. The 'OFF' parameter will disable overwrite check for this file.

    Example:
    org #4000
    incbin'makeraw.bin',0,0,0,OFF ; read in #4000 without overwrite check
    org #4001
    defb #BB ; overwrite 2nd byte without error
    INCL48, INCL49, INCLZ4, INCZX7, INCEXO 'file to read'
    Read a binary file, crunch it in LZ48, LZ49, LZ4, Exomizer or ZX7 on the fly.
    LZ48, LZ49, LZ4, LZX7, LZEXO
    Open a crunched section in LZ48,LZ49, LZ4, ZX7 or Exomizer. Outputed code will be crunched after assembling. The code placed after the zone will be relocated (labels, ...).

    You cannot call a label located after a crunched zone from the crunched zone because we cannot know where it will be located after crunching. This will trigger an error.

    Limitations:
    • Code or data of a crunched zone cannot exceed 64K.
    • You cannot imbricate crunched sections.

    Example:
    org #1000
    ld hl,zecrunch
    ld de,#8000
    call decrunch
    call #8000
    jp next ; label next after crunched zone will be relocated

    zecrunch
    lz48 ; this section will be crunched
    org #8000,$
    nop
    nop
    nop
    ret
    lzclose

    next
    ret
    LZCLOSE
    Close an LZ section.
    Conditionnal code directives
    ASSERT <condition>[,text,text,text...]
    Check condition and stop assembling when result is false.

    Example:
    assert mygenend-mygenstart<#100
    assert mygenend-mygenstart<#100,'code is too big'
    IF <condition>, IFNOT <condition>, ELSE, ELSEIF <condition>, ENDIF
    Directive in order to test and enable block of code.

    Example:
    CODE_PRODUCTION=1
    [...]
    if CODE_PRODUCTION
    or #80
    else
    print 'test version'
    endif
    IFDEF, IFNDEF <variable or label>
    Both directives test variable or label existence.
    IFUSED, IFNUSED <variable or label>
    Both directives test variable or label usage, BEFORE the test.
    MACRO
    Rasm supports macro with curly brackets (Winape compatible). You may use conditionnal assembling with macros cause a macro is barely a copy/paste with args replacement.

    Example of a long distance indexing (except B or C register):
    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
    Note: End of macro is directive MEND or ENDM


    Macros without paramètre
    Beware that any misspeled macro call will be turn into label declaration! Recommanded usage is to use a fake (VOID) parameter each call. Then a misspeled macro with (VOID) parameter will trigger an error.

    macro withoutparam
    nop
    mend

    withoutparam (void) ; secured call of macro


    Macro calls with static or dynamic args
    You can send args to macro, either as text replacement, or as formula that will be evaluated before replacement.

    macro test myarg
    defb {myarg}
    mend

    repeat 2
    test repeat_counter
    rend
    repeat 2
    test {eval}repeat_counter
    rend
    In the first loop, a 'defb repeat_counter' will be repeated.
    In the second loop, a 'defb 1' will be repeated as the counter is previously evaluated.
    PRINT 'string',<variable>,<expression>
    Write some text, variables or expression during assembly. The default output for numerical values is a floating point value but you may use prefix to change display format.

    {hex}
    Display as hexadecimal value. If the value is less than #FF then display will be forced to 2 digits. Less than #FFFF the display will be forced to 4 digits. There won't be extra zeros for upper values.
    {hex2}, {hex4}, {hex8}
    Force hex display to 2, 4 or 8 digits.
    {bin}
    Display a binary value. If the value is less than #FF display will be forced to 8 bits. Less than #FFFF the display will be forced to 16 bits. Any negative 32 bits value with all 16 upper bits to 1 will be displayed as 16 bits value.
    {bin8},{bin16},{bin32}
    Force binary display to 8, 16 ou 32 bits.
    {int}
    Display value as integer.
    FAIL 'string',<variable>,<expression>
    Directive inherited from PRINT, but trigger an error and STOP assembling.
    REPEAT <repetition number>, REND / REPEAT, UNTIL <condition>
    REPEAT directive makes a loop of a block of instructions. You may fix a number of repetition or use conditionnal mode with end block directive UNTIL. You may get the internal loop counter anytime with internal value REPEAT_COUNTER.

    Example:
    repeat 10
    ldi
    print repeat_counter
    rend

    cpt=10
    repeat
    ldi
    cpt=cpt-1
    until cpt>0
    Example with exported counter:
    repeat 10,hello
    ldi
    print hello
    rend

    Note: The 'hello' variable may exist or not. If not it will be created when starting the REPEAT loop.
    STOP
    STOP assembly, there will be no outputed files.
    SWITCH, CASE, BREAK, DEFAULT, ENDSWITCH
    SWITCH/CASE syntax mimics the C syntax. You may use multiple cases with the very same value. This allow you to write more complex cases.

    Below, the macro call with '5' will output all defb with 'oui' or 'encore oui':
    macro ouioui mavar
    switch {mavar}
    nop ; outside case, won't be outputed
    case 3
    defb 'non'
    case 5
    defb 'oui'
    case 7
    defb 'encore oui'
    break
    case 8
    defb 'non'
    case 5
    defb 'encore oui'
    break
    default
    defb 'non'
    endswitch
    mend
    WHILE, WEND
    Repeat a block as long as the condition is checked and true. You may use anytime a WHILE_COUNTER variable to get the internal counter.

    Example:
    cpt=10
    while cpt>0
    ldi
    cpt=cpt-1
    print 'cpt=',cpt,' while_counter=',while_counter
    wend
    Loop will run 10 times with the following output:
    Pre-processing [while.asm]
    Assembling
    cpt= 9.00 while_counter= 1.00
    cpt= 8.00 while_counter= 2.00
    cpt= 7.00 while_counter= 3.00
    cpt= 6.00 while_counter= 4.00
    cpt= 5.00 while_counter= 5.00
    cpt= 4.00 while_counter= 6.00
    cpt= 3.00 while_counter= 7.00
    cpt= 2.00 while_counter= 8.00
    cpt= 1.00 while_counter= 9.00
    cpt= 0.00 while_counter= 10.00
    Write binary file rasmoutput.bin (20 bytes)
    Output file directives
    AMSDOS
    This directive add an AMSDOS header to the automatic binary output file of Rasm. Header won't be add to binaries produced by SAVE directives (except if you explicitly set it).
    BREAKPOINT [<adress>]
    Add a breakpoint (this won't be assembled) to the current adress or to the adress of the parameter. Breakpoints may be exported to a raw file or in snapshots (Winape and ACE compatible).

    Note: Any label starting with BRK or @BRK will generate a breakpoint.

    BUILDCPR
    This deprecated directive was supposed to force cartridge output when using multiple kind of binaries.
    BUILDSNA [V2]
    This directive force Rasm to output a snapshot instead of a cartridge when using BANK(s).

    The default settings of snapshot is a CPC 6128 with CRTC 0 but you may use SETCRTC and SETCPC directives to change those settings.

    The snapshot will include a classic CPC screen (like AMSDOS basic) with all non blinking colors (like AMSDOS basic). Audio channels are disabled, ROMS are disabled and interrupt mode is 1.

    Example:
    buildsna ; recommanded usage when using snapshot is to set it first

    bankset 0 ; assembling in first 64K
    org #1000
    run #1000 ; snapshot will run at #1000

    ld b,#7F
    ld a,{page}mydata ; get gate array value for paging memory
    out (c),c
    ld a,(mydata)
    jr $

    bank 6 ; choose 3th bank of 2nd 64K set
    nop
    mydata defb #DD

    bank ; without parameter, this is a temporary memory space
    ; that won't be save in the snapshot

    pouet
    repeat 10
    cpi
    rend
    camion
    save"another",pouet,camion-pouet

    Compatibility option for snapshot version 2: Some emulators or hardware board do not support snapshot v3. Just add arg 'v2' to SNAPSHOT directive. (New limitations are 128K max):
    buildsna v2
    RUN <adress>[,<gate array configuration>}
    Select the memory location where to start a snapshot. Optional parameter to set the gate array configuration.
    SAVE 'filename',<adress>,<size>[,AMSDOS|DSK[,'dsk filename'[,<side>]]]
    Record a binary file from the adress until adress+taille of the current memory space. All SAVE directives are delayed until the end of assembling. If there is no error then files will be written. You cannot SAVE intermediate assembling state.

    When recording file on a floppy image (DSK), the filename will be forced to upper case and truncated if it does not respect AMSDOS limitations

    Examples:
    SAVE 'myfile.bin',start,size ; Save a raw binary file
    SAVE 'myfile.bin',start,size,AMSDOS ; Save a binary file with AMSDOS header
    SAVE 'myfile.bin',start,size,DSK,'fichierdsk.dsk' ; Save a binary file (AMDOS header mandatory) on a DSK file
    SETCPC <model>
    Select CPC model when recording snapshot v3:
    • 0 : CPC 464
    • 1 : CPC 664
    • 2 : CPC 6128
    • 4 : 464 Plus
    • 5 : 6128 Plus
    • 6 : GX-4000
    SETCRTC <CRTC model>
    Select CRTC model when writing snapshot v3 file. Range value for CRTC model is from 0 to 4. CPC Plus and GX-4000 share CRTC 3.
    Compatibility directives
    LIST, NOLIST, LET
    Those directives are ignored. Usage for Maxam/Winape compatibility only.
    Instruction set
    Rasm support all documented and undocumented instructions, even complex instructions like bitops+load.

    8 bits register storage of IX and IY is doing indifferently with LX, IXL or XL, and so on.

    Complex instructions syntax:
    res 0,(ix+0),a
    bit 0,(ix+0),a
    sll 0,(ix+0),a
    rl 0,(ix+0),a
    rr 0,(ix+0),a

    Undocumented opcodes syntax:
    out (#12),a ; #12 is any 8 bits value
    in a,(#12)
    in 0,(c) ou in f,(c)
    sll <register> or sl1 <register>

    Multi-arg syntax available for PUSH, POP and NOP:
    - PUSH multi-args
    push bc,de,hl ; → push bc : push de : push hl
    - NOP repetition
    nop 4 ; → nop : nop : nop : nop
    - complex LD
    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 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 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+n) ; → LD H,(IX+n+1) : LD L,(IX+n)
    LD HL,(IY+n) ; → LD H,(IY+n+1) : LD L,(IY+n)
    LD DE,(IX+n) ; → LD D,(IX+n+1) : LD E,(IX+n)
    LD DE,(IY+n) ; → LD D,(IY+n+1) : LD E,(IY+n)
    LD BC,(IX+n) ; → LD B,(IX+n+1) : LD C,(IX+n)
    LD BC,(IY+n) ; → LD B,(IY+n+1) : LD C,(IY+n)

    LD (IX+n),HL ; → LD (IX+n+1),H : LD (IX+n),L
    LD (IY+n),HL ; → LD (IY+n+1),H : LD (IY+n),L
    LD (IX+n),DE ; → LD (IX+n+1),D : LD (IX+n),E
    LD (IY+n),DE ; → LD (IY+n+1),D : LD (IY+n),E
    LD (IX+n),BC ; → LD (IX+n+1),B : LD (IX+n),C
    LD (IY+n),BC ; → LD (IY+n+1),B : LD (IY+n),C
    Limitations
    Rasm is using licence MIT "expat"

    « Copyright © BERGÉ Édouard (roudoudou)

    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation/source files of RASM, to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. The Software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the Software. »