├── LICENSE
├── README.md
├── assets
├── development.png
└── device.gif
├── bin
├── example.bin
├── example.c
└── example.hex
├── evm-esp32.ino
├── include
├── opcodes.h
└── vm.h
├── opcodes.ino
├── paper
├── ICEST-2022-5014.pdf
├── Integrating a Virtual Machine on a system-on-a-chip.docx
├── Integrating a Virtual Machine on a system-on-a-chip.odp
└── diagram.drawio
└── vm.ino
/LICENSE:
--------------------------------------------------------------------------------
1 | evm-esp32
2 | Copyright (C) 2014, 2022 Boro Sitnikovski
3 |
4 | This program is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | This program is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with this program. If not, see .
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | evm-esp32
2 | =========
3 |
4 | A virtual machine for ESP32, allowing dynamic program updates.
5 |
6 | Based on my [implementation of CHIP-8](https://github.com/bor0/chip-8), where the gaming related instructions are removed, and instead instructions for IO pin controlling are added.
7 |
8 | The following instructions that are related to games (drawing, etc.) are removed:
9 |
10 | - `00E0`
11 | - `DXYN`
12 | - `EX9E`
13 | - `EXA1`
14 | - `FX0A`
15 | - `FX18`
16 | - `FX29`
17 |
18 | The following instructions that are related to IO control are added:
19 |
20 | - `E0A0` - sleep for second register's value milliseconds
21 | - `EXA1` - sets the pin mode X to the pin (value in first register)
22 | - `E0A2` - does `analogRead` to the first register and stores the result in the second register
23 | - `E0A3` - does `analogWrite` to the first register using the value from the second register
24 |
25 | Registers are changed from 8 bit to 16 bit.
26 |
27 | In order to reload a program, use the `0000` instruction to halt the VM and restart.
28 |
29 | Tested on ESP32 Arduino (WROOM32).
30 |
31 | Prerequisites:
32 |
33 | - Install analogWrite polyfill
34 |
35 | References:
36 |
37 | - http://en.wikipedia.org/wiki/CHIP-8
38 | - https://github.com/bor0/chip-8
39 | - https://ieeexplore.ieee.org/document/9828788
40 | - https://hackaday.com/2022/02/27/esp32-virtual-machine-lets-you-change-programs-on-the-fly/
41 |
42 | Boro Sitnikovski
43 |
--------------------------------------------------------------------------------
/assets/development.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bor0/evm-esp32/519c0b88c4cf924d9a13d64800c0e1e3d3aa44d2/assets/development.png
--------------------------------------------------------------------------------
/assets/device.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bor0/evm-esp32/519c0b88c4cf924d9a13d64800c0e1e3d3aa44d2/assets/device.gif
--------------------------------------------------------------------------------
/bin/example.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bor0/evm-esp32/519c0b88c4cf924d9a13d64800c0e1e3d3aa44d2/bin/example.bin
--------------------------------------------------------------------------------
/bin/example.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | void setup() {
4 | pinMode(LED_BUILTIN, OUTPUT);
5 | }
6 |
7 | void loop() {
8 | analogWrite(LED_BUILTIN, 255);
9 | delay(1000);
10 | analogWrite(LED_BUILTIN, 0);
11 | delay(1000);
12 | }
13 |
--------------------------------------------------------------------------------
/bin/example.hex:
--------------------------------------------------------------------------------
1 | 6002 ; V[0] = 2 (LED_BUILTIN for ESP WROOM32)
2 | E2A1 ; pinMode(V[0], 2); (OUTPUT for ESP WROOM32)
3 |
4 | 62FA ; V[2] = 250
5 | 823E ; V[2] = 500 (LSH)
6 | 823E ; V[2] = 1000 (LSH)
7 |
8 | 61FF ; V[1] = 255, loop begin
9 | E0A3 ; analogWrite(V[0], V[1]);
10 | E0A0 ; delay(V[2]);
11 |
12 | 6100 ; V[1] = 0
13 | E0A3 ; analogWrite(V[0], V[1]);
14 | E0A0 ; delay(V[2]);
15 |
16 | 120A ; jump to loop
17 |
--------------------------------------------------------------------------------
/evm-esp32.ino:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of evm-esp32.
3 |
4 | evm-esp32 is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | evm-esp32 is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with evm-esp32. If not, see .
16 |
17 | */
18 | #include
19 | #include
20 |
21 | #include
22 |
23 | #include "include/vm.h"
24 |
25 | // Sometimes downloading from GitHub causes the device to reboot. Best store on a local web server without https: https://raw.githubusercontent.com/bor0/evm-esp32/main/bin/example.bin
26 | // E.g. http://192.168.0.108:8080/example.bin
27 | char binary_url[255];
28 |
29 | void setup()
30 | {
31 | Serial.begin(115200);
32 | delay(10);
33 |
34 | Serial.println("Enter WiFi SSID (newline terminated): ");
35 | while (!Serial.available()) delay(10);
36 | String ssid = Serial.readStringUntil('\n');
37 |
38 | Serial.println("Enter WiFi password (newline terminated): ");
39 | while (!Serial.available()) delay(10);
40 | String password = Serial.readStringUntil('\n');
41 |
42 | Serial.println("Enter URL for binary (newline terminated): ");
43 | while (!Serial.available()) delay(10);
44 | String url = Serial.readStringUntil('\n');
45 |
46 | memcpy(binary_url, url.c_str(), sizeof(binary_url));
47 |
48 | Serial.print("Connecting to WiFi");
49 |
50 | WiFi.begin(ssid.c_str(), password.c_str());
51 |
52 | while (WiFi.status() != WL_CONNECTED) {
53 | delay(500);
54 | Serial.print(".");
55 | }
56 |
57 | Serial.println("\nConnected to WiFi");
58 | }
59 |
60 | void loop()
61 | {
62 | HTTPClient client;
63 | struct vm VM;
64 |
65 | printf("Requesting (extended) CHIP-8 binary\n");
66 |
67 | client.begin(binary_url);
68 | client.GET();
69 |
70 | printf("Initializing VM\n");
71 |
72 | vm_init(&VM, client.getString());
73 |
74 | printf("Entering VM loop\n");
75 |
76 | while (!VM.halt) {
77 | vm_cycle(&VM);
78 | delay(1);
79 | }
80 |
81 | printf("VM halted\n");
82 | }
83 |
--------------------------------------------------------------------------------
/include/opcodes.h:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of evm-esp32.
3 |
4 | evm-esp32 is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | evm-esp32 is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with evm-esp32. If not, see .
16 |
17 | */
18 | #ifndef OPCODES_H
19 | #define OPCODES_H
20 | #include "vm.h"
21 |
22 | void parse_opcode(struct vm *, uint16_t);
23 | #endif
24 |
--------------------------------------------------------------------------------
/include/vm.h:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of evm-esp32.
3 |
4 | evm-esp32 is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | evm-esp32 is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with evm-esp32. If not, see .
16 |
17 | */
18 | #ifndef VM_H
19 | #define VM_H
20 | #include
21 | #include
22 | #include
23 |
24 | struct vm {
25 | struct {
26 | /* 16 16-bit data registers named from V0 to VF. The VF register doubles as a carry flag. */
27 | uint16_t v[16];
28 |
29 | /* The address register is 16 bits wide and is used with several opcodes that involve memory operations. */
30 | uint16_t I;
31 |
32 | /* Program counter. */
33 | uint16_t pc;
34 |
35 | /* Stack pointer. */
36 | uint16_t sp;
37 | } registers;
38 |
39 | /* CHIP-8's memory addresses range from 200h to FFFh, making for 3,584 bytes. */
40 | uint8_t memory[0x0FFF];
41 |
42 | /* The original 1802 version allocated 48 bytes for up to 12 levels of nesting; modern implementations normally have at least 16 levels. */
43 | uint16_t stack[16];
44 |
45 | /* Halt variable. */
46 | uint8_t halt;
47 |
48 | /* Timers. */
49 | uint8_t delay_timer;
50 | };
51 |
52 | void vm_init(struct vm *, String);
53 | uint16_t calc_opcode(struct vm *);
54 | void vm_cycle(struct vm *);
55 | #endif
56 |
--------------------------------------------------------------------------------
/opcodes.ino:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of evm-esp32.
3 |
4 | evm-esp32 is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | evm-esp32 is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with evm-esp32. If not, see .
16 |
17 | */
18 | #include "include/opcodes.h"
19 | #include
20 |
21 | void parse_opcode(struct vm *VM, uint16_t opcode)
22 | {
23 | uint8_t i, vx, vy;
24 |
25 | switch (opcode) {
26 | case 0x00EE:
27 | /* Returns from a subroutine. */
28 | VM->registers.sp--;
29 | if (VM->registers.sp < 0) {
30 | printf("Fatal error: Stack underflow\n");
31 | VM->halt = 1;
32 | return;
33 | }
34 | VM->registers.pc = VM->stack[VM->registers.sp];
35 | return;
36 | break;
37 | case 0xE0A0:
38 | delay(VM->registers.v[2]);
39 | return;
40 | break;
41 | case 0xE0A2:
42 | /* Read from input (first register) into output (second register) */
43 | VM->registers.v[1] = analogRead(VM->registers.v[0]);
44 | return;
45 | break;
46 | case 0xE0A3:
47 | /* Write to pin (first register) a value (second register) */
48 | analogWrite(VM->registers.v[0], VM->registers.v[1]);
49 | return;
50 | break;
51 | default:
52 | break;
53 | }
54 |
55 | switch (opcode & 0xF000) {
56 | case 0x1000:
57 | /* Jumps to address NNN. */
58 | VM->registers.pc = opcode & 0x0FFF;
59 | return;
60 | break;
61 | case 0x2000:
62 | /* Calls subroutine at NNN. */
63 | if (VM->registers.sp > sizeof(VM->stack) / sizeof(uint16_t)) {
64 | printf("Fatal error: Stack overflow\n");
65 | VM->halt = 1;
66 | }
67 | VM->stack[VM->registers.sp] = VM->registers.pc;
68 | VM->registers.sp++;
69 | VM->registers.pc = opcode & 0x0FFF;
70 | return;
71 | break;
72 | case 0x3000:
73 | /* Skips the next instruction if VX equals NN. */
74 | if (VM->registers.v[(opcode & 0x0F00) >> 8] == (opcode & 0x00FF)) {
75 | VM->registers.pc += 2;
76 | }
77 | return;
78 | break;
79 | case 0x4000:
80 | /* Skips the next instruction if VX doesn't equal NN. */
81 | if (VM->registers.v[(opcode & 0x0F00) >> 8] != (opcode & 0x00FF)) {
82 | VM->registers.pc += 2;
83 | }
84 | return;
85 | break;
86 | case 0x6000:
87 | /* Sets VX to NN. */
88 | VM->registers.v[(opcode & 0x0F00) >> 8] = opcode & 0x00FF;
89 | return;
90 | break;
91 | case 0x7000:
92 | /* Adds NN to VX. */
93 | VM->registers.v[(opcode & 0x0F00) >> 8] += opcode & 0x00FF;
94 | return;
95 | break;
96 | case 0xA000:
97 | /* Sets I to the address NNN. */
98 | VM->registers.I = opcode & 0x0FFF;
99 | return;
100 | break;
101 | case 0xB000:
102 | /* Jumps to the address NNN plus V0. */
103 | VM->registers.pc = (opcode & 0x0FFF) + VM->registers.v[0];
104 | return;
105 | break;
106 | case 0xC000:
107 | /* Sets VX to a random number and NN. */
108 | VM->registers.v[(opcode & 0x0F00) >> 8] = (clock() % 255) & (opcode & 0x00FF);
109 | return;
110 | break;
111 | default:
112 | break;
113 | }
114 |
115 | switch (opcode & 0xF00F) {
116 | case 0x5000:
117 | /* Skips the next instruction if VX equals VY. */
118 | if (VM->registers.v[(opcode & 0x0F00) >> 8] == VM->registers.v[(opcode & 0x00F0) >> 4]) {
119 | VM->registers.pc += 2;
120 | }
121 | return;
122 | break;
123 | case 0x8000:
124 | /* Sets VX to the value of VY. */
125 | VM->registers.v[(opcode & 0x0F00) >> 8] = VM->registers.v[(opcode & 0x00F0) >> 4];
126 | return;
127 | break;
128 | case 0x8001:
129 | /* Sets VX to VX or VY. */
130 | VM->registers.v[(opcode & 0x0F00) >> 8] |= VM->registers.v[(opcode & 0x00F0) >> 4];
131 | return;
132 | break;
133 | case 0x8002:
134 | /* Sets VX to VX and VY. */
135 | VM->registers.v[(opcode & 0x0F00) >> 8] &= VM->registers.v[(opcode & 0x00F0) >> 4];
136 | return;
137 | break;
138 | case 0x8003:
139 | /* Sets VX to VX xor VY. */
140 | VM->registers.v[(opcode & 0x0F00) >> 8] ^= VM->registers.v[(opcode & 0x00F0) >> 4];
141 | return;
142 | break;
143 | case 0x8004:
144 | /* Adds VY to VX. VF is set to 1 when there's a carry, and to 0 when there isn't. */
145 | vx = VM->registers.v[(opcode & 0x0F00) >> 8];
146 | vy = VM->registers.v[(opcode & 0x00F0) >> 4];
147 |
148 | if (vx + vy > 0xFF) {
149 | VM->registers.v[15] = 1;
150 | } else {
151 | VM->registers.v[15] = 0;
152 | }
153 |
154 | VM->registers.v[(opcode & 0x0F00) >> 8] += vy;
155 | return;
156 | break;
157 | case 0x8005:
158 | /* VY is subtracted from VX. VF is set to 0 when there's a borrow, and 1 when there isn't. */
159 | vx = VM->registers.v[(opcode & 0x0F00) >> 8];
160 | vy = VM->registers.v[(opcode & 0x00F0) >> 4];
161 |
162 | if (vx - vy >= 0) {
163 | VM->registers.v[15] = 1;
164 | } else {
165 | VM->registers.v[15] = 0;
166 | }
167 |
168 | VM->registers.v[(opcode & 0x0F00) >> 8] -= vy;
169 | return;
170 | break;
171 | case 0x8006:
172 | /* Shifts VX right by one. VF is set to the value of the least significant bit of VX before the shift. */
173 | VM->registers.v[15] = VM->registers.v[(opcode & 0x0F00) >> 8] & 1;
174 | VM->registers.v[(opcode & 0x0F00) >> 8] >>= 1;
175 | return;
176 | break;
177 | case 0x8007:
178 | /* Sets VX to VY minus VX. VF is set to 0 when there's a borrow, and 1 when there isn't. */
179 | vx = VM->registers.v[(opcode & 0x0F00) >> 8];
180 | vy = VM->registers.v[(opcode & 0x00F0) >> 4];
181 |
182 | if (vy - vx >= 0) {
183 | VM->registers.v[15] = 1;
184 | } else {
185 | VM->registers.v[15] = 0;
186 | }
187 |
188 | VM->registers.v[(opcode & 0x0F00) >> 8] = vy - VM->registers.v[(opcode & 0x0F00) >> 8];
189 | return;
190 | break;
191 | case 0x800E:
192 | /* Shifts VX left by one. VF is set to the value of the most significant bit of VX before the shift. */
193 | VM->registers.v[15] = (VM->registers.v[(opcode & 0x0F00) >> 8] >> 7) & 0x80;
194 | VM->registers.v[(opcode & 0x0F00) >> 8] <<= 1;
195 | return;
196 | break;
197 | case 0x9000:
198 | /* Skips the next instruction if VX doesn't equal VY. */
199 | if (VM->registers.v[(opcode & 0x0F00) >> 8] != VM->registers.v[(opcode & 0x00F0) >> 4]) {
200 | VM->registers.pc += 2;
201 | }
202 | return;
203 | break;
204 | default:
205 | break;
206 | }
207 |
208 | switch (opcode & 0xF0FF) {
209 | case 0xE0A1:
210 | /* Sets pinmode */
211 | pinMode(VM->registers.v[0], (opcode & 0x0F00) >> 8);
212 | return;
213 | break;
214 | case 0xF007:
215 | /* Sets VX to the value of the delay timer. */
216 | VM->registers.v[(opcode & 0x0F00) >> 8] = VM->delay_timer;
217 | return;
218 | break;
219 | case 0xF015:
220 | /* Sets the delay timer to VX. */
221 | VM->delay_timer = VM->registers.v[(opcode & 0x0F00) >> 8];
222 | return;
223 | break;
224 | case 0xF01E:
225 | /* Adds VX to I. */
226 | if ((VM->registers.I += VM->registers.v[(opcode & 0x0F00) >> 8]) > 0x0FFF) {
227 | VM->registers.v[15] = 1;
228 | } else {
229 | VM->registers.v[15] = 0;
230 | }
231 | return;
232 | break;
233 | case 0xF033:
234 | /* Stores the Binary-coded decimal representation of VX, with the most significant
235 | of three digits at the address in I, the middle digit at I plus 1, and the least
236 | significant digit at I plus 2. (In other words, take the decimal representation of VX,
237 | place the hundreds digit in VM at location in I, the tens digit at location I+1,
238 | and the ones digit at location I+2.) */
239 | vx = VM->registers.v[(opcode & 0x0F00) >> 8];
240 | VM->memory[VM->registers.I] = (vx / 100) % 10;
241 | VM->memory[VM->registers.I + 1] = (vx / 10) % 10;
242 | VM->memory[VM->registers.I + 2] = vx % 10;
243 | return;
244 | break;
245 | case 0xF055:
246 | /* Stores V0 to VX in VM starting at address I. */
247 | for (i = 0; i <= ((opcode & 0x0F00) >> 8); i++) {
248 | VM->memory[VM->registers.I++] = VM->registers.v[i];
249 | }
250 | return;
251 | break;
252 | case 0xF065:
253 | /* Fills V0 to VX with values from VM starting at address I. */
254 | for (i = 0; i <= ((opcode & 0x0F00) >> 8); i++) {
255 | VM->registers.v[i] = VM->memory[VM->registers.I++];
256 | }
257 | return;
258 | break;
259 | default:
260 | break;
261 | }
262 |
263 | printf("Unknown opcode %X\n", opcode);
264 | VM->halt = 1;
265 | }
266 |
--------------------------------------------------------------------------------
/paper/ICEST-2022-5014.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bor0/evm-esp32/519c0b88c4cf924d9a13d64800c0e1e3d3aa44d2/paper/ICEST-2022-5014.pdf
--------------------------------------------------------------------------------
/paper/Integrating a Virtual Machine on a system-on-a-chip.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bor0/evm-esp32/519c0b88c4cf924d9a13d64800c0e1e3d3aa44d2/paper/Integrating a Virtual Machine on a system-on-a-chip.docx
--------------------------------------------------------------------------------
/paper/Integrating a Virtual Machine on a system-on-a-chip.odp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bor0/evm-esp32/519c0b88c4cf924d9a13d64800c0e1e3d3aa44d2/paper/Integrating a Virtual Machine on a system-on-a-chip.odp
--------------------------------------------------------------------------------
/paper/diagram.drawio:
--------------------------------------------------------------------------------
1 | 1Vjbbts4EP0aP0aQRN/yWNtxUmyKGs12u7svAS1REjeUKJCUL/n6HUqkJZmO26BOLwGSiMMRL3POzBxogOb57lbgMvvAY8IGoR/vBmgxCMPpeAp/tWHfGIYjvzGkgsaNKWgND/SZGKN1q2hMZM9Rcc4ULfvGiBcFiVTPhoXg275bwll/1xKnxDE8RJi51i80Vpm5lr2Ftt8RmmZ258A3Mzm2zsYgMxzzbceEbgZoLjhXzVO+mxOmY2fj0ry3fGH2cDBBCvUtL6R3ePnH8uMjGs1IvqfDxb/b0dV1s8oGs8pcOPRgvCQqyuD/mhZY7M351d4GhcQQIzPkQmU85QVmN611JnhVxETv7MOo9bnnvARjAMb/iFJ7AziuFAdTpnJmZuFSYv+3eb8e/KMH3sgOF7vu5GJvRm5UTKAkr0REzoTCsguLlKgzfsPGT8egs4GJ+S3hOVE6ZL4gDCu66fMIGzqmB78WMXgwoL0CQJsnPwmdK9/zwz5C6Dr4Ckb1aEUEhRAQ8cOAQ5cGrn71nRB433EoOS2U7Ky80gZw2NkKYQhkCmIwnR6RoFmxpcThaN/BksDJc6Tz/FNVnM/ye7yGet7DHjOaFvAcAVAavdmGCEWhYr4zEzmN44ZmRNJnvK7X0xCb0MDio9lgtDgJ+lmS653IroeLqftml15p7YFq3gJ6omHYB6AZvQ71FlbrwpNEAv2O8/kS4DnY3TysUN1UyYZCahwDF2OZtdldKUYLMj/0R/8rcDIN+YpLqig/ifP9kYPSRcNhwZorxXOYgL5X6oPlu1QrBG9L1gzKjfSwiCta6MqSUMbmnHFRnx+hMcgGTQ+pBH8idqbghWZVxgV95oXC9gYvVg6HLi/SYur3KGGH27bjjyeeydus0+5R+EZlHTmYg7KiCQVYwzGD3WdrAU+pfprfvV9dTR0WyKe6iTeAY1k26Cd0p6nRZYCblg4naF7LpFkCcTdNIQhb+4LmKVyT0bW+rIwwcHL5FxWqwuzxA44yIKAnN+llwArHo34CT1y4rD7rgjUcvxFYQwesL2StwSACbuUA42BRdlohbAfSlnS649lkPYhKDXIv72WJI1qkf9YdHXUwPIUZjnTpe4ypgJNxHZIlpOljc4ELIjfp59kkcIAbngDOgnl58YR+qng6DN5c2oa/p7Z1EyvQquWWFERgRTrS5bgmJgJ6DxwJfj+//5WVjS30F1A24SjsK5vw+5SNTdq+Xr06anlvqHtCB/7P8kQ97SD4kkQ5iJHTameGo6e0zuuOCknqnxMsaPROo03sN4HQUSuwgu9P/aV/vkofiSPK5cSjAL30Klkf8AJld3hUdqeBN3IKLzpReMPrV2c2DNtPGg0T2u9C6OZ/
--------------------------------------------------------------------------------
/vm.ino:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of evm-esp32.
3 |
4 | evm-esp32 is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | evm-esp32 is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with evm-esp32. If not, see .
16 |
17 | */
18 | #include
19 | #include
20 | #include "include/vm.h"
21 | #include "include/opcodes.h"
22 |
23 | void vm_init(struct vm *VM, String program)
24 | {
25 | memset(VM, '\0', sizeof(struct vm));
26 | VM->registers.pc = 0x200;
27 | int size = program.length();
28 |
29 | // 1 opcode per iteration
30 | for (uint16_t i = 0; i < size && i < 0xFFF - 0x200; i += 2) {
31 | VM->memory[VM->registers.pc + i] = program[i];
32 | VM->memory[VM->registers.pc + i + 1] = program[i+1];
33 | }
34 | }
35 |
36 | uint16_t calc_opcode(struct vm *VM)
37 | {
38 | uint8_t fH = VM->memory[VM->registers.pc];
39 | uint8_t fL = VM->memory[VM->registers.pc + 1];
40 | return (fH << 8) | fL;
41 | }
42 |
43 | void vm_cycle(struct vm *VM)
44 | {
45 | uint16_t opcode = calc_opcode(VM);
46 | VM->registers.pc += 2;
47 | parse_opcode(VM, opcode);
48 | }
49 |
--------------------------------------------------------------------------------