Implemented basic arduino vm.
Updated LDA to be M[D] = RX + IMM Updated STA to be RD = M[RX + IMM] Updated SYS to be RD = SYSCALL(RX, IMM) Added error callback to VM, replaced printfs with callback. Added syscalls.
This commit is contained in:
@@ -14,6 +14,11 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example Programs
|
||||||
|
* Hello World: 0x311833021210c0323401411412103416e4203406d400000048656c6c6f20576f726c640a
|
||||||
|
*/
|
||||||
|
|
||||||
#include "vm.h"
|
#include "vm.h"
|
||||||
#include "console.h"
|
#include "console.h"
|
||||||
|
|
||||||
@@ -21,15 +26,57 @@ VM vm;
|
|||||||
Console console(&vm);
|
Console console(&vm);
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
// put your setup code here, to run once:
|
|
||||||
vm_reset(&vm);
|
vm_reset(&vm);
|
||||||
Serial.begin(9600);
|
vm.error = vm_print_error;
|
||||||
|
vm.syscall = vm_syscall;
|
||||||
|
Serial.begin(1200);
|
||||||
|
console.setSerial(&Serial);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
// put your main code here, to run repeatedly:
|
console.loop();
|
||||||
console.loop(&Serial);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void vm_print_error(uint8_t err) {
|
||||||
|
Serial.print("\n");
|
||||||
|
switch(err) {
|
||||||
|
case VM_ERR_MISALIGN:
|
||||||
|
Serial.println("Halted. PC misaligned.");
|
||||||
|
break;
|
||||||
|
case VM_ERR_UNKNOWN_OP:
|
||||||
|
Serial.println("Halted. Unknown instruction. (this should never happen)");
|
||||||
|
break;
|
||||||
|
case VM_ERR_OUT_OF_BOUNDS:
|
||||||
|
Serial.println("Halted. Operation out of bounds.");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Serial.println("Halted. Unknown Error.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t vm_syscall(VM* vm, uint8_t callno, uint8_t imm) {
|
||||||
|
// Serial.print("SYSCALL 0x");
|
||||||
|
// Serial.print(callno, HEX);
|
||||||
|
// Serial.print(". IMM=");
|
||||||
|
// Serial.print(imm);
|
||||||
|
// Serial.println("");
|
||||||
|
switch(callno) {
|
||||||
|
case 0:
|
||||||
|
delay(1<<imm);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
delayMicroseconds(1<<imm);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (imm < VM_REG_SIZE) {
|
||||||
|
Serial.print((char)vm->R[imm]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,17 +16,86 @@ limitations under the License.
|
|||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <HardwareSerial.h>
|
#include <HardwareSerial.h>
|
||||||
|
#include <errno.h>
|
||||||
#include "console.h"
|
#include "console.h"
|
||||||
|
|
||||||
Console::Console(VM* _vm) {
|
Console::Console(VM* _vm) {
|
||||||
this->state = CONSOLE_NONE;
|
this->state = CONSOLE_NONE;
|
||||||
this->vm = _vm;
|
this->vm = _vm;
|
||||||
this->inputBuffer = new String("");
|
this->inputBuffer = new String("");
|
||||||
|
this->serial = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Console::loop(HardwareSerial * serial) {
|
int Console::hexToDec(String str) {
|
||||||
|
errno = 0;
|
||||||
|
long int result = strtol(str.c_str(), NULL, 16);
|
||||||
|
if (errno != 0 || result < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return (int)result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Console::setSerial(HardwareSerial *serial) {
|
||||||
|
this->serial = serial;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Console::printRegisters(bool header) {
|
||||||
|
if (header) {
|
||||||
|
serial->println(" 0 1 2 3 4 5 6 7 8 9 A B C D E F");
|
||||||
|
}
|
||||||
|
for(short i = 0; i < VM_REG_SIZE; i++) {
|
||||||
|
if (this->vm->R[i] < 0x10) {
|
||||||
|
serial->print("0");
|
||||||
|
}
|
||||||
|
serial->print(this->vm->R[i], HEX);
|
||||||
|
serial->print(" ");
|
||||||
|
}
|
||||||
|
serial->print("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Console::printMemory(uint16_t from, uint16_t to) {
|
||||||
|
uint16_t i = from;
|
||||||
|
serial->println(" 0 1 2 3 4 5 6 7 8 9 A B C D E F");
|
||||||
|
if (from > 0 && from % 16 != 0) {
|
||||||
|
i = from - (from % 16);
|
||||||
|
serial->print("0x");
|
||||||
|
if (i < 0x10) {
|
||||||
|
serial->print("0");
|
||||||
|
}
|
||||||
|
serial->print(i, HEX);
|
||||||
|
serial->print(" ");
|
||||||
|
for(; i < from; i++) {
|
||||||
|
serial->print(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(i = from; i <= to && i < VM_MEM_SIZE; i++) {
|
||||||
|
if (i % 16 == 0) {
|
||||||
|
if (i > 0) {
|
||||||
|
serial->print("\n");
|
||||||
|
if (i % 128 == 0 ) {
|
||||||
|
serial->println(" 0 1 2 3 4 5 6 7 8 9 A B C D E F");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
serial->print("0x");
|
||||||
|
if (i < 0x10) {
|
||||||
|
serial->print("0");
|
||||||
|
}
|
||||||
|
serial->print(i, HEX);
|
||||||
|
serial->print(" ");
|
||||||
|
}
|
||||||
|
uint8_t m = this->vm->M[i];
|
||||||
|
if (m < 0x10) {
|
||||||
|
serial->print("0");
|
||||||
|
}
|
||||||
|
serial->print(m, HEX);
|
||||||
|
serial->print(" ");
|
||||||
|
}
|
||||||
|
serial->print("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Console::loop() {
|
||||||
|
HardwareSerial * serial = this->serial;
|
||||||
int i;
|
int i;
|
||||||
char d;
|
|
||||||
|
|
||||||
switch(this->state) {
|
switch(this->state) {
|
||||||
case CONSOLE_NONE:
|
case CONSOLE_NONE:
|
||||||
@@ -46,107 +115,37 @@ void Console::loop(HardwareSerial * serial) {
|
|||||||
this->state = CONSOLE_ACTIVE;
|
this->state = CONSOLE_ACTIVE;
|
||||||
break;
|
break;
|
||||||
case CONSOLE_ACTIVE:
|
case CONSOLE_ACTIVE:
|
||||||
if(serial->available()) {
|
this->stateActive();
|
||||||
while((d = (char)serial->read()) != -1) {
|
|
||||||
if (d == 27) {
|
|
||||||
//escape, deactivate console
|
|
||||||
this->inputBuffer = new String("");
|
|
||||||
serial->println("Console deactivated.");
|
|
||||||
this->state = CONSOLE_NONE;
|
|
||||||
} else if (d == '\n') {
|
|
||||||
//Finished input
|
|
||||||
serial->print("\n");
|
|
||||||
if (this->inputBuffer->equals("?")) {
|
|
||||||
serial->print(F(
|
|
||||||
"Commands\n"
|
|
||||||
"? | this help\n"
|
|
||||||
"q | deactivate console\n"
|
|
||||||
"r | run until halted\n"
|
|
||||||
"s | step one instruction\n"
|
|
||||||
"e | examine and update memory\n"
|
|
||||||
"v | show vm status\n"
|
|
||||||
"rst | reset PC and registers\n"
|
|
||||||
"clr | reset and clear memory\n"
|
|
||||||
));
|
|
||||||
serial->print("> ");
|
|
||||||
} else if (this->inputBuffer->equals("q")) {
|
|
||||||
serial->println("Console deactivated.");
|
|
||||||
this->state = CONSOLE_NONE;
|
|
||||||
} else if (this->inputBuffer->equals("r")) {
|
|
||||||
this->state = CONSOLE_RUN;
|
|
||||||
} else if (this->inputBuffer->equals("s")) {
|
|
||||||
this->state = CONSOLE_STEP;
|
|
||||||
} else if (this->inputBuffer->equals("e")) {
|
|
||||||
this->state = CONSOLE_EXAMINE;
|
|
||||||
} else if (this->inputBuffer->equals("v")) {
|
|
||||||
this->state = CONSOLE_VIEW;
|
|
||||||
} else if (this->inputBuffer->equals("rst")) {
|
|
||||||
this->state = CONSOLE_RESET;
|
|
||||||
} else if (this->inputBuffer->equals("clr")) {
|
|
||||||
this->state = CONSOLE_CLEAR;
|
|
||||||
} else {
|
|
||||||
serial->println("Unknown command. Type ? for help.");
|
|
||||||
serial->print("> ");
|
|
||||||
}
|
|
||||||
this->inputBuffer = new String("");
|
|
||||||
} else if (isAlphaNumeric(d) || isPunct(d) || isSpace(d)) {
|
|
||||||
//Append input to buffer
|
|
||||||
this->inputBuffer->concat(d);
|
|
||||||
serial->print(d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case CONSOLE_RUN:
|
case CONSOLE_RUN:
|
||||||
|
if(this->vm->halted == false) {
|
||||||
|
vm_step(this->vm);
|
||||||
|
} else {
|
||||||
|
serial->print("VM Halted. PC=0x");
|
||||||
|
serial->print(this->vm->PC);
|
||||||
|
serial->println(". Registers:");
|
||||||
|
this->printRegisters(true);
|
||||||
|
this->state = CONSOLE_ACTIVATE;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CONSOLE_STEP:
|
case CONSOLE_STEP:
|
||||||
|
if (this->vm->halted == false) {
|
||||||
|
vm_step(this->vm);
|
||||||
|
if (this->vm->halted == true) {
|
||||||
|
serial->print("VM Halted. ");
|
||||||
|
}
|
||||||
|
serial->print("PC=0x");
|
||||||
|
serial->print(this->vm->PC);
|
||||||
|
serial->println(". Registers:");
|
||||||
|
this->printRegisters(true);
|
||||||
|
}
|
||||||
|
this->state = CONSOLE_ACTIVATE;
|
||||||
break;
|
break;
|
||||||
case CONSOLE_EXAMINE:
|
case CONSOLE_EXAMINE:
|
||||||
|
this->stateExamine();
|
||||||
break;
|
break;
|
||||||
case CONSOLE_VIEW:
|
case CONSOLE_VIEW:
|
||||||
//PC
|
this->stateView();
|
||||||
serial->print( "PC ");
|
|
||||||
serial->print(this->vm->PC, HEX);
|
|
||||||
serial->print("\n");
|
|
||||||
|
|
||||||
//Registers
|
|
||||||
serial->print( "R ");
|
|
||||||
for(i = 0; i < VM_REG_SIZE; i++) {
|
|
||||||
if (this->vm->R[i] < 0x10) {
|
|
||||||
serial->print("0");
|
|
||||||
}
|
|
||||||
serial->print(this->vm->R[i], HEX);
|
|
||||||
serial->print(" ");
|
|
||||||
}
|
|
||||||
serial->print("\n");
|
|
||||||
|
|
||||||
//Memory
|
|
||||||
serial->println(" 0 1 2 3 4 5 6 7 8 9 A B C D E F");
|
|
||||||
for(i = 0; i < VM_MEM_SIZE; i++) {
|
|
||||||
if (i % 16 == 0) {
|
|
||||||
if (i > 0) {
|
|
||||||
serial->print("\n");
|
|
||||||
if (i % 128 == 0 ) {
|
|
||||||
serial->println(" 0 1 2 3 4 5 6 7 8 9 A B C D E F");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
serial->print("0x");
|
|
||||||
if (i < 0x10) {
|
|
||||||
serial->print("0");
|
|
||||||
}
|
|
||||||
serial->print(i, HEX);
|
|
||||||
serial->print(" ");
|
|
||||||
}
|
|
||||||
uint8_t m = this->vm->M[i];
|
|
||||||
if (m < 0x10) {
|
|
||||||
serial->print("0");
|
|
||||||
}
|
|
||||||
serial->print(m, HEX);
|
|
||||||
serial->print(" ");
|
|
||||||
}
|
|
||||||
serial->print("\n");
|
|
||||||
|
|
||||||
this->state = CONSOLE_ACTIVATE;
|
|
||||||
break;
|
break;
|
||||||
case CONSOLE_RESET:
|
case CONSOLE_RESET:
|
||||||
vm_reset(this->vm);
|
vm_reset(this->vm);
|
||||||
@@ -164,3 +163,182 @@ void Console::loop(HardwareSerial * serial) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Console::stateActive() {
|
||||||
|
HardwareSerial * serial = this->serial;
|
||||||
|
char d;
|
||||||
|
if(serial->available()) {
|
||||||
|
while((d = (char)serial->read()) != -1) {
|
||||||
|
if (d == 27) {
|
||||||
|
//escape, deactivate console
|
||||||
|
this->inputBuffer = new String("");
|
||||||
|
serial->println("Console deactivated.");
|
||||||
|
this->state = CONSOLE_NONE;
|
||||||
|
} else if (d == '\n') {
|
||||||
|
//Finished input
|
||||||
|
serial->print("\n");
|
||||||
|
if (this->inputBuffer->equals("?")) {
|
||||||
|
serial->print(F(
|
||||||
|
"Commands\n"
|
||||||
|
"? | This help\n"
|
||||||
|
"q | Deactivate console\n"
|
||||||
|
"r | Run until halted\n"
|
||||||
|
"s | Step one instruction\n"
|
||||||
|
"e | Examine/update\n"
|
||||||
|
"v | Show vm status\n"
|
||||||
|
"rst | Reset PC and registers\n"
|
||||||
|
"clr | Reset and clear memory\n"
|
||||||
|
));
|
||||||
|
serial->print("> ");
|
||||||
|
} else if (this->inputBuffer->equals("q")) {
|
||||||
|
serial->println("Console deactivated.");
|
||||||
|
this->state = CONSOLE_NONE;
|
||||||
|
} else if (this->inputBuffer->equals("r")) {
|
||||||
|
this->vm->halted = false;
|
||||||
|
this->state = CONSOLE_RUN;
|
||||||
|
} else if (this->inputBuffer->equals("s")) {
|
||||||
|
this->vm->halted = false;
|
||||||
|
this->state = CONSOLE_STEP;
|
||||||
|
} else if (this->inputBuffer->equals("e")) {
|
||||||
|
serial->println("Type q to return to console. Type ? for help.");
|
||||||
|
serial->print("E> ");
|
||||||
|
this->state = CONSOLE_EXAMINE;
|
||||||
|
} else if (this->inputBuffer->equals("v")) {
|
||||||
|
this->state = CONSOLE_VIEW;
|
||||||
|
} else if (this->inputBuffer->equals("rst")) {
|
||||||
|
this->state = CONSOLE_RESET;
|
||||||
|
} else if (this->inputBuffer->equals("clr")) {
|
||||||
|
this->state = CONSOLE_CLEAR;
|
||||||
|
} else {
|
||||||
|
serial->println("Unknown command. Type ? for help.");
|
||||||
|
serial->print("> ");
|
||||||
|
}
|
||||||
|
this->inputBuffer = new String("");
|
||||||
|
} else if (isAlphaNumeric(d) || isPunct(d) || isSpace(d)) {
|
||||||
|
//Append input to buffer
|
||||||
|
this->inputBuffer->concat(d);
|
||||||
|
serial->print(d);
|
||||||
|
}
|
||||||
|
}//while serial read
|
||||||
|
}//if serial available
|
||||||
|
}//Console::stateActive
|
||||||
|
|
||||||
|
void Console::stateView() {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
//PC
|
||||||
|
serial->print( "PC ");
|
||||||
|
serial->print(this->vm->PC, HEX);
|
||||||
|
serial->print("\n");
|
||||||
|
|
||||||
|
//Registers
|
||||||
|
serial->print( "R ");
|
||||||
|
this->printRegisters(false);
|
||||||
|
|
||||||
|
//Memory
|
||||||
|
this->printMemory(0, VM_MEM_SIZE-1);
|
||||||
|
|
||||||
|
this->state = CONSOLE_ACTIVATE;
|
||||||
|
}//Console::stateView
|
||||||
|
|
||||||
|
void Console::stateExamine() {
|
||||||
|
char d;
|
||||||
|
int location, value;
|
||||||
|
if(serial->available()) {
|
||||||
|
while((d = (char)serial->read()) != -1) {
|
||||||
|
if (d == 27) {
|
||||||
|
//escape, go back to console
|
||||||
|
this->inputBuffer = new String("");
|
||||||
|
this->state = CONSOLE_ACTIVATE;
|
||||||
|
} else if (d == '\n') {
|
||||||
|
//Finished input
|
||||||
|
serial->print("\n");
|
||||||
|
this->inputBuffer->toUpperCase();
|
||||||
|
if (this->inputBuffer->equals("?")) {
|
||||||
|
serial->print(F(
|
||||||
|
"Commands\n"
|
||||||
|
"? | This help\n"
|
||||||
|
"q | Return to console\n"
|
||||||
|
"PC | Show program counter\n"
|
||||||
|
"PC=yyyy | Set program counter\n"
|
||||||
|
"R | Show register contents\n"
|
||||||
|
"Rx=yy | Update register\n"
|
||||||
|
"Myy,zz | Show zz bytes of memory starting at xx\n"
|
||||||
|
"Myy=zz | Update memory at yy with zz, may repeat zz for subsequent bytes\n"
|
||||||
|
"x = 0-F, yy/zz = 00-FF\n"
|
||||||
|
));
|
||||||
|
} else if (this->inputBuffer->equals("Q")) {
|
||||||
|
this->state = CONSOLE_ACTIVATE;
|
||||||
|
} else if (this->inputBuffer->equals("PC")) {
|
||||||
|
serial->println(this->vm->PC, HEX);
|
||||||
|
} else if (this->inputBuffer->startsWith("PC") && this->inputBuffer->indexOf("=") != -1) {
|
||||||
|
value = this->hexToDec(this->inputBuffer->substring(this->inputBuffer->indexOf("=")+1));
|
||||||
|
if (value >= 0 && value < VM_MEM_SIZE) {
|
||||||
|
this->vm->PC = value;
|
||||||
|
} else {
|
||||||
|
serial->println("Invalid value");
|
||||||
|
}
|
||||||
|
} else if (this->inputBuffer->equals("R")) {
|
||||||
|
this->printRegisters(true);
|
||||||
|
} else if (this->inputBuffer->startsWith("R") && this->inputBuffer->indexOf("=") != -1) {
|
||||||
|
location = this->hexToDec(this->inputBuffer->substring(1, this->inputBuffer->indexOf("=")));
|
||||||
|
value = this->hexToDec(this->inputBuffer->substring(this->inputBuffer->indexOf("=")+1));
|
||||||
|
if (location >= 0 && location < VM_REG_SIZE) {
|
||||||
|
if (value >= 0 && value <= 255) {
|
||||||
|
this->vm->R[location] = (uint8_t)value;
|
||||||
|
} else {
|
||||||
|
serial->println("Invalid value");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
serial->println("Invalid register");
|
||||||
|
}
|
||||||
|
} else if (this->inputBuffer->startsWith("M") && this->inputBuffer->indexOf(",") != -1) {
|
||||||
|
location = this->hexToDec(this->inputBuffer->substring(1, this->inputBuffer->indexOf(",")));
|
||||||
|
value = this->hexToDec(this->inputBuffer->substring(this->inputBuffer->indexOf(",")+1));
|
||||||
|
if (value + location >= VM_MEM_SIZE) {
|
||||||
|
value = VM_MEM_SIZE-1;
|
||||||
|
}
|
||||||
|
if (location >= 0 && location < VM_MEM_SIZE) {
|
||||||
|
if (value > 0) {
|
||||||
|
this->printMemory(location, location + value);
|
||||||
|
} else {
|
||||||
|
serial->println("Invalid value");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
serial->println("Invalid start location");
|
||||||
|
}
|
||||||
|
} else if (this->inputBuffer->startsWith("M") && this->inputBuffer->indexOf("=") != -1) {
|
||||||
|
location = this->hexToDec(this->inputBuffer->substring(1, this->inputBuffer->indexOf("=")));
|
||||||
|
String x = this->inputBuffer->substring(this->inputBuffer->indexOf("=")+1);
|
||||||
|
if (x.length() % 2 != 0) {
|
||||||
|
serial->println("Invalid data. 2 characters per byte required");
|
||||||
|
} else {
|
||||||
|
for(int i = 0; i < x.length(); i += 2) {
|
||||||
|
value = this->hexToDec(x.substring(i, i+2));
|
||||||
|
if (value >= 0 && value <= 255) {
|
||||||
|
this->vm->M[location++] = value;
|
||||||
|
} else {
|
||||||
|
serial->println(i);
|
||||||
|
serial->println(x.substring(i, i+2));
|
||||||
|
serial->println(value);
|
||||||
|
serial->println("Invalid data.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
serial->println("Unknown command.");
|
||||||
|
serial->print("E> ");
|
||||||
|
}
|
||||||
|
if (this->state == CONSOLE_EXAMINE) {
|
||||||
|
serial->print("E> ");
|
||||||
|
}
|
||||||
|
this->inputBuffer = new String("");
|
||||||
|
} else if (isAlphaNumeric(d) || isPunct(d) || isSpace(d)) {
|
||||||
|
//Append input to buffer
|
||||||
|
this->inputBuffer->concat(d);
|
||||||
|
serial->print(d);
|
||||||
|
}
|
||||||
|
}//while serial read
|
||||||
|
}//if serial available
|
||||||
|
}//Console::stateExamine
|
||||||
|
|
||||||
|
|||||||
@@ -38,9 +38,20 @@ class Console {
|
|||||||
ConsoleState state;
|
ConsoleState state;
|
||||||
VM *vm;
|
VM *vm;
|
||||||
String *inputBuffer;
|
String *inputBuffer;
|
||||||
|
HardwareSerial *serial;
|
||||||
|
|
||||||
|
int hexToDec(String);
|
||||||
|
void printRegisters(bool);
|
||||||
|
void printMemory(uint16_t, uint16_t);
|
||||||
|
|
||||||
|
void stateActive();
|
||||||
|
void stateView();
|
||||||
|
void stateExamine();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Console (VM*);
|
Console (VM*);
|
||||||
void loop(HardwareSerial*);
|
void setSerial(HardwareSerial*);
|
||||||
|
void loop();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //UVM_CONSOLE_H
|
#endif //UVM_CONSOLE_H
|
||||||
|
|||||||
47
arduino/vm.c
47
arduino/vm.c
@@ -16,7 +16,6 @@ limitations under the License.
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include "vm.h"
|
#include "vm.h"
|
||||||
|
|
||||||
VM *new_vm() {
|
VM *new_vm() {
|
||||||
@@ -94,7 +93,9 @@ void vm_step(VM *vm) {
|
|||||||
|
|
||||||
uint8_t PC = vm->PC;
|
uint8_t PC = vm->PC;
|
||||||
if (PC % 2 != 0) {
|
if (PC % 2 != 0) {
|
||||||
fprintf(stderr, "Halted. PC not aligned.\n");
|
if (vm->error != NULL) {
|
||||||
|
vm->error(VM_ERR_MISALIGN);
|
||||||
|
}
|
||||||
vm->halted = true;
|
vm->halted = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -112,12 +113,32 @@ void vm_step(VM *vm) {
|
|||||||
uint16_t temp16 = 0;
|
uint16_t temp16 = 0;
|
||||||
switch (inst.op) {
|
switch (inst.op) {
|
||||||
case OP_LDA:
|
case OP_LDA:
|
||||||
vm_decode_Q(&inst, raw);
|
vm_decode_T(&inst, raw);
|
||||||
d = vm->M[inst.imm];
|
x = vm_get_r(vm, inst.rx);
|
||||||
|
temp16 = x;
|
||||||
|
temp16 += inst.imm;
|
||||||
|
if (temp16 < VM_MEM_SIZE) {
|
||||||
|
d = vm->M[temp16];
|
||||||
|
} else {
|
||||||
|
if (vm->error != NULL) {
|
||||||
|
vm->error(VM_ERR_OUT_OF_BOUNDS);
|
||||||
|
}
|
||||||
|
vm->halted = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case OP_STA:
|
case OP_STA:
|
||||||
vm_decode_Q(&inst, raw);
|
vm_decode_T(&inst, raw);
|
||||||
vm->M[inst.imm] = vm_get_r(vm, inst.rd);
|
x = vm_get_r(vm, inst.rx);
|
||||||
|
temp16 = vm_get_r(vm, inst.rd);
|
||||||
|
temp16 += inst.imm;
|
||||||
|
if (temp16 < VM_MEM_SIZE) {
|
||||||
|
vm->M[temp16] = x;
|
||||||
|
} else {
|
||||||
|
if (vm->error != NULL) {
|
||||||
|
vm->error(VM_ERR_OUT_OF_BOUNDS);
|
||||||
|
}
|
||||||
|
vm->halted = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case OP_LDI:
|
case OP_LDI:
|
||||||
vm_decode_Q(&inst, raw);
|
vm_decode_Q(&inst, raw);
|
||||||
@@ -172,11 +193,12 @@ void vm_step(VM *vm) {
|
|||||||
d = x >> vm_get_r(vm, inst.imm);
|
d = x >> vm_get_r(vm, inst.imm);
|
||||||
break;
|
break;
|
||||||
case OP_SYS:
|
case OP_SYS:
|
||||||
vm_decode_Q(&inst, raw);
|
vm_decode_T(&inst, raw);
|
||||||
|
x = vm_get_r(vm, inst.rx);
|
||||||
if (vm->syscall != NULL) {
|
if (vm->syscall != NULL) {
|
||||||
d = vm->syscall(vm, inst.imm);
|
d = vm->syscall(vm, x, inst.imm);
|
||||||
} else {
|
} else {
|
||||||
printf("SYSCALL #%d\n", inst.imm);
|
d = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case OP_JMP:
|
case OP_JMP:
|
||||||
@@ -200,13 +222,14 @@ void vm_step(VM *vm) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case OP_HLT:
|
case OP_HLT:
|
||||||
printf("Halted at 0x%04X\n", PC);
|
|
||||||
vm->halted = true;
|
vm->halted = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
printf("Unknown Instruction at 0x%04X: 0x%hhX\n", PC, inst.op);
|
if (vm->error != NULL) {
|
||||||
|
vm->error(VM_ERR_UNKNOWN_OP);
|
||||||
|
}
|
||||||
vm->halted = true;
|
vm->halted = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
vm_put_r(vm, inst.rd, d);
|
vm_put_r(vm, inst.rd, d);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,10 @@ limitations under the License.
|
|||||||
#define OP_JEQ 0xe
|
#define OP_JEQ 0xe
|
||||||
#define OP_JLT 0xf
|
#define OP_JLT 0xf
|
||||||
|
|
||||||
|
#define VM_ERR_MISALIGN 0x1
|
||||||
|
#define VM_ERR_UNKNOWN_OP 0x2
|
||||||
|
#define VM_ERR_OUT_OF_BOUNDS 0x3
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
@@ -59,7 +63,8 @@ struct VM_t {
|
|||||||
uint8_t M[VM_MEM_SIZE];
|
uint8_t M[VM_MEM_SIZE];
|
||||||
uint8_t carry;
|
uint8_t carry;
|
||||||
bool halted;
|
bool halted;
|
||||||
uint8_t (*syscall)(struct VM_t* vm, uint8_t callno);
|
uint8_t (*syscall)(struct VM_t* vm, uint8_t callno, uint8_t imm);
|
||||||
|
void (*error)(uint8_t err);
|
||||||
};
|
};
|
||||||
typedef struct VM_t VM;
|
typedef struct VM_t VM;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user