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+
).
Makra czytane są tylko w pierwszym przebiegu assemblacji, dla n/w przykładu makra znajdujące się w pliku dołączanym przez
ICL
nie zostaną rozpoznane:
.macro test
icl 'dodatkowe_makra.mac'
.endm
Deklaracja makra
Makr dotyczą n/w pseudo rozkazy i dyrektywy:
name .MACRO [arg1, arg2 ...] ['separator'] ["separator"]
.MACRO name [(arg1, arg2 ...)] ['separator'] ["separator"]
.EXITM [.EXIT]
.ENDM [.MEND]
:[%%]parameter
:[%%]label
name .MACRO [(arg1, arg2 ...)] ['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).
Dopuszczalna jest lista z etykietami nazw argumentów jakie będą przekazywane do makra, taka lista może być ograniczona dodatkowo nawiasem okrągłym ( )
. Przedstawianie argumentów w postaci nazw etykiet ma na celu poprawienie przejrzystości kodu makra. W samym ciele makra można używać zamiennie nazw etykiet argumentów lub ich numerycznych odpowiedników.
.macro SetColor val,reg
lda :val
sta :reg
.endm
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 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
UAWAGA #1: Parametry ze znakiem operatora <
, >
zostają obliczone i dopiero ich wynik jest przekazywany do makra (podstawiany pod parametr).
UAWAGA #2: Jeśli parametrem makra jest licznik pętli #
, .R
(!!! pojedyńczy znak #
lub dyrektywa .R
a nie wyrażenie z udziałem tego znaku, tej dyrektywy !!!) wówczas do makra przekazywana jest wartość licznika pętli (podstawiana 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 warunkiem że taka etykiety istnieje (została zdefiniowana).
.EXITM [.EXIT]
Zakończenie działania makra. Powoduje bezwzględne zakończenie działania makra.
.ENDM [.MEND]
Przy pomocy dyrektywy .ENDM
lub .MEND
kończymy deklarację aktualnego makra. Nie ma możliwości użycia dyrektywy .END
jak ma to miejsce dla innych obszarów deklarowanych przez dyrektywy .LOCAL
, .PROC
, .ARRAY
, .STRUCT
, .REPT
:[%%]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 domyślnego 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 4095 (błąd Infinite recursion).
Przykład makra, które spowoduje przepełnienie stosu MADS-a:
jump .macro
jump
.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 wirtualny bank pamięci
.if :2<=$FFFF ; jeśli 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 wirtualnego 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 wirtualnego 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].