Informatyka dla klas II – Przykładowa Maszyna Cyfrowa

Co to jest PMC

Współczesne komputery przebyły długą drogę rozwoju od dużych, prymitywnych maszyn z lat 50 i 60-tych ubiegłego wieku do superszybkich komputerów osobistych opartych na najnowszej technologii. Programowanie takich nowoczesnych maszyn jest sztuką ogromnie skomplikowaną, która wymaga od programisty olbrzymiej wiedzy na temat funkcjonowania poszczególnych składników komputera. Wiedzę tę zdobyć nie jest łatwo, a zajmuje to lata.

PMC jest Przykładową Maszyną Cyfrową, czyli prostym komputerem. Jego nieskomplikowana budowa pozwala w szybkim tempie opanować podstawowe składniki i przystąpić bezpośrednio do programowania w kodzie maszynowym – asemblerze. Tak, właśnie kod maszynowy jest podstawą pracy wszelkich komputerów i zrozumienie jego funkcji i zasad działania uważam za kluczowe zadanie każdego programisty. Znajomość pracy komputera na najniższym poziomie sprzętowym znakomicie ułatwia programowanie w każdym języku programowania.

To, co w rzeczywistym systemie jest trudne i skomplikowane, w PMC jest proste i zrozumiałe. Jednocześnie zasady programowania PMC i rzeczywistego komputera są bardzo podobne. Opanowanie programowania PMC ułatwi zatem późniejsze przejście na prawdziwe systemy komputerowe – programista będzie znał podstawowe zasady, które tu i tam są identyczne.

 

Podstawowe składniki PMC

obrazek

prof. John von Neumann

Wszystkie współczesne komputery osobiste są zbudowane wg koncepcji zaproponowanej w latach 40-tych ubiegłego wieku przez profesora Johna von Neumanna z Uniwersytetu w Princeton. Jego model zakładał, iż komputer wyposażony będzie w trzy podstawowe składniki:

  • pamięć: do przechowywania przetwarzanej informacji, wyników oraz samego programu dla komputera
  • urządzenia wejścia/wyjścia: umożliwiające komunikację komputera z otoczeniem
  • procesor: przetwarzający informację umieszczoną w pamięci i komunikującego się z otoczeniem poprzez urządzenia wejścia/wyjścia.

Brak któregokolwiek z wymienionych składników uniemożliwia korzystanie z maszyny cyfrowej. Bez pamięci komputer nie może gromadzić danych oraz wyników obliczeń. Bez układów wejścia/wyjścia nie można się z nim skomunikować, staje się zatem kosztownym przyciskiem do papieru. Bez procesora nie może być wykonywany program i przetwarzane dane.

PMC jest również zbudowana logicznie wg architektury Johna von Neumanna.

Pamięć PMC

obrazek

Pamięć komputerowa (ang. computer memory) jest urządzeniem, które służy do przechowywania przetwarzanej informacji, wyników tego przetwarzania oraz programu dla procesora. Zewnętrznie wygląda tak jak po prawej stronie. Czarna, płaska kostka plastikowa z wystającymi metalowymi końcówkami, którymi podawane są odpowiednie sygnały elektryczne sterujące pracą pamięci.

Wewnątrz pamięć komputerowa zawiera tysiące (a nawet miliony) komórek (ang. memory cells). W każdej komórce można zapisać informację w postaci ciągu bitów (zwykle 8 bitów, zwanych bajtem). Cała sztuka polega na zakodowaniu informacji w odpowiedni ciąg zer i jedynek.

Komórki pamięci komputerowej są ponumerowane. Numer komórki nazywamy adresem (ang. memory cell address). Dzięki adresowi procesor może bez problemów uzyskać dostęp do wybranej komórki i odczytać zawartą tam informację lub zapisać nową.

obrazek

Wyprowadzenia pamięci komputerowej tworzą trzy grupy sygnałów, które nazywamy magistralami (ang. buses). Pierwsza magistrala umożliwia odczyt danych oraz ich wpis do pamięci, jest to magistrala danych (ang. data bus). Druga magistrala przekazuje pamięci adres wybranej komórki, jest to magistrala adresowa (ang. address bus). Trzecia magistrala zawiera różne sygnały sterujące, nazywa się magistralą sterującą (ang. control bus).

Zapis danych odbywa się następująco:

  1. Procesor umieszcza na magistrali danych dane do zapisu (w postaci liczby binarnej, każdy bit jest reprezentowany przez jedną linię sygnału).
  2. Na magistrali adresowej procesor umieszcza adres komórki pamięci (również w postaci binarnej), w której należy zapisać dane znajdujące się na magistrali danych.
  3. Na magistrali sterującej procesor umieszcza polecenie zapisu danych.
  4. Układ sterujący pamięci odczytuje informację z magistrali sterującej. Stwierdza, że chodzi o zapis danych. Adres z magistrali sterującej służy do wyszukania odpowiedniej komórki pamięci, następnie dane z magistrali danych zostają przeniesione i zapisane do wybranej komórki pamięci.
  5. Po wykonaniu operacji układ sterujący pamięci umieszcza na magistrali sterującej potwierdzenie zapisu – wtedy procesor wie, iż polecenie zapisu danych zostało wykonane i może przystąpić do realizacji innych zadań.


Odczyt danych odbywa się następująco:

  1. Procesor umieszcza na magistrali adresowej adres komórki pamięci, z której chce odczytać informację.
  2. Na magistralę sterującą procesor przesyła polecenie odczytu danych z pamięci.
  3. Układ sterujący pamięcią odczytuje stan magistrali sterującej i stwierdza, że chodzi o odczyt zawartości komórki pamięci. Wykorzystuje zatem adres z magistrali adresowej do wyszukania wybranej komórki, a następnie zawartą w niej informację umieszcza na magistrali danych.
  4. Na magistrali sterującej układ sterujący pamięcią umieszcza potwierdzenie odczytu danych. Wtedy procesor wie, iż dane są gotowe do odczytu z magistrali danych. Pobiera je zatem i odpowiednio przetwarza.

Pamięć PMC zbudowana jest z 63 komórek 16-bitowych. Komórki mają adresy od 1 do 63. Może stwierdzisz, że nie jest to zbyt wiele – będziesz miał zupełną rację. Jednakże przypominam – PMC jest przeznaczona TYLKO do ćwiczeń, a nie do poważnych zadań. Zapewniam cię, iż 63 komórki to ilość zupełnie wystarczająca do naszych celów.

Ostatnim terminem wartym zapamiętania jest przestrzeń adresowa (ang. address space). Jest to zbiór adresów, które może wykorzystywać procesor do komunikacji z pamięcią. Im większa przestrzeń adresowa, tym więcej danych można umieszczać w pamięci oraz przetwarzać. W przypadku PMC przestrzeń adresowa obejmuje 64 adresy (od 0 do 63). Adres 0 jest zarezerwowany do specjalnych zadań. Pozostałe adresy można dowolnie wykorzystywać. Jeśli spojrzymy na liczby 0...63 od strony binarnej, to zauważymy, iż wszystkie adresy można zapisać przy pomocy 6 bitów w naturalnym kodzie dwójkowym:

 

000000(2) = 0(10)
111111(2) = 63(10).

 

Wynika z tego, iż nasza PMC ma 6-bitową magistralę adresową oraz 16-bitową magistralę danych.

 

Urządzenia wejścia/wyjścia w PMC

obrazek

Kolejnym elementem w architekturze komputera są urządzenia wejścia/wyjścia lub krótko wejście/wyjście (ang. Input/Output – I/O). Zewnętrznie są to klawiatury, monitory, drukarki, wszelkiego typu urządzenia zapisująco-odtwarzające, itp. Z punktu widzenia mikroprocesora, który nic nie musi wiedzieć o prawdziwej budowie tych urządzeń, stanowią one pewne szczególne pozycje w przestrzeni adresowej, do których można przesłać informację lub z których odczytuje się informację.

Na przykład wyobraźmy sobie, iż wyposażyliśmy nasz komputer w klawiaturę. Każde naciśnięcie klawisza powoduje przesłanie do komputera kodu binarnego tego klawisza. Kod klawisza jest dostępny w specjalnym rejestrze wejścia/wyjścia, który może się nazywać na przykład rejestrem odczytu klawiatury. Dostęp do tego rejestru może być wykonywany w identyczny sposób jak dostęp do komórki pamięci. Po prostu w przestrzeni adresowej procesora nie wszystkie adresy odnoszą się do komórek pamięci. Niektóre oznaczają właśnie takie specjalne rejestry przechowujące informacje pochodzące ze współpracujących z komputerem urządzeń. Jeśli na magistralę adresową procesor wyśle adres takiego rejestru, to logika sterująca komputerem (płyta główna – ang. mother board) nie odwoła się do układów pamięciowych, tylko do wskazanego rejestru.

Oczywiście z punktu widzenia procesora rejestr I/O nie różni się od zwykłej komórki pamięci. Odczyt czy zapis danych odbywa się wg identycznego schematu (co znacznie upraszcza budowę samego procesora).

Co więcej, ten sam adres może wskazywać dwa różne rejestry w zależności od tego, czy operacja dotyczy zapisu, czy odczytu. Tak właśnie jest w PMC. Adres 0 (zero) nie oznacza komórki pamięci, lecz wejście danych (przy odczycie) lub wyjście danych (przy zapisie). Adres ten nosi nazwę INOUT, która w PMC jest zarezerwowana właśnie dla adresu 0.

Zapis informacji do rejestru INOUT powoduje przesłanie znaku do wyświetlacza. Znak ten pojawi się na ekranie. Zatem zapis informacji pod adres 0 oznacza przesłanie danych do urządzenia wyjścia, jakim jest wiersz wyświetlacza. Wyświetlacz w PMC ma postać wiersza o maksymalnej pojemności 64 znaków. Wyświetlacz ten możesz potraktować jak taśmę. Każdy przesłany znak zostaje dopisany do końca taśmy. Jeśli znaki nie mieszczą się w wierszu, to zostają przewinięte w lewo. Znaki drukowalne mają kody od 32 (spacja) do 126. Dodatkowo są rozpoznawane dwa kody:

KOD 0  – wyświetlacz jest czyszczony, znika z niego cała treść.
KOD  127  – ostatni znak zostaje skasowany.

Pozostałe kody (od 1 do 31 i powyżej 127) nie są drukowane.

Z kolei odczyt danych z adresu 0 powoduje próbę pobrania danych z bufora wejściowego. W buforze tym użytkownik PMC może w trakcie działania programu umieszczać znaki z klawiatury. Każdy odczyt powoduje pobranie jednego znaku i zwrócenie jego kodu ASCII. Jeśli bufor zostanie opróżniony, to odczyt zwróci wartość 0 – w ten sposób można prosto sprawdzić, czy w buforze skończyły się znaki. Kod znaku ma zawsze 8 bitów, zatem wejście PMC jest 8-bitowe.

Adres 0 (INOUT) to jedyny rejestr wejścia/wyjścia w PMC. Wszystkie inne adresy w przestrzeni adresowej odnoszą się do zwykłych komórek pamięci.

 

Procesor PMC II

obrazek

Bez wątpienia procesor jest najbardziej skomplikowanym elementem każdego komputera. Musi tak być, ponieważ to właśnie on odpowiada za działanie całego systemu i przetwarzanie danych.

Procesor przetwarza informację pobierając ją z pamięci do wewnętrznych rejestrów, na których wykonuje różne operacje. Wynik jest z powrotem umieszczany w pamięci.

Rejestr (ang. register) jest jakby wewnętrzną mikropamięcią procesora. W rejestrze można umieszczać dane kilkubitowe (zależy to od typu procesora). Następnie procesor może przetwarzać w różny sposób informację w rejestrach (np. dodawać je, mnożyć, dzielić, wykonywać nad nimi operacje logiczne itp.) Takie rozwiązanie wybrano ze względu na szybkość – działania na rejestrach są bardzo szybkie, natomiast pamięć jest stosunkowo wolna, ponieważ procesor musi adresować wybrane komórki i czekać, aż zawartość będzie dla niego dostępna. Dlatego właśnie procesor wyposaża się w wewnętrzne rejestry (komórki pamięci wewnętrznej), w których umieszcza on sobie najczęściej potrzebne mu dane.

obrazek

Procesor PMC posiada tylko jeden rejestr dla danych zwany akumulatorem (ang. accumulator) – nie, nie chodzi tutaj o baterię, tylko o pewne cechy tego rejestru, które zrozumiemy, gdy przejdziemy do omawiania instrukcji procesora. Do akumulatora można pobrać zawartość dowolnej komórki pamięci. Przy operacjach arytmetycznych i logicznych zawsze jeden z argumentów znajduje się w akumulatorze. Również wynik operacji trafia do akumulatora (akumuluje się). Rejestr akumulatora w PMC mieści 16 bitów. Zwróć uwagę, iż odpowiada to dokładnie rozmiarowi pojedynczej komórki pamięci.

Program dla procesora zbudowany jest z pojedynczych rozkazów, poleceń, które dokładnie instruują go, co ma w danej chwili robić. Rozkazy te mają postać kodów binarnych, liczb, które umieszcza się w pamięci komputera. Procesor odczytuje kolejne kody instrukcji, rozpoznaje zawarte w nich polecenia i wykonuje je.

Ponieważ instrukcje zapisane są w kolejnych komórkach pamięci, to procesor w trakcie wykonywania programu musi pobierać dane z tych komórek. Do tego celu służy mu specjalny rejestr zwany licznikiem rozkazów lub licznikiem programu (ang. program counter). Licznik rozkazów przechowuje adres wykonywanej instrukcji.

Faza pobrania i wykonania rozkazu jest następująca:

  1. Procesor umieszcza na magistrali adresowej zawartość licznika rozkazów.
  2. Na magistrali sterującej procesor umieszcza polecenie odczytu pamięci.
  3. Procesor czeka, aż układ sterowania pamięcią zgłosi mu gotowość danych do odczytu z magistrali danych. Czas ten jest niezbędny do wyszukania wybranej komórki w strukturze pamięci i przesłania jej zawartości na magistralę danych.
  4. Po odczycie kodu rozkazu trafia on do specjalnego rejestru wewnętrznego procesora zwanego rejestrem instrukcji (ang. instruction register).
  5. Układy logiczne procesora badają odczytany kod rozkazu w celu rozpoznania rodzaju polecenia. Gdy to zostanie zrobione, procesor wykonuje odczytany rozkaz.
  6. Po wykonaniu rozkazu licznik rozkazów jest zwiększany o 1. W ten sposób wskazuje kolejną instrukcję w pamięci. Cały cykl powtarza się z kolejnym rozkazem.

Architektura PMC II

Gdy znamy już wszystkie elementy składowe naszej maszyny cyfrowej, to złóżmy je w całość. Otrzymamy następujący system:

 

obrazek

 

Po lewej stronie mamy układ pamięci złożony z 63 komórek 16-bitowych.

W środku są dwa rejestry wejścia/wyjścia. Rejestr WEJŚCIE umożliwia odczyt danych wejściowych zgromadzonych w buforze wejścia. Jeśli bufor jest pusty, odczyt zwraca wartość zero. Rejestr WYJŚCIE umożliwia wyprowadzenie przesłanego doń znaku na wyświetlacz wierszowy. Oba rejestry zajmują ten sam adres w przestrzeni adresowej procesora – adres 0 (zero). Jeśli procesor zapisuje dane pod ten adres, to trafią one do rejestru WYJŚCIA. Jeśli procesor odczytuje dane z tego adresu, to otrzyma on zawartość rejestru WEJŚCIE.

Po prawej stronie widzimy procesor PMC z trzema rejestrami:

akumulator służy do przetwarzania danych
licznik rozkazów służy do adresowania kolejnych instrukcji w pamięci programu
rejestr instrukcji służy do dekodowania pobranego kodu rozkazu.

Elementy składowe komputera połączone są ze sobą za pomocą trzech magistral:

 magistrala danych służy do transportowania danych pomiędzy procesorem a pozostałymi składnikami systemu
 magistrala adresowa umożliwia wybór komórki pamięci lub rejestrów wejścia/wyjścia, z którymi procesor chce wymienić dane poprzez magistralę danych
 magistrala sterująca przekazuje polecenia odczytu/zapisu od procesora oraz potwierdzenia wykonania tych operacji przez pozostałe składniki.

Asembler PMC

Asembler jest częścią symulatora PMC, która zamienia tekst programu zapisanego w postaci symbolicznej na program, który zostanie umieszczony w pamięci. Poniżej opisujemy budowę instrukcji PMC oraz składnię wierszy programu.

 

Budowa instrukcji PMC

Procesor PMC pobiera kolejne instrukcje z pamięci (przy pomocy licznika programu) i umieszcza je w rejestrze instrukcji. Pobrana instrukcja jest analizowana w celu określenia rodzaju operacji do wykonania. Po rozkodowaniu instrukcji procesor wykonuje wskazane polecenie i pobiera kolejną instrukcję.

Aby zrozumieć kodowanie rozkazów w PMC musimy przedstawić instrukcję w postaci binarnej - czyli tak, jak jest ona przechowywana wewnątrz komputera. Ponieważ instrukcje są umieszczane w 16 bitowych komórkach pamięci PMC, to każda z nich zbudowana jest z 16 bitów. Bity te podzielimy na trzy grupy, które będziemy zwać polami (ang. bit field).

 

Bity instrukcji PMC
b15 b14 b13 b12 b11 b10 b9 b8 b7 b6 b5 b4 b3 b2 b1 b0
Pole instrukcji Pole
trybu
adreso-
wania
Pole argumentu

 

Bity b12...b15 (cztery najstarsze) tworzą pole instrukcji. Od kombinacji bitów w tym polu zależy rodzaj instrukcji, czyli rodzaj operacji, które wykona procesor po rozkodowaniu instrukcji. Cztery bity pozwalają na 16 różnych kombinacji i tyle różnych instrukcji może wykonać procesor PMC.

Dwa następne bity b10 i b11 określają tzw. tryb adresowania. Tryb adresowania jest sposobem pobierania przez procesor argumentu dla instrukcji. Dwa bity pozwalają na cztery różne tryby adresowania.

Pozostałe bity b0...b9 określają argument dla instrukcji. Ich dokładne znaczenie zależy od zawartości pola trybu adresowania.

 

Tryby adresowania procesora PMC

Procesor PMC potrafi pobierać argument dla rozkazu na cztery różne sposoby. Rodzaj adresowania zapisany jest w postaci dwubitowego kodu w polu tryby adresowania instrukcji PMC. W zależności od kombinacji tych bitów rozróżniamy następujące tryby adresowania:

 

b11 b10
0 0
Tryb adresowania akumulatora.

Argument dla instrukcji znajduje się w rejestrze akumulatora.

b11 b10
0 1
Tryb adresowania natychmiastowy.

Argument znajduje się w bitach od b0...b9 kodu instrukcji. Bity te są traktowane jako liczba w kodzie U2 (jeśli nie wiesz, co to jest liczba w kodzie U2, to musisz przeczytać odpowiedni rozdział w artykule Binarne Kodowanie Liczb). Ponieważ bitów tych jest 10, to możliwe wartości argumentu w trybie natychmiastowym zawierają się w granicach od -512 do 511.

b11 b10
1 0
Tryb adresowania bezpośredni

Argument znajduje się w komórce pamięci, której adres przechowują bity b0...b5 kodu instrukcji. Pozostałe bity pola argumentu są ignorowane. Jeśli adres wynosi 0, to argument jest w rejestrze INOUT.

b11 b10
1 1
Tryb pośredni

Bity b0...b5 kodu instrukcji zawierają adres komórki pamięci, która przechowuje adres właściwego argumentu. Stąd nazwa tryb pośredni - w instrukcji wskazujemy komórkę zawierającą właściwy adres argumentu. Taki tryb znakomicie nadaje się do adresowania danych umieszczonych w tablicy, ponieważ w prosty sposób można zmieniać adres zawarty w komórce.

Wszystkie podane tryby adresowania argumentu mogą być stosowane z każdą instrukcją PMC (chociaż nie zawsze ma to sens).

 

Instrukcje procesora PMC

Rodzaj instrukcji do wykonania określa 4-bitowe pole instrukcji. Instrukcje są następujące:

 

b15 b14 b13 b12
0 0 0 0
END  arg

Instrukcja powoduje zakończenie wykonywania przez PMC programu. Argument jest pobierany i zwracany jako wynik instrukcji END (pojawia się w okienku informacyjnym). Po wykonaniu tej instrukcji program może być uruchomiony jedynie od nowa - nie ma możliwości kontynuacji.

b15 b14 b13 b12
0 0 0 1
LDA  arg

LoaD Accumulator

Instrukcja pobiera podany argument do rejestru akumulatora.

b15 b14 b13 b12
0 0 1 0
STA  arg

STore Accumulator

Instrukcje umieszcza zawartość akumulatora w podanym argumencie. Jeśli argument jest adresowany przez tryb 0 lub 1 (akumulatora lub natychmiastowy), to instrukcja nic nie robi.

b15 b14 b13 b12
0 0 1 1
ADD  arg

Instrukcja dodaje do akumulatora podany argument. Wynik operacji arytmetycznej i logicznej zawsze jest umieszczany w akumulatorze. Jeśli argument jest adresowany przez tryb 0 (tryb akumulatora), to w wyniku zawartość akumulatora ulegnie podwojeniu. Instrukcja nie sprawdza, czy wynik operacji mieści się w 16-bitowym zakresie liczb U2. Jeśli zakres zostanie przekroczony, to wynik jest zawsze obcinany do 16 bitów - w rezultacie może powstać błędna wartość sumy. Zagadnienia te są dokładnie opisane w artykule o Binarnym Kodowaniu Liczb.

b15 b14 b13 b12
0 1 0 0
SUB  arg

SUBstract

Instrukcja odejmuje od akumulatora podany argument. Jeśli argument jest adresowany przez tryb 0, to w wyniku otrzymujemy 0. Jeśli wynik operacji przekracza zakres 16 bitowych liczb U2, to zostaje obcięty do 16 bitów.

b15 b14 b13 b12
0 1 0 1
MUL  arg

MULtiply

Instrukcja mnoży zawartość akumulatora przez podany argument. Jeśli argument jest adresowany przez tryb 0, to w wyniku otrzymujemy kwadrat poprzedniej zawartości akumulatora. Wynik mnożenia jest zawsze obcinany do 16 bitów w kodzie U2.

b15 b14 b13 b12
0 1 1 0
DIV  arg

DIVide

Instrukcja dzieli zawartość akumulatora przez podany argument. Jeśli argument jest adresowany przez tryb 0, to wynik wynosi 1. Jeśli argument ma wartość 0, to powstaje błąd dzielenia przez 0 i wykonanie programu zostaje zatrzymane z wyświetleniem odpowiedniego komunikatu.

b15 b14 b13 b12
0 1 1 1
MOD  arg

MODulo

Instrukcja oblicza wartość reszty z dzielenia akumulatora przez podany argument. Jeśli argument jest adresowany przez tryb 0, to wynik wynosi 0. Jeśli argument jest równy 0, to powstaje błąd dzielenia przez 0 i wykonanie programu zostaje wstrzymane z wyświetleniem odpowiedniego komunikatu.

b15 b14 b13 b12
1 0 0 0
INC  arg

INCrement

Instrukcja zwiększa o 1 podany argument. Jeśli argument jest adresowany przez tryb 1 (tryb natychmiastowy), instrukcja nic nie robi. Jeśli wynik operacji przekracza zakres 16-bitowych liczb U2, zostanie obcięty do 16 bitów.

b15 b14 b13 b12
1 0 0 1
DEC  arg

DECrement

Instrukcja zmniejsza o 1 podany argument. Jeśli argument jest adresowany przez tryb 1 (tryb natychmiastowy), instrukcja nic nie robi. Jeśli wynik operacji przekracza zakres 16-bitowych liczb U2, zostanie obcięty do 16 bitów.

b15 b14 b13 b12
1 0 1 0
JMP  arg

JuMP

Instrukcja skoku pod adres równy podanemu argumentowi. Wykonanie tej instrukcji polega na umieszczeniu wartości argumentu w rejestrze licznika rozkazów. Dzięki temu następna instrukcja zostanie wykonana nie z następnego adresu, lecz z nowego. Instrukcje skoku umożliwiają tworzenie tzw. pętli oraz rozgałęzień w programie. Chociaż instrukcja może być stosowana z dowolnym trybem adresowania, jednak najczęściej jest stosowany tryb natychmiastowy - adres skoku znajduje się bezpośrednio w bitach pola argumentu instrukcji.

b15 b14 b13 b12
1 0 1 1
JZR  arg

Jump on ZeRo

Instrukcja wykonuje skok pod adres wskazany argumentem tylko wtedy, gdy akumulator zawiera wartość zero. W przeciwnym razie skok nie zostanie wykonany - następną instrukcją dla procesora będzie instrukcja za rozkazem skoku. Tego typu operacja nazywana jest skokiem warunkowym (ang. conditional jump).

b15 b14 b13 b12
1 1 0 0
JMI  arg

Jump on MInus

Instrukcja wykonuje sok pod adres wskazany argumentem tylko wtedy, gdy akumulator zawiera wartość ujemną. W przeciwnym razie skok nie zostanie wykonany i procesor przejdzie do instrukcji znajdującej się za rozkazem skoku.

b15 b14 b13 b12
1 1 0 1
AND  arg

Instrukcja wykonuje operację logiczną AND na bitach akumulatora i argumentu. Wynik jest umieszczany w akumulatorze. (Jeśli nic nie wiesz na temat operacji logicznych, to przeczytaj artykuł o  Binarnym Kodowaniu Liczb).

b15 b14 b13 b12
1 1 1 0
ORA  arg

OR with Accumulator

Instrukcja wykonuje operację logiczną OR na bitach akumulatora i argumentu. Wynik jest umieszczany w akumulatorze.

b15 b14 b13 b12
1 1 1 1
XOR  arg

eXclusive OR

Instrukcja wykonuje operację logiczną XOR na bitach akumulatora i argumentu. Wynik jest umieszczany w akumulatorze.

 

Przykład:

Dla przykładu zakodujmy w postaci binarnej instrukcję, która dodaje do zawartości akumulatora liczbę 15. Kod instrukcji dodawania, to 0011. W ten sposób określamy bity pola kodu instrukcji. Liczba 15 jest daną natychmiastową, zatem pole trybu adresowania ma kod 01. Pozostałe dziesięć bitów określa w kodzie U2 liczbę 15 - 0000001111. Łączymy poszczególne pola w całość i otrzymujemy:

 

0011010000001111 - dodaj do akumulatora liczbę 15. Proste?

 

Mnemoniki instrukcji PMC

Ponieważ zapis bitowy instrukcji PMC jest niewygodny, każda instrukcja posiada 3 literową nazwę, którą nazywamy mnemonikiem. Mnemonik ułatwia zapamiętanie polecenia. W poniższej tabeli zebraliśmy wszystkie instrukcje PMC

 

Mnemonik Pole  instrukcji Krótki opis
END arg
0000 Koniec programu
LDA arg
0001 AC ← arg,  PCPC + 1
STA arg
0010 arg ← ACPCPC + 1
ADD arg
0011 ACAC + arg,  PCPC + 1
SUB arg
0100 ACAC - arg,  PCPC + 1
MUL arg
0101 ACAC x arg,  PCPC + 1
DIV arg
0110 ACAC : arg,  PCPC + 1
MOD arg
0111 AC ←  AC MOD arg,  PCPC + 1
INC arg
1000 arg ← arg + 1,  PCPC + 1
DEC arg
1001 arg ← arg - 1, PCPC + 1
JMP arg
1010 PC ← arg
JZR arg
1011 Jeśli AC = 0, PC ← arg, inaczej PCPC + 1
JMI arg
1100 Jeśli AC < 0, PC ← arg, inaczej PCPC + 1
AND arg
1101 ACAC AND arg,  PCPC + 1
ORA arg
1110 ACAC OR arg,  PCPC + 1
XOR arg
1111 ACAC XOR arg,  PCPC + 1

 

Oprócz rozkazów dla procesora PMC zdefiniowana jest jeszcze tzw, dyrektywa DAT, która umożliwia umieszczenie danych w komórce pamięci. Dyrektywa nie jest rozkazem i nie zostaje przetłumaczona na kod instrukcji. Zamiast tego w bieżącej komórce umieszcza się argument tej dyrektywy. Argument może być liczbą 16-bitową U2 o zakresie od -32768 do 32767.

DAT 115 ;umieszczenie w komórce liczby 115

Oznaczenia trybów adresowania PMC

PMC posiada 4 tryby adresowania argumentu.

Tryb adresowania akumulatora

Tryb adresowania akumulatora tworzymy nie umieszczając za instrukcją żadnych danych. Na przykład

ADD

Oznacza dodanie do siebie zawartości rejestru akumulatora.

Uwaga - jeśli zastosujemy rozkaz skoku JMP, JZR lub JMI bez argumentu, to PMC wykona skok pod adres zawarty w akumulatorze. Czasami może to być potrzebne (tzw. skoki wyliczane).

 

Tryb natychmiastowy

W tym trybie wartość argumentu jest bezpośrednio zakodowana w bitach pola argumentu. Tryb natychmiastowy oznaczamy umieszczając bezpośrednio przed wartością znak #. Na przykład:


LDA #15

oznacza polecenie umieszczenia w akumulatorze liczby 15. Po wykonaniu tej instrukcji akumulator będzie zawierał wartość 15. Inne przykłady:


ADD #8 ;dodanie do zawartości akumulatora liczby 8
MUL #5 ;pomnożenie akumulatora przez 5

Tryb natychmiastowy stosowany jest standardowo z rozkazami skoków:


JMP #26 ;skok do instrukcji umieszczonej pod adresem 26

Tryb bezpośredni

Pole argumentu zawiera w tym trybie adres komórki pamięci PMC, w której jest właściwy argument. Tryb bezpośredni oznaczamy umieszczając bezpośrednio przed adresem znak $. Na przykład:


LDA $15

oznacza polecenie umieszczenia w akumulatorze zawartości komórki pamięci PMC o adresie 15. Zwróć uwagę, iż nie jest to to samo co poprzednio, ponieważ komórka 15 może zawierać dowolną wartość. Inne przykłady:


STA $0  ;umieść zawartość akumulatora w rejestrze INOUT
STA $10 ;umieść zawartość akumulatora w komórce pamięci pod adresem 10
ADD $5  ;dodaj do zawartości akumulatora zawartość komórki pod adresem 5

Zapamiętaj dobrze różnicę pomiędzy trybem natychmiastowym, a bezpośrednim. W trybie natychmiastowym argument jest bezpośrednio podany w instrukcji. W trybie bezpośrednim w instrukcji podajemy adres komórki zawierającej argument:


MUL #6 ;mnóż akumulator przez liczbę 6
MUL $6 ;mnóż akumulator przez to, co zawiera komórka o adresie 6

Tryb pośredni

W trybie tym instrukcja w polu argumentu zawiera adres komórki przechowującej właściwy adres argumentu. Dostęp do argumentu jest zatem pośredni poprzez wskazywaną komórkę. Tryb pośredni oznaczamy umieszczając bezpośrednio przed adresem komórki znak *:


LDA *15

Instrukcja ta oznacza pobranie do akumulatora zawartości komórki pamięci PMC, której adres przechowuje komórka o adresie 15. Jeśli przykładowo w komórce tej jest liczba 25, to do akumulatora trafi zawartość komórki o adresie 25.

Tryb pośredni jest najtrudniejszym do zrozumienia trybem adresowania, lecz z drugiej strony najczęściej się go stosuje przy dostępie do tablic. Zapamiętaj, iż w trybie pośrednim instrukcja zawiera adres komórki, w której jest właściwy adres argumentu.

 

Etykiety

Posługiwanie się adresami liczbowymi jest dosyć żmudne i nużące. Dlatego programiści wymyślili etykiety. Etykieta jest nazwą, którą dużo łatwiej zapamiętać. W PMC etykiety muszą rozpoczynać się od litery lub od znaku podkreślenia. Następne znaki muszą być literami, cyframi lub znakami podkreślenia. Tylko pierwsze 8 znaków etykiety jest znaczące. Etykieta musi się kończyć znakiem dwukropka.

Etykiety umieszcza się zawsze przed instrukcją lub przed dyrektywą DAT. Etykieta przyjmuje wartość adresu instrukcji, przy której została umieszczona. Umożliwia to później łatwiejsze wykonanie skoku do wybranej instrukcji lub odwołanie się do danych. Na przykład poniższy program sumuje dwie komórki i wynik umieszcza w trzeciej komórce.

Wersja bez etykiet

 

DAT 10 ;pierwsza liczba
DAT 15 ;druga liczba
DAT  0 ;tutaj będzie wynik dodawania
LDA $1 ;pobieramy do akumulatora pierwszą liczbę
ADD $2 ;dodajemy do niej drugą liczbę
STA $3 ;wynik umieszczamy w trzeciej komórce
END

 

Wersja z etykietami

 

L1:    DAT 10     ;pierwsza liczba
L2:    DAT 15     ;druga liczba
WYNIK: DAT  0     ;tutaj będzie wynik dodawania
START: LDA $L1    ;pobieramy do akumulatora pierwszą liczbę
       ADD $L2    ;dodajemy do niej drugą liczbę
       STA $WYNIK ;wynik umieszczamy w trzeciej komórce
       END

 

Program jest zawsze umieszczany w pamięci PMC począwszy od komórki o adresie 1. Zatem pierwsza instrukcja programu zawsze znajduje się w komórce o adresie 1. Bez etykiet musimy sami pamiętać, w których komórkach są dane i instrukcje. Z etykietami zadanie to mamy rozwiązane w prosty sposób. Do komórek będziemy się odwoływać za pomocą symbolicznych etykiet, zatem nie musimy nawet wiedzieć, pod którym adresem dana komórka się znajduje - zadanie to przejmuje za nas asembler, który tłumaczy tekst programu PMC na odpowiednią zawartość komórek pamięci.

 

Dane

Jako dane w instrukcjach PMC można stosować:

 

Liczby dziesiętne

 

DAT 1745
DAT -15
LDA #33
STA $25
ADD *12

 

Liczby szesnastkowe

 

DAT 0FFFFH
DAT 35AH
AND #3FH
STA $4CH

 

Liczby szesnastkowe (jeśli nic nie wiesz o liczbach szesnastkowych, ósemkowych i binarnych, przeczytaj artykuł o Binarnym Kodowaniu Liczb) kończą się zawsze literką H. Jeśli liczba szesnastkowa rozpoczyna się od cyfry A,B,C,D,E lub F, to cyfrę tę należy poprzedzić cyfrą 0 - w przeciwnym razie asembler będzie sądził, iż jest to etykieta i powstanie błąd.

 

Liczby ósemkowe

 

DAT 777Q
DAT 33Q
DIV #45Q

 

Liczby ósemkowe kończą się zawsze literą Q.

 

Liczby dwójkowe

 

DAT 1100101B
DAT 1100B
AND #11110000B

 

Liczby dwójkowe zawsze kończą się literą B.

 

Znaki

 

DAT "H
DAT "A
LDA #"B

 

Znak jest zawsze poprzedzony cudzysłowem. Znak zostaje zamieniony na kod ASCII i w takiej postaci jest umieszczany w pamięci lub w kodzie instrukcji. Z kilku znaków tylko pierwszy jest znaczący:

 

LDA #"ABC ;w akumulatorze znajdzie się kod ASCII litery A

 

Etykiety

 

DAT TEKST
DAT DANE
LDA $CZYNNIK
JMP #START

 

Jako dane można również stosować etykiety. Wartością etykiety jest zawsze adres komórki, do której etykieta się odnosi.

 

Budowa wiersza programu PMC

Tworząc program dla PMC musimy przestrzegać kilku prostych zasad.

 

Wiersz pusty

W programie można wstawiać dowolną ilość wierszy pustych (w przykładzie wiersz pusty pomiędzy etykietami LICZNIK oraz START):

 

LICZNIK: DAT 0

START:   LDA $LICZNIK
         ...

 

Wiersze puste nie są tłumaczone na zawartość pamięci, nie wpływają zatem na długość programu wynikowego.

 

Komentarz

Komentarz w PMC rozpoczyna się od znaku średnika, po którym możemy umieścić dowolny tekst. Tekst ten nie jest analizowany przez asembler. Komentarze można umieszczać w dowolnym miejscu wiersza. Od początku komentarza do końca wiersza programu treść jest ignorowana. Komentarze są doskonałym narzędziem do dokumentowania działania programu

 

;Zmienne programu
PRCNT: DAT  10  ;wartość procentu
KONTO: DAT 120  ;stan konta

 

Etykieta

Jeśli wiersz ma definiować etykietę, to musi się ona znajdować na początku wiersza:

 

LP1:  LDA $LICZNIK
      ADD #23
      STA $LICZNIK
      JMI #LP1

 

Jeśli w wierszu umieścimy samą etykietę, to definiuje ona pierwszą komórkę z instrukcją, która wystąpi w najbliższym wierszu:

 

START:
        LDA $XXX
        ...

 

W tym przykładzie etykieta START wskazuje komórkę z instrukcją LDA $XXX. Nie wolno definiować więcej niż jednej etykiety dla tej samej komórki.

 

Wcięcia

Początkowe spacje są nieznaczące, zatem można je wykorzystywać do pozycjonowania instrukcji programu w tej samej pionowej kolumnie, co znakomicie poprawia czytelność. Porównaj dwa przykłady tego samego programu bez wcięć i z wcięciami. Który jest bardziej estetyczny i czytelny?

 

WSP_A: DAT -5
WSP_B: DAT 3
X: DAT 0
START: LDA $WSP_B
JZR #KONIEC
LDA $WSP_A
DIV $WSP_B
STA $X
KONIEC: END

 

WSP_A:  DAT -5
WSP_B:  DAT  3
X:      DAT  0

START:  LDA $WSP_B
        JZR #KONIEC
        LDA $WSP_A
        DIV $WSP_B
        STA $X
KONIEC: END

 

Programowanie PMC

Najprostszy program

Najprostszy program dla PMC II nawet nie musisz pisać. Po uruchomieniu symulatora w okienku tekstu programu masz:

 

; Program dla PMC II
; Autor:
; Wersja:
; Data:

START: END

 

Oczywiście program ten nic nie robi. Po kompilacji i uruchomieniu program natychmiast się zatrzymuje z powodu instrukcji END.

 

Ładowanie danych do akumulatora

Kolejny program również jest bardzo prosty. Umieszcza ona w rejestrze akumulatora liczbę 133.

 

START: LDA #133 ;umieść w akumulatorze 133
       END      ;zakończ program

 

Gdy go skompilujemy i uruchomimy, to po zatrzymaniu powinniśmy zauważyć, iż akumulator zawiera wartość 133.

 

Akumulator Licznik Rozkazów Rejestr Instrukcji
0000000010000101 : 0085 : 133 0002 : 2 0000 : END

 

Zwróć uwagę, iż zawartość akumulatora jest prezentowana przez symulator PMC II na trzy sposoby:

 

- jako liczba binarna: 0000000010000101
- jako liczba szesnastkowa: 0085
- jako liczba dziesiętna: 133

 

Dzięki temu rozwiązaniu możesz w prosty sposób interpretować zawartość akumulatora wg potrzeb.

Licznik rozkazów zatrzymał się na adresie 2. Adres ten zawiera instrukcję END, co widzimy w rejestrze instrukcji. To właśnie ta instrukcja zatrzymała dalsze wykonywanie programu.

 

Zapis danych do pamięci

Program umieszcza liczbę 125 w pierwszej komórce pamięci.

 

DANE:  DAT    0  ;tutaj umieścimy liczbę 125

START: LDA #125  ;umieszczamy najpierw w akumulatorze liczbę 125
       STA $DANE ;teraz zawartość akumulatora umieszczamy w komórce 1
       END

 

Zwróć uwagę, iż przed uruchomieniem programu komórka 1 (o etykiecie DANE) zawiera wartość 0. Wartość tę umieszcza tam dyrektywa DAT. Po wykonaniu programu w komórce tej powinna znaleźć się wartość 125, czyli to, co program tam umieścił.

 

 Adr  Etykieta Inst  Argument  Pamięć
00  INOUT:      
01 DANE: DAT  125 007D    125
02 START: LDA  #125 147D   5245
03   STA  $DANE 2801  10241
04   END   0000      0

 

Przesłanie danych

Przesłanie danych ma na celu pobranie informacji przechowywanej w jednej komórce i umieszczenie jej w innej. Poniższy program przenosi dane z komórki DANE1 do DANE2:

 

DANE1: DAT 147    ;tę liczbę skopiujemy do komórki następnej
DANE2: DAT   0    ;tutaj będzie skopiowana zawartość komórki poprzedniej

START: LDA $DANE1 ;pobieramy zawartość pierwszej komórki
       STA $DANE2 ;i umieszczamy ją w drugiej komórce
       END

 

Po zakończeniu programu w obu komórkach DANE1 i DANE2 mamy tę samą wartość 147.

 

Adr  Etykieta Inst  Argument  Pamięć
00  INOUT:      
01 DANE1: DAT  147 0093    147
02 DANE2: DAT  147 0093    147
03 START: LDA  $DANE1 1801   6145
04   STA  $DANE2 2802  10242
05   END   0000      0

 

Zamiana zawartości

Operacja zamiany zawartości polega na przesłaniu danych pomiędzy komórkami, tak aby ich zawartości zostały nawzajem zamienione: w komórce pierwszej ma się znaleźć to co było w drugiej, a we drugiej to co było w pierwszej Operacja taka wymaga dodatkowej zmiennej pomocniczej, w której przechowujemy tymczasowo zawartość jednej z komórek. Zamiany dokonujemy w trzech krokach:

  1. Komórkę A przesyłamy do komórki pomocniczej X
  2. Do komórki A przesyłamy komórkę B
  3. Do komórki B przesyłamy komórkę pomocniczą X

Poniższy program zamienia miejscami zawartości komórek DANE1 i DANE2:

 

DANE1: DAT  15     ;zawartości tych dwóch komórek zostaną
DANE2: DAT 188     ;zamienione miejscami
X:     DAT   0     ;to jest zmienna pomocnicza

START: LDA $DANE1  ;przesyłamy DANE1 do X
       STA $X
       LDA $DANE2  ;przesyłamy DANE2 do DANE1
       STA $DANE1
       LDA $X      ;przesyłamy X do DANE1
       STA $DANE2
       END

 

Po wykonaniu programu komórki DANE1 i DANE2 mają zamienioną zawartość.

 

 Adr  Etykieta Inst  Argument  Pamięć
00  INOUT:      
01 DANE1: DAT  188 00BC    188
02 DANE2: DAT  15 000F     15
03 X: DAT  15 000F     15
04 START: LDA  $DANE1 1801   6145
05   STA  $X 2803  10243
06   LDA  $DANE2 1802   6146
07   STA  $DANE1 2801  10241
08   LDA  $X 1803   6147
09   STA  $DANE2 2802  10242
10   END   0000      0

 

Wyprowadzanie znaków

PMC II posiada tylko jedno urządzenie wyjścia - rejestr INOUT, który zajmuje adres 0 w przestrzeni adresowej. Każdy zapis do tego rejestru powoduje przesłanie na wyświetlacz jednego znaku. Poniższy program wyświetla literkę A:

 

START: LDA #"A    ;pobieramy do akumulatora kod literki A
       STA $INOUT ;przesyłamy go do rejestru wyjścia
       END

 

Po wykonaniu tego programu na wyświetlaczu pojawi się literka A.

 

 
A 

 

Pętla nieskończona

Pętla to konstrukcja programowa, w której wybrana grupa instrukcji jest cyklicznie powtarzana. Pętla nieskończona wykonywana jest ciągle, bez końca. Aby zatrzymać program z pętlą nieskończoną musisz posłużyć się przyciskiem STOP. Poniższy program wyświetla ciąg zer i jedynek.

 

START: LDA #"0    ;pobieramy do akumulatora znak 0
       STA $INOUT ;przesyłamy go na wyjście
       LDA #"1    ;pobieramy znak 1
       STA $INOUT ;przesyłamy go na wyjście
       JMP #START ;skaczemy na początek programu - powtarzamy od nowa

 

Cechą charakterystyczną pętli nieskończonej jest instrukcja skoku JMP do początku pętli. Skok ten powoduje ponowne wykonywanie rozkazów zawartych pomiędzy początkiem pętli a instrukcją skoku.

 

 
1010101010101010101010101010101010101010101010101010101010101010 

 

Podejmowanie decyzji

Cechą odróżniającą komputery od prostych liczydeł jest możliwość podejmowania różnych działań w zależności od sytuacji napotkanej w trakcie obliczeń. Obliczenia niejako mogą być wykonywane różnymi drogami w zależności od wyników operacji poprzednich. Dzięki tym cechom komputer może działać celowo - mądrze, inteligentnie.

W repertuarze rozkazów PMC II mamy dwie instrukcje, których wykonanie zależy od spełnienia pewnego warunku:

 

JZR - skok do adresu, gdy akumulator zawiera zero
JMI - skok do adresu, gdy akumulator zawiera wartość ujemną

 

Wbrew pierwszemu wrażeniu te dwie instrukcje pozwalają badać nawet złożone warunki. Poniżej podajemy odpowiednie przykłady:

 

Wyznaczanie znaku liczby

Poniższy program bada liczbę przechowywaną w zmiennej DANE. W zależności od wartości tej liczby wyświetla jeden z napisów <0, =0 lub >0.

 

DANE:  DAT -256

START: LDA $DANE  ;pobieramy liczbę do akumulatora
       JZR #ZERO  ;czy liczba równa zero?
       JMI #MINUS ;czy liczba ujemna?
       LDA #">    ;liczba dodatnia
       JMP #PISZ  ;wyświetlamy tekst
ZERO:  LDA #"=    ;liczba równa zero
       JMP #PISZ
MINUS: LDA #"<    ;liczba ujemna
PISZ:  STA $INOUT ;wypisujemy odpowiedni tekst
       LDA #"0
       STA $INOUT
       END

 

Wartość bezwzględna

Poniższy program bada zawartość komórki DANE. Jeśli jest ona ujemna, to zmienia ją na dodatnią.

 

DANE:  DAT -68    ;komórka z danymi

START: LDA $DANE  ;sprawdzamy, czy liczba jest ujemna
       JMI #MINUS ;jeśli tak, skaczemy do etykiety MINUS
       END        ;jeśli nie, kończymy program. Liczba jest dodatnia

MINUS: LDA #0     ;zmieniamy znak liczby odejmując ją od 0
       SUB $DANE
       STA $DANE
       END

 

Wartość większa

Ten program bada zawartość dwóch komórek DANE1 i DANE2. W komórce MAX umieszcza większą z nich.

 

DANE1:  DAT  139   ;komórki z danymi
DANE2:  DAT  278
MAX:    DAT    0   ;tutaj program umieści większą z liczb

START:  LDA $DANE1 ;pobieramy pierwszą liczbę
        SUB $DANE2 ;testowo odejmujemy od niej drugą liczbę
        JMI #D2    ;jeśli różnica mniejsza od 0, to DANE2 jest większa
        LDA $DANE1 ;tutaj większa jest DANE1
        JMP #KONIEC
D2:     LDA $DANE2 ;tutaj większa jest DANE2
KONIEC: STA $MAX   ;w MAX umieszczamy większą z liczb
        END 

 

Pętla warunkowa

Pętla warunkowa powstaje wtedy, gdy wybrana grupa instrukcji jest powtarzana w zależności od spełnienia pewnego warunku.

Poniższy program odczytuje dane z wejścia PMC II. Jeśli bufor jest pusty, to odczyt rejestru daje wartość 0. W takim przypadku program czeka aż na wejściu pojawi się jakaś dane. To jest pierwsza pętla warunkowa - cykliczny odczyt wejścia aż pojawi się na nim dana.

Gdy na wejściu pojawią się dane program wchodzi w drugą pętle warunkową - odczytuje dane i przesyła je na wyjście aż skończą się. W takim przypadku program jest kończony.

 

START:  LDA $INOUT  ;pobieramy znak z wejścia
        JZR #START  ;jeśli brak znaku, czekamy aż się pojawi
PISZ:   STA $INOUT  ;odczytany znak przesyłamy na wyjście
        LDA $INOUT  ;pobieramy kolejny znak
        JZR #KONIEC ;jeśli dane się skończyły, kończymy
        JMP #PISZ   ;w przeciwnym razie kontynuujemy
KONIEC: END

 

Kolejny program wykorzystuje pośredni tryb adresowania do przesłania zadanego tekstu na wyjście. Tekst jest przesyłany z kolejnych komórek pamięci dotąd, aż zostanie napotkany znak o kodzie 0. Wtedy program kończy swoje działanie.

 

TEKST:  DAT "W      ;w kolejnych komórkach umieszczamy
        DAT "I      ;poszczególne literki tekstu witaj
        DAT "T
        DAT "A
        DAT "J
        DAT 0       ;to jest koniec tekstu
TPTR:   DAT TEKST   ;tutaj mamy adres pierwszego znaku tekstu

START:  LDA *TPTR   ;pobieramy znak tekstu
        JZR #KONIEC ;jeśli koniec tekstu, kończymy
        STA $INOUT  ;znak przesyłamy na wyjście
        INC $TPTR   ;zwiększamy adres tekstu - następny znak
        JMP #START  ;kontynuujemy pętle z następnym znakiem
KONIEC: END

 

Pętla iteracyjna

Pętla iteracyjna jest pętlą warunkową, która wykonuje się zadaną ilość razy. Cechą charakterystyczną pętli iteracyjnej jest licznik zliczający kolejne obiegi. Gdy licznik osiągnie zadaną wartość, pętla kończy działanie.

Poniższy program wyświetla zadaną ilość literek X.

 

LICZNIK: DAT 15       ;liczba obiegów pętli

START:   LDA $LICZNIK ;sprawdzamy, czy licznik osiągnął 0
         JZR #KONIEC  ;jeśli tak, kończymy
         SUB #1       ;zmniejszamy licznik o 1
         STA $LICZNIK
         LDA #"X      ;na wyjście przesyłamy znak X
         STA $INOUT
         JMP #START   ;kontynuujemy pętlę
KONIEC:  END

 

Drugi program wypisuje na wyświetlaczu wszystkie duże literki od A do Z.

 

LICZNIK: DAT "A       ;wartość początkowa licznika - kod literki A

START:   LDA $LICZNIK ;licznik przesyłamy na wyjście
         STA $INOUT
         SUB #"Z      ;sprawdzamy, czy licznik osiągnął wartość Z
         JZR #KONIEC  ;jeśli tak, kończymy
         INC $LICZNIK ;jeśli nie, przechodzimy do kolejnej literki
         JMP #START   ;i kontynuujemy pętlę
KONIEC:  END

 

Wypisywanie liczb

Większość komputerów wypisuje liczby wykonując odpowiedni program. W tym celu zamienia się wartość liczby na odpowiedni ciąg znaków. Algorytm jest następujący:

Kolejne od końca cyfry otrzymujemy jako resztę z dzielenia liczby przez podstawę systemu, w którym chcemy zapisać liczbę. Wartość liczby dzielimy przez podstawę. Operację kontynuujemy dotąd, aż liczba osiągnie wartość 0. Wtedy otrzymane cyfry wyświetlamy. Dokładne algorytmy zamiany liczb na ciągi znaków znajdziesz w artykule Binarne Kodowanie Liczb.

Poniższy program wyświetla dziesiętnie zawartość komórki DANE

 

DANE:   DAT 26541   ;liczba do wyświetlenia - dodatnia!
CYFRY:  DAT 0       ;bufor na 5 cyfr
        DAT 0
        DAT 0
        DAT 0
        DAT 0
CPTR:   DAT 0       ;wskaźnik cyfr w buforze

START:  LDA #CYFRY  ;ustawiamy CPTR na ostatnią cyfrę
        ADD #4
        STA $CPTR   ;CPTR -> CYFRY[4]
LP1:    LDA $DANE   ;pobieramy liczbę
        MOD #10     ;wyznaczamy cyfrę
        ADD #48     ;dodajemy kod ASCII
        STA *CPTR   ;wstawiamy cyfrę do bufora
        LDA $CPTR   ;przesuwamy się na kolejną cyfrę
        SUB #1
        STA $CPTR
        LDA $DANE   ;liczbę dzielimy przez podstawę 10
        DIV #10
        STA $DANE
        JZR #PISZ   ;jeśli koniec, wypisujemy bufor
        JMP #LP1    ;jeśli nie, kontynuujemy pętlę
PISZ:   INC $CPTR   ;CPTR wskazuje o jedną cyfrę wcześniej. Korygujemy
        LDA *CPTR   ;pobieramy cyfrę
        STA $INOUT  ;przesyłamy ją na wyjście
        LDA $CPTR   ;sprawdzamy, czy koniec bufora
        SUB #CYFRY
        SUB #4
        JZR #KONIEC
        JMP #PISZ   ;jeśli nie, kontynuujemy
KONIEC: END  

 

Odczyt liczb

Odczyt liczb dokonuje się w sposób programowy wg tzw. schematu Hornera:

  1. Ustawiamy wartość liczby na 0.
  2. Odczytujemy z wejścia kolejny znak cyfry. Jeśli cyfry się skończyły - kończymy.
  3. Od kodu znaku odejmujemy 48, aby otrzymać wartość cyfry
  4. Liczbę mnożymy przez podstawę systemu i dodajemy do niej cyfrę
  5. Wracamy do punktu 2.

Dokładne algorytmy odczytu liczb w dowolnym systemie pozycyjnym znajdziesz w artykule Binarne Kodowanie Liczb.

Poniższy program odczytuje z wejścia liczbę (musi być zapisana poprawnie) w postaci ciągu znaków i wyznacza jej wartość w komórce DANE. Tam też zobaczymy wynik po zakończeniu programu.

 

DANE:   DAT 0       ;tutaj znajdzie się odczytana z wejścia liczba
X:      DAT 0       ;zmienna pomocnicza

START:  LDA $INOUT  ;czekamy, aż na wejściu pojawią się znaki
        JZR #START
LP1:    SUB #48     ;odejmujemy kod ASCII
        STA $X      ;cyfrę umieszczamy w zmiennej pomocniczej
        LDA $DANE   ;liczbę mnożymy przez 10
        MUL #10
        ADD $X      ;dodajemy cyfrę
        STA $DANE
        LDA $INOUT  ;pobieramy kolejną cyfrę
        JZR #KONIEC ;jeśli koniec cyfr, kończymy
        JMP #LP1    ;w przeciwnym razie kontynuujemy
KONIEC: END

 


   I Liceum Ogólnokształcące   
im. Kazimierza Brodzińskiego
w Tarnowie

©2024 mgr Jerzy Wałaszek

Dokument ten rozpowszechniany jest zgodnie z zasadami licencji
GNU Free Documentation License.

Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl

W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe