MAD-ASSEMBLER 1.7.5

Tebe/Madteam (31.10.2006)


SPIS TREŚCI


WPROWADZENIE

Wstęp

Mad-Assembler (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 ;).

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 (SDX). Umożliwione zostało użycie dodatkowych znaków w nazwach etykiet. Poza tym dodana została obsługa CPU 65816, makr, procedur, podziału pamięci na wirtualne banki, wielowymiarowych nazw etykiet.

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 INT64 (signed 64 bit), wynik reprezentowany jest na wartościach typu CARDINAL (unsigned 32 bit).

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

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

  • Strona domowa XASM-a: http://atariarea.krap.pl/x-asm/

    Dzięki darmowemu kompilatorowi Free Pascal Compiler możliwa jest kompilacja MADS-a dla innych platform systemowych, np. Linux, Mac, OS/2 itp. Więcej informacji na temat kompilacji w rodziale Kompilacja.

    Jeśli dla Twojego systemu istnieje odpowiednia wersja Free Pascala, tzn. że możesz używać MADS-a.


    Kompilacja

    Aby skompilować źródło MADS-a, można użyć kompilatora z Delphi, jeśli ktoś ma akurat zainstalowane środowisko Delphi 7.0 lub Delphi 2005.

    Innym sposobem, bardziej multi platformowym jest użycie kompilatora z pakietu Free Pascal Compiler (FPC), który można pobrać ze strony http://www.freepascal.org/. Najnowszą wersję dla Windows 32-bit można pobrać stąd: fpc-2.0.0.i386-win32.exe.

    Uruchamiamy instalator, wybieramy katalog w którym zostanie zainstalowany FP. Ważne jest aby nie używać w nazwie katalogu znaku wykrzyknika '!' czy innych nie standardowych znaków. Jeśli nie uda nam się skompilować żadnego pliku, najpewniej winna jest nie standardowa nazwa ścieżki. Linia komend uruchamiająca kompilacje może wyglądać następująco (wielkość liter w nazwach parametrów ma znaczenie):
    fpc -Mdelphi -v mads.pas
    
    
  • -Mdelphi pozwala kompilować plik w formacie Delphi
  • -v wyświetla wszystkie komunikaty błędów i ostrzeżeń
  • W porównaniu z kompilatorem Delphi, kod wygenerowany przez FPC jest dłuższy, za to prędkość działania skompilowanego nim MADS-a znacznie szybsza, nawet o kilka sekund. Załączony plik MADS.EXE jest kompilowany przy użyciu FPC.


    RÓŻNICE I PODOBIEŃSTWA POMIĘDZY XASM I MADS

    Podobieństwa

  • ta sama składnia
  • te same kody wyjścia
  • 99.9% makro rozkazów XASM-a udało zastąpić się makrami MADS-a (plik ..\EXAMPLES\MACROS\XASM.MAC)


    Różnice i nowe możliwości

  • mała różnica w ORG, np. 'ORG [[expression]]adres[,adres2]'
  • XASM nie lubi "białych spacji", MADS toleruje je i akceptuje dla wyrażeń logicznych, arytmetycznych, definicji stałych i zmiennych
  • MADS pozwala na umieszczanie wyrażeń pomiędzy nawiasami (), [], XASM tylko pomiędzy []
  • MADS udostępnia definicje stałych i zmiennych lokalne, globalne, tymczasowe, XASM tylko globalne
  • MADS nie akceptuje ORG a:adres i ORG f:adres
  • MADS nie udostępnia zapisu liczb rzeczywistych DTA R(real)
  • MADS oferuje bardziej rozbudowaną obsługę pseudo rozkazu INS
  • MADS nie akceptuje składni typu 'lda (203),0'
  • MADS umożliwia pisanie programów dla Sparta DOS X
  • MADS umożliwia generowanie kodu relokowalnego w swoim własnym formacie
  • 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+    ->    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   ->    lda $2002,x
    

    SPOSÓB UŻYCIA

    Opcje assemblera

    Syntax: MADS source [options]
    
    -c              Label case sensitivity
    -d:label=value  Define a label
    -e              Eat white spaces
    -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
    -s              Silent mode
    -t[:filename]   List label table
    -x              Exclude unreferenced procedures
    

    Domyślne nazwy plików to:

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

    Jeśli nie podamy rozszerzenia dla pliku source, wówczas MADS domyślnie przyjmie rozszerzenie .ASM.

    Parametry możemy podawać w dowolnej kolejności uprzednio poprzedzając je znakiem '/' lub '-', wielkość liter nie ma znaczenia. Parametry możemy łączyć ze sobą, wyjątkiem są te parametry, które wymagają podania nowej nazwy pliku (po znaku dwukropka :), je musimy umieszczać na końcu ciągu znakowego np.:
    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
    

    /c

    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.

    /d:label=value

    Użycie przełącznika /d pozwala na wprowadzenie nowej etykiety do pamięci MADS-a z poziomu linii poleceń. Przełącznika można użyć wielokrotnie podczas jednego wywołania MADS-a, może być przydatny gdy asemblujemy z użyciem plików wsadowych (BAT).

    /e

    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. Domyślnie MADS ignoruje białe spacje, ten przełącznik wymusi ignorowanie ich w wyrażeniach, argumentach rozkazów, np.:
     lda $100,y+           ; zadziała bez przełącznika /e
     lda $100 , y +        ; nie zadziała bez przełącznika /e, należy go tutaj użyć
    

    /hc[:filename]

    Przełącznik /hc włącza zapis pliku z nagłówkami dla kompilatora CC65. Pozwala także określić nową nazwę dla takiego pliku. Domyślna nazwa pliku nagłówkowego dla CC65 to *.H

    /hm[:filename]

    Przełącznik /hm włącza zapis pliku z nagłówkami dla MADS-a. Pozwala także określić nową nazwę dla takiego pliku. Domyślna nazwa pliku nagłówkowego dla MADS-a to *.HEA. Plik taki zawiera informacje o bankach przypisanych etykietom i ich wartości. Dodatkowo etykiety posortowane zostają wg typu CONSTANS, VARIABLES, PROCEDURES.

    /i:path

    Przełącznik /i 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"
    

    /l[:filename]

    Przełącznik /l włącza zapis pliku z listingiem. Pozwala także określić nową nazwę dla takiego pliku.

    /m:filename

    Przełącznik /m służy do określenia pliku z deklaracjami makr. W przypadku jego użycia MADS asembluje taki plik przed głównym plikiem .ASM

    /o:filename

    Przełącznik /o pozwala określić nową nazwę pliku wykonywalnego Atari DOS lub Atari Sparta DOS X, który powstanie po procesie asemblacji.

    /p

    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 .PROC
    

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

    /s

    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).

    /t[:filename]

    Przełącznik /t włącza zapis pliku z użytymi definicjami etykiet. Pozwala także określić nową nazwę dla takiego pliku.

    /x

    Przełącznik /x pozwala na pominięcie w procesie asemblacji procedur zadeklarowanych dyrektywą .PROC, do których nie nastąpiło odwołanie w programie.


    Kody wyjścia

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

    Struktura pliku LST

    Format listingu nie odbiega od tego znanego z XASM, jedyną zmianą jest dodanie przed adresem, numeru wirtualnego banku pamięci. Więcej o wirtualnych bankach w rozdziale Banki pamięci.

    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
    

    Struktura 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 wirtualnego banku przypisany do etykiety.
  • Druga kolumna to wartość etykiety.
  • Trzecia kolumna to nazwa etykiety.

    Numery wirtualnych banków przypisane do etykiety o wartościach >= $FFF9 mają specjalne znaczenie:
     $FFF9   etykieta parametru procedury zdefiniowanej przez dyrektywę .PROC
     $FFFA   etykieta tablicy zdefiniowanej przez dyrektywę .ARRAY
     $FFFB   etykieta danych strukturalnych zdefiniowanej przez pseudo rozkaz DTA STRUCT_LABEL
     $FFFC   etykieta symbolu Sparta DOS X - SMB
     $FFFD   etykieta makra zdefiniowanego przez dyrektywę .MACRO
     $FFFE   etykieta struktury zdefiniowanej przez dyrektywę .STRUCT
     $FFFF   etykieta procedury zdefiniowanej przez dyrektywę .PROC
    

    Specjalne znaczenie w nazwach etykiet mają znaki:

  • etykieta zdefiniowana w makrze (dwa dwukropki) ::
  • znak kropki '.' rozdziela nazwę struktury (.MACRO, .PROC, .LOCAL, .STRUCT) 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
    

    Struktura 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
    

    PSEUDO ROZKAZY

     REQ, RNE, RPL, RMI, RCC, RCS, RVC, RVS
     SEQ, SNE, SPL, SMI, SCC, SCS, SVC, SVS
    
     JEQ, JNE, JPL, JMI, JCC, JCS, JVC, JVS
    
     IFT [.IF] expression
     ELS [.ELSE]
     ELI [.ELSEIF] expression
     EIF [.ENDIF]
    
     label EQU address
     label  =  address
    
     label EXT address
    
     OPT [bchlmost][+-]
     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 [.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 #value
    

    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).


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

    W/w pseudo rozkazy swoimi nazwami nawiązują do odpowiednich mnemoników 6502, odpowiednio BEQ, BNE, BPL, BMI, BCC, BCS, BVC, BVS. Posiadają dodatkową właściwość jaką jest skok do poprzednio asemblowanej instrukcji, np.:

       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

    W/w pseudo rozkazy swoimi nazwami nawiązują do odpowiednich mnemoników 6502, odpowiednio BEQ, BNE, BPL, BMI, BCC, BCS, BVC, BVS. Posiadają dodatkową właściwość jaką jest skok do następnej asemblowanej instrukcji, np.:

       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

    W/w pseudo rozkazy swoimi nazwami nawiązują do odpowiednich mnemoników 6502, odpowiednio BEQ, BNE, BPL, BMI, BCC, BCS, BVC, BVS. Posiadają dodatkową właściwość jaką jest skok warunkowy pod wskazany adres, z ich pomocą możemy skakać nie tylko w zakresie -128..+127 bajtów ale w całym zakresie 64kB np.:

     jne dest   ->  beq *+4
                ->  jmp dest
    
    Jeśli skok jest krótki (zakres -128..+127) wówczas MADS użyje krótkiego skoku, odpowiednio BEQ, BNE, BPL, BMI, BCC, BCS, BVC, BVS.


    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] E[xternal]         - blok aktualizujący adresy etykiet external (nagłówek $FFEE)
                                       UWAGA: nie dotyczy Sparta DOS X, jest to rozszerzenie MADS-a
    
     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
    
    Więcej informacji na temat bloków w plikach Sparta DOS X w rozdziale
    Budowa plików SPARTA DOS X oraz Programowanie SPARTA DOS X.


    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 instrukcji JSR system 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

    Example: 
               :4 asl @
               :2 dta a(*)
               :256 dta #/8
    
    Znak ':' określa liczbę powtórzeń linii (w przypadku makr określa numer parametru pod warunkiem że wartość liczbowa zapisana została w systemie decymalnym). Liczba powtórzeń powinna być z zakresu <0..2147483647>. W powtarzanej linii ':repeat' możliwe jest skorzystanie z licznika pętli - znaku hash '#'.

    Jeśli użyjemy znaku ':' w makrze w znaczeniu liczby powtórzeń linii, np.:
    .macro test             .macro test
     :2 lsr @                :+2 lsr @
    .endm                   .endm
    
    Wówczas znak ':' zostanie zinterpretowany jako drugi parametr makra. Aby zapobiec takiej interpretacji przez MADS należy po znaku dwukropka ':' umieścić znak który nic nie robi, np. znak plusa '+'.

    Teraz znak dwukropka ':' zostanie prawidłowo zinterpretowany jako :repeat.


    OPT

    Pseudo rozkaz OPT pozwala włączać/wyłączać dodatkowe opcje podczas asemblacji.
     b+  bank sensitive on
     b-  bank sensitive off                                               (default)
     c+  włącza obsługę CPU 65816 (16bit)
     c-  włącza obsługę CPU 6502 (8bit)                                   (default)
     h+  zapisuje nagłówek pliku dla DOS                                  (default)
     h-  nie zapisuje nagłówka pliku dla DOS
     l+  zapisuje listing do pliku (LST)
     l-  nie zapisuje listingu (LST)                                      (default)
     m+  zapisuje całe makra w listingu
     m-  zapisuje w listingu tylko tą część makra która zostaje wykonana  (default)
     o+  zapisuje wynik asemblacji do pliku wynikowego (OBX)              (default)
     o-  nie zapisuje wyniku asemblacji do pliku wynikowego (OBX)
     s+  drukuje listing na ekranie
     s-  nie drukuje listingu na ekranie                                  (default)
     t+  track SEP REP on (CPU 65816)
     t-  track SEP REP off (CPU 65816)                                    (default)
     ?+  etykiety ze znakiem '?' na początku są lokalne (styl MAE)
     ?-  etykiety ze znakiem '?' na początku są tymczasowe                (default)
    
    Example:
     
     OPT c+ c  - l  + s +
     OPT h-
     OPT o +
    

    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

    Pseudo rozkaz ORG ustawia nowy adres asemblacji, a więc i lokalizację zasemblowanych danych w pamięci RAM.
     adr                 asembluj od adresu ADR, ustaw adres w nagłówku pliku na ADR
     adr,adr2            asembluj od adresu ADR, ustaw adres w nagłówku pliku 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 pliku na ADR
     [$d0,$fe],adr,adr2  zmień nagłówek na $D0FE, asembluj od adresu ADR, ustaw adres w nagłówku pliku na ADR2
     [a($FFFA)],adr      nagłówek SpartaDOS $FAFF, ustaw adres w nagłówku pliku na ADR
    
    Example:
    
     ORG $80,$2000
     ORG [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 za zamykającym nawiasem kwadratowym ']', rozdzielone znakiem przecinka ',' oznaczają odpowiednio: adres asemblacji, adres w nagłówku pliku.


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

    Pseudo rozkaz INS pozwala na dołączenie dodatkowego pliku binarnego. Dołączany plik nie musi znajdować się w tym samym katalogu co główny asemblowany plik. Wystarczy, że odpowiednio wskazaliśmy MADS-owi ścieżki poszukiwań za pomocą przełącznika /i (patrz Opcje assemblera).

    Dodatkowo można przeprowadzić na dołączanym pliku binarnym operacje:
    *          invers bajtów pliku binarnego
    +-VALUE    zwiększenie/zmniejszenie wartości bajtów pliku binarnego o wartość wyrażenia VALUE
    
    +OFSET     ominięcie OFSET bajtów z początku pliku binarnego (SEEK OFSET)
    -OFSET     odczyt pliku binarnego od jego końca              (SEEK FileLength-OFSET)
    
    LENGTH     odczyt LENGTH bajtów pliku binarnego
    
    Jeśli wartość LENGTH nie została określona, domyślnie plik binarny zostanie odczytany aż do końca.


    ICL 'filename'["filename"]

    Pseudo rozkaz ICL pozwala na dołączenie dodatkowego pliku źródłowego i jego asemblację. Dołączany plik nie musi znajdować się w tym samym katalogu co główny asemblowany plik. Wystarczy, że odpowiednio wskazaliśmy MADS-owi ścieżki poszukiwań za pomocą przełącznika /i (patrz Opcje assemblera).


    DTA

    Pseudo rozkaz DTA służy do definicji danych określonego typu. Jeśli typ nie został określony wówczas domyślnie zostanie ustawiony typ BYTE (b).
       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'*
    
    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(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>.
    Example: dta b(rnd(0,33,256))
    

    IFT, ELS, ELI, EIF

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

    W/w pseudo rozkazy i dyrektywy wpływają na przebieg asemblacji (można ich używać zamiennie).


    DYREKTYWY

     .IF [IFT] expression
     .ELSE [ELS]
     .ELSEIF [ELI] expression
     .ENDIF [EIF]
    
     label .LOCAL
     .ENDL
    
     .REPT expression
     .ENDR
     .R
    
     .PAGES [expression]
     .ENDPG 
    
     label .STRUCT
     .ENDS
     
     label .ARRAY count type [= default_value]
     .ENDA
    
     label .PROC
     .ENDP
     .REG, .VAR
    
     label .MACRO
     .ENDM
     :[%%]parameter
     .EXIT
    
     .VAR var1[=value],var2[=value]... (.BYTE|.WORD|.LONG|.DWORD)
    
     .END
    
     .PRINT 'string1','string2'...,value1,value2,...
     .ERROR [ERT] 'string'["string"] lub .ERROR [ERT] expression
     
     .BYTE
     .WORD
     .LONG
     .DWORD
    
     .OR
     .AND
     .XOR
     .NOT
    
     .DB
     .DW
     .DS expression
    
     .BY [+byte] bytes and/or ASCII
     .WO words
     .HE hex bytes
     .SB [+byte] bytes and/or ASCII
     .FL floating point numbers
    
     .WHILE type expression
     .ENDW
     
     .TEST type expression
     .ENDT
    
     .DEF label
    
     .GET [index] 'filename'["filename"][*][+-value][,+-ofset[,length]]
     .PUT [index] = value
     .SAV [index] ['filename',] length
     
     .EXTRN label name
     .PUBLIC label [,label2,...]
    
     .RELOC
     .LINK 'filename'
    

    .REPT expression

    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 którą nie powinna znajdować się żadna etykieta.

    Dodatkowo w obrębie bloku <.REPT, .ENDR> mamy możliwość skorzystania z dyrektywy .R, która zwraca aktualną wartość licznika pętli .REPT


    .PAGES [expression]

    Dyrektywa .PAGES określa liczbę stron pamięci w których powinien zmieścić się nasz fragment kodu, ograniczony przez <.PAGES .. .ENDPG> (domyślnie jest to wartość 1). Jeśli kod programu przekroczy zadeklarowaną liczbę stron pamięci wówczas zostanie wygenerowany błąd Page error at ????.

    Dyrektywy te mogą nam pomóc gdy zależy nam aby fragment programu mieścił się w granicach jednej strony pamięci albo gdy piszemy program mieszczący się w dodatkowym banku pamięci (64 strony pamięci), np.:
    Example:
    
     org $4000
     
     .pages $40
      ...
      ...
     .endpg
    

    .END

    Dyrektywa .END może być zamiennie używana z dyrektywami .ENDP, .ENDM, .ENDS, .ENDA, .ENDL, .ENDR, .ENDPG.


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

    Dyrektywa .VAR służy do deklaracji i inicjacji zmiennych w głównym bloku programu oraz w blokach .PROC i .LOCAL. Dopuszczalne typy zmiennych to .BYTE, .WORD, .LONG, .DWORD oraz ich wielokrotności, np.:
    Example:
    
     .var a,b , c, d  .word          ; 4 zmienne typu .WORD
     .var a,b,f  :256 .byte          ; 3 zmienne każda o wielkości 256 bajtów
     .var c=5,d=2,f=$123344 .dword   ; 3 zmienne .DWORD o wartościach 5, 2, $123344
    
      .proc name
      .var p1,p2,p3 .word
      .endp
    
     .local
      .var a,b,c .byte
      lda a
      ldx b
      ldy c
     .endl
    
    Tak zadeklarowane zmienne zostaną fizycznie odłożone dopiero na końcu bloku, po dyrektywie .ENDP, .ENDL (.END).

    .PRINT

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

    Example:
    
     .print "End: ",*,$8000-*
    

    .ERROR, ERT

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


    .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, .DWORD). Możliwe jest także ich użycie w celu definicji danych, w zastępstwie pseudo rozkazu DTA.
    Example:
    
    .proc test (.word tmp,a,b .byte value)
    
     .byte "atari",5,22
     .word 12,$FFFF
     .long $34518F
     .dword $11223344
    

    .DB

    Definicja danych typu BYTE, odpowiednik pseudo rozkazu DTA B lub dyrektywy .BYTE.


    .DW

    Definicja danych typu WORD, odpowiednik pseudo rozkazu DTA A lub dyrektywy .WORD.


    .DS expression

    Ta dyrektywa zapożyczona została z MAC'65, pozwala zarezerwować pamięć bez jej uprzedniej inicjalizacji. Jest to odpowiednik pseudo rozkazu ORG *+expression. Dyrektywy .DS nie można używać w kodzie relokowalnym podobnie jak ORG-a.

    purpose: reserves space for data without initializing then space to any particular value(s).
    
    usage: [label] .DS expression
    
    Using ".DS expression" is exactly equivalent of using "ORG *+expression". That is, the label
    (if it is given) is set equal to the current value of the location counter. Then then value
    of the expression is added to then location counter.
    
    Example: BUFFERLEN .DS 1 ;reserve a single byte
             BUFFER   .DS 256  ;reserve 256 bytes
    

    .BY [+byte] bytes and/or ASCII

    Store 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
    
    will generate:
            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

    Stores words in memory. Multiple words can be entered.


    .HE hex bytes

    Store hex bytes in memory. This is a convenient method to enter strings of hex bytes, since it does not require the use of the '$' character. The bytes are still separated by spaces however, 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


    .SB [+byte] bytes and/or ASCII

    This is in the same format as the .BY pseudo-op, 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.


    .FL floating point numbers

    Stores 6-byte BCD floating point numbers for use with the OS FP ROM routines.


    .EN

    Dyrektywa .EN jest odpowiednikiem pseudo rozkazu END This is an optional pseudo-op to mark the end of assembly. It can be placed before the end of your source file to prevent a portion of it from being assembled.


    .WHILE type expression

    Dyrektywa .WHILE jest odpowiednikiem instrukcji WHILE z języków wyższego poziomu (C, PAscal).

    Dyrektywy .WHILE i .ENDW pozwalają na wygenerowanie kodu maszynowego CPU 6502 pętli dla wyznaczonego bloku programu, możliwe jest ich zagnieżdżanie.

    Sekwencja działań przy wykonywaniu instrukcji while jest następująca:

  • 1. Oblicz wartość wyrażenia i sprawdź, czy jest równe zeru (fałsz). Jeżeli tak, to pomiń krok 2; jeżeli nie (prawda), przejdź do kroku 2.
  • 2. Wykonaj blok programu ograniczonego dyrektywami .WHILE i .ENDW, następnie przejdź do kroku 1.

    Jeżeli pierwsze wartościowanie wyrażenia wykaże, że ma ono wartość zero, to blok programu nigdy nie zostanie wykonany i sterowanie przejdzie do następnej instrukcji programu za dyrektywą .ENDW


    .TEST type expression

    Dyrektywa .TEST to ubogi odpowiednik instrukcji IF z języków wyższego poziomu (C, Pascal).

    Dyrektywy .TEST i .ENDT pozwalają na wygenerowanie kodu maszynowego CPU 6502 instrukcji warunkowej IF dla wyznaczonego bloku programu, możliwe jest ich zagnieżdżanie.

    Wykonanie dyrektywy .TEST zaczyna się od obliczenia wartości wyrażenia prostego tzn. takiego które składa się z dwóch argumentów i jednego operatora. Jeżeli wyrażenie ma wartość różną od zera (prawda), to będzie wykonywany blok programu ograniczony dyrektywami .TEST i .ENDT; jeżeli wyrażenie ma wartość zero (fałsz), to blok programu jest pomijany i sterowanie przejdzie do następnej instrukcji programu za dyrektywą .ENDT


    .DEF label

    W/w dyrektywa pozwala sprawdzić obecność definicji etykiety label lub ją zdefiniować. Jeśli etykieta została zdefiniowana zwraca wartość 1 czyli true, w przeciwnym wypadku zwraca 0 czyli false.

    This unary operator tests whether the following label has been defined yet, returning TRUE or FALSE as appropriate.

    CAUTION: Defining a label AFTER the use of a .DEF which references it can be dangerous, particularly if the .DEF is used in a .IF directive.


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

    Jest to odpowiednik pseudo rozkazu INS (podobna składnia), z tą różnicą że plik nie jest dołączany do asemblowanego pliku tylko ładowany do pamięci MADS-a. Ta dyrektywa pozwala wczytać określony plik do pamięci MADS-a i odwoływać się do bajtów tego pliku jak do tablicy jednowymiarowej np.:

    Example:
    
     .get 'file'                    ; wczytanie pliku do tablicy MADS-a
     .get [5] 'file'                ; wczytanie pliku do tablicy MADS-a od indeksu = 5
    
     .get 'file',0,3                ; wczytanie do tablicy MADS-a 3-ech wartości
    
     lda #.get[7]                   ; odczytanie 7 bajtu z tablicy MADS-a
     adres = .get[2]+.get[3]<<8     ; 2 i 3 bajt w nagłówku pliku DOS zawiera informacje o adresie ładowania
    
    Przy pomocy dyrektyw .GET, .PUT można odczytać np moduł Theta Music Composer (TMC) i dokonać jego relokacji. Realizuje to załączone do MADS-a makro z katalogu ../EXAMPLES/MSX/TMC_PLAYER/tmc_relocator.mac.

    Dopuszczalny zakres wartości dla INDEX = <0..65535>. Wartości odczytywane przez .GET są typu BYTE.


    .PUT [index] = value

    Dyrektywa .PUT pozwala odwołać się do tablicy jednowymiarowej w pamięci MADS-a i zapisać w niej wartość typu BYTE. Jest to ta sama tablica do której dyrektywa .GET zapisuje plik.

    Dopuszczalny zakres wartości dla INDEX = <0..65535>.

    Example:
    
     .put [5] = 12       ; zapisanie wartosci 12 w talicy MADS-a na pozycji 5-ej
    

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

    Dyrektywa .SAV pozwala zapisać bufor używany przez dyrektywy .GET, .PUT do pliku zewnętrznego lub dołączenie do aktualnie asemblowanego.
    Example:
    
     .sav ?length            ; dołączenie do asemblowanego pliku zawartości bufora [0..?length-1]
     .sav [200] 256          ; dołączenie do asemblowanego pliku zawartości bufora [200..200+256-1]
     .sav [6] 'filename',32  ; zapisanie do pliku FILENAME zawartości bufora [6..6+32-1]
    
    Dopuszczalny zakres wartości dla INDEX = <0..65535>.


    .OR, .AND, .XOR, .NOT

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


    .IF, .ELSE, .ELSEIF, .ENDIF

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

    W/w dyrektywy i pseudo rozkazy wpływają na przebieg asemblacji (można ich używać zamiennie), np.:

     .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
    

    W w/w przykładzie nawiasy (kwadratowe lub okrągłe) są koniecznością, ich brak spowodowałby że dla pierwszej dyrektywy .DEF parametrem byłaby nazwa etykiety label_name.AND.NOT.DEFlabel_name2 (spacje są pomijane, a znak kropki akceptowany w nazwie etykiety).


    Asemblacja na stronie zerowej

    W przeciwieństwie do dwu-przebiegowych asemblerów takich jak QA i XASM, MADS jest wielo-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 i najprostsza do wytłumaczenia właściwość większej liczby przebiegó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. Więcej informacji na temat mnemoników CPU 6502, 65816 oraz ich dopuszczalnych rozszerzeń w rodziale
    Mnemoniki.

    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, ostatni przebieg 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 $0020, ale załaduj pod adres $3080. Oczywiście późniejsze przeniesienie kodu pod właściwy adres (w naszym przykładzie $0020) należy już do zadań programisty.

    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.


    STRUKTURY

    Jeśli programowaliście w C, to pewnie spotkaliście się już ze strukturami. Ogólnie w MADS struktura definiuje tablicę wirtualną, jednowymiarową o polach różnego typu .BYTE, .WORD, .LONG, .DWORD i ich wielokrotności. Wirtualna ponieważ istnieje ona jak na razie tylko w pamięci assemblera.

    Pola takiej struktury zawierają informację o ofsecie do początku struktury.


    Deklaracja struktury (.STRUCT)

    Struktur dotyczą n/w dyrektywy:

    name .STRUCT
         .STRUCT name
         .ENDS
    

    name .STRUCT

    Deklaracja struktury name przy użyciu dyrektywy .STRUCT. Nazwa struktury jest wymagana i konieczna, jej brak wygeneruje błąd. Do nazw struktur nie można używać nazw mnemoników i pseudo rozkazów. Jeśli nazwa jest zarezerwowana wystąpi błąd z komunikatem Reserved word.

    Przykład deklaracji struktury:
    .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)
    
    Pola struktury definiujemy przez podanie nazwy i typu pola (.BYTE, .WORD, .LONG, .DWORD). Nazwa pola może być poprzedzona "białymi spacjami". W obszarze ograniczonym dyrektywami .STRUCT i .ENDS nie ma możliwości używania mnemoników CPU, jeśli je użyjemy lub użyjemy innych niedozwolonych znaków wystąpi błąd z komunikatem Improper syntax .

    Podsumowując, etykieta name zawiera informację o całkowitej długości struktury (w bajtach). Pozostałe etykiety opisujące pola zawierają informację o ofsecie do początku struktury.

    Deklaracji struktur nie można zagnieżdżać, można zagnieżdżać już zadeklarowane struktury (kolejność wystąpienia w listingu nie ma znaczenia), np.:
    .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
    
    Do czego może przydać się struktura ?

    Przypuśćmy, że mamy jakąś tablicę z polami różnego typu, możemy odczytywać pola takiej tablicy przy pomocy z góry określonych wartości offsetów. Jednak gdy dodamy dodatkowe pole do tablicy, czy też zmodyfikujemy ją w inny sposób, będziemy zmuszeni poprawiać kod programu który posługiwał się z góry określonymi wartościami offsetów. Gdy zdefiniujemy taką tablicę przy pomocy struktury będziemy mogli odczytywać jej pola posługując się offsetami zapisanymi w deklaracji struktury, czyli zawsze odczytamy właściwe pole niezależnie od zmian jakie zaszły w tablicy.

    Inny przykład zastosowania struktur został opisany w rozdziale Symbole zewnętrzne, przykład zastosowania symboli external i struktur .STRUCT.


    Definiowanie danych strukturalnych, odwołania

    Definiowanie danych strukturalnych polega na przypisaniu etykiecie konkretnej struktury z użyciem pseudo rozkazu DTA. Wynikiem takiego przypisania jest zarezerwowana pamięć, nie jest już to twór wirtualny.

    label DTA struct_name [count] (data1,data2,data3...) (data1,data2,data3...) ...
    

    COUNT określa liczbę z przedziału <0..COUNT>, która definiuje maksymalną wartość indeksu tablicy jednowymiarowej, a przez to także liczbę odłożonych w pamięci danych typu strukturalnego.

    Przykład deklaracji struktury i definicji danych strukturalnych:
    ;----------------------;
    ; deklaracja struktury ;
    ;----------------------;
    .STRUCT temp
    
    x .word
    y .word
    v .byte
    z .word
    
    .ENDS
    
    ;---------------------;
    ; definiowanie danych ;
    ;---------------------;
    
    data dta temp [12] (1,20,200,32000) (19,2,122,42700)
    
    data2 dta temp [0]
    
    Po nazwie struktury w nawiasie kwadratowym musi znajdować się wartość z przedziału <0..2147483647>, która definiuje maksymalną wartość indeksu tablicy jednowymiarowej, a jednocześnie liczbę odłożonych w pamięci danych typu strukturalnego.

    Po nawiasie kwadratowym może wystąpić opcjonalnie lista wartości początkowych (ograniczona nawiasami okrągłymi), jeśli nie wystąpi wówczas domyślnymi wartościami pól struktury są zera. Z kolei jeśli lista wartości początkowych jest mniejsza od liczby pól zadeklarowanych, wówczas pozostałe pola inicjowane są ostatnimi wartościami jakie zostały podane, np.

    data dta temp [12] (1,20,200,32000)
    
    Taka deklaracja spowoduje, że wszystkie pola zostaną zaincjowane wartościami 1,20,200,32000, a nie tylko pierwsze pole data[0].

    Jeśli lista wartości początkowych będzie większa lub mniejsza od liczby pól struktury, wówczas wystąpi błąd z komunikatem Constant expression violates subrange bounds.

    Aby odwołać się do pól tak nowo powstałych danych należy podać ich nazwę, koniecznie indeks w nawiasie kwadratowym i nazwę pola po kropce, np.:
     lda data[4].y
     ldx #data[0].v
    

    Brak nawiasu kwadratowego zakończy się komunikatem błędu Undeclared label.


    TABLICE

    Deklaracja tablicy jednowymiarowej (.ARRAY)

    Tablic dotyczą n/w dyrektywy:

    name .ARRAY count type [= default_value]
         .ARRAY name count type [= default_value]
         .ENDA
    
    Dostępne typy danych to .BYTE, .WORD, .LONG, .DWORD.

    COUNT określa maksymalną dopuszczalną wartość indeksu tablicy (0..COUNT), wartość ta powinna być z przedziału <0..65535>.

    W obszarze ograniczonym dyrektywami .ARRAY i .ENDA nie ma możliwości używania mnemoników CPU, jeśli je użyjemy lub użyjemy innych niedozwolonych znaków wówczas wystąpi błąd z komunikatem Improper syntax.

    Ten twór może wydać się dziwny, a jego zastosowanie ograniczone, jednak zdarzają się sytuacje kiedy może się przydać, np. tablica tłumacząca kod naciśniętego klawisza na kod ATASCII czy INTERNAL.

    .array TAB [255] .byte = $ff
    
     [63]:[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
    

    W/w przykład tworzy tablicę TAB o rozmiarze 0..255, typie danych .BYTE i wypełnia pola wartościami domyślnymi $FF, dodatkowo wstawia wartości kodów literowych INTERNAL na pozycje o indeksach równych kodowi naciśnięcia klawisza (bez SHIFT-a i z SHIFT-em, czyli duże i małe litery).

    Znak dwukropka ':' rozdziela poszczególne indeksy tablicy.

    Podsumowując, dyrektywa .ARRAY pozwala na stworzenie tablicy jednowymiarowej i wypełnienie jej wartościami zadeklarowanego typu.

    Do tak stworzonej tablicy odwołujemy się następująco:
     lda tab,y
     lda tab[23],x
     lda #tab[200]
    
    Jeśli w nawiasie kwadratowym podamy wartość indeksu przekraczającą zadeklarowaną dla danej tablicy, wówczas wystąpi błąd z komunikatem Constant expression violates subrange bounds.


    MAKRA

    Makra ułatwiają nam wykonywanie powtarzających się czynności, automatyzują je. Istnieją tylko w pamięci assemblera, dopiero w momencie wywołania są asemblowane. Przy ich pomocy MADS może odkładać i zdejmować z programowego stosu parametry dla procedur zadeklarowanych dyrektywą .PROC oraz przełączać banki rozszerzonej pamięci w trybie BANK SENSITIVE (OPT B+).

    Deklaracja makra

    Makr dotyczą n/w pseudo rozkazy i dyrektywy:

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

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

    Deklaracja makra o nazwie name za pomocą dyrektywy .MACRO. 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).

    Na końcu deklaracji makra może wystąpić deklaracja separatora i zarazem trybu przekazywania parametrów do makra (pojedyńczy apostrof bez zmian, podwójny apostrof z rozbijaniem parametrów na tryb adresacji i argument). Domyślnym separatorem, rozdzielającym parametry przekazywane do makra jest znak przecinka ',' oraz spacji ' '.
  • name .MACRO 'separator'
  • Pomiędzy pojedyńczymi apostrofami '' możemy umieścić znak separatora, który będzie używany do oddzielenia parametrów przy wywołaniu makra (tylko do tego mogą służyć pojedyńcze apostrofy).
  • name .MACRO "separator"
  • Pomiędzy podwójnymi apostrofami "" możemy także umieścić znak separatora, który będzie używany do oddzielenia parametrów przy wywołaniu makra. Dodatkowo użycie podwójnego apostrofu sygnalizuje MADS-owi aby rozkładał przekazywane parametry na dwa elementy: tryb adresacji i argument, np.:
     test #12 200 <30
    
    test .macro " "
    .endm
    
    Makro TEST ma zadeklarowany separator-spację przy użyciu podwójnego apostrofu ", czyli po wywołaniu makra parametry zostaną rozłożone na dwa elementy: tryb adresacji i argument.
     #12   ->  tryb adresacji '#' argument 12
     200   ->  tryb adresacji ' ' argument 200
     <30   ->  tryb adresacji '#' argument 0   (obliczona wartość wyrażenia "<30")
    
     test '#' 12 ' ' 200 '#' 0
    
    Parametry ze znakiem operatora '<', '>' zostają obliczone i dopiero ich wynik jest przekazywany do makra (podstawiany pod parametr). Tą właściwość możemy wykorzystać do stworzenia "samopiszącego" się kodu, kiedy potrzebujemy tworzyć nowe etykiety typu "label0", "label1", "label2", "label3" ... itd. , np.:
     :32 find #
    
    find .macro
          ift .def label:1
          dta a(label:1)
          eif
         .endm
    
    W/w przykład zapisuje adres etykiety pod warunkeim że taka etykiety istnieje (została zdefiniowana).


    .EXIT

    Zakończenie działania makra. Powoduje bezwzglę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, np.:
      .IF :0<2 || :0>5
        .ERROR "Wrong number of arguments"
      .ENDIF
    
      IFT %%0<2 .or :0>5
        ERT "Wrong number of arguments"
      EIF 
    

    Przykład makra:

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

    Wywołanie makra

    Makro wywołujemy poprzez jego nazwę, po niej mogą wystąpić parametry makra, rozdzielone separatorem którym jest domyślnie znak przecinka ',' lub spacji ' '. 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 danym makrze, wówczas pod brakujące parametry zostanie podstawiona wartość -1 ($FFFFFFFF). Tą właściwość można wykorzystać do sprawdzenia czy został przekazany parametr czy też nie, łatwiej jednak tego dokonać za pomocą parametru zerowego %%0.

     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 do makra razem ze znakami znajdującymi się 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 do makra

    Wszelkie definicje etykiet w obrębie makra mają zasięg lokalny.

    Jeśli poszukiwana przez assembler etykieta nie wystąpiła w obszarze 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 MADS-a. MADS zabezpiecza się przez rekurencją makr bez końca i zatrzymuje asemblacje gdy liczba wywołań makra przekroczy 1023 (błąd Infinite recursion).

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

    skok .macro
    
          skok
    
         .endm
    
    Przykład programu, który przekazuje parametry do pseudo procedur ..\EXAMPLES\MACRO.ASM:
     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 definicje etykiet 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 definicje etykiet 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 zdefiniowane 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 procedury .PROC jaką umożliwia MADS. Więcej o deklaracji procedury i operacjach jej dotyczących w rozdziale Procedury.


    DEFINIOWANIE KODU DLA ITERACJI

    Dyrektywa iteracji w MADS została zapożyczona z języka C. W języku C instrukcje powtarzania (pętli) lub iteracji pozwalają wykonywać daną instrukcję, prostą lub złożoną, zero lub więcej razy. W języku C są do dyspozycji trzy instrukcje iteracyjne (pętli): while, do-while i for. Różnią się one przede wszystkim metodą sterowania pętlą. Dla pętli while i for sprawdza się warunek wejścia do pętli, natomiast dla pętli do-while sprawdza się warunek wyjścia z pętli.

    Instrukcje iteracyjne, podobnie jak instrukcje selekcji, można zagnieżdżać do dowolnego poziomu zagnieżdżenia. Jednak przy wielu poziomach zagnieżdżenia program staje się mało czytelny. W praktyce nie zaleca się stosować więcej niż trzech poziomów zagnieżdżenia.

    MADS umożliwia automatyczne wygenerowanie kodu maszynowego dla najczęściej spotykanej i najbardziej popularnej pętli WHILE. Pozostałe rodzaje pętli FOR, DO-WHILE możliwe są do uzyskania przez odpowiednie użycie pętli WHILE.

    Definicja iteracji .WHILE

    Iteracji WHILE dotyczą n/w dyrektywy:

     .WHILE (.BYTE | .WORD) (ARG1 WARUNEK ARG2)
     .ENDW
    

    .WHILE (.BYTE | .WORD) (WARUNEK)

    Definicja iteracji przy użyciu dyrektyw .WHILE, .ENDW. Nazwa dla iteracji WHILE nie jest wymagana i nie jest konieczna.

    Po dyrektywie .WHILE musi wystąpić dyrektywa określająca typ argumentów dla warunku pętli. Warunek pętli jest jedno lub dwu-argumentowy (użycie okrągłych nawiasów jest opcjonalne), akceptowane typy argumentów to .BYTE lub .WORD, oba argumentu muszą być tego samego typu.

    Argument z lewej strony warunku musi być zmienną lub akumulatorem (@). Argument z prawej strony warunku powinien być stałą (#) lub zmienną.

     .while .byte (764 = #$ff)      ; pętla czekająca na naciśnięcie dowolnego klawisza
     .endw
    
     lda #1
     .while .byte @               ; pętla bez końca
     .endw
    
      lda $14
      .while .byte @ = $14        ; pętla czekająca na następną ramkę (1/50 sekundy dla PAL)
      .endw
    

    Dopuszczalnych jest 5 operatorów dla warunku:
  • < mniejszy (BCC)
  • > większy
  • <> różny (BNE)
  • <= mniejszy równy
  • >= większy równy (BCS)
  • = równy (BEQ)

    Cztery z w/w operatorów mają swoje odpowiedniki w rozkazach CPU 6502, 65816 są więc najszybsze w działaniu, pozostałe (<=, >) otrzymywane są przez operację odejmowania przez co są nieznacznie wolniejsze jednak bardziej niebezpieczne jeśli używamy akumulatora @, te operatory zmodyfikują zawartość akumulatora.


    DEFINIOWANIE KODU DLA DECYZJI


    PROCEDURY .PROC

    MADS wprowadza nowe możliwości w obsłudze procedur z parametrami. Nowe możliwości upodabniają ten mechanizm do tych znanych z języków wysokiego poziomu i są tak samo łatwe w użyciu dla programisty.

    Aktualnie dołączone do MADS-a deklaracje makr (@CALL.MAC, @PULL.MAC, @EXIT.MAC) umożliwiają obsługę stosu programowego o wielkości 256 bajtów, czyli podobnej wielkości jak stos sprzętowy, udostępniają mechanizm zdejmowania ze stosu programowego i odkładania na stos programowy parametrów potrzebnych podczas wywoływania procedur, jak i wychodzenia z takich procedur. MADS uwzględnia możliwość rekurencji takich procedur.

    Programista nie jest zaangażowany w ten mechanizm, może skupić uwagę na swoim programie. Musi tylko pamiętać o potrzebie zdefiniowania odpowiednich etykiet i dołączeniu odpowiednich makr podczas asemblacji programu.

    Dodatkowo istnieje możliwość pominięcia "mechanizmu" stosu programowego MADS-a i skorzystanie z klasycznego sposobu ich przekazywania, za pomocą rejestrów CPU (dyrektywa .REG). Taka procedura nie potrzebuje już stosu programowego, jest najszybsza jednak jej wadą jest ograniczenie liczby przekazywanych bajtów do maksimum trzech.

    Inną właściwością procedur .PROC jest możliwość pominięcia ich podczas asemblacji jeśli nie wystąpiło żadne odwołanie do nich, czyli zostały zdefiniowane ale nie są wykorzystane. Wystąpi wówczas komunikat ostrzeżenia Unreferenced procedure ????. Pominięci takiej procedury podczas asemblacji możliwe jest poprzez podanie parametru do MADS-a w linii poleceń -x.

    Wszelkie etykiety zdefiniowane w obszarze procedury .PROC są zasięgu lokalnego, można je też określić jako etykiety globalne zdefiniowane lokalnie o dostępie swobodnym, ponieważ można się do nich odwoływać co nie jest normalne w innych językach programowania.

    W obszarze procedury .PROC istnieje możliwość zdefiniowania etykiet o zasięgu globalnym (patrz rozdział
    Etykiety globalne).

    Jeśli chcemy dostać się do etykiet zdefiniowanych w procedurze spoza obszaru procedury, wówczas adresujemy z użyciem znaku kropki '.', np.:
     lda test.pole
    
    .proc test
    
    pole nop
    
    .endp
    
    Jeśli poszukiwana przez assembler etykieta nie wystąpiła w obszarze procedury .PROC, wówczas MADS będzie poszukiwał ją w obszarze niższym aż dojdzie do obszaru globalnego. Aby odczytać natychmiastowo wartość etykiety globalnej z poziomu procedury .PROC (czy też innego obszaru lokalnego) poprzedzamy nazwę etykiety znakiem dwukropka ':'.

    MADS wymaga dla procedur wykorzystujących stos programowy, trzech globalnych definicji etykiet o konkretnych nazwach (adres stosu, wskaźnik stosu, adres parametrów procedury):
  • @PROC_VARS_ADR
  • @STACK_ADDRESS
  • @STACK_POINTER
  • Brak definicji w/w etykiet i próba użycia bloku .PROC wykorzystującego stos programowy spowoduje że MADS przyjmie swoje domyślne wartości tych etykiet: @PROC_VARS_ADR = $0500, @STACK_ADDRESS = $0600, @STACK_POINTER = $FE

    MADS dla procedur wykorzystujących stos programowy wymaga także deklaracji makr o konkretnych nazwach. Dołączone do MADS-a deklaracje tych makr znajdują się w plikach:
  • @CALL ..\EXAMPLES\MACROS\@CALL.MAC
  • @PUSH ..\EXAMPLES\MACROS\@CALL.MAC
  • @PULL ..\EXAMPLES\MACROS\@PULL.MAC
  • @EXIT ..\EXAMPLES\MACROS\@EXIT.MAC
  • W/w makra realizują przekazywanie i odkładanie na programowy stos parametrów, oraz zdejmowanie i odkładanie parametrów dla procedur wykorzystujących stos programowy i wywoływanych z poziomu innych procedur wykorzystujących stos programowy.

    Deklaracja procedury .PROC

    Procedur dotyczą n/w dyrektywy:

     name .PROC [(.TYPE PAR1 .TYPE PAR2 ...)]
     .PROC name [(.TYPE PAR1 .TYPE PAR2 ...)]
     .ENDP
    

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

    Deklaracja procedury name przy użyciu dyrektywy .PROC. 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 nazwa jest zarezerwowana wystąpi błąd z komunikatem Reserved word.

    Jeśli chcemy wykorzystać jeden z mechanizmów MADS-a do przekazywania parametrów do procedur, musimy je wcześniej zadeklarować. Deklaracja parametrów procedury mieści się pomiędzy nawiasami okrągłymi ( ). Dostępne są cztery typy parametrów:
  • .BYTE (8-bit) relokowalne
  • .WORD (16-bit) relokowalne
  • .LONG (24-bit) nierelokowalne
  • .DWORD (32-bit) nierelokowalne
  • W obecnej wersji MADS-a nie ma możliwości przekazywania parametrów za pomocą struktur .STRUCT.

    Bezpośrednio po deklaracji typu, oddzielona minimum jedną spacją, następuje nazwa parametru. Jeśli deklarujemy więcej parametrów tego samego typu możemy rozdzielić ich nazwy znakiem przecinka ','.

    Przykład deklaracji procedury wykorzystującej stos programowy:
    name .PROC ( .WORD par1 .BYTE par2 )
    name .PROC ( .BYTE par1,par2 .LONG par3 )
    name .PROC ( .DWORD p1,p2,p3,p4,p5,p6,p7,p8 )
    

    Dodatkowo używając dyrektyw .REG lub .VAR mamy możliwość określenia sposobu i metody przekazywania parametrów do procedur MADS-a. Przez rejestry CPU (.REG) lub przez zmienne (.VAR). Dyrektywy określające sposób przekazywania parametrów umieszczamy na końcu naszej deklaracji procedury .PROC

    Przykład deklaracji procedury wykorzystującej rejestry CPU:
    name .PROC ( .BYTE x,y,a .BYTE ) .REG
    name .PROC ( .WORD xa .BYTE y ) .REG
    name .PROC ( .LONG axy ) .REG
    

    Dyrektywa .REG wymaga aby nazwy parametrów składały się z liter 'A', 'X', 'Y' lub ich kombinacji. Litery te odpowiadają nazwom rejestrów CPU i wpływają na kolejność użycia rejestrów. Ograniczeniem w liczbie przekazywanych parametrów jest ilość rejestrów CPU, przez co możemy przekazać do procedury w sumie maksimum 3 bajty. Zaletą takiego sposobu jes natomiast szybkość i małe zużycie pamięci RAM.

    Przykład deklaracji procedury wykorzystującej zmienne:
    name .PROC ( .BYTE x1,x2,y1,y2 ) .VAR
    name .PROC ( .WORD inputPointer, outputPointer ) .VAR
    
    Dla .VAR nazwy parametrów wskazują nazwy zmiennych do których będą ładowane przekazywane parametry. Metoda ta jest wolniejsza od .REG jednak nadal szybsza od metody ze stosem programowym.

    Procedurę opuszczamy w standardowy sposób, czyli przy pomocy rozkazu RTS. Dodanie rozkazu RTS w ciele procedury przy wyjściu z każdej ścieżki jest obowiązkiem programującego, a nie assemblera.

    Po opuszczeniu procedury MADS wywołuje makro @EXIT, którego zadaniem jest modyfikacja wskaźnika stosu programowego @STACK_POINTER, jest to konieczne dla prawidłowego działania stosu programowego. Użytkownik może sam zaprojektować swoje makro @EXIT, albo skorzystać z dołączonego do MADS-a (plik ..\EXAMPLES\MACROS\@EXIT.MAC), ma ono obecnie następującą postać:
    .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
    

    Makro @EXIT nie powinno zmieniać zawartości rejestrów CPU jeśli chcemy zachować możliwość zwrócenie wyniku działania procedury .PROC poprzez rejestry CPU.

    .ENDP

    Dyrektywa .ENDP kończy deklarację bloku procedury.

    Wywołanie procedury

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

    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 to wystąpi komunikat błędu Improper number of actual parameters. Wyjątkiem jest procedura do której parametry przekazywane są przez rejestry CPU (.REG) lub zmienne (.VAR), w takich przypadkach możemy pominąć parametry, w domyśle są one już załadowane do odpowiednich rejestrów czy też zmiennych.

    Możliwe są trzy sposoby przekazania parametru:

  • '#' przez wartość
  • ' ' przez wartość spod adresu (bez znaku poprzedzającego)
  • '@' przez akumulator (parametry typu .BYTE)
  • "string" przez ciąg znakowy, np. "label,x"

    Przykład wywołania procedury:

     name @ , #$166 , $A400  ; dla stosu programowego
     name , @ , #$3f         ; dla .REG lub .VAR
     name "(hlp),y" "tab,y"	 ; dla .VAR lub dla stosu programowego (stos programowy korzysta z regX)
    

    MADS po napotkaniu wywołania procedury, która korzysta ze stosu programowego wymusza wykonanie makra @CALL. Jeśli jednak procedura nie korzysta ze stosu programowego, zamiast makra @CALL zostanie wynegerowany zwykły rozkaz JSR PROCEDURE.

    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).
    @CALL_INIT 3\ @PUSH_INIT 3\ @CALL '@','B',0\ @CALL '#','W',358\ @CALL ' ',W,"$A400"\ @CALL_END PROC_NAME
    
    Makro @CALL odłoży na stos zawartość akumulatora, następnie wartość $166 (358 dec), następnie wartość spod adresu $A400. Więcej informacji na temat sposobu przekazywania parametrów do makr (znaczenia apostrofów '' i "") w rozdziale
    Wywołanie makra.

    Parametr przekazywany przez akumulator '@' powinien być zawsze pierwszym parametrem przekazywanym do procedury, 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. W przypadku procedur .REG lub .VAR kolejność wystąpienia parametru '@' nie ma znaczenia.

    Wyjście z procedury .PROC następuje poprzez rozkaz RTS. Po powrocie z procedury MADS wywołuje makro @EXIT które zawiera program modyfikujący 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, liczba bajtów przekazywana jest do makra jako parametr.

    Dodanie rozkazu RTS w ciele procedury przy wyjściu z każdej ścieżki jest obowiązkiem programującego, a nie assemblera.


    Parametry procedury, odwołania

    Odwołania do parametrów procedury z poziomu procedury nie wymagają dodatkowych operacji ze strony programisty, np.:
    @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'
    
    MADS w momencie napotkania deklaracji .PROC z parametrami, dokonuje automatycznej definicji tych parametrów przypisując im wartości na podstawie @PROC_VARS_ADR. W w/w przykładzie MADS dokona automatycznej definicji parametrów PAR1 = @PROC_VARS_ADR, PAR2 = @PROC_VARS_ADR + 2.

    Programista odwołuje się do tych parametrów po nazwie jaka została im nadana w deklaracji procedury, czyli podobnie jak ma to miejsce w językach wyższego poziomu. W MADS istnieje możliwość dostępu do parametrów procedury spoza procedury co nie jest już normalne w językach wyższego poziomu. Możemy odczytać z w/w przykładu zawartość PAR1, np.:

     lda name.par1
     sta $a000
     lda name.par1+1
     sta $a000+1
    
    Wartość PAR1 została przepisane pod adres $A000, wartość PAR1+1 pod adres $A000+1. Oczywiście możemy tego dokonać tylko bezpośrednio po zakończeniu tej konkretnej procedury. Trzeba pamiętać że parametry takich procedur odkładane są pod wspólnym adresem @PROC_VARS_ADR, więc z każdym nowym wywołaniem procedury wykorzystującej stos programowowy zawartość obszaru <@PROC_VARS_ADR .. @PROC_VARS_ADR + $FF> ulega zmianom.

    Jeśli procedura ma zadeklarowane parametry typu .REG programista powinien zatroszczyć się o to aby je zapamiętać czy też właściwie wykorzystać zanim zostaną zmodyfikowane przez kod procedury. W przypadku parametrów typu .VAR nie trzeba się o nic martwić ponieważ parametry zostały zapisane do konkretnych komórek pamięci skąd zawsze możemy je odczytać.


    OBSZAR LOKALNY

    Głównym zadaniem obszaru lokalnego w MADS jest stworzenie nowej przestrzeni nazw dla etykiet.

    Wszelkie etykiety zdefiniowane w obszarze lokalnym .LOCAL są zasięgu lokalnego, można je też określić jako etykiety globalne zdefiniowane lokalnie o dostępie swobodnym, ponieważ można się do nich odwoływać co nie jest normalne w innych językach programowania.

    W obszarze lokalnym .LOCAL istnieje możliwość zdefiniowania etykiet o zasięgu globalnym (patrz rozdział
    Etykiety globalne).

    Jeśli poszukiwana przez assembler etykieta nie wystąpiła w obszarze lokalnym .LOCAL, wówczas MADS będzie poszukiwał ją w obszarze niższym aż dojdzie do obszaru globalnego. Aby odczytać natychmiastowo wartość etykiety globalnej z poziomu obszaru lokalnego .LOCAL (czy też innego obszaru lokalnego) poprzedzamy nazwę etykiety znakiem dwukropka ':'.

    Deklaracja obszaru lokalnego .LOCAL

    Obszarów lokalnych dotyczą n/w dyrektywy:

     [name] .LOCAL
     .LOCAL [name]
     .ENDL
    

    [name] .LOCAL

    Deklaracja obszaru lokalnego o nazwie name za pomocą dyrektywy .LOCAL. Nazwa obszaru nie jest wymagana i nie jest konieczna. Do nazw obszarów lokalnych nie można używać nazw mnemoników i pseudo rozkazów. Jeśli nazwa jest zarezerwowana wystąpi błąd z komunikatem Reserved word.

    Wszelkie definicje etykiet w obszarze .LOCAL są typu lokalnego. Aby odwołać się do etykiety globalnej o tej samej nazwie co etykieta lokalna należy poprzedzić ją znakiem dwukropka ':', np.:
    lab equ 1
    
    .local
    
    lab equ 2
    
     lda #lab
     ldx #:lab
    
    .endl
    
    W w/w przykładzie do rejestru A zostanie załadowana wartość 2, natomiast do rejestru X wartość 1.

    Jeśli poszukiwana przez assembler etykieta nie wystąpiła w obszarze .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 definicje etykiet rozróżniane są na podstawie nazwy obszaru lokalnego. Aby dotrzeć do zdefiniowanej etykiety w obszarze lokalnym spoza obszaru lokalnego musimy znać nazwę obszaru i etykiety w nim występującej, np.:
     lda #name.lab1
     ldx #name.lab2
    
    .local name
    
    lab1 = 1
    lab2 = 2
    
    .endl
    

    Czyli używamy znaku kropki '.' w adresowaniu takiej struktury .LOCAL.

    Obszary lokalne możemy zagnieżdżać, możemy je umieszczać w ciele procedur zadeklarowanych przez dyrektywę .PROC.

    .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
                         |    |
    .local obszar        |    |   deklaracja obszaru lokalnego
                         |    |
     lda tmp   <---      |    |
                  |      |    |
     lda :tmp     |      | <---   odwolanie do obszaru globalnego
                  |      |
    tmp nop    <---      |        definicja w obszarze lokalnym
                         | 
    pole lda #0       <---   <--- definicja w obszarze lokalnym
                                |
     lda pole  <----------------- odwolanie w obszarze lokalnym
    
    .endl                        koniec deklaracji obszaru lokalnego
    

    SKŁADNIA

    MADS akceptuje podobną składnię jak QA, XASM. Jest jednak mniej tolerancyjny w kwestii komentarzy umieszczanych na końcu linii (komentarze powinny być poprzedzone odpowiednim znakiem) lub bardziej tolerancyjny w przypadku białych spacji i w przypadkach braku argumentu dla mnemonika rozkazu CPU, np.:
       asl            ->  asl @
       lda            ->  lda *
       lda #          ->  lda #0
       bcc            ->  bcc *
    
    Dla w/w przypadków zostanie dodatkowo wygenerowane ostrzeżenie WARNING: Default addressing mode.

    MADS akceptuje brak białych spacji rozdzielających mnemonik i argument, pod warunkiem że znaki argumentu nie zaczynają się znakiem '@' używanym w nazwach etykiet lub znakami '%', ':' używanymi do oznaczenia numeru parametru w makrach (%%numer, :numer), np.:
       lda$44
       lda#
       lda(80),y
    

    Komentarze

    Znaki komentarza jednoliniowego powinniśmy poprzedzać znakiem ';' lub '*'. Do oznaczania komentarza jednoliniowego najbezpieczniej jest jednak używać średnika ';' ponieważ znak '*' ma też inne znaczenia, może oznaczać operację mnożenia, czy też aktualny adres podczas asemblacji. Średnik natomiast dedykowany jest tylko i wyłącznie do oznaczania komentarzy.

    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 '/* */' i znaki oznaczające komentarz jednoliniowy '//' można stosować bez ograniczeń.


    Łączenie wielu wierszy listingu w jeden wiersz

    Dowolną ilość wierszy listingu możemy połączyć (rozdzielić) w jeden wiersz używając znaku '\', np.:

     lda 20\ cmp 20\ beq *-2
     
        lda 20   \ cmp  20   \   beq *-2
     
      lda #0  \lop  sta $a000,y  \ iny  \ bne lop     ; komentarz tylko na końcu takiego wiersza
    

    Jeśli po znaku '\' nie umieścimy znaku spacji, wówczas mnemonik czy inny ciąg znakowy może zostać zinterpretowany jako etykieta, użytkownik powinien pamiętać że znak '\' oznacza początek nowej linii.

    MADS kończy przetwarzać taki wiersz, aż do napotkania komentarza lub napotkania końca ciągu znakowego, dlatego komentarze możemy umieszczać tylko na końcu takiego wielo-wiersza.


    Łączenie wielu mnemoników

    Możliwość łączenia dwóch mnemoników za pomocą znaku dwukropka ':' znana jest już z XASM-a. W MADS ta możliwość została rozszerzona o łączenie dowolnej liczby znanych MADS-owi mnemoników, np.:

     lda:cmp:req 20
    
     lda:iny:sta:iny $600,y
    


    WYRAŻENIA

    Termin wyrażenie oznacza sekwencję operatorów i operandów (argumentów), która określa operacje, tj. rodzaj i kolejność obliczeń. Wyrażeniem złożonym nazywa się takie wyrażenie, w którym występuje dwa lub więcej operatorów. Operatory, które oddziaływują tylko na jeden operand, nazywa się jednoargumentowymi (unarnymi). Operatory dwuargumentowe nazywa się binarnymi.

    Wartościowanie wyrażenia przebiega w porządku, określonym pierwszeństwem operatorów i w kierunku, określonym przez kierunek wiązania operatorów.

    Liczby

    MADS akceptuje zapis liczb w formacie decymalnym, hexadecymalnym, binarnym oraz w kodach ATASCII i INTERNAL.

  • zapis decymalny:
     -100
     -2437325
     1743
    
  • zapis hexadecymalny:
     $100
     $e430
     $000001
    
  • zapis binarny:
     %0001001010
     $000000001
     $001000
    
  • zapis kodami ATASCII:
     'a'
     'fds'
     'W'*
    
  • zapis kodami INTERNAL:
     "B"
     "FDSFSD"
     "."*
    
    Tylko pierwszy znak ciągu ATASCII, INTERNAL jest znaczący. Znak '*' za apostrofem zamykającym powoduje invers znaku.


    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)
    

    ETYKIETY

    Etykiety zdefiniowane w programie mogą posiadać zasięg lokalny lub globalny, w zależności od miejsca w jakim zostały zdefiniowane. Oprócz tego można zdefiniować etykiety tymczasowe, które także mogą posiadać zasięg lokalny lub globalny.

  • Zasięg globalny etykiety oznacza, że jest ona widoczna z każdego miejsca w programie, niezależnie czy jest to makro .MACRO, procedura .PROC czy obszar lokalny .LOCAL.

  • Zasięg lokalny etykiety oznacza, że jest ona widoczna tylko w konkretnie zdefiniowanym obszarze, np. przez dyrektywy: .MACRO, .PROC, .LOCAL.

  • etykiety występują zawsze na początku wiersza (wyjątkiem są etykiety które kończą się znakiem ':')
  • etykiety muszą zaczynać się znakiem ['A'..'Z','_','?','@']
  • pozostałe dopuszczalne znaki etykiety to ['A'..'Z','0'..'9','_','?','@']

    Przykład definicji etykiet:

    ?nazwa   EQU  $A000      ; definicja etykiety tymczasowej globalnej
    nazwa     =   *          ; definicja etykiety globalnej
    nazwa2=12                ; definicja etykiety globalnej
    @?nazwa  EQU  'a'+32     ; definicja etykiety globalnej
      name: equ 12           ; definicja etykiety globalnej nie zaczynającej się od pierwszego znaku wiersza
             nazwa: = 'v'    ; definicja etykiety globalnej nie zaczynającej się od pierwszego znaku wiersza
    

    Czyli doszła możliwość użycia znaku zapytania '?' i "małpki" '@' w nazwach etykiet.

    Użycie znaku kropki '.' w nazwie etykiety jest dopuszczalne, jednak nie zalecane. Znak kropki zarezerwowany jest do oznaczania rozszerzenia mnemonika, do oznaczenia dyrektyw assemblera, w adresowaniu nowych struktur MADS-a.

    Znak kropki '.' na początku nazwy etykiety sugeruje że jest to dyrektywa assemblera, natomiast znak zapytania '?' na początku etykiety oznacza etykietę tymczasową, taką której wartość może się zmieniać wielokrotnie w trakcie asemblacji.


    Etykiety lokalne

    Każda definicja etykiety w obrębie makra .MACRO, procedury .PROC czy obszaru lokalnego .LOCAL domyślnie jest zasięgu lokalnego, innymi słowy jest lokalna. Takich etykiet użytkownik nie musi oznaczać dodatkowo.

    Etykiety lokalne definiujemy używając n/w równoważnych pseudo rozkazów:
     EQU
      =
    
    Aby mieć dostęp do etykiet o zasięgu globalnym (czyli zdefiniowanych poza makrem .MACRO, procedurą .PROC, obszarem lokalnym .LOCAL) i o takich samych nazwach jak lokalne, należy użyć operatora ':', np.:
    lp   ldx #0         ; definicja globalna etykiety LP
    
         test
         test
     
    test .macro
    
          lda :lp       ; znak ':' przed etykietą odczyta wartość etykiety globalnej LP
    
          sta lp+1      ; odwołanie do etykiety lokalnej LP w obszarze makra
    lp    lda #0        ; definicja etykiety lokalnej LP w obszarze makra
    
         .endm
    
    W w/w przykładzie występują definicje etykiet o tych samych nazwach (LP), lecz każda z nich ma inną wartość i jest innego zasięgu.


    Etykiety globalne

    Każda definicja etykiety dokonana w bloku programu poza obszarem makra .MACRO, procedury .PROC czy obszaru lokalnego .LOCAL jest zasięgu globalnego, innymi słowy jest globalna.

    Etykiety globalne definiujemy używając n/w równoważnych pseudo rozkazów:
     EQU
      =
    

    Etykietę globalną możemy zdefiniować także będąc w obszarze makra .MACRO, procedury .PROC lub obszaru lokalnego .LOCAL, wystarczy że nazwa etykiety nie będzie zaczynać się od pierwszego znaku w wierszu, tylko zostanie poprzedzona minimum jedną spacją lub tabulatorem. Dzięki takiej właściwości możemy dokonać deklaracji i definicji etykiet globalnych z poziomu makr.

    Przykład definicji etykiet globalnych:
    lab equ *
       lab2 equ $4000
    
    	?tmp = 0
    	?tmp += 40
     
    .proc name
    
          ?nazwa   = $A000
               nazwa  EQU 20
            @?nazw   = 'a'+32
    .endp
    
    Przykładem zastosowania definicji etykiety globalnej oraz tymczasowej jest m.in. makro @CALL (plik ..\EXAMPLES\MACROS\@CALL.MAC), w którym występuje definicja etykiety tymczasowej ?@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        ; definicja etykiety globalnej i tymczasowej
     
      ...
      ...
     
     
    @CALL_@ .macro
    
      sta @stack_address+?@stack_offset,x
      ?@stack_offset += 1
    
     .endm 
    

    Etykiety tymczasowe

    Definicja etykiety tymczasowej posiada tą właściwość, że jej wartość może ulegać zmianie wielokrotnie nawet podczas jednego przebiegu asemblacji. Normalnie próba ponownej definicji etykiety kończy się komunikatem 'Label declared twice'. Nie będzie takiego komunikatu jeśli jest to etykieta tymczasowa.

    Zasięg etykiet tymczasowych uzależniony jest od obszaru w jakim etykieta została zdefiniowana. Etykiety tymczasowe mogą posiadać zasięg lokalny (Etykiety lokalne) lub globalny (Etykiety globalne).

    Etykietę tymczasową może zdefiniować użytkownik poprzez umieszczenie na początku nazwy etykiety znaku zapytania '?', np.:
     ?label
    
    Etykiet tymczasowych nie powinno używać się do nazw procedur .PROC, makr .MACRO, obszarów lokalnych .LOCAL, struktur .STRUCT, tablic .ARRAY.

    Etykiety tymczasowe definiujemy używając n/w równoważnych pseudo rozkazów:
     EQU
      =
    
    Dodatkowo możemy je modyfikować za pomocą znanych z C operatorów:
     -= expression
     += expression
     --
     ++
    
    W/w operatory modyfikujące dotyczą tylko etykiet tymczasowych, próba ich użycia dla innego typu etykiety skończy się komunikatem błędu Improper syntax.

    Przykład użycia etykiet tymczasowych:
    ?loc = $567
    ?loc2 = ?loc+$2000
    
    	 lda ?loc
    	 sta ?loc2
    
    ?loc = $123
    
    	 lda ?loc
    

    SPARTA DOS X

    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 "przemieszczalnego" 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.


    Programowanie Sparta DOS X (SDX)

    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 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 !


    KOD RELOKOWALNY

    Kod relokowalny to taki kod, który nie ma z góry określonego adresu ładowania do pamięci komputera, kod taki musi zadziałać niezależnie od adresu załadowania. W Atari XE/XL kod relokowalny udostępnia system Sparta DOS X (SDX), więcej na ten temat można przeczytać w rozdziale
    Programowanie Sparta DOS X.

    Kod relokowalny dla SDX posiada podstawowe ograniczenie jakim jest relokowanie tylko adresów typu WORD, nie ma także obsługi rozkazów CPU 65816. MADS udostępnia możliwość generowania kodu relokowalnego w formacie SDX jak i swoim własnym niekompatybilnym z SDX, który znosi wcześniej wymienione ograniczenia.

    Format zapisu pliku z kodem relokowalnym MADS-a jest podobny do tego znanego z SDX, podobnie występuje tutaj blok główny i bloki dodatkowe z informacją o adresach które należy poddać relokacji. MADS stosuje prostszy zapis bloków aktualizacji, bez "kompresji" jaką stosuje SDX.

    Zalety kodu relokowalnego MADS-a:

  • uwzględnia rozmiar argumentów dla CPU 6502, 65816
  • można używać wszystkie rozkazy CPU, bez ograniczeń
  • pozwala na relokacje młodszych i starszych bajtów adresu

    Ograniczenia kodu relokowalnego MADS-a:

  • deklaracji etykiet przez EQU dokonujemy przed blokiem .RELOC
  • jeśli chcemy zdefiniować nową etykietę w bloku .RELOC musimy jej nazwę poprzedzić spacją lub tabulatorem (etykieta globalna)
  • nie można używać pseudo rozkazów ORG, RMB, LMB, NMB oraz dyrektywy .DS
  • nie można relokować najstarszego bajtu ze słowa 24bit, np. lda ^$121416

    Przykładem tego jak prosto można stworzyć kod relokowalny jest plik ..\EXAMPLES\TETRIS_RELOC.ASM, który od strony użytej listy rozkazów CPU i pseudo rozkazów definiujących dane niczym nie różni się od wersji nierelokowalnej ..\EXAMPLES\TETRIS.ASM


    Blok relokowalny .RELOC

    Blok relokowalny MADS-a zostanie wygenerowany po użyciu dyrektywy:

     .RELOC [type]
    
    Blok aktualizacji dla bloku relokowalnego MADS-a wywołujemy używając pseudo rozkazu BLK:
     BLK UPDATE ADDRESS
    
    Po dyrektywie .RELOC możliwe jest podanie typu bloku relokowalnego (.BYTE, .WORD), domyślnie jest to typ .WORD. Typ .BYTE dotyczy bloku przeznaczonego do umieszczenia wyłącznie na stronie zerowej (będzie zawierał rozkazy strony zerowej), MADS będzie asemblował taki blok od adresu $0000. Typ .WORD oznacza że MADS będzie asemblował blok relokowalny od adresu $0100 i będzie przeznaczony do umieszczenia w dowolnym obszarze pamięci (nie będzie zawierał rozkazów strony zerowej).

    Nagłówek bloku .RELOC przypomina ten znany z DOS-a, dodatkowo został on rozszerzony o 10 nowych bajtów czyli w sumie zajmuje 16 bajtów, np.:

    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 zawsze o wartości $524D co odpowiada znakom 'MR' (M-ADS R-ELOC)
    FILE_LENGTH to długość bloku relokowalnego bez 16 bajtowego nagłówka
    CONFIG wykorzystany jest obecnie tylko bit0 tego bajtu, bit0=0 oznacza blok relokowalny asemblowany od adresu $0000, bit0=1 blok relokowalny asemblowany od adresu $0100

    Ostatnie 6 bajtów zawiera informację o wartościach etykiet potrzebnych do działania stosu programowego @STACK_POINTER, @STACK_ADDRESS, @PROC_VARS_ADR jeśli zostały użyte podczas asemblacji bloków relokowalnych. Jeśli poszczególne bloki .RELOC zostały zasemblowane z różnymi wartościami tych etykiet i są one linkowane wystąpi wówczas komunikat ostrzeżenia Incompatible stack parameters. Jeśli stos programowy nie został użyty wartościami tych etykiet są zera.

    Pseudo rozkaz .RELOC powoduje przełączenie MADS-a w tryb generowania kodu relokowalnego z uwzględnianiem rozmiaru argumentów rozkazów CPU 6502, 65816. W obszarze takiego kodu niemożliwe jest używanie pseudo rozkazów ORG, LMB, NMB, RMB oraz dyrektywy .DS. Niemożliwy jest powrót MADS-a do trybu generowania kodu nie relokowalnego, możliwe jest wygenerowanie więcej niż jednego bloku .RELOC.

    Użycie dyrektywy .RELOC powoduje dodatkowo zwiększenie licznika wirtualnych banków MADS-a, przez co taki obszar staje się lokalny i niewidoczny dla innych bloków. Więcej informacji na temat wirtualnych banków w rozdziale Wirtualne banki pamięci (OPT B-).

    Na końcu bloku .RELOC wymagane jest wygenerowanie bloku aktualizacji, realizuje to pseudo rozkaz BLK z identyczną składnią jak dla bloku relokowalnego SDX ("BLK UPDATE ADDRESS"). Format zapisu takiego bloku aktualizacji nie jest jednak identyczny z SDX, ma następującą postać:

    HEADER       WORD ($FFEF)
    TYPE         CHAR (B-YTE, W-ORD, L-ONG, D-WORD, <, >)
    DATA_LENGTH  WORD
    DATA         WORD [BYTE]
    
    HEADER zawsze o wartości $FFEF
    TYPE typ danych zapisany jest na bitach 0..6 tego bajtu i określa typ modyfikowanych adresów, znak "<" oznacza młodszy bajt adresu, znak ">" oznacza starszy bajt adresu.
    DATA_LENGTH to liczba 2-bajtowych danych (adresów) do modyfikacji
    DATA to właściwy ciąg danych służących modyfikacji głównego bloku relokowalnego. Pod wskazanym tutaj adresem należy odczytać wartość typu TYPE a następnie zmodyfikować na podstawie nowego adresu ładowania.

    Wyjątek stanowi blok aktualizacji dla starszych bajtów adresów ">", dla takiego bloku w DATA zapisywany jest jeszcze dodatkowy bajt BYTE (młodszy bajt modyfikowanego adresu). Aby dokonać aktualizacji starszych bajtów, musimy odczytać bajt spod adresu WORD w DATA, dodać go do aktualnego adresu relokacji i dodać jeszcze młodszy bajt z BYTE w DATA. Tak nowo obliczony starszy bajt umieszczamy pod adresem WORD z DATA.


    Symbole zewnętrzne

    Symbole zewnętrzne informują, że zmienne i procedury które reprezentują będą znajdowały się gdzieś na zewnątrz, poza aktualnym programem. Nie musimy określać gdzie. Musimy jedynie podać ich nazwy oraz typy. W zależności od typu danych jakie reprezentuje symbol instrukcje asemblera tłumaczone są na odpowiednie kody maszynowe, asembler musi znać rozmiar używanych danych.

    Symbole zewnętrzne mogą być używane w blokach relokowalnych .RELOC jak i w zwykłych blokach DOS-a.

    Symbole zewnętrzne (external) deklarujemy używając pseudo rozkazu EXT lub dyrektywy .EXTRN:
    label EXT type
    label .EXTRN type
    .EXTRN label1,label2,label3... type
    
    Blok aktualizacji dla symboli external wywołujemy używając pseudo rozkazu BLK:
     BLK UPDATE EXTERNAL
    
    UWAGA: Zostaną zapisane symbole tylko te, które zostały użyte w programie.

    Symbole external nie mają zdefiniowanej wartości tylko typ (.BYTE, .WORD, .LONG , .DWORD) np.:

    name EXT .BYTE
    
    label_name EXT .WORD
    
     .EXTRN label_name .WORD
    
    wait EXT .PROC (.BYTE delay)
    
    Symbol external z deklaracją procedury .PROC przyjmuje domyślnie typ .WORD, próba odwołania się do nazwy takiej etykiety zostanie potraktowana przez MADS jako próba wywołania procedury, więcej na temat wywołań procedur .PROC w rozdziale
    Procedury.

    W procesie asemblacji po napotkaniu odwołania do symboli external zawsze wstawiane są zera.

    Symbole external przydać się nam mogą wówczas gdy chcemy zasemblować program oddzielnie, niezależnie od reszty właściwego programu. W takim programie występują wówczas najczęściej odwołania do procedur, zmiennych które zostały zdefiniowane gdzieś indziej, na zewnątrz, a my nie znamy ich wartości tylko typ. W tym momencie z pomocą przychodzą symbole external, które umożliwiają asemblację takiego programu mimo braku właściwych procedur czy zmiennych.

    Innym zastosowaniem symboli external mogą być tzw. "pluginy" programy zewnętrzne połączone z programem głównym i realizujące dodatkowe czynności. Są to swoistego rodzaje biblioteki, wykorzystujące procedury programu głównego, rozszerzające jego funkcjonalność. Aby stworzyć taki plugin należałoby określić jakie procedury udostępnia program główny (ich nazwy i typ) oraz stworzyć procedurę odczytu pliku z symbolami external, ta procedura realizowałaby dołączanie pluginów do głównego programu.

    Poniżej format zapisu nagłówka w pliku z symbolami external typu B-YTE, W-ORD, L-ONG i D-WORD po wywołaniu przez BLK UPDATE EXTERNAL:

    HEADER        WORD ($FFEE)
    TYPE          CHAR (B-YTE, W-ORD, L-ONG, D-WORD)
    DATA_LENGTH   WORD
    LABEL_LENGTH  WORD
    LABEL_NAME    TEXT
    DATA          WORD .. .. ..
    
    HEADER zawsze o wartości $FFEE
    TYPE typ danych zapisany jest na bitach 0..6 tego bajtu i określa typ modyfikowanych adresów
    DATA_LENGTH to liczba 2-bajtowych danych (adresów) do modyfikacji
    LABEL_LENGTH to długość nazwy symbolu wyrażona w bajtach
    LABEL_NAME to nazwa symbolu w kodach ATASCII
    DATA właściwy ciąg danych służących modyfikacji głównego bloku relokowalnego. Pod wskazanym tutaj adresem należy odczytać wartość typu TYPE a następnie zmodyfikować na podstawie nowej wartości symbolu.

    Przykładem zastosowania symboli external i struktur .STRUCT jest przykładowa biblioteka prymitywów graficznych (PLOT, LINE, CIRCLE) z katalogu ..\EXAMPLES\LIBRARIES\GRAPHICS\LIB. Poszczególne moduły wykorzystują tutaj dość sporą liczbę zmiennych na stronie zerowej, jeśli chcemy aby adresy tych zmiennych były relokowalne musielibyśmy każdą z osobna zmienną zadeklarować jako symbol zewnętrzny przez EXT (lub .EXTRN). Wprowadziłoby to spore zamieszanie, na szczęście można to zrealizować w sposób bardziej kulturalny i z tylko jednym symbolem zewnętrznym dzięki strukturom danych .STRUCT. Za pomocą struktur definiujemy "mapę" zmiennych ZP, potem jeden symbol external ZPAGE typu .BYTE bo chcemy aby zmienne były na stronie zerowej. Teraz odwołując się do jakiejś zmiennej musimy zapisać to w sposób wymuszający relokowalność np. ZPAGE+ZP.DX i tak powstał moduł całkowicie relokowalny z możliwością zmiany adresu zmiennych w przestrzeni strony zerowej.


    Symbole publiczne

    Symbole publiczne udostępniają zmienne i procedury występujące w bloku relokowalnym pozostałej części asemblowanego programu. Dzięki symbolom publicznym możemy odwoływać się do zmiennych i procedur "zaszytych" np. w bibliotekach.

    Symbole publiczne mogą być używane w blokach relokowalnych .RELOC jak i w zwykłych blokach DOS-a.

    MADS sam rozpoznaje czy podana do upublicznienia etykieta jest zmienną, stałą czy też procedurą zadeklarowną przez .PROC, nie jest wymagana żadna dodatkowa informacja jak w przypadku symboli zewnętrznych.

    Symbole publiczne deklarujemy używając dyrektywy .PUBLIC:
     .PUBLIC label [,label2,...]
    
    Blok aktualizacji dla symboli publicznych wywołujemy używając pseudo rozkazu BLK:
     BLK UPDATE PUBLIC
    

    Poniżej format zapisu nagłówka w pliku z symbolami publicznymi po wywołaniu przez BLK UPDATE PUBLIC:

    HEADER        WORD ($FFED)
    LENGTH        WORD
    TYPE          CHAR (C-ONSTANT, V-ARIABLE, P-ROCEDURE)
    LABEL_LENGTH  WORD
    LABEL_NAME    TEXT
    ADDRESS       WORD
    
    MADS automatycznie dobiera odpowiedni typ do upublicznianej etykiety:
  • CONSTANT
  • etykiety nie poddające się relokacji
  • VARIABLE
  • etykiety poddające się relokacji
  • PROCEDURE
  • procedury zadeklarowane przez .PROC
    Jeśli symbol dotyczy procedury .PROC wówczas zapisywane są dodatkowe informacje, niezależnie od tego czy procedura miała czy też nie miała zadeklarowane parametry:
    PROC_CPU_REG  BYTE (bits 00 - A, 01 - X, 10 - Y)
    PROC_TYPE     BYTE (D-EFAULT, R-EGISTRY, V-ARIABLE)
    PARAM_COUNT   WORD
    

    Dla symboli dotyczących procedur .REG zapisywane są już teraz tylko typy tych parametrów w ilości PARAM_COUNT:

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

    Dla symboli dotyczących procedur .VAR zapisywane są typy parametrów i ich nazwy. PARAM_COUNT określa całkowitą długość tych danych:

    PARAM_TYPE    CHAR (B-YTE, W-ORD, L-ONG, D-WORD)
    PARAM_LENGTH  .WORD
    PARAM_NAME    TEXT
    ...
    ...
    
    HEADER zawsze o wartości $FFED
    LENGTH to liczba symboli zapisanych w bloku aktualizacji
    TYPE typ symbolu, V-ARIABLE lub P-ROCEDURE. Dla typu P zapisywane są dodatkowe informacje PROC_CPU_REG, PROC_TYPE, PARAM_COUNT, PARAM_TYPE.
    LABEL_LENGTH długość etykiety symbolu publicznego wyrażona w bajtach
    LABEL_NAME etykieta symbolu publicznego zapisana w kodach ATASCII
    ADDRESS adres przypisany symbolowi w bloku relokowalnym .RELOC. Ta wartość zostaje poddana relokacji poprzez dodanie do niej aktualnego adresu asemblacji.
    PROC_CPU_REG informacja o kolejności użycia rejestrów CPU dla procedury typu .REG
    PROC_TYPE typ procedury:
  • D-EFAULT domyślny typ wykorzystujący do przekazywania parametrów stos programowy MADS-a
  • R-EGISTRY parametry do procedury przekazywane są przez rejestry CPU (.REG)
  • V-ARIABLE parametry do procedury przekazywane są przez zmienne (.VAR)
  • PARAM_COUNT informacja o liczbie parametrów procedury (.REG) lub całkowitej długości danych zawierających informację o typie parametrów i ich nazwach (.VAR)
    PARAM_TYPE typ parametrów zapisany za pomocą znaków 'B', 'W', 'L', 'D'
    PARAM_LENGTH długość nazwy parametru (.VAR)
    PARAM_NAME nazwa parametru w kodach ATASCII (.VAR)


    Linkowanie .LINK

     .LINK 'filename'
    
    Dyrektywa .LINK wymaga podania jako parametru nazwy pliku do relokacji. Akceptowane są tylko pliki DOS Atari, pliki SDX nie są akceptowane.

    Jeśli adres ładowania pliku jest inny niż $0000 oznacza to że plik nie zawiera kodu relokowalnego, jednak może zawierać bloki aktualizacji dla symboli zewnętrznych i publicznych. Dyrektywa .LINK akceptuje pliki o dowolnym adresie ładowania, jednak relokacji poddawane są tylko te o adresie ładowania $0000, więcej szczegółów na temat budowy takiego pliku zostało zawartych w rozdziale Blok relokowalny .RELOC.

    Dyrektywa .LINK pozwala na łączenie kodu relokowalnego z nierelokowalnym. MADS na podstawie bloków aktualizacji dokonuje automatycznej relokacji takiego pliku. Uwzględniane są wszystkie 3 rodzaje bloków aktualizacji (ADDRESS, EXTERNAL, PUBLIC).

    Nie ma ograniczeń co do adresu pod którym umieszczany jest plik relokowalny.

    Jeśli blok relokowalny do działania wymaga stosu programowego MADS-a wówczas etykiety @STACK_POINTER, @STACK_ADDRESS, @PROC_VARS_ADR zostaną automatycznie zaktualizowane na podstawie nagłówka bloku .RELOC. Wymagane jest aby bloki .RELOC i program główny operowały na tym samym stosie programowym jeśli jest on konieczny.


    MNEMONIKI

    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 z komunikatem Illegal adressing mode. Aby "oszukać" assembler wystarczy dać przed kwadratowym nawiasem otwierającym '[' znak '+'.

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

    DETEKCJA CPU

    Detekcja CPU6502, CPU65816

    Przykład zaczerpnięty ze strony
    http://www.s-direktnet.de/homepages/k_nadj/cputest.html. Program potrafi zdiagnozować obecność jednego z mikroprocesorów: 6502, 65C02, 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		;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 przykład detekcji CPU, ogranicza się do określenia obecności mikroprocesora 6502 lub 65816. Program 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 zwięzłego i jakże sprytnego 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
    

    BANKI PAMIĘCI

    Pewnie każdemu, kto miał do czynienia z architekturą małego Atari, pojęcie "bank pamięci" kojarzy się z pamięcią rozszerzoną, podzieloną na banki wielkości 16kb, przełączane w obszar <$4000..$7FFF>.

    MADS też może to rozumieć w ten sposób (opcja OPT B+,
    Sprzętowe banki pamięci), jednak domyślnie rozumie to w sposób bardziej wirtualny (opcja OPT B-, Wirtualne banki pamięci).

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

    LMB # (Load Memory Bank)

    Ustawiamy licznik banków MADS-a na konkretną wartość z zakresu <$00..$FF> (BANK = value), np.
     lmb #0
     lmb #bank
     lmb #5 , $6500      ; tylko gdy OPT B+
    

    NMB (Next Memory Bank)

    Zwiększamy o 1 licznik banków MADS-a (BANK = BANK + 1).
     nmb
     nmb  $6500          ; tylko gdy OPT B+
    

    RMB (Reset Memory Bank)

    Zerujemy licznik banków MADS-a (BANK = 0).
     rmb
     rmb $3500           ; tylko gdy OPT B+
     rmb $8500           ; tylko gdy OPT B+
    

    MADS podczas asemblacji, każdej nowo zdefiniowanej etykiecie przypisuje aktualną wartość licznika banków. Programista może mieć wpływ na wartość licznika banków dzięki w/w pseudo rozkazom.

  • Etykiety z przypisanym licznikiem banków MADS-a = 0 są zasięgu globalnego.
  • Etykiety z przypisanym licznikiem banków MADS-a > 0 są zasięgu lokalnego.


    Wirtualne banki pamięci (OPT B-)

    W MADS przez pojęcie "wirtualny bank pamięci" rozumiany jest każdy obszar oznaczony przez nowo zdefiniowaną etykietę z przypisaną aktualną wartością licznika banków (domyślnie licznik banków jest wyzerowany). Czyli wirtualny bank pamięci to nie koniecznie obszar pamięci <$4000..$7FFF>, ale każda etykieta reprezentująca jakiś obszar kodu programu, której przypisany został kod (wartość licznika banków) z zakresu <$00..$FF> przy pomocy odpowiednich pseudo rozkazów oddanych na użytek programisty (NMB, RMB, LMB).

    Wyjątek stanowią bloki .RELOC w których nie można samodzielnie zmieniać licznika banków, realizuje to automatycznie MADS, który zwiększa licznik za każdym wywołaniem dyrektywy .RELOC. Licznik banków w takim przypadku przyjmuje wartości z zakresu <$0001..$FFF7>.

    Programista może odczytać wartość licznika banków, który został przypisany etykiecie za pomocą operatora '=' np.:
    label
    
     ldx #=label
    
    W w/w przykładzie do rejestru regX CPU zapisaliśmy wartość licznika banków pamięci MADS-a przypisany etykiecie LABEL.

    Innym przydatnym operatorem może być znak dwukropka ':' umieszczony na początku nazwy etykiety. Spowoduje to że MADS odczyta wartość takiej etykiety pomijając ograniczenia zasięgu, które wprowadza licznik banków MADS-a. Niekiedy może spowodować to komplikacje, np. jeśli wystąpiło więcej etykiet o tej samej nazwie ale w różnych obszarach lokalnych albo w obszarach o różnych wartościach licznika wirtualnych banków.
     lmb #5
    
    label5
     nop
    
     lmb #6
    
    label6
     nop
    
     lda :label5
    
    Brak operatora ':' na początku nazwy etykiety w rozkazie 'lda :label5' skończy się komunikatem błędu ERROR: Undeclared label LABEL5 (BANK=6).

    Wirtualnych banków pamięci można użyc do indeksowania tablicy zawierającej wartości dla PORTB. Takie też jest ich zastosowanie w przypadku wybrania opcji OPT B+.


    Sprzętowe banki pamięci (OPT B+)

    Ten tryb działania MADS-a można określić jako "czuły na banki" (BANK SENSITIVE).

    Sprzętowe banki pamięci są rozszerzeniem wirtualnych banków. Rozumiane są przez MADS jako banki rozszerzonej pamięci, włączane w obszar <$4000..$7FFF>. Działanie pseudo rozkazów NMB, RMB, LMB zostaje rozszerzone o wywołanie makra @BANK_ADD, które można znaleźć w katalogu ..\EXAMPLES\MACROS\.

    W tym trybie działania MADS potrzebuje deklaracji konkretnych makr:
     @BANK_ADD
     @BANK_JMP
    
    oraz potrzebuje definicji etykiet o nazwach:
    @TAB_MEM_BANKS
    @PROC_ADD_BANK
    
    Etykieta @TAB_MEM_BANKS definiuje adres tablicy, z której wartości będą przepisywane do rejestru PORTB odpowiedzialnego za przełączanie banków rozszerzonej pamięci. Możemy sobie ułatwić sprawę i skorzystać z gotowej procedury wykrywającej banki rozszerzonej pamięci dołączonej do MADS-a, plik ..\EXAMPLES\PROCEDURES\@MEM_DETECT.ASM.

    Etykieta @PROC_ADD_BANK używana jest przez makro @BANK_ADD i definiuje adres pod jakim znajdzie się kod programu przełączający bank pamięci rozszerzonej.

    Programista może odczytać wartość licznika banków, który został przypisany etykiecie za pomocą operatora '=', np.:
    label
    
     ldy #=label
    
    W w/w przykładzie do rejestru regY zapisaliśmy wartość licznika banków pamięci MADS-a przypisany etykiecie LABEL.

    Jeśli licznik banków MADS-a = 0 to:
  • kod programu musi znajdować się poza obszarem <$4000..$7FFF>
  • nowo zdefiniowane etykiety w tym obszarze są globalne
  • można odwoływać się do wszystkich zdefiniowanych etykiet bez ograniczeń, bez względu na numer banku
  • skok w obszar banku możliwy przy użyciu makra @BANK_JMP (..\EXAMPLES\MACROS\@BANK_JMP.MAC), parametr dla tego makra nie musi być poprzedzony operatorem ':'

    Jeśli licznik banków MADS-a > 0 to:
  • kod programu musi znajdować się w obszarze <$4000..$7FFF>
  • nowo zdefiniowane etykiety w tym obszarze są lokalne
  • można odwoływać się tylko do etykiet globalnych i tych zdefiniowanych w obszarze aktualnego banku
  • pseudo rozkaz LMB, NMB powoduje wykonanie makra @BANK_ADD, które włącza nowy bank rozszerzonej pamięci na podstawie licznika banków MADS-a oraz ustawia nowy adres asemblacji (domyślnie na $4000)
  • pseudo rozkaz RMB powoduje wyzerowanie licznika banków pamięci MADS-a oraz ustawienie nowego adresu asemblacji poza bankiem (domyślnie na $8000)
  • skok w obszar innego banku możliwy przy użyciu makra @BANK_JMP (..\EXAMPLES\MACROS\@BANK_JMP), parametr dla tego makra musi być poprzedzony operatorem ':'

    Przykładem wykorzystania tego trybu pracy MADS-a jest plik ..\EXAMPLES\XMS_BANKS.ASM. W tym przykładzie kod programu znajduje się w dwóch różnych bankach rozszerzonej pamięci i wykonuje się jakby był jedną całością.


  • HISTORIA

    1.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

    1.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
    

    1.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ł 'Opcje 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
    

    v1.6.5

    - dodany nowy przełącznik '-3' włączający 3 przebiegi zamiast 4-ech dla szybszej asemblacji
    - poprawione przetwarzanie zadeklarowanych struktur w pamięci MADS-a, kolejność wystąpienia w listingu nie ma już znaczenia
    - dodana możliwość połączenia dowolnej ilości linii listingu w jeden wiersz za pomocą znaku '\'

    v1.6.4

    - dodana dyrektywa .R, zwracająca wartość licznika pętli .REPT <0..maks>
    - dodana możliwość stosowania spacji w deklaracji typu danych np. dta a (...
    - usunięty zauważone błędy