Skip to content

Instantly share code, notes, and snippets.

@giannitedesco
Created September 15, 2011 01:02
Show Gist options
  • Save giannitedesco/1218267 to your computer and use it in GitHub Desktop.
Save giannitedesco/1218267 to your computer and use it in GitHub Desktop.
First cut of simulator for minecraft cpu
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define REG_X0_BIT (1 << 0)
#define REG_X1_BIT (1 << 1)
#define REG_Y0_BIT (1 << 2)
#define REG_Y1_BIT (1 << 3)
#define NUM_REGS 4
#define ALU_ADD 0
#define ALU_XOR 1
#define ALU_AND 2
#define ALU_OR 3
#define ALU_NOPX 4
#define ALU_NOPY 5
#define ALU_MAX 6
/* 9-bit instruction words */
#define INSN_BITS 9
#define INSN_MASK ((1 << INSN_BITS) - 1)
#define CODE_ADDR_BITS 6
#define CODE_ROM_SIZE (1 << CODE_ADDR_BITS)
#define CODE_ADDR_MASK (CODE_ROM_SIZE - 1)
struct cpu {
#define REG_X 0
#define REG_Y 2
uint8_t reg[4];
uint8_t w_en:4;
uint8_t imm:1;
uint8_t bank_x:1;
uint8_t bank_y:1;
uint8_t hlt:1;
uint8_t alu_op:3;
uint8_t carry:1;
uint8_t zero:1;
uint8_t pad_:3;
uint8_t bus;
uint8_t hold;
uint8_t ip;
uint16_t code_rom[CODE_ROM_SIZE];
unsigned int clk;
};
static const char * const cpu_reg_name(uint8_t reg)
{
static const char * const r[] = {"x0", "x1", "y0", "y1"};
if ( reg & ~0x3 )
abort();
return r[reg & 0x3];
}
static const char * const cpu_alu_name(uint8_t op)
{
static const char * const a[] = {"add", "xor", "and",
"or", "nopx", "nopy"};
if ( op >= ALU_MAX )
abort();
return a[op];
}
static unsigned int cpu_halted(struct cpu *cpu)
{
return cpu->hlt;
}
static void cpu_halt(struct cpu *cpu)
{
cpu->hlt = 1;
printf("HALT: reg x0 is 0x%.2x (%u)\n", cpu->reg[0], cpu->reg[0]);
printf("HALT: program terminated in %u cycles\n", cpu->clk);
}
static void cpu_update_alu(struct cpu *cpu)
{
uint8_t in_x, in_y, out;
uint16_t tmp;
uint8_t carry = 0;
/* get inputs */
in_x = cpu->reg[REG_X + cpu->bank_x];
in_y = cpu->reg[REG_Y + cpu->bank_y];
/* do the math */
switch(cpu->alu_op) {
case ALU_ADD:
tmp = in_x + in_y;
carry = !!(tmp & 0x100);
out = tmp;
break;
case ALU_XOR:
out = in_x ^ in_y;
break;
case ALU_AND:
out = in_x & in_y;
break;
case ALU_OR:
out = in_x | in_y;
break;
case ALU_NOPX:
out = in_x;
break;
case ALU_NOPY:
out = in_y;
break;
default:
abort();
}
/* update output state */
cpu->carry = carry;
cpu->zero = (out == 0);
cpu->hold = out;
printf(" + alu: %s %s, %s = 0x%.2x (%u) %scarry\n",
cpu_alu_name(cpu->alu_op),
cpu_reg_name(REG_X + cpu->bank_x),
cpu_reg_name(REG_Y + cpu->bank_y),
cpu->hold, cpu->hold,
cpu->carry ? "" : "no-");
}
/* Strobe write-enable lines: stores value of bus to registers */
static void cpu_wr_enable(struct cpu *cpu, uint8_t regs)
{
uint8_t i;
uint8_t in;
in = (cpu->imm) ? cpu->code_rom[cpu->ip] : cpu->bus;
for(i = 0; i < NUM_REGS; i++) {
if ( regs & (1 << i) ) {
printf(" + reg: (from %s bus) %s := 0x%.2x (%u)\n",
cpu->imm ? "code" : "data",
cpu_reg_name(i), in, in);
cpu->reg[i] = in;
}
}
/* alu state is reflected immediately on store */
cpu_update_alu(cpu);
}
/* select ALU operation */
static void cpu_op_select(struct cpu *cpu, uint8_t op)
{
if ( op >= ALU_MAX )
abort();
cpu->alu_op = op;
printf(" + alu: op select: %s\n", cpu_alu_name(op));
}
static void cpu_reset(struct cpu *cpu)
{
printf(" cpu: reset\n");
/* not halted */
cpu->hlt = 0;
/* initialize cycle counter */
cpu->clk = 0;
/* set insn ptr to reset vector */
cpu->ip = 0;
/* select 'add' */
cpu_op_select(cpu, 0);
/* select input banks for ALU */
cpu->bank_x = 0;
cpu->bank_y = 0;
/* set the input bus to zero and strobe write-enable on all regs */
cpu->bus = 0;
/* strobe write-enable to clear regs */
cpu_wr_enable(cpu, ~0);
}
/* we have 4 kinds of conditional branchs:
* jz, jnz, jc, jnc for jump [not] [zero|carry]
*/
static void cpu_op_condbranch(struct cpu *cpu, uint16_t op)
{
uint8_t invert;
uint8_t zero;
uint8_t val;
invert = (op & (1 << 7));
zero = (op & (1 << 6));
printf(" cond-branch: if%s %s to 0x%.2x (%u)\n",
(op & (1 << 7)) ? " not" : "",
(op & (1 << 6)) ? "zero" : "carry",
op & CODE_ADDR_MASK,
op & CODE_ADDR_MASK);
val = (zero) ? cpu->zero : cpu->carry;
if ( val ^ invert ) {
printf(" cond-branch: TAKEN\n");
cpu->ip = op & CODE_ADDR_MASK;
}
}
/* 1 bit for each input bank select, 3bits to encode operation and
* 2 bits to encode destination register=
*/
static void cpu_op_alu(struct cpu *cpu, uint16_t op)
{
/* select op + inputs */
cpu->bank_x = !!(op & (1 << 6));
cpu->bank_y = !!(op & (1 << 5));
cpu_op_select(cpu, (op >> 2) & 0x7);
/* ALU state updated immediately */
cpu_update_alu(cpu);
/* once output is stable in hold register, dump
* the result on to the bus */
cpu->bus = cpu->hold;
/* strobe the write enable line for the output register */
cpu_wr_enable(cpu, (1 << (op & 0x3)));
}
/* unconditional branch */
static void cpu_op_branch(struct cpu *cpu, uint16_t op)
{
printf(" branch: to address 0x%.2x (%u)\n",
op & CODE_ADDR_MASK,
op & CODE_ADDR_MASK);
cpu->ip = op & CODE_ADDR_MASK;
}
/* load immediate */
static void cpu_op_ldi(struct cpu *cpu, uint16_t op)
{
printf(" ldi:\n");
/* set bus to immediate-mode, this hooks up code-rom output
* to input busses of register */
cpu->imm = 1;
/* select register and strobe write-enable line */
cpu_wr_enable(cpu, op & 0xf);
/* put everything back how it was */
cpu->imm = 0;
/* bump the ip so we don't interpret immediate data as code */
cpu->ip++;
}
/* clear carry flag */
static void cpu_op_clc(struct cpu *cpu)
{
printf(" clc:\n");
cpu->carry = 0;
}
static void cpu_fetch_execute(struct cpu *cpu)
{
uint16_t insn;
insn = cpu->code_rom[cpu->ip++];
cpu->clk++;
printf("clu: Fetched insn 0x%.3x at address 0x%.2x (%u)\n",
insn, cpu->ip, cpu->ip);
if ( insn & (1 << 8) ) {
cpu_op_condbranch(cpu, insn & 0xff);
return;
}
if ( insn & (1 << 7) ) {
cpu_op_alu(cpu, insn & 0x7f);
return;
}
if ( insn & (1 << 6) ) {
cpu_op_branch(cpu, insn & 0x3f);
return;
}
if ( insn & (1 << 5) ) {
/* reserved for insn requiring 5 bits of operand */
printf("INVALID INSN\n");
cpu_halt(cpu);
return;
}
if ( insn & (1 << 4) ) {
cpu_op_ldi(cpu, insn & 0xf);
return;
}
switch ( insn & 0x7 ) {
case 0:
cpu_halt(cpu);
break;
case 1:
cpu_op_clc(cpu);
break;
default:
/* 6 opcodes available with no operands */
printf("INVALID INSN\n");
cpu_halt(cpu);
break;
}
}
int main(int argc, char **argv)
{
struct cpu cpu;
memset(cpu.code_rom, 0, sizeof(cpu.code_rom));
printf("cpu: state %zu bytes\n", sizeof(struct cpu));
cpu_reset(&cpu);
cpu.code_rom[0] = (1 << 4) | (REG_X1_BIT);
cpu.code_rom[1] = 7;
cpu.code_rom[2] = (1 << 4) | (REG_Y1_BIT);
cpu.code_rom[3] = 3;
cpu.code_rom[4] = (1 << 7) | (1 << 6) | (1 << 5);
while(!cpu_halted(&cpu)) {
cpu_fetch_execute(&cpu);
}
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment