//===============================================================
/*
 To jest symulator dzialania programow w jezyku wewnetrznym typowego
 prostego komputera.  Pozwala wykonywac program krok po kroku i
 sledzic, jak jego wykonanie wplywa na stan pamieci i rejestru
 arytmetycznego.

 Zakladamy bardzo prosta architekture komputera:
  -- pamiec adresowana od 0 (wlacznie) do MAXDL_PAMIECI (wylacznie)
     zlozona z komorek mieszczacych po jednej liczbie calkowitej lub
     jednym rozkazie z adresem;
  -- rejestr arytmetyczny mieszczacy jedna liczbe calkowita;
  -- nastepujaca liste rozkazow:

       loadn n     -- zaladuj liczbe nieujemna  n  do rej.arytm.
       loada adr   -- zaladuj liczbe spod adresu  adr  do rej.arytm.
       store adr   -- zapisz liczbe z rej.arytm. pod adres  adr
       add adr     -- dodaj liczbe spod adresu  adr  do rej.arytm.
       sub adr     -- odejmij liczbe spod adresu  adr  od rej.arytm.
       mul adr     -- pomnoz rej.arytm. przez liczbe spod adresu  adr
       incr adr    -- zwieksz liczbe pod adresem  adr  o 1
       decr adr    -- zmniejsz liczbe pod adresem  adr  o 1
       goto adr    -- skocz pod adres  adr
       zergoto adr -- jesli w rej.arytm. jest zero to skocz pod adres  adr
       plsgoto adr -- jesli w rej.arytm. jest liczba dodatnia,
                      to skocz pod adres  adr
       mingoto adr -- jesli w rej.arytm. jest liczba ujemna,
                      to skocz pod adres  adr
       stop        -- zatrzymaj program

     Adres  adr  jest postaci:
       albo prostego adresu, czyli liczby z zakresu [0..MAXDL_PAMIECI),
       albo adresu z modyfikatorem  n+[k] , gdzie  n  i  k  sa
            prostymi adresami.
     W tym ostatnim przypadku za adres do wykonania (adres efektywny)
     przyjmuje sie  n  powiekszone o zawartosc komorki  k .

 Plik zrodlowy symulatora (czyli ten plik) nalezy umiescic u siebie na
 koncie, np. pod nazwa  wewn.c ; nastepnie przetlumaczyc kompilatorem
 C, czyli np. takim poleceniem:

   gcc -Wall wewn.c -o wewn

 Spowoduje ono kompilacje programu do pliku wynikowego  wewn
 zawierajacego symulator gotowy do uzytku.

 Program w jezyku nalezy napisac edytorem w osobnym pliku, np. o
 nazwie  dane .  Oto przykladowy program gotowy do wykonania:

     1: loadn 2
     2: store 16        #  a0=2;
     3: loadn 0
     4: store 14        #  i=0;
     5: loada 14        #  PETLA
     6: sub 15
     7: zergoto 13      #  while (i != n)
     8: loada 16+[14]
     9: mul 16
    10: incr 14
    11: store 16+[14]   #  a[i+1] = a[i]*a[0];  i++;
    12: goto 5
    13: stop            #  KONIEC
    14: 0               #  i (pomocnicza)
    15: 16              #  n (liczba komorek)
    16: 0               #  a0
    END

 (ten program wypelnia komorki od 16 w gore kolejnymi potegami
 dwojki).

 Kazdy wiersz programu sklada sie z
   -- adresu, pod ktory nalezy wpisac zawartosc;
   -- zawartosci, ktora moze byc
        albo rozkazem z adresem,
        albo liczba calkowita;
   -- opcjonalnego komentarza (po znaku  # ).
 Program konczy sie wierszem zawierajacym tylko dyrektywe  END .
 Prosze odrozniac ta dyrektywe od rozkazu  stop :  END  jest tylko
 jeden i oznacza, ze na pliku juz nic wiecej nie jest napisane;
 stop-ow moze byc wiele i kazdy oznacza, ze program zatrzyma sie,
 jesli na ktorys z nich natrafi (ale  stop  mozna np. przeskoczyc).

 Program podany powyzej wypelnia kolejne komorki pamieci poczawszy od
 223 kolejnymi liczbami calkowitymi.  Symulator uruchamiamy na tym
 programie podajac nazwe pliku:

   ./wewn dane

 Nastepnie podajemy zakres komorek, ktorych stan zamierzamy sledzic;
 np. dla tego przykladu rozsadnym zakresem jest: od 210 do 234.  W
 koncu piszemy, od ktorej komorki nalezy program uruchomic; zwykle to
 bedzie pierwszy adres programu, ale to nie jest obowiazkowe.  Odtad
 kazde nacisniecie klawisza  ENTER  spowoduje wykonanie kolejnego
 rozkazu programu.  Symulator wyswietla aktualny stan sledzonych
 komorek oraz rejestru arytmetycznego; i zaznacza, w ktorym miejscu
 wykonywania programu aktualnie sie znajduje.

 Zabawe z symulatorem proponuje zaczac od tego, zeby powyzszy przyklad
 przepisac na plik o nazwie  dane  i postepowac zgodnie z podanymi
 wyzej sugestiami.  Od chwili, gdy wpiszemy adres startowy programu,
 nalezy juz tylko wielokrotnie naciskac  ENTER .  Potem -- zachecam do
 pisania i sledzenia wlasnych programow.
*/

//===============================================================
// WSPOLNE :

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<limits.h>

#define MAXDL_WIERSZA 202
#define MAXDL_ADRESU 40
#define MAXDL_ROZKAZU 8
#define MAXDL_ZAWARTOSCI 80
#define MAXDL_KOMENTARZA 80
#define MAXDL_KOMUNIKATU 70
#define DL_SLOWNROZK 13
#define MAXDL_PAMIECI 10000

#define BRAK_ADR -1

int min_adr=INT_MAX, max_adr=0;

typedef enum { FALSE = 0, TRUE = 1 }  Boolean;

typedef  enum { loadn   =  0,
                loada   =  1,
                store   =  2,
                add     =  3,
                sub     =  4,
                mul     =  5,
                incr    =  6,
                decr    =  7,
                got     =  8,
                zergoto =  9,
                plsgoto = 10,
                mingoto = 11,
                stop    = 12,
              }  Rozkaz;

typedef  struct {   // wspolne:
                  char koment[MAXDL_KOMENTARZA];
                  Boolean czy_licz;
                    // o ile  czy_licz == TRUE :
                  int lic;
                    // o ile  czy_licz == FALSE :
                  Rozkaz roz;
                  int adr, mod;  // brak modyfikacji: mod == -1
                }  Zawartosc;

//===============================================================
// ARCHITEKTURA:

Zawartosc  pamiec [MAXDL_PAMIECI];
int  rej_arytm;


void  inic_pamiec () {
    // Wszedzie wpisac liczby.
  int i;
  for (i=0; i<MAXDL_PAMIECI; i++)
    pamiec[i].czy_licz = TRUE;
}

//===============================================================
// BLEDY:

Boolean  zly_adr (int adr) {
    // Sprawdza, czy podany adres jest poza dopuszczalnym zakresem
    // (poza pamiecia).
  return (0 > adr || adr > MAXDL_PAMIECI);
}


void  blad (char* s0, char* s1) {
    // Drukuje komunikat bledu.  Jako argumenty bierze dwa napisy.
  fprintf (stderr, "\n  %s %s\n\n", s0, s1);
  exit(1);
}


void  blad1 (char* s0, int n1, char* s1) {
    // Drukuje komunikat bledu z jedna liczba.  Jako argumenty bierze
    // poczatek komunikatu przed liczba, liczbe i koniec komunikatu za
    // liczba.
  fprintf (stderr, "\n!!! %s %i %s!!!\n\n", s0, n1, s1);
  exit(1);
}


void  blad2 (char* s0, int n1, char* s1, int n2, char* s2) {
    // Dziala podobnie do  blad1 , tylko ze jako argumenty bierze dwie
    // liczby.
  fprintf (stderr, "\n!!! %s %i %s %i %s!!!\n\n", s0, n1, s1, n2, s2);
  exit(1);
}

//===============================================================
// WYDRUK TEKSTU PROGRAMU:

char  slownik_rozkazow [DL_SLOWNROZK][MAXDL_ROZKAZU];


void  inic_slown_rozk () {
    // Tlumaczy rozkazy z reprezentacji wewnetrznej na napisy
    // nadajace sie do wydrukowania.
  strcpy (slownik_rozkazow [loadn], "loadn");
  strcpy (slownik_rozkazow [loada], "loada");
  strcpy (slownik_rozkazow [store], "store");
  strcpy (slownik_rozkazow [add], "add");
  strcpy (slownik_rozkazow [sub], "sub");
  strcpy (slownik_rozkazow [mul], "mul");
  strcpy (slownik_rozkazow [incr], "incr");
  strcpy (slownik_rozkazow [decr], "decr");
  strcpy (slownik_rozkazow [got], "goto");
  strcpy (slownik_rozkazow [plsgoto], "plsgoto");
  strcpy (slownik_rozkazow [mingoto], "mingoto");
  strcpy (slownik_rozkazow [zergoto], "zergoto");
  strcpy (slownik_rozkazow [stop], "stop");
}

void  druk_zawartosc (int adr, Zawartosc zaw) {
  if (zaw.czy_licz)  printf ("%5i: %21i", adr, zaw.lic);
  else  if (zaw.adr == BRAK_ADR)
    printf ("%5i: %-21s", adr, slownik_rozkazow[zaw.roz]);
  else  if (zaw.mod == BRAK_ADR)
    printf("%5i: %-7s %5i %7s",
           adr,
           slownik_rozkazow[zaw.roz],
           zaw.adr,
           " ");
  else
    printf ("%5i: %-7s %5i+[%5i]",
            adr,
            slownik_rozkazow[zaw.roz],
            zaw.adr,
            zaw.mod);
  if (strlen(zaw.koment) != 0)  printf ("  # %s", zaw.koment);
  printf("\n");
}


void  druk_pom (int from, int to) {
    // Pomocnicza do  druk .
  int adr;
  for (adr=from; adr<to; adr++) {
    druk_zawartosc (adr, pamiec[adr]);
  }
}


void  druk (int from, int to, int nr_rozk) {
    // Drukuje tekst programu miedzy podanymi adresami zaznaczajac
    // rozkaz, ktory jest aktualnie do wykonania.
  if (0 > from || from > to || to > MAXDL_PAMIECI)
    blad2 ("DRUKOWANIE POZA PAMIECIA: OD ", from, " DO ", to, "");
  if (0 > nr_rozk || nr_rozk > MAXDL_PAMIECI)
    blad1 ("DRUKOWANIE ROZKAZU POZA PAMIECIA: ", nr_rozk, "");
  if (from <= nr_rozk && nr_rozk <  to) {
    druk_pom (from, nr_rozk);
    printf ("--TU---\n");
    druk_pom (nr_rozk, to);
  }
  else  druk_pom (from, to);
}


//===============================================================

/*
 Gramatyka:
 ~~~~~~~~~~
   <program>  ::=  <wiersz>*
 
   <wiersz>  ::=
     <adres prosty> : <zawartosc> <komentarz> NL

  --------------- 
 |
   <zawartosc>  ::=  <liczba>
                  |  stop
                  |  <rozkaz> <sp> <adres>
 
   <rozkaz>  ::=  loadn
               |  loada
               |  store
               |  add
               |  sub
               |  mul
               |  incr
               |  decr
               |  goto
               |  plsgoto
               |  mingoto
               |  zergoto
 
   <adres>  ::=  <adres prosty>
              |  <adres prosty> <sp> + <sp> [ <sp> <adres prosty> <sp> ]
 
   <liczba>  ::=  <znak> <adres prosty> 
 
   <adres prosty>  ::=  <cyfra> <cyfra>*
 
   <znak>  ::=  + | - | <puste>
 |
   --------------

   <cyfra>  ::=  0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
 
   <komentarz>  ::=  <puste>
                  |  # ...  -- cokolwiek az do konca wiersza
 
   <puste>  ::=

   <sp>  ::=  ...  -- ciag spacji (byc moze pusty)
 */

//===============================================================
// WCZYTYWANIE:

FILE* we;
char  komunikat [MAXDL_KOMUNIKATU];
int  nr_wiersza = 0;


Boolean  wczyt (FILE* we, char* wiersz) {
    // Wczytuje wiersz nie bojac sie spacji.  Zwraca informacje, czy
    // skonczyl sie plik.
  while ((*wiersz = getc(we)) != EOF && *wiersz != '\n')  wiersz++;
  if (*wiersz == EOF)  { *wiersz = '\0'; return TRUE; }
  else  { *wiersz = '\0'; return FALSE; }
}


Boolean  znak (char** napis, int* zn) {
    // Wczytuje znak  zn  wg produkcji:
    //
    //   <znak>  ::=  + | - | <puste>
    //
    // Zwraca informacje, czy znak jest poprawny.
  if (**napis == '+')  { (*napis)++; *zn = 1; }  
  else  if (**napis == '-')  { (*napis)++; *zn = -1; }
  else  *zn = 1;
  return TRUE;
}


Boolean  adres_prosty (char** napis, int* adr) {
    // Wczytuje liczbe calkowita bez znaku  adr  z napisu  napis  wg
    // produkcji:
    //
    //   <adres prosty>  ::=  <cyfra> <cyfra>*
    //
    // Zwraca informacje, czy liczba byla napisana poprawnie.
  if ('0' <= **napis && **napis <= '9') {
    *adr = **napis - '0'; (*napis)++;
    while ('0' <= **napis && **napis <= '9')
     { *adr = *adr * 10 + **napis - '0'; (*napis)++; }
    return TRUE;
   }
  else  return FALSE;
}


Boolean  liczba (char** napis, int* ll) {
    // Wczytuje liczbe  ll  z napisu  napis  wg produkcji:
    //
    //   <liczba>  ::=  <znak> <adres prosty>
    //
    // Zwraca informacje, czy liczba byla napisana poprawnie. 
  int  zn, k;
  if (znak (napis, &zn)) {
    if (adres_prosty (napis, &k))  {
    *ll = zn*k; return TRUE; }
    else  return FALSE;
  }
  else  return FALSE;
}


Boolean  rozkaz (char** napis, Rozkaz* rr) {
    // Wczytuje rozkaz  ll  z napisu  napis  wg produkcji:
    //
    //   <rozkaz>  ::=  loadn
    //               |  loada
    //               |  store
    //               |  add
    //               |  sub
    //               |  mul
    //               |  incr
    //               |  decr
    //               |  goto
    //               |  plsgoto
    //               |  mingoto
    //               |  zergoto
    //
    //  Zwraca informacje, czy rozkaz byl napisany poprawnie.
  int k;
  if (strncmp (*napis, "loadn", k=5) == 0)  *rr = loadn;  else
  if (strncmp (*napis, "loada", k=5) == 0)  *rr = loada;  else
  if (strncmp (*napis, "store", k=5) == 0)  *rr = store;  else
  if (strncmp (*napis, "add", k=3) == 0)  *rr = add;  else
  if (strncmp (*napis, "sub", k=3) == 0)  *rr = sub;  else
  if (strncmp (*napis, "mul", k=3) == 0)  *rr = mul;  else
  if (strncmp (*napis, "incr", k=4) == 0)  *rr = incr;  else
  if (strncmp (*napis, "decr", k=4) == 0)  *rr = decr;  else
  if (strncmp (*napis, "goto", k=4) == 0)  *rr = got;  else
  if (strncmp (*napis, "plsgoto", k=7) == 0)  *rr = plsgoto;  else
  if (strncmp (*napis, "mingoto", k=7) == 0)  *rr = mingoto;  else
  if (strncmp (*napis, "zergoto", k=7) == 0)  *rr = zergoto;  else
  if (strncmp (*napis, "stop", k=4) == 0)  *rr = stop;
  else  return FALSE;
  *napis += k;
  return TRUE;
}


Boolean  adres (char** napis, int* adr, int* mod) {
    // Wczytuje adres  adr  i modyfikator  mod  z napisu  napis
    // wg produkcji:
    //
    //  <adres> 
    //   ::=  <adres prosty>
    //    |   <adres prosty> <sp> + <sp> [ <sp> <adres prosty> <sp> ]
    //
    // Zwraca informacje, czy byly napisane poprawnie.
  if (adres_prosty (napis, adr)) {
    while (**napis == ' ' || **napis == '\t')  (*napis)++;
    if (**napis == '+')
     {
      (*napis)++;
      while (**napis == ' ' || **napis == '\t')  (*napis)++;
      if (**napis == '[')
       {
        (*napis)++;
        while (**napis == ' ' || **napis == '\t')  (*napis)++;
        if (adres_prosty (napis, mod))
         {
          while (**napis == ' ' || **napis == '\t')  (*napis)++;
          if (**napis == ']')  { (*napis)++; return TRUE; }
         }
       }
     }
    else {
      *mod = BRAK_ADR;
      return TRUE;
    }
  }
  return FALSE;
}


char*  zawartosc (int nr_rozkazu, char** napis, Zawartosc* zaw) {
    // Wczytuje zawartosc (liczbe lub rozkaz)  zaw  z napisu
    // napis wg produkcji:
    //
    //   <zawartosc>  ::=  <liczba>
    //                  |  stop
    //                  |  <rozkaz> <adres>
    //
    // Zwraca komunikat bledu albo NULL jesli nie ma bledu.
  if (liczba (napis, &(zaw->lic))) {
    zaw->czy_licz = TRUE;
    if (**napis == '\0')  return NULL;
    else {
      sprintf (komunikat, "W KOM. %i smieci po liczbie", nr_rozkazu);
      return  komunikat;
    }
  }
  else {
    if (rozkaz (napis, &(zaw->roz))) {
      zaw->czy_licz = FALSE;
      if (zaw->roz == stop) {
        zaw->adr = BRAK_ADR; zaw->mod = BRAK_ADR;
        return NULL;
      }
      else {
        while (**napis == ' ' || **napis == '\t')  (*napis)++;
        if (adres (napis, &(zaw->adr), &(zaw->mod))) {
          if (**napis == '\0')  return NULL;
          else {
            sprintf (komunikat,
                     "W KOM. %i smieci po rozkazie",
                     nr_rozkazu);
            return  komunikat;
	  }
	}
        else  return NULL;
      }
    }
    else {
      sprintf (komunikat, "W KOM. %i ani rozkaz ani liczba", nr_rozkazu);
      return  komunikat;
    }
  }
}


Boolean  rd (char* wiersz, char c, char* cz1, char* cz2) {
    // Rozdziela wiersz na czesci przed i po wystapieniu znaku  c .
    // Zwraca informacje, czy taki znak w ogole znaleziono.
  while (*wiersz != '\0' && *wiersz != c)
    *(cz1++) = *(wiersz++);
  *cz1 = '\0';
  if (*wiersz == '\0') {
    strcpy (cz2, "");
    return FALSE;
  }
  else {
    wiersz++;  strcpy (cz2, wiersz);
    return TRUE;
  }
}


char*  despac (char* napis) {
    // Oddaje  napis  oczyszczony z wiodacych i konczacych spacji i
    // tabulacji.
  int  dlugosc = strlen (napis);
  char*  kon_napisu = napis + dlugosc - 1;
  char*  wynik;
  char*  kon_wyniku;
  char*  pom;
  while (*napis == ' ' || *napis == '\t') {
    napis++; dlugosc--;
  }
  while (kon_napisu >= napis &&
	 (*kon_napisu == ' ' || *kon_napisu == '\t')
	 ) {
    kon_napisu--; dlugosc--;
  }
  wynik = (char*)malloc(dlugosc+1);
  kon_wyniku = wynik + dlugosc - 1;
  for (pom=wynik; pom<=kon_wyniku; pom++) *pom = *(napis++);
  *pom = '\0';
  return wynik;
}


char*  rozdziel (char* wiersz, int* adr, Zawartosc* zaw) {
    // Rozdziela wiersz programu na adres  adr  (przed dwukropkiem) i
    // zawartosc  zaw  (za dwukropkiem).  Komentarz (za plotkiem)
    // zostaje wlaczony do zawartosci.  Komentarza moze w ogole nie
    // byc i wtedy pole zawartosci siega az do konca wiersza.  Zwraca
    // komunikat bledu; a jesli nie ma bledu, to NULL.
  char  pole_adresu [MAXDL_ADRESU],
        pole_zawartosci [MAXDL_WIERSZA],
        pole_zawartosci_wlasc [MAXDL_ZAWARTOSCI],
        pole_komentarza [MAXDL_KOMENTARZA];
  char* pole_a = pole_adresu;
  char* pole_z = pole_zawartosci_wlasc;
  if (rd (wiersz, ':', pole_adresu, pole_zawartosci)) {
    rd (pole_zawartosci, '#', pole_zawartosci_wlasc, pole_komentarza);
    strcpy (pole_adresu, despac (pole_adresu));
    strcpy (pole_zawartosci_wlasc, despac (pole_zawartosci_wlasc));
    strcpy (zaw->koment, despac (pole_komentarza));
    if (adres_prosty (&pole_a, adr))
      return zawartosc (*adr, &pole_z, zaw);
    else  return "niecyfry w adresie rozkazu";
  }
  else
    if (strcmp (despac (pole_adresu), "END") == 0)  return "END";
    else  return "brak dwukropka w wierszu";
}


Boolean  czyt_wiersz () {
    // Czyta pelny wiersz programu z pliku.  Zwraca informacje, czy to
    // juz koniec programu.
  char  wiersz[MAXDL_WIERSZA];
  char*  komunikat;
  char  kom[MAXDL_KOMUNIKATU];
  Zawartosc zaw;
  int  adr;
  Boolean  koniec = wczyt(we, wiersz); nr_wiersza++;
  komunikat = rozdziel (wiersz, &adr, &zaw);
  if (komunikat == NULL) {
    druk_zawartosc (adr, zaw);
    if (adr<min_adr)  min_adr=adr;
    if (adr>max_adr)  max_adr=adr;
    if (0 > adr || adr >= MAXDL_PAMIECI) {
      sprintf (kom,
               "adres poza przedzialem  [0..%i]",
               MAXDL_PAMIECI
              );
      komunikat = kom;
    }
    else
      if (! zaw.czy_licz &&
	  (BRAK_ADR > zaw.adr || zaw.adr >= MAXDL_PAMIECI)
	 ) {
        sprintf (kom,
                 "W KOM.%i adres rozkazu poza przedzialem  [0..%i]",
                 adr, MAXDL_PAMIECI
                );
        komunikat = kom;
      } else
	  if
	    (! zaw.czy_licz &&
	     (BRAK_ADR > zaw.mod || zaw.mod >= MAXDL_PAMIECI)
            ) {
	    sprintf(kom,
                    "W KOM.%i modyfikator rozkazu poza przedzialem  [0..%i]",
                    adr, MAXDL_PAMIECI
		   );
	    komunikat = kom;
	  }
          else {
	    pamiec[adr] = zaw;
	    return FALSE;
	  }
  }
  else  if (strcmp (komunikat, "END") == 0 || koniec) {
    printf ("END\n\n");
    return TRUE;
  }
  printf ("!!! WIERSZ %i: %s\n\n", nr_wiersza, komunikat);
  return FALSE;
}


void  wczytaj_program () {
    // Wczytuje pelny program z pliku.
  Boolean koniec;
  do { koniec = czyt_wiersz (); }
  while (! koniec);
}

//===============================================================
// DZIALANIE:


void  krok_obliczenia (int* nr_rozkazu, Boolean* koniec) {
    // Symuluje wykonanie jednego kroku obliczenia ostatnio wczytanego
    // programu.
  int adr_efekt;

  if (zly_adr (*nr_rozkazu))
    blad1 ("Nie moge wykonac rozkazu", *nr_rozkazu, "-- poza pamiecia");
  if (pamiec[*nr_rozkazu].czy_licz)
    blad1 ("POD ADR.", *nr_rozkazu, "LICZBA ZAMIAST ROZKAZU");

  if (pamiec[*nr_rozkazu].adr == BRAK_ADR)  adr_efekt = BRAK_ADR;
  else {
    if (zly_adr (pamiec[*nr_rozkazu].adr))
      blad1 ("W ROZKAZIE POD ADR.", *nr_rozkazu, "ZLY ADRES");
        // Dobry adres:
    if (pamiec[*nr_rozkazu].mod == BRAK_ADR)
      adr_efekt = pamiec[*nr_rozkazu].adr;
    else  if (zly_adr (pamiec[*nr_rozkazu].mod))
      blad1 ("W ROZKAZIE POD ADR.", *nr_rozkazu, "ZLA MODYFIKACJA");
    else  if (! pamiec[pamiec[*nr_rozkazu].mod].czy_licz)
      blad1 ("W ROZKAZIE POD ADR.",
             *nr_rozkazu,
             "MODYFIKACJA PRZEZ NIELICZBE");
    else   // Dobry adres i dobra modyfikacja:
      adr_efekt =
	pamiec[*nr_rozkazu].adr + pamiec[pamiec[*nr_rozkazu].mod].lic;
    if (zly_adr (adr_efekt))
      blad1 ("W ROZKAZIE POD ADR.", *nr_rozkazu, "ZLY ADRES EFEKTYWNY");
  }

  *koniec = FALSE;
  
  switch (pamiec[*nr_rozkazu].roz) { 
    case loadn :
      rej_arytm = adr_efekt;
      (*nr_rozkazu)++; break;
    case loada :
      if (pamiec[adr_efekt].czy_licz)
        rej_arytm = pamiec[adr_efekt].lic;
      else  blad2 ("ROZKAZ NR ", *nr_rozkazu,
                   ": POBRANIE NIELICZBY Z ADR.", adr_efekt,
                   " DO REJ.ARYTM."
                  );
      (*nr_rozkazu)++; break;
    case store :
      pamiec[adr_efekt].czy_licz = TRUE;
      pamiec[adr_efekt].lic = rej_arytm;
      (*nr_rozkazu)++; break;
    case add :
      if (pamiec[adr_efekt].czy_licz)
        rej_arytm += pamiec[adr_efekt].lic;
      else  blad2 ("ROZKAZ NR ", *nr_rozkazu,
                   ": DODAWANIE NIELICZBY Z ADR.", adr_efekt,
                   " DO REJ.ARYTM."
                  );
      (*nr_rozkazu)++; break;
    case sub :
      if (pamiec[adr_efekt].czy_licz)
        rej_arytm -= pamiec[adr_efekt].lic;
      else  blad2 ("ROZKAZ NR ", *nr_rozkazu,
                   ": ODEJMOWANIE NIELICZBY Z ADR.", adr_efekt,
                   " OD REJ.ARYTM."
                  );
      (*nr_rozkazu)++; break;
    case mul :
      if (pamiec[adr_efekt].czy_licz)
        rej_arytm *= pamiec[adr_efekt].lic;
      else  blad2 ("ROZKAZ NR ",
                   *nr_rozkazu,
                   ": MNOZENIE REJ.ARYTM. PRZEZ NIELICZBE Z ADR.",
                   adr_efekt,
                   "");
      (*nr_rozkazu)++; break;
    case incr :
      if (pamiec[adr_efekt].czy_licz)  pamiec[adr_efekt].lic++;
      else  blad2 ("ROZKAZ NR ", *nr_rozkazu,
                   ": ZWIEKSZANIE NIELICZBY Z ADR.", adr_efekt,
                   ""
                  );
      (*nr_rozkazu)++; break;
    case decr :
      if (pamiec[adr_efekt].czy_licz)  pamiec[adr_efekt].lic--;
      else  blad2 ("ROZKAZ NR ", *nr_rozkazu,
                   ": ZMNIEJSZANIE NIELICZBY Z ADR.", adr_efekt,
                   ""
                  );
      (*nr_rozkazu)++; break;
    case got :
      *nr_rozkazu = adr_efekt; break;
    case zergoto :
      if (rej_arytm == 0)  *nr_rozkazu = adr_efekt;
      else  (*nr_rozkazu)++;
      break;
    case plsgoto :
      if (rej_arytm > 0)  *nr_rozkazu = adr_efekt;
      else  (*nr_rozkazu)++;
      break;
    case mingoto :
      if (rej_arytm < 0)  *nr_rozkazu = adr_efekt;
      else  (*nr_rozkazu)++;
      break;
    case stop :
      *koniec = TRUE; (*nr_rozkazu)++;
      break;
  }
}

//===============================================================

int main (int argc, char* argv []) {
  char wiersz[MAXDL_WIERSZA];
  if (argc == 2) {
    Boolean koniec;
    int nr_rozkazu, from, to;

    we = fopen (argv[1], "r");
    if (we == NULL)  blad("NIE DA SIE OTWORZYC PLIKU  ", argv[1]);

    inic_slown_rozk(); inic_pamiec();
    printf ("\n");
  
    wczytaj_program ();
    printf ("REJ.ARYTM: %i\n\n", rej_arytm);
    fclose(we);

    printf ("DRUKOWAC STAN KOMOREK OD (%i): ", min_adr);
    wczyt(stdin, wiersz);
    if (strlen(wiersz) == 0)  from = min_adr;
    else  sscanf (wiersz, "%i", &from);

    printf ("                      DO (%i): ", max_adr);
    wczyt(stdin, wiersz);
    if (strlen(wiersz) == 0)  to = max_adr;
    else  sscanf (wiersz, "%i", &to);

    printf ("PROGRAM STARTOWAC OD KOMORKI (%i): ", min_adr);
    wczyt(stdin, wiersz);
    if (strlen(wiersz) == 0)  nr_rozkazu = min_adr;
    else  sscanf (wiersz, "%i", &nr_rozkazu);

    printf ("\n");

    do {
      krok_obliczenia (&nr_rozkazu, &koniec);
      druk (from, to+1, nr_rozkazu);
      printf ("\nREJ.ARYTM: %i\n", rej_arytm);
      if (koniec)  printf ("\nKONIEC\n");
      else  printf ("\nDALEJ -- ENTER\n\n");
      getc(stdin);
    }
    while (! koniec);
  }
  else {
    fprintf (stderr,
             "%s",
             "\n  POPRAWNE WYWOLANIE:  wewn plik_wejsciowy\n\n");
  }

  return 0;
}

//===============================================================
