Procedury
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 tej samej 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
) lub przez zmienne (dyrektywa .VAR
).
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ęcie takiej procedury podczas asemblacji możliwe jest poprzez podanie parametru do MADS-a w linii poleceń -x 'Exclude unreferenced procedures'
.
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
Procedur dotyczą n/w dyrektywy:
name .PROC [(.TYPE PAR1 .TYPE PAR2 ...)] [.REG] [.VAR]
.PROC name [,address] [(.TYPE PAR1 .TYPE PAR2 ...)] [.REG] [.VAR]
.ENDP [.PEND] [.END]
name .PROC [(.TYPE Par1,Par2 .TYPE Par3 ...)] [.REG] [.VAR]
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 ) .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 jest 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
name .PROC ( .WORD src+1, dst+1 ) .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.
Podobnie jak w przypadku bloku .LOCAL
mamy możliwość określenia nowego adresu asemblacji dla bloku .PROC
, np.:
.PROC label,$8000
.ENDP
.PROC label2,$a000 (.word ax) .reg
.ENDP
W przypadku procedur wykorzystujących stos programowy po zakończeniu procedury przez .ENDP
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 wygenerowany 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 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 programowy 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ć.