Started on arduino version

This commit is contained in:
2018-10-21 13:47:48 +01:00
parent af0d1b0cd0
commit 6abacd4c1d
6 changed files with 551 additions and 2 deletions

35
arduino/arduino.ino Normal file
View File

@@ -0,0 +1,35 @@
/*
Copyright 2018 Sam Stevens
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "vm.h"
#include "console.h"
VM vm;
Console console(&vm);
void setup() {
// put your setup code here, to run once:
vm_reset(&vm);
Serial.begin(9600);
}
void loop() {
// put your main code here, to run repeatedly:
console.loop(&Serial);
}

166
arduino/console.cpp Normal file
View File

@@ -0,0 +1,166 @@
/*
Copyright 2018 Sam Stevens
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <Arduino.h>
#include <HardwareSerial.h>
#include "console.h"
Console::Console(VM* _vm) {
this->state = CONSOLE_NONE;
this->vm = _vm;
this->inputBuffer = new String("");
}
void Console::loop(HardwareSerial * serial) {
int i;
char d;
switch(this->state) {
case CONSOLE_NONE:
serial->println("Press Enter to activate console.");
this->state = CONSOLE_OFF;
break;
case CONSOLE_OFF:
if(serial->available()) {
if (serial->read() == 10) {
serial->println("Console active. Type ? for help.");
this->state = CONSOLE_ACTIVATE;
}
}
break;
case CONSOLE_ACTIVATE:
serial->print("> ");
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);
}
}
}
break;
case CONSOLE_RUN:
break;
case CONSOLE_STEP:
break;
case CONSOLE_EXAMINE:
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;
break;
case CONSOLE_RESET:
vm_reset(this->vm);
serial->println("VM Reset");
this->state = CONSOLE_ACTIVATE;
break;
case CONSOLE_CLEAR:
vm_reset(this->vm);
for(i = 0; i < VM_MEM_SIZE; i++) {
this->vm->M[i] = 0;
}
serial->println("VM Reset & Memory Cleared");
this->state = CONSOLE_ACTIVATE;
break;
}
}

46
arduino/console.h Normal file
View File

@@ -0,0 +1,46 @@
/*
Copyright 2018 Sam Stevens
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef UVM_CONSOLE_H
#define UVM_CONSOLE_H
#include <HardwareSerial.h>
#include "vm.h"
enum ConsoleState_t {
CONSOLE_NONE,
CONSOLE_OFF,
CONSOLE_ACTIVATE,
CONSOLE_ACTIVE,
CONSOLE_RUN,
CONSOLE_STEP,
CONSOLE_EXAMINE,
CONSOLE_VIEW,
CONSOLE_RESET,
CONSOLE_CLEAR,
};
typedef enum ConsoleState_t ConsoleState;
class Console {
ConsoleState state;
VM *vm;
String *inputBuffer;
public:
Console (VM*);
void loop(HardwareSerial*);
};
#endif //UVM_CONSOLE_H

212
arduino/vm.c Normal file
View File

@@ -0,0 +1,212 @@
/*
Copyright 2018 Sam Stevens
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "vm.h"
VM *new_vm() {
VM *vm = calloc(1, sizeof(VM));
return vm;
}
void free_vm(VM *vm) {
free(vm);
}
void vm_reset(VM *vm) {
memset(vm->R, 0, sizeof(uint8_t) * VM_REG_SIZE);
vm->PC = 0;
vm->carry = 0;
vm->halted = false;
}
void vm_clear(VM *vm) {
vm_reset(vm);
memset(&vm->M, 0, VM_MEM_SIZE);
}
inline uint8_t vm_get_r(VM *vm, uint8_t r) {
if (r == 0) {
return 0;
}
if (r < VM_REG_SIZE) {
return vm->R[r];
}
return 0;
}
inline void vm_put_r(VM *vm, uint8_t r, uint8_t v) {
if (r < VM_REG_SIZE) {
vm->R[r] = v;
}
}
inline void vm_decode_Q(VM_Instruction *inst, uint16_t raw) {
inst->rd = (uint8_t) ((raw & 0x0F00) >> 8);
inst->imm = (uint8_t) (raw & 0x00FF);
}
inline void vm_decode_S(VM_Instruction *inst, uint16_t raw) {
inst->rd = (uint8_t) ((raw & 0x0F00) >> 8);
inst->rx = (uint8_t) ((raw & 0x00F0) >> 4);
inst->ry = (uint8_t) ((raw & 0x000F) >> 0);
}
inline void vm_decode_T(VM_Instruction *inst, uint16_t raw) {
inst->rd = (uint8_t) ((raw & 0x0F00) >> 8);
inst->rx = (uint8_t) ((raw & 0x00F0) >> 4);
inst->imm = (uint8_t) ((raw & 0x000F) >> 0);
}
inline uint8_t vm_subtract(uint8_t x, uint8_t y, uint8_t *ptr_borrow) {
uint8_t borrow = *ptr_borrow,
difference = 0,
xb, yb;
for (uint8_t i = 0; i < 8; i++) {
xb = (uint8_t) ((x >> i) & 1);
yb = (uint8_t) ((y >> i) & 1);
difference |= (uint8_t) (((xb ^ yb) ^ borrow) & 1) << i;
borrow = (uint8_t) (((~xb & yb) | ~(xb ^ yb) * borrow) & 1);
}
*ptr_borrow = borrow;
return difference;
}
void vm_step(VM *vm) {
VM_Instruction inst;
memset(&inst, 0, sizeof(VM_Instruction));
uint8_t PC = vm->PC;
if (PC % 2 != 0) {
fprintf(stderr, "Halted. PC not aligned.\n");
vm->halted = true;
return;
}
uint16_t raw = (vm->M[PC] << 8) + vm->M[PC + 1];
vm->PC += 2;
inst.op = (uint8_t) (raw >> 12);
if (vm->halted) {
return;
}
uint8_t x = 0;
uint8_t y = 0;
uint8_t d = 0;
uint16_t temp16 = 0;
switch (inst.op) {
case OP_LDA:
vm_decode_Q(&inst, raw);
d = vm->M[inst.imm];
break;
case OP_STA:
vm_decode_Q(&inst, raw);
vm->M[inst.imm] = vm_get_r(vm, inst.rd);
break;
case OP_LDI:
vm_decode_Q(&inst, raw);
d = inst.imm;
break;
case OP_ADD:
vm_decode_S(&inst, raw);
vm->carry = 0;
x = vm_get_r(vm, inst.rx);
y = vm_get_r(vm, inst.ry);
d = x + y;
break;
case OP_ADC:
vm_decode_S(&inst, raw);
x = vm_get_r(vm, inst.rx);
y = vm_get_r(vm, inst.ry);
temp16 = x + y + vm->carry;
vm->carry = (uint8_t) (temp16 >> 8);
d = (uint8_t) temp16;
break;
case OP_SUB:
vm_decode_S(&inst, raw);
vm->carry = 0;
x = vm_get_r(vm, inst.rx);
y = vm_get_r(vm, inst.ry);
d = x - y;
break;
case OP_SBC:
vm_decode_S(&inst, raw);
x = vm_get_r(vm, inst.rx);
y = vm_get_r(vm, inst.ry);
d = vm_subtract(x, y, &vm->carry);
break;
case OP_NOT:
vm_decode_T(&inst, raw);
d = ~x;
break;
case OP_AND:
vm_decode_S(&inst, raw);
x = vm_get_r(vm, inst.rx);
y = vm_get_r(vm, inst.ry);
d = x & y;
break;
case OP_SHL:
vm_decode_T(&inst, raw);
x = vm_get_r(vm, inst.rx);
d = x << vm_get_r(vm, inst.imm);
break;
case OP_SHR:
vm_decode_T(&inst, raw);
x = vm_get_r(vm, inst.rx);
d = x >> vm_get_r(vm, inst.imm);
break;
case OP_SYS:
vm_decode_Q(&inst, raw);
if (vm->syscall != NULL) {
d = vm->syscall(vm, inst.imm);
} else {
printf("SYSCALL #%d\n", inst.imm);
}
break;
case OP_JMP:
vm_decode_Q(&inst, raw);
vm->PC = vm_get_r(vm, inst.rd);
break;
case OP_JEQ:
vm_decode_S(&inst, raw);
x = vm_get_r(vm, inst.rx);
y = vm_get_r(vm, inst.ry);
if (x == y) {
vm->PC = vm_get_r(vm, inst.rd);
}
break;
case OP_JLT:
vm_decode_S(&inst, raw);
x = vm_get_r(vm, inst.rx);
y = vm_get_r(vm, inst.ry);
if (x < y) {
vm->PC = vm_get_r(vm, inst.rd);
}
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);
vm->halted = true;
break;
}
vm_put_r(vm, inst.rd, d);
}

90
arduino/vm.h Normal file
View File

@@ -0,0 +1,90 @@
/*
Copyright 2018 Sam Stevens
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef UVM_VM_H
#define UVM_VM_H
#include <stdint.h>
#include <stdbool.h>
#define VM_MEM_SIZE 256
#define VM_REG_SIZE 16
#define OP_HLT 0x0
#define OP_LDA 0x1
#define OP_STA 0x2
#define OP_LDI 0x3
#define OP_ADD 0x4
#define OP_ADC 0x5
#define OP_SUB 0x6
#define OP_SBC 0x7
#define OP_NOT 0x8
#define OP_AND 0x9
#define OP_SHL 0xa
#define OP_SHR 0xb
#define OP_SYS 0xc
#define OP_JMP 0xd
#define OP_JEQ 0xe
#define OP_JLT 0xf
#ifdef __cplusplus
extern "C" {
#endif
struct VM_Instruction_t {
uint8_t op;
uint8_t rd;
uint8_t rx;
uint8_t ry;
uint8_t imm;
};
typedef struct VM_Instruction_t VM_Instruction;
struct VM_t {
uint8_t R[VM_REG_SIZE];
uint8_t PC;
uint8_t M[VM_MEM_SIZE];
uint8_t carry;
bool halted;
uint8_t (*syscall)(struct VM_t* vm, uint8_t callno);
};
typedef struct VM_t VM;
VM * new_vm();
void free_vm(VM *vm);
void vm_reset(VM *vm);
void vm_clear(VM *vm);
uint8_t vm_get_r(VM *vm, uint8_t r);
void vm_put_r(VM *vm, uint8_t r, uint8_t v);
void vm_decode_Q(VM_Instruction *inst, uint16_t raw);
void vm_decode_S(VM_Instruction *inst, uint16_t raw);
void vm_decode_T(VM_Instruction *inst, uint16_t raw);
uint8_t vm_subtract(uint8_t x, uint8_t y, uint8_t *ptr_borrow);
void vm_step(VM *vm);
#ifdef __cplusplus
}
#endif
#endif //UVM_VM_H