EMAS: Różnice pomiędzy wersjami

Z MERA 400 wiki
Przejdź do nawigacji Przejdź do wyszukiwania
 
(Nie pokazano 45 pośrednich wersji utworzonych przez tego samego użytkownika)
Linia 1: Linia 1:
EMAS jest assemblerem skrośnym dla MERRY 400, używającym, w odróżnieniu od [[ASSEM]], składni bliższej współczesnym assemblerom, oraz mnemoników MERY 400 (ale bez przyrostków zależnych od lokalizacji argumentu, patrz: [[Lista rozkazów]]).
EMAS jest assemblerem skrośnym dla komputera MERA 400, używającym (w odróżnieniu od [[ASSEM]] i ASSM) składni bliższej współczesnym assemblerom.
 
Repozytorium źródeł EMAS-a dostępne jest pod adresem: https://github.com/jakubfi/emas


= Wywołanie =
= Wywołanie =
Linia 5: Linia 7:
Wywołanie EMAS z linii poleceń ma postać:
Wywołanie EMAS z linii poleceń ma postać:


emas [opcje] [wejście [wyjście]]
emas [opcje] [wejście]


Gdzie:
Gdzie:


* '''wejście''' - nazwa wejściowego pliku źródłowego. Jeśli nie zostanie podana, EMAS czyta program ze standardowego wejścia, a wyjściem staje się plik ''out.bin''
* '''wejście''' - nazwa wejściowego pliku źródłowego. Jeśli nie zostanie podana, EMAS czyta program ze standardowego wejścia
* '''wyjście''' - nazwa wyjściowego pliku binarnego. Jeśli nie zostanie podana, przyjmowana jest nazwa ''out.bin''
* '''opcje''' - dowolne z:
* '''opcje''' - dowolne z:
** '''-c <cpu>''' - wybierz procesor, dla jakiego assembler produkuje binaria. Możliwe wybory to: ''mera400'', ''mx16''. Ustawienie procesora na MX-16 pozwala na użycie dodatkowych instrukcji oraz poszerza przestrzeń adresową programu z 32 do 64k słów. Domyślnie przyjmowany procesor to ''mera400''.
** '''-O <otype>''' - typ wyniku asemblacji. Możliwe wybory to:
** '''-O <otype>''' - wybierz typ pliku wyjściowego. Możliwe wybory to:
*** ''raw'' - binarny obraz gotowy do umieszczenia w pamięci maszyny i uruchomienia (domyślny wybór)
*** ''raw'' - binarny obraz gotowy do umieszczenia w pamięci i uruchomienia (domyślny wybór)
*** ''emelf'' - konsolidowalny obiekt [[EMELF]]
*** ''debug'' - plik tekstowy opisujący zawartość kolejnych komórek pamięci (przydatny w przypadku programowania komputera z pulpitu technicznego)
*** ''debug'' - tekstowy opis zawartości kolejnych komórek pamięci
*** ''emelf'' - obiekt dla konsolidatora (opcja nie zaimplementowana)
*** ''keys'' - tekstowy opis zawartości kolejnych komórek pamięci w formacie łatwym do wprowadzenia z pulpitu technicznego maszyny
** '''-o <wyjście>''' - nazwa pliku wyjściowego. Jeśli nie zostanie podana, domyślny wygór zależy od typu wyniku asemblacji:
*** ''raw'' - nazwa pliku wejściowego z pominiętym rozszerzeniem. Jeśli wejście nie jest plikiem, to wyjściem jest plik 'a.out'
*** ''emelf'' - nazwa pliku wejściowego z rozszerzeniem zamienionym na '.o'. Jeśli wejście nie jest plikiem, to wyjściem jest plik 'a.out'
*** ''debug'' i ''keys'' - standardowe wyjście
** '''-c <cpu>''' - wybierz procesor, dla jakiego assembler produkuje binaria. Możliwe wybory to: ''mera400'', ''mx16''. Ustawienie procesora na MX-16 pozwala na użycie dodatkowych instrukcji oraz poszerza przestrzeń adresową programu z 32 do 64k słów. Domyślnie przyjmowany procesor to ''mera400''. Opcja nadpisuje wybór dokonany w źródłe dyrektywą '''.cpu'''.
** '''-I <katalog>''' - dodaj katalog do listy katalogów, w których poszukiwane będą pliki dołączane dyrektywą .include
** '''-I <katalog>''' - dodaj katalog do listy katalogów, w których poszukiwane będą pliki dołączane dyrektywą .include
** '''-D <stała>[=wartość]''' - zdefiniuj stałą i opcjonalnie nadaj jej wartość
** '''-v''' - wyświetl informacje o wersji i zakończ działanie
** '''-v''' - wyświetl informacje o wersji i zakończ działanie
** '''-h''' - wyświetl pomoc i zakończ działanie
** '''-h''' - wyświetl pomoc i zakończ działanie
** '''-d''' - drukuj informacje przydatne w śledzeniu błędów działania asemblera (dla deweloperów)
Oprócz katalogów podanych jako argumenty opcji '-I' EMAS używa następujących domyślnych katalogów, w których poszukuje plików dołączanych dyrektywą '''.include'''" (w kolejności):
* bieżący katalog
* /usr/share/emas/include
* /usr/local/share/emas/include


= Opis języka =
= Opis języka =
Linia 41: Linia 54:
=== Liczby całkowite ===
=== Liczby całkowite ===


Liczby całkowite można zapisywać w systemie binarnym, ósemkowym, dziesiętnym lub szesnastkowym, np:
Liczby całkowite można zapisywać w systemie binarnym, ósemkowym, dziesiętnym lub szesnastkowym. Dozwolone jest też rozdzielanie cyfr znakiem podkreślenia.


  0b1101110000
  0b1101110000
0b_1111_0000_1100_0011
  0177
  0177
  -931
  -931
  0xffa9
  0xffa9


Istnieje jeszcze jeden sposób zapisu stałych całkowitoliczbowych, wynikający z ułożenia [[Rejestry i wskaźniki|wskaźników w rejestrze R0]], gdzie poszczególnym bitom przypisany jest jednoliterowy symbol ze zbioru (Z, M, V, C, L, E, G, Y, X). Można dzięki niemu zapisać liczbę szesnastobitową z ustawionymi na 1 bitami, które odpowiadają danym wskaźnikom. Przykłady takiego zapisu stałych:
Istnieje jeszcze jeden sposób zapisu stałych całkowitoliczbowych, wynikający z ułożenia [[Rejestry i wskaźniki|wskaźników w rejestrze R0]], gdzie poszczególnym bitom przypisany jest jednoznakowy symbol ze zbioru (Z, M, V, C, L, E, G, Y, X, 1, 2, 3, 4, 5, 6, 7). Można dzięki niemu zapisać liczbę szesnastobitową z ustawionymi na 1 bitami, które odpowiadają danym wskaźnikom. Przykłady takiego zapisu stałych:


  ?ZM
  ?ZM125
  ?GVZ
  ?GVZ


EMAS przechowuje liczby całkowite jako 32-bit liczby w kodzie U2 i na takich liczbach operuje. W trakcie umieszczania liczb w obiekcie wynikowym są one obcinane do 16 najmłodszych bitów. Wyjątkiem jest argument dyrektywy '''.dword''', który jest 32-bit liczbą w kodzie U2.
Wewnętrznie EMAS operuje na 32-bit liczbach ze znakiem. Kiedy ostateczna wartość będąca wynikiem operacji ma zostać umieszczona w obiekcie wynikowym, jej zakres jest sprawdzany i użytkownik jest informowany o ewentualnym jego przekroczeniu. Wyjątkiem jest argument dyrektywy '''.dword''', który jest 32-bit liczbą w kodzie U2.
 
=== Znaki ===
 
EMAS pozwala na zapisywanie stałych całkowitoliczbowych również przy pomocy znaków, dwuznaków lub trójznaków. Znaki pojedyncze oraz dwuznaki obejmują cały zakres znaków ASCII i można je podawać również używając sekwencji unikowych. Trójznaki pozwalają kodować jedynie znaki poprawne dla [[CROOK-5 - Interpretator zleceń użytkownika - XOSL#Kod R40|kodu R40]].
 
Znaki w pojedynczych cudzysłowach:
 
'a', '\n', '\xa1', '\0177'
 
kodowane są jako ich wartość w kodzie ASCII na młodszym bajcie słowa. Dwuznaki:
 
'ab', '\r\n', '\xaa\0001'
 
kodowane są jako wartości podanych znaków w kodzie ASCII odpowiednio na starszym i młodszym bajcie słowa.


=== Liczby zmiennoprzecinkowe ===
=== Liczby zmiennoprzecinkowe ===
Linia 69: Linia 97:
=== Łańcuchy ===
=== Łańcuchy ===


Łańcuchy znaków są ciągami 8-bit znaków ujętymi w podwójne cudzysłowy. Poszczególne znaki można zapisywać dosłownie, lub używając jednej z sekwencji unikowych:
Łańcuchy znaków są ciągami znaków ujętymi w podwójne cudzysłowy. Poszczególne znaki można zapisywać dosłownie, lub (w przypadku dyrektyw '''.ascii''' i '''.asciiz''') używając jednej z sekwencji unikowych:
 
* \0'''yyy''' - gdzie '''yyy''' jest ósemkowym kodem znaku
* \0'''yyy''' - gdzie '''yyy''' jest ósemkowym kodem znaku
* \x'''yy''' - gdzie '''yy''' jest szesnastkowym kodem znaku
* \x'''yy''' - gdzie '''yy''' jest szesnastkowym kodem znaku
Linia 86: Linia 115:
  "\"napis\""
  "\"napis\""


Umieszczając łańcuchy w programie wynikowym EMAS lokuje każde dwa kolejne sąsiadujące ze sobą znaki kolejno w starszej i młodszej połówce 16-bit słowa. Jeśli łańcuch zawiera nieparzystą liczbę znaków, to w ostatnim słowie na pozycji młodszego bajtu znajdzie się wartość 0.
To, w jaki sposób znaki umieszczane są w programie wynikowym zależy od dyrektywy, której łańcuch jest argumentem.
 
=== Bieżąca lokalizacja ===
 
Specjalną stałą jest "." (kropka), która w danym miejscu programu zawsze zwraca przesunięcie względem początku assemblowanego obiektu. Jej wartość jest zawsze >= 0.


== Symbole ==
== Symbole ==
Linia 121: Linia 154:
=== Zasięg symboli ===
=== Zasięg symboli ===


Wszystkie symbole definiowane są jako lokalne. Jeśli symbol ma być widoczny dla konsolidatora, należy go zadeklarować jako globalny dyrektywą '''.global'''. Zmiana zasięgu może nastąpić przed, lub po definicji:
EMAS pozwala na zawężenie zasięgu symbolu przez poprzedzenie go kropką. Tak zdefiniowany symbol dostępny jest w zakresie od poprzedniego do następnego symbolu globalnego. Dzięki temu możliwe jest używanie takich samych nazw etykiet lokalnych w różnych kontekstach. Na przykład:
 
globalny1:
...
.lokalny:
...
ujs lokalny
globalny2:
.lokalny:
...
ujs lokalny
 
Skok 'ujs' wykonany zostanie każdorazowo do adresu opisanego symbolem ".lokalny", różnego dla zasięgów "globalny1" i "globalny2". Symbol lokalny nie może być zdefiniowany, jeśli uprzednio nie wystąpił żaden symbol globalny.
 
Można również odwołać się do lokalnego symbolu spoza jego zasięgu poprzedzając jego nazwę nazwą symbolu globalnego, w kontekście którego został zdefiniowany:
 
globalny1.lokalny
globalny2.lokalny
 
Lokalnych symboli można używać również definiując stałe i zmienne:
 
zmienna:
.equ a 1
.equ b 2
 
i odwoływać się do nich:
 
lw r1, zmienna.a
lw r2, zmienna.b
 
=== Widoczność symboli dla konsolidatora ===
 
Wszystkie symbole definiowane są jako widoczne wyłącznie w ramach asemblowanego obiektu. Jeśli symbol ma być widoczny dla konsolidatora, należy go zadeklarować jako zewnętrzny dyrektywą '''.global'''. Zmiana zasięgu może nastąpić przed, lub po definicji:


  .global printf
  .global printf
  .global snprintf
  .global snprintf
  .global strdup
  .global strdup
 
  printf:
  printf:
  ...
  ...
Linia 132: Linia 198:
  ...
  ...
  strdup:
  strdup:
 
  procedura:
  procedura:
  .global procedura
  .global procedura


== Wyrażenia ==
== Wyrażenia ==
=== Operatory ===
Poniższa tabela przedstawia operatory arytmetyczne i bitowe, w kolejności malejących priorytetów
{| class="wikitable"
! Operatory !! Działanie
|-
| ~ || negacja bitowa
|-
| \ || skalowanie
|-
| *, /, % || mnożenie, dzielenie, reszta z dzielenia
|-
| +, - || dodawanie, odejmowanie
|-
| <<, >> || przesunięcie bitowe w lewo i w prawo
|-
| &, <nowiki>|</nowiki>, ^ || logiczny iloczyn, suma i suma wykluczająca
|}
Operator skalowania realizuje funkcję dokładnie taką samą, jak przesunięcie bitowe, jest tylko innym sposobem jego zapisu. Poniższe zapisy są równoznaczne:
3\7  =  3 << (15-7)
Skalowanie można rozumieć jako: "umieść liczbę 3 od 7 pozycji bitowej w górę. Bity dla tego zapisu numerowane są od 0 (bit najstarszy) do 15 (bit najmłodszy). Wynikiem będzie więc liczba:
0000001100000000


== Dyrektywy assemblera ==
== Dyrektywy assemblera ==


.cpu
=== .cpu ===
.file
 
.line
'''Składnia:''' .cpu <mera400|mx16>
.include
 
.equ
Odpowiednik przełącznika ''-c'' linii poleceń EMAS-a. Pozwala określić typ procesora, dla którego ma być generowany kod wynikowy. Użycie ''mx16'' sprawia, że:
.const
* mnemoniki '''CRON''', '''SINT''', '''SIND''' są rozpoznawane i poprawnie assemblowane
.lbyte
* maksymalny rozmiar wynikowego pliku binarnego wynosi 64k słowa (32k dla ''mera400'')
.rbyte
 
.word
=== .file ===
.dword
 
.float
'''Składnia:''' .file ''nazwa_pliku''
.ascii
 
.asciiz
Ustala nazwę assemblowanego pliku. Jeśli assembler będzie chciał użyć w komunikatach nazwy pliku, to domyślna nazwa (zgodna z nazwą assemblowanego pliku) zostanie od momentu wydania dyrektywy zastąpiona podaną.
.res
 
.org
=== .line ===
.entry
 
.global
'''Składnia:''' .line ''numer_linii''
 
Ustala numer bieżącej linii assemblacji (poczynając od linii następującej po dyrektywie). Dalsze linie będą numerowane kolejno od podanej wartości.
 
=== .include ===
 
'''Składnia:''' .include ''nazwa_pliku''
 
Powoduje dołączenie do assemblacji pliku o podanej nazwie.
 
=== .equ ===
 
'''Składnia:''' .equ ''nazwa_zmiennej'' ''wartość''
 
Ustala wartość zmiennej assemblacji. Może być ona w dalszej części programu zmieniana.
 
=== .const ===
 
'''Składnia:''' .const ''nazwa_zmiennej'' ''wartość''
 
Ustala wartość stałej assemblacji. Wartość ta nie może ulec zmianie.
 
Zapisuje w programie wynikowym na prawych pozycjach (LSB) kolejnych słów podane wartośći 8-bit.
 
=== .word ===
 
'''Składnia:''' .word ''liczba_16_bit'' [, ...]
 
Zapisuje w programie kolejne słowa 16-bit o podanej wartości.
 
=== .dword ===
 
'''Składnia:''' .dword ''liczba_32_bit'' [, ...]
 
Zapisuje w programie wynikowym pary słów 16-bit tworzące podaną liczbę 32-bit.
 
=== .float ===
 
'''Składnia:''' .float ''liczba_zmiennoprzecinkowa'' [, ...]
 
Zapisuje w programie wynikowym trójki słów 16-bit tworzące podaną 48-bit liczbę zmiennoprzecinkową w notacji używanej przez arytmometr wielokrotnej precyzji MERY-400.
 
=== .ascii ===
 
'''Składnia:''' .ascii ''łańcuch''
 
Zapisuje w programie wynikowym podany łańcuch, kodując go po dwa znaki ASCII na słowo maszynowe.
 
=== .asciiz ===
 
'''Składnia:''' .asciiz ''łańcuch''
 
Zapisuje w programie wynikowym podany łańcuch, kodując go po dwa znaki ASCII na słowo maszynowe. Łańcuch terminowany jest 8-bit wartością '0'.
 
=== .res ===
 
'''Składnia:''' .res ''rozmiar'' [,''liczba_16_bit'']
 
Rezerwuje w programie wynikowym obszar o podanym rozmiarze (w słowach), wypełniając go zerami. Jeśli podany jest drugi argument, jego wartość używana jest do wypełnienia zamiast zera.
 
=== .org ===
 
'''Składnia:''' .org ''adres''
 
Ustala nową bieżącą pozycję assemblacji. Następne wygenerowane słowo zostanie umieszczone już pod podanym, nowym adresem.
 
=== .entry ===
 
'''Składnia:''' .entry ''adres''
 
Dyrektywa ma efekt wyłącznie w przypadku generowania pliku wynikowego jako obiektu EMELF. Określa adres wejścia do programu.
 
=== .global ===
 
'''Składnia:''' .global ''nazwa_etykiety''
 
Dyrektywa ma efekt wyłącznie w przypadku generowania pliku wynikowego jako obiektu EMELF. Wskazuje, że symbol będzie widoczny dla linkera.
 
=== .ifdef, .ifndef ===
 
Dyrektywa pozwala na asemblację warunkową podejmowaną istnieniem (bądź nie) zdefiniowanego symbolu.
 
.ifdef SYMBOL
... kod asemblowany, jeśl SYMBOL jest zdefiniowany ...
.else
... kod asemblowany, jeśli SYMBOL nie jest zdefiniowany ...
.endif
 
.ifndef SYMBOL
... kod asemblowany, jeśli SYMBOL nie jest zdefiniwoany
.endif
 
Symbol określający warunek może być zarówno etykietą, stałą lub zmienną zdefiniowaną w pliku źródłowym, jak również stałą zdefiniowaną podczas wywołania emas-a z opcją '-D'
 
=== .struct ===
 
Dyrektywa ta pozwala na stworzenie opisu struktury danych w celu uczynienia kodu źródłowego bardziej czytelnym. Nie formalizuje ona struktury, a jedynie ułatwia pracę z danymi. Przykład opisu struktury 'struktura':
 
.struct struktura:
    adres .res 1
    dluga_liczba .res 2
    tekst .res 64
.endstruct
 
Kolejne pozycje nazywają kolejne pola struktury i określają ich długość w słowach. Po asemblacji symbolom o tych nazwach przypisany zostanie offset w strukturze, a nazwie struktury - jej długość.
 
Użycie struktury w programie może wyglądać następująco:
 
moja_str: .res struktura
lw r3, moja_str
ld r3 + dluga_liczba
lw r7, moja_str + tekst
 
Definicja struktury z powyższego przykładu równoważna jest de facto następującemu ciągowi dyrektyw:
 
.const adres 0        ; offset pola 'adres'
.const dluga_liczba 1  ; offset pola 'dluga_liczba'
.const tekst 3        ; offset pola 'tekst'
.const struktura 67    ; rozmiar struktury 'struktura'
 
W strukturach można również zawęzić zakres nazewnictwa pól:
 
.struct struktura:
    .adres .res 1
    .dluga_liczba .res 2
    .tekst .res 64
.endstruct
 
Wtedy nazwy pól będą dostępne wyłącznie w kontekście danej struktury, a jej użycie wyglądać będzie następująco:
 
moja_str: .res struktura
lw r3, moja_str
ld r3 + struktura.dluga_liczba
lw r7, moja_str + struktura.tekst


== Rozkazy ==
== Rozkazy ==
EMAS używa mnemoników z [[Lista rozkazów|oryginalnej listy rozkazów]] MERY-400, ale wprowadza zmiany składniowe wynikające z innego zapisu argumentu normalnego. Nie używa przyrostków zależnych od lokalizacji argumentu. Czyli np. rozkaz ```LWn`` z listy rozkazów, gdzie ```n``` zmienia się w zależności od typu argumentu normalnego, ma zawsze mnemonik ```LW```. Lokalizację argumentu określa składnia dla rejestrów rB i rC. Poniższe przykłady ładowania wartości do rejestru r1 przedstawiają wszystkie możliwe postacie argumentu normalnego:
* LW r1, r2 - ładowana jest wartość z rejestru r2
* LW r1, r2+r3 - ładowana jest wartość z rejestru r2 B-modyfikowana (sumowana z) rejestrem r3
* LW r1, [r2] - ładowana jest wartość z komórki pamięci (D-modyfikacja) wskazanej zawartością rejestru r2
* LW r1, [r2+r3] - ładowana jest wartość z komórki pamięci (D-modyfikacja) wskazanej zawartością rejestru r2 B-modyfikowanego (sumowanego z) rejestrem r3
* LW r1, 0x1000 - ładowana jest wartość 0x1000
* LW r1, 0x1000+r2 - ładowana jest wartość 0x1000 B-modyfikowana zawartością (sumowana z) rejestru r2
* LW r1, [0x1000] - ładowana jest wartość z komórki pamięci (D-modyfikacja) o adresie 0x1000
* LW r1, [x1000+r2] - ładowana jest wartość z komórki pamięci (D-modyfikacja) o adresie 0x1000 B-modyfikowanym zawartością rejestru r2
= Pliki nagłówkowe =
Z assemblerem EMAS dostarczany jest [https://github.com/jakubfi/emas/tree/master/asminc zestaw plików nagłówkowych] definiujących stałe przydatne przy programowaniu w obszarze systemu operacyjnego i dla urządzeń wejścia-wyjścia:
* cpu.inc - stałe wynikające z architektury systemu ([[Mapa pamięci|zdefiniowane lokalizacje pamięci]], [[Przerwania|przerwania, maski przerwań]]),
* io.inc - podstawowe stałe przydatne przy konfigurowaniu pamięci, stałe dla systemu wejścia-wyjścia, komendy dla kanału znakowego i wspólne komendy dla urządzeń w nim pracujących,
* mega.inc - komendy dla [[Pamięć MEGA|pamięci mega]] Amepolu,
* multix.inc - stałe i komendy przydatne przy programowaniu procesora peryferyjnego [[MULTIX]].
Znaczenie poszczególnych stałych opisane jest w plikach nagłówkowych, ich rolę opisuje [[DTR|Dokumentacha Techniczno-Ruchowa]] minikomputera.

Aktualna wersja na dzień 15:25, 1 gru 2021

EMAS jest assemblerem skrośnym dla komputera MERA 400, używającym (w odróżnieniu od ASSEM i ASSM) składni bliższej współczesnym assemblerom.

Repozytorium źródeł EMAS-a dostępne jest pod adresem: https://github.com/jakubfi/emas

Wywołanie

Wywołanie EMAS z linii poleceń ma postać:

emas [opcje] [wejście]

Gdzie:

  • wejście - nazwa wejściowego pliku źródłowego. Jeśli nie zostanie podana, EMAS czyta program ze standardowego wejścia
  • opcje - dowolne z:
    • -O <otype> - typ wyniku asemblacji. Możliwe wybory to:
      • raw - binarny obraz gotowy do umieszczenia w pamięci maszyny i uruchomienia (domyślny wybór)
      • emelf - konsolidowalny obiekt EMELF
      • debug - tekstowy opis zawartości kolejnych komórek pamięci
      • keys - tekstowy opis zawartości kolejnych komórek pamięci w formacie łatwym do wprowadzenia z pulpitu technicznego maszyny
    • -o <wyjście> - nazwa pliku wyjściowego. Jeśli nie zostanie podana, domyślny wygór zależy od typu wyniku asemblacji:
      • raw - nazwa pliku wejściowego z pominiętym rozszerzeniem. Jeśli wejście nie jest plikiem, to wyjściem jest plik 'a.out'
      • emelf - nazwa pliku wejściowego z rozszerzeniem zamienionym na '.o'. Jeśli wejście nie jest plikiem, to wyjściem jest plik 'a.out'
      • debug i keys - standardowe wyjście
    • -c <cpu> - wybierz procesor, dla jakiego assembler produkuje binaria. Możliwe wybory to: mera400, mx16. Ustawienie procesora na MX-16 pozwala na użycie dodatkowych instrukcji oraz poszerza przestrzeń adresową programu z 32 do 64k słów. Domyślnie przyjmowany procesor to mera400. Opcja nadpisuje wybór dokonany w źródłe dyrektywą .cpu.
    • -I <katalog> - dodaj katalog do listy katalogów, w których poszukiwane będą pliki dołączane dyrektywą .include
    • -D <stała>[=wartość] - zdefiniuj stałą i opcjonalnie nadaj jej wartość
    • -v - wyświetl informacje o wersji i zakończ działanie
    • -h - wyświetl pomoc i zakończ działanie
    • -d - drukuj informacje przydatne w śledzeniu błędów działania asemblera (dla deweloperów)

Oprócz katalogów podanych jako argumenty opcji '-I' EMAS używa następujących domyślnych katalogów, w których poszukuje plików dołączanych dyrektywą .include" (w kolejności):

  • bieżący katalog
  • /usr/share/emas/include
  • /usr/local/share/emas/include

Opis języka

Komentarze

EMAS pozwala na użycie komentarzy jedo- i wieloliniowych:

; samotny komentarz jednoliniowy
lw r1, 2 ; komentarz jednoliniowy na końcu dowolnej linii
/* komentarz
   wieloniniowy
*/
lw r1, 2 /* komentarz wieloliniowy, choć tak naprawdę w jedej linii */
/* można też tak */ lw r1, 2

Stałe

Stałymi w programie mogą być liczby całkowite, liczby zmiennoprzecinkowe oraz łańcuchy znaków.

Liczby całkowite

Liczby całkowite można zapisywać w systemie binarnym, ósemkowym, dziesiętnym lub szesnastkowym. Dozwolone jest też rozdzielanie cyfr znakiem podkreślenia.

0b1101110000
0b_1111_0000_1100_0011
0177
-931
0xffa9

Istnieje jeszcze jeden sposób zapisu stałych całkowitoliczbowych, wynikający z ułożenia wskaźników w rejestrze R0, gdzie poszczególnym bitom przypisany jest jednoznakowy symbol ze zbioru (Z, M, V, C, L, E, G, Y, X, 1, 2, 3, 4, 5, 6, 7). Można dzięki niemu zapisać liczbę szesnastobitową z ustawionymi na 1 bitami, które odpowiadają danym wskaźnikom. Przykłady takiego zapisu stałych:

?ZM125
?GVZ

Wewnętrznie EMAS operuje na 32-bit liczbach ze znakiem. Kiedy ostateczna wartość będąca wynikiem operacji ma zostać umieszczona w obiekcie wynikowym, jej zakres jest sprawdzany i użytkownik jest informowany o ewentualnym jego przekroczeniu. Wyjątkiem jest argument dyrektywy .dword, który jest 32-bit liczbą w kodzie U2.

Znaki

EMAS pozwala na zapisywanie stałych całkowitoliczbowych również przy pomocy znaków, dwuznaków lub trójznaków. Znaki pojedyncze oraz dwuznaki obejmują cały zakres znaków ASCII i można je podawać również używając sekwencji unikowych. Trójznaki pozwalają kodować jedynie znaki poprawne dla kodu R40.

Znaki w pojedynczych cudzysłowach:

'a', '\n', '\xa1', '\0177'

kodowane są jako ich wartość w kodzie ASCII na młodszym bajcie słowa. Dwuznaki:

'ab', '\r\n', '\xaa\0001'

kodowane są jako wartości podanych znaków w kodzie ASCII odpowiednio na starszym i młodszym bajcie słowa.

Liczby zmiennoprzecinkowe

Akceptowane są następujące sposoby zapisu liczb zmiennoprzecinkowych:

9
1.2
0.003
-9.125e-2
1e4

Jedynym miejscem w programie, w którym może wystąpić liczba zmiennoprzecinkowa, jest argument dyrektywy .float.

Łańcuchy

Łańcuchy znaków są ciągami znaków ujętymi w podwójne cudzysłowy. Poszczególne znaki można zapisywać dosłownie, lub (w przypadku dyrektyw .ascii i .asciiz) używając jednej z sekwencji unikowych:

  • \0yyy - gdzie yyy jest ósemkowym kodem znaku
  • \xyy - gdzie yy jest szesnastkowym kodem znaku
  • \y - umożliwia zakodowanie jednego znaków specjalnych (wartości y inne niż poniżej są niepoprwane):
    • \n - nowa linia
    • \t - tabulacja pozioma
    • \r - powrót karetki
    • \f - wysuw strony
    • \\ - dosłowny ukośnik w tył
    • \" - dosłowny podwójny cudzysłów

Przykłady poprawnych łańcuchów znaków:

"MERA-400\n"
"\xab\xac\xad"
"\"napis\""

To, w jaki sposób znaki umieszczane są w programie wynikowym zależy od dyrektywy, której łańcuch jest argumentem.

Bieżąca lokalizacja

Specjalną stałą jest "." (kropka), która w danym miejscu programu zawsze zwraca przesunięcie względem początku assemblowanego obiektu. Jej wartość jest zawsze >= 0.

Symbole

Symbolem jest ciąg znaków zaczynający się od małej lub dużej litery, z następującą dowolną ilością małych lub dużych liter, liczb lub znaku podkreślenia. Symbolowi przypisywana jest w trakcie asemblacji wartość oraz atrybuty. Symbol może być:

  • niezdefiniowany - wystąpiła deklaracja symbolu, ale nie został on (jeszcze) zdefiniowany,
  • lokalny lub globalny - widoczny tylko w granicach obiektu, w którym występuje, bądź również poza nim,
  • zmienny lub stały - wartość symbolu może, bądź nie może ulec zmianie w trakcie asemblacji programu.

Symbol otrzymuje atrybuty zgodne z tym w jakim kontekście został zdefiniowany bądź zadeklarowany.

Zmienne assemblera

Zmienne assemblera nie mają swojej reprezentacji w wynikowym obiekcie, są wartościami dostępnymi w trakcie asemblacji programu, a definiuje się je dyrektywą .equ. Symbol, który jest zmienną assemblera jest zawsze zmienny. Przykłady zmiennych:

.equ a 10
.equ b a+2

Stałe assemblera

Stałe assemblera również nie mają swojej reprezentacji w wynikowym obiekcie, są wartościami dostępnymi w trakcie asemblacji programu, a definiuje się je dyrektywą .const. Symbol, który jest zmienną assemblera jest zawsze stały. Przykłady stałych:

.const a 10
.const b a+9

Etykiety

Etykieta opisuje pozycję względem początku obiektu (jest nią wartość licznika rozkazów w danym momencie asemblacji). Przykład definicji etykiety:

etykieta:

Zasięg symboli

EMAS pozwala na zawężenie zasięgu symbolu przez poprzedzenie go kropką. Tak zdefiniowany symbol dostępny jest w zakresie od poprzedniego do następnego symbolu globalnego. Dzięki temu możliwe jest używanie takich samych nazw etykiet lokalnych w różnych kontekstach. Na przykład:

globalny1:
...
.lokalny:
...
ujs lokalny

globalny2:
.lokalny:
...
ujs lokalny

Skok 'ujs' wykonany zostanie każdorazowo do adresu opisanego symbolem ".lokalny", różnego dla zasięgów "globalny1" i "globalny2". Symbol lokalny nie może być zdefiniowany, jeśli uprzednio nie wystąpił żaden symbol globalny.

Można również odwołać się do lokalnego symbolu spoza jego zasięgu poprzedzając jego nazwę nazwą symbolu globalnego, w kontekście którego został zdefiniowany:

globalny1.lokalny
globalny2.lokalny

Lokalnych symboli można używać również definiując stałe i zmienne:

zmienna:
.equ a 1
.equ b 2

i odwoływać się do nich:

lw r1, zmienna.a
lw r2, zmienna.b

Widoczność symboli dla konsolidatora

Wszystkie symbole definiowane są jako widoczne wyłącznie w ramach asemblowanego obiektu. Jeśli symbol ma być widoczny dla konsolidatora, należy go zadeklarować jako zewnętrzny dyrektywą .global. Zmiana zasięgu może nastąpić przed, lub po definicji:

.global printf
.global snprintf
.global strdup

printf:
...
snprintf:
...
strdup:

procedura:
.global procedura

Wyrażenia

Operatory

Poniższa tabela przedstawia operatory arytmetyczne i bitowe, w kolejności malejących priorytetów

Operatory Działanie
~ negacja bitowa
\ skalowanie
*, /, % mnożenie, dzielenie, reszta z dzielenia
+, - dodawanie, odejmowanie
<<, >> przesunięcie bitowe w lewo i w prawo
&, |, ^ logiczny iloczyn, suma i suma wykluczająca

Operator skalowania realizuje funkcję dokładnie taką samą, jak przesunięcie bitowe, jest tylko innym sposobem jego zapisu. Poniższe zapisy są równoznaczne:

3\7  =  3 << (15-7)

Skalowanie można rozumieć jako: "umieść liczbę 3 od 7 pozycji bitowej w górę. Bity dla tego zapisu numerowane są od 0 (bit najstarszy) do 15 (bit najmłodszy). Wynikiem będzie więc liczba:

0000001100000000

Dyrektywy assemblera

.cpu

Składnia: .cpu <mera400|mx16>

Odpowiednik przełącznika -c linii poleceń EMAS-a. Pozwala określić typ procesora, dla którego ma być generowany kod wynikowy. Użycie mx16 sprawia, że:

  • mnemoniki CRON, SINT, SIND są rozpoznawane i poprawnie assemblowane
  • maksymalny rozmiar wynikowego pliku binarnego wynosi 64k słowa (32k dla mera400)

.file

Składnia: .file nazwa_pliku

Ustala nazwę assemblowanego pliku. Jeśli assembler będzie chciał użyć w komunikatach nazwy pliku, to domyślna nazwa (zgodna z nazwą assemblowanego pliku) zostanie od momentu wydania dyrektywy zastąpiona podaną.

.line

Składnia: .line numer_linii

Ustala numer bieżącej linii assemblacji (poczynając od linii następującej po dyrektywie). Dalsze linie będą numerowane kolejno od podanej wartości.

.include

Składnia: .include nazwa_pliku

Powoduje dołączenie do assemblacji pliku o podanej nazwie.

.equ

Składnia: .equ nazwa_zmiennej wartość

Ustala wartość zmiennej assemblacji. Może być ona w dalszej części programu zmieniana.

.const

Składnia: .const nazwa_zmiennej wartość

Ustala wartość stałej assemblacji. Wartość ta nie może ulec zmianie.

Zapisuje w programie wynikowym na prawych pozycjach (LSB) kolejnych słów podane wartośći 8-bit.

.word

Składnia: .word liczba_16_bit [, ...]

Zapisuje w programie kolejne słowa 16-bit o podanej wartości.

.dword

Składnia: .dword liczba_32_bit [, ...]

Zapisuje w programie wynikowym pary słów 16-bit tworzące podaną liczbę 32-bit.

.float

Składnia: .float liczba_zmiennoprzecinkowa [, ...]

Zapisuje w programie wynikowym trójki słów 16-bit tworzące podaną 48-bit liczbę zmiennoprzecinkową w notacji używanej przez arytmometr wielokrotnej precyzji MERY-400.

.ascii

Składnia: .ascii łańcuch

Zapisuje w programie wynikowym podany łańcuch, kodując go po dwa znaki ASCII na słowo maszynowe.

.asciiz

Składnia: .asciiz łańcuch

Zapisuje w programie wynikowym podany łańcuch, kodując go po dwa znaki ASCII na słowo maszynowe. Łańcuch terminowany jest 8-bit wartością '0'.

.res

Składnia: .res rozmiar [,liczba_16_bit]

Rezerwuje w programie wynikowym obszar o podanym rozmiarze (w słowach), wypełniając go zerami. Jeśli podany jest drugi argument, jego wartość używana jest do wypełnienia zamiast zera.

.org

Składnia: .org adres

Ustala nową bieżącą pozycję assemblacji. Następne wygenerowane słowo zostanie umieszczone już pod podanym, nowym adresem.

.entry

Składnia: .entry adres

Dyrektywa ma efekt wyłącznie w przypadku generowania pliku wynikowego jako obiektu EMELF. Określa adres wejścia do programu.

.global

Składnia: .global nazwa_etykiety

Dyrektywa ma efekt wyłącznie w przypadku generowania pliku wynikowego jako obiektu EMELF. Wskazuje, że symbol będzie widoczny dla linkera.

.ifdef, .ifndef

Dyrektywa pozwala na asemblację warunkową podejmowaną istnieniem (bądź nie) zdefiniowanego symbolu.

.ifdef SYMBOL
... kod asemblowany, jeśl SYMBOL jest zdefiniowany ...
.else
... kod asemblowany, jeśli SYMBOL nie jest zdefiniowany ...
.endif
.ifndef SYMBOL
... kod asemblowany, jeśli SYMBOL nie jest zdefiniwoany
.endif

Symbol określający warunek może być zarówno etykietą, stałą lub zmienną zdefiniowaną w pliku źródłowym, jak również stałą zdefiniowaną podczas wywołania emas-a z opcją '-D'

.struct

Dyrektywa ta pozwala na stworzenie opisu struktury danych w celu uczynienia kodu źródłowego bardziej czytelnym. Nie formalizuje ona struktury, a jedynie ułatwia pracę z danymi. Przykład opisu struktury 'struktura':

.struct struktura:
   adres .res 1
   dluga_liczba .res 2
   tekst .res 64
.endstruct

Kolejne pozycje nazywają kolejne pola struktury i określają ich długość w słowach. Po asemblacji symbolom o tych nazwach przypisany zostanie offset w strukturze, a nazwie struktury - jej długość.

Użycie struktury w programie może wyglądać następująco:

moja_str: .res struktura

lw r3, moja_str
ld r3 + dluga_liczba
lw r7, moja_str + tekst

Definicja struktury z powyższego przykładu równoważna jest de facto następującemu ciągowi dyrektyw:

.const adres 0         ; offset pola 'adres'
.const dluga_liczba 1  ; offset pola 'dluga_liczba'
.const tekst 3         ; offset pola 'tekst'
.const struktura 67    ; rozmiar struktury 'struktura'

W strukturach można również zawęzić zakres nazewnictwa pól:

.struct struktura:
   .adres .res 1
   .dluga_liczba .res 2
   .tekst .res 64
.endstruct

Wtedy nazwy pól będą dostępne wyłącznie w kontekście danej struktury, a jej użycie wyglądać będzie następująco:

moja_str: .res struktura

lw r3, moja_str
ld r3 + struktura.dluga_liczba
lw r7, moja_str + struktura.tekst

Rozkazy

EMAS używa mnemoników z oryginalnej listy rozkazów MERY-400, ale wprowadza zmiany składniowe wynikające z innego zapisu argumentu normalnego. Nie używa przyrostków zależnych od lokalizacji argumentu. Czyli np. rozkaz ```LWn`` z listy rozkazów, gdzie ```n``` zmienia się w zależności od typu argumentu normalnego, ma zawsze mnemonik ```LW```. Lokalizację argumentu określa składnia dla rejestrów rB i rC. Poniższe przykłady ładowania wartości do rejestru r1 przedstawiają wszystkie możliwe postacie argumentu normalnego:

  • LW r1, r2 - ładowana jest wartość z rejestru r2
  • LW r1, r2+r3 - ładowana jest wartość z rejestru r2 B-modyfikowana (sumowana z) rejestrem r3
  • LW r1, [r2] - ładowana jest wartość z komórki pamięci (D-modyfikacja) wskazanej zawartością rejestru r2
  • LW r1, [r2+r3] - ładowana jest wartość z komórki pamięci (D-modyfikacja) wskazanej zawartością rejestru r2 B-modyfikowanego (sumowanego z) rejestrem r3
  • LW r1, 0x1000 - ładowana jest wartość 0x1000
  • LW r1, 0x1000+r2 - ładowana jest wartość 0x1000 B-modyfikowana zawartością (sumowana z) rejestru r2
  • LW r1, [0x1000] - ładowana jest wartość z komórki pamięci (D-modyfikacja) o adresie 0x1000
  • LW r1, [x1000+r2] - ładowana jest wartość z komórki pamięci (D-modyfikacja) o adresie 0x1000 B-modyfikowanym zawartością rejestru r2

Pliki nagłówkowe

Z assemblerem EMAS dostarczany jest zestaw plików nagłówkowych definiujących stałe przydatne przy programowaniu w obszarze systemu operacyjnego i dla urządzeń wejścia-wyjścia:

  • cpu.inc - stałe wynikające z architektury systemu (zdefiniowane lokalizacje pamięci, przerwania, maski przerwań),
  • io.inc - podstawowe stałe przydatne przy konfigurowaniu pamięci, stałe dla systemu wejścia-wyjścia, komendy dla kanału znakowego i wspólne komendy dla urządzeń w nim pracujących,
  • mega.inc - komendy dla pamięci mega Amepolu,
  • multix.inc - stałe i komendy przydatne przy programowaniu procesora peryferyjnego MULTIX.

Znaczenie poszczególnych stałych opisane jest w plikach nagłówkowych, ich rolę opisuje Dokumentacha Techniczno-Ruchowa minikomputera.