Kurs Assemblera cz. 2
- TeBe / Madteam
1. JEDNOSTKA CENTRALNA (CPU).
Mikroprocesor 6502 jest urządzeniem należącym do rodziny mikroprocesorów typu 6500. Są one programowo kompatybilne i cechują je m.in. następujące własności:
- 56 instrukcji sterujących,
- 13 rodzajów adresowania,
- 16 bitowa szyna adresowa,
- 8 bitowa dwukierunkowa równoległa szyna danych,
- system przerwań maskowalnych i niemaskowalnych,
- 1,2 MHz system zegara (ATARI 1,76 MHz).
6502 posiada następujący model programowy:
- Akumulator
- Rejestr indeksowy X
- Rejestr indeksowy Y
- Wskaźnik stosu SP
- Licznik programu
- Rejestr stanu procesora:
- N - negative flag
- V - wskaźnik przepełnienia nie używany
- B - wskaźnik rozkazu BREAK
- D - wskaźnik rodzaju dziesiętnego
- I - bit wyłączania systemu przerwań
- Z - wskaźnik zera
- C - wskaźnik przeniesienia
Licznik programu PC
Licznik programu jest 16 bitowym rejestrem zawierającym adres następnej instrukcji lub części instrukcji programu.
Wskaźnik stosu SP
Obszar stosu ograniczony jest do strony $01 pamięci. Wskaźnik stosu zawiera adres położenia w pamięci na tej stronie stosu, oraz wskazuje jego bieżącą pozycję.
Akumulator
Akumulator jest 8 bitowym rejestrem, używanym w operacjach arytmetycznych i logicznych. Rezultat takiej operacji jest umieszczany w akumulatorze. Akumulator jest najczęściej wykorzystywanym rejestrem.
Rejestry indeksowe X i Y
Rejestry indeksowe są rejestrami 8 bitowymi i są używane w różnorodnych rodzajach adresowania związanych z adresowaniem indeksowym. Rejestry te mogą być również użyte w pewnych operacjach arytmetycznych oraz do transferu danych między akumulatorem i wskaźnikiem stosu.
Rejestr stanu procesora
Jest to 8 bitowy rejestr zawierający 6 wskaźników, bit likwidacji przerwań i jeden bit nie używany.
- Bit 0 - wskaźnik przeniesienia (C).
Wskaźnik przeniesienia pełni funkcję dziewiątego bitu w pewnych operacjach arytmetycznych i logicznych. Wskaźnik ten powinien być zerowany przed rozpoczęciem każdej arytmetycznej operacji używającej ADC i ustawiony przed użyciem operacji odejmowania SBC.
- Bit 1 - wskaźnik zera (Z).
Wskaźnik ten jest ustawiany, jeśli rezultat operacji arytmetycznych lub logicznych jest równy zeru. Jeśli ten rezultat nic jest zerem, to wskaźnik ten jest zerowany.
- Bit 2 - bit likwidacji przerwań (I).
Bit ten umożliwia lub uniemożliwia przerwania IRQ:
jeśli I = 1 - przerwanie IRQ jest niemożliwe
jeśli I = 0 - przerwanie IRQ jest możliwe.
Bit ten nie ma wpływu na przerwanie NMI.
- Bit 3 - wskaźnik rodzaju dziesiętnego (D).
Gdy wskaźnik ten jest wyzerowany, jednostka arytmetyczna w CPU wykonuje arytmetykę binarną. Gdy wskaźnik ten jest ustawiony, wykonuje kodowaną binarnie arytmetykę dziesiętną.
- Bit 4 - wskaźnik komendy BREAK (B).
Wskaźnik ten jest ustawiany przez CPU przy wykonywaniu instrukcji BRK.
- Bit 5 - bit nie używany.
- Bit 6 - wskaźnik przepełnienia (V).
Podczas określonych operacji arytmetyki binarnej wskaźnik ten będzie ustawiony, jeśli wartość bezwzględna rezultatu przekracza 7 bitów, w przeciwnym razie jest on zerowany.
- Bit 7 - negative flag (N).
Wskaźnik ten jest ustawiany, gdy najbardziej znaczący bit rezultatu operacji jest ustawiony. W przeciwnym razie jest on zerowany.
Wyżej wymienione wskaźniki są ustawiane, zerowane lub pozostawiane bez zmian jako rezultat wykonanej instrukcji. Niżej wymienione mogą być ustawiane lub zerowane bezpośrednio:
- CLC - zeruj wskaźnik przeniesienia
- CLD - zeruj wskaźnik rodzaju dziesiętnego
- CLI - bit likwidacji przerwań
- CLV - zeruj wskaźnik przepełnienia
- SEC - ustaw wskaźnik przepełnienia
- SED - ustaw wskaźnik rodzaju dziesiętnego
- SEI - ustaw bit likwidacji przerwań
Sprawdzanie wskaźników
Wskaźniki N,V,Z,C można sprawdzić niżej wymienionymi instrukcjami. W zależności od ich stanu mogą być wykonywane odgałęzienia.
- N-BMI i BPL
- V-BVS i BVC
- Z-BEQ i BNE
- C-BCS i BCC
1a. System stronicowy pamięci
Ogólny zakres adresów mikroprocesora 6502 zawiera się w przedziale ($0000,$FFFF). Pamięć zorganizowana jest w system stron, po 256 bajtów na stronę. Mamy więc 256 stron. (256*256 = 65535 = 64KB). Bardziej znaczący bajt adresu bezwzględnego jest numerem strony a mniej znaczący położeniem wewnątrz tej strony.
Na przykład adres $6289 określa komórkę $89 znajdująca się na stronie $62.
Strony $00, $01 i $FF używane są do niżej wymienionych celów:
Strona $00: różne rodzaje adresowania indeksowego (najszybsza).
Strona $0l: na tej stronie zawarty jest obszar stosu. Nie jest wskazane umieszczanie
na tej stronie linii programu użytkownika, gdyż może on zostać zniszczony przez
stos (dla zdolnego nic trudnego :))
Strona $FF: ostatnie sześć pozycji na tej stronie jest zarezerwowane dla adresów
przerwania oraz obsługi wykonania procedury RESET.
2. ELEMENTY JĘZYKA PROGRAMOWANIA
Kod maszynowy
Każdy rozkaz kodu maszynowego 6502 zawiera jeden, dwa lub trzy bajty. Pierwszy bajt zawiera zawsze kod operacji danej instrukcji razem z rodzajem adresowania. Na przykład A0 jest kodem instrukcji LDY (ładuj rejestr indeksowy Y) używającej bezpośredniego rodzaju adresowania. Drugi i trzeci bajt to symboliczny argument: jeśli będzie obecny, zawiera dane, adres lub pozycję, spod której adres maże być pobrany.
Asembler
Każda instrukcja asemblera zawiera operator i argument lub tylko sam operator. Operator wyrażany jest przez mnemonik złożony z trzech liter, które określają znaczenie operatora.
Argumenty
Format dla argumentu asemblera będzie dodatkowo zawierał dane narzucające sposób adresowania:
#operand bezpośrednie
operand strona zerowa, bezwzględne lub względne
operand,X strona zerowa i bezwzględne indeksowe X
operand,Y strona zerowa i bezwzględne indeksowe Y
(operand,X) indeksowe pośrednie
(operand),Y pośrednie indeksowe
(operand) pośrednie
A akumulatorowe
Jeśli nie ma argumentu, to rodzaj adresowania nazywa się implikowanym.
Argument symboliczny w powyższej liście może być wyrażony na wiele sposobów:
1. Jako liczba dwójkowa, dziesiętna lub szesnastkowa. Istnieją sposoby rozróżniania systemów liczbowych, ale ich znaczenie będzie zależało od użytego asemblera. Typowe przykłady:
szesnastkowo $F2
dwójkowa %10011001
dziesiętnie 39
2. Jako symbol który będzie musiał być zdefiniowany i asembler przyporządkuje mu wartość liczbową.
3. Jako proste wyrażenie arytmetyczne zawierające zwykłe operatory: +,-,*,/
4. Jako znak kodu ATASCII, poprzedzony przez pojedynczy cudzysłów (`).
Program źródłowy
Program źródłowy dzieli się na 4 pola, które zawierają kolejno: etykietę, operator, argument i komentarz.
Przykład programu źródłowego:
Pole 1 2 3 4 ORG $6000 LDY #$FF ;pętla start
LOP2 LDX #$FF LOP1 DEX BNE LOPl DEY BNE LOP2
Pierwsze pole zajmowane jest przez etykietę. Jest ono używane głównie w instrukcjach odgałęzienia i skoków (asembler sam oblicza odgałęzienia i adresy liczbowe). Ostatnie pole jest zarezerwowane dla komentarzy pomocnych w tworzeniu programu. Pole to musi być oddzielone średnikiem. Jego długość i treść jest dowolna.
Listing asemblera
Asembler przekształca program źródłowy w kod symboliczny lub maszynowy. Listing asemblera dla poprzedniego programu źródłowego:
6000 A0FF LDY #$FF ;pętla start 6002 A2FF LOP2 LDY #$FF 6004 CA LOP1 DEX 6005 D0FD BNE LOOPl 6007 88 DEY 6008 D0F8 BNE LOOP2
3. RODZAJE ADRESOWANIA
Adresowanie bezwzględne
Adresowanie bezwzględne potrzebuje 3 bajtów kodu maszynowego. Pierwszy zawiera kod operacji. Drugi i trzeci bajt zawierają mniej znaczący bajt i bardziej znaczący bajt adresu.
Przykład: LDA $3000
Instrukcja ta załaduje do akumulatora wartość położoną w pamięci pod adresem $3000. W kodzie maszynowym (po zasemblowaniu AD 00 30)
Pierwszy bajt AD jest kodem operacji. Dalej następuje adres położenia w pamięci. Należy zwrócić uwagę, że 16 bitowy adres jest tworzony z mniej znaczącego bajtu adresu i bardziej znaczącego bajtu adresu.
Następny przykład: JMP $60D0
Instrukcja ta spowoduje skok pod adres $60D0 (nasz program zacznie wykonywać
się od bajtu położonego pod adresem $60D0). Po zasemblowaniu: 0C D0 60
W Basicu odpowiada to instrukcji GOTO nr_linii
Adresowanie strony zerowej
Adresowanie strony zerowej wymaga 2 bajtów kodu maszynowego. Pierwszy bajt zawiera kod operacji.
Przykład: LDA $80 (W kodzie maszynowym: A5 80)
Do akumulatora zostanie załadowana wartość komórki $80.
Bezwzględne adresowanie indeksowe
Bezwzględne adresowanie indeksowe wymaga 3 bajtów kodu maszynowego. Pierwszy bajt zawiera kod operacji, rodzaj adresowania i określony rejestr indeksowy. Drugi i trzeci bajt zawiera adres, do którego dodawana jest zawartość jednego z rejestrów indeksowych dla utworzenia końcowego (właściwego) adresu, pod którym znajduje się dana, będąca podmiotem operacji.
Przykład: LDA $701C,X
Jeśli rejestr indeksowy X zawiera wartość $40, to do akumulatora załadowana zostanie wartość leżąca pod adresem $705C ($701C + $40). W kodzie maszynowym: BD 1C 70 16, bitowy adres jest tworzony z mniej znaczącym bajtem występującym przed bardziej znaczącym bajtem.
Indeksowe adresowanie strony zerowej
Ten rodzaj adresowania wymaga 2 bajtów kodu maszynowego. Pierwszy zawiera kod operacji, rodzaj adresowania i określenie rejestru indeksowego. Drugi bajt zawiera adres na stronie $00 pamięci do którego dodawana jest wartość rejestru indeksowego w celu stworzenia właściwego adresu na stronie zerowej, pod którym znajduje się dana będąca podmiotem instrukcji.
Przykład: LDA $80,X
Jeśli rejestr indeksowy zawiera wartość $lA, to do akumulatora zostanie załadowana wartość spod adresu $009A ($80 + $1A), czyli $9A na stronie $00 pamięci. W kodzie maszynowym: B5 80
UWAGA:
Jeśli suma osiągnie wartość większą niż $FF, to przeniesienie nie jest generowane. Jeśli w powyższym przykładzie rejestr indeksowy posiadałby wartość np.$A5, to do akumulatora byłaby załadowana dana spod adresu $0025 ($a5 + $1A).
Adresowanie pośrednie
Adresowanie pośrednie stosowane jest tylko w instrukcji JUMP (JMP). Wymaga ona trzech bajtów kodu maszynowego. Pierwszy bajt zawiera kod operacji. Drugi i trzeci zawierają bajty (mniej i bardziej znaczący) wskaźnika adresowego. Bajty właściwego adresu są znajdowane we wskaźniku adresowym.
Przykład: JMP ($3814) 3814 (22) 3815 (4A)
Wartościami ulokowanymi na pozycjach pamięci $3814 i $3815 są $22 i $4A. Po wykonaniu instrukcji, licznik programu (PC) będzie zawierał wartość $4A22.
Adresowanie indeksowe pośrednie
Ten rodzaj adresowania wymaga 2 bajtów kodu maszynowego. Pierwszy bajt zawiera kod operacji. Drugi bajt jest dodawany do wartości rejestru indeksowego w celu stworzenia wskaźnika adresu strony zerowej. W ten sposób wskaźnik ten zawiera mniej i bardziej znaczący bajt właściwego adresu, pod którym znajduje się dana będąca przedmiotem operacji.
Przykład: LDA ($32,X) 0092 (53) 0093 (Al)
Jeśli rejestr indeksowy X zawiera wartość $60 i wartościami ulokowanymi na pozycjach pamięci $0092 ($32+$60} i $0093 są $53 i $A1, to w rezultacie wykonania powyższej instrukcji do akumulatora zostanie załadowana wartość z komórki $A153
UWAGA:
W formowaniu wskaźnika adresu przeniesienie jest ignorowane.
Adresowanie pośrednie indeksowe
Ten rodzaj adresowania wymaga dwóch bajtów kodu maszynowego. Pierwszy bajt zawiera kod operacji. Drugi bajt zawiera adres na stronie $00 pamięci. Zawartość umiejscowiona pod tym adresem jest dodawana do rejestru indeksowego Y. Powstaje tym sposób adres właściwy. Każde przeniesienie z sumy jest dodawane do następnej komórki pamięci na stronie $00 dla utworzenia bardziej znaczącego bajtu właściwego adresu, pod którym leży dana, będąca podmiotem operacji.
Przykład: LDA ($32),Y 0032 (21) 0033 (45)
Mniej i bardziej znaczący bajt właściwego adresu znajduje się we wskaźniku adresu i wskaźniku adresu + 1. Jeśli rejestr indeksowy Y zawiera adres $20 i wartościami ulokowanymi na pozycjach pamięci $0032 i $0033 są $21 i $45, to w rezultacie wykonania powyższej instrukcji do akumulatora załadowana zostanie wartość komórki $4541 ($4521 + $20 = $4541)
Adresowanie implikowane
Ten rodzaj adresowania wymaga 1 bajtu kodu maszynowego.
Przykład: CLC (w kodzie maszynowym: 18).
Rezultatem tej instrukcji jest wyzerowanie (zgaszenie) wskaźnika przeniesienia.
Adresowanie akumulatorowe
Ten rodzaj adresowania wymaga 1 bajta kodu maszynowego i powoduje wykonanie operacji na wartości akumulatora.
Przykład: SEC (w kodzie maszynowym 38).
Rezultatem tej instrukcji jest ustawienie (zapalenie) wskaźnika przeniesienia.
Adresowanie względne
Adresowanie względne jest używane wyłącznie z instrukcjami odgałęzienia (BNE, BEQ, BPL, BMI, BCC, BCS) i wymaga 2 bajtów kodu maszynowego. Podczas wykonywania instrukcji odgałęzienia sprawdzany jest jeden ze wskaźników procesora i w zależności od jego stanu odgałęzienie jest wykonywane lub nie.
Pierwszy bajt zawiera instrukcję. Drugi zawiera uzupełnienie dwójkowe, które reprezentuje bieżącą pozycję licznika programu PC. Oznacza to, że nie podaje się bezwzględnego adresu skoku, lecz liczbę pozycji licznika programu zawartych między instrukcją następującą (PC+2} a żądanym adresem skoku. Liczba ta musi być podana w systemie szesnastkowym ($}.
Przykład: BEQ $08
W asemblerze:
pozycja kod maszynowy asembler PC F0 08 BEQ $08 PC+2
Jeżeli wskaźnik Z jest ustawiony, to licznik programu jest modyfikowany w sposób następujący:
(PC + 2) + 8 = PC + A
Jeżeli wskaźnik Z jest wyzerowany, to licznik programu pozostaje w stanie PC+2.
Przykład następny: BPL $FB
W asemblerze:
pozycja kod maszynowy asembler
PC 10 FB BPL $FB
Jeśli wskaźnik N=0, to następuje zmiana stanu licznika programu:
(PC+2) + (-5) = PC-3
Jeśli wskaźnik N=1, to licznik programu pozostanie w stanie PC+2.
Czasy wykonywania
Dla każdej instrukcji czas wykonywania jest wyrażany przez liczbę cykli zegarowych systemu, zależną od rodzaju adresowania i jest podany przy omawianiu poszczególnych instrukcji. Dla rozkazów odgałęzienia liczba cykli może być różna od podanej w zależności od tego, czy odgałęzienie jest wykonywane oraz od przekraczania strony. Jeżeli odgałęzienie jest wykonywane na tej samej stronie, to należy dodać 1 cykl do wartości wyszczególnionej. Jeśli odgałęzienie jest wykonywane do innej strony, to należy dodać 2 cykle.