/*
* Unnamed 8-bit processing unit
* Made by github.com/xamidev
*
* This is free and unencumbered software released into the public domain.
* For more information, please refer to
*/
#include
#include
#include
#include
#include
#define MEM_SIZE 256
#define NUM_REGISTERS 4
/*
* Instruction set
* Here, we're making a RISC (reduced instruction set computer)
* so we're staying minimalistic.
*/
typedef enum
{
// 0x00 -> No operation
NOP = 0,
// 0xA? -> Memory operations
MOV = 0xA0,
// 0xB? -> Arithmetic operations
ADD = 0xB0,
SUB = 0xB1,
// 0xC? -> Bitwise operations
OR = 0xC0,
AND = 0xC1,
XOR = 0xC2,
// 0xD? -> Input/output operations
OUT = 0xD0,
IN = 0xD1,
// 0xE? -> Jump and comparisons
JMP = 0xE0,
JEQ = 0xE1,
CMP = 0xE2,
// 0xF? -> Misc operations
HLT = 0xFF
} instruction_set_t;
/*
* CPU structure definition
* Contains 4 8-bit registers, memory, a program counter, a halt switch, and flags.
*/
typedef struct
{
uint8_t reg[NUM_REGISTERS];
uint8_t memory[MEM_SIZE];
uint16_t pc;
bool halted;
bool equal_flag;
int flag_clear_delay;
} CPU_t;
CPU_t cpu;
/*
* Initializing the CPU: program counter and registers to zero, halted flag to false.
*/
void cpu_init()
{
cpu.pc = 0;
cpu.halted = false;
cpu.equal_flag = false;
cpu.flag_clear_delay = 0;
for (size_t i=0; i 0)
{
cpu.flag_clear_delay--;
if (cpu.flag_clear_delay == 0)
{
cpu.equal_flag = false;
}
}
switch (opcode)
{
// Some lines are repeating.. Should make this a better way..
case NOP:
break;
case MOV:
reg1 = cpu.memory[cpu.pc++];
reg2 = cpu.memory[cpu.pc++];
cpu.reg[reg1] = cpu.reg[reg2];
break;
case ADD:
reg1 = cpu.memory[cpu.pc++];
reg2 = cpu.memory[cpu.pc++];
cpu.reg[reg1] += cpu.reg[reg2];
break;
case SUB:
reg1 = cpu.memory[cpu.pc++];
reg2 = cpu.memory[cpu.pc++];
cpu.reg[reg1] -= cpu.reg[reg2];
break;
case OR:
reg1 = cpu.memory[cpu.pc++];
reg2 = cpu.memory[cpu.pc++];
cpu.reg[reg1] |= cpu.reg[reg2];
break;
case AND:
reg1 = cpu.memory[cpu.pc++];
reg2 = cpu.memory[cpu.pc++];
cpu.reg[reg1] &= cpu.reg[reg2];
break;
case XOR:
reg1 = cpu.memory[cpu.pc++];
reg2 = cpu.memory[cpu.pc++];
cpu.reg[reg1] ^= cpu.reg[reg2];
break;
case HLT:
cpu.halted = true;
break;
case JMP:
addr = cpu.memory[cpu.pc++];
cpu.pc = addr;
break;
case JEQ:
reg1 = cpu.memory[cpu.pc++];
addr = cpu.memory[cpu.pc++];
if (cpu.equal_flag) {
cpu.pc = addr;
}
break;
case CMP:
reg1 = cpu.memory[cpu.pc++];
reg2 = cpu.memory[cpu.pc++];
cpu.equal_flag = (cpu.reg[reg1] == cpu.reg[reg2]);
cpu.flag_clear_delay = 2;
break;
case OUT:
reg1 = cpu.memory[cpu.pc++];
putchar(cpu.reg[reg1]);
break;
case IN:
reg1 = cpu.memory[cpu.pc++];
cpu.reg[reg1] = getchar();
break;
default:
printf("Unknown instruction: 0x%02X\n", opcode);
cpu.halted = true;
break;
}
}
/*
* Loading the program in memory
*/
void cpu_load(const uint8_t* program, size_t size)
{
for (size_t i=0; i\n", argv[0]);
return -1;
}
assemble(argv[1]);
// Dumping our program
mem_dump();
reg_write(1, 0x68);
reg_write(2, 0x69);
reg_write(3, 0x21);
cpu_run();
// Post-mortem analysis
cpu_dump();
return 0;
}