#include "insn.h"
|
|
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
|
|
#include "cpu.h"
|
|
#include "log.h"
|
|
|
|
#define TABLE_SIZE 0x34
|
|
|
|
#define BITS(x, start, len) (((x) >> (start)) & ((1 << (len)) - 1))
|
|
|
|
#define OP(insn) BITS(insn, 26, 6)
|
|
#define RS(insn) BITS(insn, 21, 5)
|
|
#define RT(insn) BITS(insn, 16, 5)
|
|
#define RD(insn) BITS(insn, 11, 5)
|
|
#define COMMENT(insn) BITS(insn, 6, 20)
|
|
#define IMM5(insn) BITS(insn, 6, 5)
|
|
#define OP2(insn) BITS(insn, 0, 6)
|
|
#define IMM(insn) BITS(insn, 0, 16)
|
|
|
|
static cpu_insn_handler primary_insn_handler[TABLE_SIZE];
|
|
static cpu_insn_handler secondary_insn_handler[TABLE_SIZE];
|
|
|
|
void insn_execute(cpu_t *cpu, uint32_t insn) {
|
|
const op_primary_t op = OP(insn);
|
|
|
|
if (op == SPECIAL) {
|
|
const op_secondary_t op2 = OP2(insn);
|
|
|
|
if (op2 > TABLE_SIZE || secondary_insn_handler[op2] == NULL) {
|
|
fatal("Unsupported instruction: insn=%08x, op=%02x, op2=%02x", insn,
|
|
op, op2);
|
|
}
|
|
|
|
secondary_insn_handler[op2](cpu, insn);
|
|
return;
|
|
}
|
|
|
|
if (op > TABLE_SIZE || primary_insn_handler[op] == NULL) {
|
|
fatal("Unsupported instruction: insn=%08x, op=%02x", insn, op);
|
|
}
|
|
|
|
primary_insn_handler[op](cpu, insn);
|
|
}
|
|
|
|
void insn_lw(cpu_t *cpu, uint32_t insn) {
|
|
const uint8_t rt = RT(insn);
|
|
const uint8_t rs = RS(insn);
|
|
const uint16_t imm = IMM(insn);
|
|
|
|
debug("LW %s, [%s + %x]", REG_NAMES[rt], REG_NAMES[rs], imm);
|
|
cpu->regs[rt] = cpu_read32(cpu, cpu->regs[rs] + imm);
|
|
}
|
|
|
|
void insn_srl(cpu_t *cpu, uint32_t insn) {
|
|
const uint8_t rd = RD(insn);
|
|
const uint8_t rt = RT(insn);
|
|
const uint8_t imm5 = IMM5(insn);
|
|
|
|
debug("SRL %s, %s, %u", REG_NAMES[rd], REG_NAMES[rt], imm5);
|
|
cpu->regs[rd] = cpu->regs[rt] >> imm5;
|
|
}
|
|
|
|
void insn_addu(cpu_t *cpu, uint32_t insn) {
|
|
const uint8_t rd = RD(insn);
|
|
const uint8_t rs = RS(insn);
|
|
const uint8_t rt = RT(insn);
|
|
|
|
debug("ADDU %s, %s, %s", REG_NAMES[rd], REG_NAMES[rs], REG_NAMES[rt]);
|
|
cpu->regs[rd] = cpu->regs[rs] + cpu->regs[rt];
|
|
}
|
|
|
|
void insn_addiu(cpu_t *cpu, uint32_t insn) {
|
|
const uint8_t rt = RT(insn);
|
|
const uint8_t rs = RS(insn);
|
|
const int16_t imm = (int16_t)IMM(insn);
|
|
|
|
debug("ADDIU %s, %s, %x", REG_NAMES[rt], REG_NAMES[rs], imm);
|
|
cpu->regs[rt] = cpu->regs[rs] + imm;
|
|
}
|
|
|
|
void insn_sw(cpu_t *cpu, uint32_t insn) {
|
|
const uint8_t rt = RT(insn);
|
|
const uint8_t rs = RS(insn);
|
|
const uint16_t imm = IMM(insn);
|
|
|
|
debug("SW %s, [%s + %x]", REG_NAMES[rt], REG_NAMES[rt], imm);
|
|
cpu_write32(cpu, cpu->regs[rt], imm + cpu->regs[rs]);
|
|
}
|
|
|
|
void insn_sll(cpu_t *cpu, uint32_t insn) {
|
|
const uint8_t rd = RD(insn);
|
|
const uint8_t rt = RT(insn);
|
|
const uint8_t imm5 = IMM5(insn);
|
|
|
|
debug("SLL %s, %s, %u", REG_NAMES[rd], REG_NAMES[rt], imm5);
|
|
cpu->regs[rd] = cpu->regs[rt] << imm5;
|
|
}
|
|
|
|
void insn_beq(cpu_t *cpu, uint32_t insn) {
|
|
const uint8_t rs = RS(insn);
|
|
const uint8_t rt = RT(insn);
|
|
const int16_t imm = IMM(insn);
|
|
|
|
debug("BEQ %s, %s, %d", REG_NAMES[rs], REG_NAMES[rt], imm);
|
|
if (cpu->regs[rs] == cpu->regs[rt]) {
|
|
cpu->pc += imm * 4;
|
|
}
|
|
}
|
|
|
|
void insn_lui(cpu_t *cpu, uint32_t insn) {
|
|
const uint8_t rt = RT(insn);
|
|
const uint16_t imm = IMM(insn);
|
|
|
|
debug("LUI %s, %x", REG_NAMES[rt], imm);
|
|
cpu->regs[rt] = imm << 16;
|
|
}
|
|
|
|
static cpu_insn_handler primary_insn_handler[TABLE_SIZE] = {
|
|
[BEQ] = insn_beq, [LW] = insn_lw, [LUI] = insn_lui,
|
|
[ADDIU] = insn_addiu, [SW] = insn_sw,
|
|
};
|
|
|
|
static cpu_insn_handler secondary_insn_handler[TABLE_SIZE] = {
|
|
[SLL] = insn_sll,
|
|
[ADDU] = insn_addu,
|
|
[SRL] = insn_srl,
|
|
};
|