Znaczenie znaczników
---------------------( Fox/Taquart )--
W poniższym tekście zajmę się opisem rejestru znaczników (wszyscy koderzy wiedzą, o co chodzi).
W tym miejscu wszyscy nie-koderzy na pewno już skończyli lekturę tego artykułu, natomiast koderzy co najmniej uśmiechnęli się pod nosem: "Co on może nowego napisać o znacznikach? Bo przecież w każdym podręczniku czy kursie asemblera już wszystkie znaczniki zostały opisane."
To błąd! Przeczytaj ten artykuł, a gwarantuję Ci, że dowiesz się czegoś ciekawego.
Umówmy się, że rejestr znaczników oznaczamy literką F. Piwo dla tego, kto mi wyjaśni, jaki sens ma oznaczenie P, bo z takim też się spotkałem.
Innym fenomenem jest nazywanie tego rejestru rejestrem FLAGOWYM. (to pewnie z angielskiego "Flag register")
Rejestr F, jak wszyscy wiemy, wygląda tak:
N V * B D I Z C
Znacznikami N, Z i C nie będę się zajmował, bo z nich to chyba każdy umie korzystać.
b6 - oVerflow - znacznik przepełnienia (nadmiaru).
To jest b. ciekawy znacznik, więc zatrzyma- my się przy nim dłużej.
Gdzieś czytałem, że bit V zawiera 9. bit wyniku, jeśli nastąpiło przeniesienie z b8, którym jest znacznik C. Śmieszne, nie?
Ale tak naprawdę na wartość znacznika V wpływa tylko kilka rozkazów:
BIT - znacznik V jest kopiowany z b6 bajtu pobranego z pamięci
ADC- z różnych źródeł wiadomo, że zostaje on ustawiony, jeśli nastąpi przeniesienie z b6 do b7 i jest wykorzystywany przy liczbach ze znakiem.
Ja po kilku próbach znalazłem prostsze wytłumaczenie:
V=1 wtedy i tylko wtedy, gdy b7 składników są jednakowe, a b7 wyniku jest od nich różny. Proste, nie?
SBC - tutaj równie skomplikowana sprawa, jak z dodawaniem: V=1 wtedy i tylko wtedy, gdy b7 akumulato- ra i pamięci są różne, a w wyniku odejmowa- nia zmienił się b7 akumulatora.
CLV - kasuje znacznik V. Ale po co? Ogłaszam konkurs! Jeśli ktoś w jakimkolwiek programie użył rozkazu CLV i naprawdę był on tam potrzebny, to niech da mi znać. Dostanie całego Snickersa!!!
Dodatkowo znacznik V zmieniają PLP i RTI, ale one po prostu zmieniają cały rejestr znaczników, więc nie ma w tym nic nadzwy- czajnego.
b5 - * - ten bit jest nieużywany. Wszędzie można spotkać właśnie taką informację, która jest niepełna. Pełna informacja jest taka: tego bitu nie ma, bo zawsze jest równy 1. Można się o tym łatwo przekonać:
LDA #0 PHA PLP PHP PLA BRK
b4 - Break - uwaga! To następny znacznik, którego NIE MA! Chodzi o to, że tak jak b5 jest zawsze ustawiony.
Mądry koder spyta, dlaczego się nazywa B, skoro go nie ma?
Odpowiedź jest prosta:
Rozkaz BRK jest traktowany jako wymusze- nie przerwania IRQ, ale nieco różni się od innych przerwań maskowalnych:
- działa nawet jeśli znacznik I jest ustawiony
- ponieważ wykorzystuje standardową procedurę IRQ wskazywaną przez wektor $FFFE, to musi być jakiś sposób wykrycia, że chodzi o przerwanie BRK.
Jak wiadomo, przy wywołaniu przerwania procesor wrzuca na stos adres powrotu oraz rejestr znaczników.
Przerwanie BRK można rozpoznać po tym, że na stosie jest zapisany rejestr znacz- ników ze skasowanym b4, czyli właśnie znacznikiem B.
Okazuje się, że tylko wybrani o tym wiedzą. Np. proszę się przyjrzeć programowi Bug Hunter pana JBW. Jest tam możliwość zmiany znacznika B. Parodia. Ale ma to swoją zaletę, bo można sprawdzić, które rozkazy są symulowane, a które naprawdę wykony- wane.
Wystarczy skasować znacznik B i uruchomić wybrany rozkaz. Jeżeli B pozostanie ska- sowany, to znaczy, że rozkaz w rzeczywis- tości nie został wykonany.
Skoro już poruszyłem temat rozkazu BRK, to dodam tylko, że jest to rozkaz dwubajtowy, tzn. jeśli mamy BRK pod adresem $600, to powrót z przerwania spowoduje wykonanie rozkazu spod $602, a nie $601.
b3 - Decimal - znacznik trybu dziesiętnego procesora (BCD)
W trybie dziesiętnym procesor inaczej wykonuje rozkazy ADC i SBC. Nie będę tu opisywał dokładnie operacji przeprowa- dzanych dodatkowo przy działaniach dziesiętnych, bo to chyba nie jest nikomu potrzebne. Np. rozkaz ADC po swoim normal- nym wykonaniu dodaje jeszcze 6 i/lub $60.
Czy to ma jakieś zastosowanie? TAK! Dosłownie przed chwilą to wymyśliłem!
Popatrzmy, w jaki sposób zamienia się cyfrę hex na ASCII. Można to zrobić np. tak:
CMP #10 ORA #'0' BCC *+4 ADC #6 +
Pewnie myślicie, że nie można tego skrócić? To patrzcie na to:
CMP #10 SED ADC #'0' CLD
To działa! A jest krótsze o dwa bajty! Co prawda czasami jest wolniejsze o cykl, ale jeśli zależy Ci na szybkości, to pewnie widzisz, że ta metoda może być szybsza przy wyświetlaniu całych bajtów (jeśli ustawiony jest D, to konwersja sprowadza się do CMP i ADC).
Oczywiście jeśli chcesz mieć kod ANTIC'a zamiast ASCII, to wystarczy zamienić '0' na $10.
b2 - Interrupts - znacznik zabronienia przerwań maskowalnych (IRQ). Jeśli jest ustawiony, to procesor nie obsługuje przerwań maskowalnych. Nie oznacza to, że przerwania te są zupełnie ignorowane! Jeżeli np. wciśniemy klawisz w chwili, gdy I jest ustawiony, ale przerwanie klawiatury dozwolone, to przerwanie zostanie wykonane po skasowaniu I.
Jakie to ma zastosowanie praktyczne? Najczęściej używamy IRQ tylko dla przerwania klawiatury. Wtedy całe IRQ może wyglądać tak:
PHA LDA #0 STA $D20E LDA #$40 STA $D20E LDA $D209 STA KEY LDA #14 STA SRT PLA RTI
I znowu pytanie: czy można to skrócić? Oczywiście. Zazwyczaj jeszcze robi się obsługę powtarzania klawiszy na VBL-ku. Wystarczy więc wykonywać IRQ tylko w czasie VBL. Robi się to w ten sposób:
CLI SEI
Teraz nie musimy przechowywać akumulatora na czas IRQ (odpada PHA/PLA), bo dokładnie wiemy, kiedy przerwanie się wykona. Ponadto IRQ nie miesza nic w czasie wyświetlania ekranu. To akurat nie jest mój pomysł, bo podpatrzyłem go w BEHIND JAGGI LINES (ZA-JE-BISTA gierka z '84 roku!).
To już koniec opisu znaczników. Mam nadzieję, że dowiedziałeś się czegoś nowego. B.