{"id":12802,"date":"2022-02-18T10:00:33","date_gmt":"2022-02-18T09:00:33","guid":{"rendered":"https:\/\/sii.pl\/blog\/?p=12802"},"modified":"2023-01-02T14:38:51","modified_gmt":"2023-01-02T13:38:51","slug":"maszyny-wirtualne-interpretery-czesc-ii-implementacja","status":"publish","type":"post","link":"https:\/\/sii.pl\/blog\/maszyny-wirtualne-interpretery-czesc-ii-implementacja\/","title":{"rendered":"Maszyny wirtualne \u2013 interpretery. Cz\u0119\u015b\u0107 II \u2013 Implementacja"},"content":{"rendered":"\n<p>W tym artykule zajmiemy si\u0119 implementacj\u0105 maszyny wirtualnej, zgodnej <a href=\"https:\/\/sii.pl\/blog\/maszyny-wirtualne-interpretery-czesc-i-architektura\/?category=development-na-twardo&amp;tag=embedded,interpreter,maszyna-wirtualna\">z postawionymi w poprzedniej cz\u0119\u015bci za\u0142o\u017ceniami oraz architektur\u0105.<\/a> Implementacj\u0119 zrealizujemy w j\u0119zyku programowania C. Kod b\u0119dzie napisany w taki spos\u00f3b, aby mo\u017cliwe by\u0142o uruchomienie maszyny na PC oraz mikrokontrolerach.<\/p>\n\n\n\n<!--more-->\n\n\n\n<p>Interpreter b\u0119dzie sk\u0142ada\u0142 si\u0119 z dw\u00f3ch plik\u00f3w: \u017ar\u00f3d\u0142owego i nag\u0142\u00f3wkowego. Oba te pliki b\u0119dzie mo\u017cna do\u0142\u0105czy\u0107 do dowolnego projektu.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Plik nag\u0142\u00f3wkowy<\/h2>\n\n\n\n<p>Plik ten zawiera:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>definicje typ\u00f3w,<\/li><li>makra,<\/li><li>interfejsy wirtualnej maszyny.<\/li><\/ul>\n\n\n\n<p>Ze wzgl\u0119du na to, \u017ce interpreter posiada 16 rejestr\u00f3w roboczych oraz 3 steruj\u0105ce, poni\u017csze makra u\u0142atwi\u0105 nawigacj\u0119 po nich.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#define VM_REG_GENERAL_COUNT    16\n#define VM_REG_CONTROL_COUNT    3\t\/* DO NOT change number of registers. *\/\n#define VM_REG_TOTAL_COUNT      (VM_REG_GENERAL_COUNT + VM_REG_CONTROL_COUNT)\n\n#define VM_REG_PC               (VM_REG_GENERAL_COUNT + 0)\n#define VM_REG_SR               (VM_REG_GENERAL_COUNT + 1)\n#define VM_REG_SP               (VM_REG_GENERAL_COUNT + 2)\n<\/pre><\/div>\n\n\n<p>Pierwsze makro odpowiada za okre\u015blenie ilo\u015bci dost\u0119pnych rejestr\u00f3w og\u00f3lnego przeznaczenia, natomiast drugie okre\u015bla ilo\u015b\u0107 rejestr\u00f3w kontrolnych. Ilo\u015b\u0107 rejestr\u00f3w kontrolnych nie powinna by\u0107 zmieniana ze wzgl\u0119du na implementacj\u0119 oraz okre\u015blone przeznaczenie.<\/p>\n\n\n\n<p>Na podstawie zadanej ilo\u015bci rejestr\u00f3w, okre\u015blane s\u0105 kolejno numery rejestr\u00f3w: <em>PC<\/em>, <em>SR<\/em> i <em>SP<\/em>.<\/p>\n\n\n\n<p>W pliku nag\u0142\u00f3wkowym zdefiniowano r\u00f3wnie\u017c poszczeg\u00f3lne bity rejestru statusu SR za pomoc\u0105 typu wyliczeniowego:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nenum {\n    VM_REG_SR_Z           = 0x001,\t\/** Zero flag. *\/\n    VM_REG_SR_NZ          = 0x002,\t\/** Not zero flag. *\/\n    VM_REG_SR_EQ          = 0x004,\t\/** Equal flag. *\/\n    VM_REG_SR_NE          = 0x008,\t\/** Not equal flag. *\/\n    VM_REG_SR_GT          = 0x010,\t\/** Greater than flag. *\/\n    VM_REG_SR_LT          = 0x020,\t\/** Less than flag. *\/\n    VM_REG_SR_ME          = 0x040,\t\/** Memory error flag. *\/\n    VM_REG_SR_DZ          = 0x080,\t\/** Division by zero flag. *\/\n    VM_REG_SR_CMP_MASK    = 0x03F,\t\/** Comparison mask. *\/\n};\n<\/pre><\/div>\n\n\n<p>Definicja ta jest przydatna podczas implementacji maszyny oraz wywo\u0142a\u0144 systemowych po stronie klienta. Z rejestru statusu korzystaj\u0105 instrukcje por\u00f3wna\u0144 (<strong>cmp<\/strong> i <strong>cmps<\/strong>).<\/p>\n\n\n\n<p>Jednym z za\u0142o\u017ce\u0144 architektury jest mo\u017cliwo\u015b\u0107 uruchomienia dowolnej ilo\u015bci interpreter\u00f3w. W tym celu zosta\u0142 utworzony typ danych <em>vm_t<\/em>, kt\u00f3ry to przechowuje stan maszyny wirtualnej. Ka\u017cdy interfejs dost\u0119pny z poziomu klienta musi przekazywa\u0107 wska\u017anik na obiekt interpretera. Podej\u015bcie to wprowadza elastyczno\u015b\u0107 oraz niezale\u017cno\u015b\u0107 wykonywanych zada\u0144. Typ ten jest zdefiniowany nast\u0119puj\u0105co:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\ntypedef struct vm_s {\n    uint32_t regs&#x5B;VM_REG_TOTAL_COUNT];\n    void     (*read) (struct vm_s *vm, uint32_t address, uint8_t *data, uint8_t size);\n    void     (*write)(struct vm_s *vm, uint32_t address, const uint8_t *data, uint8_t size);\n    uint32_t (*sys)  (struct vm_s *vm, uint32_t arg0, uint32_t arg1);\n    void *user;\n} vm_t;\n<\/pre><\/div>\n\n\n<p>Na pierwszy rzut oka mo\u017cna tutaj zauwa\u017cy\u0107 pole <em>regs<\/em>. Odpowiedzialne jest za przechowywanie warto\u015bci poszczeg\u00f3lnych rejestr\u00f3w. Na tych danych b\u0119d\u0105 wykonywane wszelakie operacje arytmetyczne, logiczne itp. Nast\u0119pnymi elementami s\u0105 interfejsy interpretera \u2013 kolejno: do pobierania danych z pami\u0119ci (<em>read<\/em>), zapisywania danych do pami\u0119ci (<em>write<\/em>) oraz wywo\u0142ania funkcji systemowej (<em>sys<\/em>).<\/p>\n\n\n\n<p>Dodano r\u00f3wnie\u017c definicje typ\u00f3w interfejs\u00f3w maszyny w celu \u0142atwiejsze integracji maszyny:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\ntypedef void     (*vm_read) (vm_t *vm, uint32_t address, uint8_t *data, uint8_t size);\ntypedef void     (*vm_write)(vm_t *vm, uint32_t address, const uint8_t *data, uint8_t size);\ntypedef uint32_t (*vm_sys)  (vm_t *vm, uint32_t arg0, uint32_t arg1);\n<\/pre><\/div>\n\n\n<p>Ostatnimi elementami w pliku nag\u0142\u00f3wkowym s\u0105 funkcje interfejsowe, to dzi\u0119ki nim mo\u017cliwa b\u0119dzie interakcja z interpreterem.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nextern void vm_init(vm_t *vm, vm_read read, vm_write write, vm_sys sys, void *user);\nextern bool vm_step(vm_t *vm);\nextern void vm_irq (vm_t *vm, uint32_t address);\n<\/pre><\/div>\n\n\n<p>Funkcja <em>vm_init() <\/em>s\u0142u\u017cy do inicjalizacji obiektu maszyny wirtualnej,\u00a0 <em>vm_step()<\/em> wykonuje jedn\u0105 instrukcj\u0119, a <em>vm_irq()<\/em> pozwala na wywo\u0142anie przerwania.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Plik \u017ar\u00f3d\u0142owy<\/h2>\n\n\n\n<p>W tym punkcie przedstawi\u0119 implementacj\u0119 poszczeg\u00f3lnych funkcji. Nie wszystkie funkcje b\u0119d\u0105 om\u00f3wione szczeg\u00f3\u0142owo ze wzgl\u0119du na ograniczon\u0105 d\u0142ugo\u015b\u0107 artyku\u0142u. Implementacja maszyny wirtualnej jest do pobrania w za\u0142\u0105cznikach pod artyku\u0142em. Interpreter zosta\u0142 zrealizowany przy u\u017cyciu oko\u0142o 300 linii kodu (kod + komentarze).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Funkcja vm_init()<\/h3>\n\n\n\n<p>Na wst\u0119pie przeanalizujemy funkcj\u0119&nbsp;<em>vm_init()<\/em>. Ta funkcja wywo\u0142ywana jest podczas inicjalizacji obiektu maszyny. Jej zadaniem jest ustawienie poszczeg\u00f3lnych p\u00f3l na odpowiednie warto\u015bci. W tym przypadku funkcja ustawia funkcje interfejsowe oraz wska\u017anik na dane klienta. Je\u015bli chodzi o interfejsy \u2013 nie ma tutaj raczej zaskoczenia. Jednak\u017ce pole&nbsp;<em>user<\/em>&nbsp;wymaga wyja\u015bnienia.<\/p>\n\n\n\n<p>Interpreter nie korzysta z tego pola bezpo\u015brednio, jest to parametr opcjonalny, kt\u00f3ry mo\u017ce zosta\u0107 wykorzystany na potrzeby implementacji np. w funkcji&nbsp;<em>vm_read()<\/em>&nbsp;jak parametr konfiguracyjny. Znacz\u0105co zwi\u0119ksza to elastyczno\u015b\u0107 i u\u0142atwia w niekt\u00f3rych przypadkach implementacj\u0119 interfejs\u00f3w. Funkcja&nbsp;<em>vm_init()<\/em>&nbsp;przedstawia si\u0119 nast\u0119puj\u0105co:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nvoid vm_init(vm_t *vm, vm_read read, vm_write write, vm_sys sys, void *user)\n{\n    vm-&gt;read = read;\n    vm-&gt;write = write;\n    vm-&gt;sys = sys;\n    vm-&gt;user = user;\n}\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">Funkcja vm_irq()<\/h3>\n\n\n\n<p>Kolejn\u0105 omawian\u0105 funkcj\u0105 jest\u00a0<em>vm_irq()<\/em>. Funkcja ta s\u0142u\u017cy do wywo\u0142ywania przerwania. Aktualnie wykonywany program zostanie przerwany, a interpreter wykona skok do okre\u015blonego adresu. Adres jest zdefiniowany przez klienta \u2013 proponuj\u0119 tutaj zdefiniowanie odpowiedniej tablicy skok\u00f3w. Ze wzgl\u0119du na to, \u017ce przerwania wykorzystuj\u0105 ten sam stos, funkcja obs\u0142ugi przerwania musi posiada\u0107 odpowiedni\u0105 instrukcj\u0119 do wyj\u015bcia z przerwania. W og\u00f3lnym przypadku jest to\u00a0<strong>pop PC.<\/strong><\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nvoid vm_irq(vm_t *vm, uint32_t address)\n{\n    \/* push on stack current PC *\/\n    vm-&gt;write(vm, vm_sp_reg, (uint8_t*)&amp;vm_pc_reg, sizeof(uint32_t));\n    vm_sp_reg += sizeof(uint32_t);\n    vm_pc_reg = address;\n}\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">Funkcja vm_step()<\/h3>\n\n\n\n<p>Nasza implementacja w zasadzie posiada jeszcze jedn\u0105, ostatni\u0105 funkcj\u0119&nbsp;<em>vm_step()<\/em>. Jest to najd\u0142u\u017csza i najwa\u017cniejsza funkcja, poniewa\u017c tutaj dokonuj\u0105 si\u0119 interpretacje instrukcji (obliczenia, skoki, od\u0142o\u017cenie rejestr\u00f3w na stos etc.). Funkcja ta zostanie om\u00f3wiona we fragmentach. Po szczeg\u00f3\u0142y odsy\u0142am do za\u0142\u0105czonego kodu.<\/p>\n\n\n\n<p>Funkcja na pocz\u0105tku deklaruje zmienne, po czym przechodzi do odczytu instrukcji z pami\u0119ci.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nbool vm_step(vm_t *vm)\n{\n    uint8_t reg = 0xFF;\n    uint32_t arg0;\n    uint32_t arg1;\n    uint8_t opcode;\n    uint8_t step = 2;\n    bool status = true;\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n\/\/ get opcode\n    vm-&gt;read(vm, vm_pc_reg, &amp;opcode, sizeof(opcode));\n    vm_pc_reg += sizeof(opcode);\n\n    ...\n}\n<\/pre><\/div>\n\n\n<h4 class=\"wp-block-heading\" style=\"font-style:normal;font-weight:400\">Odczyt instrukcji<\/h4>\n\n\n\n<p>Odczyt instrukcji dokonywany jest za pomoc\u0105 interfejsu&nbsp;<em>read()<\/em>. W zasadzie dokonuje si\u0119 tutaj odczytu jednego bajtu danych znajduj\u0105cego si\u0119 w pami\u0119ci pod adresem wskazanym przez rejestr&nbsp;<em>PC<\/em>. Po operacji odczytu nast\u0119puje inkrementacja pozycji programu (PC) o 1.<\/p>\n\n\n\n<p>Ze wzgl\u0119du na to, \u017ce maszyna obs\u0142uguje wy\u0142\u0105cznie instrukcje jedno- lub dwuargumentowe, pobrany bajt zawiera informacje o ilo\u015bci argument\u00f3w. Na potrzeby implementacji wprowad\u017amy w&nbsp;<em>opcode<\/em>&nbsp;dodatkowy bit okre\u015blaj\u0105cy ilo\u015b\u0107 argument\u00f3w. Bajt&nbsp;<em>opcode<\/em>&nbsp;prezentuje si\u0119 teraz nast\u0119puj\u0105co:<\/p>\n\n\n\n<p>Instrukcja jednoargumentowa:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Bit<\/th><th>7-2<\/th><th>1<\/th><th>0<\/th><\/tr><\/thead><tbody><tr><td><strong>Opis<\/strong><\/td><td>Kod instrukcji<\/td><td>Argument 0.<br>Rejestr: 1; sta\u0142a: 0<\/td><td>Ilo\u015b\u0107 argument\u00f3w.<br>Jeden: 0; dwa: 1<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Instrukcja dwuargumentowa:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Bit<\/th><th>7-3<\/th><th>2<\/th><th>1<\/th><th>0<\/th><\/tr><\/thead><tbody><tr><td><strong>Opis<\/strong><\/td><td>Kod instrukcji<\/td><td>Argument 0.<br>Rejestr: 1; sta\u0142a: 0<\/td><td>Argument 1.<br>Rejestr: 1; sta\u0142a: 0<\/td><td>Ilo\u015b\u0107 argument\u00f3w.<br>Jeden: 0; dwa: 1<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Dzi\u0119ki tej zmianie implementacja upraszcza si\u0119, poniewa\u017c mo\u017cliwe sta\u0142o si\u0119 \u0142adowanie odpowiednich argument\u00f3w w p\u0119tli, tak jak pokazano to w kodzie:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nbool vm_step(vm_t *vm)\n{\n    ...\n\n    \/\/ number of arguments\n    step = (opcode &amp; 0x01) + 1;\n    opcode &gt;&gt;= 1;\n    \/\/ get arguments\n    while (step &gt; 0) {\n        arg1 = arg0;\n        if ((opcode &amp; 0x01) != 0) {\n            vm-&gt;read(vm, vm_pc_reg, \u00ae, sizeof(reg));\n            vm_pc_reg += sizeof(reg);\n            arg0 = vm-&gt;regs&#x5B;reg];\n        } else {\n            vm-&gt;read(vm, vm_pc_reg, (uint8_t*)&amp;arg0, sizeof(arg0));\n            vm_pc_reg += sizeof(arg0);\n        }\n        opcode &gt;&gt;= 1;\n        step--;\n    }\n\n    ...\n}\n<\/pre><\/div>\n\n\n<p>Dalszy kod funkcji jest odpowiedzialny za wykonanie poszczeg\u00f3lnych instrukcji. Ca\u0142o\u015b\u0107 opiera si\u0119 o warunek wielokrotnego wyboru (switch). Poni\u017cszy przyk\u0142ad przedstawia implementacj\u0119 kilku instrukcji arytmetycznych:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nbool vm_step(vm_t *vm)\n{\n    ...\n\n    switch (opcode)\n    {\n        case VM_OPCODE_NOT: arg0 = ~arg0; break;\n        case VM_OPCODE_NEG: arg0 = -arg0; break;\n        case VM_OPCODE_ADD: arg0 += arg1; break;\n        case VM_OPCODE_SUB: arg0 -= arg1; break;\n        case VM_OPCODE_MUL: arg0 *= arg1; break;\n        ...\n    }\n    ...\n}\n<\/pre><\/div>\n\n\n<p>Jak wida\u0107, w przypadku tych instrukcji implementacja jest bardzo prosta.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" style=\"font-style:normal;font-weight:400\">Pozosta\u0142e instrukcje<\/h4>\n\n\n\n<p>Inne instrukcje, np. por\u00f3wnywanie warto\u015bci, s\u0105 nieco bardziej z\u0142o\u017cone:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\ncase VM_OPCODE_CMP:\n    vm_sr_reg &amp;= ~VM_REG_SR_CMP_MASK;\n    if (arg0 == arg1)\n        vm_sr_reg |= VM_REG_SR_EQ;\n    else\n        vm_sr_reg |= VM_REG_SR_NE;\n\n    if (arg0 &gt; arg1)\n        vm_sr_reg |= VM_REG_SR_GT;\n\n    if (arg0 &lt; arg1)\n        vm_sr_reg |= VM_REG_SR_LT;\n\n    reg = 0xFF;\nbreak;\n<\/pre><\/div>\n\n\n<p>Ze wzgl\u0119du na ilo\u015b\u0107 instrukcji, nie b\u0119d\u0119 omawia\u0142 ich wszystkich, odsy\u0142am do za\u0142\u0105czonego kodu.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" style=\"font-style:normal;font-weight:400\">Aktualizacja rejestru statusu<\/h4>\n\n\n\n<p>Funkcja<em> vm_step()<\/em> ko\u0144czy si\u0119 poni\u017cszym kodem:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nbool vm_step(vm_t *vm)\n{\n    ...\n\n    vm_sr_reg &amp;= ~(VM_REG_SR_Z | VM_REG_SR_NZ);\n    if (arg0 == 0)\n        vm_sr_reg |= VM_REG_SR_Z;\n    else\n        vm_sr_reg |= VM_REG_SR_NZ;\n\n    if (reg &lt; VM_REG_TOTAL_COUNT)\n        vm-&gt;regs&#x5B;reg] = arg0;\n\n    return status;\n}\n<\/pre><\/div>\n\n\n<p>Ten fragment kodu odpowiedzialny jest za aktualizacj\u0119 rejestru statusu. Sprawdzany jest tutaj argument zerowy (tylko flagi Z i NZ). Z punktu widzenia tworzenia programu, informacja ta jest szczeg\u00f3lnie istotna. Dzi\u0119ki niej mo\u017cliwa jest redukcja ilo\u015bci u\u017cytych instrukcji.<\/p>\n\n\n\n<p>Dla przyk\u0142adu, podczas kopiowania danych algorytm powinien zako\u0144czy\u0107 prac\u0119, je\u015bli ilo\u015b\u0107 element\u00f3w pozosta\u0142ych do skopiowania wynosi 0. Je\u015bli instrukcja dekrementacji (np. <strong>sub<\/strong>) b\u0119dzie zmniejsza\u0107 ilo\u015b\u0107 pozosta\u0142ych element\u00f3w do skopiowania, flagi Z i NZ b\u0119d\u0105 si\u0119 odpowiednio ustawia\u0107 w zale\u017cno\u015bci od wyniku operacji odejmowania. Je\u015bli warto\u015b\u0107 b\u0119dzie wynosi\u0107 0, wtedy program przerwie operacj\u0119 (przy u\u017cyciu instrukcji <strong>br<\/strong>).<\/p>\n\n\n\n<p>Je\u015bli chodzi o implementacj\u0119 maszyny wirtualnej to ju\u017c wszystko. Zach\u0119cam do przejrzenia plik\u00f3w \u017ar\u00f3d\u0142owych.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Integracja maszyny<\/h2>\n\n\n\n<p>U\u017cycie maszyny jest proste. Wystarczy do\u0142\u0105czy\u0107 do projektu dwa pliki\u00a0<em>vm.c<\/em>\u00a0oraz\u00a0<em>vm.h<\/em>. Jednak do\u0142\u0105czenie plik\u00f3w nie spowoduje, \u017ce maszyna b\u0119dzie od razu dzia\u0142a\u0107. W tym celu nale\u017cy przeprowadzi\u0107 integracj\u0119. Dodatkowo, nale\u017cy rozszerzy\u0107 funkcjonalno\u015b\u0107 maszyny o mo\u017cliwo\u015b\u0107 komunikacji ze \u015bwiatem. Bez tego nie b\u0119dzie mo\u017cliwa interakcja z maszyn\u0105, a co za tym idzie \u2013 brak widocznych efekt\u00f3w przetwarzania.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Do\u0142\u0105czanie plik\u00f3w nag\u0142\u00f3wkowych<\/h3>\n\n\n\n<p>Na pocz\u0105tku zacznijmy od do\u0142\u0105czenia potrzebnych plik\u00f3w nag\u0142\u00f3wkowych:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;stdint.h&gt;\n#include &quot;vm.h&quot;\n<\/pre><\/div>\n\n\n<p>P\u00f3ki co \u2013 nic szczeg\u00f3lnego, standard. Aby mo\u017cliwa by\u0142a praca maszyny, musimy zdefiniowa\u0107 obszar pami\u0119ci, na kt\u00f3rej b\u0119dzie ona operowa\u0107. Dla przyk\u0142adu, utworzy\u0142em blok pami\u0119ci jako tablic\u0119:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#define MEMORY_SIZE     4096\nuint8_t memory&#x5B;MEMORY_SIZE];\n<\/pre><\/div>\n\n\n<p>Rozmiar mo\u017ce ulec zmianie, mo\u017ce r\u00f3wnie\u017c by\u0107 dynamicznie alokowany, wszystko zale\u017cy od zastosowania. W niekt\u00f3rych implementacjach bufor taki mo\u017ce okaza\u0107 si\u0119 zb\u0119dny \u2013 np. maszyna mo\u017ce pobiera\u0107 dane z UART-a lub innego medium. Wszystko zale\u017cne jest od przypadku u\u017cycia.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Implementacja funkcji interfejsowych<\/h3>\n\n\n\n<p>Za\u0142\u00f3\u017cmy, \u017ce nasza maszyna korzysta z bloku pami\u0119ci. Aby mo\u017cliwy sta\u0142 si\u0119 dost\u0119p do zasob\u00f3w, nale\u017cy zaimplementowa\u0107 dwie funkcje interfejsowe: <em>vm_read()<\/em> i <em>vm_write()<\/em>.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nvoid memread(vm_t *vm, uint32_t address, uint8_t *data, uint8_t size)\n{\n    while (size-- &gt; 0) {\n        if (address &lt; MEMORY_SIZE) {\n            *data++ = memory&#x5B;address++];\n        } else {\n            vm-&gt;regs&#x5B;VM_REG_SR] |= VM_REG_SR_ME;\n            break;\n        }\n    }\n}\n\nvoid memwrite(vm_t *vm, uint32_t address, const uint8_t *data, uint8_t size)\n{\n    while (size-- &gt; 0) {\n        if (address &lt; MEMORY_SIZE) {\n            memory&#x5B;address++] = *data++;\n        } else {\n            vm-&gt;regs&#x5B;VM_REG_SR] |= VM_REG_SR_ME;\n            break;\n        }\n    }\n}\n<\/pre><\/div>\n\n\n<p>Jak wida\u0107, obie funkcje korzystaj\u0105 z wcze\u015bniej utworzonego bloku pami\u0119ci <em>memory. <\/em>Wystarczy nieco ruszy\u0107 wyobra\u017ani\u0105, aby spostrzec, \u017ce w zasadzie dane czytane i zapisywane przez interpreter mog\u0105 mie\u0107 r\u00f3\u017cne \u017ar\u00f3d\u0142a. Wszystko zale\u017cy od implementacji.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Funkcja vm_syscall()<\/h3>\n\n\n\n<p>Podstawowa integracja zosta\u0142a zako\u0144czona. W zasadzie wystarczy\u0142oby to, aby mo\u017cliwa by\u0142a interakcja z maszyn\u0105 wirtualn\u0105, w tym wypadku, tylko poprzez pami\u0119\u0107. Co prawda niezbyt wygodna, ale zawsze jaka\u015b.<\/p>\n\n\n\n<p>Wygodniej jest jednak skorzysta\u0107 z funkcji:&nbsp;<em>vm_syscall()<\/em>. Funkcja ta jest dedykowana do wykonywania operacji systemowych. Mo\u017cemy tutaj podpi\u0105\u0107 dowolne operacje, kt\u00f3re s\u0105 niezb\u0119dne do interakcji z np. system operacyjnym lub programem g\u0142\u00f3wnym.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nuint32_t vm_syscall(vm_t *vm, uint32_t arg0, uint32_t arg1)\n{\n    uint32_t ret = 0;\n\n    switch (arg1) {\n        case SYSCALL_PUTC:\n            putc(arg0, stdout);\n            break;\n        case SYSCALL_GETC:\n            ret = getc(stdin);\n            break;\n        default:\n            break;\n    }\n\n    return ret;\n}\n<\/pre><\/div>\n\n\n<p>Funkcja ta przyjmuje 3 argumenty:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>obiekt wirtualnej maszyny,<\/li><li>argument 0,<\/li><li>argument 1 (numer wywo\u0142ania systemowego).<\/li><\/ul>\n\n\n\n<p>Oba argumenty przekazuj\u0105 warto\u015b\u0107, kt\u00f3ra zosta\u0142a przekazana przez instrukcj\u0119 <strong>sys<\/strong>. Zawsze jest to warto\u015b\u0107 rejestru (nie numer rejestru) lub sta\u0142ej. Jak wida\u0107, zdefiniowali\u015bmy dwa wywo\u0142ania systemowe:&nbsp; <em>SYSCALL_PUTC<\/em> oraz&nbsp; <em>SYSCALL_GETC<\/em>. Definicja kod\u00f3w poszczeg\u00f3lnych wywo\u0142a\u0144 zdefiniowana jest za pomoc\u0105 typu wyliczeniowego.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nenum {\n    SYSCALL_PUTC,\n    SYSCALL_GETC\n};\n<\/pre><\/div>\n\n\n<p>Oczywi\u015bcie, numer\/kody wywo\u0142a\u0144 systemowych mog\u0105 by\u0107 dowoln\u0105 liczb\u0105 32-bitow\u0105.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Implementacja main()<\/h3>\n\n\n\n<p>Teraz przyszed\u0142 czas na <em>main()<\/em>. Implementacja jest stosunkowo prosta:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nint main(int argc, char argv&#x5B;])\n{\n    vm_t vm;\n    \n    vm_init(&vm, memread, memwrite, vm_syscall, NULL);\n    \n    while (vm_step(&vm));\n    \n    return 0;\n}\n<\/pre><\/div>\n\n\n<p>Aby kompilacja by\u0142a przyjemniejsza, doda\u0142em skrypt CMake\u2019a:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ncmake_minimum_required(VERSION 3.0)\nproject(vm LANGUAGES C)\nadd_executable(vm main.c vm.c)\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">Pierwsze uruchomienie<\/h2>\n\n\n\n<p>Aby zbudowa\u0107 przyk\u0142ad, nale\u017cy w katalogu, w kt\u00f3rym znajduj\u0105 si\u0119 pliki, utworzy\u0107 katalog <em>build<\/em>. Nast\u0119pnie z katalogu <em>build<\/em> wywo\u0142a\u0107 Cmake\u2019a i kolejno <em>make<\/em>. Jak pokazano poni\u017cej:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nmkdir build\ncd build\ncmake ..\nmake\n<\/pre><\/div>\n\n\n<p>Je\u015bli \u015brodowisko jest poprawnie skonfigurowane, proces budowania powinien przej\u015b\u0107 pomy\u015blnie.<\/p>\n\n\n\n<p>W wyniku budowania powstanie plik wykonywalny <em>vm<\/em>. Mo\u017cna by ju\u017c uruchomi\u0107 nasz\u0105 maszyn\u0119, ale jak \u0142atwo si\u0119 mo\u017cna domy\u015bli\u0107, nie b\u0119dzie \u017cadnego efektu lub efekt b\u0119dzie losowy (przypadkowe dane w pami\u0119ci).<\/p>\n\n\n\n<p>Nale\u017cy zatem napisa\u0107 program testowy <em>Hello World!<\/em>. Problem polega na tym, \u017ce, p\u00f3ki co, nie posiadamy kompilatora, kt\u00f3ry to b\u0119dzie omawiany w kolejnej cz\u0119\u015bci artyku\u0142u.<\/p>\n\n\n\n<p>\u017beby jednak nie pozostawi\u0107 czytelnik\u00f3w bez wynik\u00f3w uruchomienia naszej wirtualnej maszyny, za\u0142\u0105czam program testowy w formie binarnej. Sprawd\u017acie sami, jaki jest wynik:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nuint8_t memory&#x5B;MEMORY_SIZE] = {\n    0x8d, 0x06, 0x00, 0x00, 0x00, 0x10, 0x8d, 0x80, \n    0x00, 0x00, 0x00, 0x12, 0x8d, 0x42, 0x00, 0x00, \n    0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xff, \n    0xd5, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x97, 0x00, \n    0x0f, 0xe9, 0x12, 0x00, 0x00, 0x00, 0x01, 0x00,\n    0x00, 0x00, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x0f, \n    0x2d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2d, 0xe2, \n    0xff, 0xff, 0xff, 0x10, 0xdd, 0x02, 0x00, 0x00, \n    0x00, 0x0f, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, \n    0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x0a, 0x00, \n};\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">Za\u0142\u0105czniki do pobrania:<\/h3>\n\n\n\n<p><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2022\/08\/vm.zip\" target=\"_blank\" rel=\"noreferrer noopener\">Pliki maszyny<\/a><\/p>\n\n\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-bottom\"\n    data-payload='{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;12802&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;bottom&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;4&quot;,&quot;legendonly&quot;:&quot;&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;5&quot;,&quot;starsonly&quot;:&quot;&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;11&quot;,&quot;greet&quot;:&quot;&quot;,&quot;legend&quot;:&quot;5\\\/5 ( votes: 4)&quot;,&quot;size&quot;:&quot;18&quot;,&quot;title&quot;:&quot;Maszyny wirtualne \u2013 interpretery. Cz\u0119\u015b\u0107 II \u2013 Implementacja&quot;,&quot;width&quot;:&quot;139.5&quot;,&quot;_legend&quot;:&quot;{score}\\\/{best} ( {votes}: {count})&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}'>\n            \n<div class=\"kksr-stars\">\n    \n<div class=\"kksr-stars-inactive\">\n            <div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n    \n<div class=\"kksr-stars-active\" style=\"width: 139.5px;\">\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n                \n\n<div class=\"kksr-legend\" style=\"font-size: 14.4px;\">\n            5\/5 ( votes: 4)    <\/div>\n    <\/div>\n","protected":false},"excerpt":{"rendered":"<p>W tym artykule zajmiemy si\u0119 implementacj\u0105 maszyny wirtualnej, zgodnej z postawionymi w poprzedniej cz\u0119\u015bci za\u0142o\u017ceniami oraz architektur\u0105. Implementacj\u0119 zrealizujemy w &hellip; <a class=\"continued-btn\" href=\"https:\/\/sii.pl\/blog\/maszyny-wirtualne-interpretery-czesc-ii-implementacja\/\">Continued<\/a><\/p>\n","protected":false},"author":248,"featured_media":12196,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_editorskit_title_hidden":false,"_editorskit_reading_time":0,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","inline_featured_image":false,"footnotes":""},"categories":[1314],"tags":[563,1169,1168],"class_list":["post-12802","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development-na-twardo","tag-embedded","tag-interpreter","tag-maszyna-wirtualna"],"acf":[],"aioseo_notices":[],"republish_history":[],"featured_media_url":"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2021\/11\/Maszyny-wirtualne-interpretery.-Czesc-I-Architektura.png","category_names":["Development na twardo"],"_links":{"self":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/12802"}],"collection":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/users\/248"}],"replies":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/comments?post=12802"}],"version-history":[{"count":2,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/12802\/revisions"}],"predecessor-version":[{"id":18137,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/12802\/revisions\/18137"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media\/12196"}],"wp:attachment":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media?parent=12802"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/categories?post=12802"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/tags?post=12802"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}