Nielegalne rozkazy 6502


      Przed lekturą artykułu, lub rezygnacją z lektury zachęcam do przeczytania choćby tego krótkiego wstępu. W tej publikacji dostarczamy Wam jakby cały "pakiet" składający się z artykułu Konrada Kokoszkiewicza zatytułowanego "Nielegalne rozkazy 6502" opublikowanego w numerza marcowym z 1995 roku popularnego czasopisma "Bajtek", kilku słów komentarza Krógera, oraz programu służącego do przetestowania naszego komputera na okoliczność wykonywania nielegalnych rozkazów. Autorem tegoż "testera" jest kolega Króger, który wszystkim dociekliwcom w dzisiejszym magazynie, w pliku ze stuff'em udostęnił tekst źródłowy swojego programu ICT6502.

      Jak już wcześniej pisałem, jestem zagorzałym przeciwnikiem ogłaszania jakichkolwiek konkursów, ankiet itp. jednak tym razem zwracam się z gorącą prośbą o odostępnienie redakcji wyników testów przeprowadzanych przy pomocy ICT6502. Być może stajemy przed szansą namówienia kogoś do stworzenia zupełnie nowego asemblera, asemblera na miarę naszych czasów i naszych komputerów. Byłaby to dla Atari na pewno spora szansa...

Zapraszam wszystkich do lektury zapowiadanych tekstów...

      Lista rozkazów mikroprocesora 6502 liczy według oficjalnych publikacji 151 pozycji, które składają się na 56 podstawowych typów rozkazów. Rozkaz jest jednak liczbą ośmiobitową, cóż więc z pozostałymi stu pięcioma kodami? Otóż dzielą się one na trzy grupy.

      Grupa pierwsza

Dwanaście spośród kodów nie ujętych w oficjalnych listach rozkazów powoduje totalne zablokowanie 6502. Z ich analizy wynika, że na ogół próbują skłonić mikroprocesor do działań z definicji absurdalnych, jak na przykład: ASL #n. Oto ich wykaz (hex):

            02, 12, 22, 32, 42, 52, 62, 72, 92, B2, D2, F2

Cecha charakterystczna grupy chyba każdemu się rzuca w oczy, ale uwaga! Nie wszystkie liczby zakończone szesnastkową dwójką blokują komputer!

      Grupa druga

Wszystkie eksperymenty z następnymi dziewiętnastoma kodami są zabronione gdyż zostały one (kody, nie eksperymenty) użyte przez twórców mikroprocesora 65SC02 i funkcjonują jako rozkazy dodatkowe, nie zimplementowane w zwykłym 6502. Ponieważ 65SC02 bywa soptykany w nowszych komputerach XE, więc użycie kodów z tej grupy w programie pisanym dla 6502 mogłoby narobić niezłego zamieszania w systemie z procesorem 65SC02. Zależność ta działa też w drugą stronę - program pisany z wykorzystaniem dodatkowych rozkazów 65SC02 na pewno nie będzie funkcjonował prawidłowo na komputerach mających 6502, a więc na zdecydowanej większości. A oto "czarna lista" (szesnastkowo):

            04, 0C, 1A, 1C, 34, 3A, 3C, 5A, 64, 74, 7A, 7C, 80, 82, 89, 9C, 9E, DA, FA

Zawiedzionym ku pocieszeniu mogę dodać, że jedynie dwa z powyższych kodów wywołują jakieś reakcje 6502, pozostałe są ignorowane.

      Grupa trzecia

Pozostały nam 74 kody potęcjalnych rozkazów, z których 68 wywołuje określone działania mikroprocesora, podpadające pod dwadzieścia kategorii. Wykaz obejmujący "zwyczajową" charakterystykę zawiera zamieszczona poniżej tabela. Zanim przystąpię do omówienia poszczególnych jej pozycji, jeszcze krótka ściąga z zastosowanych tam oznaczeń trybów adresowania:

  • #n - bezpośredni
  • a - bezwzględny
  • a,X - bezwzględny indeksowany rejestrem X
  • a,Y - bezwzględny indeksowany rejestrem Y
  • z - strony zerowej
  • z,X - strony zerowej indeksowany rejestrem X
  • z,Y - strony zerowej indeksowany rejestrem Y
  • (z,X) - pośredni preindeksowany
  • (z),Y - pośredni postindeksowany
Brak symbolu oznacza domyślny (implikowany) tryb adresowania. Gołym okiem da się zauważyć, że wszystkie mnemoniki w tabeli mają po cztery litery. Na to odstępstwo od powszechnie znanego standardu pozwoliłem sobie całkiem świadomie i to z kilku powodów. Po pierwsze mnemonik taki wyróżnia się w listingu, spośród innych, co zwraca uwagę użytkownika na "nielegalność" kryjącego się pod nim rozkazu. Po drugie nieoficjalny kod wywołuje na ogół dość skomplikowane działanie będące w istocie złożeniem kilku "legalnych", a dla takich wielostopniowych operacji łatwiej jest utworzyć mnemoniczny skrót z liter czterech niż z tradycyjnych trzech. Po trzecie wreszcie utworzenie nowych mnemoników dla niektórych rozkazów było i tak konieczne, ponieważ ich działanie na Atari (65 XE, numer seryjny A1754016223 rok produkcji 1987) okazało się być odmienne niż na Comodore 64 z procesorem 6510, do którego odnoszą się treści publikacji (1). Ewentualnym purystom pragnę uświadomić, że równiż niektóre oficjalne mnemoniki mają po cztery znaki, tyle, że jest to nieco przymaskowane. Przykłady? Proszę bardzo: "LDX #", albo "ROR A".

      Pozostałe rubryki tabeli nie budzą chyba wątpliwości. Pomiar liczby taktów zużywanych przez mikroprocesor na wykonanie danego rozkazu dokonany został przez umieszczenie jego kodu wewnątrz pętli o 16777216 przebiegach. Programik ten oczywiście uprzednio "wykalibrowałem" przy pomocy oficjalnych instrukcji 6502. W następnej rubryce pokazano wpływ rozkazu na rejestr znaczników procesora. Gwiazdka (*) oznacza, że odpowiedni bit jest przez rozkaz zmieniany.

NIEOFICJALNE ROZKAZY 6502

Mnemonik
Kod HEX
Kod DEC
Bajtów
Taktów
BDIZC
ANCC #n
2B
43
2
2
*..**
ANCC #n
0B
11
2
2
*..**
ANSR #n
4B
75
2
2
*..**
ANTX #n
AB
171
2
2
*...*
ARRC #n
6B
107
2
2
**.**
ASBX #n
CB
203
2
2
*..**
DCPA (z),Y
D3
211
2
8
*..**
DCPA (z,X)
C3
195
2
8
*..**
DCPA a
CF
207
3
6
*..**
DCPA a,Y
DB
219
3
7
*..**
DCPA a,X
DF
223
3
7
*..**
DCPA z
C7
199
2
5
*..**
DCPA z,X
D7
215
2
6
*..**
INSB (z),Y
F3
243
2
8
**.**
INSB (z,X)
E3
227
2
8
**.**
INSB a
EF
239
3
6
**.**
INSB a,Y
FB
251
3
7
**.**
INSB a,X
FF
255
5
7
**.**
INSB z
E7
231
2
5
**.**
INSB z,X
F7
247
2
6
**.**
LDCX (z,X)
A3
163
2
6
*..*.
LDCX (z),Y
B3
179
2
5+
*..*.
LDCX a
AF
175
3
4
*..*.
LDCX a,Y
BF
191
3
4+
*..*.
LDCX z
A7
167
2
3
*..*.
LDCX z,Y
B7
183
2
4
*..*.
MIHB (z),Y
93
147
2
6
.....
RLAN (z),Y
33
51
2
8
*..**
RLAN (z,X)
23
35
2
8
*..**
RLAN a
2F
47
3
6
*..**
RLAN a,Y
3B
59
3
7
*..**
RLAN a,X
3F
63
3
7
*..**
RLAN z
27
39
2
5
*..**
RLAN z,X
37
55
2
6
*..**
RRAD (z),Y
73
115
2
8
**.**
RRAD (z,X)
63
99
2
8
**.**
RRAD a
6F
111
3
6
**.**
RRAD a,Y
7B
123
3
7
**.**
RRAD a,X
7F
127
3
7
**.**
RRAD z
67
103
2
5
**.**
RRAD z,X
77
119
2
6
**.**
SLOR (z),Y
13
19
2
8
*..**
SLOR (z,X)
03
3
2
8
*..**
SLOR a
0F
15
2
6
*..**
SLOR a,Y
1B
27
3
7
*..**
SLOR a,X
1F
31
3
7
*..**
SLOR z
07
7
2
5
*..**
SLOR z,X
17
23
2
6
*..**
SRXR (z,X)
43
67
2
8
*..**
SRXR (z),Y
53
83
2
8
*..**
SRXR a
4F
79
3
6
*..**
SRXR a,Y
5B
91
3
7
*..**
SRXR a,X
5F
95
3
7
*..**
SRXR z
47
71
2
5
*..**
SRXR z,X
57
87
2
6
*..**
STCX (z,X)
83
131
2
6
.....
STCX a
8F
143
3
4
.....
STCX z
87
135
2
3
.....
STCX z,Y
97
151
2
4
.....
STOP 2
E2
226
2
2
.....
STOP 3
44
68
2
3
.....
STOP 4
F4
244
2
4
.....
STOP 5
DC
220
3
5
.....
SUBC #n
EB
235
2
2
**.**
XAND
8BFF
65419
2
2
*..*.

      Tak zwane sedno

Po tym przydługim wstępie i nudnej tabelce ochoczo przystępuję do wyjaśniania, co też smakowitego kryje się pod znajdującymi się w tabeli czteroliterowymi mnemonikami.

ANCC (ANd/Copy bit Cary) - przeprowadza zwykłą operację AND podanego parametru z akumulatorem i tamże jest odkładany wynik. Następnie najstarzsy (siódmy) bit akumulatora jest kopiowany do bitu C rejestru znaczników procesora. Mnemonik ten umieszczono w tabeli dwukrotnie, co może sugerować pomyłkę, ale tak nie jest. kody $0b i 2b wykonują dokładnie tę samą operację (a przynajmniej wynik jest ten sam). Tylko bezpośredni tryb adresowania.

ANSR (ANd/Shift Right) - przeprowadzane jest logiczne AND pomiędzy podanym parametrem i akumulatorem (wynik tamże) i następuje przesunięcie zawartości akumulatora w prawo (LSR). Tylko bezpośredni tryb adresowania.

ANTX (ANd/TaX) - logiczne AND pomiędzy parametrem a akumulatorem. Wynik w akumulatorze i rejestrze X. Tylko bezpośredni tryb adresowania.

ARRC (And/Rotat e Right using Carry) rozkaz ten jest złożeniem logicznego AND pomiędzy parametrem i akumulatorem (wynik tamże) i obrotu w prawo (ROR) bitów akumulatora. Obrót dodatkowo uzależniony jest od stanu bitu C rejestru znaczników: normalnie ROR wykonywane jest tylko wtedy, gdy bit ten jest ustawiony. Gdy jest skasowany to po ROR przyjmuje stan najstarszego bitu akumulatora. Z tego powodu za pomocą ARRC łatwo jest generować liczby składające się naprzemiennie z binarnych zer i jedynek. Tylko bezpośredni tryb adresowania.

ASBX (And/SuBtract from X) - do rejestru X odkładany jest wynik logicznego AND pomiędzy tym rejestrem i akumulatorem, a następnie od otrzymanej wartości odejmowany jest podany parametr (wynik w rejestrze X). Na to odejmowanie ma wpływu stan znacznika C, natomiast po ustawieniu znacznika D przeprowadzane jest ono w trybie dziesiętnym. Tylko bezpośredni tryb adresowania.

DCPA (DeCrement/ComPare with Accumulator) - zawartość komórki pamięci określonej parametrem zgodnie z użytym trybem adresowania jest zmniejszana o jeden (DEC), a następnie porównywana z liczbą w akumulatorze (CMP). DCPA może się okazać użyteczne przedewszystkim w pętlach, w których licznikami są komórki pamięci.

INSB (INcrement/SuBtract) - zawartość komórki pamięci określonej parametrem zgodnie z użytym trybem adresowania jest zwiększana o jeden (INC), a następnie odejmowana od akumulatora (wynik tamże). Odejmowanie jest przeprowadzane z uwzględnieniem stanu bitów C i D rejestru znaczników, a więc INSB działa również w trybie dziesiętnym. Ponieważ rozkaz tak jak DCPA po CMP, odziedziczył po SBC tryby adresowania niedostępne dla INC, można próbować używać go zamiast tego ostatniego.

LDCX (LoaD acCumulator/load X) - zawartość komórki pamięci określonej parametrem zgodnie z zastosowanym trybem adresowania jest kopiowana do akumulatora i rejestru X. Rozkaz jest połączeniem LDA i LDX, względnie LDA i TAX Instrukcja LDCX może być przydatna w pętlach interpretujących, na przykład:

      ......
      LDY #0
      LDCX (COUNTER),Y
      LDA LOWTAB,X
      STA ADDR
      LDA HITAB,X
      STA ADDR+1
      JMP (ADDR)

Ten fragment programu pobiera bajt spod wskazanego przez zmienną COUNTER i traktuje go jako index w tabelach LOWTAB i HITAB dla wybrania z nich odpowiednio młodszego i starszego bajtu adresu procedury realizującej operację określoną kodem pobranym z pamięci. LDCX zastępuje tu rozkaz LDX niedostępny w użytym trybie adresowania.

MIHB - ten niezbyt zręczny skrót powstał ze słów "Move/Increment Higs Byte". Byłby to jedyny w repertuarze 6502 rozkaz przesyłania pamięć-pamięć, chociaż jego specyfika stawia użyteczność MIHB pod znakiem zapytania. Działanie tego rozkazu jest dosyć trudne do klarownego opisania, stąd podział na punkty:

  • parametr traktowany jest jako adres dwubajtowego adresu odłożonego na stronie zerowej (adresowanie pośrednie postindeksowane).
  • pod adres docelowy, wynikający z trybu adresowania przenoszony jest starszy bajt adresu odłożonego na stronie zerowej.
  • wartość komórki pod tym adresem jest zwiększana o jeden.
  • przeprowadzane jest logiczne AND pomiędzy tą komórką pamięci, a wynikiem operacji logicznej AND akumulatora i rejestru X.
  • wynik jest odkładany w komórce pamięci wskazanej operendem.
Przykład: pod adresem $80 odłożono liczbę $0600 w zwykłej konwencji młodszy starszy. Wykonanie rozkazu MIHB ($80),Y z jedynką w rejestrze indexującym Y, $FF w akumulatorze i rejestrze X spowoduje odłożenie liczby 7 pod adresem $0601. W praktyce pozwala to, przy odpowiednio dobranych wartościach rejestrów, na przesyłanie dowolnych wartości jedynie na stronę $FE. Na każdej innej stronie największą wartością możliwą do odłożenia jest numer strony zwiększony o jeden. Wyłącznie pośredni postindeksowy tryb adresowania.

RLAN (Rotate Left/ANd) - we wskazanej komórce pamięci dokonywany jest obrót bitów w lewo (ROL), a następnie dokonywana jest operacja AND pomiędzy tą komórką, a akumulatorem (wynik tamże).

RRAD (Rotate Right/ADd) - w komórce pamięci wskazanej operendem dokonywany jest obrót bitów w prawo (ROR), a następnie dodanie (ADC) zawartości tej komórki do akumulatora (wynik tamże). Dodawanie wykonywane jest z uwzględnieniem stanu bitów C i D rejestru znaczników, a więc RRAD działa również w trybie dziesiętnym.

SLOR (Shift Left/OR) - na wskazanej przez operend komórce pamięci wykonywane jest przesunięcie bitów w lewo (ASL), a następnie logiczne OR zawartości komórki i akumulatora (wynik tamże).

SRXR (Shift Right/eXclusive oR) - we wskazanej przez operend komórce pamięci dokonywane jest przesunięcie bitów w prawo (LSR), a następnie logiczne EOR zawartości komórki i akumulatora (wynik tamże).

STCX (STore acCumulator and X) - rozkaz ten jest niejako odwrotnością LDCX ponieważ jednak nie da się załadować zawartości dwóch rejestrów do jednej komórki, więc przedtem przeprowadzone jest logiczne AND akumulatora i rejestru X, a wynik ląduje w pamięci.

STOP (STop OPeration) - analogicznie jak legalny rozkaz NOP zatrzymuje działanie programu, na podaną w tabeli liczbę taktów zegara. Wartość operendu jest obojętna (STOP nie robi niczego zauważalnego), ważna jest jedynie jego długość

SUBC (SuBtract using Carry) - przeprowadza odejmowanie podanego parametru od akumulatora z uwzględnieniem bitów C i D rejestru znaczników. SUBC jest tożsamy z SBC w trybie bezpośrednim, a odrębny mnemonik przypomina tylko o jego "nielegalności" XAND - w tym przypadku niewątpliwie zwracają uwagę rubryki "KOD HEX" i "KOD DEC", spieszę więc z wyjaśnieniami. Otóż właściwym kodem jest oczywiście $8B. Powoduje on dwustopniową operację AND, najpierw pomiędzy parametrem i akumulatorem (wynik w akumulatorze), a następnie pomiędzy akumulatorem i rejestrem X (wynik jak poprzednio). Sęk w tym, że w przypadku pierwszej z tych operacji nie udało mi się uzyskać przewidywalności wyników, prawdopodobnie dochodzi tu do "wmieszania się" któregoś z niedostępnych programowo rejestrów 6502. Ponieważ jednak możliwość przeprowadzenia logicznej koniunkcji dwóch rejestrów wewnętrznych wydała mi się być interesująca więc zneutralizowałem "nieobliczlne" AND parametrem $FF. W swej obecnej postaci ($8B $FF) rozkaz XAND dokonuje operacji AND pomiędzy akumulatorem i rejestrem X odkładając wynik w akumulatorze.

      No, a reszta?

Wnikliwy czytelnik zauważył, że wspomniałem o dwudziestu kategoriach rozkazów, a opisałem tylko siedemnaście. Rzeczywiście, trzy kody, to jest $9B, $9F, $BB pozostawiłem do rozszyfrowania czytelnikom co można potraktować jako swego rodzaju łamigłówkę. Wskazówka: w operacjach realizowanych przez te trzy instrukcje aktywny udział bierze wskaźnik stosu. Pozostałe do równego rachunku sześć kodów, to jest $FC, $5C, $D4, $C2, $54, $14 dubluje się z opisanymi rozkazami STOP.

      Ostatecznie wyszło, że 6502 ma 225 rozkazów, z tego 151 oficjalnych i 74 nieoficjalne. Grupują się one w 72 kategorie rozkazów (oficjalnie 56), przy czym większość kodów nieoficjalnych (oprócz stopów) należy do jednej grupy którą możnaby nazwać "11", analogicznie do grup "00", "01", "10" zrzeszających legalne rozkazy 6502. Nazwy grup pochodzą od dwóch najmłodszych cyfr binarnych kodu rozkazu. Do grupy "11" nie należy żaden oficjalny rozkaz mikroprocesora.

      Caveant programmantes

Stosowanie nielegalnych rozkazów niesie pewne niebezpieczeństwo, gdyż nie muszą one wywoływać jednakowych reakcji na wszystkich komputerach. Dotyczy to zwłaszcza operacji bardzo skomplikowanych lub bardzo nietypowych w rodzaju ANCC, ARRC, MIHB czy XAND. Rozkazy typu LDCX, STCX, INSB czy DCPA, są niejako "naturalnym" połączeniem dwóch oficjalnych instrukcji i prawdopodobnie będą działać na każdym 6502.

Konrad Kokoszkiewicz

Literatura:

1) Klaudiusz Dybowski "Monitory ML" cz.6 i 8. BAJTEK nr. 6/89, nr.9/89.
2) Jan Ruszczyc "Assembler 6502". SOETO, Warszawa 1987.
3) Wiesław Migut "Atari BASIC - język programowania i obsługa komputerów" KAW, Warszawa 1987.

  

Krógera uwag kilka

      Prawdziwość treści ostatniego paragrafu artykułu nie jest zgodna z prawdą. Z moich badań wynika, że korzystanie ze wszystkich nielegalnych rozkazów nie jest obarczone żadnym ryzykiem. Napisałem program testujący procesor na ich rozpoznawanie. Test wypadł pozytywnie przy wszystkich trzech przeprowadzonych przeze mnie próbach (na dwóch 800XL i na 800XE). Nie jest to znaczna próbka, ale wydaje mi się, że dostatecznie reprezentatywna, by twierdzić, że wszystkie nielegalne rozkazy są rozponawane przez każdy 6502.

Od redakcji: Zachęcony przez Krógera przetestowałem trzy komputery jakie mam do dyspozycji:

  • Atari 130 XE 320 kB.
  • Atari 130 XE
  • Atari 65 XE
Na wszystkich tych komputerach test na wykonalność rozkazów nielegalnych wypadł pomyślnie. W tak zwanym międzyczasie dostałem od Krógera kartkę pocztową, w której poinformował on mnie o przetestowaniu następnych czterech komputerów (dwa 65 XE i dwa 130 XE) i we wszystkich tych przypadkach test wypadł pomyślnie! Teraz już zapraszam do lektury komentarza Krógera do artykułu Konrada Kokoszkiewicza:

Moment... jeszcze ja - Jager! Przetestowałem trzy komputerki:

  • Atari 65XE (320Kb)
  • Atari 130XE (dwie sztuki)
Jaki był wynik? Oczywiście pozytywny!

Teraz już oddajemy głos Krógerowi...

      Powyższy artykuł Konrada Kokoszkiewicza jest, śmiem twierdzić, najdonioślejszą pracą poświęconą procesorowi 6502 od pierwszej połowy lat 80'tych, kiedy to ukazały się najważniejsze opracowania dotyczące naszego komputera.

      Sam artytkuł zdobyłem cztery lata temu uznając jego treść raczej jako ciekawostkę. Nielegalne rozkazy to coś trudnego do zaakceptowania dla kodera, który wychował się na pouczkach Janusza Wiśniewskiego. W dodatku miałem świadomość, że korzystanie z nielegalnych rozkazów to prawie to samo, co palenie ogniska w stodole.

      Przez cały ten czteroletni okres nosiłem się ze sprawdzeniem przynajmniej części z opisanych przez Kokoszkiewicza rozkazów. Wreszcie przyszedł na to czas, czego efektem jest program testujący procesor na zdolność rozpoznawania nielegalnych rozkazów (ICT6502 na dysku z magazynem).

      Jak w przypadku większości programów użytkowych mego autorstwa (a tychże powstało sporo, choć nikt z was nie miał szansy ich zobaczyć) posiada on pewną wadę - nie do końca można mu ufać. Poprawność działania gwarantuję w 95%. Dla wątpiących w skuteczność programu (czyli w moją wiedzę i umiejętności koderskie) mam prezencik w postaci źródłówki programu (na dysku z magazynem).

      Wyświetlenie komunikatu "OK" po uruchomieniu programu oznaczać będzie, że Twój komputer w pełni toleruje nielegalne rozkazy. Jeśli tak się nie stanie, program poda kody rozkazów, których nie rozpoznaje.

      Program nie testuje następujących rozkazów: ARRC, MIHB, STOP, SUBC oraz XAND. Sytuacja ta ma miejsce z dwóch powodów: po pierwsze - trudno jest mi sobie wyobrazić, by ktoś potrzebował tych rozkazów: po drugie - nie chce mi się już uzupełniać kodu.

      Na koniec jeszcze mała uwaga, zaledwie powinowata z tematem. Czy nie uważacie, że QA już się trochę przeżył? To już w końcu program z brodą, pod nazwą JBW Assembler powstał 11 lat temu. Niestety, przestaje odpowiadać moim wymaganiom. Przydałby się asembler wykorzystujący dodatkową pamięć, mający tablicę adresów etykiet, z rozbudowanym Setupem, a przede wszystkim rozpoznający nielegalne i dodatkowe rozkazy. Może ktoś się szarpnie i zrobi coś, co możnaby dumnie nazwać QA 2.0? Jeśliby ktoś taki się znalazł, pozwolę sobie mu coś zasugerować. Mianowicie, nazewnictwo rozkazów autorstwa herr Kokoszkiewicza, nie wydaje mi się zbyt udane. Mam na myśli czteroliterowe mnemoniki - wątpię, czy posługiwanie się nimi byłoby wygodne.

      W następnym numerze Serious (a może nieco później) ukaże się jeszcze jeden artykuł mego autorstwa. To dopiero będzie kąsek - pełen ekshibicjonizm intelektualny.

            See ya!

Króger/Quasimodos

Od Jager'a: Myślę że Króger byłby osobą jak najbardziej odpowiednią do stworzenia takiego assemblerka! Co Wy o tym sądzicie?