From 600be14336558b1b9e48565125927d94b09e297b Mon Sep 17 00:00:00 2001 From: Sam Stevens Date: Mon, 22 Oct 2018 00:01:47 +0100 Subject: [PATCH] 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. --- arduino/arduino.ino | 55 ++++++- arduino/console.cpp | 368 ++++++++++++++++++++++++++++++++------------ arduino/console.h | 13 +- arduino/vm.c | 47 ++++-- arduino/vm.h | 7 +- 5 files changed, 377 insertions(+), 113 deletions(-) diff --git a/arduino/arduino.ino b/arduino/arduino.ino index ad7264f..ae72dc9 100644 --- a/arduino/arduino.ino +++ b/arduino/arduino.ino @@ -14,6 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ +/** + * Example Programs + * Hello World: 0x311833021210c0323401411412103416e4203406d400000048656c6c6f20576f726c640a + */ + #include "vm.h" #include "console.h" @@ -21,15 +26,57 @@ VM vm; Console console(&vm); void setup() { - // put your setup code here, to run once: vm_reset(&vm); - Serial.begin(9600); + vm.error = vm_print_error; + vm.syscall = vm_syscall; + Serial.begin(1200); + console.setSerial(&Serial); } void loop() { - // put your main code here, to run repeatedly: - console.loop(&Serial); + console.loop(); } +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<R[imm]); + } + break; + default: + return 0; + } + return 0; +} diff --git a/arduino/console.cpp b/arduino/console.cpp index 164b435..4629fcc 100644 --- a/arduino/console.cpp +++ b/arduino/console.cpp @@ -16,17 +16,86 @@ limitations under the License. #include #include +#include #include "console.h" Console::Console(VM* _vm) { this->state = CONSOLE_NONE; this->vm = _vm; 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; - char d; switch(this->state) { case CONSOLE_NONE: @@ -46,107 +115,37 @@ void Console::loop(HardwareSerial * serial) { this->state = CONSOLE_ACTIVE; break; case CONSOLE_ACTIVE: - 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 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); - } - } - } + this->stateActive(); break; 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; 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; case CONSOLE_EXAMINE: + this->stateExamine(); break; case CONSOLE_VIEW: - //PC - 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; + this->stateView(); break; case CONSOLE_RESET: 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 + diff --git a/arduino/console.h b/arduino/console.h index 748e567..ef4e740 100644 --- a/arduino/console.h +++ b/arduino/console.h @@ -38,9 +38,20 @@ class Console { ConsoleState state; VM *vm; String *inputBuffer; + HardwareSerial *serial; + + int hexToDec(String); + void printRegisters(bool); + void printMemory(uint16_t, uint16_t); + + void stateActive(); + void stateView(); + void stateExamine(); + public: Console (VM*); - void loop(HardwareSerial*); + void setSerial(HardwareSerial*); + void loop(); }; #endif //UVM_CONSOLE_H diff --git a/arduino/vm.c b/arduino/vm.c index 24583d5..8196192 100644 --- a/arduino/vm.c +++ b/arduino/vm.c @@ -16,7 +16,6 @@ limitations under the License. #include #include -#include #include "vm.h" VM *new_vm() { @@ -94,7 +93,9 @@ void vm_step(VM *vm) { uint8_t PC = vm->PC; if (PC % 2 != 0) { - fprintf(stderr, "Halted. PC not aligned.\n"); + if (vm->error != NULL) { + vm->error(VM_ERR_MISALIGN); + } vm->halted = true; return; } @@ -112,12 +113,32 @@ void vm_step(VM *vm) { uint16_t temp16 = 0; switch (inst.op) { case OP_LDA: - vm_decode_Q(&inst, raw); - d = vm->M[inst.imm]; + vm_decode_T(&inst, raw); + 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; case OP_STA: - vm_decode_Q(&inst, raw); - vm->M[inst.imm] = vm_get_r(vm, inst.rd); + vm_decode_T(&inst, raw); + 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; case OP_LDI: vm_decode_Q(&inst, raw); @@ -172,11 +193,12 @@ void vm_step(VM *vm) { d = x >> vm_get_r(vm, inst.imm); break; case OP_SYS: - vm_decode_Q(&inst, raw); + vm_decode_T(&inst, raw); + x = vm_get_r(vm, inst.rx); if (vm->syscall != NULL) { - d = vm->syscall(vm, inst.imm); + d = vm->syscall(vm, x, inst.imm); } else { - printf("SYSCALL #%d\n", inst.imm); + d = 0; } break; case OP_JMP: @@ -200,13 +222,14 @@ void vm_step(VM *vm) { } break; case OP_HLT: - printf("Halted at 0x%04X\n", PC); vm->halted = true; break; 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; break; } vm_put_r(vm, inst.rd, d); -} \ No newline at end of file +} diff --git a/arduino/vm.h b/arduino/vm.h index bc91f71..31d7cc9 100644 --- a/arduino/vm.h +++ b/arduino/vm.h @@ -40,6 +40,10 @@ limitations under the License. #define OP_JEQ 0xe #define OP_JLT 0xf +#define VM_ERR_MISALIGN 0x1 +#define VM_ERR_UNKNOWN_OP 0x2 +#define VM_ERR_OUT_OF_BOUNDS 0x3 + #ifdef __cplusplus extern "C" { #endif @@ -59,7 +63,8 @@ struct VM_t { uint8_t M[VM_MEM_SIZE]; uint8_t carry; 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;