MAD-ASSEMBLER 1.8.2
Tebe/Madteam (01.06.2008)
WPROWADZENIE
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 :)
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.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/
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 kompilację może wyglądać następująco (wielkość liter w nazwach parametrów ma znaczenie):fpc -Mdelphi -v mads.pas
RÓŻNICE I PODOBIEŃSTWA POMIĘDZY XASM I MADS
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.
lda $2000,x+ -> lda $2000,x
inx
lda $2000,x+2 -> lda $2002,x
SPOSÓB UŻYCIA
Syntax: mads source [switches] -b:address Generate binary file at specific address -c Label case sensitivity -d:label=value Define a label -f CPU command at first column -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:
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
/i:"c:\program files" /i:c:\temp /i:"d:\atari project"
D:\!Delphi\Masm\test.asm (29) ERROR: Missing .PROCTeraz wystarczy kliknąć dwukrotnie linię z tym komunikatem, a kursor edytora ustawi się w linii z błędem.
3 = bad parameters, assembling not started 2 = error occured 0 = no errorsKomunikaty ostrzeżenia nie powodują zmiany wartości kodu wyjścia.
Format listingu nie odbiega od tego znanego z XASM, jedyną zmianą jest dodanie przed adresem, numeru wirtualnego banku pamięci (pod warunkiem że numer banku <>0). Więcej o wirtualnych bankach w rozdziale Banki pamięci.
3
4 = 01,9033 obraz equ $9033
5 = 01,00A0 scr1 equ $a0
6
7
8 01,2000 EA main nop
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:
$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:
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
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
MAKRO 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 ADD, SUB ADB, SBB ADW, SBW PHR, PLR INW, INL, IND, DEW, DEL, DED MVA, MVX, MVY MWA, MWX, MWY CPB, CPW, CPL, CPDZadaniem makro rozkazów jest skrócenie czasu pisania programu i samego listingu. Makro rozkazy zastępują grupy często powtarzających się mnemoników.
REQ, RNE, RPL, RMI, RCC, RCS, RVC, RVS
W/w makro 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 makro 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 makro 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.
ADD, SUB
W/w makro rozkazy realizują odpowiednio zwiększenie/zmniejszenie bajtu pamięci bez zapisywania wyniku (wynik w akumulatorze CPU).
ADD -> CLC SUB -> SEC
-> ADC ... -> SBC ...
ADB, SBB
W/w makro rozkazy realizują odpowiednio zwiększenie/zmniejszenie bajtu pamięci z zapisaniem wyniku.
ADB SRC #$40 -> LDA SRC ADB A B C -> LDA A
-> CLC -> CLC
-> ADC #$40 -> ADC B
-> STA SRC -> STA C
SBB SRC #$80 -> LDA SRC SBB A B C -> LDA A
-> SEC -> SEC
-> SBC #$80 -> SBC B
-> STA SRC -> STA C
ADW, SBW
W/w makro rozkazy realizują odpowiednio zwiększenie/zmniejszenie słowa pamięci z zapisaniem wyniku.
ADW SRC #$40 -> CLC ADW A B C -> CLC
-> LDA SRC -> LDA A
-> ADC #$40 -> ADC B
-> STA SRC -> STA C
-> SCC -> LDA A+1
-> INC SRC+1 -> ADC B+1
-> STA C+1
ADW SRC #$40 SRC -> CLC
-> LDA SRC
-> ADC #$40
-> STA SRC
-> LDA SRC+1
-> ADC #$00
-> STA SRC+1
SBW SRC #$4080 -> SEC SBW A B C -> SEC
-> LDA SRC -> LDA A
-> SBC <$4080 -> SBC B
-> STA SRC -> STA C
-> LDA SRC+1 -> LDA A+1
-> SBC >$4080 -> SBC B+1
-> STA SRC+1 -> STA C+1
PHR, PLR
W/w makro rozkazy swoimi nazwami nawiązują do odpowiednich mnemoników 6502, odpowiednio PHA, PLA, realizują odkładanie na stosie i zdejmowanie ze stosu rejestrów A,X,Y.
PHR -> PHA PLR -> PLA
-> TXA -> TAY
-> PHA -> PLA
-> TYA -> TAX
-> PHA -> PLA
INW, INL, IND, DEW, DEL, DED
Makro rozkazy INW, INL, IND realizują zwiększenie odpowiednio słowa pamięci (.WORD), długiego słowa pamięci (.LONG), podwójnego słowa pamięci (.DWORD).
Makro rozkazy DEW, DEL, DED realizują zmniejszenie odpowiednio słowa pamięci (.WORD), długiego słowa pamięci (.LONG), podwójnego słowa pamięci (.DWORD) i wykorzystują w tym celu akumulator CPU (zawartość akumulatora może ulec zmianie po wykonaniu makro rozkazów DEW, DEL, DED).
inw dest -> inc dest -> inc dest
-> bne skip -> sne
-> inc dest+1 -> inc dest+1
-> skip ->
dew dest -> lda dest -> lda dest
-> bne skip -> sne
-> dec dest+1 -> dec dest+1
-> skip dec dest -> dec dest
MVA, MVX, MVY
Makro rozkazy MVA, MVX, MVY służą do przenoszenia bajtów (.BYTE) pamięci przy pomocy rejestrów CPU odpowiednio A, X, Y. Użycie opcji OPT R+ pozwala na potencjalne skrócenie kodu wynikowego dla następujących po sobie makro rozkazów MVA, MVX, MVY.
lda src -> mva src dst
sta dst ->
ldy $10,x -> mvy $10,x $a0,x
sty $a0,x ->
ldx #$10 -> mvx #$10 dst
stx dst ->
MWA, MWX, MWY
Makro rozkazy MWA, MWX, MWY służą do przenoszenia słów (.WORD) pamięci przy pomocy rejestrów CPU odpowiednio A, X, Y. Użycie opcji OPT R+ pozwala na potencjalne skrócenie kodu wynikowego dla następujących po sobie makro rozkazów MWA, MWX, MWY.
ldx <adr -> mwx #adr dst
stx dst ->
ldx >adr ->
stx dst+1 ->
mwa #0 $80 -> lda #0 mwy #$3040 $80 -> ldy <$3040
-> sta $80 -> sty $80
-> sta $81 -> ldy >$3040
-> sty $81
mwa ($80),y $a000,x -> lda ($80),y
-> sta $a000,x
-> iny
-> lda ($80),y
-> sta $a001,x
CPB, CPW, CPL, CPD
Makro rozkazy CPB, CPW, CPL, CPD realizują porównanie wartości odpowiednich typów, odpowiednio .BYTE, .WORD, .LONG, .DWORD.
PSEUDO ROZKAZY
IFT [.IF] expression ELS [.ELSE] ELI [.ELSEIF] expression EIF [.ENDIF] label EQU address label = address label EXT address OPT [bcfhlmorst][+-] 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).
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.
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.
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 '#'.
.macro test :2 lsr @ .endmWówczas dla w/w przykładu 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 '+'.
.macro test :+2 lsr @ .endm
Teraz znak dwukropka ':' zostanie prawidłowo zinterpretowany jako :repeat
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)
f+ plik wynikowy w postaci jednego bloku (przydatne dla carta)
f- plik wynikowy w postaci blokowej (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)
r+ optymalizacja długości kodu dla MVA, MVX, MVY, MWA, MWX, MWY
r- bez optymalizacji długości kodu dla MVA, MVX, MVY, MWA, MWX, MWY (default)
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 opcje 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).
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:
opt h-
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.
Przykład nagłówka dla pliku w postaci jednego bloku, asemblowanego od adresu $2000, w nagłówku podany adres początkowy i adres końcowy bloku.Example: opt h-f+ ORG [a(start), a(over-1)],$2000 start nop .ds 128 nop over
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 Przełączniki 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 binarnegoJeśli wartość LENGTH nie została określona, domyślnie plik binarny zostanie odczytany aż do końca.
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 Przełączniki assemblera).
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'*
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.
W/w pseudo rozkazy i dyrektywy wpływają na przebieg asemblacji (można ich używać zamiennie).
DYREKTYWY
Dyrektywa .SYMBOL to odpowiednik pseudo rozkazu SMB z tą różnicą że nie trzeba podawać symbolu, symbolem jest etykieta label. Dyrektywę .SYMBOL można umieszczać w dowolnym miejscu bloku relokowalnego SDX (BLK RELOC) w przeciwieństwie do SMB.
Jeśli wystąpiła dyrektywa .SYMBOL zostanie wygenerowany odpowiedni blok aktualizacji:
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]
.SYMBOL label
.IF [IFT] expression
.ELSE [ELS]
.ELSEIF [ELI] expression
.ENDIF [EIF]
.LOCAL label
.ENDL, [.LEND]
.REPT expression [,parameter1, parameter2, ...]
.ENDR, [.REND]
.R
.PAGES [expression]
.ENDPG, [.PGEND]
.STRUCT label
.ENDS, [.SEND]
.ARRAY label count type [= default_value]
.ENDA, [.AEND]
.PROC label
.ENDP, [.PEND]
.REG, .VAR
.MACRO label
.ENDM, [.MEND]
:[%%]parameter
.EXITM [.EXIT]
.VAR var1[=value],var2[=value]... (.BYTE|.WORD|.LONG|.DWORD)
.END
.EN
.PRINT [.ECHO] 'string1','string2'...,value1,value2,...
.ERROR [ERT] 'string'["string"] lub .ERROR [ERT] expression
.BYTE
.WORD
.LONG
.DWORD
.OR
.AND
.XOR
.NOT
.LO (expression)
.HI (expression)
.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
.ADR label
.LEN label
.DEF label [= expression]
.IFDEF label
.IFNDEF label
.USING, [.USE] proc_name, local_name
.NOWARN
.GET [index] 'filename'["filename"][*][+-value][,+-ofset[,length]]
.PUT [index] = value
.SAV [index] ['filename',] length
.EXTRN label [,label2,...] type
.PUBLIC, [.GLOBAL], [.GLOBL] label [,label2,...]
.RELOC [.BYTE|.WORD]
.LINK 'filename'
.SYMBOL label
BLK UPDATE NEW LABEL 'LABEL'
Więcej na temat deklaracji symboli SDX w rozdziale Definiowanie symbolu SMB.
.REPT expression [,parameter1, parameter2, ...]
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>, po liczbie powtórzeń opcjonalnie mogą wystąpić parametry. W przeciwieństwie do makr parametry dla .REPT zawsze są najpierw obliczane i dopiero ich wynik jest podstawiany (tą właściwość można wykorzystać do definiowania nowych etykiet). Z parametrów w bloku .REPT korzystamy podobnie jak z parametrów w bloku .MACRO. Koniec bloku .REPT definiuje dyrektywa .ENDR, przed którą nie powinna znajdować się żadna etykieta.
Example: .rept 12, #*2, #*3 ; bloki .REPT możemy łączyć z :rept :4 dta :1 :4 dta :2 .endr .rept 9, # ; definiujemy 9 etykiet label0..label8 label:1 mva #0 $d012+# .endr
.PAGES [expression]
Dyrektywa .PAGES pozwala określić 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 ????.
Example: org $4000 .pages $40 ... ... .endpg
.END
Dyrektywa .END może być zamiennie używana z dyrektywami .ENDP, .ENDM, .ENDS, .ENDA, .ENDL, .ENDR, .ENDPG, .ENDW, .ENDT
Store byte values in memory. ASCII strings can be specified by enclosing the string in either single or double quotes.
Example:
.HE 0 55 AA FF
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.
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.
Dopuszczalny zakres wartości dla INDEX = <0..65535>.
W/w dyrektywy i pseudo rozkazy wpływają na przebieg asemblacji (można ich używać zamiennie), np.:
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).
DYREKTYWY GENERUJĄCE KOD 6502
Dyrektywy #IF, #ELSE i #END pozwalają na wygenerowanie kodu maszynowego CPU 6502 instrukcji warunkowej IF dla wyznaczonego bloku programu, możliwe jest ich zagnieżdżanie. Dopuszczalne są wszystkie typy .BYTE, .WORD, .LONG, .DWORD, możliwe jest łączenie większej ilości warunków przy pomocy dyrektyw .OR i .AND, nie ma możliwości określenia kolejności wartościowania poprzez nawiasy.
Jeżeli wyrażenie ma wartość różną od zera (TRUE), to zostanie wykonywany blok programu występujący po dyrektywie #IF. Blok takiego programu automatycznie kończony jest instrukcją 'JMP' realizującą skok do następnej instrukcji programu za dyrektywą #END w przypadku występowania bloku #ELSE.
Dyrektywy #WHILE i #END pozwalają na wygenerowanie kodu maszynowego CPU 6502 pętli dla wyznaczonego bloku programu, możliwe jest ich zagnieżdżanie. Dopuszczalne są wszystkie typy .BYTE, .WORD, .LONG, .DWORD, możliwe jest łączenie większej ilości warunków przy pomocy dyrektyw .OR i .AND, nie ma możliwości określenia kolejności wartościowania poprzez nawiasy.
Sekwencja działań przy wykonywaniu dyrektywy #WHILE jest następująca:
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ą #END
W przeciwieństwie do dwu-przebiegowych asemblerów takich jak QA i XASM, MADS jest wielo-przebiegowy. Co to daje ?
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.
Innym sposobem na wymuszenie rozkazu strony zerowej jest użycie nawiasów klamrowych { } np.
Dlatego MADS umożliwia takie coś:
Podsumowując:
Asembluj od adresu adres1, umieść w pamięci od adresu adres2.
Taki ORG zawsze spowoduje stworzenie nowego bloku w pliku.
STRUKTURY
Pola takiej struktury zawierają informację o ofsecie do początku struktury.
Struktur dotyczą n/w dyrektywy:
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.
Inny przykład zastosowania struktur został opisany w rozdziale Symbole zewnętrzne, przykład zastosowania symboli external i struktur .STRUCT.
Definiowanie danych strukturalnych polega na przypisaniu etykiecie konkretnej struktury z użyciem pseudo rozkazu DTA. Wynikiem takiego przypisania jest zarezerwowana pamięć, nie jest to już twór wirtualny.
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.
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.
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.
Brak nawiasu kwadratowego zakończy się komunikatem błędu Undeclared label.
TABLICE
COUNT określa maksymalną dopuszczalną wartość indeksu tablicy (0..COUNT), wartość ta powinna być z przedziału <0..65535>.
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.
W w/w przykładzie stworzyliśmy tablicę TAB o rozmiarze 0..255, typie danych .BYTE i wypełniliśmy pola wartością = $FF, dodatkowo zapisaliśmy wartości kodów literowych INTERNAL na pozycjach (indeksach tablicy) równych kodowi naciśnięcia klawisza (bez SHIFT-a i z SHIFT-em, czyli duże i małe litery).
Podsumowując, dyrektywa .ARRAY pozwala na stworzenie tablicy jednowymiarowej i wypełnienie jej wartościami zadeklarowanego typu.
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+).
Makr dotyczą n/w pseudo rozkazy i dyrektywy:
Domyślnym separatorem, rozdzielającym parametry przekazywane do makra jest znak przecinka ',' oraz spacji ' '.
UAWAGA #2: Jeśli parametrem makra jest licznik pętli '#' (!!! pojedyńczy znak '#' a nie wyrażenie z udziałem tego znaku !!!) wówczas do makra przekazywana jest wartość licznika pętli (podstawiana pod parametr).
Przykład 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.
Parametrem może być wartość, wyrażenie lub ciąg znaków ograniczony apostrofem pojedyńczym '' lub podwójnym "".
Wszelkie definicje etykiet w obrębie makra mają zasięg lokalny.
Przykład wywołania makra:
Przykład makra, które spowoduje przepełnienie stosu MADS-a:
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.
PROCEDURY .PROC
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.
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'.
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
Procedur dotyczą n/w dyrektywy:
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
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.
.VAR var1[=value1],var2[=value2]... (.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. MADS nie wykorzystuje informacji na temat takich zmiennych w dalszych operacjach z udziałem pseudo i makro rozkazów. 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 w którym zostały zadeklarowane, po dyrektywie .ENDP, .ENDL (.END). Wyjątek stanowi blok .PROC gdzie zmienne zadeklarowane przez .VAR zawsze odkładane są przed dyrektywą .ENDP niezależnie czy w bloku procedury wystąpiły jakiekolwiek dodatkowe bloki .LOCAL ze zmiennymi deklarowanymi przez .VAR
.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), np.:
Example:
ert "halt" ; ERROR: halt
ert *>$7fff ; ERROR: User error
.BYTE, .WORD, .LONG, .DWORD
W/w dyrektywy 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
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.
.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, oznacza koniec asemblowanego bloku programu.
.ADR label
Dyrektywa .ADR zwraca wartość etykiety LABEL przed zmianą adresu asemblacji (możliwe jest umieszczenie nazwy etykiety LABEL pomiędzy nawiasami okrągłymi lub kwadratowymi), np.:
org $2000
.proc tb,$1000
tmp lda #0
.endp
lda .adr tb.tmp ; = $2000
lda tb.tmp ; = $1000
.LEN label
Dyrektywa .LEN zwraca długość (wyrażoną w bajtach) bloku .PROC, .ARRAY lub .LOCAL. Etykieta LABEL to nazwa bloku .PROC, .ARRAY lub .LOCAL (możliwe jest umieszczenie nazwy etykiety LABEL pomiędzy nawiasami okrągłymi lub kwadratowymi), np.:
label .array [255] .dword
.enda
dta a(.len label) ; = $400
.proc wait
lda:cmp:req 20
rts
.endp
dta .len wait ; = 7
.DEF label [= expression]
Dyrektywa .DEF 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. Możliwe jest umieszczenie nazwy etykiety LABEL pomiędzy nawiasami okrągłymi lub kwadratowymi, np.:
ift .not(.def label)
.def label
eif
This unary operator tests whether the following label has been defined yet, returning TRUE or FALSE as appropriate.
.IFDEF label
Dyrektywa .IFDEF jest krótszym odpowiednikiem warunku .IF .DEF LABEL
Example:
.ifdef label
jsr proc1
.else
jsr proc2
.endif
.IFNDEF label
Dyrektywa .IFNDEF jest krótszym odpowiednikiem warunku .IF .NOT .DEF LABEL
Example:
.ifndef label
clc
.else
sec
.endif
Dla n/w przykładu asemblacja bloku .IFNDEF (.IF) będzie miała miejsce tylko w pierwszym przebiegu, jeśli umieścimy w takim bloku jakikolwiek kod programu na pewno nie zostanie on wygenerowany do pliku, definicje etykiet zostaną przeprowadzone tylko w pierwszym przebiegu, jeśli wystąpiły jakiekolwiek błędy związane z ich definiowaniem dowiemy się o nich dopiero w momencie próby odwołania do takich etykiet, będzie to komunikat błędu Undeclared label LABEL_NAME
.ifndef label
.def label
lda #0 ; ten rozkaz nie zostanie zasemblowany, tylko ostatni przebieg asemblacji generuje kod
temp = 100 ; etykieta TEMP zostanie zdefiniowana tylko w 1 przebiegu asemblacji
.endif
.NOWARN
Dyrektywa .NOWARN wyłącza komunikat ostrzeżenia dla aktualnie asemblowanego wiersza programu.
Example:
.nowarn .proc temp ; nie zostanie wygenerowane ostrzeżenie 'Unreferenced procedure TEMP'
.endp
.USING, [.USE]
Dyrektywa .USING (.USE) pozwala określić dodatkową ścieżkę poszukiwań dla nazw etykiet.
Example:
.local move
tmp lda #0
hlp sta $a000
.local move2
tmp2 ldx #0
hlp2 stx $b000
.endl
.endl
.local main
.use move.move2
lda tmp2
.use move
lda tmp
.endl
.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.
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.
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).
.LO (expression), .HI (expression)
W/w dyrektywy to odpowiedniki operatorów odpowiednio '<' (młodszy bajt) i '>' (starszy bajt).
.IF, .ELSE, .ELSEIF, .ENDIF
.IF [IFT] expression
.ELSE [ELS]
.ELSEIF [ELI] expression
.ENDIF [EIF]
Example:
.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
#IF type expression [.OR type expression] [.AND type expression]
#ELSE
#END
#WHILE type expression [.OR type expression] [.AND type expression]
#END
#IF type expression [.OR type expression] [.AND type expression]
Dyrektywa #IF to skromniejszy odpowiednik instrukcji IF z języków wyższego poziomu (C, Pascal).
#if .byte label>#10 .or .byte label<#5
#end
#if .byte label>#100
#else
#if .byte label<#200
#end
#end
#if .byte label>#100 .and .byte label<#200 .or .word lab=temp
#end
#if .byte @
#end
#WHILE type expression [.OR type expression] [.AND type expression]
Dyrektywa #WHILE jest odpowiednikiem instrukcji WHILE z języków wyższego poziomu (C, Pascal).
#while .byte label>#10 .or .byte label<#5
#end
#while .byte label>#100
#while .byte label2<#200
#end
#end
#while .byte label>#100 .and .byte label<#200 .or .word lab=temp
#end
Asemblacja na stronie zerowej
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 rozkaz LDA W.
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.
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.
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.
org adres1,adres2
Deklaracja struktury (.STRUCT)
name .STRUCT
.STRUCT name
.ENDS [.SEND] [.END]
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.
.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.
Definiowanie danych strukturalnych, odwołania
label DTA struct_name [count] (data1,data2,data3...) (data1,data2,data3...) ...
;----------------------;
; 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.
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].
lda data[4].y
ldx #data[0].v
Deklaracja tablicy jednowymiarowej (.ARRAY)
Tablic dotyczą n/w dyrektywy:
name .ARRAY count type [= default_value]
.ARRAY name count type [= default_value]
.ENDA [.AEND] [.END]
Dostępne typy danych to .BYTE, .WORD, .LONG, .DWORD.
.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
org $bc40
.array txt 39 .byte
[17] = "ATARI"
.enda
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.
Deklaracja makra
name .MACRO ['separator'] ["separator"]
.MACRO name ['separator'] ["separator"]
.EXITM [.EXIT]
.ENDM [.MEND]
:[%%]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).
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).
: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
.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
macro_name [Par1, Par2, Par3, 'Par4', "string1", "string2" ...]
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).
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.
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):
Deklaracja procedury .PROC
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:
name .PROC ( .WORD par1 .BYTE par2 )
name .PROC ( .BYTE par1,par2 .LONG par3 )
name .PROC ( .DWORD p1,p2,p3,p4,p5,p6,p7,p8 )
name .PROC ( .BYTE x,y,a .BYTE ) .REG
name .PROC ( .WORD xa .BYTE y ) .REG
name .PROC ( .LONG axy ) .REG
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 .ENDPW 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.
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:
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_NAMEMakro @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.
@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+1Wartość 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 ':'.Obszarów lokalnych dotyczą n/w dyrektywy:
[name] .LOCAL [,address] .LOCAL [name] [,address] .ENDL [.LEND] [.END]
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.
Po nazwie obszaru lokalnego (lub po dyrektywie .LOCAL) możemy podać nowy adres asemblacji bloku lokalnego. Po zakończeniu takiego bloku (.ENDL) przywracany jest poprzedni adres asemblacji zwiększony o długość bloku lokalnego.
label .local,$4000 .endl .local label2,$8000 .endl .local .endl .local label3 .endlWszelkie 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 .endlW 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. Obszary lokalne są addytywne, tzn. może istnieć wiele obszarów lokalnych o tej samej nazwie, wszystkie symbole występujące w tych obszarach należeć będą do wspólnej przestrzeni nazw.
Dyrektywa .ENDL kończy deklarację 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 #0MADS 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
* 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ń.
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, należy 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.
UWAGA!!! Umieszczenie znaku '\' na końcu wiersza oznacza dla MADS-a chęć kontynuowania aktualnego wiersza od następnego wiersza, np.:lda\ #\ 12Dla w/w przykładu otrzymamy rozkaz 'LDA #12'.
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.MADS akceptuje zapis liczb w formacie decymalnym, hexadecymalnym, binarnym oraz w kodach ATASCII i INTERNAL.
-100 -2437325 1743
$100 $e430 $000001 0x12 0xa000 0xaabbccdd
%0001001010 $000000001 $001000
'a' 'fds' 'W'*
"B" "FDSFSD" "."*Tylko pierwszy znak ciągu ATASCII, INTERNAL jest znaczący. Znak '*' za apostrofem zamykającym powoduje invers znaku.
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.
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.
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 dodatkowo oznaczać.
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.
Każda definicja etykiety dokonana w głównym 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 =lub dyrektywy .DEF o składni:
.DEF :label [= expression]
Dyrektywa .DEF umożliwia zdefiniowanie etykiety w aktualnym obszarze lokalnym, znak ':' na początku etykiety sygnalizuje etykietę globalną. Użycie dyrektywy o składni .DEF :label pozwala na zdefiniowanie etykiety globalnej z pominięciem aktualnego poziomu lokalności.
Znak dwukropka ':' na początku etykiety ma specjalne znaczenie, informuje że odwołujemy się do etykiety globalnej, czyli etykiety z głównego bloku programu z pominięciem wszystkich poziomów lokalności. Więcej informacji na temat użycia dyrektywy .DEF w rozdziale Dyrektywa .DEF Przykład definicji etykiet globalnych:
lab equ *
lab2 equ $4000
?tmp = 0
?tmp += 40
.proc name
.def :?nazwa = $A000
.def :nazwa=20
.local lok1
.def :@?nazw = 'a'+32
.endl
.endp
Przykładem zastosowania definicji etykiety globalnej 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 .def ?@stack_offset = 0 ; definicja etykiety globalnej tymczasowej ?@stack_offset ... ... @CALL_@ .macro sta @stack_address+?@stack_offset,x .def ?@stack_offset = ?@stack_offset + 1 ; modyfikacja etykiety ?@stack_offset .endm
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ą definiuje użytkownik poprzez umieszczenie na początku nazwy etykiety znaku zapytania '?', np.:?labelEtykiet 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
Opcja OPT ?+ informuje MADS aby etykiety zaczynające się znakiem '?' interpretował jako etykiety lokalne tak jak robi to MAE. Domyślnie etykiety zaczynające się znakiem '?' traktowane są przez MADS jako etykiety tymczasowe.
Przykład użycia etykiet lokalnych w stylu MAE:
opt ?+
org $2000
local1 ldx #7
?lop sta $a000,x
dex
bpl ?lop
local2 ldx #7
?lop sta $b000,x
dex
bpl ?lop
SPARTA DOS X
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:
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.
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ęć rozszerzonaUstawienie 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 *
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).
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.
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.
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:
Ograniczenia kodu relokowalnego MADS-a: