Mad-Assembler 1.5.9beta






Mad-Assembler (MADS)

W założeniu MADS skierowany jest do użytkowników QA, XASM, FA. Z QA zapożyczona została składnia, z XASM niektóre makro rozkazy i zmiany składni, z FA obsługa składni Sparta DOS X. Dodatkowo specjalnie z myślą o Lizardzie :) umożliwione zostało użycie dodatkowych znaków w nazwach etykiet. Pozatym dodana została obsługa CPU 6502, 65816, oraz makr, procedur, podziału pamięci na "banki", wielowymiarowych nazw etykiet.

Makro rozkazy XASM'a zastąpione są makrami (plik ..\EXAMPLES\MACROS\XASM.ASM)


Wymagania

MADS jest aplikacją 32 bitową, napisaną w Delphi. Większość asemblerów napisano w C, więc żeby się nie powtarzać użyłem Delphi 7.0 ;)

Wymagany jest DOS 32bit z obsługą długich nazw plików, który spotkamy z Windowsami. Osobiście testowałem program tylko z Windows 2000 i XP.

Procesor, taki aby uciągnął Windows'a :)


Asemblacja

Syntax: MADS source [options]

/c              Label case sensitivity
/e              Eat white spaces
/h[:filename]   Header file for CC65
/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
/s              Silent mode
/t[:filename]   List label table

Domyślne nazwy plików to:

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

Użycie przełącznika /c spowoduje rozróżnianie wielkości liter w nazwach etykiet, zmiennych, stałych. Dyrektywy assemblera i rozkazy CPU 6502, 65816 są zawsze rozpoznawane bez względu na wielkość liter.

Użycie przełącznika /e spowoduje pomijanie tzw. "białych spacji". Używając tego przełącznika należy pamiętać aby przed komentarzami umieszczać odpowiedni symbol (';','//'), inaczej MADS zacznie analizę komentarza, która najpewniej zakończy się błędem. "Białe spacje" umożliwiają formatowanie listingu, służą jego większej przejrzystości.

Przełącznik /i:path służy do określenia ścieżek poszukiwań dla operacji ICL oraz INS. Przełącznika można użyć wielokrotnie podczas jednego wywołania MADS'a, np.:

 /i:"c:\program files" /i:c:\temp /i:"d:\atari project"
Przełącznik /p pomocny jest w połączeniu z Code Genie. Gdy wystąpi błąd podczas asemblacji, w oknie Output Bar edytora Code Genie pojawi się stosowny komunikat wygenerowany przez MADS, np.:
D:\!Delphi\Masm\test.asm (29) ERROR: Missing .EXIT

Teraz wystarczy kliknąć dwukrotnie linię z tym komunikatem, a kursor edytora ustawi się w linii z błędem.

Użycie przełącznika /s spowoduje uaktywnienie tzw. trybu pracy 'Silent mode', czyli żadne komunikaty nie zostaną wyświetlone, co najwyżej komunikaty błędów (ERROR) i ostrzeżenia (WARNING).


Zawartość pliku LST

Format listingu nie odbiega od tego znanego z XASM, jedyną zmianą jest dodanie przed adresem, numeru banku pamięci. Więcej o bankach w rozdziale "Banki pamięci, definiowanie i odwołania".

Mad-Assembler v0.8 by TeBe/Madteam
Source: D:\!Delphi\Masm\test.asm
     1 				* ---
     2 				; to jest program
     3
     4 = 00,9033		obraz equ $9033
     5 = 00,00A0		scr1 equ $a0
     6
     7 				 opt h-
     8 				 org $2000
     9
    10 00,2000 EA		main nop

Zawartość pliku LAB

Podobnie jak w przypadku XASM, w pliku *.LAB przechowywane są informacje na temat etykiet które wystąpiły w programie.

W sumie są to trzy kolumny:

  • Pierwsza kolumna to numer banku przypisany do etykiety.
  • Druga kolumna to wartość etykiety.
  • Trzecia kolumna to nazwa etykiety.

    Specjalne znaczenie w nazwach etykiet mają znaki:

  • etykieta zadeklarowana w makrze (dwa dwukropki) ::
  • znak kropki '.' rozdziela nazwę struktury (.MACRO lub .PROC lub .LOCAL) od nazwy pola w strukturze

    Wartość liczbowa, która występuje po :: oznacza numer wywołania makra.

    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
    

    Zawartość pliku H

    Nie jestem pewien czy wszystko z tym plikiem jest OK, ale Eru chciał żeby coś takiego było więc jest :) Ma on być pomocny przy łączeniu ASM'a z CC65, czyli portem C dla małego Atari. Jego zawartość może wyglądać tak (przykładowy plik 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
    

    Kody wyjścia

    3 = bad parameters, assembling not started
    2 = error occured
    1 = warning(s) only (XASM only)
    0 = no errors, no warnings
    

    Asemblacja na stronie zerowej

    W przeciwieństwie do dwu-przebiegowych asemblerów takich jak QA i XASM, MADS jest cztero-przebiegowy. Co to daje ?

    Weźmy sobie taki przykład:
     org $00
     
     lda tmp+1
     
    tmp lda #$00
    
    Dwu-przebiegowy assembler nie znając wartości etykiety 'TMP' przyjmie domyślnie, że jej wartość będzie dwu-bajtowa, czyli typu WORD i wygeneruje w sumie rozkaz 'lda W'.

    Natomiast MADS uprzejmie wygeneruje rozkaz strony zerowej 'lda Z'. I to właściwie główna właściwość czterech biegów.

    Teraz ktoś powie, że woli gdy rozkaz odwołujący się do strony zerowej ma postać 'lda W'. Nie ma sprawy, wystarczy że rozszerzy mnemonik:
     org $00
     
     lda.w tmp+1
     
    tmp lda #$00
    
    Są dopuszczalne trzy rozszerzenia mnemonika
     .b[.z]
     .w[.a][.q]
     .l[.t]
    
    czyli odpowiednio BYTE, WORD, LONG (TRIPLE). Z czego ostatni generuje 24bitową wartość i odnosi się do 65816 i pamięci o ciągłym obszarze, wątpię czy kiedykolwiek użyjecie taki rozkaz.

    Innym sposobem na wymuszenie rozkazu strony zerowej jest użycie nawiasów klamrowych { } np.

     dta {lda $00},$80    ; lda $80
    
    W MADS możemy robić tak samo, ale po co, czwarty bieg załatwi sprawę za nas :) Problem stanowi teraz umieszczenie takiego fragmentu kodu w pamięci komputera. Możemy spróbować załadować taki program bezpośrednio na stronę zerową i jeśli obszar docelowy mieści się w granicy $80..$FF to pewnie OS przeżyje, poniżej tego obszaru będzie trudniej.

    Dlatego MADS umożliwia takie coś:

     org $20,$3080
     
     lda tmp+1
     
    tmp lda #$00
    
    Czyli asembluj od adresu $20, ale załaduj pod adres $3080.

    Podsumowując:

     org adres1,adres2
    

    Asembluj od adresu adres1, umieść w pamięci od adresu adres2. Taki ORG zawsze spowoduje stworzenie nowego bloku w pliku.


    Dostępne pseudo rozkazy i dyrektywy

     label EQU address
     label  =  address
     OPT [chlos][+-]
     ORG [[expression]]address[,address2]
     INS 'filename'["filename"][*][+-value][,+-ofset[,length]]
     ICL 'filename'["filename"]
     DTA [abfghltv](value1,value2...)[(value1,value2...)]
     DTA [cd]'string'["string"]
     RUN adres
     INI adres
     END
     
     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] A[dress]
     BLK U[pdate] N[ew] X 'string'
     
     label SMB 'string'
    
     NMB
     RMB
     LMB #value
    
     .LOCAL
     .ENDL
    
     .REPT value
     .ENDR
    
     .IF [IFT] expression
     .ELSE [ELS]
     .ELSEIF [ELI] expression
     .ENDIF [EIF]
    
     .PRINT 'string1','string2'...,value1,value2,...
     .ERROR [ERT] 'string'["string"] lub .ERROR [ERT] expression
    
     .MACRO
     .ENDM
     :[%%]parameter
    
     .EXIT
    
     .PROC
     .ENDP
    
     .WORD
     .BYTE
     .LONG
     .DWORD
    
     .AND
     .OR
     .XOR
     
     .DS 
    
    Czyli w większości po staremu, chociaż parę zmian zaszło. W przypadku cudzysłowów można używać ' ' lub " ". Oba rodzaje cudzysłowów traktowane są jednakowo z wyjątkiem adresowania (dla '' zostanie wyliczona wartość ATASCII znaku, dla "" zostanie wyliczona wartość INTERNAL znaku).

    BLK

     BLK N[one] X                    - blok bez nagłówków, licznik programu ustawiany na X
    
     BLK D[os] X                     - blok DOS'a z nagłówkiem $FFFF lub bez nagłówka gdy
                                       poprzedni taki sam, licznik programu ustawiany na X
    
     BLK S[parta] X                  - blok o stałych adresach ładowania z nagłówkiem $FFFA,
                                       licznik programu ustawiany na X
    
     BLK R[eloc] M[ain]|E[xtended]   - blok relokowalny umieszczany w pamięci main lub extended
    
     BLK E[mpty] X M[ain]|E[xtended] - blok relokowalny rezerwujący X bajtów w pamięci main lub
                                       extended
                                       Uwaga: licznik programu jest natychmiastowo
                                              zwiększany o X bajtów
    
     BLK U[pdate] S[ymbols]          - blok aktualizujący w poprzednich blokach SPARTA lub
                                       RELOC adresy symboli SDX
    
     BLK U[pdate] A[dress]           - blok aktualizacji adresów w blokach RELOC
    
     BLK U[pdate] N[ew] X 'string'   - blok deklarujący nowy symbol 'string' w bloku RELOC
                                       o adresie X. Gdy nazwa symbolu poprzedzona jest znakiem @,
                                       a adres jest z pamięci podstawowej to taki symbol może być
                                       wywoływany z command.com
    

    label SMB 'string'

    Deklaracja etykiety jako symbolu SDX. Symbol może mieć maksymalnie długość 8-iu znaków. Dzięki temu po użyciu BLK UPDATE SYMBOLS asembler wygeneruje poprawny blok aktualizacji symboli. Np:

           pf  smb 'PRINTF'
               jsr pf
               ...
    
    sprawi że po jsr SDX wstawi adres symbolu.

    Uwaga: deklaracja ta nie jest przechodnia, to znaczy że poniższy przykład spowoduje błędy w czasie kompilacji:
           cm  smb 'COMTAB'
           wp  equ cm-1       (błąd !)
    
               sta wp
    
    Zamiast tego należy użyć:
           cm  smb 'COMTAB'
    
               sta cm-1       (ok !)
    

    Uwaga: wszystkie deklaracje symboli należy użyć przed deklaracjami etykiet, jak i programem właściwym !

    :repeat

    Examples: 
               :4 asl @
               :2 dta a(*)
    
    Znak ':' określa liczbę powtórzeń linii (w przypadku makr określa numer parametru). Liczba powtórzeń powinna być z zakresu <0..2147483647>.

    .REPT value

    Dyrektywa REPT jest rozwinięciem ':repeat' z tą różnicą, że nie jest powtarzana jedna linia, tylko zaznaczony blok programu. Początek bloku definiowany jest dyrektywą .REPT, po niej musi wystąpić wartość lub wyrażenie arytmetyczne określające liczbę powtórzeń z zakresu <0..2147483647>. Koniec bloku definiuje dyrektywa .ENDR.

    Przed dyrektywą .REPT może wystąpić etykieta, natomiast przed dyrektywą .ENDR już nie może występować etykieta.

    .PRINT

    Powoduje wypisanie na ekranie podanej jako parametr wartości wyrażenia lub ciągu znakowego ograniczonego znakami ' ' lub " ".

    .ERROR, ERT

    Dyrektywa .ERROR i pseudo rozkaz ERT mają to samo znaczenie. Zatrzymują asemblację programu oraz wyświetlają komunikat podany jako parametr, ograniczony znakami ' ' lub " ". Jeśli parametrem jest wyrażenie logiczne, wówczas asemblacja zostanie zatrzymana gdy wartość wyrażenia logicznego jest prawdą (komunikat 'User error').

    OPT

     c+  włącz obsługę CPU 65816 (16bit)
     c-  włącz obsługę CPU 6502 (8bit)                           (default)
     h+  zapisuj nagłówek pliku dla DOS                          (default)
     h-  nie zapisuj nagłówka pliku dla DOS
     l+  zapisuj listing do pliku (LST)
     l-  nie zapisuj listingu (LST)                              (default)
     o+  zapisuj wynik asemblacji do pliku wynikowego (OBX)      (default)
     o-  nie zapisuj wyniku asemblacji do pliku wynikowego (OBX)
     s+  drukuj listing na ekranie
     s-  nie drukuj listingu na ekranie                          (default)
    

    Wszystkie przełączniki OPT możemy używać w dowolnym miejscu listingu, czyli np. możemy włączyć zapis listingu w linii 12, a w linii 20 wyłączyć itd., wówczas plik z listingiem będzie zawierał tylko linie 12..20.

    Jeśli chcemy użyć trybów adresowania 65816, musimy o tym poinformować asembler przez 'OPT C+'.

    Jeśli używamy CodeGenie możemy użyć 'OPT S+', dzięki temu nie musimy przechodzić do pliku z listingiem, bo listing wydrukowany został w dolnym okienku (Output Bar) :)

    ORG

     adr                 asembluj od adresu ADR, ustaw adres w nagłówku na ADR
     adr,adr2            asembluj od adresu ADR, ustaw adres w nagłówku na ADR2
     [b($ff,$fe)]        zmień nagłówek na $FFFE (zostaną wygenerowane 2 bajty)
     [$ff,$fe],adr       zmień nagłówek na $FFFE, ustaw adres w nagłówku na ADR
                         (6 bajtów)
     [$d0,$fe],adr,adr2  zmień nagłówek na $D0FE, asembluj od adresu ADR, ustaw
                         adres w nagłówku na ADR2 (6 bajtów)
     [a($FFFA)],adr      nagłówek SpartaDOS $FAFF, ustaw adres w nagłówku na ADR
                         (6 bajtów)
    
     [a($ffff),d'atari',c'ble',20,30,40],adr,adr2                    
    

    Nawiasy kwadratowe [ ] służą określeniu nowego nagłówka, który może być dowolnej długości. Pozostałe wartości rozdzielone znakiem przecinka ',' oznaczają odpowiednio: adres asemblacji, adres w nagłówku pliku.

    DTA

         b  wartość typu BYTE
         a  wartość typu WORD
         v  wartość typu WORD, relokowalna
         l  młodszy bajt wartości (BYTE)
         h  starszy bajt wartości (BYTE)
         t  wartość typu LONG (24bit)
         e  wartość typu LONG (24bit)
         f  wartość typu DWORD (32bit)
         g  wartość typu DWORD (32bit) w odwróconej kolejności     
         c  ciąg znaków ATASCII ograniczony apostrofami '' lub "", znak * na końcu spowoduje
            invers wartości ciągu, np. dta c'abecadlo'*
         d  ciąg znaków INTERNAL ograniczony apostrofami '' lub "", znak * na końcu spowoduje
            invers wartości ciągu, np. dta d'abecadlo'*
    

    NMB (Next Memory Bank)

    Zwiększa o 1 aktualny numer banku (BANK = BANK + 1).

    RMB (Reset Memory Bank)

    Ustawia numer banku na zero (BANK = 0).

    LMB (Load Memory Bank)

         value        wartość z zakresu <0..$FFFB>
         =label       odczytuje numer banku przypisany do etykiety LABEL
    
    Ustawia numer banku na nową wartość (BANK = value).

    .BYTE, .WORD, .LONG, .DWORD

    W/w dyrektywy assemblera służą do oznaczenia dopuszczalnych typów parametrów w deklaracji parametrów procedury (.BYTE, .WORD, .LONG). Możliwe jest także ich użycie w zastępstwie pseudo rozkazu DTA.
    .proc test {.word tmp .byte value}
    
     .byte "atari",5,22
     .word 12,$FFFF
     .long $34518F
     .dword $11223344
    

    .AND, .OR, .XOR

    W/w dyrektywy to odpowiedniki operatorów logicznych || (.OR) i && (.AND) i ^ (.XOR)

    .DS

    W/w dyrektywa pozwala zarezerwować pamięć bez jej inicjalizacji. To odpowiednik pseudo rozkazu 'ORG *+byte'.

    .AND, .OR

    W/w dyrektywy to odpowiedniki operatorów logicznych || (.OR) i && (.AND).

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

    where: 
    
    centre     is a number which is added to every sine value 
    amp        is the sine amplitude 
    size       is the sine period 
    first,last define range of values in the table. They are optional.
               Default are 0,size-1. 
    
    Example: dta a(sin(0,1000,256,0,63))
             defines table of 64 words representing a quarter of sine with
             amplitude of 1000.
    

    RND(min,max,length)

    Ten pseudo rozkaz umożliwia wygenerowanie LENGTH losowych wartości z przedziału < MIN..MAX >.


    Dyrektywy warunkowe

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

    W/w dyrektywy i pseudo rozkazy wpływają na przebieg asemblacji.


    Procedury, deklaracja

    Procedur dotyczą n/w dyrektywy:

     name .PROC [{.Typ1 Par1 .Typ2 Par2 ...}]
     .PROC name [{.Typ1 Par1 .Typ2 Par2 ...}]
     .EXIT [expression]
     .ENDP
    
    MADS wymaga dla procedur z zadeklarowanymi parametrami dwóch etykiet, o konkretnych nazwach (adres stosu, wskaźnik stosu):
     @STACK_ADDRESS
     @STACK_POINTER
    

    Brak deklaracji dla w/w etykiet i próba użycia struktury .PROC z zadeklarowanymi parametrami spowoduje wystąpienie błędu 'Undeclared label .STACK_ADDRESS' lub 'Undeclared label .STACK_POINTER'.

    name .PROC [{.Typ1 Par1 .Typ2 Par2 ...}]

    Deklaracja procedury o nazwie name. Nazwa procedury jest wymagana i konieczna, jej brak wygeneruje błąd. Do nazw procedur nie można używać nazw mnemoników i pseudo rozkazów.

    Jeśli chcemy przekazać parametry do procedury, musimy je zadeklarować. Deklaracja parametrów procedury mieści się pomiędzy nawiasami klamrowymi { }. Dostępne są trzy typy parametrów:

  • .BYTE (8-bit)
  • .WORD (16-bit)
  • .LONG (24-bit)

    Bezpośrednio po deklaracji typu, oddzielona minimum jedną spacją, następuje nazwa parametru.

    Przykład deklaracji procedury:
    name .PROC {.WORD name_par1 .BYTE name_par2}
    

    .EXIT [expression]

    Wyjście z procedury. To taki odpowiednik RTS, z tym że w naszym przypadku musimy dodatkowo zmodyfikować wskaźnik stosu @STACK_POINTER aby programowy stos działał prawidłowo.

    Aby tego dokonać MADS po napotkaniu w strukturze .PROC rozkazu .EXIT (albo .ENDP jeśli .EXIT nie wystąpiło) wymusza wykonanie makra @ExitProc, które użytkownik sam może zaprojektować, albo skorzystać z dołączonego do MADS'a (plik EXITPROC.ASM), które ma następującą postać:

    @ExitProc .macro ' '
    
     .if :1<>0
      lda @stack_pointer
      tax
     
      sec
      sbc #:1
      sta @stack_pointer
     
       .if :0>1
         lda :2 
       .endif
    
     .endif 
    
     rts
    
     .endm
    

    Poprzez rozkaz .EXIT można dodatkowo przekazać wartość lub wyrażenie (przez akumulator). Czyli taka procedura staje się już funkcją.

    .ENDP

    Zakończenie deklaracji procedury. Jeśli w obszarze .PROC nie wystąpiła dyrektywa .EXIT, wówczas to .ENDP wymusza wykonanie makra @ExitProc.

    Jeśli jednak procedura nie miała zadeklarowanych parametrów, makro @ExitProc nie zostanie wymuszone i nie zostanie wygenerowany żaden rozkaz. Dodanie rozkazu RTS w ciele procedury przy wyjściu z każdej ścieżki jest obowiązkiem programującego, a nie assembler'a.


    Makra, deklaracja

    Makr dotyczą n/w pseudo rozkazy i dyrektywy:

    name .MACRO ['separator'] ["separator"]
     .MACRO name ['separator'] ["separator"]
     .EXIT
     .ENDM
     :[%%]parameter
    

    name .MACRO ['separator'] ["separator"]

    Deklaracja makra o nazwie name. Nazwa makra jest wymagana i konieczna, jej brak wygeneruje błąd. Do nazw makr nie można używać nazw mnemoników i pseudo rozkazów (błąd 'Reserved word').

    Za słowem .MACRO może wystąpić deklaracja separatora (pomiędzy apostrofem pojedyńczym lub podwójnym) i trybu przekazywania parametrów do makra (pojedyńczy apostrof bez zmian, podwójny apostrof z rozbijaniem parametrów).

    Domyślnym separatorem, rozdzielającym parametry przekazywane do makra jest znak przecinka ','.
  • 'separator'
    Pomiędzy pojedyńczymi apostrofami możemy umieścić znak-separator, który będzie używany do oddzielenia parametrów przy wywołaniu makra.
  • "separator"
    Pomiędzy podwójnymi apostrofami możemy umieścić znak-separator, który będzie używany do oddzielenia parametrów przy wywołaniu makra. Dodatkowo podwójny apostrof oznacza automatyczne "rozbijanie" przekazywanych parametrów na dwa elementy: tryb adresacji i argument, np.:
     test #12 200 <30
    
    test .macro " "
    .endm
    
    
     test2 #12  ,  200  , <30
    
    test2 .macro ""
    .endm
    
    Makro TEST2 ma zadeklarowany separator-przecinek oraz podwójny apostrof ", czyli po wywołaniu makra parametry będą "rozbijane" na dwa elementy: tryb adresacji i argument.

    Makro TEST ma zadeklarowany separator-spację oraz podwójny apostrof ", czyli po wywołaniu makra parametry zostaną rozłożone na:

     test '#' 12 ' ' 200 '#' 0
    

    Tylko dla znaków '<' i '>' zostanie wyliczona wartość parametru. Przykład wykorzystania tak zadeklarowanych makr w pliku z makro rozkazami XASM'a ..\EXAMPLES\MACROS\XASM.ASM


    .EXIT

    Zakończenie działania makra. Powoduje bezwględne zakończenie działania makra.

    .ENDM

    Zakończenie deklaracji makra.

    :[%%]parameter

    Parametr jest liczbą decymalną dodatnią (>=0), poprzedzoną znakiem dwukropka ':' lub dwoma znakami procentu '%%'. Jeśli w makrze chcemy aby znak ':' określał liczbę powtórzeń a nie numer parametru wystarczy że następny znak po dwukropku nie będzie z przedziału '0'..'9', tylko np:
     :$2 nop
     :+2 nop
     :%10 nop
    
    Parametr :0 (%%0) ma specjalne znaczenie, zawiera liczbę przekazanych parametrów. Z jego pomocą możemy sprawdzić czy wymagana liczba parametrów została przekazana do makra:
      .IF :0<2 || :0>5
        .ERROR "Wrong number of arguments"
      .ENDIF
    
      IFT %%0<2 || :0>5
        ERT "Wrong number of arguments"
      EIF 
    

    Przykład makra:

    load_word .macro
       lda <:1
       sta :2
       lda >:1
       sta :2+1   
     .endm
    
     test ne
     test eq
     
    .macro test
      b%%1 skip
    .endm
    

    Procedury, wywołanie

    Procedurę wywołujemy poprzez jej nazwę (identycznie jak makro), po niej mogą wystąpić parametry, rozdzielone separatorem w postaci znaku przecinka ',' (nie ma możliwości zadeklarowania innego separatora).

    Jeśli typ parametru nie będzie zgadzał się z typem zadeklarowanym w deklaracji procedury wystąpi komunikat błędu 'Incompatible types'.

    Jeśli przekazana liczba parametrów różni się od liczby zadeklarowanych parametrów w deklaracji procedury wystąpi komunikat błędu 'Improper number of actual parameters'.

    Możliwe są trzy sposoby przekazania parametru:

  • przez wartość (#)
  • przez wartość spod adresu (bez znaku poprzedzającego)
  • przez akumulator (@)

    Przykład wywołania procedury:

     proc_name @ , #$166 , $A400
    

    MADS po napotkaniu wywołania procedury, która miała zadeklarowane parametry wymusza wykonanie makra @CALL. Jeśli jednak procedura nie miała zadeklarowanych parametrów, zamiast makra @CALL zostanie wynegerowany rozkaz JSR nazwa_procedury.

    Do makra @CALL MADS przekazuje parametry wyliczone na podstawie deklaracji procedury (rozbija każdy parametr na trzy składowe: tryb adresacji, typ parametru, wartość parametru w zapisie decymalnym).
    @CALL proc_name,'@','B',0,'#','W',358,' ',W,41984
    
    Makro @CALL odłoży na stos zawartość akumulatora, następnie wartość $166, następnie wartość spod adresu $A400.

    Parametr przekazywany przez akumulator (@) powinien być zawsze na początku, jeśli wystąpi w innym miejscu zawartość akumulatora zostanie zmodyfikowana (domyślne makro @CALL nakłada takie ograniczenie). Oczywiście użytkownik może to zmienić pisząc swoją wersję makra @CALL

    Wszelkie deklaracje etykiet w obrębie procedury są typu lokalnego. Aby odwołać się do etykiety globalnej możemy (musimy jeśli w obszarze procedury występuje deklaracja etykiety o tej samej nazwie) poprzedzić ją znakiem dwukropka ':'.

    Jeśli chcemy dostać się do etykiet zadeklarowanych w procedurze spoza obszaru procedury, wówczas adresujemy (podobnie jak dla struktur .LOCAL) z użyciem znaku kropki '.', np.:

     lda test.pole1
    
    test .proc
    pole1 nop
     .endp
    
    Czyli podsumowując:
     rozkaz nazwa_struktury.pole_struktury
    

    Jeśli poszukiwana przez assembler etykieta nie wystąpiła w obrębie procedury, wówczas nastąpi jej poszukiwanie w głównym programie.

    Wyjście z procedury .PROC nastąpi po napotkaniu rozkazu .EXIT (lub .ENDP jeśli .EXIT nie wystąpiło) przez co MADS wymusi wykonanie makra @ExitProc, które użytkownik sam może zaprojektować, albo skorzystać z dołączonego do MADS'a. Możliwe jest przekazanie wartości poprzez .EXIT (w załączonym do MADS pliku EXITPROC.ASM z makrem @ExitProc wartość ta ładowana jest do akumulatora), np.:
    .proc nazwa
     
     .exit #20
     
    .endp
    

    W makrze @ExitProc musi zostać zawarty program który zmodyfikuje wartość wskaźnika stosu @STACK_POINTER, jest to niezbędne w celu prawidłowego działania stosu programowego. Od wskaźnika stosu odejmowana jest liczba bajtów które zostały przekazane do procedury. Liczbę bajtów przekazaną do procedury przekazuje do makra @ExitProc MADS w postaci parametru.

    Jeśli procedura nie miała zadeklarowanych parametrów i nie użyliśmy dyrektywy .EXIT w jej ciele tylko .ENDP, wówczas nie zostanie wywołane makro @ExitProc ani żaden inny rozkaz. Dodanie rozkazu RTS w ciele procedury przy wyjściu z każdej ścieżki jest obowiązkiem programującego, a nie assembler'a.


    Parametry procedur, odwołania

    name .PROC {.WORD name_par1 .BYTE name_par2}
    
    Zadeklarowanie parametrów procedury spowoduje automatyczne przypisanie im wartości. Aby MADS mógł wyliczyć te wartości wymagana jest obecność deklaracji etykiety @STACK_ADDRESS.

    Etykiety parametrów w naszym w/w przykładzie przyjmą wartości:

    liczba_bajtów_parametru = 0
    
    name_par1 = @STACK_ADDRESS - liczba_bajtów_parametru
    
    liczba_bajtów_parametru = liczba_bajtów_parametru + 2  (WORD)
    
    name_par2 = @STACK_ADDRESS - liczba_bajtów_parametru
    
    liczba_bajtów_parametru = liczba_bajtów_parametru + 1  (BYTE)
    
    Aby móc odwołać się do parametru z poziomu procedury, musimy użyć wskaźnika stosu @STACK_POINTER, np.:
     ldx @stack_pointer
     
     lda name_par1,x
     clc
     adc <$4000
     sta WYNIK
     
     lda name_par1+1,x
     adc >$4000
     sta WYNIK+1
    
    Czyli dokonaliśmy operacji WYNIK = NAME_PAR1 + $4000

    Odczytanie wartości parametrów możliwe jest tylko przy adresowaniu indeksowym. Użytkownik nie powinien ingerować w wartość @STACK_POINTER tylko zostawić to zadanie dedykowanym makrom.

    Można stworzyć makro, które będzie odczytywało wartość parametru i przekazywało do odpowiedniego rejestru, np. akumulatora.
    @GetPar .macro
    
     ldx @stack_pointer
     lda :1,x
    
     .endm
    
    Nasze przykładowe dodawanie będzie wyglądało teraz tak:
     @GetPar name_par1
     clc
     adc <$4000
     sta WYNIK
    
     @GetPar name_par1+1
     adc >$4000
     sta WYNIK+1
    

    Makra, wywołanie, parametry

    Makro wywołujemy poprzez jego nazwę, po niej mogą wystąpić parametry makra, rozdzielone separatorem którym jest domyślnie znak przecinka ','. Liczba parametrów uzależniona jest od wolnej pamięci komputera PC. Jeśli przekazana liczba parametrów jest mniejsza od liczby parametrów używanych, wówczas pod brakujące parametry zostanie podstawiona wartość $FFFFFFFF.

     macro_name [Par1, Par2, Par3, 'Par4', "string1", "string2" ...]
    
    Parametrem może być wartość, wyrażenie lub ciąg znaków ograniczony apostrofem pojedyńczym lub podwójnym.

    Apostrofy pojedyńcze ' ' zostaną przekazane razem ze znakami znajdującymi sie pomiędzy nimi.

    Apostrofy podwójne " " oznaczają ciąg znaków i tylko ciąg znaków znajdujący się pomiędzy apostrofami zostanie przekazany.

    Jeśli wpiszemy rozkaz składający się z siedmiu znaków, a czwartym znakiem będzie znak dwukropka':', np. 'lda:sta' wówczas znak ':' zostanie zastąpiony znakiem zapytania '?'. Tak zmodyfikowany rozkaz zostanie potraktowany jako próba wywołania makra. Jeśli nie zadeklarowaliśmy makra o nazwie 'lda?sta' wystąpi błąd. W ten sposób można zastąpić składnie XASM'a makrami MADS'a.

    Wszelkie deklaracje etykiet w obrębie makra są typu lokalnego. Aby odwołać się do etykiety globalnej o takiej samej nazwie jak lokalna należy poprzedzić ją znakiem dwukropka ':'.

    Jeśli poszukiwana przez assembler etykieta nie wystąpiła w obrębie makra, wówczas nastąpi jej szukanie w obszarze lokalnym (jeśli wystąpiła dyrektywa .LOCAL), następnie w procedurze (jeśli procedura jest aktualnie przetwarzana), na końcu w głównym programie.

    Przykład wywołania makra:

     macro_name 'a',a,>$a000,cmp    ; dla domyślnego separatora ','
     macro_name 'a'_a_>$a000_cmp    ; dla zadeklarowanego separatora '_'
     macro_name 'a' a >$a000 cmp    ; dla zadeklarowanego separatora ' '
    
    Możliwe jest wywoływanie makr z poziomu makra, oraz rekurencyjne wywoływanie makr. W tym ostatnim przypadku należy być ostrożnym bo może dojść do przepełnienia stosu.

    Przykład makra, które spowoduje przepełnienie stosu:

    skok .macro
    
          skok
    
         .endm
    
    Przykład programu, który przekazuje parametry do procedur:
     org $2000
     
     proc PutChar,'a'-64    ; wywołanie makra PROC, jako parametr
     proc PutChar,'a'-64    ; nazwa procedury która będzie wywołana przez JSR
     proc PutChar,'r'-64    ; oraz jeden argument (kod znaku INTERNAL)
     proc PutChar,'e'-64
     proc PutChar,'a'-64
    
     proc Kolor,$23         ; wywołanie innej procedurki zmieniającej kolor tła
    
    ;---
    
    loop jmp loop           ; pętla bez końca, aby zobaczyć efekt działania
    
    ;---
    
    proc .macro             ; deklaracja makra PROC
     push =:1,:2,:3,:4      ; wywołanie makra PUSH odkładającego na stos argumenty
                            ; =:1 wylicza bank pamieci
     
     jsr :1                 ; skok do procedury (nazwa procedury w pierwszym parametrze)
     
     lmb #0                 ; Load Memory Bank, ustawia bank na wartosc 0
     .endm                  ; koniec makra PROC 
    
    ;---
    
    push .macro             ; deklaracja makra PUSH
    
      lmb #:1               ; ustawia bank pamieci
    
     .if :2<=$FFFF          ; jesli przekazany argument jest mniejszy równy $FFFF to
      lda <:2               ; odłóż go na stosie
      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
     
    
    * ------------ *            ; procedura KOLOR
    *  PROC Kolor  *
    * ------------ *
     lmb #1                     ; ustawienie numeru banku na 1
                                ; wszystkie deklaracje będą teraz należeć do tego banku
    stack org *+256             ; stos dla procedury KOLOR
    color equ stack
    
    Kolor                       ; kod procedury KOLOR
     lda color
     sta 712
     rts
    
     
    * -------------- *          ; procedura PUTCHAR
    *  PROC PutChar  *
    * -------------- *
     lmb #2                     ; ustawienie numeru banku na 2
                                ; wszystkie deklaracje będą teraz należeć do tego banku
    stack org *+256             ; stos dla procedury PUTCHAR
    char  equ stack
    
    PutChar                     ; kod procedury PUTCHAR
     lda char
     sta $bc40
    scr equ *-2
    
     inc scr
     rts
    
    Oczywiście stos w tym przykładowym programie jest programowy. W przypadku 65816 można byłoby użyć stosu sprzętowego. Dzięki temu, że deklarowane zmienne przypisywane są do konkretnego numeru banku, można stworzyć strukturę wywołania procedury czy funkcji podobną do tych z języków wyższego poziomu.

    Prościej i efektywniej jednak skorzystać z deklaracji struktury .PROC jaką umożliwia MADS. Więcej o deklaracji procedury i operacjach jej dotyczących w rozdziale "Procedury".


    Banki pamięci, definiowanie i odwołania

    W MADS przez pojęcie "bank pamięci" rozumiany jest każdy obszar zdefiniowany przez numer banku. Czyli bank pamięci to nie zawsze obszar $4000..$7FFF, ale każdy obszar o zdefiniowanym numerze z zakresu <$0000..$FFFB>.

    Etykiety zadeklarowane w obszarze dla którego BANK = 0 są globalne.

    Etykiety zadeklarowane w obszarze dla którego BANK > 0 są lokalne.

    Banków dotyczą n/w pseudo rozkazy:
     NMB
     RMB
     LMB #value
    

    NMB (Next Memory Bank)

    Zwiększa o 1 aktualny numer banku (BANK = BANK + 1).

    RMB (Reset Memory Bank)

    Ustawia numer banku na zero (BANK = 0).

    LMB (Load Memory Bank)

         value        wartość z zakresu 0..$FFFE
         =label       odczytuje numer banku używany przez zmienną o etykiecie LABEL
    

    Ustawia numer banku na nową wartość (BANK = value).


    Aby załadować jakieś dane do rozszerzonej pamięci, np. dla rozszerzenia RAMBO do banku o kodzie $E3 piszemy:
     org $2000
     lda #$e3
     sta $d301
     rts
     ini $2000
    
     nmb            ; bank = bank + 1   (next memory bank)
     
     org $4000
    
     icl 'program_w_dodatkowym_banku_pamieci.asm'
    
    albo tak:
     org $2000
     lda #$e3
     sta $d301
     rts
     ini $2000
    
     lmb #$e3       ; bank = $E3        (load memory bank)
     
     org $4000
    
     icl 'program_w_dodatkowym_banku_pamieci.asm'
    

    Czyli musimy zatroszczyć się o to aby do PORTB wpisać konkretny kod banku rozszerzonej pamięci, zanim blok danych zostanie wczytany w obszar $4000..$7FFF lub inny. MADS zapisuje plik wynikowy w formacie Atari DOS, który nie przewiduje specjalnych nagłówków dla bloków ładujących się do pamięci rozszerzonej.


    Operatory

    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)
    
    

    Dostępne rozkazy 6502

       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  
    
    Możliwe jest użycie rozszerzenia mnemonika po znaku kropki '.':
       .b lub .z          BYTE
       .a lub .w lub .q   WORD
    
    np.
       lda.w $80   ; zapisze AD 80 00
       lda   $80   ; zapisze A5 80     
    

    Dostępne rozkazy 65816

    Oczywiście dostępne są rozkazy 6502, a oprócz nich:
       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
    
    Możliwe jest użycie rozszerzenia mnemonika po znaku kropki '.':
       .b lub .z          BYTE
       .a lub .w lub .q   WORD
       .t lub .l          TRIPLE, LONG (24bit)
    
    np.
       lda.w #$00     ; zapisze A9 00 00
       lda   #$80     ; zapisze A9 80
    

    Wyjątki stanowią rozkazy n/w, którym nie można zmienić rozmiaru rejestru w adresowaniu absolutnym (niektóre assemblery nie wymagają dla tych rozkazów podania znaku '#', jednak MADS wymaga tego)

    #$xx

       SEP   REP   COP
    

    #$xxxx

       PEA
    

    Innym wyjątkiem jest tryb adresowania pośredni długi, który reprezentowany jest przez nawiasy kwadratowe [ ]. Jak wiemy tego typu nawiasy wykorzystywane są też do obliczania wyrażeń, jednak jeśli asembler napotka pierwszy znak '[' uzna to za tryb adresowania pośredni długi i jeśli nie zasygnalizowaliśmy chęci używania 65816 wystąpi błąd 'Illegal adressing mode'. Aby "oszukać" assembler wystarczy dać przed kwadratowym nawiasem otwierającym '[' znak '+'.

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

    Etykiety

    Etykiety mogą być typu lokalnego lub globalnego, w zależności od miejsca w jakim zostały zadeklarowane.

    Etykiety deklarujemy używając n/w równoważnych pseudo rozkazów:
     EQU
      =
    
  • etykiety występują zawsze na początku wiersza
  • etykiety muszą zaczynać się znakiem ['A'..'Z','_','?','@']
  • pozostałe dopuszczane znaki etykiety to ['A'..'Z','0'..'9','_','?','@']

    Przykład deklaracji etykiety:

    ?nazwa   EQU  $A000
    nazwa     =   20
    nazwa2=12
    @?nazwa  EQU  'a'+32
    

    Czyli doszła możliwość użycia znaku zapytania '?' i "małpki" '@' w nazwach etykiet. Znak kropki '.' na początku nazwy sugeruje że jest to dyrektywa assemblera, natomiast znak zapytania '?' oznacza etykietę lokalną.

    We wcześniejszych wersjach MADS była możliwość użycia w nazwie etykiety znaku kropki '.', zrezygnowałem jednak z tego ze względu na mniejszą przejrzystość w oznaczaniu rozszerzenia mnemonika i adresowaniu struktur.

    Zapis z użyciem przecinka ',' odwołania do struktury, czy rozszerzenia mnemonika jest niezbyt udany:

      lda nazwa_stuktury,pole,x
      
      lda,w #$00
    
    Natomiast z użyciem kropki '.' zapis jest już przejrzysty i jednoznaczny, pozatym zgodny z zapisem znanym z C++, Delphi na PC:
      lda nazwa_stuktury.pole,x
      
      lda.w #$00
    

    Maksymalna liczba etykiet i makr ograniczona jest ilością pamięci komputera PC. Konkretnie można dokonać 2147483647 (INTEGER) wpisów do tablic dynamicznych. Jestem pewien że taka ilość jest wystarczająca :)

    Operacje arytmetyczne dokonywane są na wartościach typu INTEGER (signed 32 bit), jest to zakres wartości -2147483648..2147483647

    Jeden wiersz może mieć długość 65536 bajtów, takiej długości może być też nazwa etykiety. Nie miałem jednak okazji sprawdzić tak długich etykiet i wierszy :)


    Obszar lokalny

    Obszarów lokalnych dotyczą n/w dyrektywy:

     name .LOCAL
     .LOCAL name
     .ENDL
    
    Wszelkie deklaracje etykiet w obrębie .LOCAL są typu lokalnego. Aby odwołać się do etykiety globalnej należy poprzedzić ją znakiem dwukropka ':'.

    Jeśli poszukiwana przez assembler etykieta nie wystąpiła w obrębie obszaru .LOCAL, wówczas nastąpi jej szukanie w obszarze makra (jeśli jest aktualnie przetwarzane), potem w procedurze (jeśli procedura jest aktualnie przetwarzana), na końcu w głównym programie.

    W zadeklarowanym obszarze lokalnym wszystkie deklaracje rozróżniane są na podstawie nazwy obszaru lokalnego. Aby dotrzeć do zadeklarowanych etykiet w obszarze lokalnym spoza obszaru lokalnego musimy znać nazwę obszaru i etykiety w nim występującej, np.:
     lda obszar_lokalny.etykieta_w_obszarze_lokalnym
     
     lda name_local.local_label
    

    Czyli używamy znaku kropki '.' w adresowaniu takiej struktury .LOCAL (odwołania do .PROC spoza procedury podobnie).

    name .LOCAL

    Deklaracja obszaru lokalnego o nazwie name. Nazwa obszaru jest wymagana i konieczna, jej brak wygeneruje błąd. Do nazw obszaru nie można używać nazw mnemoników i pseudo rozkazów.

    .ENDL

    Zakończenie deklaracji obszaru lokalnego.

    Przykład deklaracji obszaru lokalnego:
     org $2000
     
    tmp ldx #0   <-------------   etykieta w obszarze globalnym
                              |
     lda obszar.pole  <---    |   odwolanie do obszaru lokalnego
                         |    |
    obszar .local        |    |   deklaracja obszaru lokalnego
                         |    |
     lda tmp   <---      |    |
                  |      |    |
     lda :tmp     |      | <---   odwolanie do obszaru globalnego
                  |      |
    tmp nop    <---      |        deklaracja w obszarze lokalnym
                         | 
    pole lda #0       <---   <--- deklaracja w obszarze lokalnym
                                |
     lda pole  <----------------- odwolanie w obszarze lokalnym
    
     .endl                        koniec deklaracji obszaru lokalnego
    

    Etykiety lokalne

    Etykieta (deklaracja) lokalna posiada tą właściwość, że jej wartość może ulegać zmianie. Normalnie próba ponownej deklaracji etykiety kończy się komunikatem 'Label declared twice'. Nie będzie takiego komunikatu jeśli jest to etykieta lokalna.

    Etykietę lokalną może zadeklarować użytkownik poprzez umieszczenie na początku nazwy znaku zapytania '?':
     ?name
    
    Przykład użycia etykiet lokalnych:
    ?loc = $567
    ?loc2 = $4444
    
    	 lda ?loc
    	 sta ?loc2
    
    ?loc = $123
    
    	 lda ?loc
    
    Deklaracje i etykiety lokalne występują także w makrach i procedurach, jednak nie trzeba ich oznaczać dodatkowo. Każda deklaracja w obrębie makra czy procedury domyślnie jest lokalna. Aby jednak mieć dostęp do deklaracji globalnych spoza makra lub procedury należy użyć operatora ':', np.:
    lp   ldx #0         ; deklaracja globalna etykiety 'LP' 
    
         test
         test
     
    test .macro
    
          lda :lp       ; znak ':' przed etykietą odczyta wartość etykiety globalnej
    
          sta lp+1      ; odwołanie do etykiety lokalnej 'LP' w makrze
    lp    lda #0        ; deklaracja etykiety lokalnej 'LP'
    
         .endm
    
    W w/w przykładzie występują deklaracje o tych samych nazwach, lecz każda z nich ma inną wartość i jest innego typu. Możemy się o tym przekonać przeglądając plik *.LAB
    Label table:
    00	2000	LP     - wartość etykiety globalnej LP
    00	2008	LP:1   - wartość etykiety lokalnej LP po pierwszym wywołaniu makra
    00	2010	LP:2   - wartość etykiety lokalnej LP po drugim wywołaniu makra
    00	2012	LP::1  - wartość etykiety lokalnej LP w pierwszym makrze przed jego wywołaniem
    

    Komentarze

    Do oznaczania komentarza jednoliniowego najbezpieczniej jest używać średnika ';' ponieważ znak '*' ma też inne znaczenia, może oznaczać operacje mnożenia, czy też aktualny adres podczas asemblacji. Średnik natomiast dedykowany jest tylko i wyłącznie do oznaczenia komentarza.

    Do oznaczenia komentarza jednoliniowego możliwe jest też użycie znaków '//', a dla wieloliniowego znaków '/* */'.
    ; to jest komentarz
                     ; to jest komentarz
     lda #0      ; to jest komentarz
     dta  1 , 3     * BŁĘDNY KOMENTARZ, ZOSTANIE ŹLE ZINTERPRETOWANY
     
     org $2000 + 1      BŁĘDNY KOMENTARZ, ZOSTANIE ŹLE ZINTERPRETOWANY
    
     nop // to jest komentarz
    
     // to jest komentarz
     
    /*
      ...
      to jest komentarz wieloliniowy
      ...
    */
    
    /*************************************
      to tez jest komentarz wieloliniowy
    **************************************/
    

    Znaki oznaczające komentarz wieloliniowy '/* */' można stosować tylko na początku linii, mogą być poprzedzone spacjami lub tabulatorami.

    Znaki oznaczające komentarz jednoliniowy '//' można stosować tak samo jak średnik ';', czyli bez ograniczeń.


    Definicje

    Definicje są zawsze typu globalnego, tzn. że niezależnie od tego czy definicja wystąpi w obszarze makra, procedury czy programu głównego to zawsze będzie widoczna.

    Etykiety definiujemy używając n/w równoważnych pseudo rozkazów:
     EQU
      =
    
    Czyli podobnie jak deklaracje etykiet, z tą różnicą że w definicji nazwa etykiety nie występuje na początku wiersza, musi poprzedzić ją minimum jedna spacja lub tabulator.

    Przykład definicji:

       ?nazwa   = $A000
       nazwa  EQU 20
       @?nazwa  = 'a'+32
    
    Przykładem zastosowania definicji etykiety lokalnej jest makro @CALL, w którym występuje definicja etykiety ?@STACK_OFFSET. Jest ona później wykorzystywana przez pozostałe makra wywoływane z poziomu makra @CALL, a służy do optymalizacji programu odkładającego parametry na stos programowy.
    @CALL .macro
     ?@stack_offset = 0
     
     ...
     ...
     
     
    @CALL_@ .macro
    
      sta @stack_address+?@stack_offset,x
      ?@stack_offset = ?@stack_offset+1
    
     .endm 
    

    Detekcja CPU6502, CPU65816

    Przykład zaczerpnięty ze strony

    http://www.s-direktnet.de/homepages/k_nadj/cputest.html
    /*
    
    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+
    
    
    ;detekcja zainstalowanego procesora
    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		;wyzerowanie bitu Z
    	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
    

    Następny program do detekcji CPU po disasemblacji inaczej wygląda dla 6502, inaczej dla 65816. 6502 rozkaz 'inc @' uzna za 'nop', rozkaz 'xba' uzna za 'sbc #'. Dzięki takiej "przeźroczystości" możemy być pewni że program nie wykona żadnej nielegalnej operacji i uczciwie rozpozna właściwy CPU. Pomysłodawcą tego testu jest Ullrich von Bassewitz.
     org $2000
    
     opt c+                 ; 65816 enabled
    
     lda #0
     
     inc @                  ; increment accumulator
     
     cmp #1
     bcc cpu6502
    
    ; ostateczny test na obecnosc 65816
    
     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 $c642
     rts
     
    cpu6502
     ldx < text6502
     ldy > text6502
     jsr $c642
     rts
    
    text6502  dta c'6502',$9b
    text65816 dta c'65816',$9b
    

    Programowanie Sparta DOS X

    Składnia dotycząca obsługi Sparta DOS X, zaczerpnięta została z FastAssemblera autorstwa Marka Goderskiego, poniżej cytat z instrukcji dołączonej do FA. Pliki źródłowe *.FAS można obecnie bez większych problemów asemblować MADS'em. Rozkazy relokowalne mają zawsze 2 bajtowy argument, nie ma obecnie możliwości relokowania 3 bajtowych argumentów (65816).

    Najważniejszą nowością w SDX dla programisty jest możliwość prostego pisania programów relokowalnych. Ponieważ procesor MOS 6502 nie posiada adresowania względnego, (prócz krótkich skoków warunkowych) programiści z ICD zastosowali specjalne mechanizmy ładowania bloków programu. Cały proces polega na załadowaniu bloku, a następnie specjalnego bloku aktualizacji adresów. Wszystkie adresy w bloku programu są liczone od zera. Wystarczy więc dodać do nich wartość memlo aby otrzymać adres właściwy. Które adresy zwiększyć, a które pozostawić? Właśnie po to jest specjalny blok aktualizacji który zawiera wskaźniki (specjalnie kodowane) do tychże adresów. Tak więc po bloku lub blokach RELOC obowiązkowe jest wykonanie UPDATE ADRESS dla poprawnego działania programu. Również po blokach SPARTA w których rozkazy (lub wektory) odwołują się do bloków RELOC lub EMPTY obowiązkowe jest wykonanie UPDATE ADRESS.

    Następną innowacją jest wprowadzenie symboli. Otóż niektóre procedury usługowe SDX zostały zdefiniowane za pomocą nazw! Nazwy te maja zawsze 8 liter (podobnie jak nazwy plików). Zamiast korzystać z tablic wektorów lub skoków (jak w OS) korzystamy z symboli definiowanych SMB. Po wczytaniu bloku lub bloków programu SDX ładuje blok aktualizacji symboli i w podobny sposób jak przy blokach relokowalnych zamienia adresy w programie. Symbole mogą być używane dla bloków RELOC i SPARTA.

    Programista może zdefiniować własne symbole zastępujące SDX lub zupełnie nowe dla wykorzystania przez inne programy. Robi się to poprzez blok UPDATE NEW. Trzeba jednak wiedzieć że nowy symbol musi być zawarty w bloku RELOC.

    Liczba bloków RELOC i EMPTY jest ograniczona do 7 przez SDX.

    Bloki takie można łączyć w łańcuchy np:

           blk sparta $600
           ...
    
           blk reloc main
           ...
    
           blk empty $100 main
           ...
    
           blk reloc extended
           ...
    
           blk empty $200 extended
    
    Oznacza to że rozkazy w tych blokach mogą odwoływać się do wszystkich bloków w łańcuchu.

    Łańcuch taki nie jest przerywany przez aktualizację adresów, lub symboli ale jest niszczony przez definicję nowego symbolu, oraz inne bloki (np: dos).

    Uwaga: Łańcuch taki ma sens tylko wtedy gdy wszystkie jego bloki ładują się do tej samej pamięci, lub gdy program przy odpowiednich odwołaniach przełącza pamięć.

    Uwaga: Rozkazy i wektory w blokach RELOC i EMPTY nie powinny odwoływać się do bloków SPARTA! Może to spowodować błąd gdy użytkownik załaduje program komendą LOAD, a użyje go po dłuższym czasie. O ile bloki RELOC i EMPTY były bezpieczne to nigdy nie wiadomo co jest w pamięci tam gdzie ostatnio był blok SPARTA!

    Równie niebezpieczne jest używanie odwołań do bloków RELOC i EMPTY przez bloki SPARTA (powód jak wyżej), jednakże podczas instalowania nakładek (*.sys) z użyciem INSTALL jest to czasem niezbędne, stąd jest dopuszczalne. Można także inicjować blok SPARTA (porzez $2E2) będzie on wtedy zawsze uruchomiony, a potem już zbędny.

    Uwaga: Pomiędzy blokami SPARTA, a RELOC i EMPTY może dojść do kolizji adresów! FA rozpoznaje odwołania do innych bloków poprzez adresy, przyjmując PC dla RELOC i EMPTY od $1000, tak więc gdy mieszamy te bloki należy mieć pewność ze SPARTA leży poniżej $1000 (np:$600) lub powyżej ostatniego bloku relokowalnego, zazwyczaj wystarcza $4000. Błąd taki nie jest przez kompilator wykrywany !


    Budowa plików Sparta DOS X, Atari DOS

    Przedruk z Serious Magazine, autor Qcyk/Dial.

    Plik sam w sobie jest tylko niewiele wartym zbiorem bajtów. Mnóstwo liczb, które mogą oznaczać wszystko, a zarazem nic, jeśli nie wiadomo jak je zinterpretować. Większość plików wyposaża się z tego powodu w różnorodne nagłówki, w których pamiętane są informacje o tym co plik zawiera, ew. jak go potem traktować przy odczycie. Do takich należą również pliki wykonywalne, binarne, czy po prostu: przeznaczone do załadowania z poziomu DOS'u. Wszak DOS to też program i jak każdy inny ma prawo oczekiwać danych o określonej, znanej mu strukturze.

    Tradycyjne pliki binarne, rozpoznawane przez wszystkie DOS'y dla komputerów Atari XL/XE, mają budowę blokową, gdzie każdy blok posiada swój nagłówek. Istnieją dwa rodzaje nagłówków:

     1. dta a($ffff),a(str_adr),a(end_adr)
    
     2. dta a(str_adr),a(end_adr)
    

    str_adr - adres, pod który zostanie załadowany pierwszy bajt danych

    end_adr - adres, pod który zostanie załadowany ostatni bajt

    Pierwszy blok w pliku musi mieć nagłówek typu $ffff, pozostałe bloki dowolnie. Za nagłówkiem oczywiście powinny znaleźć się dane w ilości:

       (end_adr-str_adr)+1
    

    Tyle tytułem przypomnienia. Twórcy systemu Sparta DOS X zachowali powyższy standard, dodając jednocześnie kilka nowych typów nagłówków. Ciągle więc mamy do czynienia z plikiem podzielonym na bloki, z tym że rodzajów bloków jest teraz dużo więcej. Oto one:


    1. Blok nierelokowalny (ładowany pod stały adres w pamięci):

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

    Jest to to samo co blok $ffff - nie ma znaczenia, który zostanie użyty. $fffa będzie jednak wyraźnie wskazywać, że program jest przeznaczony dla SDX - inny DOS takiego pliku nie odczyta.


    2. Blok relokowalny (ładowany pod MEMLO we wskazany rodzaj pamięci):

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

    blk_num - numer bloku w pliku. Każdy blok relokowalny powinien posiadać swój własny numer. Ponieważ adresy ładowania bloków nie są znane, bloki identyfikowane są właśnie poprzez swoje numery. Mogą one przyjmować wartości z zakresu 0-7, z tym że w praktyce stosuje się zwykle numerację od 1 w górę.

    blk_id - bity 1-5 stanowią indeks pamięci, do której blok ma zostać załadowany. Spotkałem się z dwoma wartościami:

     $00 - pamięć podstawowa
     $02 - pamięć rozszerzona
    
    Ustawienie dodatkowo bitu 7 oznacza brak bloku danych. SDX nic wtedy nie ładuje, ale rezerwuje pamięć.

    blk_off - tzw. przesunięcie adresów w bloku, czyli po prostu adres, pod który był assemblowany kod. Jest to potrzebne przy uaktualnianiu adresów odwołujących się do zawartości bloku.

    blk_len - długość bloku. Tyle danych powinno być za nagłówkiem chyba, że jest to blok rezerwujący pamięć wtedy danych nie ma.

    Pisząc kod relokowalny trzeba mieć na uwadze kilka ograniczeń jakie narzuca idea "przemiaszczalnego" kodu. Wszystkie adresy odwołujące się do obszaru takiego programu muszą zostać uaktualnione podczas ładowania, w związku z tym nie można używać sekwencji takich jak np.:
           lda coś
           ...
          coś equ *
           ...
        Zamiast tego, pozostaje np.:
           lda _coś
           ldx _coś+1
           ...
          _coś dta a(coś)
           ...
          coś equ *
    

    3. Blok aktualizacji adresów odnoszących się do bloku relokowalnego:

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

    blk_num - numer bloku, do którego odnoszą się uaktualniane adresy.

    blk_len - długość bloku aktualizacji (bez nagłówka). Jest ona ignorowana.

    Adresy są uaktualniane poprzez dodanie do adresu istniejącego różnicy pomiędzy adresem, pod który został załadowany wskazany blok relokowalny, a wartością blk_off (adresem asemblacji) tego bloku. Można to przedstawić wzorem:

           ADR=ADR+(blk_adr-blk_off)
    

    "Ciało" bloku aktualizacji stanowią wskaźniki do poprawianych adresów oraz rozkazy specjalne. Wskaźnik jest liczbą z zakresu $00-$fb i oznacza przesunięcie względem miejsca poprzedniej aktualizacji. Miejsce to jest pamiętane przez program ładujący jako bezpośredni adres, nazwijmy go licznikiem aktualizacji. Licznik ten można zainicjować za pomocą funkcji specjalnych, którymi są liczby większe od $fb:

    $fc oznacza koniec bloku aktualizacji,

    $fd,a(ADDR) następuje aktualizacja adresu wskazanego bezpośrednio przez ADDR. Tym samym wartość ADDR jest wpisywana do licznika aktualizacji i od niej będą liczone kolejne przesunięcia,

    $fe,b(blk_num) do licznika aktualizacji wstawiany jest adres bloku wskazanego przez blk_num, czyli kolejne aktualizacje będą się odnosiły do kodu zawartego w tym bloku,

    $ff licznik aktualizacji zwiększany jest o $fa (bez aktualizacji adresu).


    4. Blok aktualizacji adresów procedur zdefiniowanych symbolami:

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

    SMB_NAME - symboliczna nazwa procedury (lub tablicy, rejestru systemowego itp.) Osiem znaków w kodzie ATASCII,

    blk_len - jak w bloku $fffd.

    Po nagłówku występuje ciąg wskaźników określających położenie adresów do zaktualizowania - identycznie jak w bloku $fffd. Adresy są zmieniane poprzez dodanie do istniejącego adresu, adresu procedury określonej symbolem. Pozwala to na wykorzystywanie w programach procedur, których adresów nie znamy, np. procedur dodawanych przez inne programy uruchamiane w środowisku SDX. Także procedury systemowe powinny być wykorzystywane w ten sposób, przecież mogą one mieć różne adresy w różnych wersjach Sparty.


    5. Blok definicji nowych symboli:

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

    blk_num - numer bloku, w którym znajduje się definiowana procedura. Wynika z tego, że procedura musi być załadowana jako blok relokowalny.

    smb_off - przesunięcie adresu procedury w bloku, czyli offset procedury względem początku bloku (pierwszy bajt ma numer 0) powiększony o wartość blk_off tego bloku. Inaczej jest to adres pod jaki procedura została zassemblowana, SMB_NAME - symboliczna nazwa definiowanej procedury.

    Bloki typu $fffb, $fffc, $fffd nie są na stałe zatrzymywane w pamięci. System wykorzystuje je tylko podczas ładowania programu.


    Różnice i podobieństwa pomiędzy XASM i MADS

  • te same kody wyjścia
  • ta sama składnia, oprócz łączenia dwóch rozkazów w linii poprzez znak ':' (znak ':' w takim przypadku jest zastępowany znakiem '?')
  • 99% makro rozkazów XASM'a udało zastąpić się makrami MADS'a (plik ..\EXAMPLES\MACROS\XASM.ASM)
  • XASM nie lubi "białych spacji", MADS toleruje je i akceptuje dla wyrażeń logicznych, deklaracji stałych i zmiennych
  • MADS udostępnia deklaracje stałych i zmiennych lokalne i globalne, XASM tylko globalne
  • mała różnica w ORG, np. 'ORG [[expression]]adres[,adres2]'
  • MADS nie akceptuje ORG a:adres i ORG f:adres
  • MADS nie udostępnia zapisu liczb rzeczywistych DTA R(real)
  • pseudo rozkaz .IF (mads) można zamiennie używać z pseudo rozkazem IFT (xasm)
  • pseudo rozkaz .ELSE (mads) można zamiennie używać z pseudo rozkazem ELS (xasm)
  • pseudo rozkaz .ELSEIF (mads) można zamiennie używać z pseudo rozkazem ELI (xasm)
  • pseudo rozkaz .ENDIF (mads) można zamiennie używać z pseudo rozkazem EIF (xasm)
  • MADS nie akceptuje składni typu 'lda (203),0'
  • MADS umożliwia pisanie programów dla Sparta DOS X
  • jeśli użyjemy podczas adresowania wartości znakowej, np.
      lda #' '
      lda #" "
    

    MADS będzie rozróżniał apostrof pojedyńczy (kod ATASCII) i apostrof podwójny (kod INTERNAL), XASM oba rodzaje apostrofów potraktuje jednakowo (kod ATASCII). Oczywiście dla danych DTA apostrofy nie są rozróżniane przez MADS.

  • w trybach indeksowych znak '+' lub '-' zwiększa lub zmniejsza rejestr, np.
     lda $2000,x+
     
     to odpowiednik
     
     lda $2000,x
     inx
    
  • jeśli jednak umieścimy wartość za znakiem '+' lub '-' wówczas zmienimy o tą wartość główny argument (działa tylko w trybie absolutnym indeksowym), np.:
     lda $2000,x+2
     
     to odpowiednik
     
     lda $2002,x
    

    Historia

    v1.5.9
    v1.5.9
    - nowe makra w katalogu ..\examples\macros\
    - możliwe jest użycie "białych spacji" dla danych DTA, pseudo rozkazów,
      argumentu rokazów CPU, np.:
    
     .byte 1  , 3,  4           ; komentarz
     dta a(12 ) ,  111  , 9                 // komentarz
     dta b( sin( 32 , 19 , 128 , 0 , 31 ) )  ; komentarz
     dta a( [ 12 + 3] , 11 )                  ; komentarz
     org [ 1 , 2  ] ,  $2000 + 1          // komentarz
     lda ( 203 ) , y                ; komentarz
    
    - możliwy jest brak spacji rozdzielających mnemonik i argument, np.:
    
     lda#12
     lda($80),y
     ldx'a'
    
    - dodana nowa dyrektywa .DS (zapożyczona z MAC'65) służąca do rezerwacji
      pamięci bez jej inicjalizacji, np.:
    
    tmp .ds 256   =   tmp org *+256
    
    - dodany pseudo rozkaz RND(MIN,MAX,LENGTH) generujący LENGTH liczb losowych
      z przedziału MIN..MAX (przykład zastosowania SHELL_SORT.ASM)
    - naprawiony błąd dla 'org [header],address'
    
    v1.5.8
    - zmiana sposobu asemblacji makr, bez udziału zewnętrznych plików 
    - dokładniejsze wskazanie numeru linii w której wystąpił błąd podczas
      przetwarzania makra lub dyrektywy .REPT
    - rozszerzenie działania dyrektywy .PRINT, np.:
    
     .print 'end: ',*
     .print $10,',',$20,',',$10203040
    
    - możliwość zwiększania wartości bajtów w dołączanym pliku lub ciągu
      znakowym, np.:
    
     ins 'plik'+12,6,100
     ins 'plik'+128
     dta 'text'-32
    
    - w deklaracji etykiet można używać znaku '=' bez poprzedzających spacji,
      np.:
    
    DOSINI=$0C
    IN=$CC
    
    - poprawiony błąd związany z zagnieżdżonymi makrami i dyrektywami
      warunkowymi .IF, np.:
    
     test 1         ; w poprzednich wersjach MADS'a to makro nic nie
                    ; wygeneruje, w wersji 1.5.8 dziala poprawnie
    .macro test
     .if :1=0
       mwx tmp tmpZ
     .else
       mwx tmpZ tmp
     .endif  
    .endm
    	
    v1.5.7
    - usunięty błąd związany ze znakiem '/' powodujący zapętlenie MADS'a
    
    v1.5.6
    - nowe typy danych E(24bit), F(32bit), G(32bit wspak)
    - dodatkowa składnia dla obsługi plików SPARTA DOS X (zgodna z Fast Assembler MMMG)
    
    v1.5.5
    - poprawione zauważone błędy
    - dodana możliwość inwersu zawartości pliku dla pseudo rozkazu INS, np.:
    
     ins 'plik.dat'*
     ins 'plik.dat'*,256,512
    
    - możliwość deklaracji danych za pomocą dyrektyw .BYTE, .WORD, .LONG, .DWORD np.
    
     .word $1000,b(4,5),258
     .byte 6,12,a(3),44
    
    v1.5.4
    - poprawione rozpoznawanie inwersu znaku w adresowaniu, np.:
    
      lda "A"*8   ; kod INTERNAL znaku A pomnożony przez 8
      lda "A"**2  ; inwers znaku A w kodzie INTERNAL pomnożony przez 2
    
    - możliwość umieszczania pseudo rozkazów w makrach, np.:
    
    .macro load_level
    ?licz = 0
    
     .rept 160
    
      ins :1,?licz,3
    ?licz = ?licz + 32
    
     .endr
    
    .endm
    
     load_level 'level_00-09.mic'
     load_level 'level_10-19.mic'
    
    v1.5.2-1.5.3
    - dodana obsługa pseudo rozkazu SIN(centre,amp,size[,first,last]) znanego z XASM
    - dodana informacja o miejscu wystąpienia błędu podczas wykonywania makra
      (pierwsza linia informuje na czym zatrzymało się przetwarzania makra,
       dwie ostatnie linie komunikatu informują o miejscu wywołania makra), np.:
    
     mv? #3 $D01D a 8
     mva #3 $d01d           PMCTL - show sprites and missiles
    D:\!Atari\Getris\h_logo.asm (17) ERROR: Unexpected eol
    
    v1.5.1
    - dodane wyświetlanie ostrzeżenia w przypadku wystąpienia deklaracji
      zmiennej bez poprzedzającego pseudo rozkazu ORG
    - dwie nowe dyrektywy-operatory logiczne .AND, .OR
    - możliwość stosowania tzw. "białych spacji" dla wyrażeń logicznych,
      deklaracji stałych, zmiennych, np.:
    
    label equ 1 << 5   ; komentarz
    
    label equ [MaxX-MinX] & $FFFF   // komentarz
     
    .if :0<>'a' .and :0<>'A'    ; komentarz
    
     ert * >= $a000   ; komentarz
    
    v1.5.0
    - nowy przełącznik /s (Silent mode)
    - wyeliminowany problem z asemblacją makr bez poprzedzającego pseudo
      rozkazu ORG, poprzednio występował błąd 'No ORG specified'
    - możliwość przekazania do struktur .MACRO i .PROC parametrów typu string
     (ciąg znaków ograniczony podwójnym apostrofem "string")
    - dodana możliwość użycia znaków '%%' do określenia numeru parametru makra,
      poprzednio służył temu tylko znak dwukropka ':'
    - możliwość używania tzw. "białych spacji" podczas przekazywania parametrów
      do struktur .MACRO i .PROC, np.:
    
     test 10  ,   200
    
    .macro test
     lda :1
     sta :2
    .endm
    
    v1.4.9
    - usunięty błąd, który powodował "zwis" MADS'a gdy wystąpiła dyrektywa
      .ENDL bez poprzedzającej dyrektywy .LOCAL
    - poprawione drobniejsze błędy
    
    v1.4.7-1.4.8beta
    - usprawnione poszukiwanie wartości etykiety w strukturze .LOCAL
    - naprawiony błąd, który uniemożliwiał przekazanie parametru do makra z rozkazem
      w trybie indeksowym, np.:
    
    .macro test
     lda $2000,:1
    .endm
    
    .macro test2
     lda (:1),:2
    .endm
    
    v1.4.6
    - naprawiony błąd, który uniemożliwiał określenie wartości etykiety lokalnej zaczynającej się znakiem
      zapytania '?' zadeklarowanej w strukturze lokalnej .LOCAL, .PROC, jak w n/w przykładzie:
    
    ?test = 1
    
    .local tmp
    
    ?test = 2
    ?test = 4
    
    .endl
    
    v1.4.5
    - naprawiony błąd, który uniemożliwiał określenie wartości etykiety lokalnej, do której odwoływało
      sie makro, wywołane z poziomu innego makra, jak w n/w przykładzie:
    
    	icl 'xasm.asm'
    
    x	.LOCAL
    	mwa #x2 x3
    
    x2
    	lda #0
    	rts
    x3	dta 0,0
    	.ENDL
    
    v1.4.4
    - pseudo rozkaz LMB wymaga określenia trybu adresowania, konkretnie '#'
    - poprawione działanie rozkazu BRA dla długich skoków (65816)
    - nazwy struktur dla .MACRO, .PROC, .LOCAL nie muszą już koniecznie występować przed
      dyrektywą, mogą występować też po dyrektywie, np.:
    
    test .macro
    .endm
    
    .macro test2
    .endm
    
    .proc test3
    .endp
    
        .local jump
        .endl
    
    v1.4.3
    - nowe dyrektywy .REPT, .ENDR umożliwiające powtórzenie bloku programu
    
    v1.4.2
    - zmiany w zarządzaniu makrami, inny sposób reprezentacji zadeklarowanych
      etykiet w makrze i w pliku *.LAB, poprzednio wywołanie dwóch makr z tymi
      samymi etykietami kończyło się błędem 'Label declared twice', teraz jest
      OK
    - poprawione zarządzanie struktura .MACRO, .PROC zdefiniowaną w obszarze
      .LOCAL
    - zablokowanie wykonywania się dyrektyw .EXIT, .PRINT, .ERROR w deklaracji
      makra, wykonywane są teraz tylko gdy makro zostanie wywołane
    
    v1.4.1
    - mała poprawka dotycząca komunikatów sygnalizujących brak wystąpienia
     .ENDP, .ENDL, .ENDM w przypadku gdy były dołączane pliki przez ICL
    - poprawka zapobiegająca kasowaniu pliku *.LAB, w przypadku gdy nie był
      generowany listing *.LST
    
    v1.4.0
    - jesli .PROC nie ma zadeklarowanych parametrów, MADS nie podejmie próby
      ich pobrania
    - dla tak błędnie zapisanego rozkazu 'lda (),y' poprzednia wersja
      assemblera "wykładała" się
    - dla rozkazu określającego liczbę powtórzeń linii (:N), można podać
      wartość 0, przedtem tylko >0
    - poprawione testowanie wyrażen warunkowych, w szczegolności gdy .ELSE
      wystąpiło po .ELSEIF
    - dodany zapis etykiet procedur .PROC do plików *.LST, *.H
    - naprawione działanie dyrektywy .ERROR
    - poprawione wywoływanie makr z poziomu makra, w poprzedniej wersji były
      problemy gdy po wywołaniu makra występowała etykieta, wówczas MADS nie
      potrafił odnaleźć wartości takiej etykiety
    - dodane pokazywanie wartości zdefiniowanej etykiety w pliku *.LST
    
    v1.3.9
    - rozszerzone działanie pseudo-rozkazu INS, np.: INS 'filename',-N spowoduje
      odczyt N ostatnich bajtów z pliku  
    - rozszerzone działanie (możliwość zdefiniowania własnego nagłówka bloku w pliku)
      pseudo-rozkazu ORG [[expression]]adres[,adres2] 
    - dyrektywa .ENDP nie wygeneruje już rozkazu RTS dla procedury, która nie miała zadeklarowanych
      parametrów i nie wystąpiła w jej ciele dyrektywa .EXIT
    	
    v1.3.8
    - w przypadku braku zadeklarowanych parametrów dla .PROC nie są wywoływane
      makra @CALL, @ExitProc tylko asemblowane odpowiednie rozkazy JSR, RTS
    - poprawiony n/w błąd
    
    ;TEST1.A65
    ;
    	opt h-			;first part OBJ file contains headers already.
    	ins "part1.obj"
    	opt h+
    ;
    ;next parts follows here
    ;
    	org $5000
    	lda #$12
    lop	jmp lop
    ;...
    
    >mads.exe test1.a65 /o:test1.obx
    Mad-Assembler v1.3.7beta by TeBe/Madteam
            ins "part1.obj"
    test1.a65 (8) ERROR: No ORG specified
    
    v1.3.7
    - możliwość użycia komentarza jednoliniowego oznaczonego znakami //
    - nowy przełącznik /i:path (Additional include directories)
    
    v1.3.6
    - poprawne wyszczególnianie niezadeklarowanej etykiety przy błędzie
      'Undeclared label'
    - wyszczególnianie niezadeklarowanego makra przy błędzie 'Undeclared macro'
    - jeśli .PROC nie ma parametrów, nie jest już sprawdzana obecność
      deklaracji dla @STACK_ADDRESS
    - usprawnione poszukiwanie etykiet w obrębie .LOCAL
    - dla definicji można używać już rozkazu 'EQU' (poprzednio tylko ' = ')
    - w strukturze .PROC nie jest już wymagane użycie dyrektywy .EXIT,
      w przypadku jej braku zastępuje ją dyrektywa .ENDP
    - możliwość użycia komentarza wieloliniowego ograniczonego znakami /*  */
    - usunięty błąd polegający na nieprawidłowej modyfikacji adresów struktury
      .PROC (gdy .PROC występowało w strukturze .LOCAL)
    - dodana możliwość używania dyrektyw od pierwszego znaku wiersza
    - możliwość umieszczania struktur .PROC oraz .MACRO w .LOCAL i ich
      wywoływanie
    - umożliwione odczytywanie plików z ustawionym atrybutem 'Read only' oraz
      'Hidden' (FileMode=0)
    
    v1.3.4
    - dodana możliwość zagnieżdżania struktur .LOCAL
    
    v1.3.1
    - dodana obsługa pseudo rozkazów .LOCAL i .ENDL
    - dodana obsługa przełącznika /p znanego z XASM
    - usunięty błąd z repetycją linii, błąd występował gdy linia w której jest repetycja
      była ostatnią linią przetwarzanego pliku
    
    v1.2.9
    - poprawione błędy zauwazone przez Rastera (przyklad ..\RMT_PLAYER\MUSIC.A65 asembluje teraz poprawnie)
    - obsługa składni XASM'a typu 'lda:sta' za pomoca makr (przykłady w ..\EXAMPLES\MACROS\XASM.ASM)
    - dodana obsługa pseudo rozkazu .ELSEIF oraz jego odpowiednika ELI
    - dodana obsługa pseudo rozkazu ERT (ERT expression)
    - dodany przełącznik '/m:filename' który pozwoli wczytać plik z definicjami makr
    
    v1.2.8
    - w wersji 1.2.7beta byla blednie asemblowana linia w stylu dta b(value1),b(value2),...
    - naprawione problemy z dzialaniem przelacznikow /l: /o: /t:
    - dodany przelacznik /h ktory tworzy plik *.h dla cc65 (wersja testowa)
    - rozbudowana deklaracja makr o separator i tryb działania (przykład ..\EXAMPLES\MACROS\XASM.ASM)
    - zmiana w deklaracji parametrow procedury, zamiast znakow '( )' sa teraz '{ }'
    - dodane kody wyjscia, takie same jak w XASM
    - pseudo rozkaz .IF ma teraz odpowiednik w pseudo rozkazie IFT
    - pseudo rozkaz .ELSE ma teraz odpowiednik w pseudo rozkazie ELS
    - pseudo rozkaz .ENDIF ma teraz odpowiednik w pseudo rozkazie EIF
    - dodana obsluga skladni w stylu LDA ADRES,Y[+-] (LDA (BYTE),Y[+-]) oraz LDA ADRES,Y[+-]expression
    - dodane przyklady w wersji dla MADS ProTracker'a i Inertii