Generator liczb losowych (2)
Obecny stan techniki mikroprocesorowej to efekt rozwoju wielu koncepcji, zarówno w sferze sprzętowej jak i oprogramowania narzędziowego. Ciągłe udoskonalanie doprowadziło do sytuacji, że budowanie nawet bardziej złożonych konstrukcji nie stanowi wyzwania tej samej klasy, co przed laty.
W poprzedniej części była okazja zagłębić się w aspekty budowy sprzętowej. Obecne mikrokontrolery integrują w jednej strukturze wiele składników, które w dawnych czasach stanowiły oddzielne, niezależne układy scalone. Drugim elementem niezbędnym w procesie budowy docelowego urządzenia jest oprogramowanie. Tu problem należy podzielić na dwa elementy: algorytm, zgodnie z którym działa program sterujący oraz język programowania, który pozwoli przekształcić ten algorytm w kod programu. W przypadku algorytmów, to właściwie od wielu lat niewiele się zmienia. Sposoby rozwiązywania poszczególnych detali są znane od dawna. Przykładem może być obsługa klawiatury. Koncepcja rozwiązania matrycowego nie jest nowym pomysłem. Zanim do budowy urządzeń wdarły się mikroprocesory, również były tworzone konstrukcje wykorzystujące klawiatury.
Opracowanie programu sterującego mikroprocesorem wymaga odpowiedniego oprogramowania narzędziowego. Obecnie do tego celu wręcz powszechnie używa się języka C. Mając algorytm działania zapisany w tym języku, używa się kompilatora, którego zadaniem jest przetłumaczenie programu źródłowego (przykładowo w języku C) na kod rozumiany przez użyty mikroprocesor. Każdy z nich ma określoną specyfikę w zakresie swoich możliwości (różne zestawy dostępnych rejestrów roboczych, różne listy instrukcji maszynowych, różne tryby adresowania pamięci).
Oprogramowanie narzędziowe
Wykorzystany mikroprocesor nie jest produkowany od wielu lat. Ten fakt oznacza, że nie istnieje oprogramowanie narzędziowe pozwalające na generowanie kodu wynikowego dla mikroprocesora. Oczywiście w czasach, gdy 8085 był wykorzystywany, takie oprogramowanie musiało istnieć. Wtedy powszechnie był wykorzystywany język assembler. Można poszukać w Internecie odpowiedniego kompilatora, który generowałby kod binarny dla tego mikroprocesora, jednak tu tworzy się kolejny problem do pokonania: system operacyjny. W dawnych czasach używany był system CP/M, więc… kolejna przeszkoda, którą można pokonać, ale trzeba się napracować.
Postanowiłem rozwiązać te problemy w inny sposób: sam utworzyłem oprogramowanie narzędziowe pracujące pod kontrolą współczesnych systemów operacyjnych. W jego skład wchodzą dwa składniki: kompilator języka podstawowego (assembler) procesora 8085 oraz jego emulator. Zadaniem kompilatora jest wygenerowanie kodu wynikowego dla mikroprocesora jako pliku w formacie Intel-hex (obecnie powszechnie stosowany również przez programatory, gdyż niezbędną operacją jest zaprogramowanie pamięci EPROM). Ze względu na fakt, że program w języku assembler jest „dosyć wrażliwy” na pomyłki, przydatnym jest program emulatora mikroprocesora. Jego zadaniem jest udawanie w środowisku Windows stosowanego mikroprocesora 8085. Widząc stan różnych rejestrów i wskaźników można będzie pokonać trudniejsze fragmenty oprogramowania.
Podstawowe zasoby mikroprocesora
Mikroprocesor 8085 (identycznie jak jego poprzednik 8080) do 8-bitowych operacji arytmetycznych oraz logicznych wykorzystuje rejestr akumulatora (w języku assemblera identyfikowany symbolem A). Stanowi on również pierwszy operand w tych operacjach a wynik działania również lokowany jest w akumulatorze. Drugim operandem może być 8-bitowa stała, inny 8-bitowy rejestr lub zawartość komórki pamięci RAM. Oprócz akumulatora jednostka centralna procesora 8085 ma 8-bitowe rejestry B, C, D, E, H oraz L (rysunek 1).
Są to rejestry ogólnego zastosowania. Mogą one być łączone w pary (B z C, D z E oraz H z L), stanowiąc rejestry 16-bitowe. Szczególne zastosowanie ma para rejestrów HL przy adresowaniu komórki pamięci RAM w operacjach arytmetycznych oraz logicznych (w języku assembler identyfikowana symbolem M – operand będący komórką w RAM adresowany przez parę rejestrów HL). Ograniczając się przykładowo do instrukcji dodawania, możliwe kombinacje przedstawia rysunek 2.
Oprócz operacji arytmetycznych występują instrukcje przesyłania danych między rejestrami oraz rejestrami i pamięcią RAM (pełna lista instrukcji dostępnych w 8085 znajduje się w materiałach dodatkowych.
Pomiar upływu czasu
Wiele operacji realizowanych przez generator jest uzależnionych od upływu czasu. W rozwiązaniu sprzętowym jest wykorzystany układ 8253 zawierający trzy niezależne zespoły zliczające. Pierwszy licznik zlicza impulsy o częstotliwości 2 MHz, taktujące pracę mikroprocesora. W wyniku uzyskania przez licznik określonego zaprogramowanego stanu, na jego wyjściu generowany jest impuls, który jest zliczany przez kolejny zestaw licznikowy, którego zadaniem jest wygenerowanie sygnału przerwania po odmierzeniu odpowiedniego interwału czasu. Sygnał tego przerwania aktywuje w programie funkcję pokazaną na listingu 1 :
RST75Service ;ISR RST75Service ;
;begin (* RST75Service *)
push psw ;
push b ;
push d ;
push h ;
lxi h , LifeLEDTimer ; LifeLEDTimer := LifeLEDTimer + 1 ;
mov a , m ;
inr a ;
mov m , a ;
cpi LifeLedDelay ; if LifeLEDTimer = LifeLEDTimer then
jnz RST75Ser_1 ; begin
mvi a , 0 ; LifeLEDTimer := 0 ;
mov m , a ;
lxi h, LedPortCopy ; LedPortCopy.LifeLED:=
; not LedPortCopy. LifeLED ;
mov a , m ;
xri LifeLED ; RNDPort.PortC := LedPortCopy ;
mov m , a ;
out RNDPort + Port8255.PortC ;
RST75Ser_1 ; end ;
lxi d , RandomSeed ; RandomSeed := RandomSeed + 1 ;
lhlx ;
lxi b , 1 ;
dad b ;
shlx ;
mvi a , 1 ; IRQFlag := 1 ;
sta IRQFlag ;
call KeybService ; KeybService ;
mvi a , Tim1IrqGenMode ;
out Timer8253+Port8253.Control
mvi a , lo(Timer1Prescaler) ;
out Timer8253+Port8253.TimerB;
mvi a , hi(Timer1Prescaler) ;
out Timer8253+Port8253.TimerB;
pop h ;
pop d ;
pop b ;
pop psw ;
ei ;
ret ;end (* RST75Service *) ;
Działanie funkcji ilustruje rysunek 3.
Specyfiką każdego przerwania jest to, że jest aktywowane w dowolnym czasie a jego obsługa sprowadza się do „wskoczenia” do odpowiedniej funkcji obsługi. Ze względu na asynchronizm akcji, by nie „zepsuć” normalnego działania programu, obsługa przerwania musi zachować na stosie stan wszystkich rejestrów. Przed zakończeniem obsługi przerwania stan rejestrów jest odtworzony ze stosu. Ta czynność występuje zawsze we wszystkich mikrokontrolerach. Pisząc program przykładowo w języku C, kompilator „ukrywa” te operacje (funkcja przeznaczona do obsługi przerwania ma specyficzny zapis w swoim nagłówku i w takich przypadkach kompilator do jej realizacji dodaje odpowiedni prolog oraz epilog związany z obsługą przerwania). Pisząc program w językach typu assembler, programista musi ich użyć jawnie. Poza tymi podstawowymi operacjami, zadaniem funkcji obsługi tego przerwania jest „mruganie” diodą LED sygnalizującą „rytm serca” generatora. Obok wywołania funkcji obsługi klawiatury, w obsłudze przerwania inkrementowana jest zmienna RandomSeed będąca losową wartością początkową generatora. Mikroprocesor w sposób ciągły modyfikuje tę zmienną, toteż uruchomienie generatora (poprzez naciśnięcie odpowiedniego przycisku klawiatury) za każdym razem rozpocznie generację ciągu liczb od innej wartości. Specyfiką układu 8253 (generującego przerwania od upływu czasu) jest konieczność ponownego zaprogramowania go do uzyskania kolejnego przerwania.
(…)
——– ciach! ——–
To jest tylko fragment artykułu, którego pełna wersja ukazała się w lipcowym numerze czasopisma Zrozumieć Elektronikę (ZE 7/2026). Pełną wersję czasopisma znajdziesz pod tym linkiem. Natomiast niepełna, okrojona wersja, pozwalająca zapoznać się z zawartością numeru ZE 7/2026 znajduje się tutaj.
Andrzej Pawluczuk
apawluczuk@vp.pl
Uwaga! Wskazówki, jak nabyć pełne wersje dowolnych numerów ZE znajdują się na stronie:
https://piotr-gorecki.pl/n11.


