Mad-Assembler 1.2.9beta





Mad-Assembler (MADS)

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

Makro rozkazy XASM'a zastąpione są makrami (plik XASM_MACRO.ASM)


Wymagania

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

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

Procesor, taki aby uciągnął Windows'a :). Na Cel656 + Win2000 + 128MB RAM MADS mniej "mieli" dyskiem niż XASM.


Asemblacja

Syntax: MADS source [options]

/l[:filename]   Generate listing
/o:filename     Set object file name
/t[:filename]   List label table
/h[:filename]   Header file for CC65
/m:filename     File with macro definition
Domyślne nazwy plików to:

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


Zawartość pliku LST

Format listingu nie odbiega od tego znanego z XASM, jedyną zmianą jest dodanie przed adresem, numeru banku pamięci. Więcej o bankach w rozdziale "Banki pamięci, definiowanie i odwołania".
Mad-Assembler v0.8 by TeBe/Madteam
Source: D:\!Delphi\Masm\test.asm
     1 				* ---
     2 				; to jest program
     3
     4 = 00,9033		obraz equ $9033
     5 = 00,00A0		scr1 equ $a0
     6
     7 				 opt h-
     8 				 org $2000
     9
    10 00,2000 EA		main nop

Zawartość pliku LAB

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

W sumie są to trzy kolumny:

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

    Specjalne znaczenie w nazwach etykiet mają znaki:

  • etykieta zadeklarowana w makrze (dwa dwukropki) ::
  • etykieta zadeklarowana podczas wykonywania makra (dwukropek) :
  • etykieta zadeklarowana podczas wykonywania procedury (średnik) ;

    Wartość liczbowa, która występuje po znakach specjalnych, oznacza numer wywołania makra lub numer napotkanej deklaracji procedury (.PROC)
    Mad-Assembler v1.2.5beta by TeBe/Madteam
    Label table:
    00	0400	@STACK_ADDRESS
    00	00FF	@STACK_POINTER
    00	2000	MAIN
    00	2019	LOOP
    00	0080	HLP;1
    00	204B	THEX;1
    

    Zawartość pliku H

    Nie jestem pewien czy wszystko z tym plikiem jest OK, ale Eru chciał żeby coś takiego było więc jest :) Ma on być pomocny przy łączeniu ASM'a z CC65, czyli portem C dla małego Atari. Jego zawartośc może wyglądać tak (przykładowy plik TEST.ASM):
    #ifndef _TEST_ASM_H_
    #define _TEST_ASM_H_
    
    #define TEST_CPU65816 0x200F
    #define TEST_CPU6502 0x2017
    #define TEST_TEXT6502 0x201F
    #define TEST_TEXT65816 0x2024
    
    #endif
    

    Kody wyjścia

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

    Asemblacja na stronie zerowej

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

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

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

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

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

    Dlatego MADS umożliwia takie coś:
     org $20,$3080
     
     lda tmp+1
     
    tmp lda #$00
    
    Czyli asembluj od adresu $20, ale załaduj pod adres $3080.

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


    Dostępne pseudo rozkazy

     EQU value
      =  value
     OPT [chlos][+-]
     ORG adres[,adres2]
     INS 'filename'[,ofset][,length]
     ICL 'filename'
     DTA [ablht](value1,value2...)
     DTA [cd]'string'
     RUN adres
     INI adres
     END
     
     :repeat
     
     NMB
     RMB
     LMB value
     
     .IF [IFT] expression
     .ELSE [ELS]
     .ELSEIF [ELI] expression
     .ENDIF [EIF]
     
     .PRINT 'string'["string"] lub .PRINT expression 
     .ERROR [ERT] 'string'["string"] lub .ERROR [ERT] expression
     
     .MACRO
     .ENDM
     :parameter
     
     .EXIT 
    
     .PROC
     .ENDP
     
     .WORD
     .BYTE
     .LONG 
    
    Czyli w większości po staremu, chociaż parę zmian zaszło. W przypadku cudzysłowów można używać ' ' lub " ".

    :repeat

    Examples: 
               :4 asl @
               :2 dta a(*)
    
    Znak ':' określa liczbę powtórzeń linii (w przypadku makr określa numer parametru).

    OPT

         c+  włącz obsługę CPU 65816 (16bit)
         c-  włącz obsługę CPU 6502	 (8bit)
         h+  zapisuj nagłówek pliku dla DOS
         h-  nie zapisuj nagłówka pliku dla DOS
         l+  zapisuj listing do pliku (LST)
         l-  nie zapisuj listingu (LST)
         o+  zapisuj wynik asemblacji do pliku wynikowego (OBX)
         o-  nie zapisuj wyniku asemblacji do pliku wynikowego (OBX)
         s+  drukuj listing na ekranie
         s-  nie drukuj listingu na ekranie    
    
    Wszystkie przełączniki OPT możemy używać w dowolnym miejscu listingu, czyli np. możemy włączyć zapis listingu w linii 12, a w linii 20 wyłączyć itd., wówczas plik z listingiem będzie zawierał tylko linie 12..20.

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

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

    ORG

         adr          asembluj od adresu ADR      
         adr1,adr2    asembluj od adresu ADR1, ustaw nagłówek pliku na ADR2
    

    DTA

         b  wartość typu BYTE
         a  wartość typu WORD
         l  młodszy bajt wartości (BYTE)
         h  starszy bajt wartości (BYTE)
         t  wartość 24bit (TRIPLE, TRIBYTE)
         c  ciąg znaków ATASCII ograniczony apostrofem '' lub "", znak * na końcu spowoduje
            invers wartości ciągu, np. dta c'abecadlo'*
         d  ciąg znaków INTERNAL ograniczony apostrofem '' lub "", znak * na końcu spowoduje
            invers wartości ciągu, np. dta d'abecadlo'*
    

    NMB (Next Memory Bank)

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

    RMB (Reset Memory Bank)

    Ustawia numer banku na zero (BANK = 0).

    LMB (Load Memory Bank)

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

    .BYTE .WORD .LONG

    Oznaczenia dopuszczalnych typów parametrów w deklaracji parametrów procedury. Wymagane i możliwe jest ich użycie tylko w deklaracji parametrów procedury lub deklaracji struktury (struktury nie są jeszcze zaimplementowane).


    Procedury, deklaracja

    Procedur dotyczą n/w pseudo rozkazy:
     name .PROC [{.Typ1 Par1 .Typ2 Par2 ...}]
     .EXIT
     .ENDP
    
    MADS wymaga dla procedur zadeklarowania dwóch etykiet, o konkretnych nazwach (adres stosu, wskaźnik stosu):
     @STACK_ADDRESS
     @STACK_POINTER
    
    Brak deklaracji dla w/w etykiet i próba użycia struktury .PROC spowoduje wystąpienie błędu 'Undeclared label .STACK_ADDRESS' lub 'Undeclared label .STACK_POINTER'.

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

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

    Jeśli chcemy przekazać parametry do procedury, musimy je zadeklarować. Deklaracja parametrów procedury mieści się pomiędzy nawiasami klamrowymi { }. Dostępne są trzy typy parametrów:
  • .BYTE (8-bit)
  • .WORD (16-bit)
  • .LONG (24-bit)

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

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

    .EXIT

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

    Aby tego dokonać MADS po napotkaniu w strukturze .PROC rozkazu .EXIT wymusza wykonanie makra .EXITPROC, które użytkownik sam może zaprojektować, albo skorzystać z dołączonego do MADS'a, które ma następującą postać:
    .ExitProc .macro
    
      lda @STACK_POINTER
      sec
      sbc #:1            ; parametr przekazuje MADS
      sta @STACK_POINTER
      
      rts
    
     .endm
    

    .ENDP

    Zakończenie deklaracji procedury.


    Makra, deklaracja

    Makr dotyczą n/w pseudo rozkazy:
     name .MACRO ['separator'] ["separator"]
     .EXIT
     .ENDM
     :parameter
    

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

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

    Za słowem .MACRO może wystąpić deklaracja separatora (pomiędzy apostrofem pojedyńczym lub podwójnym) i trybu przekazywania parametrów do makra (pojedyńczy apostrof bez zmian, podwójny apostrof z rozbijaniem parametrów). Domyślnym separatorem, rozdzielającym parametry przekazywane do makra jest znak przecinka ','.
  • 'separator'
    Pojedyńczy apostrof określa separator, który będzie używany do oddzielenia parametrów przy wywołaniu makra.
  • "separator"
    Podwójny apostrof określa separator, który będzie używany do oddzielenia parametrów przy wywołaniu makra. Parametry zostaną automatycznie rozbite na dwa elementy: tryb adresacji i argument, np.:
     przyklad #12 200 <30
    
    przyklad .macro " "
    
      .endm
    
    Makro przyklad ma zadeklarowany separator-spację oraz podwójny apostrof ",czyli po wywołaniu makra parametry zostaną rozłożone na:
     przyklad '#' 12 ' ' 200 '#' 0
    
    Tylko dla znaków '<' i '>' zostanie wyliczona wartość parametru. Przykład wykorzystania tak zadeklarowanych makr w pliku z makro rozkazami XASM'a XASM_MACRO.ASM


    .EXIT

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

    .ENDM

    Zakończenie deklaracji makra.

    :parameter

    Parametr jest liczbą decymalną dodatnią (> = 0), poprzedzoną znakiem dwukropka ':'. 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 ma specjalne znaczenie, zawiera liczbę przekazanych parametrów. Z jego pomocą możemy sprawdzić czy wymagana liczba parametrów została przekazana do makra:
      .IF :0<2||:0>5 
       .ERROR "Wrong number of arguments" 
      .ENDIF 
    

    Przykład makra:
     load_word .macro
       lda <:1
       sta :2
       lda >:1
       sta :2+1   
     .endm
    


    Procedury, wywołanie

    Procedurę wywołujemy poprzez jej nazwę (identycznie jak makro), po niej mogą wystąpić parametry, rozdzielone znakiem przecinka ','.

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

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

    Możliwe są trzy sposoby przekazania parametru:
  • przez wartość (#)
  • przez wartość spod adresu (bez znaku poprzedzającego)
  • przez akumulator (@)

    Przykład wywołania procedury:
     proc_name @,#$166,$A400
    
    MADS po napotkaniu wywołania procedury, wymusza wykonanie makra @CALL z parametrami wyliczonymi na podstawie deklaracji procedury (rozbija każdy parametr na trzy składowe: tryb adresacji, typ parametru, wartość parametru w zapisie decymalnym).
    @CALL proc_name,'@','B',0,'#','W',358,' ',W,41984
    
    Makro @CALL odłoży na stos zawartość akumulatora, następnie wartość $166, następnie wartość spod adresu $A400.

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

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

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

    Wyjście z procedury .PROC nastąpi po napotkaniu rozkazu .EXIT, przez co MADS wymusi wykonanie makra .EXITPROC, które użytkownik sam może zaprojektować, albo skorzystać z dołączonego do MADS'a.

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


    Parametry procedur, odwołania

    name .PROC {.WORD name_par1 .BYTE name_par2}
    
    Zadeklarowanie parametrów procedury spowoduje automatyczne przypisanie im wartości.

    Etykiety parametrów w naszym w/w przykładzie przyjmą automatycznie następujące wartości:
    name_par1 = @STACK_ADDRESS-liczba_bajtów_zajętych_przez_parametry    (2)
    name_par2 = @STACK_ADDRESS-liczba_bajtów_zajętych_przez_parametry+2  (1)
    
    Aby móc odwołać się do parametru z poziomu procedury, musimy użyć wskaźnika stosu @STACK_POINTER, np.:
     ldx @stack_pointer
     
     lda name_par1,x
     clc
     adc <$4000
     sta WYNIK
     
     lda name_par1+1,x
     adc >$4000
     sta WYNIK+1
    
    Czyli dokonaliśmy operacji WYNIK=NAME_PAR1+$4000

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

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


    Makra, wywołanie

    Makro wywołujemy poprzez jego nazwę, po niej mogą wystąpić parametry makra, rozdzielone separatorem którym jest domyślnie znak przecinka ','. Liczba parametrów może być nieskończenie duża. Jeśli przekazana liczba parametrów jest mniejsza od liczby parametrów używanych, wówczas zostanie podstawiona wartość $FFFFFFFF pod brakujący parametr.
     macro_name [Par1,Par2,Par3 ...]
    
    Jeśli wpiszemy rozkaz składający się z siedmiu znaków, a czwartym znakiem będzie znak dwukropka':', np. 'lda:sta' wówczas znak ':' zostanie zastąpiony znakiem zapytania '?'. Tak zmodyfikowany rozkaz zostanie potraktowany jako próba wywołania makra. Jeśli nie zadeklarowaliśmy makra o nazwie 'lda?sta' wystąpi błąd. W ten sposób można zastąpić składnie XASM'a makrami MADS'a. Wszelkie deklaracje etykiet w obrębie makra są typu lokalnego. Aby odwołać się do etykiety spoza makra należy poprzedzić ją znakiem dwukropka ':'.

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

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

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

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


    Banki pamięci, definiowanie i odwołania

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

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

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

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

    NMB (Next Memory Bank)

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

    RMB (Reset Memory Bank)

    Ustawia numer banku na zero (BANK = 0).

    LMB (Load Memory Bank)

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


    Aby załadować jakieś dane do rozszerzonej pamięci, np. dla rozszerzenia RAMBO do banku o kodzie $E3 piszemy:
     org $2000
     lda #$e3
     sta $d301
     rts
     ini $2000
    
     nmb            ; bank = bank + 1   (next memory bank)
     
     org $4000
    
     icl 'program_w_dodatkowym_banku_pamieci.asm'
    
    albo tak:
     org $2000
     lda #$e3
     sta $d301
     rts
     ini $2000
    
     lmb $e3        ; bank = $E3        (load memory bank)
     
     org $4000
    
     icl 'program_w_dodatkowym_banku_pamieci.asm'
    
    Czyli musimy zatroszczyć się o to aby do PORTB wpisać konkretny kod banku rozszerzonej pamięci, zanim blok danych zostanie wczytany w obszar $4000..$7FFF lub inny. MADS zapisuje plik wynikowy w formacie Atari DOS, który nie przewiduje specjalnych nagłówków dla bloków ładujących się do pamięci rozszerzonej.


    Operatory

    Binary operators:
    
    +   Addition
    -   Subtraction
    *   Multiplication
    /   Division
    %   Remainder
    &   Bitwise and
    |   Bitwise or
    ^   Bitwise xor
    <<  Arithmetic shift left
    >>  Arithmetic shift right
    =   Equal
    ==  Equal (same as =)
    <>  Not equal
    !=  Not equal (same as <>)
    <   Less than
    >   Greater than
    <=  Less or equal
    >=  Greater or equal
    &&  Logical and
    ||  Logical or
    
    
    Unary operators:
    
    +  Plus (does nothing)
    -  Minus (changes sign)
    ~  Bitwise not (complements all bits)
    !  Logical not (changes true to false and vice versa)
    <  Low (extracts low byte)
    >  High (extracts high byte)
    ^  High 24bit (extracts high byte)
    =  Extracts memory bank
    :  Extracts global variable value
    
    Operator precedence:
    
    first []              (brackets)
     + - ~ < >            (unary)
     * / % & << >>        (binary)
     + - | ^              (binary)
     = == <> != < > <= >= (binary)
     !                    (unary)
     &&                   (binary)
    last  ||              (binary)
    
    

    Dostępne rozkazy 6502

       LDA   LDX   LDY   STA   STX   STY   ADC   AND  
       ASL   SBC   JSR   JMP   LSR   ORA   CMP   CPY  
       CPX   DEC   INC   EOR   ROL   ROR   BRK   CLC  
       CLI   CLV   CLD   PHP   PLP   PHA   PLA   RTI  
       RTS   SEC   SEI   SED   INY   INX   DEY   DEX  
       TXA   TYA   TXS   TAY   TAX   TSX   NOP   BPL  
       BMI   BNE   BCC   BCS   BEQ   BVC   BVS   BIT  
    
    Możliwe jest użycie rozszerzenia mnemonika po znaku kropki '.':
       .b lub .z          BYTE
       .a lub .w lub .q   WORD
    
    np.
       lda.w $80   ; zapisze AD 80 00
       lda   $80   ; zapisze A5 80     
    

    Dostępne rozkazy 65816

    Oczywiście dostępne są rozkazy 6502, a oprócz nich:
       STZ   SEP   REP   TRB   TSB   BRA   COP   MVN  
       MVP   PEA   PHB   PHD   PHK   PHX   PHY   PLB  
       PLD   PLX   PLY   RTL   STP   TCD   TCS   TDC  
       TSC   TXY   TYX   WAI   WDM   XBA   XCE   INA
       DEA
    
    Możliwe jest użycie rozszerzenia mnemonika po znaku kropki '.':
       .b lub .z          BYTE
       .a lub .w lub .q   WORD
       .t lub .l          TRIPLE,TRIBYTE,LONG (24bit)
    
    np.
       lda.w #$8047   ; zapisze A9 47 80
       lda   #$80     ; zapisze A9 80
    
    Wyjątki stanowią rozkazy n/w, którym nie można zmienić rozmiaru rejestru w adresowaniu absolutnym:

    #$xx

       SEP   REP   COP
    

    #$xxxx

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

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


    Etykiety

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

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

    Przykład deklaracji etykiety:
    ?nazwa   EQU  $A000
    nazwa     =   20
    @?nazwa  EQU  'a'+32
    

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

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

    Zapis z użyciem przecinka ',' odwołania do struktury, czy rozszerzenia mnemonika jest niezbyt udany:
      lda nazwa_stuktury,pole,x
      
      lda,w #$00
    
    Natomiast z użyciem kropki '.' zapis jest już przejrzysty i jednoznaczny, pozatym zgodny z zapisem znanym z C++, Delphi na PC:
      lda nazwa_stuktury.pole,x
      
      lda.w #$00
    

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

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

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


    Etykiety lokalne

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

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


    Komentarze

    Ogólnie komentarz oznaczamy poprzez znak '*' lub ';'
    ; to jest komentarz
                     ; to jest komentarz
     lda #0      ; to jest komentarz
     lda #0     * to tez jest komentarz
     
     lda #0      i to tez jest komentarz, chociaż nie ma znaku ';' lub '*'
    
    Najbezpieczniej jest używać średnika ';' ponieważ znak '*' ma też inne znaczenia, może oznaczać operacje mnożenia, czy też aktualny adres podczas asemblacji. Średnik natomiast dedykowany jest tylko i wyłącznie do oznaczenia komentarza.


    Definicje

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

    Etykiety definiujemy używając znaku przypisania:
      =
    
    Z tą różnicą że etykieta nie występuje na początku wiersza, musi poprzedzić ją minimum jedna spacja.

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


    Detekcja CPU6502, CPU65816

    Zaprezentowany programik po disasemblacji inaczej wygląda dla 6502, inaczej dla 65816. 6502 rozkaz 'inc @' uzna za 'nop', rozkaz 'xba' uzna za 'sbc #'. Dzięki takiej "przeźroczystości" możemy być pewni że program nie wykona żadnej nielegalnej operacji i uczciwie rozpozna właściwy CPU. Pomysłodawcą tego testu jest Ullrich von Bassewitz.
     org $2000
    
     opt c+
    
     lda #0
     
     inc @
     
     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
    


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

  • te same kody wyjścia
  • ta sama składnia, oprócz łączenia dwóch rozkazów w linii poprzez znak ':' (znak ':' w takim przypadku jest zastępowany znakiem '?')
  • wszystkie makro rozkazy dały zastąpić się makrami (plik XASM_MACRO.ASM)
  • mała różnica w ORG, np. 'ORG adres1,adres2'
  • pseudo rozkaz .IF (mads) można zamiennie używać z pseudo rozkazem IFT (xasm)
  • pseudo rozkaz .ELSE (mads) można zamiennie używać z pseudo rozkazem ELS (xasm)
  • pseudo rozkaz .ELSEIF (mads) można zamiennie używać z pseudo rozkazem ELI (xasm)
  • pseudo rozkaz .ENDIF (mads) można zamiennie używać z pseudo rozkazem EIF (xasm)
  • MADS nie akceptuje składni typu 'lda (203),0'
  • w trybach indeksowych znak '+' lub '-' zwiększa lub zmniejsza rejestr, np.
     lda $2000,x+
     
     to odpowiednik
     
     lda $2000,x
     inx
    
  • jeśli jednak umieścimy wartość za znakiem '+' lub '-' wówczas zmienimy o tą wartość główny argument (działa tylko w trybie absolutnym indeksowym), np.:
     lda $2000,x+2
     
     to odpowiednik
     
     lda $2002,x
    


    Historia

    
    v1.2.9beta
    - poprawione błędy zauwazone przez Rastera (przyklad ..\RMT_PLAYER\MUSIC.A65 asembluje teraz poprawnie)
    - obsługa składni XASM'a typu 'lda:sta' za pomoca makr (przykłady w XASM_MACRO.ASM)
    - dodana obsługa pseudo rozkazu .ELSEIF oraz jego odpowiednika ELI
    - dodana obsługa pseudo rozkazu ERT (ERT expression)
    - dodany przełącznik '/m:filename' który pozwoli wczytać plik z definicjami makr
    
    v1.2.8beta
    - w wersji 1.2.7beta byla blednie asemblowana linia w stylu dta b(value1),b(value2),...
    - naprawione problemy z dzialaniem przelacznikow /l: /o: /t:
    - dodany przelacznik /h ktory tworzy plik *.h dla cc65 (wersja testowa)
    - rozbudowana deklaracja makr o separator i tryb dzialania (przyklad XASM_MACRO.ASM)
    - zmiana w deklaracji parametrow procedury, zamiast znakow '( )' sa teraz '{ }'
    - dodane kody wyjscia, takie same jak w XASM
    - pseudo rozkaz .IF ma teraz odpowiednik w pseudo rozkazie IFT
    - pseudo rozkaz .ELSE ma teraz odpowiednik w pseudo rozkazie ELS
    - pseudo rozkaz .ENDIF ma teraz odpowiednik w pseudo rozkazie EIF
    - dodana obsluga skladni w stylu LDA ADRES,Y[+-] (LDA (BYTE),Y[+-]) oraz LDA ADRES,Y[+-]expression
    - dodane przyklady w wersji dla MADS ProTracker'a i Inertii