Serwis Edukacyjny w I-LO w Tarnowie Materiały dla uczniów liceum |
Wyjście Spis treści Wstecz Dalej Autor artykułu: mgr Jerzy Wałaszek |
©2024 mgr Jerzy Wałaszek
|
SPIS TREŚCI |
Podrozdziały |
Umówmy się na początku, iż teksty będziemy umieszczać w pamięci PMC ze specjalnym kodem o wartości 0 na końcu. Kod ten nazywa się znacznikiem końca tekstu. W PMC można to osiągnąć następująco:
... TEKST: DAT "Poznaj swój komputer" ;właściwy tekst DAT 0 ;znacznik końca tekstu ...
Program odczytujący taki tekst z pamięci po natrafieniu na znak o kodzie 0 będzie wiedział, iż tekst się właśnie mu skończył. Znak zero można w prosty sposób przetestować poleceniem skoku warunkowego JZR.
Poniższy program wypisuje tekst powitalny na wyświetlaczu znakowym. Zasada działania jest bardzo prosta. Kolejne znaki tekstu pobierane są z pamięci przy pomocy polecenia LDA z trybem pośrednim z postinkrementacją. W trybie tym wykorzystywana jest jedna z komórek pamięci PMC do przechowywania adresu danych. Każde zaadresowanie powoduje pobranie danych z pamięci i zwiększenie ich adresu o 1, dzięki czemu wskazywane są kolejne dane (w naszym przypadku kolejne literki tekstu). Pobrany znak zostaje sprawdzony poleceniem JZR na wartość 0, która oznacza koniec tekstu. Jeśli nie jest to znak końca tekstu, to zostaje przesłany na wyświetlacz znakowy do portu CHP, a pozycja na wyświetlaczu w porcie CHI zostaje zwiększona.
RUN START TXT: DAT "Witamy w PMC" ;tekst do wyświetlenia DAT 0 ;znacznik końca tekstu TPTR: DAT TXT ;adres początku tekstu START: LDA (TPTR++) ;pobieramy kolejny znak tekstu JZR #EX1 ;kończymy, jeśli kod zero STA CHP ;przesyłamy na port znakowy INC CHI ;zwiększamy pozycję na wyświetlaczu JMP #START EX1:
Tego typu program potrafi wyświetlać tylko krótkie teksty. Dłuższe spowodują nadpisanie informacji, ponieważ przekroczenie pozycji nr 15 na wyświetlaczu spowoduje przewinięcie się licznika pozycji CHI, który jest 4 bitowy. Innym sposobem prezentacji tekstu może być przewijanie w stylu reklamy lub napisów na giełdzie. W PMC można to w prosty sposób osiągnąć ustawiając na stałe pozycję wyświetlania w CHI na 15, a następnie przesyłanie znaku do CHP, po którym następuje przesłanie kodu kontrolnego o wartości 258. Kod ten powoduje przesunięcie w lewo wszystkich literek na wyświetlaczu z wpisaniem znaku spacji na ostatniej pozycji.
Poniższy program wyświetla w kółko zadany napis. Ponieważ wyświetlanie napisu
jest powtarzane, to przed wejściem do pętli program ustawia na nowo adres tekstu
w zmiennej wskaźnikowej TPTR. W pętli głównej
wprowadziłem małą pętlę opóźniającą, aby tekst był czytelny na szybszym
komputerze. W razie konieczności należy dostosować zawartość akumulatora.
RUN START TPTR: DAT 0 ;tutaj będzie adres tekstu TEKST: DAT "W dzisiejszych rozgrywkach klas III w kategorii " DAT "programowania w języku Pascal pierwsze miejsce " DAT "zajęła klasa III J. Na miejscu drugim uplasowała " DAT "się klasa III A. Miejsce trzecie przypadło " DAT "w udziale klasie III C. GRATULUJEMY!" DAT " " DAT 0 ;znacznik końca tekstu START: LDA #15 ;ustawiamy pozycję na wyświetlaczu STA CHI LP1: LDA #TEKST ;ustawiamy adres początku testu STA TPTR LP2: LDA (TPTR++) ;pobieramy znak JZR #LP1 ;jeśli koniec, to od początku STA CHP ;znak na wyświetlacz LDA #-10 ;pętla opóźniająca, powtarzana aż LP3: ADD #1 ;ACR osiągnie wartość 0 JMI #LP3 LDA #258 ;kod przesunięcia w lewo STA CHP JMP #LP2
Tekst odczytujemy z portu INP. Brzmi to dosyć prosto, jednak w praktyce takie proste nie jest, ponieważ musimy obsłużyć kilka sytuacji, które mogą się zdarzyć w trakcie odczytu:
Z tego prostego przykładu możesz wyobrazić sobie złożoność operacji wejścia / wyjścia w prawdziwym systemie komputerowym, gdzie mogą wystąpić dodatkowe sytuacje (np. pełny dysk, błąd odczytu, zerwanie transmisji itp.).
Poniższy program obsługuje odczyt tekstu z portu INP. Odczytywany tekst wstawiony zostaje do tablicy o szesnastu elementach. Wielkość tablicy limituje długość tekstu do 15 znaków (dziesiąty znak to znak o kodzie 0). Jeśli użytkownik poda więcej znaków, to zostaną one odczytane, ale będą zignorowane i nie trafią do tablicy, w której będzie tylko pierwszych 15 znaków. Jeśli będzie ich mniej, to tablica nie zostanie zapełniona w całości. W każdym przypadku na końcu wczytanego tekstu zawsze umieszczany jest znak 0. Po wczytaniu tekstu zostanie on następnie wyprowadzony na wyświetlacz znakowy za pomocą metody opisanej w poprzednim rozdziale.
RUN START TPTR: DAT TEXT ;zmienna wskaźnikowa, przechowuje adres tekstu TEXT: DAT "0123456789ABCDEF" ;bufor dla tekstu - 16 znaków I: DAT 15 ;maksymalna liczba znaków do odczytu START: LDA INP ;odczytujemy pierwszy znak JZR #START ;bufor zajęty, czekamy JMI #START ;bufor pusty, też czekamy LP1: STA (TPTR++) ;jest znak, umieszczamy go w buforze LDA I ;sprawdzamy, czy bufor pełny, co stanie się, SUB #1 ;gdy licznik osiągnie zero STA I JZR #INPE ;tak, kończymy odczyt LP2: LDA INP ;czytamy kolejny znak JZR #LP2 ;jeśli bufor zajęty, to czekamy JMI #INPE ;jeśli pusty, to kończymy odczyt JMP #LP1 ;inaczej wykonujemy kolejny obieg INPE: LDA INP ;pozbywamy się reszty znaków JMI #EX1 JMP #INPE EX1: LDA #0 ;na końcu tekstu umieszczamy kod 0 STA (TPTR++) LDA #TEXT ;odtwarzamy adres początku tekstu STA TPTR LP3: LDA (TPTR++) ;wyświetlamy odczytany tekst JZR #0 ;znak końca tekstu, kończymy STA CHP ;zwykły znak, wyświetlamy go INC CHI ;następna pozycja JMP #LP3
Program produkuje wyniki, które umieszcza w komórkach pamięci. W PMC możemy podglądnąć zawartość dowolnych komórek, nie istnieje więc konieczność wyprowadzania liczb na wyświetlacz. Jednak w prawdziwym komputerze opcja taka nie jest dostępna. Dlatego musimy poznać metody przekształcania wartości liczby na ciąg cyfr w wybranym systemie pozycyjnym. Teoretycznie zadanie to rozwiązaliśmy we wcześniejszych rozdziałach. Teraz nadszedł czas na część praktyczną.
Pierwszy program oblicza kolejne cyfry dziesiętne podanej liczby dodatniej z zakresu od 0 do 32767. Do obliczeń stosujemy odwrotny algorytm Hornera. Polega on na znajdowaniu reszty z dzielenia liczby przez 10. Reszty te tworzą cyfry od ostatniej do pierwszej. Po obliczeniu reszty liczbę należy podzielić przez 10. Obliczenia kontynuujemy aż do otrzymania liczby o wartości zero. Na przykład:
Liczba ma wartość 23756.
23756 : | 10 = | 2375 i reszta 6 |
2375 : | 10 = | 237 i reszta 5 |
237 : | 10 = | 23 i reszta 7 |
23 : | 10 = | 2 i reszta 3 |
2 : | 10 = | 0 i reszta 2 |
Reszta jest wartością liczbową od 0 do 9. Aby otrzymać kod znaku ASCII przedstawiającego odpowiednią cyfrę, należy do otrzymanej reszty dodać kod znaku "0" równy 48. Ponieważ otrzymujemy cyfry w kolejności odwrotnej, to będą one wstawiane do 6 elementowej tablicy począwszy od 5 elementu (piąty element ma indeks 4 - nie zapominaj o tym, to bardzo ważne!!!) w dół (maksymalnie może być 5 cyfr). Tablica jest wypełniona znakiem spacji przed rozpoczęciem obliczania cyfr. W szóstym elemencie przechowywany jest znak o kodzie 0. Po uzyskaniu cyfr liczba zostaje wyświetlona na wyświetlaczu znakowym.
RUN START N: DAT 12654 ;liczba do wyświetlenia NTAB: DAT " " ;tablica 5 spacji DAT 0 ;znak końca tekstu NPTR: DAT 0 ;adres poszczególnych cyfr X: DAT 0 ;zmienna pomocnicza START: LDA #NTAB ;obliczamy adres 5 elementu + 1 ADD #5 STA NPTR ;adres do zmiennej wskaźnikowej LP1: LDA N ;obliczamy iloraz N przez 10 DIV #10 STA X ;zapamiętujemy go chwilowo MUL #10 ;obliczamy resztę z dzielenia SUB N ;reszta ujemna XOR #-1 ;obliczamy liczbę przeciwną w kodzie U2 ADD #49 ;obliczamy kod ASCII cyfry STA (--NPTR) ;umieszczamy ją w tablicy LDA X ;do N idzie wynik dzielenia przez 10 STA N JZR #EX1 ;jeśli zero, to koniec przetwarzania JMP #LP1 ;w przeciwnym razie następna cyfra EX1: LDA #NTAB ;wyświetlamy cyfry na wyświetlaczu STA NPTR LP2: LDA (NPTR++) JZR #0 STA CHP INC CHI JMP #LP2
Przedstawiony powyżej program potrafi wyświetlać poprawnie tylko liczby dodatnie i zero. Nie radzi sobie zupełnie z liczbami ujemnymi. Powodem jest sposób reprezentacji liczb ujemnych w pamięci maszyny - kod U2. Jednak prosta modyfikacja pozwoli wyświetlić również liczby ujemne.
Przed obliczaniem kolejnych cyfr sprawdzamy, czy liczba jest ujemna - jeśli tak, to zapamiętujemy ten fakt w osobnej zmiennej, np. wpisując tam znak "-". Następnie zamieniamy wartość liczby na przeciwną, obliczamy cyfry i na początku zapisu wpisujemy zapamiętany na początku znak "-". Ponieważ zapis liczby składa się teraz z maksymalnie 5 cyfr oraz znaku minus, to musimy powiększyć rozmiar tablicy przechowującej cyfry. Dla sprawdzenia, po wyliczeniu cyfr wyświetlamy je na wyświetlaczy znakowym.
RUN START N: DAT -2457 ;liczba do wyświetlenia NTAB: DAT " " ;tablica 6 spacji DAT 0 ;znak końca tekstu NPTR: DAT 0 ;adres poszczególnych cyfr SGN: DAT "+" ;przechowuje informacje o znaku liczby X: DAT 0 ;zmienna pomocnicza START: LDA #NTAB ;obliczamy adres 6 elementu + 1 ADD #6 STA NPTR ;adres do zmiennej wskaźnikowej LDA N ;sprawdzamy, czy N < 0 JMI #MINUS JMP #LP1 MINUS: XOR #-1 ;obliczamy wartość przeciwną ADD #1 STA N LDA #"-" ;do SGN wprowadzamy znak minus STA SGN LP1: LDA N ;obliczamy iloraz N przez 10 DIV #10 STA X ;zapamiętujemy go chwilowo MUL #10 ;obliczamy resztę z dzielenia SUB N ;reszta ujemna XOR #-1 ;obliczamy liczbę przeciwną w kodzie U2 ADD #49 ;obliczamy kod ASCII cyfry STA (--NPTR) ;umieszczamy ją w tablicy LDA X ;do N idzie wynik dzielenia przez 10 STA N JZR #EX1 ;jeśli zero, to koniec przetwarzania JMP #LP1 ;w przeciwnym razie następna cyfra EX1: LDA SGN ;na początku dopisujemy znak liczby STA (--NPTR) LDA #NTAB ;wyświetlamy cyfry na wyświetlaczu STA NPTR LP2: LDA (NPTR++) JZR #0 STA CHP INC CHI JMP #LP2
Program wciąż nie radzi sobie z wartością -32768, która jest poprawna w kodzie U2. Spowodowane jest to tym, iż wartości tej nie można poprawnie zapisać jako liczby przeciwnej, dodatniej. Kod U2 jest kodem niesymetrycznym i dla 16 bitowych liczb ma zakres (-32768, 32767). Jedynym sensownym rozwiązaniem jest sprawdzenie tej wartości w programie i jeśli mamy z nią do czynienia, to w tablicy należy bezpośrednio umieścić odpowiednie cyfry i zakończyć obliczenia. Oto kompletny program wyświetlania dowolnych liczb w systemie PMC:
RUN START N: DAT -2457 ;liczba do wyświetlenia NTAB: DAT " " ;tablica 6 spacji DAT 0 ;znak końca tekstu NPTR: DAT 0 ;adres poszczególnych cyfr SPCVAL: DAT -32768 ;wartość specjalna -32768 SPCASE: DAT "-32768" ;przechowuje bezpośredni zapis -32768 DAT 0 SPTR: DAT SPCASE ;używane przy kopiowaniu tablic SGN: DAT "+" ;przechowuje informacje o znaku liczby X: DAT 0 ;zmienna pomocnicza START: LDA N ;sprawdzamy, czy N = -32768 SUB SPCVAL JZR #SPEC JMP #NORMAL ;jeśli nie, to idziemy normalnym torem SPEC: LDA #NTAB ;skopiujemy tablicę znaków SPCASE STA NPTR ;do NTAB LP0: LDA (SPTR++) STA (NPTR++) JZR #EX2 JMP #LP0 NORMAL: LDA #NTAB ;obliczamy adres 6 elementu + 1 ADD #6 STA NPTR ;adres do zmiennej wskaźnikowej LDA N ;sprawdzamy, czy N < 0 JMI #MINUS JMP #LP1 MINUS: XOR #-1 ;obliczamy wartość przeciwną ADD #1 STA N LDA #"-" ;do SGN wprowadzamy znak minus STA SGN LP1: LDA N ;obliczamy iloraz N przez 10 DIV #10 STA X ;zapamiętujemy go chwilowo MUL #10 ;obliczamy resztę z dzielenia SUB N ;reszta ujemna XOR #-1 ;obliczamy liczbę przeciwną w kodzie U2 ADD #49 ;obliczamy kod ASCII cyfry STA (--NPTR) ;umieszczamy ją w tablicy LDA X ;do N idzie wynik dzielenia przez 10 STA N JZR #EX1 ;jeśli zero, to koniec przetwarzania JMP #LP1 ;w przeciwnym razie następna cyfra EX1: LDA SGN ;na początku dopisujemy znak liczby STA (--NPTR) EX2: LDA #NTAB ;wyświetlamy cyfry na wyświetlaczu STA NPTR LP2: LDA (NPTR++) JZR #0 STA CHP INC CHI JMP #LP2
Z podanych przykładów jasno wynika, iż procedury wyprowadzania liczb na wyświetlacze wcale nie są proste. Jest to prawdą również w świecie IBM. Zwróć uwagę, iż procedury wyświetlania liczb można bardzo prosto przystosować do innych systemów liczenia (np. piątkowego). Jednak przy wyświetlaniu liczb w systemie dwójkowym, ósemkowym i szesnastkowym korzysta się z innych metod, które uwzględniają sposób reprezentacji danych w pamięci maszyny, tj. bity.
Zapis dwójkowy zbudowany jest tylko z dwóch cyfr - 0 i 1. Teoretycznie moglibyśmy stosować odwrotny algorytm Hornera do obliczenia poszczególnych cyfr, tylko po co - przecież w komórce pamięci komputer przechowuje wartość w systemie dwójkowym. Wystarczy więc odczytać kolejne bity i wypisywać na wyświetlaczu cyfry 0 lub 1 w zależności od wartości tych bitów. Metody operacji bitowych omówiliśmy w poprzednim rozdziale. Poniżej przedstawiamy prosty program wyświetlający dowolną wartość w systemie dwójkowym. Wyświetlane jest zawsze 16 cyfr.
RUN START N: DAT 23 ;tutaj umieszczamy liczbę do wyświetlenia I: DAT 0 ;zlicza bity START: LDA N ;wydzielamy kolejne bity liczby RLA #1 ;obracając je w lewo STA N AND #1 ;gotowe ADD #48 ;tworzymy kod ASCII cyfry STA CHP ;teraz na wyświetlacz INC CHI ;następna pozycja INC I ;zwiększamy licznik bitów LDA I SUB #16 ;koniec? JMI #START ;następna cyfra, jeśli nie
Przy wyświetlaniu wartości ósemkowych skorzystamy z faktu, iż licząc od końca kolejne trzy bity wartości dwójkowej przedstawiają jedną cyfrę ósemkową w zakresie od 0 do 7. Jeśli przyjrzymy się komórce pamięci PMC, to stwierdzimy, iż pierwszą cyfrą ósemkową może być tylko cyfra 0 lub 1, a za nią następuje 5 cyfr już dowolnych:
Komórka pamięci PMC ósemkowo i dwójkowo | |||||||||||||||
c5 | c4 | c3 | c2 | c1 | c0 | ||||||||||
b15 | b14 | b13 | b12 | b11 | b10 | b09 | b08 | b07 | b06 | b05 | b05 | b03 | b02 | b01 | b00 |
Poszczególne cyfry uzyskamy obracając bity w lewo i wykonując operację AND z maską 1 dla bitu b15 oraz 7 dla pozostałych bitów.
RUN START N: DAT 864 ;tutaj podajemy wartość liczby I: DAT 0 ;licznik obiegów START: LDA N ;najpierw bit 15 RLA #1 ;bit b15 jest teraz na pozycji b0 STA N AND #1 ;wydzielamy go JMP #DIGIT LP1: LDA N ;wydzielamy po trzy bity RLA #3 STA N AND #0B111 ;maska wydzielania bitów DIGIT: ADD #48 ;tworzymy kod cyfry STA CHP ;przesyłamy go na wyświetlacz znakowy INC CHI ;następna pozycja INC I ;zwiększamy licznik obiegów LDA I ;koniec? SUB #6 ;należy wykonać 6 obiegów pętli JMI #LP1
Przy wyświetlaniu liczb szesnastkowych również wykorzystuje się fakt, iż licząc od końca każde kolejne 4 bity liczby dwójkowej przedstawiają jedną cyfrę szesnastkową. Problemem jest jedynie uzyskanie poprawnego kodu ASCII takiej cyfry. Nie wystarczy dodać do wartości cyfry kodu znaku "0" (48), ponieważ cyfry "A"..."F" będą źle przedstawione (literka A ma kod ASCII równy 65). My pójdziemy zupełnie inną drogą i po prostu umieścimy w pamięci tablicę ze wszystkimi cyframi szesnastkowymi. Otrzymana wartość cyfry będzie indeksem w tej tablicy, według którego pobierzemy odpowiedni znak.
RUN START N: DAT 14978 ;tutaj umieszczamy liczbę do wyświetlenia HEXTAB: DAT "0123456789ABCDEF" HPTR: DAT 0 ;do adresowania cyfr w tablicy I: DAT 0 ;licznik pętli START: LDA N ;wydzielamy czwórki bitów obracając wartość RLA #4 ;o cztery pozycję w lewo i wykonując STA N ;operację AND z maską 0B0000000000001111 AND #15 ;w ACR wartość cyfry szesnastkowej ADD #HEXTAB ;obliczamy adres znaku tej cyfry w tablicy STA HPTR LDA (HPTR++) ;pobieramy ten znak z tablicy STA CHP ;i wyświetlamy go na wyświetlaczu znakowym INC CHI INC I ;zwiększamy licznik obiegów pętli LDA I SUB #4 ;koniec? JMI #START ;następna cyfra, jeśli nie
Liczby odczytujemy jako kolejne znaki cyfr. Znak cyfry ma kod ASCII (np. cyfra 0 to kod 48). Najpierw więc musimy przekształcać odczytane kody cyfr na ich wartość - polega to na odjęciu od kodu ASCII wartości 48 (kod cyfry 0). Następnie stosujemy algorytm Hornera do obliczenia wartości liczby.
Brzmi to dosyć prosto. Jednak musisz rozważyć kilka ważnych spraw przy opracowaniu poprawnego programu odczytu liczby. Przede wszystkim program będzie współpracował z użytkownikiem, który jest istotą nieprzewidywalną i omylną. Dobrze skonstruowany algorytm powinien działać poprawnie w każdej sytuacji, nawet jeśli użytkownik wprowadzi bezsensowny stek bzdur (są tacy, nawet wśród was, którzy lubią w ten sposób męczyć komputery). Z drugiej strony użytkownik może wprowadzić liczbę za dużą lub za małą, to też musi sprawdzać program Widzisz więc, iż odczyt liczb komplikuje się znacznie, gdy chcemy się zabezpieczyć przed możliwymi błędami ze strony człowieka.
Poniższa wersja programu nie sprawdza poprawności wprowadzonych danych, dlatego nie jest odporna na błędy użytkownika. Najpierw program czeka, aż w buforze pojawią się dane, które musi wprowadzić użytkownik. Gdy dane są dostępne, to program odczytuje je znak po znaku. Zakładamy, iż są to kolejne cyfry liczby. Każdy znak cyfry jest sprowadzany do wartości cyfry przez zmniejszenie kodu o 48. Wartości te są dodawane do wyniku przemnożonego przez 10 (schemat Hornera). Pętla jest kończona, gdy w buforze wyczerpią się znaki. Program działa poprawnie dla liczb z zakresu od 0 do 65535. Ponieważ jednak PMC pracuje w kodzie U2, to liczby większe od 32767 będą przedstawiane jako ujemne (najstarszy bit ma wartość 1).
Program można rozbudować według następujących wskazówek:
Rozbudowę pozostawiamy ambitnym czytelnikom.
RUN START N: DAT 0 ;tutaj trafi odczytana liczba X: DAT 0 ;zmienna pomocnicza START: LDA INP ;odczytujemy znak z bufora JZR #START ;bufor zajęty? JMI #START ;bufor pusty? LP1: SUB #"0" ;obliczamy wartość cyfry STA X ;i zapamiętujemy ją tymczasowo LDA N ;N przemnażamy przez 10 MUL #10 ADD X ;dodajemy cyfrę STA N ;zapisujemy wynik LP2: LDA INP ;pobieramy kolejny znak JZR #LP2 ;czekamy na dostępność bufora JMI #0 ;ale kończymy, gdy pusty. Wynik w N JMP #LP1 ;inaczej przetwarzamy znak
Odczyt liczb ze znakiem nie różni się zasadniczo od odczytu liczb bez znaku. Jedyną nowością jest dodatkowy znak, który może pojawić się na początku zapisu, mianowicie minus. Program odczytujący powinien sprawdzić, czy pierwszy znak jest minusem. Jeśli tak, to zostaje ustawiony odpowiedni znacznik. Teraz następuje odczyt liczby bez znaku. Po odczytaniu liczby program sprawdza znacznik i jeśli jest on ustawiony, zmienia wartość liczby na przeciwną.
RUN START N: DAT 0 ;tutaj trafi odczytana liczba SGN: DAT 0 ;znacznik liczby ujemnej X: DAT 0 ;zmienna pomocnicza START: LDA INP ;odczytujemy znak z bufora JZR #START ;bufor zajęty? JMI #START ;bufor pusty? STA X ;zapamiętujemy chwilowo znak SUB #"-" ;sprawdzamy, czy odczytano minus JZR #MINUS LDA X ;jeśli nie, to odtwarzamy znak JMP #LP1 ;i przetwarzamy go MINUS: INC SGN ;ustawiamy znacznik JMP #LP2 ;i przechodzimy do odczytu cyfr LP1: SUB #"0" ;obliczamy wartość cyfry STA X ;i zapamiętujemy ją tymczasowo LDA N ;N przemnażamy przez 10 MUL #10 ADD X ;dodajemy cyfrę STA N ;zapisujemy wynik LP2: LDA INP ;pobieramy kolejny znak JZR #LP2 ;czekamy na dostępność bufora JMI #EX1 ;ale kończymy, gdy pusty JMP #LP1 ;inaczej przetwarzamy znak EX1: LDA SGN ;liczba ujemna? JZR #0 ;kończymy, jeśli nie. Wynik w N. LDA #0 ;inaczej zmieniamy znak N SUB N STA N
Program odczytujący liczbę dwójkową można skonstruować na bazie poprzednio przedstawionego programu dla liczb dziesiętnych. Jednak chciałbym pokazać wam nieco inne podejście do tego problemu. Ponieważ liczby są przechowywane w pamięci komputera w postaci bitów, to możemy bezpośrednio odczytywać cyfry dwójkowe, zamieniać je na bity o wartości 0 lub 1 i wstawiać do tworzonej liczby, która przed tą operacją jest przesuwana bitowo o jedną pozycję w lewo.
RUN START N: DAT 0 ;tutaj trafi odczytana liczba dwójkowa X: DAT 0 ;zmienna pomocnicza START: LDA INP ;odczytujemy znak z bufora JZR #START ;bufor zajęty? JMI #START ;bufor pusty? LP1: SUB #"0" ;obliczamy wartość cyfry 0 lub 1 STA X ;i zapamiętujemy ją tymczasowo LDA N ;N przesuwamy o 1 pozycję w lewo RLA #1 ORA X ;wstawiamy bit STA N ;zapisujemy wynik LP2: LDA INP ;pobieramy kolejny znak JZR #LP2 ;czekamy na dostępność bufora JMI #0 ;ale kończymy, gdy pusty. Wynik w N JMP #LP1 ;inaczej przetwarzamy znak
Dla liczb ósemkowych modyfikacja algorytmu jest minimalna. Wystarczy przesuwać bitowo zmienną N o trzy pozycję w lewo - cyfra ósemkowa zastępuje trzy bity liczby dwójkowej.
RUN START N: DAT 0 ;tutaj trafi odczytana liczba ósemkowa X: DAT 0 ;zmienna pomocnicza START: LDA INP ;odczytujemy znak z bufora JZR #START ;bufor zajęty? JMI #START ;bufor pusty? LP1: SUB #"0" ;obliczamy wartość cyfry od 0 do 7 STA X ;i zapamiętujemy ją tymczasowo LDA N ;N przesuwamy o 3 pozycję w lewo RLA #3 ORA X ;wstawiamy bity cyfry ósemkowej STA N ;zapisujemy wynik LP2: LDA INP ;pobieramy kolejny znak JZR #LP2 ;czekamy na dostępność bufora JMI #0 ;ale kończymy, gdy pusty. Wynik w N JMP #LP1 ;inaczej przetwarzamy znak
Sam algorytm obliczania wartości liczby szesnastkowej nie różni się niczym od opisanych powyżej algorytmów dla liczb dwójkowych i ósemkowych - jedna cyfra szesnastkowa zajmuje cztery bity, więc liczbę przesuwamy w lewo bitowo o cztery pozycje i łączymy bity cyfry szesnastkowej z liczbą. Problemem jednak będzie obliczenie wartości cyfry szesnastkowej. Z bufora wejściowego INP cyfry odczytujemy jako znaki w kodzie ASCII. Dla cyfr "0" ... "9" nie ma problemu. Wystarczy od kodu znaku odjąć wartość 48, aby otrzymać wartość tej cyfry. Jednak cyfry literowe "A" ... "F" mogą początkującemu programiście sprawić nieco kłopotu. Ich kody leżą dalej w zestawie ASCII od kodów cyfr "0" ... "9". Co gorsza, użytkownik może wprowadzić zamiast dużych liter małe znaki "a" ... "f". W poniższej tabelce zebraliśmy kody ASCII wszystkich cyferek szesnastkowych.
cyfry normalne | cyfry literowe | ||||||||||||||
0 48 |
1 49 |
2 50 |
3 51 |
4 52 |
5 53 |
6 54 |
7 55 |
8 56 |
9 57 |
A 65 |
B 66 |
C 67 |
D 68 |
E 69 |
F 70 |
a 97 |
b 98 |
c 99 |
d 100 |
e 101 |
f 102 |
Aby otrzymać poprawną wartość cyfry musimy wykonać następujące obliczenia:
cyfra ← cyfra - 48
jeśli cyfra > 9, to cyfra ← cyfra - 7
jeśli cyfra >
15, to cyfra ← cyfra - 32
Sprawdźmy: użytkownik podał cyfrę "d" o kodzie 100
cyfra = 100 - 48 = 52
52 > 9, więc cyfra = 52 - 7 = 45
45 > 15, więc
cyfra = 45 - 32 = 13, co jest wartością cyfry
D.
Po tych wyjaśnieniach możemy przejść do programu odczytującego liczby w kodzie szesnastkowym.
RUN START N: DAT 0 ;tutaj trafi odczytana liczba szesnastkowa X: DAT 0 ;zmienna pomocnicza START: LDA INP ;odczytujemy znak z bufora JZR #START ;bufor zajęty? JMI #START ;bufor pusty? LP1: SUB #48 ;cyfra <- cyfra - 48 STA X ;i zapamiętujemy ją tymczasowo SUB #10 ;cyfra < 9 ? JMI #CYFRA ADD #3 ;cyfra <- cyfra - 7 STA X SUB #16 ;cyfra < 15 ? JMI #CYFRA SUB #16 ;cyfra <- cyfra - 32 STA X CYFRA: LDA N ;N przesuwamy o 4 pozycję w lewo RLA #4 ORA X ;wstawiamy bity cyfry ósemkowej STA N ;zapisujemy wynik LP2: LDA INP ;pobieramy kolejny znak JZR #LP2 ;czekamy na dostępność bufora JMI #0 ;ale kończymy, gdy pusty. Wynik w N JMP #LP1 ;inaczej przetwarzamy znak
Odczytując port IOP dostajemy stan przycisków panelu IOP. Każdy przycisk ma skojarzony ze sobą jeden bit portu. Odczyt będzie więc polegał na sprawdzeniu stanu odpowiedniego bitu. Poniższy program zaprzestaje działania, po kliknięciu przycisku nr 3.
RUN START START: LDA IOP ;odczytujemy stan przycisków AND #8 ;sprawdzamy stan bitu nr 3 JZR #START ;jeśli nie jest naciśnięty, to kontynuujemy
A ten prosty program odczytuje stan przycisków IOP i mruga diodami LED nad wciśniętymi przyciskami:
RUN START MASKA: DAT 0 START: LDA IOP ;odczytujemy stan przycisków AND MASKA ;bity ustawiamy lub zerujemy STA IOP ;zaświecamy diody LDA MASKA ;negujemy bity maski XOR #-1 STA MASKA LDA #-5 ;małe opóźnienie LP1: ADD #1 JMI #LP1 JMP #START
W następnym programie odczytywany jest stan trzech przycisków. W zależności od ich stanu program obraca na wyświetlaczu znakowym literkę A w lewo, gdy naciśnięty jest przycisk 1; w prawo, gdy naciśnięty jest przycisk 0 oraz kończy działanie, gdy naciśnięty jest przycisk 2.
RUN START START: LDA #"A" ;umieszczamy znak A na wyświetlaczu STA CHP LP1: LDA IOP ;sprawdzamy stan przycisku 0 AND #1 JZR #N1 ;jeśli wyciśnięty, to dalej LDA #261 ;wciśnięty, obrót znaków w prawo STA CHP N1: LDA IOP ;sprawdzamy stan przycisku 1 AND #2 JZR #N2 ;jeśli wyciśnięty, to dalej LDA #260 ;wciśnięty, obrót znaków w lewo STA CHP N2: LDA IOP ;sprawdzamy stan przycisku 2 AND #4 JZR #LP1 ;jeśli wyciśnięty, to kontynuujemy pętlę
Kolejny program demonstruje sposób odczytu sekwencji przycisków. Program kończy wykonanie, gdy naciśnięte są jednocześnie trzy przyciski 0, 2 i 5.
RUN START START: LDA IOP ;odczytujemy stan wszystkich przycisków AND #0B100101 ;wydzielamy bity 5,2 i 0 XOR #0B100101 ;sprawdzamy, czy są ustawione na 1 JZR #EX1 ;jeśli tak, to kończymy JMP #START EX1:
Sterowanie wyświetlaczami 7-mio segmentowymi LED odbywa się podobnie do sterowania portem znakowym. Mamy tutaj port indeksowy DSI, do którego wprowadzamy numer aktywnego wyświetlacza. Port ten jest 4 bitowy. Pierwszy wyświetlacz znajduje się po prawej stronie i ma numer 0. Po wprowadzeniu numeru do DSI komputer uzyskuje dostęp do poszczególnych segmentów LED wybranego wyświetlacza poprzez port DSP. Odczyt tego portu zwraca informację, które z segmentów są zaświecone (skojarzone z nimi bity mają stan 1). Zapis do tego portu powoduje zaświecenie odpowiednich segmentów.
Świeceniem segmentów sterują bity od b0 do b6. Bit b7 steruje świeceniem kropki przy cyfrze. Port DSP jest 8-mio bitowy.
Taki sposób sterowania, chociaż nie pozwala bezpośrednio wyświetlać cyfr (wpisanie wartości cyfry do portu DSP nie spowoduje pojawienia się jej na wyświetlaczu), to jednak daje pełną kontrolę nad świeceniem poszczególnych segmentów. Dzięki temu można również tak wysterować segmenty wyświetlacza, aby pojawiły się na nich niektóre literki lub znaki.
Prezentowany poniżej program zaświeca wszystkie segmenty we wszystkich
wyświetlaczach.
RUN START START: LDA #0B11111111 ;ustawione wszystkie 8 bitów STA DSP ;zapalamy wszystkie segmenty INC DSI ;przechodzimy do następnego wyświetlacza LDA DSI ;koniec? JZR #0 ;kończymy, jeśli tak JMP #START ;następny wyświetlacz, jeśli nie
Poeksperymentuj w tym programie z wartością wprowadzaną na jego początku do rejestru akumulatora. Np. wpisz taką wartość, aby na wszystkich wyświetlaczach pojawiły się cyfry 0, 1, 2, itd.
Kolejny program demonstruje sposób wyświetlania cyfr. Dane dla wyświetlacza LED zgromadzone zostały w tablicy w ten sposób, iż element o indeksie np. 3 zawiera kształt cyfry 3 dla portu DSP. Program zamienia więc wartość cyfry na element z tablicy DSPTAB o indeksie równym tej cyfrze, następnie przesyła ten element do portu DSP. W rezultacie na wyświetlaczu otrzymujemy kształty kolejnych cyfr.
RUN START N: DAT 0 ;licznik DSPTAB: DAT 0B0111111 ;cyfra 0 DAT 0B0000011 ;cyfra 1 DAT 0B1101101 ;cyfra 2 DAT 0B1100111 ;cyfra 3 DAT 0B1010011 ;cyfra 4 DAT 0B1110110 ;cyfra 5 DAT 0B1111110 ;cyfra 6 DAT 0B0100011 ;cyfra 7 DAT 0B1111111 ;cyfra 8 DAT 0B1110111 ;cyfra 9 DTPTR: DAT 0 ;do adresowania tablicy START: LDA N ;N zamieniamy na kod dla portu wyświetlacza ADD #DSPTAB ;obliczamy adres elementu STA DTPTR LDA (DTPTR++) ;pobieramy kod dla wyświetlacza STA DSP ;zapalamy odpowiednie segmenty LED LDA #-25 ;pętla opóźniająca LP1: ADD #1 JMI #LP1 INC N ;zwiększamy N o 1 LDA N ;sprawdzamy, czy N > 9 SUB #10 JMI #START LDA #0 ;jeśli tak, to zerujemy licznik STA N JMP #START
A tutaj mamy przykład licznika liczącego w kodzie szesnastkowym. W tablicy zdefiniowano dodatkowe cyfry A...F.
RUN START N: DAT 0 ;licznik DSPTAB: DAT 0B0111111 ;cyfra 0 DAT 0B0000011 ;cyfra 1 DAT 0B1101101 ;cyfra 2 DAT 0B1100111 ;cyfra 3 DAT 0B1010011 ;cyfra 4 DAT 0B1110110 ;cyfra 5 DAT 0B1111110 ;cyfra 6 DAT 0B0100011 ;cyfra 7 DAT 0B1111111 ;cyfra 8 DAT 0B1110111 ;cyfra 9 DAT 0B1111011 ;cyfra A DAT 0B1011110 ;cyfra b DAT 0B0111100 ;cyfra C DAT 0B1001111 ;cyfra d DAT 0B1111100 ;cyfra E DAT 0B1111000 ;cyfra F DTPTR: DAT 0 ;do adresowania tablicy START: LDA N ;N zamieniamy na kod dla portu wyświetlacza AND #0B1111 ;pozostawiamy tylko cztery najmłodsze bity ADD #DSPTAB ;obliczamy adres elementu STA DTPTR LDA (DTPTR++) ;pobieramy kod dla wyświetlacza STA DSP ;zapalamy odpowiednie segmenty LED LDA #-25 ;pętla opóźniająca LP1: ADD #1 JMI #LP1 INC N ;zwiększamy N o 1 JMP #START
Kolejny program jest modyfikacją programu wyświetlania liczb dziesiętnych bez znaku. Tym razem liczba zostaje umieszczona na wyświetlaczach 7-mio segmentowych LED. Wyświetlacze są sterowane bezpośrednio bez umieszczania cyfr w pamięci. Program łatwo można dostosować do innych systemów pozycyjnych przez zmianę wartości stałej P.
RUN START P: EQU 10 ;określa podstawę systemu 2..16 N: DAT 31857 ;liczba do wyświetlenia X: DAT 0 ;zmienna pomocnicza DSPTAB: DAT 0B0111111 ;cyfra 0 DAT 0B0000011 ;cyfra 1 DAT 0B1101101 ;cyfra 2 DAT 0B1100111 ;cyfra 3 DAT 0B1010011 ;cyfra 4 DAT 0B1110110 ;cyfra 5 DAT 0B1111110 ;cyfra 6 DAT 0B0100011 ;cyfra 7 DAT 0B1111111 ;cyfra 8 DAT 0B1110111 ;cyfra 9 DAT 0B1111011 ;cyfra A DAT 0B1011110 ;cyfra b DAT 0B0111100 ;cyfra C DAT 0B1001111 ;cyfra d DAT 0B1111100 ;cyfra E DAT 0B1111000 ;cyfra F DTPTR: DAT 0 ;do adresowania tablicy START: LDA #0 ;ustawiamy wyświetlacz na ostatnią cyfrę STA DSI LP1: LDA N ;obliczamy iloraz N przez P DIV #P STA X ;zapamiętujemy go chwilowo MUL #P ;obliczamy resztę z dzielenia SUB N ;reszta ujemna XOR #-1 ;obliczamy liczbę przeciwną w kodzie U2 ADD #1 ADD #DSPTAB ;obliczamy adres kształtu cyfry STA DTPTR LDA (DTPTR++) ;pobieramy definicję cyfry STA DSP ;wyświetlamy ją INC DSI ;ustawiamy kolejną pozycję LDA X ;do N idzie wynik dzielenia przez 10 STA N JZR #0 ;jeśli zero, to koniec przetwarzania JMP #LP1 ;w przeciwnym razie następna cyfra
Oprócz wyświetlania liczb wyświetlacze 7-mio segmentowe LED mogą również służyć do prostych animacji. Poniższy program wyświetla na ostatnim wyświetlaczu biegającą w kółko kreseczkę.
RUN START MASK: DAT 1 START: LDA #-10 ;małe opóźnienie LP1: ADD #1 JMI #LP1 LDA MASK ;odczytujemy maskę STA DSP ;zapalamy segment RLA #1 ;maskę przesuwamy o 1 bit w lewo STA MASK AND #0B1000000 JZR #START ;jeśli bit 7 zero, to powtarzamy LDA #1 ;inaczej ustawiamy maskę od nowa STA MASK JMP #START
Ten program z kolei wyświetla kreseczkę przebiegającą przez kolejne wyświetlacze.
RUN START START: LDA #0B1000000 STA DSP ;zapalamy kreseczkę (segment g) LDA #-10 ;małe opóźnienie LP1: ADD #1 JMI #LP1 STA DSP ;gasimy kreseczkę INC DSI ;przenosimy się do następnej pozycji JMP #START
Sprawdźcie osobiście, co robi poniższy program:
RUN START START: LDA #0B1001110 XOR DSP STA DSP INC DSI JMP #START
PMC wyposażona jest w prosty ekranik graficzny, którym sterujemy za pomocą dwóch portów: GRP i GRI. W pierwszym porcie ustawiamy lub odczytujemy kolor punktu. W drugim porcie ustawiamy lub odczytujemy położenie punktu na ekranie. Współrzędne x i y mają zakres od 0 do 15.
Umieszczenie punktu na ekranie graficznym składać się będzie z następujących operacji:
Poniższy program realizuje wymienione operacje. Umieszcza on punkt o kolorze określonym przez zmienne R (składowa czerwona), G (składowa zielona) oraz B (składowa niebieska) na współrzędnych określonych przez zmienne X oraz Y.
RUN START R: DAT 15 ;czterobitowa składowa czerwona koloru G: DAT 0 ;czterobitowa składowa zielona koloru B: DAT 0 ;czterobitowa składowa niebieska koloru X: DAT 7 ;współrzędna x Y: DAT 7 ;współrzędna y START: LDA Y ;łączymy współrzędne x i y w jedno słowo RLA #4 ORA X STA GRI ;ustawiamy pozycję punktu LDA R ;łączymy składowe kolorów RLA #4 ORA G RLA #4 ORA B STA GRP ;ustawiamy kolor punktu
W niektórych algorytmach graficznych (np. wypełniania obszaru kolorem) istotne jest odczytanie koloru punktu leżącego na zadanych współrzędnych. Polega to na wykonaniu następujących operacji:
Poniższy program odczytuje składowe koloru punktu na zadanych współrzędnych i wpisuje je do zmiennych R (składowa czerwona), G (składowa zielona) i B (składowa niebieska). Przed odczytem ekranik graficzny jest wypełniany zadanym kolorem w celu przetestowania działania procedury.
RUN START KOL: DAT 0xFF0 ;kolor żółty do testowego wypełnienia R: DAT 0 ;tutaj trafi składowa czerwona G: DAT 0 ;tutaj trafi składowa zielona B: DAT 0 ;tutaj trafi składowa niebieska X: DAT 5 ;współrzędna X odczytywanego punktu Y: DAT 2 ;współrzędna Y odczytywanego punktu START: LDA KOL ;wypełniamy ekranik graficzny kolorem STA GRP INC GRI ;następny punkt LDA GRI JZR #D1 JMP #START D1: LDA Y ;łączymy współrzędne RLA #4 ORA X STA GRI ;udostępniamy punkt (x,y) LDA GRP ;pobieramy kolor STA R ;chwilowo zapamiętujemy go w R AND #0B1111 ;wydzielamy bity koloru niebieskiego STA B LDA R ;odtwarzamy kolor RLA #-4 ;przesuwamy bity koloru zielonego na pozycję AND #0B1111 ;wydzielamy je STA G LDA R ;i to samo z bitami koloru czerwonego RLA #-8 AND #0B1111 STA R
Efekty graficzne można uzyskiwać bardzo prostymi środkami. Poniższy program wypełnia ekranik graficzny punktami o coraz wyższych kolorach.
RUN START START: INC GRP ;zwiększamy kolor punktu na danych współrzędnych INC GRI ;przechodzimy do następnego punktu JMP #START
Jeszcze ciekawszy efekt da nam poniższy program:
RUN START START: LDA #0 ;rozpoczynamy od koloru czarnego LP1: STA GRP ;ustawiamy kolor punktu INC GRI ;przechodzimy do następnego punktu ADD #1 ;zwiększamy kolor w akumulatorze JMP #LP1 ;kontynuujemy pętlę
Następny program tworzy na ekranie graficznym efekt szachownicy. Kolory punktów jasnych i ciemnych są przechowywane w zmiennych KOL1 i KOL2.
RUN START KOL1: DAT 0xFF0 ;żółty kolor KOL2: DAT 0x00F ;niebieski kolor MASK: DAT 0 START: LDA MASK ;jaki punkt postawić? JZR #D1 ;0 - KOL2 LDA KOL1 ;1 - KOL1 JMP #D2 D1: LDA KOL2 D2: STA GRP ;ustawiamy kolor punktu LDA MASK ;zmieniamy bit 0 maski na przeciwny XOR #1 STA MASK INC GRI ;przesuwamy się do następnej pozycji LDA GRI ;sprawdzamy, czy koniec JZR #0 ;kończymy, jeśli tak AND #0xF ;sprawdzamy, czy wypełniliśmy 1 wiersz JZR #D3 JMP #START ;jeśli nie, to kontynuujemy D3: LDA MASK ;jeśli tak, to w następnym wierszu musimy XOR #1 ;odwrócić układ kolorów, aby powstawała STA MASK ;szachownica JMP #START
Kolejny z programów wnosi nieco animacji na ekranik graficzny. PMC nie pracuje zbyt szybko, więc efekty animacyjne nie będą zaawansowane. Program symuluje odbijającą się piłeczkę. Piłeczka jest punktem, który zmienia współrzędne. Gdy osiągnie jedną z krawędzi ekranu następuje zmiana kierunku ruchu w osi, w której wystąpiła ta krawędź.
RUN START KOL: DAT 0xF0F ;kolor piłeczki X: DAT 5 ;współrzędna x Y: DAT 2 ;współrzędna y OX: DAT 0 ;stara współrzędna x OY: DAT 0 ;stara współrzędna y DX: DAT 1 ;przyrost w osi x DY: DAT 1 ;przyrost w osi y START: LDA Y ;łączymy współrzędne i wpisujemy RLA #4 ;wynik do portu indeksowego grafiki ORA X STA GRI LDA KOL ;ustawiamy kolor punktu (piłeczka) STA GRP LDA X ;zapamiętujemy bieżące współrzędne STA OX ADD DX ;przesuwamy piłeczkę w osi x STA X JZR #MDX ;sprawdzamy, czy piłeczka osiągnęła ścianę XOR #15 ;w osi x JZR #MDX JMP #ON1 ;jeśli nie, to przechodzimy do współrzędnej y MDX: SUB DX ;jeśli tak, to zmieniamy kierunek ruchu STA DX ;w osi x (przeciwny przyrost) ON1: LDA Y ;ze współrzędną y wykonujemy identyczne STA OY ;operacje ADD DY STA Y JZR #MDY XOR #15 JZR #MDY JMP #ON2 MDY: SUB DY STA DY ON2: LDA OY ;łączymy stare współrzędne i zapisujemy RLA #4 ;do portu indeksowego grafiki ORA OX STA GRI LDA #0 ;następnie wymazujemy punkt (piłeczkę) STA GRP JMP #START ;i kontynuujemy pętlę
Zespół Przedmiotowy Chemii-Fizyki-Informatyki w I Liceum Ogólnokształcącym im. Kazimierza Brodzińskiego w Tarnowie ul. Piłsudskiego 4 ©2024 mgr Jerzy Wałaszek |
Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone
pod warunkiem podania źródła oraz niepobierania za to pieniędzy.
Pytania proszę przesyłać na adres email:
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.
Informacje dodatkowe.