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