Init
This commit is contained in:
commit
9d02af574f
|
|
@ -0,0 +1 @@
|
||||||
|
main
|
||||||
|
|
@ -0,0 +1,161 @@
|
||||||
|
* Overture CPU Emulator
|
||||||
|
|
||||||
|
This project implements an emulator for the Overture Instruction set architecture.
|
||||||
|
|
||||||
|
This theoretical ISA is described and implemented in the steam game Turing Complete.
|
||||||
|
|
||||||
|
** ISA Spec
|
||||||
|
|
||||||
|
Overture has 1 byte-wide instructions.
|
||||||
|
|
||||||
|
This contains 6 registers, of which the first 4 have designated uses.
|
||||||
|
|
||||||
|
Additionally, it contains a single input and a single output, which together are mapped as if they were a 7th register.
|
||||||
|
|
||||||
|
|
||||||
|
The instructions can be split into 4 categories, which can be distinguished by the most significant two bits.
|
||||||
|
|
||||||
|
🔴🔴🟤🟤🟤🟤🟤🟤 Immidiate instructions.
|
||||||
|
🔴🟢🟤🟤🟤🟤🟤🟤 Calculate instructions.
|
||||||
|
🟢🔴🟤🟤🟤🟤🟤🟤 Copy instructions.
|
||||||
|
🟢🟢🟤🟤🟤🟤🟤🟤 Condition instructions.
|
||||||
|
|
||||||
|
* Immediate 🔴🔴🟤🟤🟤🟤🟤🟤
|
||||||
|
These instructions represent a number between 0 and 63 (inclusive), which will be put into register 0
|
||||||
|
|
||||||
|
Assembly Mnemonics
|
||||||
|
Generally, these instructions are represented directly by using the value (a number between 0 and 63) directly, since they represent the actual number.
|
||||||
|
|
||||||
|
* Calculate 🔴🟢🟤🟤🟤🟤🟤🟤
|
||||||
|
This set of instructions use the 3 least significant bits to indicate which instruction to preform on the contents of Register 1 and Register 2
|
||||||
|
|
||||||
|
🔴🟢🟤🟤🟤🔴🔴🔴 OR
|
||||||
|
🔴🟢🟤🟤🟤🔴🔴🟢 NAND
|
||||||
|
🔴🟢🟤🟤🟤🔴🟢🔴 NOR
|
||||||
|
🔴🟢🟤🟤🟤🔴🟢🟢 AND
|
||||||
|
🔴🟢🟤🟤🟤🟢🔴🔴 ADD
|
||||||
|
🔴🟢🟤🟤🟤🟢🔴🟢 SUB
|
||||||
|
|
||||||
|
|
||||||
|
OR 🔴🟢🟤🟤🟤🔴🔴🔴
|
||||||
|
A bitwise OR of the values stored in Register 1 and register 2, with the result being placed in Register 3
|
||||||
|
|
||||||
|
NAND 🔴🟢🟤🟤🟤🔴🔴🟢
|
||||||
|
A bitwise NAND of the values stored in Register 1 and register 2, with the result being placed in Register 3
|
||||||
|
|
||||||
|
NOR 🔴🟢🟤🟤🟤🔴🟢🔴
|
||||||
|
A bitwise NOR of the values stored in Register 1 and register 2, with the result being placed in Register 3
|
||||||
|
|
||||||
|
AND 🔴🟢🟤🟤🟤🔴🟢🟢
|
||||||
|
A bitwise AND of the values stored in Register 1 and register 2, with the result being placed in Register 3
|
||||||
|
|
||||||
|
ADD 🔴🟢🟤🟤🟤🟢🔴🔴
|
||||||
|
Mathematically adds the the values stored in Register 1 and register 2 together, (without support for a carry), with the result being placed in Register 3
|
||||||
|
|
||||||
|
SUB 🔴🟢🟤🟤🟤🟢🔴🟢
|
||||||
|
Subtracts the the values stored in Register 2 from the value stored in register 1 using 2-complements logic, with the result being placed in Register 3
|
||||||
|
|
||||||
|
Assembly Mnemonics
|
||||||
|
The assembly mnemonics for these commands are simply these commands written on their own.
|
||||||
|
|
||||||
|
#+BEGIN_SRC
|
||||||
|
or
|
||||||
|
nand
|
||||||
|
nor
|
||||||
|
and
|
||||||
|
add
|
||||||
|
sub
|
||||||
|
|
||||||
|
const or 64
|
||||||
|
const nand 65
|
||||||
|
const nor 66
|
||||||
|
const and 67
|
||||||
|
const add 68
|
||||||
|
const sub 69
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
* Copy 🟢🔴🟤🟤🟤🟤🟤🟤
|
||||||
|
These instructions allow you to represent a copy action between a source location and a destination location.
|
||||||
|
|
||||||
|
There are 7 possible sources to read from:
|
||||||
|
|
||||||
|
🟢🔴🔴🔴🔴🟤🟤🟤 Register 0
|
||||||
|
🟢🔴🔴🔴🟢🟤🟤🟤 Register 1
|
||||||
|
🟢🔴🔴🟢🔴🟤🟤🟤 Register 2
|
||||||
|
🟢🔴🔴🟢🟢🟤🟤🟤 Register 3
|
||||||
|
🟢🔴🟢🔴🔴🟤🟤🟤 Register 4
|
||||||
|
🟢🔴🟢🔴🟢🟤🟤🟤 Register 5
|
||||||
|
🟢🔴🟢🟢🔴🟤🟤🟤 Input
|
||||||
|
|
||||||
|
|
||||||
|
There are 7 possible destinations to write to:
|
||||||
|
|
||||||
|
🟢🔴🟤🟤🟤🔴🔴🔴 Register 0
|
||||||
|
🟢🔴🟤🟤🟤🔴🔴🟢 Register 1
|
||||||
|
🟢🔴🟤🟤🟤🔴🟢🔴 Register 2
|
||||||
|
🟢🔴🟤🟤🟤🔴🟢🟢 Register 3
|
||||||
|
🟢🔴🟤🟤🟤🟢🔴🔴 Register 4
|
||||||
|
🟢🔴🟤🟤🟤🟢🔴🟢 Register 5
|
||||||
|
🟢🔴🟤🟤🟤🟢🟢🔴 Output
|
||||||
|
|
||||||
|
|
||||||
|
Assembly Mnemonics
|
||||||
|
|
||||||
|
A common method of representing the commands is by idividually specifying the parts of the command and adding them together using the OR operator.
|
||||||
|
|
||||||
|
#+BEGIN_SRC
|
||||||
|
const cp 128
|
||||||
|
const s0 0
|
||||||
|
const s1 8
|
||||||
|
const s2 16
|
||||||
|
const s3 24
|
||||||
|
const s4 32
|
||||||
|
const s5 40
|
||||||
|
const in 48
|
||||||
|
const d0 0
|
||||||
|
const d1 1
|
||||||
|
const d2 2
|
||||||
|
const d3 3
|
||||||
|
const d4 4
|
||||||
|
const d5 5
|
||||||
|
const out 6
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
Example, which allows you to copy from Register 3 to Register 0
|
||||||
|
cp|s3|d0
|
||||||
|
|
||||||
|
Off Label Usage
|
||||||
|
This section is non-normative
|
||||||
|
|
||||||
|
Most common implementations are implemented in such way that if you specify the non-defined option as a source, a zero is read
|
||||||
|
|
||||||
|
🟢🔴🟢🟢🟢🟤🟤🟤 Clear Destination Register
|
||||||
|
|
||||||
|
* Condition 🟢🟢🟤🟤🟤🟤🟤🟤
|
||||||
|
This set of instructions allow you to jump to a certain point in your program.
|
||||||
|
|
||||||
|
The destination of the jump is specified by the Register 0.
|
||||||
|
|
||||||
|
The jump will only be performed if the value in Register 3 matches the condition set by the least significant 3 bits of the instruction.
|
||||||
|
|
||||||
|
🟢🟢🟤🟤🟤🔴🔴🔴 Never
|
||||||
|
🟢🟢🟤🟤🟤🔴🔴🟢 Equals 0
|
||||||
|
🟢🟢🟤🟤🟤🔴🟢🔴 Less Than 0
|
||||||
|
🟢🟢🟤🟤🟤🔴🟢🟢 Less than or equals 0
|
||||||
|
🟢🟢🟤🟤🟤🟢🔴🔴 Always
|
||||||
|
🟢🟢🟤🟤🟤🟢🔴🟢 Not equals 0
|
||||||
|
🟢🟢🟤🟤🟤🟢🟢🔴 Greater than or equals 0
|
||||||
|
🟢🟢🟤🟤🟤🟢🟢🟢 Greater than 0
|
||||||
|
|
||||||
|
|
||||||
|
Assembly Mnemonics
|
||||||
|
#+BEGIN_SRC
|
||||||
|
const never 192
|
||||||
|
const eq 193
|
||||||
|
const less 194
|
||||||
|
const less_eq 195
|
||||||
|
const always 196
|
||||||
|
const not_eq 197
|
||||||
|
const greater_eq 198
|
||||||
|
const greater 199
|
||||||
|
#+END_SRC
|
||||||
|
|
@ -0,0 +1,310 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
enum overture_mode {
|
||||||
|
IMMEDIATE,
|
||||||
|
CALCULATE,
|
||||||
|
COPY,
|
||||||
|
CONDITION
|
||||||
|
};
|
||||||
|
|
||||||
|
enum overture_calculate_mode {
|
||||||
|
OR,
|
||||||
|
NAND,
|
||||||
|
NOR,
|
||||||
|
AND,
|
||||||
|
ADD,
|
||||||
|
SUB
|
||||||
|
};
|
||||||
|
|
||||||
|
enum overture_condition_mode {
|
||||||
|
NEVER,
|
||||||
|
EQZ,
|
||||||
|
LTZ,
|
||||||
|
LTEQZ,
|
||||||
|
ALWAYS,
|
||||||
|
NEQZ,
|
||||||
|
GTEQZ,
|
||||||
|
GTZ
|
||||||
|
};
|
||||||
|
|
||||||
|
enum overture_registers {
|
||||||
|
R0,
|
||||||
|
R1,
|
||||||
|
R2,
|
||||||
|
R3,
|
||||||
|
R4,
|
||||||
|
R5,
|
||||||
|
INPUT_OUTPUT
|
||||||
|
};
|
||||||
|
|
||||||
|
void overture_immediate(uint8_t immediate, uint8_t* r0) {
|
||||||
|
*r0 = immediate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void overture_alu_calculate(enum overture_calculate_mode mode, uint8_t r1, uint8_t r2, uint8_t* r3) {
|
||||||
|
switch (mode) {
|
||||||
|
case OR:
|
||||||
|
*r3 = r1 | r2;
|
||||||
|
break;
|
||||||
|
case NAND:
|
||||||
|
*r3 = ~(r1 & r2);
|
||||||
|
break;
|
||||||
|
case NOR:
|
||||||
|
*r3 = ~(r1 | r2);
|
||||||
|
break;
|
||||||
|
case AND:
|
||||||
|
*r3 = r1 & r2;
|
||||||
|
break;
|
||||||
|
case ADD:
|
||||||
|
*r3 = r1 + r2;
|
||||||
|
break;
|
||||||
|
case SUB:
|
||||||
|
*r3 = r1 - r2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void overture_register_copy(uint8_t* source, uint8_t* destination) {
|
||||||
|
*destination = *source;
|
||||||
|
};
|
||||||
|
|
||||||
|
void overture_condition_jump(enum overture_condition_mode mode, uint8_t r0, uint8_t r3, uint8_t* program_counter) {
|
||||||
|
switch (mode) {
|
||||||
|
case NEVER:
|
||||||
|
return;
|
||||||
|
case EQZ:
|
||||||
|
if (r3 == 0)
|
||||||
|
*program_counter = r0;
|
||||||
|
break;
|
||||||
|
case LTZ:
|
||||||
|
if (r3 < 0)
|
||||||
|
*program_counter = r0;
|
||||||
|
break;
|
||||||
|
case LTEQZ:
|
||||||
|
if (r3 <= 0)
|
||||||
|
*program_counter = r0;
|
||||||
|
break;
|
||||||
|
case ALWAYS:
|
||||||
|
*program_counter = r0;
|
||||||
|
break;
|
||||||
|
case NEQZ:
|
||||||
|
if (r3 != 0)
|
||||||
|
*program_counter = r0;
|
||||||
|
break;
|
||||||
|
case GTEQZ:
|
||||||
|
if (r3 >= 0)
|
||||||
|
*program_counter = r0;
|
||||||
|
break;
|
||||||
|
case GTZ:
|
||||||
|
if (r3 > 0)
|
||||||
|
*program_counter = r0;
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct overture_decoded_instruction {
|
||||||
|
uint8_t overture_mode : 2;
|
||||||
|
uint8_t left_operand : 3;
|
||||||
|
uint8_t right_operand : 3;
|
||||||
|
uint8_t combined_immediate : 6;
|
||||||
|
} overture_decoded_instruction;
|
||||||
|
|
||||||
|
overture_decoded_instruction overture_decode_instruction(uint8_t current_instruction) {
|
||||||
|
overture_decoded_instruction instruction = {
|
||||||
|
.overture_mode = current_instruction >> 6,
|
||||||
|
.left_operand = (current_instruction << 2) >> 5,
|
||||||
|
.right_operand = (current_instruction << 5) >> 5,
|
||||||
|
.combined_immediate = (current_instruction << 2) >> 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
return instruction;
|
||||||
|
};
|
||||||
|
|
||||||
|
void overture_fetch_instruction(uint8_t program_counter, uint8_t* current_instruction, uint8_t rom[256]) {
|
||||||
|
*current_instruction = rom[program_counter - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct overture {
|
||||||
|
bool step_mode;
|
||||||
|
int clock_speed_hz;
|
||||||
|
uint8_t program_counter;
|
||||||
|
|
||||||
|
uint8_t r0;
|
||||||
|
uint8_t r1;
|
||||||
|
uint8_t r2;
|
||||||
|
uint8_t r3;
|
||||||
|
uint8_t r4;
|
||||||
|
uint8_t r5;
|
||||||
|
|
||||||
|
uint8_t input;
|
||||||
|
uint8_t output;
|
||||||
|
bool output_enable;
|
||||||
|
|
||||||
|
uint8_t current_instruction;
|
||||||
|
} overture;
|
||||||
|
|
||||||
|
void sleep_hz(int hz) {
|
||||||
|
usleep(1000000 / hz);
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t* multiplex_source(uint8_t operand, overture* cpu) {
|
||||||
|
uint8_t* source;
|
||||||
|
|
||||||
|
switch (operand) {
|
||||||
|
case R0:
|
||||||
|
source = &(cpu->r0);
|
||||||
|
break;
|
||||||
|
case R1:
|
||||||
|
source = &(cpu->r1);
|
||||||
|
break;
|
||||||
|
case R2:
|
||||||
|
source = &(cpu->r2);
|
||||||
|
break;
|
||||||
|
case R3:
|
||||||
|
source = &(cpu->r3);
|
||||||
|
break;
|
||||||
|
case R4:
|
||||||
|
source = &(cpu->r4);
|
||||||
|
break;
|
||||||
|
case R5:
|
||||||
|
source = &(cpu->r5);
|
||||||
|
break;
|
||||||
|
case INPUT_OUTPUT:
|
||||||
|
source = &(cpu->input);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* multiplex_destination(uint8_t operand, overture* cpu) {
|
||||||
|
uint8_t* destination;
|
||||||
|
|
||||||
|
switch (operand) {
|
||||||
|
case R0:
|
||||||
|
destination = &(cpu->r0);
|
||||||
|
break;
|
||||||
|
case R1:
|
||||||
|
destination = &(cpu->r1);
|
||||||
|
break;
|
||||||
|
case R2:
|
||||||
|
destination = &(cpu->r2);
|
||||||
|
break;
|
||||||
|
case R3:
|
||||||
|
destination = &(cpu->r3);
|
||||||
|
break;
|
||||||
|
case R4:
|
||||||
|
destination = &(cpu->r4);
|
||||||
|
break;
|
||||||
|
case R5:
|
||||||
|
destination = &(cpu->r5);
|
||||||
|
break;
|
||||||
|
case INPUT_OUTPUT:
|
||||||
|
destination = &(cpu->output);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
void overture_execute_instruction(overture_decoded_instruction instruction, overture* cpu) {
|
||||||
|
uint8_t* source_register = multiplex_source(instruction.left_operand, cpu);
|
||||||
|
uint8_t* destination_register = multiplex_destination(instruction.right_operand, cpu);
|
||||||
|
|
||||||
|
switch (instruction.overture_mode) {
|
||||||
|
case IMMEDIATE:
|
||||||
|
overture_immediate(instruction.combined_immediate, &(cpu->r0));
|
||||||
|
break;
|
||||||
|
case CALCULATE:
|
||||||
|
overture_alu_calculate(instruction.right_operand, cpu->r1, cpu->r2, &(cpu->r3));
|
||||||
|
break;
|
||||||
|
case COPY:
|
||||||
|
overture_register_copy(source_register, destination_register);
|
||||||
|
|
||||||
|
if (instruction.right_operand == INPUT_OUTPUT) {
|
||||||
|
cpu->output_enable = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CONDITION:
|
||||||
|
overture_condition_jump(instruction.right_operand, cpu->r0, cpu->r3, &(cpu->program_counter));
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void cpu_diagnostics(overture* cpu) {
|
||||||
|
if (cpu->step_mode) {
|
||||||
|
printf("Step mode\n");
|
||||||
|
} else {
|
||||||
|
printf("Clock speed: %dhz\n", cpu->clock_speed_hz);
|
||||||
|
}
|
||||||
|
printf("Program counter: %d\n\n", cpu->program_counter);
|
||||||
|
|
||||||
|
printf("r0: %d\n", cpu->r0);
|
||||||
|
printf("r1: %d\n", cpu->r1);
|
||||||
|
printf("r2: %d\n", cpu->r2);
|
||||||
|
printf("r3: %d\n", cpu->r3);
|
||||||
|
printf("r4: %d\n", cpu->r4);
|
||||||
|
printf("r5: %d\n\n", cpu->r5);
|
||||||
|
|
||||||
|
printf("input: %d\n", cpu->input);
|
||||||
|
printf("output: %d\n", cpu->output);
|
||||||
|
|
||||||
|
printf("Current instruction: %d\n\n", cpu->current_instruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
void overture_cycle(overture* cpu, uint8_t rom[256]) {
|
||||||
|
if (cpu->program_counter == 255) {
|
||||||
|
cpu->program_counter = 0;
|
||||||
|
};
|
||||||
|
cpu->program_counter += 1;
|
||||||
|
cpu->output_enable = false;
|
||||||
|
overture_fetch_instruction(cpu->program_counter, &(cpu->current_instruction), rom);
|
||||||
|
|
||||||
|
overture_decoded_instruction instruction = overture_decode_instruction(cpu->current_instruction);
|
||||||
|
overture_execute_instruction(instruction, cpu);
|
||||||
|
|
||||||
|
cpu_diagnostics(cpu);
|
||||||
|
}
|
||||||
|
|
||||||
|
void overture_start_clock(overture* cpu, uint8_t rom[256]) {
|
||||||
|
while (true) {
|
||||||
|
if (cpu->step_mode) {
|
||||||
|
getchar();
|
||||||
|
} else {
|
||||||
|
sleep_hz(cpu->clock_speed_hz);
|
||||||
|
}
|
||||||
|
|
||||||
|
overture_cycle(cpu, rom);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
uint8_t rom[256] = { 63, 129, 7, 130, 68 };
|
||||||
|
overture cpu = {
|
||||||
|
.step_mode = true,
|
||||||
|
.clock_speed_hz = 1,
|
||||||
|
.program_counter = 0,
|
||||||
|
|
||||||
|
.r0 = 0,
|
||||||
|
.r1 = 0,
|
||||||
|
.r2 = 0,
|
||||||
|
.r3 = 0,
|
||||||
|
.r4 = 0,
|
||||||
|
.r5 = 0,
|
||||||
|
|
||||||
|
.input = 0,
|
||||||
|
.output = 0,
|
||||||
|
.output_enable = true,
|
||||||
|
|
||||||
|
.current_instruction = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
overture_start_clock(&cpu, rom);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue