Kurs Assemblera cz. 4

ASEMBLER 6502
KOMPUTER – ZNACZY LICZYDŁO!

Jak zdążyłem już nadmienić, słowo computer w języku angielskim oznacza mniej więcej tyle co liczydło. Sugerowałoby to istnienie możliwości liczenia na naszej ma­szynie. Jak już zapewne wiesz, liczenie w BASIC problemem nie jest. Wiemy też, że wszystko, co da się zrobić w BASIC, da się też zrobić w asemblerze. l rzeczywiście, do­dawać w asemblerze można i temu też po­święcimy dzisiejsze spotkanie.

Nasz komputer jest jednak zbyt cwana ma­szyna, by po prostu wziąć dwie liczby, dodać je i wstawić w jakieś miejsce, z którego łatwo je będzie wyciągnąć. Operacja dodawania przebiega w C-64 w następujący sposób: komputer bierze podaną mu liczbę, traktując ją jako argument lub też adres, pod którym szukać ma argumentu. Po ustaleniu, czym w końcu jest argument, dodaje do niego aktual­ną zawartość akumulatora i UWAGA! – aktualny stan flagi Carry (przeniesienie). Otrzy­maną liczbę wstawia do akumulatora i odpowiednio przestawia flagi Carry (przeniesie­nie), Zero (zero), Negative (ujemne) i oVer-flow (przepełnienie).

Instrukcja, o której ciągle mówimy, nazywa się ADC. Tłumaczy się to na angielski ADC willi Carry, zaś na polski: dodawanie z prze­niesieniem. Specjalną cechą dodawania z przeniesieniem jest fakt, że do wyniku działa­nia dodaje się jeszcze zawartość flagi C. Jeśli więc chcemy, by wynikiem dodawania było to, co zwykle, bez żadnych dodatkowych sensacji, musimy wyzerować znacznik C roz­kazem CLC. ADC działa więc tak:

	ASEMBLER        MONITOR 
*=10000
LDA #$02 2710 LDA #$02
CLC 2712 CLC
ADC #$02 2713 ADC #$02
STA $2800 2715 STA $2800
BRK 2718 BRK

Jeśli zadasz sobie trud wpisania i urucho­mienia programu (G2710, SYS 10000 albo strzałka w lewo, 3 i S), to komputer zada so­bie trud dodania dwóch do dwóch i wstawie­nia wyniku do komórki $2800, czyli (10240). Po zakończeniu pracy programu (chyba nie­zauważalnie krótkiej) możemy sprawdzić efekty za pomocą np. PRINT PEEK (10240). Jest4? Powinno…

Czym jednak byłoby dodawanie bez odro­biny odejmowania? Na szczęście, 6502 ma także odejmowanie. Jest za nie odpowiedzial­na instrukcja SBC (SuBstract with Carry czyli odejmowanie z przeniesieniem). Działa ona dokładnie odwrotnie niż ADC. Odwrotnie też interpretuje wskaźnik C. To znaczy od zawar­tości akumulatora odejmuje argument a nas­tępnie odejmuje jeszcze 1, jeśli C jest równy zeru, zaś jeśli flaga C jest zapalona, ta dodat­kowa liczba odejmowana nie jest. Musimy to więc pamiętać, by znacznik przeniesienia za­palić rozkazem SEC (ciągle więc odwrotnie niż w dodawaniu). Dla przykładu odejmijmy więc od 10 liczbę 7, którą uprzednio wstawi­my do komórki $2800. Wynik wstawimy pod adres $2801.

	ASEMBLER          MONITOR 
*=10000
LDA #7 A2710 LDA #$07
STA $2800 A2712 STA $2800
SEC A2715 SEC
LDA #10 A2716 LDA #$0a
SBC $2800 A2718 SBC $2800
STA $2801 A271B STA $2801
BRK A271E BRK

Sprawdźmy teraz wynik w komórce 10241. Powinno być 3. Dla eksperymentu zmień jeszcze rozkaz SEC na CLC. I co? Jest 2? OK. Czy do tej pory nie zaskoczył, zirytował lub choćby zdziwił Cię fakt, że przy dodawaniu i odejmowaniu zawsze używany jest wskaźnik przeniesienia? Wskaźnik ten służy nam do uproszczenia wykonywanych operacji pod­czas dodawania/odejmowania dwubajtowe-go. Skomplikowanie dodawania jednobajtowego jest nieznaczne – wstawiamy tylko je­den rozkaz, który byłby zbędny, gdyby istniało dodawanie bez przeniesienia. Natomiast go­rzej byłoby, gdyby nie istniało dodawanie z przeniesieniem. Dodawanie dwubajtowe roz­szerzyłoby się ponad miarę – trzeba by do­dawać skoki warunkowe, odniesienia do flagi C, jeszcze jedno dodawanie, gdyby flaga ta była ustawiona. Oczywiście, zawsze można by powiedzieć, że projektanci mogli wstawić OBIE te instrukcje. Byłoby to jednak sprzecz­ne z ogólną filozofią procesora (szybki, mała ilość rozkazów). Dzięki temu właśnie, mimo powolniejszego zegara, procesor ten okazał się lepszym od np. Z80, znacznie bardziej skomplikowanego i zagmatwanego a przez to wolniejszego.

Czyli, musimy się po prostu przyzwycza­ić, żeby przed każdym dodawaniem wyzero­wać (CLC), a odejmowaniem, ustawić (SEC) znacznik przeniesienia, l już. Przeniesienie jest bardzo przydatne gdy dodajemy do siebie dwie liczby dwubajtowe (ew. przewidujemy, że wynik naszej operacji przekroczy 255). Musimy wówczas postąpić tak:

  1. Pierwszy ze składników dzielimy na młodszy i starszy bajt. Mł. wstawiamy do komórki $2800, st. do $2801.
  2. Drugi składnik również dzielimy na bajty. Młodszy kierujemy do $2802 a starszy ? do $2803.
  3. Wygaszamy wskaźnik przeniesienia (CLC).
  4. Dodajemy zawartość komórki $2800 do zawartości komórki $2802 a wynik odsyła­my do $2804.
  5. Dodajemy $2801 do tego, co znajdziemy w komórce $2803. Wynik trafia do $2805.

Wtedy wynik składa się z dwóch bajtów ? młodszego ($2804) i starszego ($2805). Zna­ną już nam metodą przerabiamy to na liczbę dwubajtową i odchodzimy w szczęściu. Przedtem jednak zwróć uwagę, że przed dru­gim dodawaniem wcale nie zmienialiśmy flagi C. Jeżeli wynik pierwszego dodawania bę­dzie mniejszy niż $ff (255), to znacznik C po-ostanie zgaszony i druga operacja przebiega Normalnie. Jeżeli jednak liczba ta zostanie przekroczona, to ?w pamięci” (jak przy pisemnym dodawaniu) zostaje 1, które trzeba dorzucić do dodawania wyższego rzędu. ?Pamięcią” tą, którą na papierze symbolizuje zazwyczaj jedynka w kółeczku, jest tutaj właśnie C. Jeżeli pierwsze dodawanie je ustawi, drugie będzie musiało je uwzględnić. Może ę też zdarzyć, że także po drugim dodawaniu znacznik C będzie zapalony. Wtedy musimy traktować wynik jako liczbę trzybajtową, której siedemnastym bitem jest flaga przeniesienia.

Oto zaś program na dodawanie liczb dwubitowych. Polecam go Twojej szczególnej uwadze:


ASEMBLER MONITOR
*=10000
CLC A2710 CLC
LDA $2800 A2711 LDA $2800
ADC $2802 A2714 ADC $2802
STA $2804 A2717 STA $2804
LDA $2801 A271A LDA $2801
ADC $2803 A271D ADC $2803
STA $2805 A2720 STA $2805
BRK A2721 BRK

Po udanym wpisaniu i przetestowaniu programu proponuję choć chwilę poeksperymentować i zbadać, czy wszystko się zgadza. Jeśli tak – przystąp do napisania analogicznego programu wykonującego operację odejmowania dwubajtowego. Ha! Prowadzimy tu przecież w końcu kurs PROGRAMOWANIA a nie tylko uważnego przepisywania programów. Mam nadzieję, że zadanie to zbytnio e przeciąży Twojego napiętego harmono­gramu dnia i już za miesiąc porozmawiamy o zwijaniu i rolowaniu bitów.

Kurs Assemblera cz. 3

ASEMBLER 6502
Czyli adresowanie indeksowane oraz pośrednie i co z tego wynika.

Czym właściwie jest adresowanie? Zadaję to pytanie nie bez powodu, chociaż, na dobrą sprawę, bez dokładnej znajomości tego terminu można się doskonale obejść. Jednak jasne i jednoznaczne wyłożenie tej kwestii może być zwyczajnie dużym uproszczeniem w tłumaczeniu różnych spraw z programowaniem związanych. O trybach nie wspominałem na początku naszych spotkań po prostu dlatego, że nie wydawało się to konieczne. Teraz jednak tłumaczenie coraz bardziej zawiłych problemów bez wprowadzenia tej terminologii staje się coraz trudniejsze. Dlatego też postanowiłem poświęcić dzisiejszy artykuł właśnie trybom adresowania.

Zacznijmy więc jeszcze raz: Czym właściwie jest adresowanie? Adresowanie to sposób podania komputerowi informacji o tym, jak każdy rozkaz ma być wykonany. Jeżeli dalibyśmy rozkaz np. LDA 39, to niekoniecznie byłoby wiadomo, o co nam właściwie chodzi. Dlatego też zostały opracowane różne tryby adresowania. Jeżeli przed liczbą (argumentem) postawimy znak „#”, to jako argument będzie pobrana liczba, którą wpisaliśmy za rozkazem. Ten tryb adresowania nazywamy absolutnym lub natychmiastowym. Na przykład jeśli wydamy rozkaz LDA # $98 to w akumulatorze znajdzie się szesnastkowa liczba 98.

Kolejnym prostym trybem adresowania jest adresowanie bezpośrednie (ang. immediate). Podana po rozkazie liczba jest przez komputer traktowana jako adres, pod którym ma szukać argumentu. Jeśli więc napiszemy LDA $3000, komputer odszuka w pamięci komórkę o adresie szesnastkowym 3000 (dziesiętnie 12288) a następnie jej zawartość wczyta do akumulatora. Analogicznie dzieje się, kiedy napiszemy STA $1000. Wtedy zawartość akumulatora zostanie wpisana do komórki o adresie $1000 (4096).

Szczególną odmianą tego adresowania jest adresowanie strony zerowej, czyli pierwszych 256 komórek pamięci. Jej odmienność polega na tym, że do adresowania strony zerowej potrzebna jest tylko jedna komórka. Właśnie dlatego strona zerowa jest tak często przez programistów wykorzystywana – daje to czasem duże oszczędności czasowe.

Innym trybem adresowania jest adresowanie pośrednie. Jego idea polega na tym, że adres podany po rozkazie JMP (bo JMP jest JEDYNYM rozkazem, który może być tak adresowany) jest dla komputera adresem pod którym znajduje się adres (w postaci młodszego i starszego bajtu) procedury, do której należy skoczyć. Wygląda to np. tak: JMP ($0314). Pod adresem znajduje się wektor, który wskazuje na komórkę, do której trzeba skoczyć (zwykle w tym przypadku $ea31).

Jakieś dwa miesiące temu rozmawialiśrny trochę o tym, jak wydrukować na ekranie napisy. Robiliśmy to jeszcze bez użycia różnych procedur zapisanych w ROM komputera. Jak zapewne pamiętacie, używaliśmy do tego celu rejestru X jako rejestru pomocniczego. Właśnie operacje w rodzaju LDA $AAAA,X albo STA $BBBB,X nazywamy operacjami indeksowanymi. Zaś takie podanie instrukcji LDA lub np. STA nazywane jest adresowaniem indeksowanym. Indeksować można też oczywiście i inne rozkazy, ale o tym – potem. Przypomnę jeszcze tylko znaczenie takiego adresowania. Rozkaz LDA 10000,X oznacza dla komputera, że do akumulatora ma wstawić liczbę, która znajduje się w komórce o adresie obliczonym przez dodanie 10000 do zawartości rejestru X. Rozkazy LDA i STA można również indeksować drugim z rejestrów pomocniczych, Y.

Dwa kolejne tryby adresowania nie są już takie oczywiste. Tryby te to pośredni postindeksowany i pośredni preindeksowany. Adresowanie postindeksowane ma składnię LDA ($fb),y. Liczba podana po rozkazie jest tu miejscem, w którym znajduje się adres w pamięci. Do adresu tego trzeba dodać jeszcze zawartość rejestru Y i dopiero ta liczba wskazuje lokację pamięci, z której należy pobrać liczbę do akumulatora. Do tego indeksowania można zastosować wyłącznie rejestr Y. Adresowanie preindeksowane polega na tym, do liczby po rozkazie dodajemy jeszcze zawartość rejestru X i ta suma jest adresem komórki, której zawartość wstawiamy do akumulatora. Składnia wygląda tak: LDA ($f0, X) i trzeba pamiętać, że do indeksowania można użyć wyłącznie rejestru X. Moim skromnym zdaniem, tryb ten jest dość mało praktyczny.

W rozkazach skoków warunkowych, jak np. poznane już przez nas BEQ i BNE, mimo tego, że podajemy pełne adresy pod które należy skoczyć, komputer przechowuje tylko liczbę komórek o którą musi przeskoczyć w tył lub w przód. Dlatego też nazywamy ten tryb adresowaniem względnym.

Dwa ostatnie tryby, które zostały nam do omówienia są stricte wewnętrzne i użytkownik ma na nie znikomy wpływ. Pierwszy to adresowanie akumulatora i obejmuje wszystkie operacje wykonywane na samym akumulatorze, bez udziału pamięci. Drugi zaś – to adresowanie niejawne. Należą do niego podobne rozkazy, np. poznany przez nas INX. Zwiększa on zawartość rejestru X 0 1 i wcale nie interesuje nas, gdzie we wnętrzu komputera znajduje się komórka, której rozkaz ten dotyczy. Do rodzinki tej należą jeszcze NOP, RTS, BRK i inne tego typu rozkazy.

Wiem, że podanie wszystkich trybów adresowania było troszkę „na wyrost”, ale na pewno przyda się później. Przepraszam, że może byłem tym razem trochę nudnawy i nie podałem listingów przykładowych, ale taka solidna dawka teorii zaprocentuje na pewno w przyszłości.

Kurs Assemblera cz. 2

W tym odcinku zajmiemy się już wyświetlaniem całych wyrazów, a nawet zdań. Najpierw jednak postaraj się, by w komórce $2000 (czyli 8192) znalazła się wartość 1 . Jak – to już Twój problem. Sugerowałbym jednak małe POKE…

POKE 8192,1

I po co nam to było? To proste – dzięki temu mamy w komórce pamięci o adresie 8192 zapisaną wartość i dzięki czemu możemy ją pobrać w dowolnym momencie. Ale jak? – zapyta spostrzegawczy Czytelnik. Posłużymy się rozkazem LDA. Tym razem jednak wartość po rozkazie nie będzie oznaczać LICZBY, którą chcemy wpisać do akumulatora, lecz ADRES komórki, w której będziemy szukać LICZBY, wpisanej do akumulatora. Wpisz więc programik przedstawiony poniżej:


*=$2710
LDA $2000
STA $0400
RTS
strzałka w lewo, 3, s

No i proszę, efekt zgodny z tym, co przed chwilą napisałem. Jeżeli przed liczbą po rozkazie LDA nie wpiszemy znaczka #, to komputer będzie traktował tę liczbę jako adres komórki pamięci w której jest zapisana liczba przeznaczona do wczytania do akumulatora. Teraz zajmijmy się obiecanymi przedtem słowami. Jak zapewne zdążyłeś zauważyć, wpisywanie słów dłuższych, niż dwu-trzyliterowe metodą LDA- STA przestaje być rozrywką i staje się średnio przyjemnym wklepywaniem kolejnych liczb. Jeśli umiesz programować w jakimś innym języku, zapewne przypominasz sobie jakieś pętle: FOR…NEXT, REPEAT…UNTIL, DO…LOOP i inne. Jeśli zaś niczego sobie nie przypominasz, to wiedz, że pętla, to taki chytry chwyt, dzięki któremu możesz napisać jakiś ciąg instrukcji raz, a potem powtarzać go kilka razy. Odbywa się to nie za pomocą przepisywania tego samego fragmentu programu na nowo, lecz poprzez poinformowanie komputera, że ma wykonywać tę część programu tyle razy, ile sobie zażyczysz. W asemblerze jest kilka metod zrobienia pętli. Możemy na przykład wstawić do jednej z komórek liczbę powtórzeń i po każdym przebiegu pętli zmniejszać jej wartość o 1 . Po dojściu do 0 — kończymy powtarzanie pętli. Znacznie wygodniejszym rozwiązaniem jest jednak wykorzystanie jednego z DODATKOWYCH REJESTRÓW procesora. Bowiem procesor ma nie tylko akumulator. Ma także rejestry oznaczone jako X i Y. Za pomocą np. rejestru X możemy także utworzyć pętlę i to znacznie lepszą niż przy pomocy komórki pamięci. Sama zasada będzie oczywiście taka sama. A oto przykład takiej pętli – wydrukujemy kilka liter za pomocą jednego STA!


*=$2710
LDA #2
LDX #$10
U STA $0400,X
DEX
BNE U
RTS
Strzałka w lewo, 3, s

Wszystko się zgadza? Mamy aż szesnaście literek „B”, a rozkazów wydaliśmy, jak dla trzech! Pojawiły się jednak nieznane Ci rozkazy. Zaraz je wyjaśnię.

LDX #xx – weź liczbę xx i wpisz ją do rejestru X (LoaD X register)
DEX – zmniejsz zawartość rejestru X o 1 (DEcrement X register)
STA xxxx,X – zapisz zawartość akumulatora do komórki o adresie
xxxx, zwiększonym o zawartość rejestru X. (STore Accumulator)
BNE xxxx – jeżeli ostatnia wykonana operacja nie przyniosła wyniku zerowego, wykonaj skok do adresu xxxx. (Branch, if Not Equal to zero)

Posiadacze asemblera mają właśnie okazję, by dowiedzieć się, czym właściwie jest etykieta. Popatrz tylko, co wpisałeś po rozkazie BNE – tylko literkę U. A co powinieneś wpisać? Adres, do którego komputer ma wykonać skok. Wniosek – etykieta służy nam do określenia adresu bez potrzeby dokładnej jego znajomości. Takie samo U postawiliśmy przed rozkazem STA. Komputer porównuje jedną etykietę z drugą i, jeśli się zgadzają, wykonuje skok. Przejdźmy do obiecanych całych słów. Przedtem jednak gorąco apeluję, by wszyscy zaopatrzyli się w TurboAssembler 5.1 . Jak się zaraz przekonamy, ułatwi to wpisywanie programów znacznie bardziej, niż używanie jakiegokolwiek monitora. Inne makroasemblery mają inne, sobie tylko właściwe, możliwości i mogą z tego wyniknąć nieporozumienia. Teraz jednak posiadacze monitorów muszą się posłużyć językiem BASIC. Chodzi tu o to, aby tekst do wyświetlenia na ekranie wpis określonego obszaru pamięci skąd zostanie on pobrany i wyświetli na ekranie przez program napisany w języku maszynowym.

10 A$=”PRAWDZIWY MANIAK PETLI SIE NIE BOI”
20 FOR A=1 TO LEN(A$)
30 B=ASC(MID$(A$, A, 1))
40 IF B>64 THEN B=B-64
50 POKE 8191 +A, B
60 NEXT


A 2710 LDX #$2C
A 2712 LDA $2000, X
A 2715 STA $0400, X
A 2718 DEX
A 2718 BNE $2712
A 271 A RTS

*=$2710
LDX #$2C
WPAK LDA TEKST, X
STA $0400, X
DEX
BNE WPAK
RTS
TEKST .TEXT "PRAWDZIWY MANIAK PETLI SIE NIE BOI"
strzałka w lewo, 3, s

No i mamy obiecane całe wyrazy i zdania. I tylko jeden nowy rozkaz:

LDA xxxx, X- wstaw do akumulatora zawartość komórki o adresie xxxx, zwiększonym o zawartość rejestru X.

Użytkowników asemblera informuję że instrukcja .TEXT pozwala na wpisanie w cudzysłowie jakiegokolwiek tekstu. W chwilach wolnych proponuję zastanowić się nad innym zagadnieniem: jak wyświetlić ten tekst od końca?

 

 

DZIŚ POZNALIŚMY INSTRUKCJE:
LDA xxxx – wstaw zawartość komórki o adresie xxxx do akumulatora
LDA xxxx, X – wstaw zawartość komórki o adresie xxxx, zwiększonym o zawartość rejestru X do akumulatora
STA xxxx, X – zapisz akumulator do komórki o adresie xxxx, zwiększonym o zawartość rejestru X
LDX #xx – wstaw liczbę xx do rejestru xx
DEX – zmniejsz zawartość rejestru X o 1
BNE xxxx – skocz do komórki o adresie xxxx, jeżeli wynik ostatniej wykonanej operacji nie był równy 0

Kurs Assemblera cz. 1

Miałeś chyba wystarczająco dużo czasu, by przynajmniej zacząć kompletować oprogramowanie i książki o którym pisałem w poprzednim odcinku. Jeśli dysponujesz już omawianym wyposażeniem możemy zabrać się za ćwiczenia praktyczne. Pierwszym celem, jaki sobie postawimy, będzie wyświetlenie na ekranie literki „A”. Za pomocą BASIC zrobić to można bardzo prosto – wystarczy napisać:

PRINT „A”
wcisnąć RETURN. „A” na pewno się pokaże.
No dobrze, ale jeśli chcemy mieć „A” w lewym górnym rogu?
Proszę bardzo:

PRINT CHR$ (147); „A”
Bardzo ładnie. Skrzaty mówią jednak, że w komputerze Commodore 64 można zrobić wszystko za pomocą jednej lub kilku instrukcji POKE. Wpisz więc:

POKE 1024,1
Tak naprawdę BASIC stanowi jedynie ogniwo pośrednie pomiędzy użytkownikiem i asemblerem. Komputer nie jest w stanie zrozumieć kierowanych do niego angielskich słów PRINT, POKE czy LIST bez odpowiedniego programu w asemblerze. Wynika z tego, że program na wyświetlenie literki „A” napisany w BASIC został, PRZED wykonaniem, przetłumaczony na język maszynowy. Czy więc nie byłoby prościej dać komputerowi rozkazy od razu w asemblerze? Otóż byłoby! Uruchom teraz jeden ze swoich programów (monitor, asembler). Jeśli masz monitor, to wpisz:


A 2710 LDA #$01
A 2712 STA $0400
A 2715 RTS

Teraz wpisz G 2710 i wciśnij RETURN. Jeśli dysponujesz programem TurboAssembler wpisz:


*=$2710
LDA #$01
STA $0400 RTS

Naciśnij teraz strzałkę w lewo, trójkę, a potem „S”.

Obie operacje powinny dać taki sam efekt: w lewym górnym rogu ekranu pojawi się literka „A”. Jeśli niezbyt podoba się nam „A”, możemy ją zastąpi dowolnym innym znakiem, wpisując zamiast LDA #$01, np. LDA #$02 (litera B), LDA #$03 (C) i tak dalej. Jeśli nie podoba Ci się miejsce w którym jest wyświetlany Twój znak spróbuj zmodyfikować wiersz zawierający instrukcję STA $0400.

Tu należy się Tobie słowo wyjaśnienia na temat struktury ekranu na którym pojawia się tekst. Zastanów się nad następującym zagadnieniem: włączysz komputer, wpisujesz jakiś tekst, powiedzmy „ZABAWA Z KOMPUTEREM”. O ile w domu nie wyłączą prądu tekst ten będzie na ekranie widoczny tak długo jak komputer będzie włączony, chociaż ekran telewizora czy monitora na pewno NIE JEST wyposażony w pamięć. Skąd więc komputer wie GDZIE i JAKI tekst na ekranie należy umieścić?

Rozwiązano to niezwykle prosto: dane przeznaczone do wyświetlenia na ekranie są najpierw zapisywane w specjalnym obszarze pamięci określanej zwykle mianem „pamięci ekranowej”. Stamtąd są one pobierane i po przetworzeniu wysyłane na ekran telewizora. Wniosek z tego jest prosty: tekst można wyświetlać tak długo jak długo jest on zawarty w pamięci ekranowej komputera. Podobnie jest z grafiką i kolorami, chociaż dla nich zarezerwowano inne obszary pamięci, mają one również różną wielkość (ok. 1 KB dla ekranu tekstowego lub 8 KB dla grafiki).

Obszar pamięci ekranowej (tzn. pamięci przeznaczonej do przechowywania TEKSTU) jest zlokalizowany w Commodore 64 od adresu 1024 do 2023 co w systemie szesnastkowym odpowiada adresom $0400 do $07e7. Zwróć uwagę, że adres $0400 wymieniony w drugim wierszu programu odpowiada dokładnie komórce o adresie 1024 będącej pierwszą komórką pamięci ekranu tekstowego. Zmiana adresu $0400 powinna więc spowodować wyświetlenie litery „A” w innym miejscu ekranu. Spróbujmy teraz przyjrzeć się uważnie naszemu programikowi.

LDA jest mnemonikiem czyli trzyliterowym skrótem (synonimem) rozkazu LoaD Accumulator (załaduj akumulator). Wymieniona za nim liczba (zwana argumentem) jest, w tym wypadku, wartością do wczytania do akumulatora.

STA jest mnemonikiem rozkazu STore Accumulator (zapisz zawartość akumulatora). Rozkaz ten powoduje zapisanie liczby wpisanej uprzednio do akumulatora w miejscu określonym liczbą wpisaną bezpośrednio po STA. W naszym programie będzie to komórka pamięci o adresie $0400 (1024 dziesiętnie).

Rozkaz RTS umożliwia powrót do BASIC zaraz po zakończeniu naszego programu. Jest to jeden z rozkazów nie wymagających podawania argumentów.

Jeśli zajrzysz do spisu rozkazów asemblera, zobaczysz:

LDA – LoaD Accumulator (załaduj akumulator)
STA – STore Accumulator (zapisz akumulator)
RTS – ReTurn from Subroutine (powróć z podprogramu)

Możemy z tego wywnioskować, że miejsce w pamięci, do którego kom ter wpisał liczbę $01 (1 dziesiętnie), nazywa się akumulatorem. Akumulator może przechowywać tylko jedną liczbę; jeśli wpiszesz doń nową liczbę, stara zawartość akumulatora zostanie bezpowrotnie skasowana. No dobrze. O rozkazach, których używamy, wiemy już nieco więcej. Wiemy też, czego się po nich spodziewać. Wpiszmy więc:


*=$2710
LDA #$01
STA $0400
LDA #$02
STA $0401
LDA #$03
STA $0402
RTS
Strzałka w lewo "3","a"

Teraz na ekranie powinien się pokazać ciąg liter: A, B. C. I to wszystko za pomocą dwóch, najprostszych rozkazów asemblera. Aby nabrać wprawy proponuję jeszcze trochę samemu poeksperymentować. Spróbuj na przykład wyświetlić zamiast liter „ABC” litery X, Y i Z w drugim wierszu na ekranie (w jednym wierszu mieści się 40 znaków).

Po jakichś trzech, czterech próbach wyświetlania liter, proponuję coś bardziej rozbudowanego: oprócz litery, zmienić jeszcze jej kolor. Wiemy już, że kolory są przechowywane, podobnie jak tekst, w określonym obszarze pamięci RAM komputera; pamięć tę nazywamy pamięcią koloru. Mieści się ona w obszarze od $d800 a $dbff (55296 – 56319 dziesiętnie).

Jak jednak obliczyć numer komórki, do której wstawimy kolor? To stosunkowo proste – od adresu komórki pamięci ekranowej, w której zawarta jest nasza litera (np. $0428) odejmij $0400 (zostanie $28), a potem dodaj $d800 w wyniku czego otrzymasz $d828. Wartość zapisana w tej komórce ($d828) decyduje o kolorze znaku zapisanego w pamięci ekranowej (komórka $0428). Jeśli masz problemy z przeliczaniem adresów w systemie szesnastkowym to podaję adresy w postaci dziesiętnej:


pamięć ekranowa

$0400 = 1024
+$0028 = 40
-------------
$0428 = 1064

pamięć koloru
$d800 = 55296
+$0028 = 40
--------------
$d828 = 55336

Jeżeli kod szesnastkowy nie chce Ci wejść żadną miarą do głowy może warto byłoby zaopatrzyć się w jakiś podręczny kalkulator mający możliwości przeliczania wartości w trzech systemach: dziesiętnym, szesnastkowym i binarnym. Teraz wpisz i spróbuj uruchomić ten oto program:


*=$2710
LDA #$01
STA $0428
LDA #$00
STA $D828
RTS
Strzałka w lewo "3", "a"

Mamy już czarną literę „A”! Może być też biała, czerwona – jaką tylko chcemy. Trzeba w tym celu zmienić wartość argumentu po rozkazie LDA w linii 2715.

Dziś poznaliśmy instrukcje:
LDA #xx – wstaw wartość xx do akumulatora
STA xxxx – zapisz akumulator do komórki o adresie xxxx
RTS – powróć z podprogramu.

Podstawowe polecenia monitora

Dla osób chcących programować lub modyfikować programy w języku wewnętrznym znajomość poleceń monitorów jest po prostu niezbędna. Jednocześnie powszechnie wiadomo, że większość użytkowników C-64 korzysta z programów typu monitor bez jakiejkolwiek instrukcji. Ot, po prostu „na czuja” nawet do popularnych modułów z monitorami sprzedawcy nie zawsze dołączają odpowiednią dokumentację).

Poniższe zestawienie obejmuje naturalnie jedynie instrukcje podstawowe, bowiem tylko one mają praktycznie we wszystkich monitorach identyczny format. Uwaga: oczywiście wiecie o tym, ale dla porządku przypominamy, że wszystkie argumenty w programie monitora muszą być podawane w kodzie heksadecymalnym (szesnastkowyrn).

A (ASSEMBLE)

Umożliwia wprowadzenie linii zawierającej kod asemblera (mnemonik + argument). Programista musi określić numer pierwszej komórki pamięci, od której będzie zaczynał się program, dalszą numerację zazwyczaj przejmuje program monitora. Format instrukcji:

A C000 JSR $FCE2 – gdzie:
A – rozkaz monitora,
C000 – adres komórki pamięci,
JSR – mnemonik,
$FCE2 – argument mnemonika.

Jeżeli podczas wprowadzania instrukcji programista popełni błąd, wówczas na ekranie zostanie wyświetlony znak zapytania „?”, albo program monitora nie policzy (wyświetli) następnego adresu pamięci.

C (COMPARE)

Instrukcja ta porównuje ze sobą dwa obszary pamięci. W wypadku różnic wyświetlane są komórki, w których ta różnica występuje. W niektórych monitorach wyświetlane są również zawartości tych komórek. Format instrukcji:

C 1000 2000 5400

W wyniku działania tej instrukcji zostają porównane dwa obszary. W przykładzie: pierwszy, począwszy od adresu 1000 do 2000, drugi – od 5000 do 6000

D (DISASSEMBLE)

Za pomocą tego rozkazu dokonujemy disasemblacji tzn. zamiany kodu maszynowego na kody asemblera. Korzystając z tego rozkazu należy określić adres początku obszaru pamięci, od którego ma się rozpocząć disasemblacja. Format instrukcji:

D FCE2 – gdzie:
D – instrukcja monitora,
FCE2 – początkowy adres pamięci, od którego chcemy rozpocząć disasemblację.

W przypadku, gdy program monitora natrafi na kod, który nie ma mnemonika, wówczas wyświetlone zostaną trzy znaki zapytania – (???).

F (FILL)

Rozkaz ten powoduje wypełnienie zadanego obszaru pamięci podaną wartością lub wartościami. Format instrukcji:

F C000 CFFF 01 02 – gdzie:
F – instrukcja monitora,
C000 – Początkowy adres pamięci, od którego można zacząć wypełnianie,
CFFF – końcowy adres pamięci,
O1 02 – wartości, którymi można wypełniać zadaną Pamięć.

G (GO)

Instrukcja ta uruchamia program począwszy od podanego adresu. Format instrukcji:

G C000 – gdzie:
G – instrukcja monitora, .
C000 – adres, od którego zostanie uruchomiony program.

W przypadku, gdy w programie wystąpi instrukcja RTS lub BRK, program automatycznie wróci do monitora.

H (HUNT)

Zadaniem tej instrukcji jest poszukiwanie w zadanym obszarze pamięci sekwencji danych. Dane te możemy podać w formie danych bajtów lub w formie znaków. Format instrukcji:

1. H C000 C100 78 A9 lub
2. H C000 C100 ‚TEXT’ – gdzie:
H – instrukcja monitora,
C000 – początkowy adres pamięci, od którego można poszukiwać danych,
CFFF – końcowy adres pamięci,
78 A9 lub TEXT – poszukiwane dane.

Jeżeli w przeszukiwanym obszarze wystąpią podane dane, wówczas zostaną wyświetlone ich adresy.

L (LOAD)

Odpowiednik instrukcji LOAD w BASIC-u. Zazwyczaj dostępne są dwa formaty instrukcji:

1. L”Nazwa programu” 08
Powoduje załadowanie programu pod podaną nazwą z urządzenia o numerze 8 (stacja dysków) pod adres podany w nagłówku programu dwa pierwsze bajty.

2. L”Nazwa programu” 01 C000
Powoduje załadowanie programu pod podaną nazwą z urządzenia o numerze 1 magnetofon) pod adres C000. I tu mała uwaga: niektóre monitory nie są wyposażone w komunikaty błędów przy operacji wczytywania, co jest istotne dla użytkowników magnetofonów, gdyż program może zostać wczytany z błędem.

M (MONITOR)

W wyniku działania tej instrukcji na ekranie zostanie wyświetlona zawartość pamięci w formie liczb szesnastkowych. Zawartość pamięci można modyfikować najeżdżając kursorem na daną wartość, a następnie wprowadzając nową. Po całej operacji należy nacisnąć klawisz RETURN. Format instrukcji:

M C000 C450 – gdzie:
C000 – adres, od którego zaczyna się oglądanie pamięci, C050 – adres końcowy.

W niektórych monitorach, oprócz zawartości pamięci w firmie Iiczb szesnastkowych, zostaje wyświetlona zawartość w kodzie ASCII. Jeżeli dany znak w kodzie ASCII nie ma swej farmy możliwej do zobrazowania na ekranie, zostaje zastąpiony kropką.

R (REGISTER)

Wydanie tej dyrektywy powoduje wyświetlenie zawartości rejestrów procesora. Najczęściej są to:

PC – licznik programu,
SR – bajt stanu procesora,
AC – akumulator,
XR – rejestr indeksowy X,
YR – rejestr indeksowy Y,
SP – wskaźnik stosu.

Ponadto w niektórych monitorach wyświetlane są adresy przerwań IRQ i NMI. Jeżeli chce się zmodyfikować zawartość jakiego rejestru, wystarczy najechać kursorem w odpowiednie miejsce, za pomocą klawiatury wpisać nowi wartość i nacisną klawisz RETURN.

S (SAVE)

Odpowiednik instrukcji SAVE w BASIC-u. Pozwala na zapisanie na nośniku zewnętrznym dysk lub magnetofon) zawartości podanego obszaru pamięci. Format instrukcji:

S”Nazwa programu” 08 0801 2020 – gdzie:
S – instrukcja monitora,
08 – nr urządzenia zewnętrznego,
0801 – adres startowy,
2020 – adres końcowy.

W wyniku działania powyższej instrukcji na dyskietce (urządzenie 8) nagrany zostanie program o adresie startowym 0801 i końcowym 2020.

T (TRANSFER)

Rozkaz ten służy do przenoszenia dowolnych obszarów pamięci. Użytkownik musi określić adresy początku i końca wybranego do przeniesienia obszaru oraz adres początku obszaru, do którego ma nastąpi przeniesienie. Format:

T 0801 1000 C000
W wyniku działania tej instrukcji wartości będące w obszarze począwszy od adresu 0801, a skończywszy na adresie 1000, zostaną przeniesione do obszaru począwszy od adresu C000.

V (VERIFY)

Odpowiednik instrukcji VERIFY w BASIC-u. Pozwala na zweryfikowanie zapisanego na dyskietce czy taśmie programu. Format instrukcji:

V „Nazwa programu” 01 0801 – gdzie:
V – instrukcja monitora,
01 – nr urządzenia zewnętrznego magnetofon),
0801- adres startowy.

I tu mała uwaga: spotkałem się z monitorami, w których program monitora wykazywał, że weryfikacja została przeprowadzona bezbłędnie, mimo że wcześniej wprowadziłem zmiany w programie.

Lista rozkazów procesora 6502


Wersja pod HTML Excel dostepna jest rowniez w tym pliku (10 KB)

ADC

DODANIE Z PRZENIESIENIEM

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

x

x

 

 

 

 

x

x

A+M+C -> A,C

 

 

 

 

 

Pośrednie postindeksowe

ADC (aa),Y

71

2

5+1

Pośrednie preindeksowe

ADC (aa,X)

61

2

6

Indeksowe Y

ADC aaaa,Y

79

3

4+1

Natychmiastowe

ADC # dd

69

2

2

Indeksowe na stronie 0

ADC aa,X

75

2

4

Bezpośrednie

ADC aaaa

6d

3

4

Indeksowe X

ADC aaaa,X

7d

3

4+1

Bezpośrednie na stronie 0

ADC aa

65

2

3

AND

ILOCZYN LOGICZNY

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

x

 

 

 

 

 

x

 

A^M -> A

 

 

 

 

 

Bezpośrednie na stronie 0

AND aa

25

2

3

Pośrednie postindeksowe

AND (aa),Y

31

2

5+1

Natychmiastowe

AND # dd

29

2

2

Indeksowe na stronie 0

AND aa,X

35

2

4

Indeksowe X

AND aaaa,X

3d

3

4+1

Bezpośrednie

AND aaaa

2d

3

4

Indeksowe Y

AND aaaa,Y

39

3

4+1

Pośrednie preindeksowe

AND (aa,X)

21

2

6

ASL

PRZESUNIĘCIE W LEWO

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

x

 

 

 

 

 

x

x

C <- 76543210 <- 0

 

 

 

 

 

Implikowane

ASL

0a

1

2

UWAGA 1

Bezpośrednie na stronie 0

ASL aa

06

2

5

Indeksowe na stronie 0

ASL aa,X

16

2

6

Bezpośrednie

ASL aaaa

0e

3

6

Indeksowe X

ASL aaaa,X

1e

3

7

BCC

SKOK, GDY NIE MA PRZENIESIENIA

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

 

 

 

 

 

 

 

 

SKOK

BRAK

SKOK GDY C = 0

 

 

 

 

 

 

Względne

BCC dp

90

2

3+1

2

BCS

SKOK, GDY PRZENIESIENIE

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

 

 

 

 

 

 

 

 

SKOK

BRAK

SKOK GDY C = 1

 

 

 

 

 

 

Względne

BCS dp

b0

2

3+1

2

BEQ

SKOK, GDY REZULTAT ZEROWY

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

 

 

 

 

 

 

 

 

SKOK

BRAK

SKOK GDY Z = 1

 

 

 

 

 

 

Względne

BEQ dp

f0

2

3+1

2

BIT

TESTOWANIE BITÓW

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

7 6

 

 

 

 

x

A^M,Z ; 7->N,6->V

 

 

 

 

 

Bezpośrednie na stronie 0

BIT aa

24

2

3

UWAGA 2

Bezpośrednie

BIT aaaa

2c

3

4

BMI

SKOK, GDY REZULTAT UJEMNY

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

 

 

 

 

 

 

 

 

SKOK

BRAK

SKOK GDY N = 1

 

 

 

 

 

 

Względne

BMI dp

30

2

3+1

2

BNE

SKOK, GDY REZULTAT NIEZEROWY

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

 

 

 

 

 

 

 

 

SKOK

BRAK

SKOK GDY Z = 0

 

 

 

 

 

 

Względne

BNE dp

d0

2

3+1

2

BPL

SKOK, GDY REZULTAT NIEUJEMNY

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

 

 

 

 

 

 

 

 

SKOK

BRAK

SKOK GDY N = 0

 

 

 

 

 

 

Względne

BPL dp

10

2

3+1

2

BRK

PRZERWANIE PROGRAMOWE

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

 

1

 

1

PC+2Ą,PĄ

 

 

 

 

 

Implikowane

BRK

00

1

7

BVC

SKOK, GDY NIE MA NADMIARU

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

 

 

 

 

 

 

 

 

SKOK

BRAK

SKOK GDY V = 0

 

 

 

 

 

 

Względne

BVC dp

50

2

3+1

2

BVS

SKOK, GDY NADMIAR

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

 

 

 

 

 

 

 

 

SKOK

BRAK

SKOK GDY V = 1

 

 

 

 

 

 

Względne

BVS dp

70

2

3+1

2

CLC

ZEROWANIE ZNACZNIKA PRZENIESIENIA

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

 

 

0

0 -> C

 

 

 

 

 

Implikowane

CLC

18

1

2

CLD

USTAWIENIE TRYBU BINARNEGO

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

 

0

0 -> D

 

 

 

 

 

Implikowane

CLD

d8

1

2

CLI

WŁĄCZENIE PRZERWAŃ

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

 

0

0 -> I

 

 

 

 

 

Implikowane

CLI

58

1

2

CLV

ZEROWANIE ZNACZNIKA NADMIARU

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

0

 

0 -> V

 

 

 

 

 

Implikowane

CLV

b8

1

2

CMP

PORÓWNANIE Z AKUMULATOREM

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

x

 

 

 

 

x

x

A<>M,N,Z,C

 

 

 

 

 

Natychmiastowe

CMP # dd

c9

2

2

Bezpośrednie na stronie 0

CMP aa

c5

2

3

Indeksowe na stronie 0

CMP aa,X

d5

2

4

Bezpośrednie

CMP aaaa

cd

3

4

Indeksowe X

CMP aaaa,X

dd

3

4+1

Indeksowe Y

CMP aaaa,Y

d9

3

4+1

Pośrednie preindeksowe

CMP (aa,X)

c1

2

6

Pośrednie postindeksowe

CMP (aa),Y

d1

2

5+1

CPX

PORÓWNANIE Z REJESTREM X

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

x

 

 

 

 

x

x

X<>M,N,Z,C

 

 

 

 

 

Natychmiastowe

CPX # dd

e0

2

2

Bezpośrednie na stronie 0

CPX aa

e4

2

3

Bezpośrednie

CPX aaaa

ec

3

4

CPY

PORÓWNANIE Z REJESTREM Y

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

x

 

 

 

 

x

x

Y<>M,N,Z,C

 

 

 

 

 

Natychmiastowe

CPY # dd

c0

2

2

Bezpośrednie na stronie 0

CPY aa

c4

2

3

Bezpośrednie

CPY aaaa

cc

3

4

DEC

ZMNIEJSZENIE O 1

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

x

 

 

 

 

x

M-1 -> M

 

 

 

 

 

Bezpośrednie na stronie 0

DEC aa

c6

2

5

UWAGA 1

Indeksowe na stronie 0

DEC aa,X

d6

2

6

Bezpośrednie

DEC aaaa

ce

3

6

Indeksowe X

DEC aaaa,X

de

3

7

DEX

ZMNIEJSZENIE O 1 REJESTRU X

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

x

 

x

X-1 -> X

 

 

 

 

 

Implikowane

DEX

ca

1

2

DEY

ZMNIEJSZENIE O 1 REJESTRU Y

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

x

 

x

Y-1 -> Y

 

 

 

 

 

Implikowane

DEY

88

1

2

EOR

SUMA MODULO DWA

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

x

 

 

 

 

x

A (+) M -> A

 

 

 

 

 

Natychmiastowe

EOR # dd

49

2

2

Bezpośrednie na stronie 0

EOR aa

45

2

3

Indeksowe na stronie 0

EOR aa,X

55

2

4

Bezpośrednie

EOR aaaa

4d

3

4

Indeksowe X

EOR aaaa,X

5d

3

4+1

Indeksowe Y

EOR aaaa,Y

59

3

4+1

Pośrednie preindeksowe

EOR (aa,X)

41

2

6

Pośrednie postindeksowe

EOR (aa),Y

51

2

5+1

INC

ZWIĘKSZENIE O 1

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

x

 

 

 

 

x

M+1 -> M

 

 

 

 

 

Bezpośrednie na stronie 0

INC aa

e6

2

5

UWAGA 1

Indeksowe na stronie 0

INC aa,X

f6

2

6

Bezpośrednie

INC aaaa

ee

3

6

Indeksowe X

INC aaaa,X

fe

3

7

INX

ZWIĘKSZENIE O 1 REJESTRU X

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

x

 

x

X+1 -> X

 

 

 

 

 

Implikowane

INX

e8

1

2

INY

ZMNIEJSZENIE O 1 REJESTRU Y

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

x

 

x

Y+1 -> Y

 

 

 

 

 

Implikowane

INY

c8

1

2

JMP

SKOK

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

 

 

 

 

aaaa -> PC

 

 

 

 

 

Bezpośrednie

JMP aaaa

4c

3

3

UWAGA 3

Pośrednie

JMP (aaaa)

6c

3

5

JSR

SKOK DO PODPROGRAMU

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

 

PC+2Ą,aaaa -> PC

 

 

 

 

 

UWAGA 4

Bezpośrednie

JSR aaaa

20

3

6

LDA

ŁADOWANIE AKUMULATORA

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

x

 

 

 

 

x

M -> A

 

 

 

 

 

Natychmiastowe

LDA # dd

a9

2

2

Bezpośrednie na stronie 0

LDA aa

a5

2

3

Indeksowe na stronie 0

LDA aa,X

b5

2

4

Bezpośrednie

LDA aaaa

ad

3

4

Indeksowe X

LDA aaaa,X

bd

3

4+1

Indeksowe Y

LDA aaaa,Y

b9

3

4+1

Pośrednie preindeksowe

LDA (aa,X)

a1

2

6

Pośrednie postindeksowe

LDA (aa),Y

b1

2

5+1

LDX

ŁADOWANIE REJESTRU X

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

x

 

 

 

 

x

M -> X

 

 

 

 

 

Natychmiastowe

LDX # dd

a2

2

2

Bezpośrednie na stronie 0

LDX aa

a6

2

3

Indeksowe na stronie 0

LDX aa,Y

b6

2

4

Bezpośrednie

LDX aaaa

ae

3

4

Indeksowe Y

LDX aaaa,Y

be

3

4+1

LDY

ŁADOWANIE REJESTRU Y

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

x

 

 

 

 

x

M -> Y

 

 

 

 

 

Natychmiastowe

LDY # dd

a0

2

2

Bezpośrednie na stronie 0

LDY aa

a4

2

3

Indeksowe na stronie 0

LDY aa,X

b4

2

4

Bezpośrednie

LDY aaaa

ac

3

4

Indeksowe X

LDY aaaa,X

bc

3

4+1

LSR

PRZESUNIĘCIE W PRAWO

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

0

 

 

 

 

x

x

0 -> 76543210 -> C

 

 

 

 

 

Implikowane

LSR

4a

1

2

Bezpośrednie na stronie 0

LSR aa

46

2

5

Indeksowe na stronie 0

LSR aa,X

56

2

6

Bezpośrednie

LSR aaaa

4e

3

6

Indeksowe X

LSR aaaa,X

5e

3

7

NOP

NIC NIE RÓB

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

 

 

 

 

 

 

Implikowane

NOP

ea

1

2

ORA

SUMA LOGICZNA

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

x

 

 

 

 

x

AĄ M -> A

 

 

 

 

 

Natychmiastowe

ORA # dd

09

2

2

Bezpośrednie na stronie 0

ORA aa

05

2

3

Indeksowe na stronie 0

ORA aa,X

15

2

4

Bezpośrednie

ORA aaaa

0d

3

4

Indeksowe X

ORA aaaa,X

1d

3

4+1

Indeksowe Y

ORA aaaa,Y

19

3

4+1

Pośrednie preindeksowe

ORA (aa,X)

01

2

6

Pośrednie postindeksowe

ORA (aa),Y

11

2

5+1

PHA

ZAPIS AKUMULATORA NA STOS

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

 

A Ą

 

 

 

 

 

Implikowane

PHA

48

1

3

PHP

ZAPIS REJESTRU ZNACZNIKÓW NA STOS

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

 

P Ą

 

 

 

 

 

Implikowane

PHP

08

1

3

PLA

ZDJĘCIE AKUMULATORA ZE STOSU

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

x

 

x

A ^

 

 

 

 

 

Implikowane

PLA

68

1

3

PLP

ZDJĘCIE REJESTRU ZNACZNIKÓW ZE STOSU

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

z

e

 

s

t

o

s

u

P ^

 

 

 

 

 

Implikowane

PLP

28

1

3

ROL

OBRÓT W LEWO

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

x

 

 

 

 

x

x

C <- 76543210 <- C

 

 

 

 

 

Implikowane

ROL

2a

1

2

UWAGA 1

Bezpośrednie na stronie 0

ROL aa

26

2

5

Indeksowe na stronie 0

ROL aa,X

36

2

6

Bezpośrednie

ROL aaaa

2e

3

6

Indeksowe X

ROL aaaa,X

3e

3

7

ROR

OBRÓT W PRAWO

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

x

 

 

 

 

x

x

C -> 76543210 -> C

 

 

 

 

 

Implikowane

ROR

6a

1

2

UWAGA 1

Bezpośrednie na stronie 0

ROR aa

66

2

5

Indeksowe na stronie 0

ROR aa,X

76

2

6

Bezpośrednie

ROR aaaa

6e

3

6

Indeksowe X

ROR aaaa,X

7e

3

7

RTI

POWRÓT Z PRZERWANIA

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

z

e

 

s

t

o

s

u

P ^, PC ^

 

 

 

 

 

Implikowane

RTI

40

1

6

RTS

POWRÓT Z PODPROGRAMU

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

 

PC ^, PC+1 -> PC

 

 

 

 

 

UWAGA 5

Implikowane

RTS

60

1

6

SBC

ODEJMIJ Z PRZENIESIENIEM

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

x

x

 

 

 

 

x

x

A-M-C -> A

 

 

 

 

 

Natychmiastowe

SBC # dd

e9

2

2

UWAGA 6

Bezpośrednie na stronie 0

SBC aa

e5

2

3

Indeksowe na stronie 0

SBC aa,X

f5

2

4

Bezpośrednie

SBC aaaa

ed

3

4

Indeksowe X

SBC aaaa,X

fd

3

4+1

Indeksowe Y

SBC aaaa,Y

f9

3

4+1

Pośrednie preindeksowe

SBC (aa,X)

e1

2

6

Pośrednie postindeksowe

SBC (aa),Y

f1

2

5+1

SEC

USTAWIENIE ZNACZNIKA PRZENIESIENIA

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

 

1

1 -> C

 

 

 

 

 

Implikowane

SEC

38

1

2

SED

USTAWIENIE TRYBU DZIESIĘTNEGO

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

 

1

1 -> D

 

 

 

 

 

Implikowane

SED

f8

1

2

SEI

WŁĄCZENIE PRZERWAŃ

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

 

1

1 -> I

 

 

 

 

 

Implikowane

SEI

78

1

2

STA

PAMIĘTAJ AKUMULATOR

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

 

 

 

 

A -> M

 

 

 

 

 

Bezpośrednie na stronie 0

STA aa

85

2

3

Indeksowe na stronie 0

STA aa,X

95

2

4

Bezpośrednie

STA aaaa

8d

3

4

Indeksowe X

STA aaaa,X

9d

3

5

Indeksowe Y

STA aaaa,Y

99

3

5

Pośrednie preindeksowe

STA (aa,X)

81

2

6

Pośrednie postindeksowe

STA (aa),Y

91

2

6

STX

PAMIĘTAJ REJESTR X

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

 

 

 

 

X -> M

 

 

 

 

 

Bezpośrednie na stronie 0

STX aa

86

2

3

Indeksowe na stronie 0

STX aa,Y

96

2

4

Bezpośrednie

STX aaaa

8e

3

4

STY

PAMIĘTAJ REJESTR Y

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

 

 

 

 

Y -> M

 

 

 

 

 

Bezpośrednie na stronie 0

STY aa

84

2

3

Indeksowe na stronie 0

STY aa,X

94

2

4

Bezpośrednie

STY aaaa

8c

3

4

TAX

PRZESŁANIE AKUMULATORA DO REJESTRU X

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

x

 

x

A -> X

 

 

 

 

 

Implikowane

TAX

aa

1

2

TAY

PRZESŁANIE AKUMULATORA DO REJESTRU Y

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

x

 

x

A -> Y

 

 

 

 

 

Implikowane

TAY

a8

1

2

TSX

PRZESŁANIE WSKAŹNIKA STOSU DO REJESTRU X

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

x

 

x

S -> X

 

 

 

 

 

Implikowane

TSX

ba

1

2

TXA

PRZESŁANIE REJESTRU X DO AKUMULATORA

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

x

 

x

X -> A

 

 

 

 

 

Implikowane

TXA

8a

1

2

TXS

PRZESŁANIE REJESTRU X DO WSKAŹNIKA STOSU

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

x

 

x

X -> S

 

 

 

 

 

Implikowane

TXS

9a

1

2

TYA

PRZESŁANIE REJESTRU Y DO AKUMULATORA

N

V

B

D

I

Z

C

TRYB ADRESOWANIA

MNEMONIK

KOD

BAJTY

CYKLE

x

 

x

Y -> A

 

 

 

 

 

Implikowane

TYA

98

1

2

Indeks alfabetyczny mnemoników

ADC
AND
ASL

BCC
BCS
BEQ
BIT
BMI
BNE
BPL
BRK
BVC
BVS

CLC
CLD
CLI
CLV
CMP
CPX
CPY

DEC
DEX
DEY

EOR

INC
INX
INY

LDA
LDX
LDY
LSR

NOP

ORA

PHA
PHP
PLA
PLP

ROL
ROR
RTI
RTS

SBC
SEC
SED
SEI
STA
STX
STY

TAX
TAY
TSX
TXA
TXS
TYA

UWAGI:
1 Adresowanie indeksowe X zawsze 7 cykli, niezależnie od granic stron.
2 Znaczniki N i V ustawiane przed wykonaniem operacji, Z po wykonaniu
3 Adres (aaaa) nie może znależć się na styku dwu stron pamięci, gdyż procesor pobierze młodszy bajt adresu z wyższej a starszy bajt z niższej – z komórki 00 na tej stronie.
4 Po wykonaniu instrukcji JSR, procesor odłoży na stos pomniejszony o 1, adres kolejnego rozkazu za instrukcją JSR aaaa.
5 Po napotkaniu RTS procesor pobierze ze stosu adres powrotu do podprogramu (pierwszy jest młodszy bajt).
6 Jeżeli procesor pracuje w systemie BCD (10-tny), to +1 cykl.

Turbo Assembler – opis

Wgraj Turbo Assembler wpisując polecenie

LOAD”ASS-NAZWA”,8,1
i uruchom go wpisując (jeśli nie jest spakowany):

SYS(9*4096) lub SYS 36864
Jeśli jest spakowany, wpisz polecenie RUN.

UWAGA : Znak „{” w tym artykule oznacza „strzałkę” w lewo (Escape) na klawiaturze Commodore 64. Oznacza po prostu, że należy ten klawisz wpierw wcisnąć, następnie wykonać dalsze polecenia.

KURSOR:

CRSR LEFT/RIGHT

ruchy kursora w lewo/prawo

CRSR UP/DOWN

przesuwanie o 1 linię do góry/w dół

RETURN

wstawia linię (jeśli jest tryb „insert line”) i idzie do tej linii

SHIFT+RETURN

skok do następnej linii

F1/F7

scroll 20 linii w górę/dół

F2/F8

skok do początku/końca kodu źródłowego

{CRSR UP/DOWN

scroll 200 linii w górę/dół

{Q

skok do początku linii

{N

skok do linii #

{Y

skok do końca linii

DEL

usuwanie znaku od strony lewej kursora

INST

auto włączenie char mode on/off

{DEL

kasuje aktualną linię

{INST

auto wstawianie line mode on/off

{RETURN

wstawienie linii z pozycji kursora


OPERACJE NA LINIACH:

{2

wstawia linię ‚;—————————————‚

{/

usuwanie z pozycji kursora do końca linii

{SPACE

wypełnianie linii SPACJAMI

{^

(strzałka w górę) kopiowanie linii

{=

wycinanie linii

{~

(funt Ł) wstawia skopiowaną linię

{Z

undo linii (wykonywalne po {2, {/, {SPACJA, {~)


OPERACJE NA BLOKACH I ZNAKACH:

{M

wstaw zaznaczenie (#0-9), blok startowy (#s) lub blok końcowy (#e)

{G

idź do zaznaczenia(#0-9)

{;

usuń zaznaczenie(0-9)

{:

lista wszystkich zaznaczeń

{B

operacje na blokach: wstaw (do pliku ASCII SEQ ), kill, copy.

{E

wstaw (IMPORT ASCII SEQ) plik z dysku do wybranej pozycji

UWAGA:
nigdy nie oznaczaj pierwszej linii kodu źródłowego jako bloku startowego

SZUKANIE I PRZEMIESZCZANIE:

{F

szukaj łańcucha znaków

{H

wyszukaj następne zdarzenie

{R

zastąp łańcuch1 z łańcuchem2, wyszukaj pierwsze zdarzenie

{T

zastąp i szukaj następnych

{Y

zastąp wszystkie zdarzenia


PROGRAMOWANIE KLAWISZY FUNKCYJNYCH:

{F1

Reset klawiszy funkcyjnych (F3=.słowo, F4=assembluj i uruchom, F5=.bajt, F6=usuń linie)

{A

Wstaw wszystkie znaki (wyjście z „{„); aby to zrobić „{” naciśnij „{” dwukrotnie;
przydatne do definiowania klawiszy F(unkcyjnych)

{K

naciśnij klawisz F (F3-F6) do redefinicji


OPERACJE DYSKOWE:

{*

katalog dysku

{@

status dysku

{D

komenda dyskowa

{P

ustal kod EOR dla kodowania źródła

{W

zapisz cały kod źródłowy do pliku ASCII SEQ (EXPORT)

{L

załaduj kod źródłowy

{S

nagraj kod źródłowy (możliwy nadpis z @: prefix)

UWAGA:
1. TURBO-ASM ładuje źródło od najwyższego adresu ($8FEB) do najniższego
($0800) pamięci i zastępuje kody operacji , własnymi numerami
(jak Basic’owe tokeny).

2. Podczas zapisywania i ładowania kodu źródłowego TURBO-ASM. także zapisuje
i ładuje konfigurację np. kolorów, klawiszy F, zaznaczeń i
aktualnej pozycji kursora.


OPERACJE NA PAMIĘCI:

{6

konwertuje blok pamięci pomiędzy adresami do danych kodu źródłowego

{J

zrzuca blok pamięci pomiędzy adresami

{SHIFT+F

wypełnia blok pamięci pomiędzy adresami z kodem

{SHIFT+L

ładuje do pamięci adresy

{SHIFT+S

zapisuje blok pamięci pomiędzy adresami


ASEMBLACJA:

{3

assemblacja

{4

lista asemblowanych plików: ?=drukarka, *=screen, filename=plik

{5

assembluj do wynikowego (wykonywalnego) pliku

{U

lista etykiet: ?=drukarka, *=screen, filename=plik


RÓŻNOŚCI:

{C

„zimny start” (inicjuje TURBO-ASM. i czyści kod źródłowy)

{I

inicjuje TURBO-ASM i czyści niewykorzystywaną pamięci

{+

dodaje dwie hexagonalne liczby

{-

odejmuje dwie hexagonalne liczby

{V

wyświetla używaną pamięć: – wolne bloki, + używane bloki

{O

zmiana pamięci

{SHIFT+K

klawisz włącz/wyłącz

{1

wyjście z TURBO-ASM. (SYS 36864 lub SYS 333 do restartu)

UWAGA:
{V pracuje niewłaściwie w obszarze pamięci $E000-$FFFF, lecz czyta I/O i KERNAL
(Action Repley zupełnie wystarczy przy zawieszeniu systemu)


WYRAŻENIA:

Assembler pracuje z 8 i 16 bitowymi numerami z następującymi notacjami:

$

dla hexagonalnych numerów

%

dla binarnych numerów

! (or none)

dla dziesiętnych numerów

„x”

dla znaków ASCII

Operacje pozwalające na wyrażenie (od najwyższego do najniższego priorytetu):
( ) – przedziału
* / – iloczynu/ilorazu
+ – – plusa, minusa
: & . – EOR, AND, OR operacji logicznych

Specjalne operacje:
< – mniej znaczący bajt 16 bit-owego wyrażenia
> – bardziej znaczący bajt 16 bit-owego wyrażenia
* – aktualnie asemblowany adres lub definicja startowego kodu wynikowego (*=start)

UWAGA:
1. Nigdy nie używaj etykiety po etykiecie=* lub podobnej kombinacji.
2. Nie używaj obszaru pomiędzy $E000-$FFFF (zarezerwowane dla etykiet TURBO-ASM).

Etykiety:
Pierwsze 8 znaków jest rozpoznawanych natomiast pozostałe są obcinane.

PSEUDO OPS
———-
.OFFS addr – przesuwanie kodu wynikowego (przez dodawanie adresów)
.BYTE bajt1,bajt2,… – konwertowanie bajtów
.WORD słowo1,słowo2,… – konwertowanie słów
.TEXT „text1″,”text2”,… – konwertowanie łańcuchów tekstowych
; – komentarz

Niektóre bugi zostały usunięte przeze mnie.:
1. Niektóre wersje TURBO-ASM (V6.0 tronic/rizing) zawodzą z rozkazami {G.
2. {V teraz poprawione dla Action Replay.


Jest wiele różnych wersji Turbo Assemblera, to są niektóre rozkazy (Mam je oznaczone V5). Załaduj to tak:
LOAD”foo”,8,1
SYS(9*4096)
Skąd tamto SYS pochodzi? Adres hex jest $9000, dlatego 9*4096, jeżeli byłoby $0900to wtedy byłoby (9*256)….

„<-” to jest tylna strzałka, to nie jest „<” mające za sobą „-„

„<-” rozkazy

1 wyjście
2 linia komentarza
3 kompilacja (z opcjami do uruchomienia)
4 drukuj źródło jako plik
5 kompiluj na dysk
6 stwórz „.byte”
8 wstaw tab
+ dodaj
– odejmji
[DEL] usuń
[INS] wstaw
A przełączanie bloku (tekstu)
B opcje bloku (kopiowanie, zapis, kill)
C „zimny start”
D komendy dyskowe
E wejście plikowe (import pliku tekstowego)

F znajdź
G idź do zaznaczenia
H poszukuj
K redefinicja klawiszy funkcyjnych
L załaduj
M wstaw zaznaczenie (0-9, start, end)
N idź do linii
O zmień kolory
R zastąp tekst
S zapisz
U lista etykiet
W zapis pliku
^ przełączanie klawisza on/off (strzałka do góry)
@ status napędu
* katalog
: lista zaznaczeń
; zabij zaznaczenie

Pseudo Ops
.BYTE $00,$FF,0,255,”x” dane bajtowe
.WORD dane słowne
.TEXT „This is text” dane tekstowe
*=$2000 ustawienie adresy startowego
#<etykieta zawiera mniej znaczący bajt etykiety adresu
#>etykieta zawiera bardziej znaczący bajt etykiet adresu
* wybrany adres (JMP *+20)

Koniec Twojego kodu z „JMP $9000” i to będzie powrót do asemblera…

REU – Rozszerzenie Pamięci

Niniejszy artykuł dedykowany jest szerokiemu gronu użytkowników Commodore 64 oraz C128. Powstał on z potrzeby chwili, jako odzew na szereg (powtarzających się) Waszych pytań odnośnie tego modułu. Ale do rzeczy…

Jak mówi pewne stare przysłowie: „pamięci nigdy nie za wiele”, pokusiłem się i Ja (tzn. TSD) o zakup tego cudeńka, ładnych kilka lat temu. Podobnie jak większość z Was, miałem wtedy blade pojęcie o jego wadach czy zaletach. Pewnym plusem okazała się wówczas dołączona instrukcja, w języku wprawdzie nie ojczystym – ale na szczęście dla mnie zrozumiałym. I teraz postaram się przekazać, czego można się było z niej dowiedzieć 🙂

Na początek rozszyfrujemy nazwę REU – Ram Expansion Module. Co jest trochę mylące, gdyż samo REU nie rozszerza pamięci ADRESOWALNEJ przez CPU (widzianej normalnie przez system, np. Basic), a jedynie zapewnia dostęp do tej dodatkowej pamięci – na drodze programowej. Przy zasadzie zbliżonej nieco do obsługi Ram-Dysku. Diabeł jak to mówią tkwi jednak w szczegółach, możemy więc mieć nawet aż 16 MB ram’u, ale nie bezpośrednio!

Inaczej mówiąc – pamięć tą możemy czytać i zapisywać, jednak bez możliwości uruchamiania w niej własnych programów. Co oznacza dla przeciętnego użytkownika – brak zastosowań praktycznych, gdyż większość gier i programów po prostu nie widzi tej dodatkowej pamięci, nie licząc kilku użytków czy systemu GEOS. Więc jeżeli czytasz jeszcze nadal ten tekst, oznacza to iż NIE jesteś już normalnym:) użytkownikiem komodorka. Pasjonują cię wyzwania… Zacznij zatem odkrywać i ujarzmiać owe niezmierzone przestrzenie RAM-u.

1. Wielkość pamięci

Podstawowym kryterium podziału REU, jest wielkość zamontowanej w nich pamięci. Najpopularniejsze są modele 1700, 1764, 1750 – mające odpowiednio 128 KB, 256 KB i 512 KB ram. Jednak rzesze zapaleńców wprawnie posługujących się lutownicą, zaczęły wyposażać je w dodatkowe kości pamięci i tak powstało REU 2 MB i większe:) My jednak dzięki emulatorom C-64, nie musimy już brać do rąk tego groźnego narzędzia. Odpalamy sobie tylko VICE lub CCS-a i cieszymy oczy szesnastoma MB.

2. Podział pamięci

Niezależnie od wielkości wbudowanej pamięci, dzieli się ona na tzw. Banki o wielkości 64 KB każdy! Więc model REU 1700 – wyposażony jest w dwa banki (numer 0 i 1), REU 1764 w cztery (0, 1, 2, 3) a 1750 w osiem itd. Spójrzcie zatem na poniższą tabelkę:

 

Model REU

Wielkość zamontowanej pamięci

Ilość banków

1700

128 KB

2

1764

256 KB

4

1750

512 KB

8

1 MB

1024 KB

16

2 MB

2048 KB

32

4 MB

4096 KB

64

8 MB

8192 KB

128

16 MB

16384 KB

256

3. Dostęp do pamięci

Uzyskujemy poprzez podanie numeru banku (liczonego od zera) i adresu danych. Sam adres zaś składa się z adresu względem początku banku (czyli zawsze liczonego od $0000) i długości danych do pobrania lub zapisania. Można uprościć sobie życie stosując pseudo zapis adresu w formacie 24-bitowym. Ja posługuje się czymś takim: $13:1a00, gdzie liczba po „$” to numer banku, a dane po „:” to adres wewnątrz tego banku. Ale o tym powiem więcej w drugiej części mego poradnika:) która powinna ukazać się już wkrótce…

4. Podtrzymywanie zawartości pamięci

Nie słyszałem o takim:( w fizycznym module REU, jednak w wirtualnym umożliwia to emulator VICE. Gdzie rolę pamięci modułu emuluje plik zdefiniowany przez użytkownika. Oznacza to iż tworzymy „lipny” plik i ustawiamy go jako obraz REU. Po inicjalizacji, emulator powiększy rozmiar pliku do żądanego przez nas rozmiaru. Co uwolni użytkownika od skomplikowanego stworzenia go samemu. Możemy więc przechowywać wiele obrazów REU równocześnie, przełączając się pomiędzy nimi wedle własnej woli.

5. Rejestry sterujące dostępem do pamięci

Nie jest tego za wiele, ale jak do tej pory – na co daję głowę – nie było wcześniej NIGDZIE opublikowane (mam tu na myśli Commodore & Amiga, Kebab, Bajtek) więc śpieszę naprawić te ewidentne niedopatrzenie… Pozbawiające Was tych informacji przyczynili się mimochodem do braku popularyzacji tego sprytnego urządzenia, które możemy umieścić w Expansion porcie. Jeszcze należy tylko wspomnieć, że sterowanie odbywa się poprzez obszar I/O przeznaczony dla modułów i jako taki UMIESZCZONY w pamięci od adresu $DF00 poczynając (nie w RAM’ie pod tym samym adresem – czyli do komórki $01 ładujemy $37 by odblokować ROM), polecam w tym celu lekturę mapy pamięci. Pytania i wątpliwości w tej materii nadsyłajcie na mój adres maniac64@nostalgia.pl.

PS. Ze względu na możliwość przekłamania (użycie przeze mnie potocznych nazw), przytaczam oryginalne angielskie nazewnictwo.

 

Address

Bits

Function

$DF00:0

7 – 0

Status Register – Read only

7 – Interrput Pending – 1 = Interrput waiting to be serviced
6 – End of Block – 1 = Transfer complete
5 – Fault – 1 = Block verify error
4 – Size – 0 = Total expansion = 128K
1 = Total expansion = 512K
3 – 0 – Version

Note: Bits 7-5 are cleared when this register is read

$DF00:1

7 – 0

Command Register – Read/Write

7 – Execute – 1 = Transfer per current config
6 – Reserved
5 – Load 1 = Enable AUTOLOAD option
4 – FF001 = Disable FF00 decode
3 – Reserved
2 – Reserved
1 – 0 – Transfer type 00 – transfer C128 -> RAM module
01 – transfer C128 <- RAM module
10 – swap C128 <-> RAM module
11 – verify C128 – RAM module

$DF00:2

7 – 0

C128 Base Address, LSB – Read/Write
Lower 8 bits of base address, C128

$DF00:3

7 – 0

C128 Base Address, MSB – Read/Write
Upper 8 bits of base address, C128

$DF00:4

7 – 0

Expansion RAM Address, LSB – Read/Write
Lower 8 bits of base address, expansion RAM

$DF00:5

7 – 0

Expansion RAM Address, MSB – Read/Write
Upper 8 bits of base address, expansion RAM

$DF00:6

7 – 0

Expansion RAM bank – Read/Write
Expansion RAM bank pointer
Bits 2 (MSB) to 0 (LSB) are significant

$DF00:7

7 – 0

Transfer lenght, LSB – Read./Write
Lower 8 bits of the byte counter

 

7 – 0

 

$DF00:8

7 – 0

Transfer lenght, MSB – Read./Write
Upper 8 bits of the byte counter

$DF00:9

7 – 5

Interrput mask register – Read/Write

7 – Interrput enable – 1 = Interrputs enabled
6 – End of Block mask – 1 = Interrput on end of block
5 – Verify error – 1 = Interrput on verify error

$DF00:A

7 – 6

Address control register – Read/Write

00 – Inrement both addresses (default)
01 – Fix expansion address
10 – Fix C128 address
11 – Fix both addresses

* np. zapis $DF00:8 oznacza adres $DF08

…a teraz małe uproszczenie w naszym języku i do naszego 1-16 MB REU dla C-64, gdyż różnice są symboliczne i tylko w adresie $DF00:01 – obsługa rejestru $FF00 czyli trybu DMA dla C128 w przypadku transferu przez REU, mam nadzieję iż właściciele C128 mi wybaczą 🙂

$DF00:1 – operacja na pamięci (tj. zapis/odczyt itd.)
$DF00:2-3 – adres początku pamięci dla C64
$DF00:4-5 – adres początku pamięci dla REU
$DF00:6 – bank dla REU
$DF00:7-8 – ilość danych

dodatkowo możemy ustalić czy REU ma nas powiadamiać o wyniku naszych poczynań, i tak w:

$DF00:0 – odczytamy status operacji na pamięci
$DF00:9 – ustalimy sposób czyli przyczynę powiadamiania nas o wyniku operacji
$DF00:A – dopasujemy obsługę licznika adresowego

W praktyce może to wyglądać jak np. w załączonym pliku reu-example.zip kopiującym zawartość ekranu tekstowego do i z modułu – odpowiednio SYS $1000 i $2000 po kompilacji pod TAS-em. Na tak ekstremalnie prostym przykładzie możemy już dostrzec potencjalne zastosowania REU. W następnym wcieleniu tego poradnika spróbuje was zarazić napisaniem własnego Ram-Dysku, opartego na tych kilku prostych komendach. Do tego czasu radzę ostro potrenować, bo wrzucę Was od razu na głęboką wodę. Pokażę, w jaki sposób możemy zaprojektować obsługę 16 MB RAM-u, mając zaledwie procka z 1 MHz na pokładzie.

Niech moc REU będzie z Wami 🙂

Podstawowe polecenia monitora

Dla osób chcących programować lub modyfikować programy w języku wewnętrznym znajomość poleceń monitorów jest po prostu niezbędna. Jednocześnie powszechnie wiadomo, że większość użytkowników C-64 korzysta z programów typu monitor bez jakiejkolwiek instrukcji. Ot, po prostu „na czuja” nawet do popularnych modułów z monitorami sprzedawcy nie zawsze dołączają odpowiednią dokumentację).

Poniższe zestawienie obejmuje naturalnie jedynie instrukcje podstawowe, bowiem tylko one mają praktycznie we wszystkich monitorach identyczny format. Uwaga: oczywiście wiecie o tym, ale dla porządku przypominamy, że wszystkie argumenty w programie monitora muszą być podawane w kodzie heksadecymalnym (szesnastkowyrn).

A (ASSEMBLE)

Umożliwia wprowadzenie linii zawierającej kod asemblera (mnemonik + argument). Programista musi określić numer pierwszej komórki pamięci, od której będzie zaczynał się program, dalszą numerację zazwyczaj przejmuje program monitora. Format instrukcji:

A C000 JSR $FCE2 – gdzie:
A – rozkaz monitora,
C000 – adres komórki pamięci,
JSR – mnemonik,
$FCE2 – argument mnemonika.

Jeżeli podczas wprowadzania instrukcji programista popełni błąd, wówczas na ekranie zostanie wyświetlony znak zapytania „?”, albo program monitora nie policzy (wyświetli) następnego adresu pamięci.

C (COMPARE)

Instrukcja ta porównuje ze sobą dwa obszary pamięci. W wypadku różnic wyświetlane są komórki, w których ta różnica występuje. W niektórych monitorach wyświetlane są również zawartości tych komórek. Format instrukcji:

C 1000 2000 5400

W wyniku działania tej instrukcji zostają porównane dwa obszary. W przykładzie: pierwszy, począwszy od adresu 1000 do 2000, drugi – od 5000 do 6000

D (DISASSEMBLE)

Za pomocą tego rozkazu dokonujemy disasemblacji tzn. zamiany kodu maszynowego na kody asemblera. Korzystając z tego rozkazu należy określić adres początku obszaru pamięci, od którego ma się rozpocząć disasemblacja. Format instrukcji:

D FCE2 – gdzie:
D – instrukcja monitora,
FCE2 – początkowy adres pamięci, od którego chcemy rozpocząć disasemblację.

W przypadku, gdy program monitora natrafi na kod, który nie ma mnemonika, wówczas wyświetlone zostaną trzy znaki zapytania – (???).

F (FILL)

Rozkaz ten powoduje wypełnienie zadanego obszaru pamięci podaną wartością lub wartościami. Format instrukcji:

F C000 CFFF 01 02 – gdzie:
F – instrukcja monitora,
C000 – Początkowy adres pamięci, od którego można zacząć wypełnianie,
CFFF – końcowy adres pamięci,
O1 02 – wartości, którymi można wypełniać zadaną Pamięć.

G (GO)

Instrukcja ta uruchamia program począwszy od podanego adresu. Format instrukcji:

G C000 – gdzie:
G – instrukcja monitora, .
C000 – adres, od którego zostanie uruchomiony program.

W przypadku, gdy w programie wystąpi instrukcja RTS lub BRK, program automatycznie wróci do monitora.

H (HUNT)

Zadaniem tej instrukcji jest poszukiwanie w zadanym obszarze pamięci sekwencji danych. Dane te możemy podać w formie danych bajtów lub w formie znaków. Format instrukcji:

1. H C000 C100 78 A9 lub
2. H C000 C100 ‚TEXT’ – gdzie:
H – instrukcja monitora,
C000 – początkowy adres pamięci, od którego można poszukiwać danych,
CFFF – końcowy adres pamięci,
78 A9 lub TEXT – poszukiwane dane.

Jeżeli w przeszukiwanym obszarze wystąpią podane dane, wówczas zostaną wyświetlone ich adresy.

L (LOAD)

Odpowiednik instrukcji LOAD w BASIC-u. Zazwyczaj dostępne są dwa formaty instrukcji:

1. L”Nazwa programu” 08
Powoduje załadowanie programu pod podaną nazwą z urządzenia o numerze 8 (stacja dysków) pod adres podany w nagłówku programu dwa pierwsze bajty.

2. L”Nazwa programu” 01 C000
Powoduje załadowanie programu pod podaną nazwą z urządzenia o numerze 1 magnetofon) pod adres C000. I tu mała uwaga: niektóre monitory nie są wyposażone w komunikaty błędów przy operacji wczytywania, co jest istotne dla użytkowników magnetofonów, gdyż program może zostać wczytany z błędem.

M (MONITOR)

W wyniku działania tej instrukcji na ekranie zostanie wyświetlona zawartość pamięci w formie liczb szesnastkowych. Zawartość pamięci można modyfikować najeżdżając kursorem na daną wartość, a następnie wprowadzając nową. Po całej operacji należy nacisnąć klawisz RETURN. Format instrukcji:

M C000 C450 – gdzie:
C000 – adres, od którego zaczyna się oglądanie pamięci, C050 – adres końcowy.

W niektórych monitorach, oprócz zawartości pamięci w firmie Iiczb szesnastkowych, zostaje wyświetlona zawartość w kodzie ASCII. Jeżeli dany znak w kodzie ASCII nie ma swej farmy możliwej do zobrazowania na ekranie, zostaje zastąpiony kropką.

R (REGISTER)

Wydanie tej dyrektywy powoduje wyświetlenie zawartości rejestrów procesora. Najczęściej są to:

PC – licznik programu,
SR – bajt stanu procesora,
AC – akumulator,
XR – rejestr indeksowy X,
YR – rejestr indeksowy Y,
SP – wskaźnik stosu.

Ponadto w niektórych monitorach wyświetlane są adresy przerwań IRQ i NMI. Jeżeli chce się zmodyfikować zawartość jakiego rejestru, wystarczy najechać kursorem w odpowiednie miejsce, za pomocą klawiatury wpisać nowi wartość i nacisną klawisz RETURN.

S (SAVE)

Odpowiednik instrukcji SAVE w BASIC-u. Pozwala na zapisanie na nośniku zewnętrznym dysk lub magnetofon) zawartości podanego obszaru pamięci. Format instrukcji:

S”Nazwa programu” 08 0801 2020 – gdzie:
S – instrukcja monitora,
08 – nr urządzenia zewnętrznego,
0801 – adres startowy,
2020 – adres końcowy.

W wyniku działania powyższej instrukcji na dyskietce (urządzenie 8) nagrany zostanie program o adresie startowym 0801 i końcowym 2020.

T (TRANSFER)

Rozkaz ten służy do przenoszenia dowolnych obszarów pamięci. Użytkownik musi określić adresy początku i końca wybranego do przeniesienia obszaru oraz adres początku obszaru, do którego ma nastąpi przeniesienie. Format:

T 0801 1000 C000
W wyniku działania tej instrukcji wartości będące w obszarze począwszy od adresu 0801, a skończywszy na adresie 1000, zostaną przeniesione do obszaru począwszy od adresu C000.

V (VERIFY)

Odpowiednik instrukcji VERIFY w BASIC-u. Pozwala na zweryfikowanie zapisanego na dyskietce czy taśmie programu. Format instrukcji:

V „Nazwa programu” 01 0801 – gdzie:
V – instrukcja monitora,
01 – nr urządzenia zewnętrznego magnetofon),
0801- adres startowy.

I tu mała uwaga: spotkałem się z monitorami, w których program monitora wykazywał, że weryfikacja została przeprowadzona bezbłędnie, mimo że wcześniej wprowadziłem zmiany w programie.

Czy istnieje lepsze narzędzie od monitora?

Co to jest monitor?

Pierwszym usprawnieniem programowania w języku maszynowym jest opracowanie języka pomocniczego, nazywanego asemblerem. Zalicza się go do języków symbolicznych. Każdemu rozkazowi asemblera odpowiada tylko jeden rozkaz w języku maszynowym. Nie jest to zatem język wysokiego poziomu, gdzie jednemu rozkazowi odpowiadać może kilka, kilkanaście lub kilkadziesiąt rozkazów w języku maszynowym. Podstawowym ułatwieniem jakie daje asembler, to umożliwienie zastąpienia kodów liczbowych, łatwiejszymi do zapamiętania nazwami symbolicznymi. Programy, które umożliwiają taki sposób programowania nazywa się również asemblerami lub monitorami. Różnica między asemblerem a monitorem jest taka, jak między kompilatorem i interpreterem z dodatkowym zastrzeżeniem, że monitor używa adresów bezpośrednich, a asembler adresów symbolicznych. W programach tych w momencie pisania nazw symbolicznych następuje tłumaczenie ich na ciąg odpowiednich kodów liczbowych języka maszynowego. Umożliwiają one również podgląd pamięci nie tylko w kodach maszynowych, ale i w postaci tzw. mnemoników i operandów. Na ogół monitory zgłaszają się po inicjacji następującym komunikatem:

B*______PC___NMI__SR AC XR YR SP NV#BDIZC
.;______0401 FE47 32 04 5E 00 F6 ..**..*.
.;

„_” – spacje (w języku html nie da się stawiać więcej niż jednej spacji bez znaku)

gdzie PC oznacza licznik rozkazów (dwubajtowy), NMI – adres procedury przerwania niemaskowalnego, AC – zawartość Akumulatora, XR – zawartość rejestru X, YR – zawartość rejestru Y i SP – wskaźnik stosu. Pozostałe litery i znaki oznaczają rejestry stanu procesora i ich stan. Bity N (Negative – ujemny), V (oVerflow – wystąpił nadmiar), Z (Zero) i C (Carry – wystąpiło przeniesienie) sygnalizują, że w ostatniej operacji powstał wynik o określonych cechach (*) lub, że wynik takich cech nie ma (.). Bit B (Break) sygnalizuje, że wykonana została instrukcja BRK, I (Interrupt) – zabronione przerwania, a D (Decimal) informuje o wykonaniu obliczeń w systemie dziesiętnym. Jeśli stan operacji jest oznaczony gwiazdką, to będziemy mówili, że wciągnięta jest flaga danej operacji (operacja taka wystąpiła), a jeśli stan jest oznaczony kropką, to flaga odpowiadająca tej operacji jest opuszczona. Później podane zostaną instrukcje, które służą do podnoszenia lub opuszczania flag niektórych operacji.

Krzysztof Gajewski, Bogusław Radziszewski „Commodore 64 od środka”, wyd. Fundacja Edukacji Technologicznej, Warszawa 1992

 

Czy istnieje lepsze narzędzie od monitora?

W programach nazywanych monitorami tłumaczenie z asemblera na język maszynowy odbywa się w trakcie pisania programu. Jest to jedna z niewielu zalet. Uciążliwością pisania programu w języku pod monitorem jest konieczność operowania na adresach bezpośrednich, brak w nich mechanizmów ułatwiających wywoływanie procedur (ach, te adresy bezpośrednie), brak elementów programowania strukturalnego itp. Na ogół nie mają tych wad programy nazywane asemblerami lub makroasemblerami.

Program pisany pod asemblerem najczęściej nie jest tłumaczony na język maszynowy w trakcie pisania. W pierwszej chwili otrzymujemy tzw. program źródłowy (source), z którego, po poddaniu go asemblacji, otrzymuje się program w postaci wynikowej (destination). Dla mikroprocesorów 65xx napisano wiele asemblerów i makroasemblerów. Jednym z najbardziej rozbudowanych makroasemblerów, znanych autorom, jest makroasembler o nazwie Merlin. Zupełnie wystarczającym może być również Assembler 64. W czasie pisania tego tekstu (tej książki w sumie – to tylko fragment z niej – Luc) i tworzenia programów takich jak Warsaw Basic, Edytor PL itd. autorzy tych opracowań mieli niestety dostęp tylko do monitora, a w pierwszym okresie swoich zainteresowań mikrokomputerowych tylko do programu monitorującego zawartość pamięci w kodach szesnastkowych. Tak więc zdecydowanie Asemblery są lepsze od „zwykłych” monitorów, których możemy używać w często dodawanych programach w cartridge’ach.