strona główna

Spis treści

Opis maszyny RAM

Jednym z modeli obliczeniowych jest maszyna RAM - maszyna o swobodnym dostępie do pamięci; jej schemat funkcjonalny został przedstawiony poniżej.

schemat
Schemat dla przeglądarek tekstowych
Pamięć programu
Przechowuje instrukcje interpretowane i wykonywane przez maszynę. Nie można jej odczytywać ani zapisywać.
Licznik rozkazów
Wskazuje instrukcję, która ma zostać wykonana w następnym cyklu maszynowym.
Pamięć (zespół rejestrów)
Składa się z nieskończonej liczby rejestrów, mogących przechowywać dowolnie duże liczby całkowite.
Rejestr o numerze zero nazywa się sumatorem lub akumulatorem i jest domyślnym argumentem dla większości instrukcji.
Taśma wejściowa
Z taśmy wejściowej można wyłącznie odczytywać dane. Ma ona nieskończoną długość, dostęp do niej jest sekwencyjny: odczytywane są kolejne elementy, nie ma możliwości jej przewijania.
Taśma wyjściowa
Na taśmę wyjściową można wyłącznie zapisywać dane. Ma ona nieskończoną długość, dostęp do niej jest sekwencyjny: zapisywane są kolejne elementy, nie ma możliwości jej przewijania.

Ponieważ komputer, na którym działa emulator, ma ograniczenia fizyczne, więc i model maszyny RAM musiał się im poddać. Pojemność rejestrów jest limitowana przez implementację JavaScript, ich liczba została ustalona na 1024, co wydaje się rozsądną liczbą liczba rejestrów jest ograniczona tylko przez zasoby systemowe. Pojemność taśm jest ograniczona przez zasoby systemowe. Wersja konsolowa jest pozbawiona tych ograniczeń.

Zestaw instrukcji

Maszyna RAM interpretuje 12 instrukcji, dla większości z nich sumator jest domyślnym argumentem.

1 load =i r0 := i
load p r0 := rP
load *p r0 := r[rP]
2 store p rP := r0
store *p r[rP] := r0
3 add =i r0 := r0 + i
add p r0 := r0 + rP
add *p r0 := r0 + r[rP]
4 sub =i r0 := r0 - i
sub p r0 := r0 - rP
sub *p r0 := r0 - r[rP]
5 mult =i r0 := r0 * i
mult p r0 := r0 * rP
mult *p r0 := r0 * r[rP]
6 div =i r0 := floor(r0 / i)
div p r0 := floor(r0 / rP)
div *p r0 := floor(r0 / r[rP])
7 read p rP := wartość z taśmy wejściowej
read *p r[rP] := wartość z taśmy wejściowej
8 write =i zapisz na taśmę wyjściową i
write p zapisz na taśmę wyjściową rP
write *p zapisz na taśmę wyjściową r[rP]
9 jump etykieta skocz do etykiety
10 jzero etykieta skocz do etykiety gdy r0 = 0
11 jgtz etykieta skocz do etykiety gdy r0 > 0
12 halt   zatrzymaj program
Tabelka dla dla przeglądarek tekstowych

Składnia programu

Uwagi wstępne:

Pojedyncza linia programu składa się z następujących elementów:

numer_linii etykieta: instrukcja operand # komentarz
Numer linii (element opcjonalny, regexp: [0-9]+)
Liczba całkowita, dodatnia bez znaku. Pełni rolę informacyjną dla piszącego program, jej wartość jest ignorowana przez kompilator.
Linię, w której występuje wyłącznie numer linii, uznaje się za pustą.
Etykieta (element opcjonalny, regexp: [a-zA-Z_][a-zA-Z0-9_]*:)
Jest nazwą definiującą punkt w programie, do którego może zostać przekazane sterowanie przez instrukcję skoku (jump, jzero, jgtz). Nazwy etykiet nie mogą się powtarzać.
W poniższym przykładzie zostaną wykonane wyłącznie dwie instrukcje oznaczone znakiem X.
	jump etykieta2 # X

	# pomijany kod

etykieta1:

	# pomijany kod

etykieta2:
	halt           # X
Instrukcja (element wymagany!)
Jest jedną z nazw instrukcji z powyższej tabelki, autor wprowadził jednak pewien wyjątek odnośnie obowiązkowego występowanie tego elementu.
Teoretycznie poniższa konstrukcja jest błędna:
1 etykieta: # brak instrukcji
2	load 1
3	...
ale kompilator ją dopuszcza. Automatycznie wstawia instrukcję NOP (ang. No OPeration), czyli nie-rób-nic - precyzyjniej rzecz ujmując, instrukcja NOP zwiększa wskaźnik instrukcji, ale nie wpływa na stan pamięci ani taśm wejściowej i wyjściowej. Dzięki temu zabiegowi kilka etykiet może wskazywać na ten sam fragment kodu.

Emulator ma własną, wbudowaną instrukcję NOP, jednak w kodzie maszyny RAM występują konstrukcje o takich cechach; oto ich lista:
	load  0
	store 0
	div   =1
	mult  =1
	add   =0
	sub   =0
Operand (wymagany przez wszystkie rozkazy, za wyjątkiem halt)
W przypadku rozkazów skoku operand - adres docelowy - jest nazwą etykiety bez kończącego dwukropka (regexp: [a-zA-Z_][a-zA-Z0-9_]*).

Dla pozostałych rozkazów zdefiniowano trzy tryby adresowania:
Natychmiastowe (=i)
Wartość argumentu i (liczba całkowita ze znakiem; regexp [+-]?[0-9]+) jest zapisana bezpośrednio w kodzie rozkazu.
Bezpośrednie (p)
Wartość argumentu p (liczba całkowita, dodatnia, bez znaku; regexp [0-9]+) jest numerem rejestru, którego zawartość ma zostać pobrana/zapisana.
Pośrednie (*p)
Wartość argumentu p (liczba całkowita, dodatnia, bez znaku; regexp [0-9]+) jest numerem rejestru, którego zawartość jest traktowana jako numer rejestru, którego zawartość ma zostać pobrana/zapisana.
Poniżej przykłady adresowania:
# r0 = 0, r1 = 333, r2 = 222, r3 = 2

	load =777  # r0 = 777                 natychmiastowe
	load  1    # r0 = r1 = 333            bezpośrednie
	load *3    # r0 = r[r3] = r[2] = 222  pośrednie