MAD-ASSEMBLER 1.9.5

Tebe/Madteam (01.01.2013)
Translated poorly to English by Phaeron using Google Translate (26.01.2013)
Formatted and revised by JAC!, not final yet (29.01.2013)


INTRODUCTION

Preface

Mad-Assembler (MADS) is a 32-bit application, written in Delphi. Most applications are written in C, so to be different I used Delphi 7.0 ;).

MADS is oriented toward users of QA, XASM, and FA. The syntax is borrowed from QA, some macro commands and syntax come from XASM, and SpartaDOS X (SDX) syntax is inherited from FA. Additional characters are allowed in label names. Also added are support for the 65816 CPU, macros, procedures, splitting across virtual memory banks, and nested label names.

The number of labels and macros are limited only by the memory in your PC. Specifically, you can have 2147483647 (INTEGER) table entries. I'm sure this amount is sufficient. :)

Arithmetic operations are done with values of type INT64 (signed 64 bit), with the result represented with a value of type CARDINAL (unsigned 32 bit).

A line can be up to 65535 bytes, which is also the length limit of a label name. However, I have not had the opportunity to check labels as long as poems. :)

With the free compiler Free Pascal Compiler, it is possible to compile MADS for other platforms, such as Linux, Mac, OS/2, etc. For more information on how to build, see the chapter on Compilation. For more information on XASM check its home page: http://atariarea.krap.pl/x-asm/.

Compilation

To compile MADS source, you can use the Delphi compiler, if Delphi 7.0 or later is installed. Another, more multi-platform way is to use the compiler package from the Free Pascal Compiler (FPC), which can be downloaded from: http://www.freepascal.org/

Run the installer, and select the folder in which you installed FPC. It is important not to use a directory name which contains '!' or other special characters, or else you will not be able to compile any files, and it should probably not be a standard path name. The command-line to start compilation could look like this (the case in the parameter names is important):
fpc -Mdelphi -v mads.pas

    -Mdelphi    compile in Delphi mode
    -v          show all errors and warnings
    -O3         enable code optimization
Compared to the Delphi compiler, FPC generates bigger code, but the speed of the compiled MADS is much faster, even by a few seconds. The included MADS.EXE is compiled using FPC.

DIFFERENCES AND SIMILARITIES BETWEEN MADS AND XASM

Similarities

Differences and new behaviors

USAGE

Assembler switches

Syntax: mads source [switches]

-b:address      Generate binary file at specific address
-c              Label case sensitivity
-d:label=value  Define a label
-f              CPU command at first column
-hc[:filename]  Header file for CC65
-hm[:filename]  Header file for MADS
-i:path         Additional include directories
-l[:filename]   Generate listing
-m:filename     File with macro definition
-o:filename     Set object file name
-p              Print fully qualified file names in listing and error messages
-s              Silent mode
-t[:filename]   List label table
-x              Exclude unreferenced procedures
-vu             Verify code inside unreferenced procedures
-u              Warn of unused labels

The default filenames are:

source.lst
source.obx
source.lab
source.h
source.hea
source.mac

If no extension is specified for the source file, MADS will default to the extension .ASM.

Switches can be given in any order, preceded by '/' or '-', and case does not matter. Also, switches can be combined:
mads -lptd:label=value -d:label2=value source.asm
mads -l  -p  -t  source
mads source.asm  -lpt
mads.exe "%1" -ltpi:"d:\!atari\macro\"
mads -i:"c:\atari\macros\" -c source.asm  -lpt
By default, after assembly MADS will save the output with the extension '.OBX', which can be changed with a batch file:
mads "%1" -o:%~n1.xex
You can learn more about the operators by entering "CALL /?" in the Microsoft Windows command shell.

-b:address
Using the -b switch allows specification of a target address for a file that does not specify an address itself via the the ORG pseudo-command.

-c
The -c switch causes label, variable, and constant names to be treated as case-sensitive. Assembler directives and 6502/65816 CPU instructions are always recognized regardless of case.

-d:label=value
Use the -d switch to define a new label in MADS memory from the command-line. This switch can be used multiple times in a single invocation of MADS, which is useful when assembling from a batch (*.BAT) file.

-f
Use the -f switch to allow CPU instructions to be recognized in the first column instead of just labels.

-hc[:filename]
The switch -hc saves a header file for the CC65 compiler. It also lets you specify a new name for the file. The default extension for the CC65 header file is *.H.

-hm[:filename]
The switch -hm saves a header file for MADS. It also lets you specify a new name for the file. The default extension for the MADS is *.HEA. Such a file contains information about the banks and values assigned to labels. Additionally, the labels are grouped by their types: CONSTANTS, VARIABLES, PROCEDURES.

-i:path
The -i switch is used to set the search path for ICL and INS. This switch can be use multiple times in the same command-line to MADS
Example: mads -i:"D:\program files" -i:C:\temp -i:"D:\atari project" ...

-l:filename
The -l switch enables writing of a listing file. It also lets you specify a new name for the file.

-m:filename
The -m switch specifies a file with macro definitions, which MADS assembles before the main *.ASM file.

-o:filename
The -o switch specifies a new name for the Atari DOS or SpartaDOS X executable file, which is created at the end of assembly.

-p
The -p switch is useful in conjunction with Code Genie and WUDSN IDE. When an error occurs during assembly, MADS generated error messages will appear in the Output Bar of Code Genie:
Example: D:\!Delphi\Masm\test.asm (29) ERROR: Missing .PROC
Now just double-click the message line, and the cursor in the editor will move to the line with the error. In WUDSN IDE, error messages are added automatically to the Problems view with the correct line number.

-s
Use the -s switch to activate the so-called 'Silent mode', where no messages will be displayed, only errors (ERROR) and warnings (WARNING).

-t[:filename]
The -t switch writes a file with referenced label definitions. It also lets you specify an alternate name for the file.

-x
The -x switch skips assembly of unreferenced procedures defined with the .PROC directive.

-vu
The -vu switch enables verification of code inside procedures even if they are not emitted, usually used in conjunction with -x.

-u
The -u switch lists unreferenced labels in the program.

Exit codes

The following exit codes are returned by the executable. Warnings do not change the value of the exit code.
  0 = no errors
  2 = error occurred
  3 = bad parameters, assembling not started

.LST file format

The listing format is the same as XASM, except that the virtual memory bank is added before the address (if the bank is <>0). For more on virtual banks, see Memory banks.
  3
  4 = 01,9033        picture equ $9033
  5 = 01,00A0        scr1 equ $a0
  6
  7
  8 01,2000 EA       main nop

.LAB file format

As with XASM, the *.LAB file stores information about labels in the program.

There are three columns: Virtual bank numbers with values >= $FFF9 have special meanings:
 $FFF9   label for parameter in procedure defined by .PROC
 $FFFA   label for array defined by .ARRAY
 $FFFB   label for structured data defined by the pseudo-command DTA STRUCT_LABEL
 $FFFC   label for SpartaDOS X symbol defined by SMB
 $FFFD   label for macro defined by .MACRO directive
 $FFFE   label for structure defined by .STRUCT directive
 $FFFF   label for procedure defined by .PROC directive

Characters with special meanings in label names:

The numeric value after :: is the number of the macro call.

Mad-Assembler v1.4.2beta by TeBe/Madteam
Label table:
00	0400	@STACK_ADDRESS
00	00FF	@STACK_POINTER
00	2000	MAIN
00	2019	LOOP
00	201C	LOOP::1
00	201C	LHEX
00	0080	LHEX.HLP
00	204C	LHEX.THEX
00	205C	HEX
00	205C	HEX.@GETPAR0.LOOP
00	2079	HEX.@GETPAR1.LOOP

.H file format

I'm not sure if the file is OK, but Eru wanted something so here it is. :) It is useful for linking together ASM and CC65, or C ported to the Atari. Its contents can look like this (example file TEST.ASM):
#ifndef _TEST_ASM_H_
#define _TEST_ASM_H_

#define TEST_CPU65816 0x200F
#define TEST_CPU6502 0x2017
#define TEST_TEXT6502 0x201F
#define TEST_TEXT65816 0x2024

#endif

MACRO COMMANDS

The purpose of macro commands is to save time when writing programs and to shorten listings. Macro commands replace commonly used groups of instructions.
 REQ, RNE, RPL, RMI, RCC, RCS, RVC, RVS

 SEQ, SNE, SPL, SMI, SCC, SCS, SVC, SVS

 JEQ, JNE, JPL, JMI, JCC, JCS, JVC, JVS

 ADD, SUB

 ADB, SBB

 ADW, SBW

 PHR, PLR

 INW, INL, IND, DEW, DEL, DED

 MVA, MVX, MVY

 MWA, MWX, MWY

 CPB, CPW, CPL, CPD

REQ, RNE, RPL, RMI, RCC, RCS, RVC, RVS

These macro commands are similar to the 6502 branch instructions BEQ, BNE, BPL, BMI, BCC, BCS, BVC, BVS, but branch to the previously assembled instruction ("repeat").
  lda:cmp:req 20          ->      lda 20
                          -> wait cmp 20
                          ->      beq wait
  ldx #0                  ->      ldx #0 

  mva:rne $500,x $600,x+  -> loop lda $500,x
                          ->      sta $600,x
                          ->      inx
                          ->      bne loop

SEQ, SNE, SPL, SMI, SCC, SCS, SVC, SVS

These macro commands are similar to the 6502 branch instructions BEQ, BNE, BPL, BMI, BCC, BCS, BVC, BVS, but branch over the next assembled instruction ("skip").
  lda #40        ->      lda #40
  add:sta  $80   ->      clc
  scc:inc  $81   ->      adc $80
                 ->      sta $80
                 ->      bcc skip
                 ->      inc $81
                 -> skip

JEQ, JNE, JPL, JMI, JCC, JCS, JVC, JVS

These macro commands are similar to the 6502 branch instructions BEQ, BNE, BPL, BMI, BCC, BCS, BVC, BVS, but can target the entire 64KB address space ("jump"). If the distance is short (in the range of [-128..+127] bytes), MADS uses a regular BEQ, BNE, BPL, BMI, BCC, BCS, BVC, or BVS branch instruction.
  jne dest  -> beq *+4
            -> jmp dest

ADD, SUB

These macro commands add or subtract from the accumulator without including the carry flag.
  add  -> clc        sub  -> sec
       -> adc ...         -> sbc ...

ADB, SBB

These macro commands add or subtract a value from a byte location in memory and store it either back or to another location.
  adb src #$40 -> lda src       adb a b c  -> lda a
               -> clc                      -> clc
               -> adc #$40                 -> adc b
               -> sta src                  -> sta c

  sbb src #$80 -> lda src       sbb a b c  -> lda a
               -> sec                      -> sec
               -> sbc #$80                 -> sbc b
               -> sta src                  -> sta c

ADW, SBW

These macro commands add or subtract a value from a word location in memory and store it either back or to another location.
  adw src #$40 -> clc             adw a b c  -> clc
               -> lda src                    -> lda a
               -> adc #$40                   -> adc b
               -> sta src                    -> sta c
               -> scc                        -> lda a+1
               -> inc src+1                  -> adc b+1
                                             -> sta c+1

  adw src #$40 src -> clc
                   -> lda src
                   -> adc #$40
                   -> sta src
                   -> lda src+1
                   -> adc #$00
                   -> sta src+1
                                             
  sbw src #$4080 -> sec           sbw a b c  -> sec
                 -> lda src                  -> lda a
                 -> sbc <$4080               -> sbc b
                 -> sta src                  -> sta c
                 -> lda src+1                -> lda a+1
                 -> sbc >$4080               -> sbc b+1
                 -> sta src+1                -> sta c+1 

PHR, PLR

These macros are similar to the 6502 mnemonics PHA and PLA, but push or pop the A, X, and Y registers to and from the stack.
  phr  -> pha         plr  -> pla
       -> txa              -> tay
       -> pha              -> pla
       -> tya              -> tax
       -> pha              -> pla

INW, INL, IND, DEW, DEL, DED

The macro commands INW, INL, and IND increment 16-bit (word), 24-bit (long), and 32-bit (dword) memory locations.

The macro commands DEW, DEL, and DED decrement 16-bit (word), 24-bit (long), and 32-bit (dword) memory locations. The accumulator is also changed after the decrement macro commands.

  inw dest  ->      inc dest    ->  inc dest
            ->      bne skip    ->  sne
            ->      inc dest+1  ->  inc dest+1
            -> skip             ->

  dew dest  ->      lda dest    ->  lda dest
            ->      bne skip    ->  sne
            ->      dec dest+1  ->  dec dest+1
            -> skip dec dest    ->  dec dest   

MVA, MVX, MVY

The macro commands MVA, MVX, MVY transfer bytes using the A, X, or Y registers, respectively. OPT R+ can potentially reduce the code size from these macros by removing redundant instructions.
  lda src    -> mva src dst
  sta dst    ->

  ldy $10,x  -> mvy $10,x $a0,x
  sty $a0,x  ->

  ldx #$10   -> mvx #$10 dst
  stx dst    ->

MWA, MWX, MWY

The macro commands MWA, MWX, MWY transfer words using the A, X, or Y registers, respectively. OPT R+ can potentially reduce the code size from these macros by removing redundant instructions.
  ldx <adr    -> mwx #adr dst
  stx dst     ->
  ldx >adr    ->
  stx dst+1   ->

  mwa #0 $80  -> lda #0           mwy #$3040 $80  -> ldy <$3040
              -> sta $80                          -> sty $80
              -> sta $81                          -> ldy >$3040
                                                  -> sty $81

  mwa ($80),y $a000,x  -> lda ($80),y
                       -> sta $a000,x
                       -> iny
                       -> lda ($80),y
                       -> sta $a001,x

CPB, CPW, CPL, CPD

The macro commands CPB, CPW, CPL, and CPD perform comparison of byte, word (16-bit), long (24-bit), and dword (32-bit) values, respectively.
  cpw temp #$4080
  bcc skip

  cpd v0 v1
  beq skip

PSEUDO-COMMANDS

Pseudo-commands are not directly translated into code but control the assembly itself and the content of the output file.
  IFT [.IF] expression
  ELS [.ELSE]
  ELI [.ELSEIF] expression
  EIF [.ENDIF]
 
  ERT ERT 'string'["string"] | ERT expression

  label EQU expression
  label  =  expression

  label SET expression
 
  label EXT type

  OPT [bcfhlmorst][+-]
  ORG [[expression]]address[,address2]
  INS 'filename'["filename"][*][+-value][,+-ofset[,length]]
  ICL 'filename'["filename"]
  DTA [abfghltv](value1,value2...)[(value1,value2...)]
  DTA [cd]'string'["string"]
  RUN expression
  INI expression
  END [.EN]

  SIN (centre,amp,size[,first,last])
  RND (min,max,length)

  :repeat

  BLK N[one] X
  BLK D[os] X
  BLK S[parta] X
  BLK R[eloc] M[ain]|E[xtended]
  BLK E[mpty] X M[ain]|E[xtended]
  BLK U[pdate] S[ymbols]
  BLK U[pdate] E[xternal]
  BLK U[pdate] A[dress]
  BLK U[pdate] N[ew] X 'string'

 label  SMB 'string'

  NMB
  RMB
  LMB #expression
These are mostly the same as in XASM, although there are some differences with quoted strings. Both '' and "" are treated the same except in operands, where '' gives the ATASCII code of a character, and "" gives the INTERNAL code.

BLK

The pseudo-command BLK defines and updates SpartaDOS X blocks which allow for relocatable code.
  BLK N[one] X                    - set program counter to X without adding a header block

  BLK D[os] X                     - DOS block with header $FFFF or without header when
                                    the program counter is already set to X

  BLK S[parta] X                  - fixed address block with header $FFFA, set program counter to X

  BLK R[eloc] M[ain]|E[xtended]   - relocatable block in MAIN or EXTENDED memory

  BLK E[mpty] X M[ain]|E[xtended] - relocatable block reserving MAIN or EXTENDED memory
                                    Note: The program counter is immediately raised by X bytes

  BLK U[pdate] S[ymbols]          - update block for updating previous SPARTA or RELOC blocks with the
                                    addresses of SDX symbols

  BLK U[pdate] E[xternal]         - update block for updating addresses of external labels ($FFEE header)
                                    Note: Does not apply to SpartaDOS X, as this is a MADS extension

  BLK U[pdate] A[dress]           - update block for addresses in RELOC blocks

  BLK U[pdate] N[ew] X 'string'   - block declaring a new symbol 'string' in a RELOC block
                                    with address X. If the symbol name is prefixed with @,
                                    the address is in main memory and can be invoked by
                                    command.com
For more information on SpartaDOS X blocks, see SpartaDOS X and Atari DOS file formats and Programming SpartaDOS X.

label SET expression

The pseudo-command SET redefines a label, with similar effect as temporary labels that begin with '?'.
Example: redfine label temp

temp set 12
     lda #temp

temp set 23
     lda #temp

label SMB 'string'

The pseudo-command SMB declares a label as a SDX symbol. The symbol name can have a maximum of 8 characters. The pseudo-command BLK UPDATE SYMBOLS will instruct the assembler then to generate the correct update block for symbols.
Example: declare and use SDX symbol

  pf  smb 'PRINTF'
      jsr pf
      ...
This example code will instruct the SDX system to insert the correct address into the JSR instruction.
Note: This statement is not transitive, so the following example will cause errors at compile time:
  cm  smb 'COMTAB'
  wp  equ cm-1     -> ERROR!
      sta wp
Use this instead:
  cm  smb 'COMTAB'
      sta cm-1     -> OK

Note: Use labels for all symbols and declare before usage for correct operation!

:repeat

The ':' operator specifies the number of times to repeat the line. The repeat count should be in the range [0..2147483647]. When repeating a line with ':repeat', it is possible to use the loop counter in the repeated line using a hash sign ('#') or the parameter :1.
Example: repeat code

       :4   asl @
       :2   dta a(*)
       :256 dta #/8

  ladr :4   dta l(line:1)
  hadr :4   dta h(line:1)           
Within macros, the ':' specifies a macro parameter by number if it is in decimal. When attempting to use the character ':' to repeat lines in a macro:
  .macro test
    :2 lsr @
  .endm
The ':' prefixed number in this case is interpreted as the second macro parameter. To prevent this interpretation by MADS, add something after the ':' that does nothing, such as the plus sign ('+').
  .macro test
    :+2 lsr @
  .endm

Now, the ':' is correctly interpreted as :repeat.

OPT

The OPT pseudo-command allows options to be enabled or disabled during assembly.
  b+  enable bank sensitivity
  b-  disable bank sensitivity                                  (default)
  c+  use 65816 instruction set (16bit)
  c-  use 6502 instruction set (8bit)                           (default)
  f+  emit file as a single block (useful for cartridges)
  f-  emit file as multiple blocks                              (default)
  h+  write DOS executable file headers                         (default)
  h-  omit DOS executable file headers
  l+  enable writing to listing file (*.LST)
  l-  disable writing to listing file (*.LST)                   (default)
  m+  expand macros in listing
  m-  include only macro invocation in listing                  (default)
  o+  enable writing to object file (*.OBX)                     (default)
  o-  disable writing to object file (*.OBX)
  r+  enable optimization for MVA, MVX, MVY, MWA, MWX, MWY
  r-  disable optimization for MVA, MVX, MVY, MWA, MWX, MWY     (default)
  s+  print listing to screen
  s-  do not print listing to screen                            (default)
  t+  enable SEP/REP tracking (65816 CPU)
  t-  disable SEP/REP tracking (65816 CPU)                      (default)
  ?+  labels beginning with '?' are local (MAE style)
  ?-  labels beginning with '?' are temporary                   (default)
Example:
 
  opt c+ c  - l  + s +	;Sequence is important, so result is c-
  opt o +               ;Spaces are ignored
  opt h-;

All options controlled by OPT can be used anywhere in the listing, e.g. if it is turned on at line 12 and off at line 20 then the listing will only contain lines 12 through 20.

OPT c+ is required to use 65816 addressing modes.

If you use CodeGenie and 'OPT s+', the listing file is unnecessary as the listing is printed in the lower pane (Output Bar).

ORG [[header]][,adr][,loadadr]

The ORG pseudo-command sets the new assembly address and therefore the new position for data in RAM. Different types and standard and user defined headers can be created in the output file, so the DOS can load the data to the correct position.
  adr                    - assembles to ADR, setting the address in the header to ADR
  adr,loadadr            - assembles to ADR, setting the address in the header to LOADADR
  [b($ff,$fe)]           - writes $FFFE header (will generate 2 bytes)
  [$ff,$fe],adr          - writes $FFFE header, setting the address in the header to ADR
  [$d0,$fe],adr,loadadr  - writes $D0FE header, assemble at address ADR, set the address in the header to LOADADR
  [a($FFFA)],adr         - writes SpartaDOS $FAFF header, set address in the header to ADR
Example: disable standard header and generate own header

 opt h- 
 org [a($ffff),d'atari',c'ble',20,30,40],adr,loadadr
Brackets [ ] are used to write a new header, which can be any length. Other values following closing ']', separated by a comma, are the assembly address and the address in the header.

Here is an example of a file with a single header, assembled at address $2000, with valid block start and end addresses in the header.

Example: gerenate DOS header

  opt h-f+
  org [a(start), a(over-1)],$2000

start
   nop
   .ds 128
   nop
over

INS 'filename'["filename"][*][+-value][,+-ofset[,length]]

The INS pseudo-command allows inclusion of an external binary file. The included file does not have to be in the same directory as the main file being assembled. Search paths for the file can be configured using the -i switch (see Assembler switches).

Additionally, you can perform the following operations on the binary data:
  *          invert bytes
  +-VALUE    increase or decrease each byte by the value of the expression VALUE

  +OFSET     skip OFSET bytes at the beginning of the file     (seek to OFSET)
  -OFSET     read OFSET bytes at the end of the file           (seek to FILE_LENGTH-OFSET)

  LENGTH     read LENGTH bytes from the file
If the LENGTH value is not specified, the default behavior is to read to the end.

ICL 'filename'["filename"]

The pseudo-command ICL includes an additional source file in the assembly process. The attached file does not have to be in the same directory as the main assembly file. Additional paths are added to the MADS search path using the -i switch (see Assembler switches).

DTA

The pseudo-command DTA defines typed data. If the type is not specified, byte data (b) is assumed. The following types are supported:
  b   byte data (8-bit)
  a   word data (16-bit)
  v   relocatable WORD data (16-bit)
  l   byte data (8-bit)
  h   byte data (8-bit)
  t   long data (24-bit)
  e   long data (24-bit)
  f   double word data (32-bit)
  g   double word data (32-bit) in reversed byte order (big-endian)
  c   ATASCII string, delimited by '' or ""
      Using a star (*) at the end encodes inverse video, e.g. dta c'abecadlo'*
  d   INTERNAL string, delimited by '' or "";
      Using a star (*) at the end encodes inverse video, e.g. dta d'abecadlo'*
Example:

  dta 1 , 2, 4
  dta a ($2320 ,$4444)
  dta d'sasasa', 4,a ( 200 ), h($4000)
  dta c 'file', $9b
  dta c'invers'*

SIN(center,amp,size[,first,last])

Generates a sine table when used with the DTA pseudo-command. CENTER is a number which is added to every sine value. AMP is the sine amplitude and SIZE is the sine period. The optional arguments FIRST and LAST define range for which the sine values are generated. The default values are FIRST=0 and LAST=size-1. The following example defines table of 64 words representing a quarter of a sine with amplitude of 1000.
Example: generate sine table

  dta a(sin(0,1000,256,0,63))    

RND(min,max,length)

This pseudo-command generates LENGTH pseudorandom values in the range [MIN..MAX].
Example: genenate random value

  dta b(rnd(0,33,256))

IFT, ELS, ELI, EIF

These pseudo-commands and directives conditionally include lines based on the value of an expression.
 ift [.if] expression
 els [.else]
 eli [.elseif] expression
 eif [.endif]

DIRECTIVES

  .ALIGN n[,fill]
 
  .ARRAY label index type [= default_value]
  .ENDA, [.AEND]

  .DEF label [= expression]

  .ENUM label
  .ENDE, [.EEND]

  .ERROR [ERT] 'string'["string"] or .ERROR [ERT] expression

  .EXTRN label [,label2,...] type

  .IF [IFT] expression
  .ELSE [ELS]
  .ELSEIF [ELI] expression
  .ENDIF [EIF]

  .IFDEF label
  .IFNDEF label

  .LOCAL label
  .ENDL, [.LEND]

  .LINK 'filename'
 
  .MACRO label
  .ENDM, [.MEND]
  :[%%]parameter
  .EXITM [.EXIT]

  .NOWARN

  .PRINT [.ECHO] 'string1','string2'...,value1,value2,...

  .PAGES [expression]
  .ENDPG, [.PGEND] 

  .PUBLIC, [.GLOBAL], [.GLOBL] label [,label2,...]

  .PROC label
  .ENDP, [.PEND]
  .REG, .VAR

  .REPT expression [,parameter1, parameter2, ...]
  .ENDR, [.REND]
  .R

  .RELOC [.BYTE|.WORD]

  .STRUCT label
  .ENDS, [.SEND]

  .SYMBOL label
 
  .SEGDEF label address length [bank]
  .SEGMENT label
  .ENDSEG
 
  .USING, [.USE] proc_name, local_name

  .VAR var1[=value],var2[=value]... (.BYTE|.WORD|.LONG|.DWORD)
  .ZPVAR var1, var2... (.BYTE|.WORD|.LONG|.DWORD)

  .END

  .EN

  .BYTE
  .WORD
  .LONG
  .DWORD

  .OR
  .AND
  .XOR
  .NOT

  .LO (expression)
  .HI (expression)

  .DB
  .DW
  .DS expression

  .BY [+byte] bytes and/or ASCII
  .WO words
  .HE hex bytes
  .SB [+byte] bytes and/or ASCII
  .CB [+byte] bytes and/or ASCII
  .FL floating point numbers

  .ADR label
  .LEN label

  .GET [index] 'filename'["filename"][*][+-value][,+-offset[,length]]
  .PUT [index] = value
  .SAV [index] ['filename',] length

.SYMBOL label

The directive .SYMBOL is equivalent to the pseudo-command SMB except that the symbol name does not need to be specified, as it is implied to be LABEL. A .SYMBOL directive can be placed anywhere within a relocatable SDX block defined by BLK RELOC, unlike SMB.

Whenever there is a .SYMBOL directive, the following block update will be generated:

  BLK UPDATE NEW LABEL 'LABEL'
More on the declaration of SDX symbols is found in the section for the Pseudo-command SMB.

.ALIGN n [,fill]

The directive .ALIGN aligns the assembly address to the value N, using FILL as the memory preset value for any needed fill. It is possible to align relocatable code provided that the FILL value is given. The default values are N=$0100 and FILL=0.
Example:

 .align
 .align $400
 .align $100,$ff

.REPT expression [,parameter1, parameter2, ...]

The directive REPT is the same as the :repeat operator, except that a block is repeated instead of a single line. The beginning of the block is defined with the .REPT directive, which is followed by a repeat count in the range [0..2147483647] and then optional parameters. Unlike macro parameters, the parameters to .REPT are always calculated first and the result is substituted immediately (this feature can be used to define new labels). The parameters specified in the .REPT block are used like parameters to a .MACRO block. A .REPT block is ended using the .ENDR directive, before which there should be no label.

Additionally, within a .REPT....ENDR block, the hash sign '#' (or directive .R) gives the current value of the loop counter (like for :repeat).

Example:

  .rept 12, #*2, #*3        ; A .REPT block can be combined with :rept
  :+4 dta :1                ; :+4 to distinguish from repeat block parameter :4
  :+4 dta :2
  .endr

  .rept 9, #                ; Define 9 labels label0..label8
label:1 mva #0 $d012+#
  .endr

.PAGES [expression]

The directive .PAGES specifies that a piece of code delimited by a .PAGES .. .ENDPG block should fit within a number of memory pages (the default is 1). If the program code exceeds the given number of memory pages, the error message Page error at $xxxx is generated.

These directives can help if you want part of the program to be within one memory page, or if you write a program stored in an additional memory bank (64 pages of memory), such as:

Example:

  org $4000
 
  .pages $40
   ...
  .endpg

.SEGDEF label address length [attribute] [bank]
.SEGMENT label
.ENDSEG

The directive .SEGDEF defines a new segment LABEL at address ADDRESS and with length LENGTH. Read/write attributes (R-read, W-write, RW-read/write) can be assigned to the segment, as well as a virtual bank number BANK (default=0).

The .SEGMENT directive begins writing of code and data to the code segment LABEL. Exceeding the preset length of the segment generates the error message Segment LABEL error at $xxxx where XXXX is the offset in the segment.

The directive .ENDSEG ends writing to the current segment and returns to the main program block.

Example:

      .segdef sdata adr0 $100
      .segdef test  adr1 $40

      org $2000

      nop
      .cb 'ALA'

      .segment sdata
      nop
      .endseg

      lda #0

      .segment test
      ldx #0
      clc
      dta c'ATARI'
      .endseg

adr0  .ds $100
adr1  .ds $40 

.END

The directive .END can be used interchangeably with the directives .ENDP, .ENDM, .ENDS, .ENDA, .ENDL, .ENDR, .ENDPG, .ENDW, and .ENDT.

.VAR var1[=value1],var2[=value2]... (.BYTE|.WORD|.LONG|.DWORD) [=address]

The directive .VAR is used to declare and initialize variables in the main program block and in .PROC and .LOCAL blocks. MADS does not use the information in these variables in further operations involving pseudo and macro commands. Acceptable types of variables are .BYTE, .WORD, .LONG, and .DWORD, multiples of those types, and types declared by .STRUCT and .ENUM.
Example:

  .var a,b , c,d   .word          ; 4 variables of type .WORD
  .var a,b,f  :256 .byte          ; 3 variables each with a size of 256 bytes
  .var c=5,d=2,f=$123344 .dword   ; 3 .DWORD variables with values 5, 2, and $123344

  .var .byte i=1, j=3             ; 2 variables of type .BYTE with values 1, 3

  .var a,b,c,d .byte = $a000      ; 4 variables of type .BYTE with addresses $A000, $A001, $A002, $A003
 
  .var .byte a,b,c,d = $a0        ; 4 variables of type .BYTE, with the last variable 'D' having address $A0
                                  ; Note: In this form it is not possible to determine the address of variables

  .proc name
  .var .word p1,p2,p3             ; Declare three variable of type .WORD
  .endp

  .local
      .var a,b,c .byte
      lda a
      ldx b
      ldy c
  .endl

  .struct Point                   ; New structure of type Point
      x .byte
      y .byte
  .ends

  .var a,b Point                  ; Declare structured variables
  .var Point c,d                  ; Equivalent to 'label DTA Point'
Declared variables are physically allocated at the end of the block, as determined by the directives .ENDP, .ENDL, or .END. The exception is that in a .PROC block, variables declared with .VAR are always allocated in front of the .ENDP directive even if the .VAR statements are within nested .LOCAL blocks.

.ZPVAR var1, var2... (.BYTE|.WORD|.LONG|.DWORD) [=address]

The directive .ZPVAR is used to declare zero page variables in the main program block and in .PROC and .LOCAL blocks. Attempting to assign to or initialize a variable of this type will generate the warning message Uninitialized variable. MADS does not use the information in these variables in further operations involving pseudo and macro commands. Acceptable variable types are .BYTE, .WORD, .LONG, .DWORD, multiples of those types, and types declared by .STRUCT and .ENUM:
Example:

  .zpvar a b c d  .word = $80     ; 4 variables of type .WORD starting at address $0080
  .zpvar i j .byte                ; two more byte variables starting at address $0080+8

  .zpvar .word a,b                ; 2 variables of type .WORD
                                  ; Note: In this form it is not possible to determine the address of variables

  .struct Point                   ; Declare new structure Point
      x .byte
      y .byte
  .ends

  .zpvar a,b Point                ; Declare structured variables
  .zpvar Point c,d                ; Equivalent to 'label DTA Point'                                
The zero page variable will be assigned addresses only at the end of the block in which it was declared, at .ENDP, .ENDL, or .END. The exception is a .PROC block where variables declared with .ZPVAR are assigned at the .ENDP directive even if variables are declared within nested .LOCAL blocks.

Using .ZPVAR with only an address will specify the first address to assign to the next variable. The default address is $0080.

Example:

 .zpvar = $40                       
The address is automatically incremented by MADS and the warning message Access violations at address $xxxx is generated if addresses are repeated. In the case of a zero page overflow, the error message Value out of range is generated.

.PRINT [.ECHO]

Prints on screen parameter values or string expressions delimited by '' or "" quotes:
Example:

  .print "End: ",*,'..',$8000-*
  .echo  "End: ",*,'..',$8000-*

(Editor's note: Values are printed out as hexadecimal with a $ prefix.)

.ERROR [ERT] 'string'["string"] | .ERROR [ERT] expression

The directive .ERROR and the pseudo-command ERT are equivalent and stop assembly, displaying a message given as a parameter delimited by '' or "". If the parameter is a boolean expression, then assembly will stop and generate the error message User error if EXPRESSION is true.
Example:

  ert    "halt"         ; ERROR: halt
  .error "halt"

  ert    *>$7fff        ; ERROR: User error
  .error *>$7fff

.BYTE, .WORD, .LONG, .DWORD

These directives are used to determine the allowable types for parameters in a procedure declaration. They can also be used instead of the pseudo-command DTA for data definition.
Example:

  .proc test (.word tmp,a,b .byte value)

  .byte "atari",5,22
  .word 12,$FFFF
  .long $34518F
  .dword $11223344

.DB

This directive defines data of type BYTE. It is equivalent to the pseudo-command DTA B or the .BYTE directive.

.DW

This directive defines data of type WORD. It equivalent to the pseudo-command DTA A or the .WORD directive.

.DS expression

This directive has been adapted from MAC'65 and reserves uninitialized memory. This is equivalent to the pseudo-command ORG *+expression. The .DS directive cannot be used in relocatable code like ORG can.
Example:

  bufferlen .ds 1    ; Reserves a single byte
  buffer    .ds 256  ; Reserves 256 bytes

.BY [+byte] bytes and/or ASCII

This directive stores byte values in memory. ASCII strings can be specified by enclosing the string in either single or double quotes. If the first character of the operand field is a '+', then the following byte will be used as a constant and added to all remaining bytes of the instruction.
Example:

  .by +$80 1 10 $10 'Hello' $9B	; Generates 81 8A 90 C8 E5 EC EC EF 1B
Values in .BY statements may also be separated with commas for compatibility with other assemblers. Spaces are allowed since they are easier to type.

.WO words

This directive stores words in memory. Multiple words can be entered. Values in .WO statements may also be separated with commas for compatibility with other assemblers. Spaces are allowed since they are easier to type.

.HE hex bytes

This directive stores hex bytes in memory. This is a convenient method to enter strings of hex bytes, since it does not require using the '$' character. The bytes are separated by spaces, which I feel makes a much more readable layout than the 'all run together' form of hex statement that some other assemblers use.
Example:

  .HE 0 55 AA FF
Values in .HE statements may also be separated with commas for compatibility with other assemblers. Spaces are allowed since they are easier to type.

.SB [+byte] bytes and/or ASCII

This directive is the same as the .BY pseudo-command, except that it will convert all bytes into ATASCII screen codes before storing them. The ATASCII conversion is done before any constant is added with the '+' modifier.

Values in .SB statements may also be separated with commas for compatibility with other assemblers. Spaces are allowed since they are easier to type.

.CB [+byte] bytes and/or ASCII

This directive is the same as the .BY pseudo-command, except that the last character on the line will be EOR'ed with $80.

Values in .CB statements may also be separated with commas for compatibility with other assemblers. Spaces are allowed since they are easier to type.

.FL floating point numbers

This directive stores 6-byte BCD floating point numbers for use with the OS floating point ROM routines.

Values in .FL statements may also be separated with commas for compatibility with other assemblers. Spaces are allowed since they are easier to type.

.EN

The directive .EN is equivalent to the pseudo-command END and ends the assembly.

This is an optional pseudo-command to mark the end of assembly. It can be placed before the end of your source file to prevent the remaining portion of it from being assembled.

.ADR label

The directive .ADR returns the address of LABEL before the assembly address is changed. You can put LABEL between parentheses or square brackets.
Example:

  org $2000

  .proc tb,$1000
  tmp lda #0
  .endp

  lda .adr tb.tmp   ; = $2000
  lda tb.tmp        ; = $1000

.LEN label

The directive .LEN returns the length in bytes of a block declared with .PROC, .ARRAY, .LOCAL, or .STRUCT. The LABEL is the name of the .PROC, .ARRAY, .LOCAL, or .STRUCT block. It is possible to put the name LABEL between parentheses or square brackets.
 
Example:

label
  .array [255] .dword
  .enda

  dta a(.len label) ; = $400

  .proc wait
  lda:cmp:req 20
  rts
  .endp

  dta .len wait     ; = 7

.DEF label [= expression]

The directive .DEF is used as unary operator to check for the presence of the LABEL definition or to define it. If the label is defined it returns 1 or TRUE, otherwise it returns 0 or FALSE. It is possible to put the name LABEL between parentheses or square brackets.
Example:

  ift .not(.def label)
   .def label
  eif
Note: Defining a label after the use of a .DEF which references it can be dangerous, particularly if the .DEF is used in an .IF directive.

.IFDEF label

The directive .IFDEF is equivalent to .IF .DEF LABEL.
Example:

  .ifdef label
      jsr proc1
  .else
      jsr proc2
  .endif

.IFNDEF label

The directive .IFNDEF is equivalent to .IF .NOT .DEF LABEL.
Example:

  .ifndef label
      clc
  .else
      sec
  .endif
In the following example, the .IFNDEF (.IF) block will be processed and the label defined only when the block is first encountered. If there are any errors associated with their definition, this will only be reported when a reference to one of them is attempted, resulting in the error message Undeclared label LABEL.
Example:

 .ifndef label
     .def label
     lda #0           ; This will only be generated once and not be reassembled
     temp = 100       ; Label TEMP is defined only once during assembly
 .endif

.NOWARN

The directive .NOWARN disables warning messages for the current line being assembled.
Example:

  .nowarn .proc temp  ; Warning message Unreferenced procedure TEMP not generated
          .endp

.USING, [.USE] label

The directive .USING (.USE) allows an additional scope to be specified for label lookup. .USING (.USE) is valid in the current namespace.
Example:

    .local move
tmp  lda #0
hlp  sta $a000

     .local move2
tmp2 ldx #0
hlp2 stx $b000
     .endl

     .endl

    .local main
    .use move.move2
    lda tmp2        ; Will be move.move2.tmp2
    .use move
    lda tmp         ; Will be move.tmp
    .endl

.GET [index] 'filename'... [.BYTE, .WORD, .LONG, .DWORD]

This directive equivalent to the pseudo-command INS (similar syntax), except that the file is loaded into the MADS memory instead of beining included in the assembly. This directive allows the specified file to be loaded into memory and for its bytes to be referred to as a one-dimensional array.

The INDEX must be in the range [0..65535]. The values read by .GET are of type .BYTE.

Example:

  .get 'file'                     ; Load the file into a MADS array
  .get [5] 'file'                 ; Load the file into an array starting at index 5

  .get 'file',0,3                 ; Load the file into an array of size 3

  lda #.get[7]                    ; Load the value of element 7 of the file array
  address = .get[2]+.get[3]<<8    ; Use bytes 2 and 3 of the file array as an address
With the help of the directives .GET and .PUT you can transform binary files during the assmbley. For example sound modules can be relocated using macros and these directives. See MADS macro in the file "../EXAMPLES/MSX/TMC_PLAYER_RELOCATOR/TMC_RELOCATOR.MAC" for an example which relocates a module for Theta Music Composer (TMC).

.PUT [index] = value

The directive .PUT stores a value of type .BYTE into a one-dimensional array in the MADS memory. This is the same array in which the directive .GET reads a file.

The INDEX must be in the range [0..65535].

Example:

  .put [5] = 12          ; Store the value 12 into MADS memory at element 5

.SAV [index] ['filename',] length

The directive .SAV saves the MADS memory used by the directives .GET and .PUT to an external file or inserts it into the current assembly output.

The INDEX must be in the range [0..65535].

Example:

  .sav ?length            ; Insert elements [0..?LENGTH-1] to current assembly output
  .sav [200] 256          ; Insert elements  [200..200+256-1] to current assembly output
  .sav [6] 'filename',32  ; Save elements [6..6+32-1] to file FILENAME

.OR, .AND, .XOR, .NOT

These directives are equivalent to the logical operators || (.OR), && (.AND), ^ (.XOR),!/ (.NOT).

.LO (expression), .HI (expression)

These directives are equivalent to the operators < (low byte) and > (high byte), respectively.

.IF, .ELSE, .ELSEIF, .ENDIF

  .if     [ift] expression
  .else   [els]
  .elseif [eli] expression
  .endif  [eif]
These directives and pseudo-commands can be used interchangeably to conditionally assemble portions of code.
Example:

  .if .not .def label_name
    label_name = 1
  .endif

  .if [.not .def label_name] .and [.not .def label_name2]
    label_name = 1
    label_name2 = 2
  .endif
In these examples the parentheses or square brackets are required. Otherwise the parameter of the first .DEF directive would be the label label_name.AND.NOT.DEFlabel_name2. because spaces are ignored and periods are allowed in label names.

6502 CODE GENERATION DIRECTIVES

  #IF type expression [.OR type expression] [.AND type expression]
  #ELSE
  #END

  #WHILE type expression [.OR type expression] [.AND type expression]
  #END

  #CYCLE #N

#IF type expression [.OR type expression] [.AND type expression]

The #IF directive is somewhat equivalent to the IF statement of higher-level languages (C, Pascal).

The #IF, #ELSE, and #END directives produce 6502 machine code for an IF conditional statement around the designated program block and can be nested. All types are acceptable (.BYTE, .WORD, .LONG, and .DWORD). It is possible to combine terms using the .AND and .OR directives, but it is impossible to control the order of evaluation with parentheses.

The implementation of the #IF directive begins with the calculation of the value that is a simple expression consisting of two operands and one operator (expression can be combined using the .OR or .AND directives).

If the expression is non-zero (TRUE), the program block within the #IF is executed, terminated by a JMP instruction to the next instruction after #END if there is an #ELSE block.

If the expression is zero (FALSE), the code following #ELSE is executed. If there is no #ELSE directive, then control is transferred to the next instruction after the #END directive.
Example:

  #if .byte label>#10 .or .byte label<#5
  #end

  #if .byte label>#100
    #if .byte label<#200
    #end
  #end

  #if .byte label>#100 .and .byte label<#200 .or .word lab=temp
  #end

  #if .byte @
  #end

#WHILE type expression [.OR type expression] [.AND type expression]

The #WHILE directive is equivalent to the WHILE statement in higher-level languages (C, Pascal).

The directives #WHILE and #END allow generation of 6502 machine code for a loop around the given program block and can be nested. The types .BYTE, .WORD, .LONG, and .DWORD are acceptable. Multiple terms can be connected with the .OR and .AND directives, but the order of evaluation cannot be controlled by parentheses.

The sequence of operations in the expansion of the #WHILE statement is as follows:
  1. Calculate the value of the expression and check if it is equal to zero (FALSE).
    • if so, skip step 2;
    • if not (TRUE), go to step 2.
  2. Execute the code block bounded by the #WHILE and #END directives, then go to step 1.
If the first evaluation of the expression produces zero, the program block will never be executed and control passes to the next statement after #END.
Example:

  #while .byte label>#10 .or .byte label#5
  #end

  #while .byte label>#100
   #while .byte label2#200
   #end
  #end

  #while .byte label>#100 .and .byte label#200 .or .word lab=temp
  #end

#CYCLE #N

The directive #CYCLE generates code to take a given number of cycles. The generated code does not modify any memory or any general purpose register.
  #cycle #17  ; pha      3 cycles
              ; pla      4 cycles
              ; pha      3 cycles
              ; pla      4 cycles
              ; cmp $00  3 cycles
                        ---------
                        17 cycles

Assembling into zero page

Unlike two-pass assemblers like QA and XASM, MADS is a multi-pass assembler. This has many advantages. Why? Take this example.
Example: detection of zero page addressing

      org $00
 
      lda tmp+1
  tmp lda #$00
A two-pass assembler does not know the value of the label TMP. It will assume that it is 16-bit (.WORD type) and generate LDA abs. However, MADS nicely generates LDA zp because the actual size is known in the last pass. This is the simplest benefit of multiple passes. Now, suppose referring to zero page with LDA abs is explicitly desired. No problem, just extend the mnemonic to force 16-bit addressing.
Example: force 16-bit addressing

     org $00
 
     lda.w tmp+1
 tmp lda #$00
Three mnemonic extensions are allowed:
  .b[.z]      - .BYTE or zero page
  .w[.a][.q]  - .WORD or address
  .l[.t]      - .LONG or triple byte
The last one generates a 24-bit value for 65816 long addressing (seldom used). For more information 6502 and 65816 CPU mnemonics, see Mnemonics.

Another way to force zero page addressing is to use curly braces { } to generate opcodes with DTA pseudo-opcode.

Example: 

  dta {lda $00},$80 ; Generates $a5,$80, i.e. lda $80
Either will work with MADS as the last pass will do the trick. The next problem is putting this code on your computer. Loading directly into page zero will probably work if the target area is within [$80..$FF], but when loading below that the OS is unlikely to survive.

Therefore, MADS provides the possibility to relocate code. The pseudo-command ORG address1,address2 assembles at address1, but loads to address address2. The pseudo-command ORG always creates a new block in the file, which adds an additional four bytes for the header of the new block.

Example: relocate zero page code

      org $20,$3080
 
      lda tmp+1
  tmp lda #$00
This assembles at address $0020, but with a load address of $3080. Of course, moving the code to the correct address ($0020 in our example) is now the responsibility of the programmer.

If it is OK for the new address of the data in memory to be the current address, then the property of .LOCAL and .PROC blocks can be used to avoid writing a new header.

Example: relocate inline blocks

   1
   2 					org $2000
   3
   4 FFFF> 2000-200D> A9 00  lda #0
   5 2002 EA                 nop
   6
   7 0060                   .local temp, $60
   8
   9 0060 BD FF FF           lda $ffff,x
  10 0063 BE FF FF           ldx $ffff,y
  11
  12                         .endl
  13
  14 2009 A5 60              lda temp
  15 200B AD 03 20           lda .adr temp
  16
In this example the block TEMP will be assembled with the new address $60 and be placed in memory at address $2003. After the block end directive (.ENDL, .ENDP, .END), assembly will resume at the previous assembly address plus the length of the block, which in this case is $2009.

The directives .ADR and .LEN can then be used to copy the block to the correct address.

Example:

        ldy #0
  copy  mva .adr(temp),y temp,y+
        cpy #.len temp
        bne copy
For more information on the directives, see .ADR and .LEN.

TYPES

MADS allows the declaration of two types of data: structured data (.STRUCT) data and enumerated data (.ENUM).

STRUCTURED TYPES, STRUCTURES

If you have programmed in C, you have probably already encountered structures. Overall, a MADS structure defines an array of virtual, one-dimensional fields of varying .BYTE, .WORD, .LONG, or .DWORD types or multiples thereof. They are virtual because they exist only in the MADS memory during assembly.

The fields of the structure contain information about offsets from the beginning of the structure.

Declaring structures (.STRUCT)

Structures can be declared with the following directives.
  name .struct
       .ends [.send] [.end]

       .struct name
       .ends [.send] [.end]

name .STRUCT

Declares a structure with the name name preceding the .STRUCT directive. The name of the structure is required and an error is generated if it is missing. Structures cannot be named with mnemonic or pseudo-command names. If the name is reserved, a Reserved word error is reported.
Example: structure declaration

.struct name

  x .word      ; lda #name.x = 0
  y .word      ; lda #name.y = 2
  z .long      ; lda #name.z = 4
  v .dword     ; lda #name.v = 7

  q :3 .byte   ; lda #name.q = 11

.ends          ; lda #name   = 14 (length)
Each line defines a field by name and type (.BYTE, .WORD, .LONG, or .DWORD). The field name may be preceded by whitespace. Between the .STRUCT and .ENDS directives, CPU mnemonics may not be used. Attempting to do so or having other invalid characters will result in an Improper syntax or Illegal instruction error message.

In summary, the label name contains information about the total length of the structure (in bytes). The other labels describing the fields contain information about the offsets to each field from the beginning of the structure.

Structure declarations cannot be nested, but previously declared structures can be nested in other ones (declaration order does not matter). For example:
.struct temp

x .word
y .word
v .byte
z .word

.ends


.struct test

tmp  temp

.ends

 lda #temp.v
 lda #test.tmp.x
 lda #test.tmp.z

What are structures useful for?

Suppose you have a table of different types, where you can read each table field with a predetermined offset value. If a field is added to the table, or the table is modified in any other way, the program code will have to be updated to use new field offsets. Defining the table using a structure means that offsets can be determined by the structure definition, which are then automatically updated even if the structure of the table is changed.

Another example of structure usage can be found in the section on External symbols, Using external symbols with structures (.STRUCT).

Defining structured data, references

Structured data can be defined by assigning a new label with the pseudo-command DTA and a specific structure name, or just with the structure name without the pseudo-command. The result is that the virtual structure definition is turned into actual reserved memory.
label DTA struct_name [count] (data1,data2,data3...) (data1,data2,data3...) ...

label struct_name

COUNT specifies a number in the range [0..COUNT], which defines the maximum element index in a one-dimensional array and thus the amount of memory reserved.

Examples of structure and structured data declarations:
;-----------------------;
; structure declaration ;
;-----------------------;
.STRUCT temp

x .word
y .word
v .byte
z .word

.ENDS

;---------------;
; defining data ;
;---------------;

data dta temp [12] (1,20,200,32000) (19,2,122,42700)

data2 dta temp [0]

data3 temp          // shorter equivalent to DATA2
The value in square brackets must be a value between [0..2147483647], which defines the maximum value of a one-dimensional array index and thus the amount of memory reserved for the structured data.

After the square brackets an optional list of initializer values (in parentheses) may follow. Otherwise, the field values default to zero. However, if the initializer list is shorter than the number of declared fields, the remaining fields are initialized to the previous value given for those fields:

data dta temp [12] (1,20,200,32000)
Such a declaration will result in all fields being initialized to the values 1,20,200,32000, not just the first element data[0].

If the list of initializers is longer than the number of elements, the error Constant expression violates subrange bounds will result.

To refer to fields in the structured data, use its name, followed by an index in square brackets and the field name after a dot:
 lda data[4].y
 ldx #data[0].v
Forgetting the brackets with an index in the syntax label[index] results in the error message Undeclared label.

ENUMERATED TYPES, ENUMERATION

These directives are used with enumerations:
name .enum
     .ende [.eend] [.end]

Example:

.enum portb
 rom_off = $fe
 rom_on = $ff
.ende

.enum test
 a             ; a=0
 b             ; b=1
 c = 5         ; c=5
 d             ; d=6
.ende

Enumerations are declared using the directives .ENUM and .ENDE. The name of the enumeration is required and an error will be generated otherwise. Enumeration names may not be the same as mnemonics or pseudo-commands, which will produce a Reserved word error.

The label values are automatically assigned starting with a default value of 0 and incrementing by 1. You can define the value of each label directly or have them be automatically set.

Enumerated labels are referenced using this syntax:

 enum_name (field)
or directly like with .LOCAL and .PROC blocks, with a dot between the enumeration name and the field name:
 lda #portb(rom_off)

 dta portb.rom_on, portb.rom_off
Enumerations can be used for field declarations in structures (.STRUCT) and variable declarations (.VAR):
bank portb           // allocate variable BANK of size 1 byte
.var bank portb      // allocate variable BANK of size 1 byte

.struct test
 a portb
 b portb
.ends
The size of an enumeration is dependent upon the maximum value of its labels:
    .enum estate
        done, directory_search=$ff, init_loading, loading
    .ende
In this example, the enumeration "EState" will have a size of two bytes (WORD).

The size of an enumeration can be checked with the .LEN directive (equivalent to SIZEOF), where the result will be a value in the range [1..4] (1=BYTE, 2=WORD, 3=LONG, 4=DWORD):

 .print .len estate

ARRAYS

Declaring dimensional arrays (.ARRAY)

Directives for arrays:
name .array index type [= default_value]
     .array name count type [= default_value]
     .enda [.aend] [.end]
Available types are .BYTE, .WORD, .LONG, and .DWORD.

INDEX specifies the maximum permitted value of the array index range [0..INDEX]. This value can be a constant or an expression in the range [0..65535]. If INDEX is omitted, the range is determined by the number of input values.

CPU mnemonics cannot be used between .ARRAY and .ENDA, and attempting to do so or having other illegal characters will result in the error Improper syntax.

The array index used for initialization can be specified along with initializer values. A new array index is set by placing it in square brackets at the beginning of a new line, i.e. [expression]. Additional indices can be supplied, separated by a colon (':'). The array values then follow after a equals sign ('='):
.array tab .byte      ; Define array TAB with an unspecified number of elements
 1,3                  ; [0]=1, [1]=3
 5                    ; [2]=5 
 [12] = 1             ; [12]=1
 [3]:[7]:[11] = 9,11  ; [3]=9, [4]=11, [7]=9, [8]=11, [11]=9, [12]=11
.enda
This facility may seem strange and of limited use, but it is occasionally useful, such as for declaring lookup tables for translating the scan code of a pressed key or between ATASCII and INTERNAL code.
.array TAB [255] .byte = $ff   ; Allocate 256 bytes [0..255] with initial value $FF

 [63]:[127] = "A"              ; Assign new values TAB[63]="A", TAB[127]="A"
 [21]:[85]  = "B"
 [18]:[82]  = "C"
 [58]:[122] = "D"
 [42]:[106] = "E"
 [56]:[120] = "F"
 [61]:[125] = "G"
 [57]:[121] = "H"
 [13]:[77]  = "I"
 [1] :[65]  = "J"
 [5] :[69]  = "K"
 [0] :[64]  = "L"
 [37]:[101] = "M"
 [35]:[99]  = "N"
 [8] :[72]  = "O"
 [10]:[74]  = "P"
 [47]:[111] = "Q"
 [40]:[104] = "R"
 [62]:[126] = "S"
 [45]:[109] = "T"
 [11]:[75]  = "U"
 [16]:[80]  = "V"
 [46]:[110] = "W"
 [22]:[86]  = "X"
 [43]:[107] = "Y"
 [23]:[87]  = "Z"
 [33]:[97]  = " "

 [52]:[180] = $7e
 [12]:[76]  = $9b

.enda

In this example, an array TAB is created with indices [0..255] and 256 .BYTEs in size, pre-initialized to $FF. The array elements are then set to translate keyboard scan codes (both upper and lowercase, ignoring case) to INTERNAL.

The colon (':') is used to separate array indices.

Another example is to center a string:
 org $bc40

.array txt 39 .byte
 [17] = "ATARI"
.enda

In summary, the .ARRAY directive allows creation of a one-dimensional array of values with a specified type.

To refer to an array:
 lda tab,y
 lda tab[23],x
 ldx tab[200]
If the index given in square brackets exceeds the maximum index of the array, the error message Constant expression violates subrange bounds is reported.

MACROS

Macros help perform repetitive tasks by automating them. They are only kept in memory during assembly unless invoked. With their aid, MADS can push and pop parameters off a software stack for a procedure declared with the directive .PROC and switch extended memory banks with the option BANK SENSITIVE (OPT B+).

Declaring macros

Macro pseudo-commands and directives:
name .macro [arg1, arg2 ...] ['separator'] ["separator"]
     .macro name [(arg1, arg2 ...)] ['separator'] ["separator"]
     .exitm [.exit]
     .endm [.mend]
     :[%%]parameter
     :[%%]label

name .macro [(arg1, arg2 ...)] ['separator'] ["separator"]

Declares a macro named name with the directive .MACRO. The macro name is required and an error is reported without it. Macro names cannot be the same as an instruction mnemonic or pseudo-command, which will cause a Reserved word error message.

A list of named arguments can be specified for the macro, optionally wrapped in parentheses. Assigning name to macro arguments improves readability of macro code. Argument names and numeric arguments can be used interchangeably.
.macro SetColor val,reg
 lda :val
 sta :reg
.endm

At the end of the macro declaration, the argument separator and the argument parsing mode can be supplied (unchanged for single quotes, split into parameters and addressing modes for double quotes).

The default separators for macro argument parsing are a comma (',') and a space (' ').

name .macro 'separator'
Between the quotes '' we place the separator character used to separate parameters when invoking the macro (only with single quotes).
name .macro "separator"
Double quotes ("") can also be used to set separators for the macro parameters, but this also indicates to MADS that the parameters should be split into two parts: addressing mode and argument.
     test #12 200 <30

test .macro " "
     .endm
This TEST macro is declared with a space as the separator using ", which then causes the macro parameter to be divided into two parts, addressing mode and argument.
 #12   ->  addressing mode '#' argument 12
 200   ->  addressing mode ' ' argument 200
 <30   ->  addressing mode '#' argument 0   (calculated expression value of "<30")

 test '#' 12 ' ' 200 '#' 0
NOTE #1: The sign operators '<', '>' are evaluated before parameters are passed to a macro, with the result substituted as the parameter.

NOTE #2: If the macro parameter is the loop counter '#' or '.R' (!!! the single character '#' or the directive '.R', and not an expression involving one of them !!!) the value of the loop counter is substituted as the macro parameter.

This property can be used to create iterated label names like "label0", "label1", "label2", "label3"... :
 :32 find #

find .macro
      ift .def label:1
      dta a(label:1)
      eif
     .endm
In this example, the address of each numbered label is written (if it is defined).

.EXITM [.EXIT]

End the macro. This terminates the macro invocation.

.ENDM [.MEND]

The directive .ENDM or .MEND ends the current macro definition. The .END directive cannot be used as for the .LOCAL, .PROC, .ARRAY, .STRUCT, and .REPT directives.

:[%%]parameter

The parameter is a positive decimal number (>=0), followed by a colon (':') or two percent signs ('%%'). If in a macro you want to use ':' for repetition and not to signify a macro parameter, ensure that the next character after the colon is outside of the range ['0'..'9']:
 :$2 nop
 :+2 nop
 :%10 nop
Parameter :0 (%%0) has special meaning and contains the number of parameters passed. This can be used to check if the required number of parameters was passed to a macro:
  .if :0<2 || :0>5
    .error "Wrong number of arguments"
  .endif

  ift %%0<2 .or :0>5
    ert "Wrong number of arguments"
  eif 
Example: macro definition

.macro load_word

   lda <:1
   sta :2
   lda >:1
   sta :2+1   
 .endm

 test ne
 test eq

.macro test
  b%%1 skip
.endm

Calling macros

Macros are called by name, with arguments separated by default with commas (',') or spaces (' '). The maximum number of parameters is limited only by PC memory. If the number of parameters passed is fewer than the number of parameters in the macro, the missing parameters are set to the value -1 ($FFFFFFFF). This property can be used to test if a parameter has been passed, but it is easier to use parameter zero (%%0).
 macro_name [Par1, Par2, Par3, 'Par4', "string1", "string2" ...]
A parameter can be a value or a string delimited by either single quotes ('') or double quotes. ("").

All label definitions within a macro are local.

If the assembler does not find a label within the macro, it will then look in the local scope (if there was a .LOCAL directive), then in the procedure (if a procedure is currently being defined), and then finally in the main program.
Example: macro call

  macro_name 'a',a,>$a000,cmp    ; The default separator ','
  macro_name 'a'_a_>$a000_cmp    ; The declared separator '_'
  macro_name 'a' a >$a000 cmp    ; The default separator ' '
Macros can be called from other macros as well as called recursively. In the latter case, care should be taken to avoid causing stack overflow in MADS. MADS is protected against infinite recursion and will stop with the error message Infinite recursion once the nesting depth reaches 4095.
Example: cause stack overflow in MADS stack

jump .macro
      jump
     .endm
Example program that passes parameters to pseudo-procedures, from the file "..\EXAMPLES\MACRO.ASM".
Example:

 org $2000
 
 proc PutChar,'a'-64    ; Call macro PROC, with as parameters
 proc PutChar,'a'-64    ; The name of a procedure to call by JSR
 proc PutChar,'r'-64    ; and a single argument (INTERNAL character code)
 proc PutChar,'e'-64
 proc PutChar,'a'-64

 proc SetColor,$23      ; Call another procedure to change the background color

;---

loop jmp loop           ; Endless loop to show the effect

;---

proc .macro             ; Declare PROC macro
 push =:1,:2,:3,:4      ; Call PUSH macro to push arguments onto the stack
                        ; =:1 calculates the memory bank
 
 jsr :1                 ; Jump to procedure (procedure name is the first parameter)
 
 lmb #0                 ; Load memory bank, setting bank to 0
 .endm                  ; End of PROC macro

;---

push .macro             ; Declare PUSH macro

  lmb #:1               ; Set up virtual memory bank

  .if :2<=$FFFF      ; If passed argument is less than or equal to $FFFF
    lda <:2          ; put it on the stack
    sta stack
    lda >:2
    sta stack+1
  .endif 

  .if :3<=$FFFF
    lda <:3
    sta stack+2
    lda >:3
    sta stack+3
  .endif 

  .if :4<=$FFFF
    lda <:4
    sta stack+4
    lda >:4
    sta stack+5
  .endif 
 
  .endm
 

* --------------- *     ; SetColor procedure
*  PROC SetColor  *
* --------------- *
  lmb #1                ; Set virtual bank to 1
                        ; Label definitions are now local to this bank
stack org *+256         ; Stack for SetColor procedure
color equ stack

SetColor                ; Code for SetColor procedure
  lda color
  sta 712
  rts
 
* -------------- *      ; PutChar procedure
*  PROC PutChar  *
* -------------- *
  lmb #2                ; Set virtual bank to 2
                        ; Label definitions are now local to this bank
stack org *+256         ; Stack for PutChar procedure
char  equ stack

PutChar                 ; Code for PutChar procedure
  lda char
  sta $bc40
scr equ *-2

  inc scr
  rts
Of course, this example uses a software stack, whereas with the 65816 the hardware stack could be used instead. Because the defined variables are each local to a particular bank, procedure calls can be created with similar structure and function to those of higher-level languages.

However, it is simpler and more efficient to use the procedures allowed by MADS, which are declared with .PROC. For more information on procedure declaration and operations, see Procedures.

PROCEDURES (.PROC)

MADS adds the ability to use procedures with parameters. This feature resembles the familiar mechanisms of high-level languages and is just as easy for a programmer to use.

The built-in MADS macros (@CALL.MAC, @PULL.MAC, @EXIT.MAC) provide a software stack of 256 bytes, the same size as the hardware stack, a way to pop from the software stack, and to save and restore parameters when calling other procedures. MADS supports recursive procedure calls.

The programmer is not involved in this mechanism and can focus on his program, only needing to define the appropriate labels and include the needed macros when assembling the program.

Also, the software stack can be omitted and arguments passed using a more classical method with CPU registers (.REG directive) or with variables (.VAR directive).

Another feature of .PROC procedures is that it is possible to omit them during assembly if they are not referenced. This generates the warning message Unreferenced procedure ????. They can be removed during assembly by specifying the -x 'Exclude unreferenced procedures' command-line parameter to MADS.

All labels defined in a .PROC procedure are local but can also be accessed globally, which is uncommon in other programming languages.

It is possible to define a global label from Within a .PROC procedure (see Global labels).

To access labels within a procedure from outside of it, address it using a dot ('.'):
 lda test.pole

.proc test

pole nop

.endp
If a referenced label is not found within a .PROC procedure, MADS will then look for in enclosing scopes until the global scope is reached. To directly address a global label from within a .PROC procedure (or any other scope) prefix the label name with a colon (':').

For procedures that use a software stack, MADS requires three specific globally defined labels (the stack location, the stack pointer, and the address of the procedure variables):
@PROC_VARS_ADR
@STACK_ADDRESS
@STACK_POINTER

If these labels are undefined and a ..PROC procedure with a software stack is used, MADS assumes the following default values: @PROC_VARS_ADR = $0500, @STACK_ADDRESS = $0600, and @STACK_POINTER = $FE.

For procedures using a software stack, MADS also requires the declaration of macros with specific names. Declarations of these macros are included with MADS in the following files:
@CALL    ..\EXAMPLES\MACROS\@CALL.MAC
@PUSH    ..\EXAMPLES\MACROS\@CALL.MAC
@PULL    ..\EXAMPLES\MACROS\@PULL.MAC
@EXIT    ..\EXAMPLES\MACROS\@EXIT.MAC
These macros implement the loading and pushing of parameters onto the software stack, the popping and saving of procedure parameters off the software stack, and calling other procedures using the software stack.

Declaration of .PROC procedures

Procedure declaration directives:
 name .PROC [(.TYPE PAR1 .TYPE PAR2 ...)] [.REG] [.VAR]
 .PROC name [,address] [(.TYPE PAR1 .TYPE PAR2 ...)] [.REG] [.VAR]
 .ENDP [.PEND] [.END]

name .PROC [(.TYPE Par1,Par2 .TYPE Par3 ...)] [.REG] [.VAR]

Declares a procedure name using the .PROC directive. The procedure name is required and an error is generated without it. Mnemonic names and pseudo-commands cannot be used as procedure names and will cause a Reserved word error.

To use MADS's parameter passing mechanism, the parameters have to be declared beforehand. Parameter declarations are enclosed in parentheses ( ). There are four types of parameters:
.BYTE  (8-bit)  relocatable
.WORD  (16-bit) relocatable
.LONG  (24-bit) non-relocatable
.DWORD (32-bit) non-relocatable
Note: In current versions of MADS, it is not possible to pass parameters of structure type (.STRUCT).

The parameter type comes first, followed by at least one space, followed by the parameter name. Multiple parameters of the same type can be declared, separated by commas (',').
Example: procedure declaring using the software stack
name .PROC ( .WORD par1 .BYTE par2 )
name .PROC ( .BYTE par1,par2 .LONG par3 )
name .PROC ( .DWORD p1,p2,p3,p4,p5,p6,p7,p8 )

Additionally, by using the .REG or .VAR directives, parameters can be passed to MADS procedures by CPU registers (.REG) or variables (.VAR). The directive specifying the parameter passing convention is placed at the end of the .PROC procedure declaration.

Example: procedure declarations using CPU registers
name .PROC ( .BYTE x,y,a ) .REG
name .PROC ( .WORD xa .BYTE y ) .REG
name .PROC ( .LONG axy ) .REG

The .REG directive requires that the parameter names are made up of the letters 'A', 'X', 'Y', or a combination thereof. These refer to the names of the CPU registers and affect the order in which they are used. The number of parameters passed is limited by the number of CPU registers, so at most three bytes can be passed to the procedure. The advantage of this method is speed and lower memory usage.

Example: procedure declarations using variables

name .PROC ( .BYTE x1,x2,y1,y2 ) .VAR
name .PROC ( .WORD inputPointer, outputPointer ) .VAR
name .PROC ( .WORD src+1, dst+1 ) .VAR
With .VAR, the parameter names indicate the variable names which will be loaded with the passed parameters. This method is slower than .REG but still faster than software stack methods.

Procedures are exited in the usual way, using the RTS command. Adding the RTS instruction at the end of each code path is the responsibility of the programmer, not the assembler.

As with a .LOCAL block, a new address can be specified for assembling a .PROC block:
.PROC label,$8000
.ENDP

.PROC label2,$a000 (.word ax) .reg
.ENDP
For procedures that use the software stack, MADS invokes the macro @EXIT at the end of the procedure, whose task is to modify the software stack pointer @STACK_POINTER. This is necessary for proper software stack operation. Users can write their own @EXIT macro, or use the one included with MADS in the file "..\EXAMPLES\MACROS\@EXIT.MAC", which currently looks like this:
.macro @EXIT

 ift :1<>0

  ift :1=1
   dec @stack_pointer

  eli :1=2
   dec @stack_pointer
   dec @stack_pointer

  els
   pha
   lda @stack_pointer
   sub #:1
   sta @stack_pointer
   pla

  eif

 eif

.endm

The macro @EXIT should not alter the contents of CPU registers if you want to return a result from a .PROC procedure using CPU registers.

.ENDP

The .ENDP directive ends a procedure declaration block.

Procedure calls

A procedure is called by name (the same as for a macro), followed by any provided parameters, separated by a commas (',') or spaces (' '). It is not possible to use other separators.

If the type of a parameter is different than the type used in the procedure declaration, an Incompatible types error is reported.

If the number of parameters passed is different than the number of parameters in the procedure declaration, the result is an Improper number of actual parameters error. The exception is procedures which use parameters passed by CPU register (.REG) or variable (.VAR), in which case the extra parameters are assumed to be already loaded into the correct registers or variables.

There are three ways to pass parameters:
Example: procedure call

 name @ , #$166 , $A400  ; with the software stack
 name , @ , #$3f         ; with .REG or .VAR
 name "(hlp),y" "tab,y"	 ; with .VAR or the software stack (the software stack uses the X register)

When MADS encounters a procedure call that uses the software stack, it executes the macro @CALL. However, if the procedure does not use the software stack, a plain JSR PROCEDURE is used instead of the @CALL macro.

To the @CALL macro, MADS passes parameters computed based on the procedure declaration, breaking each parameter into three components: addressing mode, parameter type, and parameter value.
@CALL_INIT 3\ @PUSH_INIT 3\ @CALL '@','B',0\ @CALL '#','W',358\ @CALL ' ',W,"$A400"\ @CALL_END PROC_NAME
Here, the @CALL macro pushes the contents of the accumulator, then the value $166 (358 dec), then the value at the address $A400. For more information on how parameters are passed to the macro (and the importance of '' and ""), see Calling macros.

Parameters passed using the accumulator ('@') should always be the first parameter passed to the procedure. If it is used elsewhere, the accumulator is already modified by that point (this restriction is imposed by the @CALL macro). Of course, this can be lifted with a custom version of the @CALL macro. With procedures that use .REG or .VAR, a '@' parameter can be used in any position.

The end of a .PROC procedure is marked by an RTS. After the procedure call, MADS invokes the @EXIT macro to modify the @STACK_POINTER, which is necessary for proper operation of the software stack. The number of bytes passed to the procedure is passed as a parameter to the macro, which then subtracts that number of bytes from the software stack.

(Editor's note: This is known as a caller-pops convention, since the caller both pushes and pops the parameters off the stack, and is common in C implementations.)

Adding an RTS instruction at the end of every code path in the procedure is the responsibility of the programmer, and not the assembler.

Procedure parameters

Referring to procedure parameters does not require additional effort by the programmer:
@stack_address equ $400
@stack_pointer equ $ff
@proc_vars_adr equ $80

name .PROC (.WORD par1,par2)

 lda par1
 clc
 adc par2
 sta par1
 
 lda par1+1
 adc par2+1
 sta par1+1

.endp

 icl '@call.mac'
 icl '@pull.mac'
 icl '@exit.mac'
At the time of declaration, MADS automatically defines these parameters by assigning values based on @PROC_VARS_ADR. In the preceding example, MADS will define the parameters as PAR1 = @PROC_VARS_ADR and PAR2 = @PROC_VARS_ADR + 2.

The programmer uses these parameters by the names given in the procedure declaration, similarly as with higher-level languages. In MADS, it is possible to access procedure parameters externally, which is unusual in higher-level languages. For instance, PAR1 can be read as follows:

 lda name.par1
 sta $a000
 lda name.par1+1
 sta $a000+1
This copies the two bytes at PAR1 to addresses $A000 and $A000+1. Of course, this can only be done after completion of this particular procedure. Remember that each procedure has parameters stored at the same area addressed by @PROC_VARS_ADR, so with each new procedure call the parameter area at <@PROC_VARS_ADR .. @PROC_VARS_ADR + $FF> changes.

If a procedure is declared using .REG type parameters, the programmer should remember to preserve or use parameters before they are modified by the procedure code. With .VAR type parameters, this is not an issue because the parameters are saved in specific memory locations where they can always be read.

LOCAL AREA

The main purpose of a local area in MADS is to create a new scope for labels.
All labels defined in a .LOCAL area are local. However, you can still refer to a local label globally, which is uncommon in other programming languages..
It is possible to define a global label within a .LOCAL area (see Global labels). .
If the assembler cannot find a label within a .LOCAL area, MADS will then look in the global scope. To refer to global labels directly from a .LOCAL area, prefix the label name with a colon (':')..

Declaring .LOCAL scopes

Local scope directives:

 [name] .LOCAL [,address]
 .LOCAL [name] [,address]
 .ENDL [.LEND] [.END]

[name] .LOCAL [,address]

Declares a local scope named name with the directive .LOCAL. The local scope name is not required and can be omitted. Local scope names cannot be the same as mnemonics and pseudo-commands, and attempting to use a reserved name will result in a Reserved word error.

After the name of the local scope (or the .LOCAL directive), a new assembly address can be provided. After the end of the block (.ENDL), the assembly address reverts to the previous address plus the length of the block.

label .local,$4000
.endl

.local label2,$8000
.endl

.local
.endl

.local label3
.endl
All definitions within a .LOCAL block are local. To refer to a global label with the same name as a local name, prefix the name with a colon (':'):
lab equ 1

.local

lab equ 2

 lda #lab
 ldx #:lab

.endl
In this example, the A register is set to 2, while the X register is set to 1.

If the assembler cannot find a label in a .LOCAL scope, it will then check an enclosing macro (if one is being processed), then the enclosing procedure (if there is one), and finally the main program at global scope.

Within a local scope, all label definitions are qualified by the local scope's name. To reach labels defined in another local scope, both the name of the local scope and the label must be supplied:
 lda #name.lab1
 ldx #name.lab2

.local name

lab1 = 1
lab2 = 2

.endl

A dot ('.') is used to address labels within a .LOCAL block.

Local scopes can be nested, as well as placed within procedures declared using the .PROC directive. Local scopes are cumulative, i.e. there may be multiple scopes of the same name and all symbols in those scopes will belong to a common namespace.

.ENDL

The .ENDL directive ends a local scope.

Example: local scope declaration

 org $2000
 
tmp ldx #0   <-------------   label in global scope
                          |
 lda obszar.pole  <---    |   reference to local scope
                     |    |
.local obszar        |    |   local scope declaration
                     |    |
 lda tmp   <---      |    |
              |      |    |
 lda :tmp     |      | <---   reference to global scope
              |      |
tmp nop    <---      |        definition in local scope
                     | 
pole lda #0       <---   <--- definition in local scope
                            |
 lda pole  <----------------- reference within local scope

.endl                         end of local scope

SYNTAX

MADS accepts the same syntax as QA and XASM. However, it is stricter with comments placed at the end of a line (comments should be preceded the appropriate symbol) and more lenient with whitespace and CPU instruction mnemonics without arguments, i.e.:
   asl            -> asl @
   lda #          -> lda #0
MADS will accept missing white spaces between the mnemonic and operand, provided that the operand does not start with a '@', which is used in the names of labels, or '%' and ':', which are used to denote numbered macro parameters (%%number, :number), e.g.:
   lda$44
   lda#
   lda(80),y

Comments

Comment lines must start with a ';' or '*'. For a single-line comment, however, the safest way is to use a semicolon (';'), as the asterisk ('*') has other meanings and can mean multiplication or the current assembly address. On the other hand, the semicolon is dedicated only for comments.

The characters '//' can also be used for a single-line comment, and '/* */' for a multi-line or inline comment.
 * this is a comment
                 ; This is a comment
 lda #0      ; This is a comment
 dta  1 , 3     * BAD COMMENT, WILL BE MISINTERPRETED

 org $2000 + 1      BAD COMMENT, WILL BE MISINTERPRETED

 nop // this is a comment

 // this is a comment

 dta 1,2, /* comment */ 3,4

 lda /* comment */ #0

/*
  ...
  this is a multi-line comment
  ...
*/

/************************************
  this is also a multi-line comment
*************************************/
The multi-line comment signs '/* */' and the end-line comment sign '//' can be used without restrictions.

Combining multiple lines into a single line

Multiple lines can be combined into a single line using '\', e.g.:
  lda 20\ cmp 20\ beq *-2
  lda 20   \ cmp  20   \   beq *-2
  lda #0  \lop  sta $a000,y  \ iny  \ bne lop     ; Comments only at the end of this line

If there is no space after the '\' character, a mnemonic or other string can be interpreted as a label, so make sure to treat '\' as the beginning of a new line.

MADS stops processing the line once either at the end of the chain or when a comment is reached, so comments can be placed only at the end of the line.

WARNING: Putting a '\' at the end of a line tells MADS to combine the current line with the next line.
Exmaple: Combining lines
  lda\
  #\
  12
In this case, we get the result 'LDA #12'.

Combining multiple mnemonics

XASM already provided the ability to combine two mnemonics using a ':'. In MADS, this is extended to combine any number of MADS-known mnemonics:
 lda:cmp:req 20

 lda:iny:sta:iny $600,y

(Editor's note: All mnemonics in a combined statement are processed with the same argument. This is fine when some of the instructions only have implied addressing. However, it can lead to accidents with instructions that have both implied and memory forms. For instance, "STA:INC:RNE pixels,X+" will assemble as STA pixels,X / INX / INC pixels,X / INX / BNE *.)

EXPRESSIONS

The term expression means a series of operators and operands (arguments) that specifies the order of operations, i.e. type and order of evaluation. A compound expression is an expression with two or more operators. Operators that affect only one operand are called unary, and operators with two arguments are called binary.

Expressions are evaluated in the order determined by priority of each operator in the direction specified by the precedence of each operator.

NUMBERS

MADS accepts numbers in decimal, hexadecimal, binary, ATASCII, and INTERNAL format.

decimal notation:
  -100
  -2437325
  1743
hexadecimal notation:
  $100
  $e430
  $000001
 
  0x12
  0xa000
  0xaabbccdd
binary notation:
  %0001001010
  %000000001
  %001000
ATASCII code:
  'a'
  'fds'
  'W'*
INTERNAL code:
  "B"
  "FDSFSD"
  "."*
Only the first character of ATASCII or INTERNAL coding is significant. A '*' after the closing quote specifies inverse characters.

Additionally, there are two possible operations '+' and '-' for strings, which increase or decrease the values of the quoted characters.
  "FDttrteSFSD"-12
  'FDSFdsldksla'+2

Operators

Binary operators:

+   Addition
-   Subtraction
*   Multiplication
/   Division
%   Remainder
&   Bitwise and
|   Bitwise or
^   Bitwise xor
<<  Arithmetic shift left
>>  Arithmetic shift right
=   Equal
==  Equal (same as =)
<=  Not equal
!=  Not equal (same as <>)
<   Less than
>   Greater than
<=  Less or equal
>=  Greater or equal
&&  Logical and
||  Logical or


Unary operators:

+  Plus (does nothing)
-  Minus (changes sign)
~  Bitwise not (complements all bits)
!  Logical not (changes true to false and vice versa)
<  Low (extracts low byte)
>  High (extracts high byte)
^  High 24bit (extracts high byte)
=  Extracts memory bank
:  Extracts global variable value


Operator precedence:

first []              (brackets)
 + - ~ < >            (unary)
 * / % & << >>        (binary)
 + - | ^              (binary)
 = == <> != < > <= >= (binary)
 !                    (unary)
 &&                   (binary)
last  ||              (binary)

LABELS

Labels may be defined in the program with local or global scope, depending on the location where they are defined. Additionally, labels may be defined as temporary, also with local or global scope.
Example: label definitions

?name    EQU  $A000      ; Defining a temporary global label
name      =   *          ; Defining a global label
name2=12                 ; Defining a global label
@?name   EQU  'a'+32     ; Defining a global label
  name: equ 12           ; Defining a non-global label that does not start at the beginning of the line
         name: = 'v'     ; Defining a non-global label that does not start at the beginning of the line
Unlike QA/XASM, a question mark ('?') and at-sign ('@') can be used in label names.

Using a dot ('.') in a label name is allowed but not recommended. The dot is reserved for extended mnemonics, assembler directives, and addressing new MADS structures.

A dot ('.') at the beginning of a label name suggests that it is an assembler directive, and a question mark ('?') at the beginning signifies a temporary label that can be changed several times during assembly.

Anonymous labels

To ensure clarity when using anonymous labels, their use is limited only to jumps no more than 10 instances away.

The character '@' is reserved for anonymous labels, and there must be a sign character indicating either forward direction ('+') or backwards direction ('-'). Additionally, you can specify the number of anonymous labels in the range [1..9].

 @+[1..9]     ; Forward
 @-[1..9]     ; Backward

@ dex   ---- -------
  bne @+   |  --   |
  stx $80  |   |   |
@ lda #0   |  --   |
  bne @- ---       |
  bne @-1  ---------

Local labels

Labels defined within a macro (.MACRO), procedure (.PROC), or local scope (.LOCAL) are local labels by default and do not need additional markup.

Local labels are defined using the following equivalent pseudo-commands:
 EQU
  =
To access global labels, i.e. those defined outside of a macro (.MACRO), procedure (.PROC), or local scope (.LOCAL), use the ':' operator:
lp   ldx #0         ; Define global label LP

     test
     test
 
test .macro

      lda :lp       ; The ':' prefix causes reference to the global label LP

      sta lp+1      ; Reference to the local label LP in the macro
lp    lda #0        ; Definition of local label LP in the macro

     .endm
In this example, there are two definitions of labels with the same name (LP), but each has a different value and a different scope.

Global labels

Each definition outside of a macro (.MACRO), procedure (.PROC), or local scope (.LOCAL) is global.

Global labels are defined using the following equivalent pseudo-commands:
 EQU
  =
Or with .DEF directive syntax:
 .DEF :label [= expression]

The .DEF directive normally defines local labels, but the ':' at the beginning of the label indicates a global label instead. Using the syntax .DEF :label allows definition of global labels within a local scope.

The ':' at the beginning of the label has special meaning and indicates a global label, or label at mainline level, disgarding any current local scopes.

For more information on use of the .DEF directive, see .DEF Directive.

Example: defining global labels

lab equ *
   lab2 equ $4000

	?tmp = 0
	?tmp += 40
 
.proc name

      .def :?nazwa   = $A000
           .def :nazwa=20

      .local lok1
        .def :@?nazw   = 'a'+32
      .endl

.endp
Examples of defining a global temporary labels include the macro @CALL in the file "..\EXAMPLES\MACROS\@CALL.MAC", which defines the temporary label ?@STACK_OFFSET. This is later used by other macros called called by the macro @CALL, and is used to optimize access to parameters on the stack.
@CALL .macro

  .def ?@stack_offset = 0    ; Definition of temporary global lavel ?@stack_offset
 
  ...
  ...
 
 
@CALL_@ .macro

  sta @stack_address+?@stack_offset,x
  .def ?@stack_offset = ?@stack_offset + 1    ; Modify ?@stack_offset label

 .endm 

Temporary labels

A temporary label has the property that its value can be changed many times during assembly. Normally, attempting to redefine a label results in a Label declared twice error, which does not occur with a temporary label.

The lifetime of a temporary label depends on the scope in which it is defined. Temporary labels may have local scope (Local labels) or global scope (Global labels).

A temporary label is created by placing a '?' at the beginning:
 ?label
Temporary labels should not be used to name procedures (.PROC), macros (.MACRO), local scopes (.LOCAL), structures (.STRUCT), or arrays (.ARRAY).

A temporary label is defined using one of the following equivalent pseudo-commands:
 EQU
  =
Additionally, they can be modified using recognizable C operators:
 -= expression
 += expression
 --
 ++
These assignment operators only work with temporary labels, and attempting to use them for other types of labels results in the error message Improper syntax.

Example: temporary labels

?loc = $567
?loc2 = ?loc+$2000

	 lda ?loc
	 sta ?loc2

?loc = $123

	 lda ?loc

MAE-style temporary labels

Option OPT ?+ tells MADS that labels starting with '?' should be interpreted as local labels as MAE does. By default, labels starting with '?' are interpreted by MADS as temporary labels (Temporary labels).
Example: MAE-style temporary labels

       opt ?+
       org $2000

local1 ldx #7
?lop   sta $a000,x
       dex
       bpl ?lop

local2 ldx #7
?lop   sta $b000,x
       dex
       bpl ?lop

SPARTADOS X

SpartaDOS X and Atari DOS file formats

Reproduced from Serious Magazine, author Qcyk/Dial.

A file itself is only a collection of bytes, lots of numbers that can mean both everything and nothing at the same time if you do not know how to interpret them. For this reason, most files are equipped with a variety of headers which store information about what the file contains and are necessary to read them. This includes binary executables loaded under DOS. After all, DOS is a program and like everyone else has a right to expect a certain structure in its data.

Traditional binary files, recognized by all DOSes for Atari XL/XEs, are built of blocks where each block has its own header. There are two types of headers:
 1. dta a($ffff),a(str_adr),a(end_adr)

 2. dta a(str_adr),a(end_adr)

str_adr - address at which the first byte of data will be loaded

end_adr - address at which the last byte of data will be loaded

The first block in the file header must be $ffff, followed by other blocks. Each block header should of course be followed by this amount of data:

   (end_adr-str_adr)+1

That's enough of a refresher. The developers of SpartaDOS X have kept the above standard, while adding several new types of headers. The file is still divided into blocks, except that now there are a lot more types of blocks. Here they are:

1. Non-relocatable block (loaded at fixed address in memory):

    dta a($fffa),a(str_adr),a(end_adr)

This is the same as a $ffff block - it does not matter which you use. However, $fffa will clearly indicate that the program is designed for SDX - another DOS cannot read the file.

2. Relocatable block (loaded starting at MEMLO):

    dta a($fffe),b(blk_num),b(blk_id)
    dta a(blk_off),a(blk_len)

blk_num - block number in the file. Each relocatable block should have its own number. Because the load addresses of blocks are not known, blocks are just identified by number. They may be in the range [0..7], except that in practice they are usually numbered from 1 upwards.

blk_id - bits 1-5 are the memory type, indicating where the block is to be loaded. I have encountered two values:
 $00 - main memory
 $02 - extended memory
Also, bit 7 indicates no data block if set. In this case, SDX loads nothing, but still reserves memory.

blk_off - block base address, which is simply the address at which the code was assembled. This is necessary when relocating addresses referring to the contents of the block.

blk_len - the length of the block. There should be as much data following as indicated by the header, unless the block is only a reserved area in which case there is no data.

When writing relocatable code, several limitations imposed by the idea of "relocatable code" must be kept in mind. All addresses referring to program areas must be updated during load, so sequences such as this cannot be used:
       lda <something
       ldx >something
       ...
      something equ *
       ...
    Instead, use something like this, for example:
       lda _something
       ldx _something+1
       ...
      _something dta a(something)
       ...
      something equ *

3. Update addresses in a block referring to a relocatable block:

    dta a($fffd),b(blk_num),a(blk_len)

blk_num - number of block containing the reference targets

blk_len - update block length (without the header). This is ignored.

Addresses are updated by adding the difference between the address at which the relocatable block was loaded and the value of blk_off (where the block was originally assembled). This can be illustrated as follows:
       adr=adr+(blk_adr-blk_off)
The payload of an update block contains pointers to addresses and special commands. Numbers between $00-$fb are offsets from the last updated location. This location is stored as an address in an update pointer. This pointer can be updated through special functions invoked by values greater than $fb:

$fc marks the end of the update block,

$fd,a(addr) directly updates the address ADDR. Thus, the update pointer is set to ADDR, which is used as the base for the next offset,

$fe,b(blk_num) sets the update pointer to the base address of the block specified by blk_num, which is then used for the next offset,

$ff increases the update pointer by $fa (without updating an address).


4. Update addresses in blocks targeting procedures with defined symbols:

    dta a($fffb),c'SMB_NAME',a(blk_len)

SMB_NAME - procedure symbol name (or array, system registry, etc.) Eight characters in ATASCII code,

blk_len - as in a $fffd block.

After the header, there is a sequence of offsets to locations of addresses to update - the same as in a $fffd block. Addresses are updated by adding the address of the procedure denoted by the symbol to the existing address. This allows use in programs of procedures whose addresses are unknown, such as procedures added by other applications running in the SDX environment. Also, system procedures must be used this way, as they have different addresses in different versions of SpartaDOS.

5. Symbol definition block:

    dta a($fffc),b(blk_num),a(smb_off)
    dta c'SMB_NAME'

blk_num - number of the block in which the procedure is defined. This means that the procedure must be defined in a relocatable block.

smb_off - procedure offset in the block, which is an offset from the start of the block (the first byte is 0) plus the value of blk_off of the block. Basically, the address at which the procedure was assembled.

SMB_NAME - symbol name being defined for the procedure.

Block types $fffb, $fffc, and $fffd are not kept in memory. The system uses them only during program load.

Programming SpartaDOS X (SDX)

The syntax for handling SpartaDOS X programs, was taken from FastAssemblera by the author Marka Goderskiego. Below is a quote from the manual that came with FA. MADS can now assemble source files in *.FAS format without any major problems. Relocatable commands always have two-byte arguments; it is impossible to relocate 3-byte arguments (65816).

The most important innovation in SDX is the ability for developers to write relocatable code. Since the MOS 6502 does not have relative addressing (except for short branches), the ICD developers created a way to do so using special program blocks. The process is based on loading blocks and then updating addresses within the blcok using special update blocks. It is enough to add the value of memlo to correct addresses, but what addresses to correct, and which ones to leave? That's the point of a special block that contains (specially coded) offsets to those addresses. Therefore, an UPDATE ADDRESS operation must be applied to each RELOC block before running the program. UPDATE ADDRESS must also be performed on any block which refers to SPARTA commands or vectors.

Another innovation is the introduction of symbols. Some of the SDX service procedures are defined by name! These names always have 8 letters (like filenames). Instead of using arrays of vectors or jumps (like in the OS), use symbols defined with SMB. After loading a block or blocks, SDX loads symbol blocks and updates symbol addresses the same way as with relocatable addresses in the program. Symbols can refer to items in either RELOC or SPARTA blocks.

Programmers can define custom symbols to replace the ones in SDX or completely new ones for use by other programs. This is done by the UPDATE NEW block. It should be kept in mind that new symbols must be stored in a RELOC block.

The number of RELOC and EMPTY blocks is limited to 7 by SDX.

Such blocks can be combined into chains:
       blk sparta $600
       ...

       blk reloc main
       ...

       blk empty $100 main
       ...

       blk reloc extended
       ...

       blk empty $200 extended
This means that commands for these blocks can refer to all blocks in the chain.

The chain is not interrupted by updating addresses or symbols, but is ended by the definition of a new symbol and by other block types, e.g. DOS.

Note: The chain only makes sense if all blocks are loaded into the same memory, or when a program switches to the appropriate memory references.

Note: Commands and vectors in RELOC and EMPTY blocks should not refer to SPARTA blocks! This may cause an error when the user loads the program using the LOAD command and uses it after a long time. While RELOC and EMPTY are safe, you never know what is in memory where a SPARTA block was last loaded!

Equally dangerous is the references to RELOC and EMPTY blocks by SPARTA blocks (for the same reason as above), but during the installation of overlays (*.sys) using INSTALL this is sometimes necessary and therefore acceptable. You can also invoke a SPARTA block (through $2E2) to run immediate, then discard it.

Note: Addresses can collide between SPARTA blocks and RELOC/EMPTY addresses! FA recognizes references to other blocks by address, assuming a PC for RELOC and EMPTY blocks of $1000, so for mixed programs SPARTA blocks should be below $1000 (e.g. $600) or above the last relocatable block, $4000 using being enough. This error is not detected by the compiler!

RELOCATABLE CODE

Relocatable code is code without a fixed address, such that when loaded into a computer it has to work regardless of the load address. SpartaDOS X (SDX) provides a relocation facility for Atari XL/XE code, of which more than be read in the section Programming SpartaDOS X.

A basic limitation of SDX relocatable code is that only WORD addresses are relocated and there is no support for 65816 code. MADS provides the ability to generate code both in SDX format and in a non-SDX format that removes the aforementioned limitations.

The MADS relocatable code format is similar to that of SDX, as there are main blocks and then blocks with additional address relocation information. MADS uses a simpler update block format, without the "compression" used by SDX.

Advantages of MADS relocatable code: Limitations of MADS relocatable code: An example of how easy it is to create relocatable code is in the file "..\EXAMPLES\TETRIS_RELOC.ASM", which uses the CPU instruction set and data definition pseudo-commands no differently than the non-relocatable version in the file "..\EXAMPLES\TETRIS.ASM".

Reloctable blocks (.RELOC)

A MADS relocatable block is created using the directive:
 .RELOC [.BYTE|.WORD]
A block for updating a relocatable MADS block is created using the BLK pseudo-command:
 BLK UPDATE ADDRESS
After the .RELOC directive, it is possible to specify a relocation block type (.BYTE, .WORD), with the default being .WORD type. type .BYTE is for blocks that must be placed in zero page (instructions will use zero page), which MADS will assemble to address $0000. Type .WORD means that MADS will assemble the block starting at address $0100 and that it can be placed anywhere in memory (except for zero page).

The .RELOC block produces a header like the famous DOS header, further expanded by 10 bytes to a total of 16 bytes:

HEADER            .WORD = $FFFF
START_ADDRESS     .WORD = $0000
END_ADDRESS       .WORD = FILE_LENGTH-1
MADS_RELOC_HEADER .WORD = $524D
UNUSED            .BYTE = $00
CONFIG            .BYTE (bit0)
@STACK_POINTER    .WORD
@STACK_ADDRESS    .WORD    
@PROC_VARS_ADR    .WORD
MADS_RELOC_HEADER always $524D, corresponding to the characters 'MR' (M-ADS R-ELOC)
FILE_LENGTH the length of the relocatable file without the 16-byte header
CONFIG currently only bit 0 is used, where bit0=0 means relocatable blocks are assembled starting at $0000, and bit0=1 means blocks are assembled at $0100.

The last 6 bytes contain information about the values of labels needed for the operation of the software stack, @STACK_POINTER, @STACK_ADDRESS, and @PROC_VARS_ADR, if used in relocatable blocks. If the individual .RELOC blocks were assembled with different values of these labels, linking them together will produce an Incompatible stack parameters warning. If the software stack labels are not used, the values are zero.

The pseudo-command .RELOC switches MADS to relocatable code generation mode, taking into account the operand sizes of 6502 and 65816 instructions. In this mode, it is impossible to use the ORG, LMB, NMB, and RMB pseudo-commands or the .DS directive. It is impossible to exit MADS relocatable code generation mode, but it is possible to have more than one .RELOC block.

Using the .RELOC directive will also increase the MADS virtual bank counter, and thus create a local scope not visible to other blocks. For more information on virtual banks, see the section Virtual memory banks (OPT B-).

At the end of a .RELOC block it is necessary to generate an update block, using the same BLK syntax for SDX relocatable blocks ("BLK UPDATE ADDRESS"). The encoding format for this block is different than for SDX and is as follows:

HEADER       word ($FFEF)
TYPE         char (B-YTE, W-ORD, L-ONG, D-WORD, <, >)
DATA_LENGTH  word
DATA         word [byte]
HEADER always set to $FFEF
TYPE data type is stored in bits 0..6 and specifies the type of addresses to update, where "<" means the lower address byte and ">" means the upper address byte.
DATA_LENGTH number of 2-byte data items (addresses) to modify
DATA offsets for updating the relocatable block, where at each address a value of type TYPE is read and then updated to the final address

The exception is an update block for address high bytes ">", where for such a block an extra BYTE is stored for each address (low byte of address being modified). To adjust the high bytes, the high byte has to be read from the WORD address in the DATA, added to the current relocation address, and then added to the low byte in the BYTE field of the DATA. The newly calculated byte is then store back at the WORD address in the DATA.

External symbols

External symbols indicate that the represented variables and procedures are stored somewhere outside, beyond the current program. The location need not be specified, only the names and the types. Since the translation of instructions to appropriate machine code depends on the type of data represented by a symbol, the assembler needs to know the size of the data used.

Currently there is no support for manipulating external symbols of type '^' (highest byte).

External symbols may be used in both relocatable .RELOC blocks as well as usual DOS ones.

External symbols are declared using the pseudo-command EXT or the .EXTRN directive:
label EXT type
label .EXTRN type
.EXTRN label1,label2,label3... type
An update block for referencing external symbols is produced using the BLK pseudo-command:
 BLK UPDATE EXTERNAL
Note: This will write symbol names used by the program.

External symbols of value type can also be defined (.BYTE, .WORD, .LONG , .DWORD):

name EXT .BYTE

label_name EXT .WORD

 .EXTRN label_name .WORD

wait EXT .PROC (.BYTE delay)
External symbol declarations of procedure (.PROC) type default to .WORD, and attempts to reference the label will be interpreted by MADS as an attempt to call the procedure. For more about procedure calls, see Procedures.

During the assembly process, references to external symbols are replaced with the value zero.

External symbols can be useful when assembling part of a program separately from the rest of it. In this case, there are often references to other procedures and variables are defined elsewhere, and only the type and not value are known. With the help of external symbols, such a program can be assembled without the full procedure and variable definitions.

Another use for external symbols is for "plugins", or external programs connected to the main program to add additional functionality. These are types of libraries, using the procedures of the main program and expanding its capabilities. Creating such a plugin requires determining what procedures the main program provides (their name + parameters and type), and a procedure to read the file from the external symbols, which then attaches the plugins to the main program.

The following is the format of the file header produced by BLK UPDATE EXTERNAL for external symbols of type B-YTE, W-ORD, L-ONG, and D-WORD:

HEADER        word ($FFEE)
TYPE          char (B-YTE, W-ORD, L-ONG, D-WORD, <, >)
DATA_LENGTH   word
LABEL_LENGTH  word
LABEL_NAME    ATASCII string
DATA          word .. .. ..
HEADER always set to $FFEE
TYPE bits 0..6 contain the type of data to modify
DATA_LENGTH number of 2-byte data items (addresses) to modify
LABEL_LENGTH symbol name length in bytes
LABEL_NAME the symbol name, coded in ATASCII
DATA offset data for the relocation process. At each indicated address here, the value of type TYPE is read and then based on the actual value of the symbol.

An applied example of external symbols and .STRUCT structures is the graphical primitives library in the directory "..\EXAMPLES\LIBRARIES\GRAPHICS\LIB". Individual modules use a fairly large number of zero page variables, and if we want to place these in relocatable code each individual variable would have to be declared as an external symbol by EXT (or .EXTRN). We can simplify this by using only one symbol and external data of structure (.STRUCT) type. These structures define a "map" of variables called ZP and one ZPAGE external symbol, of type .BYTE because we want it to be in zero page. Now, when referring to the variable, we need to do it in a way that forces relocation such as ZPAGE + ZP.DX. The result is a fully relocatable module with relocated variables in zero page.

Public symbols

Public symbols make variables and procedures available to other blocks in the rest of the relocatable assembly program. With public symbols, you can refer to variables and procedures from libraries.

Public symbols can be used in relocatable .RELOC blocks as well as usual DOS blocks.

MADS automatically detects whether a public label is a variable, constant, or procedure defined by .PROC, and does not need any additional information as for external symbols.

Public symbols are declared using the following directives:
 .PUBLIC label [,label2,...]
 .GLOBAL label [,label2,...]
 .GLOBL label [,label2,...]
The directives .GLOBAL and .GLOBL have been added for compatibility with other assemblers. They are identical in meaning to .PUBLIC directive.

Update blocks for public symbols are created using the BLK pseudo-command:
 BLK UPDATE PUBLIC

Below is the header format produced by BLK UPDATE PUBLIC:

HEADER        word ($FFED)
LENGTH        word
TYPE          byte (B-YTE, W-ORD, L-ONG, D-WORD)
LABEL_TYPE    char (C-ONSTANT, V-ARIABLE, P-ROCEDURE, A-RRAY, S-TRUCT)
LABEL_LENGTH  word
LABEL_NAME    ATASCII string
ADDRESS       word
MADS automatically selects the appropriate type for public labels:
>
C-ONSTANT  label is not subject to relocation
V-ARIABLE  label is subject to relocation
P-ROCEDURE procedures defined by .PROC, subject to relocation
A-RRAY     array defined by .ARRAY, subject to relocation
S-TRUCT    structure defined by .STRUCT, not subject to relocation
If the symbol is of structure (.STRUCT) type, this additional information is saved (the type of the structure, name of the structure, and number of elements of the structure):
STRUCT_LABEL_TYPE    char (B-YTE, W-ORD, L-ONG, D-WORD)
STRUCT_LABEL_LENGTH  word
STRUCT_LABEL_NAME    ATASCII string
STRUCT_LABEL_REPEAT  word
If the symbol is of array (.ARRAY) type, this additional information is saved (maximum element index, declared element type):
ARRAY_MAX_INDEX  WORD
ARRAY_TYPE       CHAR (B-YTE, W-ORD, L-ONG, D-WORD)
If the symbol is of procedure (.PROC) type, this additional information is saved, regardless of whether the procedure does or does not declare parameters:
PROC_CPU_REG  BYTE (bits 00 - regA, 01 - regX, 10 - regY)
PROC_TYPE     BYTE (D-EFAULT, R-EGISTRY, V-ARIABLE)
PARAM_COUNT   WORD

.REG symbols of the procedure are then included, of PARAM_COUNT count:

PARAM_TYPE    CHAR (B-YTE, W-ORD, L-ONG, D-WORD)
...

.VAR symbols of the procedure are included next along with their names, where PARAM_COUNT specifies the total length of the data:

PARAM_TYPE    char (B-YTE, W-ORD, L-ONG, D-WORD)
PARAM_LENGTH  word
PARAM_NAME    ATASCII string
...
HEADER always set to $FFED
LENGTH number of symbols stored in the update block
TYPE type of symbol data: B-YTE, W-ORD, L-ONG, D-WORD
LABEL_TYPE symbol type: V-ARIABLE, C-ONSTANT, P-ROCEDURE, A-RRAY, S-TRUCT
For type P, additional stored information: PROC_CPU_REG, PROC_TYPE, PARAM_COUNT, PARAM_TYPE
For type A, additional stored information: ARRAY_MAX_INDEX, ARRAY_TYPE
For type S, additional stored information: STRUCT_LABEL_TYPE, STRUCT_LABEL_LENGTH, STRUCT_LABEL_NAME, STRUCT_LABEL_REPEAT
LABEL_LENGTH public symbol name length in bytes
LABEL_NAME public symbol name stored as ATASCII
ADDRESS address assigned to the symbol in the relocatable .RELOC block. This value is relocated by adding its current assembly address.
PROC_CPU_REG information about the .REG CPU register usage records for a procedure
PROC_TYPE type of procedure:
  • D-EFAULT default type: parameters passed using MADS software stack
  • R-EGISTRY: procedure parameters passed using CPU registers (.REG)
  • V-ARIABLE: procedure parameters passed by variables (.VAR)
PARAM_COUNT information about the number of register-passed parameters (.REGs) or total length of type and name data for variable-passed parameters (.VARs)
PARAM_TYPE parameter types, recorded using the characters 'B', 'W', 'L', 'D'
PARAM_LENGTH parameter name length (.VAR)
PARAM_NAME parameter name coded as ATASCII (.VAR)

Linking (.LINK)

 .LINK 'filename'
The directive .LINK requires as a parameter the filename of the file to link. Only Atari DOS files are accepted, not SDX files.

If the file load address is different than $0000, it means that the file does not contain relocatable code, but may include update blocks for external and public symbols. The .LINK directive accepts files at any address, but only those starting at $0000 are subject to relocation. More information on how to construct such a file is included in the section Relocatable blocks (.RELOC).

The .LINK directive allows linking of relocatable and non-relocatable code. MADS automatically relocates the file based on all three types of update blocks (ADDRESS, EXTERNAL, PUBLIC).

There is no limit to the address at which a file can be relocated.

If a block being relocated requires the MADS software stack, the labels @STACK_POINTER, @STACK_ADDRESS, and @PROC_VARS_ADR are automatically updated based on the .RELOC block header. It is necessary that the main program and the .RELOC blocks operate on the same software stack.

MNEMONICS

Available 6502 opcodes

   LDA   LDX   LDY   STA   STX   STY   ADC   AND  
   ASL   SBC   JSR   JMP   LSR   ORA   CMP   CPY  
   CPX   DEC   INC   EOR   ROL   ROR   BRK   CLC  
   CLI   CLV   CLD   PHP   PLP   PHA   PLA   RTI  
   RTS   SEC   SEI   SED   INY   INX   DEY   DEX  
   TXA   TYA   TXS   TAY   TAX   TSX   NOP   BPL  
   BMI   BNE   BCC   BCS   BEQ   BVC   BVS   BIT  
A mnemonic extension can be placed after a dot '.' for LDA, LDX, LDY, STA, STX, and STY:
   .b or .z                BYTE
   .a or .w or .q   WORD

e.g.:
   lda.w $80   ; AD 80 00
   lda   $80   ; A5 80

Available illegal 6502 opcodes

   ASO   RLN   LSE   RRD   SAX   LAX   DCP   ISB
   ANC   ALR   ARR   ANE   ANX   SBX   LAS   SHA
   SHS   SHX   SHY   NPO   CIM

Available 65816 opcodes

Of course, all 6502 opcodes are available, as well as:
   STZ   SEP   REP   TRB   TSB   BRA   COP   MVN  
   MVP   PEA   PHB   PHD   PHK   PHX   PHY   PLB  
   PLD   PLX   PLY   RTL   STP   TCD   TCS   TDC  
   TSC   TXY   TYX   WAI   WDM   XBA   XCE   INA
   DEA   BRL   JSL   JML
It is possible to use XASM-style a:, z:, and r: mnemonic extensions:
 XASM        MADS
 lda a:0     lda.a 0
 ldx z:0     lda.z 0

 org r:$40   org $40,*
Mnemonic extensions can be placed after a dot ('.') for LDA, LDX, LDY, STA, STX, and STY:
   .b or .z                BYTE
   .a or .w or .q   WORD
   .t or .l                TRIPLE, LONG (24bit)

e.g.:
   lda.w #$00   ; A9 00 00
   lda   #$80   ; A9 80

The following commands cannot have their operand given in absolute addressing form (some assemblers do not require the '#' character, but MADS requires it):

#$xx

   SEP   REP   COP

#$xxxx

   PEA

Another exception is long indirect addressing mode, which is represented by square brackets [ ]. These brackets are used to group subexpressions, but if the assembler encounters the character '[' first it is interpreted as the start of long indirect addressing, and if 65816 mode is not enabled a Illegal adressing mode results. To "trick" the assembler, place the character '+' before the '['.

 lda [2+4]     ; lda [6]
 lda +[2+4]    ; lda 6

CPU DETECTION

Detecting the 6502 and 65816 CPUs

This example is taken from http://www.s-direktnet.de/homepages/k_nadj/cputest.html. The program is able to detect the presence of the following microprocessors: 6502, 65C02, and 65816.
/*
How to detect on which CPU the assembler code is running
(This information is from Draco, the author of SYSINFO 2.0)

You can test on plain 6502-Code if there is a 65c816 CPU, the 16-Bit processor avaible
in some XLs as a turbo-board, avaible. Draco told me how to do this:

First we make sure, whether we are running on NMOS-CPU (6502) or CMOS (65c02,65c816).
I will just show the "official" way which doesn`t uses "illegal opcodes":
*/

 org $2000

 opt c+

DetectCPU

 lda #$99
 clc
 sed
 adc #$01
 cld
 beq DetectCPU_CMOS

DetectCPU_02

 ldx #<_6502
 ldy #>_6502
 jsr $c642

 lda #0
 rts

DetectCPU_CMOS

 lda #0
 rep #%00000010		;reset Z bit
 bne DetectCPU_C816

DetectCPU_C02

 ldx #<_65c02
 ldy #>_65c02
 jsr $c642

 lda #1
 rts

DetectCPU_C816

 ldx <_65816
 ldy >_65816
 jsr $c642

 lda #$80
 rts

_6502   dta c'6502',$9b
_65c02  dta c'65c02',$9b
_65816  dta c'65816',$9b
The next CPU detection example is limited to distinguishing a 6502 from a 65816 microprocessor. The program instructions are read differently by a 6502 than by a 65816. A 6502 executes 'INC @' and a 'NOP' instead of 'XBA' followed by 'SBC #'. With this "transparency" we can be assured that the program does not perform illegal operations and correctly recognizes the correct CPU. The idea for this concise and very clever test comes from Ullrich von Bassewitz.
  org $2000
 
  opt c+        ; 65816 enabled
 
  lda #0
  inc @         ; Increment accumulator
  cmp #1
  bcc cpu6502
 
 ; ultimate test for 65816 presence
 
  xba           ; Put $01 in B accu
  dec @         ; A=$00 if 65C02
  xba           ; Get $01 back if 65816
  inc @         ; Make $01/$02
  
  cmp #2
  bne cpu6502
 
cpu65816
  ldx <text65816
  ldy >text65816
  jsr print_text
  rts
  
cpu6502
  ldx <text6502
  ldy >text6502
  jsr print_text
  rts
 
text6502  dta c'6502',$9b
text65816 dta c'65816',$9b

MEMORY BANKS

For probably anyone who has ever worked with the 8-bit Atari architecture, the term "memory bank" is associated with extended memory, divided into 16KB size banks, and switched in the address range [$4000..$7FFF].

MADS can interpret banks this way (option OPT B+, Hardware memory banks), but by default they are interpreted as virtual banks (option OPT B-, Virtual memory banks).

The following pseudo-commands apply to banks:
 LMB #value
 NMB
 RMB

LMB # (Load Memory Bank)

Sets the MADS bank counter to a value in the range [$00..$FF] (BANK = value):
 lmb #0
 lmb #bank
 lmb #5 , $6500      ; only with OPT B+

NMB (Next Memory Bank)

Increments the MADS bank counter (BANK = BANK + 1).
 nmb
 nmb  $6500          ; only with OPT B+

RMB (Reset Memory Bank)

Resets the MADS bank counter (BANK = 0).
 rmb
 rmb $3500           ; only with OPT B+
 rmb $8500           ; only with OPT B+
During assembly, MADS assigns the current bank counter value to each newly defined label. The programmer can affect the value of the bank counter through pseudo-commands.

Virtual memory banks (OPT B-)

In MADS, the term "virtual memory bank" refers to any area designated by a newly defined label set to the current value of the bank counter (the default bank counter is zero). That is, a virtual memory bank is not necessary a memory area in the range [$4000..$7FFF], but each label presents a code area of the program which has been assigned a code (bank counter value) in the range [$00..$FF] with appropriate pseudo-commands for use by the programmer (NMB, RMB, LMB).

The exceptions are .RELOC blocks, where the bank counter cannot be changed manually and is automatically updated by MADS, incrementing it for each instance of the .RELOC directive. Bank counter values are in the range [$0001..$FFF7].

The programmer can read the bank counter value associated with a label using the equal operator '=':
label

 ldx #=label
In this example, the X CPU register is set to the bank counter value associated by MADS with the label LABEL.

Another useful operator is the colon (':') placed at the beginning of the label name, which causes MADS to allow references to labels outside of the area specified by the current MADS bank counter. Sometimes this may cause problems, such as if there is more than one label with the same name in different local scopes or in areas with a different virtual bank number.
 lmb #5

label5
 nop

 lmb #6

label6
 nop

 lda :label5
In this example, without the operator ':' at the beginning of the label name in the instruction 'lda :label5' the error ERROR: Undeclared label LABEL5 (BANK=6) would result.

Virtual memory banks can be used to index an array containing values for the PORTB register. This is the main use of the option OPT B+.

Hardware memory banks (OPT B+)

This mode of operation can be called "bank sensitive".

Hardware banks are an extension of virtual memory banks and are understood by MADS as expanded memory banks in the range [$4000..$7FFF]. The pseudo-commands NMB, RMB, and LMB are extended to call the macro @BANK_ADD, which can be found in the directory "..\EXAMPLES\MACROS\"".

In this mode of operation, MADS requires the definitions for the following macros:
 @BANK_ADD
 @BANK_JMP
and requires label definitions with the names:
@TAB_MEM_BANKS
@PROC_ADD_BANK
The @TAB_MEM_BANKS label specifies the address of an array with values to write to the PORTB register for switching extended memory banks. You can take advantage of pre-written expanded memory bank detection routines included with MADS, in the file "..\EXAMPLES\PROCEDURES\@MEM_DETECT.ASM".

The label @PROC_ADD_BANK is used by the macro @BANK_ADD and defines the address of the code for switching extended memory banks.

The programmer can read the bank counter value associated with a label using the '=' operator:
label

 ldy #=label
In this example, the Y register is set to the memory bank value associated with the label LABEL by MADS.

If the MADS bank counter = 0:

If the MADS bank counter > 0:

An example of the use of MADS bank sensitive mode can be found in the file "..\EXAMPLES\XMS_BANKS.ASM". In this example, the program code is located in two different extended memory banks and runs as if it were a single program.

HISTORY

v1.9.5
- added pseudo-command SET to redefine labels, with a similar effect as temporary labels that begin with '?', e.g.:
temp set 12

     lda #temp

temp set 23

     lda #temp
- added ability to force addressing mode in XASM-style ('a:', 'z:'):
 XASM        MADS
 lda a:0     lda.a 0
 ldx z:0     lda.z 0
- added ability to specify a new code relocation address in XASM-style ('r:'):
 XASM        MADS
 org r:$40   org $40,*
- improved performance of '-x Exclude unreferenced procedures' switch; .VAR variables are not allocated for unused procedures
- extended single-line syntax for :rept loops so that the loop counter can now be used as a parameter :1 (%%1):
line0
line1
line2
line3

ladr :4 dta l(line:1)
hadr :4 dta h(line:1)
- added a warning message when unstable illegal 6502 opcodes are used, i.e. CIM
- added new functionality to pseudo-commands RUN and INI to retain the previous assembly address when assembling to $2E0 (RUN), $2E2 (INI)
- added support for anonymous labels @, @+[1..9] (forward), @-[1..9] (backward); for clarity, their use is limited only to conditional branches no more than 10 instances away:
@ dex   ---- -------
  bne @+   |  --   |
  stx $80  |   |   |
@ lda #0   |  --   |
  bne @- ---       |
  bne @-1  ---------
- extended directives #IF and #WHILE to allow arguments declared by .VAR, whereas previously only a variable was allowed:
 .var temp .word

 #if temp>#2100
 #end

 #if .word temp>#2100
 #end
v1.9.4
- dodana normalizacja ścieżek dla plików, tak aby działały pod Unixami, znaki '\' zamieniane są na '/'
- poprawione przekazywanie dyrektyw jako parametrów do procedur i makr, dyrektywy nie były rozpoznawane przy włączonym przełączniku -c (case sensitive)
- poprawione działanie .USE [.USING]
- dodana informacja w postaci ostrzeżenia (WARNING) o etykiecie powodującej nieskończoną ilość przebiegów asemblacji (INFINITE LOOP)
- dodany zapis dwóch bajtów nagłówka FF FF dla pliku zawierającego blok o adresie ładowania $FFFF
- komentarze po mnemonikach nie wymagających argumentu zostaną potraktowane jako błąd, wyjątkiem jest łączenie rozkazów w stylu xasm poprzez znak ':', np.:
 pla $00          -> ERROR: Extra characters on line
 pha:pla $00      -> OK
- rozszerzona składnia makr o możliwość używania parametrów w postaci nazw a nie tylko wartości numerycznych-decymalnych, np.:
.macro SetColor val,reg
 lda :val
 sta :reg
.endm

.macro SetColor2 (arg1, arg2)
 lda #:arg1
 sta :arg2
.endm
- naprawione definiowanie etykiet dla n/w sytuacji, pierwsza etykieta nie zostanie zignorowana
temp  label = 100
v1.9.3
- poprawione przetwarzanie bloków .PROC, które w pewnych okolicznościach mogły zostać pominięte podczas asemblacji
- poprawiony zapis BLK EMPTY dla plików SDX jeśli zastosowaliśmy deklarację takiego bloku przez .DS
- poprawki dotyczące testowania końca linii
- dodane dyrektywy .FILESIZE, .SIZEOF jako odpowiednik dotychczasowej dyrektywy .LEN
- rozszerzona składnia dla pól struktury .STRUCT, np.:
.struct name
 .byte label0
 .byte :5 label1
 label2 .byte
 label3 :2 .word
.ends
v1.9.2
- możliwość określenia adresu dla .ZPVAR = $XX
- usprawnione odwołania do etykiet wyliczeniowych .ENUM, np. enum_label(field0, field1)
- dodana możliwość generowania bloku dla symboli zewnętrznych BLK UPDATE EXTRN dla plików DOS-a, poprzednio tylko dla plików .RELOC, np.:
  .extrn vbase .word
  org $2000
  lda #$80
  sta vbase+$5d

  blk update extrn
- dodany komunikat błędu "Could not use NAME in this context" w przypadku rozkazów odwołań do bloków .MACRO, .ENUM, .STRUCT
- poprawiony błąd który uniemożliwiał użycie 'EQU' w nazwie etykiety
- dodana dyrektywa .CB +byte,....., ostatni bajt ciągu znakowego zapisywany jest w inwersie
- dodana obsługa segmentów poprzez dyrektywy .SEGDEF, .SEGMENT, .ENDSEG
- dodana nowa dyrektywa #CYCLE #N generująca kod 6502 o zadanej liczbie cykli N
- dodana obsługa nielegalnych rozkazów CPU 6502, przykład w pliku "..\EXAMPLES\TEST6502_ILLEGAL.ASM"
- uaktualnione pliki konfiguracyjne dla Notepad++ '..\syntax\Notepad++'
- poprawiony zapis pliku LST
- naprawiona alokacja pamięci dla zmiennych strukturalnych, rozszerzona składnia dla .STRUCT
.struct LABEL
 x,y,z .word     // wiele zmiennych tego samego typu w jednej linii
 .byte a,b
.ends

.enum type
  a=1,b=2
.ende

.struct label2
  x type
  type y
.ends
v1.9.0
- naprawiony zapis linii z komentarzem /* */ do pliku listingu *.LST, poprzednio takie linie nie były zapisywane
- poprawka dla etykiet deklarowanych z linii komend -d:label, poprzednio takie etykiety widziane były tylko w pierwszym przebiegu
- w przypadku addytywności bloków .LOCAL tylko pierwszy adres z takich bloków jest zapisywany
- poprawki dotyczące parsowania makr, poprzednio etykiety zaczynające się od END mogły zostać zinterpretowane jako pseudo rozkaz END
- poprawka odczytu dla pustego pliku relokowalnego, poprzednio występował błąd 'Value out of range'
- poprawki dla .USING (.USE)
v1.8.8 - 1.8.9
- uaktualniony silnik duchów programowych ..\EXAMPLES\SPRITES\CHARS o duchy 8x24
- w przypadku braku podania rozszerzenia pliku i braku istnienia takiego pliku dla ICL 'filename' zostanie domyślnie przyjęte rozszerzenie *.ASM ICL 'filename.asm'
- poprawione działanie komentarzy /* */ w blokach .MACRO i .REPT
- usunięty błąd uniemożliwiający poprawną asemblację bloku #IF, #WHILE dla wyrażeń łączonych przez .OR, .AND
- przełączniki w linii komend mogą być poprzedzone tylko znakiem '-', poprzednio także '/' jednak były problemy z działaniem tego znaku na MacOSX
- poprawiony zakres działania dyrektywy .USING, dla aktualnej przestrzeni nazw i kolejnych zawierających się w tej przestrzeni nazw
v1.8.6 - 1.8.7
- usprawnione rozpoznawanie komentarzy /* */ w wyrażeniach
- domyślny adres dla .ZPVAR ustawiony na $0080, poprzednio $0000
- dodana nowa dyrektywa .ELIF jako krótszy odpowiednik dyrektywy .ELSEIF
- rozszerzone działanie dyrektywy .LEN o możliwość podania jako parametru nazwy pliku, zwracana jest wówczas długość takiego pliku
- usprawnione działanie dyrektywy .DEF w wyrażeniach warunku .IF (.IFDEF, .IFNDEF)
v1.8.5
- dodane makro relokujące moduły RMT ...\EXAMPLES\MSX\RMT_PLAYER_RELOCATOR\
- dodany test składni dla nie asemblowanych procedur .PROC gdy aktywny jest przełącznik -x "Exclude unreferenced procedures"
- poprawione działanie przełącznika "-d:label[=value]", podanie wartości dla etykiety jest teraz opcjonalne, domyślnie mads przypisze wartość 1
- dyrektywy .DS i .ALIGN nie spowodują alokacji zmiennych zdefiniowanych przez .VAR
- alokacja zmiennych .VAR przed nowym blokiem ORG nie nastąpi jeśli blok ORG znajduje się w bloku .LOCAL lub .PROC
- poprawione łamanie wierszy znakiem '\' w ciągach ograniczonych nawiasami ()
- usunięty błąd powodujący relokowanie adresu dla wyrażenia dyrektywy .ERROR (ERT)
- usunięte zauważone błędy przy parsowaniu parametrów linii komend
- usunięte zauważone błędy dotyczące optymalizacji długości kodu makro rozkazów MVA, MWA itp.
- poprawiony kod realizujący zagnieżdżanie bloków .PROC
- poprawiony kod realizujący działanie pseudo rozkazów warunku IFT ELI ELS EIF
- dodany komunikat "'#' is allowed only in repeated lines" dla przypadków użycia licznika pętli # (.R) poza pętlą
- usunięty błąd powodujący błędne alokowanie zmiennych zadeklarowanych przez dyrektywę .VAR podczas wykonywania makra
- w celu ujednolicenia składni odwołania do etykiet typów wyliczeniowych możliwe są tylko poprzez znak kropki '.', poprzednio także przez '::'
- możliwe krótsze odwołania do typów wyliczeniowych enum_label(fields), np. :
.enum typ
 val0 = 1
 val1 = 5
 val2 = 9
.ende

 lda #typ(val0|val2)  ; == "lda #typ.val0|typ.val2"
- rozszerzona składnia dyrektywy .SAV, np.:
 .sav 'filename',offset,length
 .sav 'filenema',length
 .sav [offset] 'filename',offset2,length
 .sav length
 .sav offset,length
- rozszerzona składnia dyrektywy .ARRAY, w przypadku braku podania maksymalnego indeksu tablicy zostanie on obliczony na podstawie ilości wprowadzonych elementów, elementy można wprowadzać bez konieczności poprzedzenia ich indeksem [expression], np.:
.array temp .byte
 1,4,6                  ; [0..2]   = 1,4,6
 [12] = 9,3             ; [12..13] = 9,3
 [5]:[8] = 10,16        ; [5..6]   = 10,16 ; [8..9] = 10,16
 0,0,\                  ; [14..17] = 0,0,1,1
 1,1
.enda                   ; 18 elementów, TEMP [0..17]
- dodana możliwość alokacji zmiennej typu strukturalnego przy pomocy dyrektyw .VAR i .ZPVAR, np.:
.struct Point
 x .byte
 y .byte
.ends

 .var a,b,c Point
 .zpvar Point f,g,i
- dodana możliwość alokacji zmiennej typu wyliczeniowego przy pomocy dyrektyw .VAR i .ZPVAR, np.:
.enum Boolean
 false = 0
 true = 1
.ende

 .var test Boolean
 .zpvar Boolean test
- dodana możliwość deklaracji pól struktury przy pomocy typów wyliczeniowych, np.:
.enum EState
  DONE, DIRECTORY_SEARCH, INIT_LOADING, LOADING
.ende
 
.struct SLoader
    m_file_start .word
    m_file_length .word
 
    m_state EState
.ends
v1.8.3 - 1.8.4
- nowy silnik duchów programowych z minimalnymi wymaganiami pamięci, bez dodatkowych buforów pamięci obrazu ...EXAMPLES\SPRITES\CHARS_NG
- nowa wersja pakera Huffmana (kompatybilna z Free Pascal Compiler-em, "fpc -MDelphi sqz15.pas") i dekompresora Huffmana SQZ15 ...EXAMPLES\COMPRESSION\SQUASH
- poprawiony kod generowany dla rozkazów MVP, MVN, PEA, BRA (CPU 65816)
- dodane nowe rozkazy BRL, JSL, JML (CPU 65816), jako odpowiedniki rozkazów długich skoków BRA, JSR, JMP
- blok aktualizacji etykiet zewnętrznych (external) został rozszerzony o zapis młodszego i starszego bajtu adresu takiej etykiety
- poprawione działanie dyrektywy .USE (.USING), działa niezależnie od przestrzeni nazw w której zostanie użyta
- usunięty błąd, który powodował w pewnych sytuacjach pomijanie asemblacji bloku #IF, #WHILE
- dodana możliwość definiowania zmiennych poprzez dyrektywę .DS lub pseudo rozkaz ORG przed blokiem .RELOC
- dodana dodatkowa forma składni dla dyrektywy .VAR, z tym że dla takiego przypadku nie ma możliwości określenia adresu umiejscowienia zmiennych w pamięci
 .VAR .TYPE lab1 lab2 lab3 .TYPE lab4 .TYPE lab5 lab6 ...

 .var .byte a,b,c .dword i j
- dodana możliwość definicji pojedyńczych zmiennych typu strukturalnego w krótszy sposób aniżeli dotąd przez DTA
.struct @point
 x .byte
 y .byte
.ends

pointA	@point		; pointA dta @point [0] <=> pointA dta @point
pointB	@point		; pointB dta @point [0] <=> pointB dta @point

points	dta @point [100]
- dodana nowa dyrektywa .ZPVAR umożliwiająca automatyczne przydzielenie miejsca zmiennym na stronie zerowej
 .ZPVAR TYPE label1, label2 label3 = $80	; LABEL1=$80, LABEL2=LABEL1+TYPE, LABEL3=LABEL2+TYPE
 .ZPVAR label4, label5 TYPE			; LABEL4=LABEL3+TYPE, LABEL5=LABEL4+TYPE

 .print .zpvar
- poprawione działanie dyrektywy .ERROR i pseudo rozkazu ERT, możliwe jest umieszczenie dodatkowych informacji w wierszu podobnie jak dla .PRINT (.ECHO) np.:
  ERT *>$6000 , 'BUUU przekroczyliśmy zakres pamięci o ' , *-$6000 , ' bajtów'
- dodana możliwość zagnieżdżania bloków procedur .PROC, ten sam kod może być wywoływany z różnymi parametrami np.:
.proc copySrc (.word src+1) .var

 .proc ToDst (.word src+1, dst+1) .var
 .endp

	ldy #0
src	lda $ffff,y
dst	sta $ffff,y
	iny
	bne src

	rts
.endp

	copySrc.ToDst #$a080 #$b000

	copySrc #$a360
- dodane nowe dyrektywy .ENUM i .ENDE (.EEND)
.enum	dni_tygodnia

	poniedzialek = 1
	wtorek, sroda = 5, czwartek = 7
	piatek
	sobota
	niedziela

.ende

	ift dzien==dni_tygodnia::wtorek
	.print 'wtorek'
	eif
- rozszerzona funkcjonalność komentarzy wieloliniowych /* */ o możliwość umieszczania ich gdziekolwiek
 lda #12+ /* komentarz */ 23
- umożliwiona relokacja adresów definiowanych dyrektywą .DEF
 .reloc 
 .def label=*
 lda label
- dodana możliwość użycia znaków { } do oznaczenia bloku (z wyjątkiem bloków .MACRO), znak '{','}' zostaje rozpoznany na początku nowego wiersza, np.:
#while .word ad+1<=#$bc40+39
{
ad	sta $bc40

	inw ad+1
}

.proc lab
{
	.local temp2
	{
	}

	.array tab [255] .long
	{}
}
v1.8.2
- zniesione ograniczenie długości pliku dla pseudo rozkazu INS (poprzednio długość wczytywanego pliku ograniczona była do 65536 bajtów)
- dodany komunikat błędu 'The referenced label ... has not previously been defined properly' w przypadku etykiet, które nie zostały zdefiniowane do końca, np. tylko w pierwszym przebiegu wartością nieokreśloną
- dodana nowa dyrektywa .ECHO jako odpowiednik dyrektywy .PRINT, dodatkowo informacje generowane przez .PRINT (.ECHO) zapisywane są teraz także w listingu *.LST
- dodana nowa dyrektywa .ALIGN pozwalająca na wyrównanie do zadanego zakresu pamięci, dodatkowo można określić wartość jaką wypełnić pamięć
  [label] .ALIGN N[,fill]
- dodany nowy przełącznik -U (Warn of unused labels)
v1.8.1
- rozszerzone działanie znaku backslash '\', umieszczenie go na końcu wiersza oznacza kontynuację aktualnego wiersza od nowego wiersza, np.:
  macro_temp \
  _____________________________________parametr1_________________________________________________\
  _____________________________________parametr2_________________________________________________\
  _____________________________________parametr3_________________________________________________

  lda\
  #____________________________________label________________________________________\
  +__________________________________expression___________________________________
- zmienione testowanie niekończącego wywoływania się makr po którym wystąpi błąd 'Infinite loop'
- naprawiony zapis etykiet do pliku *.LAB, błąd powstał po dodaniu addytywności obszarów LOCAL
- poprawione działanie pseudo rozkazu SIN (kod zapożyczony z XASM)
- poprawione rozpoznawanie dyrektyw przy włączonym przełączniku -C (Case sensitive)
- usprawniony odczyt bloków .REPT (wskazanie prawidłowej linii z błędem) i .MACRO
- zablokowane użycie .VAR w bloku .REPT
- umożliwione zagnieżdżanie oraz wielokrotne uruchamianie (poprzez makra) pętli .REPT i :repeat (poprzednio występował komunikat 'Use .REPT directive')
- umożliwione przekazywanie parametrów do bloku .REPT, np.
.REPT 10, #
label:1           ; LABEL0, LABEL1, LABEL2 ... LABEL9
.ENDR

.REPT 5, $12,$33,$44,$55,$66
 dta :1,:2,:3,:4,:5            ; $12,$33,$44,$55,$66
 dta :5,:4,:3,:2,:1            ; $66,$55,$44,$33,$12
.ENDR
v1.7.9 - 1.8.0
- poprawiony błąd w opisie przełącznika -F, poprzednio 'Label at first column', prawidłowy opis to 'CPU command at first column'
- przepisana od nowa obsługa dyrektywy .DS i opcji OPT F+ (dodana możliwość użycia bloków RUN i INI)
- przepisana od nowa obsługa opcji OPT ?+ (etykiety lokalne w standardzie MAE)
- dodana możliwość upublicznienia w blokach PUBLIC tablic zadeklarowanych przez .ARRAY oraz deklaracji struktur .STRUCT
- dyrektywa generująca kod 6502 dla decyzji .TEST zastąpiona została przez dyrektywę #IF, dyrektywa .ENDT przez #END, dodatkowo możliwe jest użycie dyrektywy #ELSE np.:
 # if .byte i>#8 .and .byte i<#200
 # else
       #if .word j = #12
       #end
 # end
- dyrektywa generująca kod 6502 dla iteracji .WHILE zastąpiona została przez dyrektywę #WHILE, dyrektywa .ENDW przez #END, np.:
 lda 20               ->      lda 20
 # while .byte @=20   -> wait cmp 20
 # end                ->      sne
                      ->      jmp wait
- dyrektywy #IF i #WHILE akceptują dwa dodatkowe operatory '==' i '!='
- dodana dyrektywa .EXITM jako odpowiednik .EXIT
- dodana dyrektywa .FI jako odpowiednik .ENDIF
- dodana dyrektywa .IFDEF jako krótszy odpowiednik dyrektyw .IF .DEF
- dodana dyrektywa .IFNDEF jako krótszy odpowiednik dyrektyw .IF .NOT .DEF
- umożliwione zostało definiowanie makr w obszarze procedury .PROC, podsumowując aktualnie dopuszczalne jest zdefiniowanie makra w obszarze .LOCAL i .PROC
- wystąpienie jakiegokolwiek ostrzeżenia podczas asemblacji nie zmieni kodu wyjścia (exit_code=0), zmiana podyktowana potrzebą kompatybilności z linuxowym makefile
- ujednolicony sposób deklaracji etykiet lokalnych i globalnych, "białe znaki" przed nazwą etykiety nie wymuszą zdefiniowania takiej etykiety jako globalnej, umożliwi to tylko dyrektywa .DEF :LABEL
- poprawione makra @CALL.MAC i @CALL_2.MAC, zmienna tymczasowa globalna ?@stack_offset modyfikowana jest teraz przez dyrektywę .DEF
- rezygnacja z opcji -E (Eat White spaces), aktualnie jest ta opcja zawsze włączona
- poprawione wyświetlanie numeru linii z błędem w aktualnie wykonywanym makrze
- skrócone nazwy etykiet tworzonych podczas wykonywania makr (łatwiejsza ich identyfikacja w pliku *.LAB)
- poprawione działanie opcji OPT H-
- dodane nowe makro rozkazy INL (increse LONG), IND (increse DWORD), DEL (decrese LONG), DED (decrese DWORD)
- dodane nowe makro rozkazy CPB (compare BYTE), CPW (compare WORD), CPL (compare LONG), CPD (compare DWORD)
- usprawnione i rozszerzone działanie dyrektyw #TEST i #WHILE w oparciu o kod generowany przez makro rozkazy CPB, CPW, CPL, CPD, dyrektywy #TEST i #WHILE dla wyrażeń '=#0' i '<>#0' generują najkrótszy kod wynikowy
- dodana optymalizacja długości generowanego kodu dla makro rozkazów MWA, MWX, MWY
- dodana nowa opcja OPT R optymalizująca kod makro rozkazów MWA, MWX, MWY, MVA, MVX, MVY ze względu na zawartość rejestrów, np.:
                    opt r-        opt r+
    mva #0 $80  -> lda #$00  -> lda #0 
    mva #0 $81  -> sta $80   -> sta $80
                    lda #$00  -> sta $81
                    sta $81   ->
- rozszerzona funkcjonalność dyrektywy .DEF o możliwość przypisania wartości nowo deklarowanej etykiecie, np.:
 .def label = 1
- rozszerzona funkcjonalność dyrektywy .DEF o możliwość zdefiniowania etykiety globalnej niezależnie od aktulnego obszaru lokalnego, np.:
 .def :label
- umożliwiona została addytywność obszarów .LOCAL, tzn. może istnieć wiele obszarów lokalnych o tej samej nazwie, symbole zawarte w takich obszarach należeć będą do wspólnej przestrzeni nazw, np.:
.local namespace

 .proc proc1
 .endp

.endl

.local namespace

 .proc proc2
 .endp

.endl
v1.7.8
- dodane dyrektywy .MEND, .PGEND, .REND jako odpowiedniki .ENDM, .ENDPG, .ENDR
- obecnie deklaracja makra musi kończyć się dyrektywą .ENDM lub .MEND (poprzednio dopuszczalne było użycie dyrektywy .END)
- poprawiony sposób wykonywania makr dzięki czemu umożliwione zostało wykonanie dyrektywy .ENDL z poziomu wykonywanego makra
- poprawione zauważone błędy dotyczące starszych bajtów relokowanego adresu oraz bloku aktualizacji symboli publicznych
- dodana nowa dyrektywa .USING (.USE) pozwalająca określić ścieżkę poszukiwań dla nazw etykiet
- poprawione działanie dyrektyw .LOCAL, .DEF, których błędne działanie objawiało się w szczególnych przypadkach
- poprawione działanie makro rozkazów skoków (SNE, RNE itp.), których błędne działanie objawiało się w szczególnych przypadkach
- rozszerzona składnia dyrektywy .TEST (kod 6502 dla warunku) o dowolną ilość wyrażeń połączonych przez .OR lub .AND (brak możliwości zmiany piorytetu wartościowania przy pomocy nawiasów), np.:
 .test .byte k>#10+1 .or .word j>#100 .and .word j<#105 .or .byte k<=#5
 ...
 ...
 .endt
- rozszerzona składnia dyrektywy .WHILE (kod 6502 dla pętli) o dowolną ilość wyrażeń połączonych przez .OR lub .AND (brak możliwości zmiany piorytetu wartościowania przy pomocy nawiasów), np.:
 .while .byte k>#4 .and .byte k<#39
 ...
 ...
 .endw
v1.7.6 - 1.7.7
- dodany nowy przełącznik -B:ADDRESS umożliwiający asemblacje od zadanego adresu
- dodany nowa opcja OPT F+- pozwalająca tworzyć bloki ciągłej pamięci (przydatne dla cartów)
- dodana obsługa parametrów typu .LONG i .DWORD przekazywanych do procedur .PROC typu .VAR (poprzednio akceptowanymi typami parametrów był tylko .BYTE i .WORD)
- dodana nowa dyrektywa .FL realizująca zapis liczb rzeczywistych REAL w formacie FP Atari, np.:
pi .fl 3.1415926535897932384626433832795  ; 40 03 14 15 92 65
tb .fl 0.5 12.34 -2.30 0.00002
tb .fl 0.5, 12.34, -2.30, 0.00002
- umożliwiony został zapis wartości innych typów niż tylko .BYTE w bloku .ARRAY
- dodana obsługa typów wielokrotnych dla .STRUCT, poprzednio takie typy były akceptowane jednak pamięć nie była właściwie dla nich rezerwowana, np.:
  .struct test
   x :200 .byte
   y :999 .long
  .ends

buf dta test [0]
- poprawione błędy dotyczące generowania kodu relokowalnego zauważone przez Laoo, np.:
  .reloc

   lda temp
temp .long $aabbcc
- błąd 'Addres relocation overload' wystąpi teraz tylko gdy wyrażenie będzie dotyczyć więcej niż jednej etykiety relokowalnej, poprzednio każde wyrażenie z udziałem etykiety relokowalnej powodowało wyświetlenie tego komunikatu błędu
- blok aktualizacji symboli plublicznych rozszerzony został o możliwość przekazywania stałych różnych typów B-YTE, W-ORD, L-ONG, D-WORD, poprzednio przekazywanym typem był tylko W-ORD
- zmienione działanie dyrektywy .VAR w blokach .LOCAL znajdujących się w bloku .PROC, zmienne takie zawsze odkładane są na końcu bloku przed dyrektywą .ENDP, w pozostałych przypadkach na końcu bloku .LOCAL przed dyrektywą .ENDL
- umożliwiona została relokowalność kodu generowanego przez dyrektywy .WHILE i .TEST
- poprawione działanie testowania wartości typu .WORD w kodzie generowanym przez dyrektywy .WHILE i .TEST
- dodana nowa dyrektywa .ADR zwracająca adres etykiety przed zmianą adresu asemblacji
- dodana nowa dyrektywa .LEN zwracająca długość bloków zdefiniowanych przez .PROC i .ARRAY
- poprawione działanie operacji dzielenia, mnożenia i modulo, poprzednio błędnie był interpretowany piorytet dla tych operacji
- komentarze z końca linii nie poprzedzone znakiem komentarza będą powodować wystąpienie błędu 'Unexpected end of line'
- dodana możliwość przypisania zmiennej pól zdefiniowanych przez strukture, np.:
@point .struct
       x .byte
       y .byte
       .ends

a @point
b @point
c @point
- rozszerzona składnia .STRUCT o możliwość dodania nowych pól bez definiowania nazwy pola, np.:
 .struct @id
  id .word
 .ends

 .struct @mem
  @id
  adr .word
 .ends
- rozszerzona składnia makro rozkazu MWA o możliwość użycia adresowania pośredniego strony zerowej postindeksowanego Y, np.:
  mwa ($80),y $a000,x
  mwa $bc40,y ($f0),y
  mwa ($80),y ($82),y
- rozszerzona składnia dyrektywy .EXTRN, obecnie możliwe jest zapowiedzenie większej ilości etykiet różnych typów w jednym wierszu, zapowiedzenie procedury .PROC w takim wierszu musi znajdować się na jego końcu, np.:
  .extrn a,b,c,d .byte  x y z .word  line .proc(.byte x,y) .reg
- rozszerzona składnia dyrektywy .VAR, obecnie możliwe jest zadeklarowanie większej ilości etykiet różnych typów w jednym wierszu oraz przypisanie im adresu od którego zostaną odłożone w pamięci, np.:
  .var x y z .byte bit :2 .dword = $80
- rozszerzona składnia dla parametrów procedur przekazywanych przez zmienne .VAR, możliwe jest podanie przesunięcia np.:
move .proc (.word src+1,dst+1) .var

src lda $ffff
dst sta $ffff

     .endp
- dodana nowa dyrektywa .NOWARN wyłączająca wyświetlenie ostrzeżenia dla aktualnie asemblowanego wiersza, np.:
 .nowarn PROCNAME
- dodane nowe makro rozkazy PHR, PLR, realizujące odkładanie i zdejmowanie wartości rejestrów z udziałem stosu sprzętowego, np.:
  phr -> pha         plr -> pla
         txa                tay
         pha                pla
         tya                tax
         pha                pla
- dodane nowe makro rozkazy ADB, SBB realizujące dodawanie i odejmowanie wartości typu .BYTE, np.:
 ADB $80 #12 $b000  -> lda $80
                        clc
                        adc #12
                        sta $b000

 SBB #200 $a000     -> lda #200
                        sec
                        sbc $a000
                        sta $a000
- dodana możliwość użycia składni C dla liczb szestnastkowych, np.:
 lda 0x2000
 ldx #0x12

temp = 0x8000
v1.7.5
- dyrektywa .DS w blokach relokowalnych SDX RELOC i MADS RELOC deklaruje od teraz pusty blok
- dodany nowy przełącznik -F, który umożliwia umieszczanie rozkazów CPU i pseudo rozkazów od pierwszej kolumny w wierszu
- przepisane od nowa procedury odczytu bloków .MACRO, .REPT oraz procedura realizująca dzielenie wiersza przy pomocy znaku '\'
- dodane nowe pseudo rozkazy ADW, SBW realizujące dodawanie i odejmowanie wartości typu WORD dla CPU6502, np.:
  adw hlp #40        ; hlp=hlp+40
  adw hlp #20 pom    ; pom=hlp+20
- rozszerzone działanie dyrektywy .DEF o możliwość zdefiniowania etykiety, np.: .DEF label
- zwiększona liczba przebiegów dla deklaracji etykiet przez EQU dla pewnych szczególnych przypadków
v1.7.4
- naprawione działanie dyrektywy .PRINT, dotąd mogła nie wyświetlić wartości etykiet zaczynającej się na literę 'A','B','C','D','E','F','G','H','L','T','V'
- zablokowane działanie dyrektywy .DS w blokach .RELOC i SDX oraz naprawione jej działanie z instrukcją warunkową .IF (IFT)
- usprawnione przeszukiwanie ścieżek dostępu -i:path (można odwoływać się do podkatalogów tam zawartych)
- w przypadku wystąpienia błędów podczas asemblacji wyświetlane są one wszystkie a nie tylko pierwszy z błędów
- poprawione zauważone błędy, m.in. użycie makra w pliku .RELOC mogło spowodować w pewnych sytuacjach zapis błędnej informacji o relokownych adresach
- uproszczony został sposób kończenia procedur wykorzystujących stos programowy MADS-a, nie ma potrzeby używania dyrektywy .EXIT, a dyrektywa .ENDP nie powoduje już dodatkowych działań na stosie programowym
- dodana nowa dyrektywa .SYMBOL jako odpowiednik bloku aktualizacji BLK UPDATE NEW SYMBOL 'SYMBOL', dyrektywę .SYMBOL można użyć w dowolnym miejscu programu
- dodane automatyczne wywoływanie bloków aktualizacji (ADDRESS, EXTERNAL, PUBLIC, SYMBOL) dla .RELOC i SDX
- dodane nowe dyrektywy .BY, .WO, .HE, .EN, .SB (zapożyczone z MAE)
- dodany nowy przełącznik OPT ?- (domyślnie) etykiety ze znakiem zapytania (?labels) traktowane są jako etykiety tymczasowe, OPT ?+ etykiety ze znakiem zapytania (?labels) traktowane są jako lokalne i tymczasowe, nazwą obszaru lokalnego jest ostatnio użyta etykieta bez znaku zapytania
- dodane dyrektywy .LEND, .PEND, .AEND, .WEND, .TEND, .SEND jako odpowiedniki dyrektyw .ENDL, .ENDP, .ENDW, ENDW, .ENDT, .ENDS
- dodane nowe dyrektywy .GLOBAL i .GLOBL jako odpowiednik (zamiennik) dyrektywy .PUBLIC
- dodana optymalizacja skoków warunkowych JEQ, JNE, JPL, JMI, JCC, JCS, JVC, JVS, jeśli jest taka możliwość wybierany jest skok krótki typu BEQ, BNE, BPL, BMI, BCC, BCS, BVC, BVS
- dodany nowy domyślny separator znak spacji dla przekazywanych parametrów do .PROC, .MACRO, dotąd był to tylko znak przecinka
- usprawnienia dotyczące przekazywania parametrów do makr i procedur, np. paramatrem makra może być dyrektywa zwracająca wartość wyrażenia lub symbol licznika pętli '#'
  :12 makro #
- dodana możliwość użycia znaku spacji jako separatora dla .VAR, .EXTRN, np.
  .EXTRN a b c d .word
  .VAR i = 1  j = 2 .byte
  .VAR a b c d .byte
- rozszerzona składnia dla .VAR umożliwiająca zaincjowanie zmiennych stałą, np.:
 .var i = 10  j = 12 .byte
 .var a , b = 2 .byte
- dodane nowe dyrektywy .WHILE, .ENDW pozwalające na automatyczne wygenerowanie kodu dla pętli WHILE, np.:
         ldx #$ff
 .while .word adr < #$bc40+40*24
         stx $bc40
    adr: equ *-2
         inw adr
 .endw
- dodane nowe dyrektywy .TEST, .ENDT pozwalające na automatyczne wygenerowanie kodu dla warunku, np.:
 .test .byte (@>=#'a')
  .test .byte (@<=#'z')
      
  .endt
 .endt
v1.7.3
- dodana możliwość zmiany adresu asemblacji .PROC lub .LOCAL bez zmiany adresu ładowania
- usunięto optymalizację kodu dla makro rozkazów MWA itp., która mogła powodować w szczególnych przypadkach zapętlenie się MADS-a
- dodane dyrektywy .REG, .VAR pozwalające określić sposób przekazywania parametrów do procedur (.REG przez rejestry CPU, .VAR przez zmienne)
- dodana dyrektywa .VAR pozwalająca na deklarację zmiennych w blokach .PROC, .LOCAL, zadeklarowane zmiennne są fizycznie odkładane na końcu takiego bloku
- rozszerzona składnia dla dyrektywy .EXTRN, np. EXTRN label1,label2,label3... TYPE
- jesli brak deklaracji etykiet dla stosu programowego MADS-a, przyjmowane są domyślne wartości @PROC_VARS_ADR=$0500, @STACK_ADDRESS=$0600, @STACK_POINTER=$FE
- dodany repeat_counter #, który można używać zamiennie z dyrektywą .R
- wystapi błąd '^ not relocatable' przy próbie relokacji rozkazu 'lda ^label'
- dodana obsługa symboli publicznych dla stałych (C-ONSTANT) w blokach PUBLIC
- poprawiona relokowalnosc dla tablic .ARRAY, danych stworzonych przez .STRUCT, parametrów przekazywanych do procedur przez stała #
v1.7.2
- przepisana na nowo obsługa pseudo rozkazów REQ, RNE, RPL, RMI, RCC, RCS, RVC, RVS, SEQ, SNE, SPL, SMI, SCC, SCS, SVC, SVS
- poprawione działanie dyrektywy .LINK dla bloków o stałych adresach
- poprawione testowanie słów zarezerwowanych (można używać nazw zarezerwowanych dla 65816 gdy używamy tylko 6502)
- zmiany w listingu, wyświetla informacje o numerze banku tylko gdy bank > 0
- dodana obsługa makro rozkazów MWA, MWX, MWY, MVA, MVX, MVY, ADD, SUB, INW, DEW (do ich obsługi nie są już potrzebne makra)
v1.7.1
- dodana możliwość używania nazw mnemoników 65816 w trybie pracy 6502, w trybie 65816 wystąpi już błąd Reserved word
- poprawione działanie pseudo rozkazów skoków SCC, RNE itp. w makrach
- usprawnione wykonywanie wielu makr rozdzielonych znakiem dwukropka ':'
v1.7.0
- usunięty błąd, który powodował zbyt mała liczbę przebiegów asemblacji
- dodana obsługa pseudo rozkazów JEQ, JNE, JPL, JMI, JCC, JCS, JVC, JVS (makra nie są już potrzebne do ich obsługi)
v1.6.9
- rozszerzona składnia dla .ARRAY, .PUT
- dodany pseudo rozkaz EXT pozwalający na deklaracje etykiety external
- dodane makra JEQ, JNE, JPL, JMI, JCC, JCS
- dodane dyrektywy .PAGES i .ENDPG
- dodana dyrektywa .END zastepujaca inne dyrektywy .END?
- przełącznik -H zastąpiony został przez -HC (generuje plik nagłówkowy dla CC65)
- dodany nowy przełącznik -HM generujący plik nagłówkowy dla MADS-a z sortowaniem na etykiety typu CONSTANTS, VARIABLES, PROCEDURES
- dodana nowa dyrektywa .RELOC generująca kod relokowalny w formacie MADS-a
v1.6.8
- dodana nowa dyrektywa .PUT oraz rozszerzona składnia dla dyrektywy .GET (../EXAMPLES/MSX/MPT_PLAYER/MPT_RELOCATOR.MAC , ../EXAMPLES/MSX/TMC_PLAYER/TMC_RELOCATOR.MAC)
- dodana obsługa pseudo rozkazów XASM-a REQ, RNE, RPL, RMI, RCC, RCS, RVC, RVS, SEQ, SNE, SPL, SMI, SCC, SCS, SVC, SVS
- dodana możliwość łączenia dowolnej liczby znanych MADS-owi mnemoników przy pomocy znaku ':' (styl XASM-a), np.:
  lda:cmp:req 20
  ldx:ldy:lda:iny label
v1.6.6 - 1.6.7
- źródło MADS-a kompatybilne z Free Pascal Compiler, po kompilacji możliwe jest jego używanie na innych platformach systemowych, jak np. Linux, Mac OS, OS/2 itp.
- od teraz MADS sam dobiera odpowiednią liczbę przebiegów asemblacji, przełącznik '/3' nie jest już potrzebny
- poprawiony i rozbudowany został mechanizm przekazywania parametrów do MADS-a (rozdział 'Przełączniki assemblera')
- poprawione zostało wywołanie makra w linii rozdzielanej znakiem '\' oraz usprawnione rozpoznawanie i wykonywanie linii rozdzielanych znakami '\'
- poprawiony błąd, w którym MADS mylił dyrektywę .ENDM z pseudorozkazem IFT
- poprawione działanie instrukcji warunkowych .ELSEIF, .ELSE
- poprawione testowanie poprawności instrukcji warunkowych w makrach
- obsługa procedur .PROC została rozbudowana o nowe makra i mechanizmy, dzięki którym podobna jest w działaniu jak i łatwości użycia do procedur z języków wyższego poziomu
- dla procedur .PROC z zadeklarowanymi parametrami potrzebna jest teraz dodatkowa deklaracja @PROC_VARS_ADR
- brak ograniczeń w liczbie parametrów przekazywanych do procedur, jedynym ograniczeniem jest dostępna pamięć
- dodany nowy przełącznik /d:label=value pozwalający zdefiniować nową etykietę MADS-a z poziomu linii poleceń
- dodany nowy przełącznik /x "Exclude unreferenced procedures" pozwalający pominąć podczas asemblacji nie używane w programie procedury zadeklarowane dyrektywą .PROC
- nowa opcja OPT T+ (track sep, rep) śledząca zmiany rozmiaru rejestrów A,X,Y dokonywane przez rozkazy SEP, REP (CPU 65816)
- nowe biblioteki w katalogu ..\EXAMPLES\LIBRARIES
- w deklaracji obszaru lokalnego .LOCAL nie jest wymagane podanie nazwy obszaru
- nowe operatory '-=', '+=', '++', '--' pozwalające zmniejszyć/zwiększyć wartość etykiety tymczasowej, np.:
  ?label --      ->  ?label=?label-1
  ?lab ++        ->  ?lab=?lab+1
  ?temp += 3     ->  ?temp=?temp+3
  ?ofset -= 5    ->  ?ofset=?ofset-5
- rozszerzona o znak przecinka składnia deklaracji parametrów procedur, np.:
 .proc nazwa (.byte a,b,c .word d,e)
 .endp