├── 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 | --------------------------------------------------------------------------------