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
|
||||
// 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,
|
||||
// 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/>.
|
||||
// along with uVM. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#include <vector> |
||||
|
||||
#ifndef CPU_H_ |
||||
#define CPU_H_ |
||||
|
||||
// CPU is a wrapper around our representation of a virtual CPU.
|
||||
class CPU { |
||||
private: |
||||
int sp; // Stack pointer
|
||||
int ip; // Instruction pointer
|
||||
int fp; // Frame pointer
|
||||
int sp = -1; // Stack pointer
|
||||
int ip = 0; // Instruction 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: |
||||
CPU(); // The CPU should be initialized with the code and stuff
|
||||
fetch(); |
||||
decode(); |
||||
execute(); |
||||
run(); // Runs the loaded code...this should have something in args
|
||||
enum opcode { |
||||
IPUSH = 0x0, |
||||
ISAVE0 = 0x1, |
||||
ISAVE1 = 0x2, |
||||
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_
|
||||
|
@ -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
|
||||
// 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,
|
||||
// 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/>.
|
||||
// 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
|
||||
// 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,
|
||||
// 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/>.
|
||||
// along with uVM. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#include <stdio.h> |
||||
|
||||
#ifndef OPCODES_H_ |
||||
#define OPCODES_H_ |
||||
void LogMem(int addr, const char opcode[]) { |
||||
printf("[%.8d]\t%s\n", addr, opcode); |
||||
} |
||||
|
||||
// Instructions
|
||||
|
||||
#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_
|
||||
void LogMemWithArg(int addr, const char opcode[], int arg) { |
||||
printf("[%.8d]\t%s\t%x\n", addr, opcode, arg); |
||||
} |
@ -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
|
||||
// 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,
|
||||
// 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/>.
|
||||
// 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