Taylor Bockman
7 years ago
committed by
GitHub
9 changed files with 432 additions and 76 deletions
@ -1,36 +1,91 @@ |
|||||||
// This file is part of UVM.
|
// This file is part of uVM.
|
||||||
//
|
//
|
||||||
// UVM is free software: you can redistribute it and/or modify
|
// uVM is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
//
|
||||||
// UVM is distributed in the hope that it will be useful,
|
// uVM is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with UVM. If not, see <http://www.gnu.org/licenses/>.
|
// along with uVM. If not, see <http://www.gnu.org/licenses/>.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include <vector> |
||||||
|
|
||||||
#ifndef CPU_H_ |
#ifndef CPU_H_ |
||||||
#define CPU_H_ |
#define CPU_H_ |
||||||
|
|
||||||
|
// CPU is a wrapper around our representation of a virtual CPU.
|
||||||
class CPU { |
class CPU { |
||||||
private: |
private: |
||||||
int sp; // Stack pointer
|
int sp = -1; // Stack pointer
|
||||||
int ip; // Instruction pointer
|
int ip = 0; // Instruction pointer
|
||||||
int fp; // Frame pointer
|
int fp; // Frame pointer
|
||||||
|
std::vector<int> code; // Our code in memory
|
||||||
|
|
||||||
|
int *stack = NULL; |
||||||
|
|
||||||
// Stack goes here
|
|
||||||
|
|
||||||
|
// XXX: FIND A C++ TEST FRAMEWORK OMG LOL
|
||||||
public: |
public: |
||||||
CPU(); // The CPU should be initialized with the code and stuff
|
enum opcode { |
||||||
fetch(); |
IPUSH = 0x0, |
||||||
decode(); |
ISAVE0 = 0x1, |
||||||
execute(); |
ISAVE1 = 0x2, |
||||||
run(); // Runs the loaded code...this should have something in args
|
ISAVE2 = 0x3, |
||||||
|
ILOAD0 = 0x4, |
||||||
|
ILOAD1 = 0x5, |
||||||
|
ILOAD2 = 0x6, |
||||||
|
CMP = 0x7, |
||||||
|
IADD = 0x8, |
||||||
|
ISUB = 0x9, |
||||||
|
JMP = 0xA, |
||||||
|
JC = 0xB, |
||||||
|
HALT = 0xC, |
||||||
|
PRINT = 0xD, |
||||||
|
IMUL = 0xE, |
||||||
|
CALL = 0xF, // XXX: IMPLEMENT
|
||||||
|
IDIV = 0x10, |
||||||
|
IREM = 0x11, |
||||||
|
}; |
||||||
|
|
||||||
|
// Temporary registers
|
||||||
|
int t0; |
||||||
|
int t1; |
||||||
|
int t2; |
||||||
|
|
||||||
|
// Comparison flag register
|
||||||
|
int cf; |
||||||
|
|
||||||
|
const char* opcode_map[18] = { |
||||||
|
"IPUSH", |
||||||
|
"ISAVE0", |
||||||
|
"ISAVE1", |
||||||
|
"ISAVE2", |
||||||
|
"ILOAD0", |
||||||
|
"ILOAD1", |
||||||
|
"ILOAD2", |
||||||
|
"CMP", |
||||||
|
"IADD", |
||||||
|
"ISUB", |
||||||
|
"JMP", |
||||||
|
"JC", |
||||||
|
"HALT", |
||||||
|
"PRINT", |
||||||
|
"IMUL", |
||||||
|
"CALL", |
||||||
|
"IDIV", |
||||||
|
"IREM", |
||||||
|
}; |
||||||
|
|
||||||
|
CPU(unsigned int); |
||||||
|
~CPU(); |
||||||
|
bool load(std::vector<int> code); |
||||||
|
void run(); |
||||||
}; |
}; |
||||||
|
|
||||||
#endif // CPU_H_
|
#endif // CPU_H_
|
||||||
|
@ -0,0 +1,18 @@ |
|||||||
|
// This file is part of uVM.
|
||||||
|
//
|
||||||
|
// uVM is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// uVM is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with uVM. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
void LogMem(int, const char[]); |
||||||
|
void LogMemWithArg(int, const char[], int); |
@ -1,15 +1,165 @@ |
|||||||
// This file is part of UVM.
|
// This file is part of uVM.
|
||||||
//
|
//
|
||||||
// UVM is free software: you can redistribute it and/or modify
|
// uVM is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
//
|
||||||
// UVM is distributed in the hope that it will be useful,
|
// uVM is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with UVM. If not, see <http://www.gnu.org/licenses/>.
|
// along with uVM. If not, see <http://www.gnu.org/licenses/>.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include <vector> |
||||||
|
#include <iostream> |
||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
#include "../include/cpu.h" |
||||||
|
#include "../include/logging.h" |
||||||
|
|
||||||
|
CPU::CPU(unsigned int stack_size) { |
||||||
|
// If someone says stack_size 0 they are in for a surprise
|
||||||
|
stack = (int*)malloc(stack_size * sizeof(int)); |
||||||
|
} |
||||||
|
|
||||||
|
CPU::~CPU() { |
||||||
|
if(stack != NULL) { |
||||||
|
free(stack); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Load loads the code into CPU memory...and always returns true
|
||||||
|
bool CPU::load(std::vector<int> c) { |
||||||
|
code = c; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
// Run is the main loop of the entire CPU. It handles each opcode individually.
|
||||||
|
//
|
||||||
|
// At the end of each run the stack pointer and instruction pointer are reset to get the CPU
|
||||||
|
// ready to load the next program.
|
||||||
|
void CPU::run() { |
||||||
|
while(code[ip] != CPU::opcode::HALT && ip != code.size()) { |
||||||
|
switch(code[ip]) { |
||||||
|
case CPU::opcode::IPUSH: { |
||||||
|
LogMemWithArg(ip, CPU::opcode_map[code[ip]], code[ip + 1]); |
||||||
|
stack[++sp] = code[++ip]; |
||||||
|
ip++; |
||||||
|
break; |
||||||
|
} |
||||||
|
case CPU::opcode::ISAVE0: { |
||||||
|
LogMem(ip, CPU::opcode_map[code[ip]]); |
||||||
|
t0 = stack[sp--]; |
||||||
|
ip++; |
||||||
|
break; |
||||||
|
} |
||||||
|
case CPU::opcode::ISAVE1: { |
||||||
|
LogMem(ip, CPU::opcode_map[code[ip]]); |
||||||
|
t1 = stack[sp--]; |
||||||
|
ip++; |
||||||
|
break; |
||||||
|
} |
||||||
|
case CPU::opcode::ISAVE2: { |
||||||
|
LogMem(ip, CPU::opcode_map[code[ip]]); |
||||||
|
t2 = stack[sp--]; |
||||||
|
ip++; |
||||||
|
break; |
||||||
|
} |
||||||
|
case CPU::opcode::ILOAD0: { |
||||||
|
LogMem(ip, CPU::opcode_map[code[ip]]); |
||||||
|
stack[++sp] = t0; |
||||||
|
ip++; |
||||||
|
break; |
||||||
|
} |
||||||
|
case CPU::opcode::ILOAD1: { |
||||||
|
LogMem(ip, CPU::opcode_map[code[ip]]); |
||||||
|
stack[++sp] = t1; |
||||||
|
ip++; |
||||||
|
break; |
||||||
|
} |
||||||
|
case CPU::opcode::ILOAD2: { |
||||||
|
LogMem(ip, CPU::opcode_map[code[ip]]); |
||||||
|
stack[++sp] = t2; |
||||||
|
ip++; |
||||||
|
break; |
||||||
|
} |
||||||
|
case CPU::opcode::IADD: { |
||||||
|
LogMem(ip, CPU::opcode_map[code[ip]]); |
||||||
|
int a = stack[sp--]; |
||||||
|
int b = stack[sp--]; |
||||||
|
stack[sp] = a + b; |
||||||
|
ip++; |
||||||
|
break; |
||||||
|
} |
||||||
|
case CPU::opcode::IDIV: { |
||||||
|
LogMem(ip, CPU::opcode_map[code[ip]]); |
||||||
|
int a = stack[sp--]; |
||||||
|
int b = stack[sp--]; |
||||||
|
stack[sp] = a / b; |
||||||
|
ip++; |
||||||
|
break; |
||||||
|
} |
||||||
|
case CPU::opcode::IREM: { |
||||||
|
LogMem(ip, CPU::opcode_map[code[ip]]); |
||||||
|
int a = stack[sp--]; |
||||||
|
int b = stack[sp--]; |
||||||
|
stack[sp] = a % b; |
||||||
|
ip++; |
||||||
|
break; |
||||||
|
} |
||||||
|
case CPU::opcode::CMP: { |
||||||
|
LogMem(ip, CPU::opcode_map[code[ip]]); |
||||||
|
int a = stack[sp--]; |
||||||
|
int b = stack[sp--]; |
||||||
|
cf = a == b; |
||||||
|
ip++; |
||||||
|
break; |
||||||
|
} |
||||||
|
case CPU::opcode::JC: { |
||||||
|
LogMemWithArg(ip, CPU::opcode_map[code[ip]], code[ip + 1]); |
||||||
|
if(cf) { |
||||||
|
ip = code[ip + 1]; |
||||||
|
} else { |
||||||
|
ip += 2; |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
case CPU::opcode::ISUB: { |
||||||
|
LogMem(ip, CPU::opcode_map[code[ip]]); |
||||||
|
int a = stack[sp--]; |
||||||
|
int b = stack[sp--]; |
||||||
|
stack[sp] = a - b; |
||||||
|
ip++; |
||||||
|
break; |
||||||
|
} |
||||||
|
case CPU::opcode::IMUL: { |
||||||
|
LogMem(ip, CPU::opcode_map[code[ip]]); |
||||||
|
int a = stack[sp--]; |
||||||
|
int b = stack[sp--]; |
||||||
|
stack[sp] = a * b; |
||||||
|
ip++; |
||||||
|
break; |
||||||
|
} |
||||||
|
case CPU::opcode::JMP: { |
||||||
|
LogMemWithArg(ip, CPU::opcode_map[code[ip]], code[ip + 1]); |
||||||
|
ip = code[ip + 1]; |
||||||
|
break; |
||||||
|
} |
||||||
|
case CPU::opcode::PRINT: { |
||||||
|
LogMem(ip, CPU::opcode_map[code[ip]]); |
||||||
|
printf("%d\n", stack[sp]); |
||||||
|
sp--; |
||||||
|
ip++; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
sp = -1; |
||||||
|
ip = 0; |
||||||
|
} |
||||||
|
@ -1,32 +1,25 @@ |
|||||||
// This file is part of UVM.
|
// This file is part of uVM.
|
||||||
//
|
//
|
||||||
// UVM is free software: you can redistribute it and/or modify
|
// uVM is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
//
|
||||||
// UVM is distributed in the hope that it will be useful,
|
// uVM is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with UVM. If not, see <http://www.gnu.org/licenses/>.
|
// along with uVM. If not, see <http://www.gnu.org/licenses/>.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include <stdio.h> |
||||||
|
|
||||||
#ifndef OPCODES_H_ |
void LogMem(int addr, const char opcode[]) { |
||||||
#define OPCODES_H_ |
printf("[%.8d]\t%s\n", addr, opcode); |
||||||
|
} |
||||||
|
|
||||||
// Instructions
|
void LogMemWithArg(int addr, const char opcode[], int arg) { |
||||||
|
printf("[%.8d]\t%s\t%x\n", addr, opcode, arg); |
||||||
#define IPUSH 0x0 |
} |
||||||
#define IPUSH0 0x1 |
|
||||||
#define IPOP0 0x2 |
|
||||||
#define CMP 0x3 |
|
||||||
#define IADD 0x4 |
|
||||||
#define ISUB 0x5 |
|
||||||
#define JMP 0x6 |
|
||||||
#define JC 0x7 |
|
||||||
|
|
||||||
#endif // OPCODES_H_
|
|
@ -1,16 +1,120 @@ |
|||||||
// This file is part of UVM.
|
// This file is part of uVM.
|
||||||
//
|
//
|
||||||
// UVM is free software: you can redistribute it and/or modify
|
// uVM is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
//
|
||||||
// UVM is distributed in the hope that it will be useful,
|
// uVM is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with UVM. If not, see <http://www.gnu.org/licenses/>.
|
// along with uVM. If not, see <http://www.gnu.org/licenses/>.
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include "../include/cpu.h" |
||||||
|
|
||||||
|
int main(int argc, char* argv[]) { |
||||||
|
// Allocate an 800 word stack size to the CPU on initialization
|
||||||
|
CPU c(800); |
||||||
|
|
||||||
|
// XXX:
|
||||||
|
// Until I find a unit testing framework these vectors of bytecode will
|
||||||
|
// serve as (terrible) tests.
|
||||||
|
|
||||||
|
// Answer: 3
|
||||||
|
std::vector<int> code_iadd { |
||||||
|
CPU::opcode::IPUSH, 0x1, |
||||||
|
CPU::opcode::IPUSH, 0x2, |
||||||
|
CPU::opcode::IADD, |
||||||
|
CPU::opcode::PRINT, |
||||||
|
CPU::opcode::HALT |
||||||
|
}; |
||||||
|
|
||||||
|
// Answer: 1
|
||||||
|
std::vector<int> code_isub { |
||||||
|
CPU::opcode::IPUSH, 0x1, |
||||||
|
CPU::opcode::IPUSH, 0x2, |
||||||
|
CPU::opcode::ISUB, |
||||||
|
CPU::opcode::PRINT, |
||||||
|
CPU::opcode::HALT |
||||||
|
}; |
||||||
|
|
||||||
|
// Answer: 16
|
||||||
|
std::vector<int> code_imul { |
||||||
|
CPU::opcode::IPUSH, 0x4, |
||||||
|
CPU::opcode::IPUSH, 0x4, |
||||||
|
CPU::opcode::IMUL, |
||||||
|
CPU::opcode::PRINT, |
||||||
|
CPU::opcode::HALT, |
||||||
|
}; |
||||||
|
|
||||||
|
// Answer: 1
|
||||||
|
std::vector<int> code_idiv { |
||||||
|
CPU::opcode::IPUSH, 0x4, |
||||||
|
CPU::opcode::IPUSH, 0x4, |
||||||
|
CPU::opcode::IDIV, |
||||||
|
CPU::opcode::PRINT, |
||||||
|
CPU::opcode::HALT, |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
// Answer: 3 % 4 = 3 (0 remainder 3)
|
||||||
|
std::vector<int> code_irem { |
||||||
|
CPU::opcode::IPUSH, 0x4, |
||||||
|
CPU::opcode::IPUSH, 0x3, |
||||||
|
CPU::opcode::IREM, |
||||||
|
CPU::opcode::PRINT, |
||||||
|
CPU::opcode::HALT, |
||||||
|
}; |
||||||
|
|
||||||
|
// Answer: 8
|
||||||
|
// This tests unconditional jumping. If the JMP instruction fails the code will fail to
|
||||||
|
// answer correctly.
|
||||||
|
std::vector<int> code_jmp { |
||||||
|
CPU::opcode::IPUSH, 0x4, |
||||||
|
CPU::opcode::IPUSH, 0x4, |
||||||
|
CPU::opcode::JMP, 0x7, // JMP to 0x7 - the 8th line of code in memory
|
||||||
|
CPU::opcode::ISUB, |
||||||
|
CPU::opcode::IADD, |
||||||
|
CPU::opcode::PRINT, |
||||||
|
CPU::opcode::HALT, |
||||||
|
}; |
||||||
|
|
||||||
|
// Answer: 10
|
||||||
|
// This tests the use of conditional jumps to implement a simple while loop.
|
||||||
|
std::vector<int> code_jc { |
||||||
|
CPU::opcode::IPUSH, 0xA, // Limit
|
||||||
|
CPU::opcode::ISAVE1, // Store the limit
|
||||||
|
CPU::opcode::IPUSH, 0x0, // Counter
|
||||||
|
CPU::opcode::ISAVE2, // Store the counter
|
||||||
|
CPU::opcode::IPUSH, 0x0, // Accumulator (for our print)
|
||||||
|
CPU::opcode::ISAVE0, // Store the accumulator
|
||||||
|
CPU::opcode::ILOAD1, // Load the limit
|
||||||
|
CPU::opcode::ILOAD2, // Load the counter
|
||||||
|
CPU::opcode::CMP, // Compare limit to counter
|
||||||
|
CPU::opcode::JC, 0x1A, // Jump to PRINT if true
|
||||||
|
CPU::opcode::ILOAD0, // Load the accumulator
|
||||||
|
CPU::opcode::IPUSH, 0x1, // Push 1 onto the stack to be added to the accumulator
|
||||||
|
CPU::opcode::IADD, // Add 1 to the accumulator
|
||||||
|
CPU::opcode::ISAVE0, // Take the top of the stack, store it in t0 and pop it off
|
||||||
|
CPU::opcode::ILOAD2, // Reload the counter
|
||||||
|
CPU::opcode::IPUSH, 0x1, // Push 1 onto the stack to be added to the counter
|
||||||
|
CPU::opcode::IADD, // Add one to the accumulator
|
||||||
|
CPU::opcode::ISAVE2, // Store the counter
|
||||||
|
CPU::opcode::JMP, 0x9, // Jump to the CMP statement
|
||||||
|
CPU::opcode::ILOAD0, // Load the accumulator for PRINT
|
||||||
|
CPU::opcode::PRINT, |
||||||
|
CPU::opcode::HALT, |
||||||
|
}; |
||||||
|
|
||||||
|
c.load(code_jc); |
||||||
|
c.run(); |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
Loading…
Reference in new issue