0x01 – Stack OR/AND Registers

Posted on Marzec 2, 2016  in daj sie poznac

Notice: Korzystanie z get_currentuserinfo uznawane jest za przestarzałe od wersji 4.5.0! Zamiast tego użyj wp_get_current_user(). in /home/hotsauce/domains/karolpysklo.pl/public_html/wp-includes/functions.php on line 3830

Co? Maszyna wirtualna. Dlaczego? Dlaczego nie? Po co? Może w końcu coś zrozumiem 🙂 Tak chyba najkrócej można podsumować genezę projektu. Nie będzie to VMka, na której będzie można “postawić” kolejnego Linuxa, myślę raczej o czymś prostszym co będzie emulować zachowanie procesora, aplikacji która uruchomiona na systemie hosta wspierać będzie pojedynczy proces. Zakładam że nasz maszyna na początek powinna: pobierać instrukcję do wykonania, dekodować operand, wykonać instrukcję. Istnieją kilka sposobów na osiągniecie naszego celu, dwoma podstawowymi sposobami implementacji są: maszyna wirtualna oparta na stosie oraz oparta na rejestrach.

 

Maszyna stosowa – jak sama jej nazwa wskazuje ów maszyna operuje na stosie – czym jest stos chyba nie trzeba przypominać (jeśli tak: wiki). Podczas wykonywania instrukcji wartości są umieszczane na szczycie stosu oraz ściągane z tego szczytu,  należy tu wspomnieć o wskaźniku który na ten szczyt wskazuje. W trakcie operacji nie jest wykorzystywana żadna inna struktura pamięci. Dodając dwie liczby kładziemy je na stosie jedna po drugiej, następnie obie zastają ściągnięte a na ich miejsce umieszczany jest wynik.

// PUSH - 0x10; push imm8 - immediate value 1 byte
// POP  - 0x20; pop
// ADD  - 0x30; add - pop two values and push result
// EXIT - 0x00; exit

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

#define SIZE_STACK 7
#define PUSH 0x10
#define POP  0x20
#define ADD  0x30
#define EXIT 0x00

unsigned char sample[] = {0x10,0x09, // push 9
                          0x10,0x02, // push 2
                          0x10,0x01, // push 1
                          0x30,      // add 1+2
                          0x10,0x01, // push 1
                          0x10,0x01, // push 1
                          0x20,      // pop
                          0x10,0x02, // push 2
                          0x30,      // add 1+2
                          0x00};     // exit

unsigned int stack[SIZE_STACK];

int main()
{
 unsigned int ip = 0;
 unsigned int sp = 0;
 int vm_running = 1;

 printf("   ----->\n");
 while(vm_running)
 {
     switch(sample[ip])
     {
     case PUSH:
         stack[sp] = sample[ip+1];
         sp++;
         ip += 2;
         break;
     case POP:
         stack[sp-1] = 0;
         sp--;
         ip++;
         break;
     case ADD:
         sp -= 2;
         stack[sp] = stack[sp] + stack[sp+1];
         stack[sp+1] = 0;
         sp++;
         ip++;
         break;
     case EXIT:
         vm_running = 0;
         break;
 }

 int size = sizeof(stack)/sizeof(*stack);
 for(int i=0; i < size; i++)
     printf("%d ", stack[i]);
 printf("\n");
 }
 return 0;
}

Bardzo prosta implementacja z przykładowym bytecodem – stack machine.

 

Po uruchomieniu:

    —–>

9 0 0 0 0 0 0

9 2 0 0 0 0 0

9 2 1 0 0 0 0

9 3 0 0 0 0 0

9 3 1 0 0 0 0

9 3 1 1 0 0 0

9 3 1 0 0 0 0

9 3 1 2 0 0 0

9 3 3 0 0 0 0

9 3 0 0 0 0 0

9 3 0 0 0 0 0

 


 

Maszyna rejestrowa – jak łatwo można się domyślić, nie korzysta ze stosu. Wszystkie wartości przechowywane są w rejestrach. Aby przekazać wartość do rejestru, instrukcja oprócz samej wartości musi zawierać także adres rejestru (w odróżnieniu od modelu opartego na stosie w którym wszystko załatwiał wskaźnik). Dodając dwie liczby, obie z nich należy przekazać do rejestrów, a następnie dodać umieszczając wynik w rejestrze (może być w innym lub w jednym z użytych wcześniej do przechowania wartości)

// MOV  - 0x10; mov register, imm; A = imm8
// ADD  - 0x20; add register, register, register; A = B + C
// EXIT - 0x00; exit

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

#define MOV  0x10
#define ADD  0x20
#define EXIT 0x00

unsigned char sample[] = {0x10,0x00,0x01,      // MOV reg0, 1
                          0x10,0x01,0x02,      // MOV reg1, 2
                          0x10,0x02,0x03,      // MOV reg2, 3
                          0x20,0x00,0x01,0x02, // ADD reg0, reg1, reg2
                          0x00};               // exit
unsigned int reg[3];

int main()
{
 unsigned int ip = 0;
 int vm_running = 1;

 printf("reg0 reg1 reg2\n");
 while(vm_running)
 {
     ip++;
     switch(sample[ip-1])
     {
      case MOV:
          reg[sample[ip]] = sample[ip+1];
          ip++;
          break;
      case ADD:
          reg[sample[ip]] =
          reg[sample[ip+1]] + reg[sample[ip+2]];
          ip++;
          break;
      case EXIT:
          vm_running = 0;
          break;
 }
 if(sample[ip-1] % 2 != 0)
     printf(" %d %d %d\n", reg[0],reg[1],reg[2]);
 }
 return 0;
}

Bardzo prosta implementacja z przykładowym bytecodem – register machine

 

Po uruchomieniu:

reg0  reg1   reg2

   1        0        0

   1        2        0

   1        2        3

   5        2        3

Co wybieram ja? Hmm. Obie, fajnie jest operować na rejestrach, ale stos również się na pewno przyda chociaż by do przechowania czegoś.
Należy zastanowić się nad “specyfikacją” naszej maszyny, budową opkodów, zestawem instrukcji i czymś o czym zapomniałem a co na pewno się przyda. Wszystkie linijki kodu które popełnię powinny znaleźć się na moim githubie. W przypadku gdybym wypisywał głupoty, niezwłocznie proszę mnie poinformować.

,

0 thoughts on “0x01 – Stack OR/AND Registers”

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *