--- /dev/null
+#include <stdint.h>
+#include "cpu.h"
+#include "emu.h"
+
+enum {
+ FLAGS_C = 0x01,
+ FLAGS_N = 0x02,
+ FLAGS_PV = 0x04,
+ FLAGS_H = 0x10,
+ FLAGS_Z = 0x40,
+ FLAGS_S = 0x80
+};
+
+enum {
+ R_B = 0,
+ R_C = 1,
+ R_D = 2,
+ R_E = 3,
+ R_H = 4,
+ R_L = 5,
+ R_INVAL = 6,
+ R_A = 7
+};
+
+#define RRSET2 4
+enum {
+ RR_BC = 0,
+ RR_DE = 1,
+ RR_HL = 2,
+ RR_SP = 3,
+
+ RR2_BC = RRSET2 | RR_BC,
+ RR2_DE = RRSET2 | RR_DE,
+ RR2_HL = RRSET2 | RR_HL,
+ RR2_AF = RRSET2 | RR_SP
+};
+
+enum {
+ PREFIX_ED = 1,
+ PREFIX_CB = 2,
+ PREFIX_DD = 4,
+ PREFIX_FD = 8
+};
+
+enum {
+ ALUOP_ADD = 0,
+ ALUOP_ADC = 1,
+ ALUOP_SUB = 2,
+ ALUOP_SBC = 3,
+ ALUOP_AND = 4,
+ ALUOP_XOR = 5,
+ ALUOP_OR = 6,
+ ALUOP_CP = 7
+};
+
+enum {
+ CC_NZ = 0,
+ CC_Z = 1,
+ CC_NC = 2,
+ CC_C = 3,
+ CC_PO = 4,
+ CC_PE = 5,
+ CC_P = 6,
+ CC_M = 7
+};
+
+struct regs8 {
+ uint8_t a, f, b, c, d, e, h, l;
+};
+struct regs16 {
+ uint16_t af, bc, de, hl;
+};
+
+union genregs {
+ struct regs8 r;
+ struct regs16 rr;
+};
+
+struct registers {
+ union genregs g, shadow; /* general purpose and shadow registers */
+ uint8_t i, r;
+ uint16_t ix, iy, sp, pc;
+ uint8_t iff, imode;
+};
+
+static void runop_main(uint8_t op);
+static void runop_ed(uint8_t op);
+static void runop_cb(uint8_t op);
+static void runop_dd(uint8_t op);
+static void runop_fd(uint8_t op);
+static void runop_ddcb(uint8_t op);
+static void runop_fdcb(uint8_t op);
+
+static void op_load_reg8_reg8(int rdest, int rsrc);
+static void op_load_reg8_imm8(int rdest, uint8_t imm);
+static void op_load_reg8_mem(int rdest, uint16_t addr);
+static void op_store_mem_reg8(uint16_t addr, int rsrc);
+static void op_store_mem_imm8(uint16_t addr, uint8_t imm);
+static void op_store_mem_reg16(uint16_t addr, int rsrc);
+static void op_load_reg16_imm16(int rdest, uint16_t imm);
+static void op_load_reg16_reg16(int rdest, int rsrc);
+static void op_exch_mem_reg16(uint16_t addr, int rr);
+static void op_alu_reg8(int op, int r);
+static void op_alu_imm8(int op, uint8_t imm);
+static void op_incdec_reg8(int r, int adj);
+static void op_incdec_reg16(int r, int adj);
+static void op_incdec_mem(uint16_t addr, int adj);
+static void op_add_reg16_reg16(int rdest, int rsrc);
+static void op_rl_reg8(int r, int width);
+static void op_rr_reg8(int r, int width);
+static void op_push_reg16(int r);
+static void op_pop_reg16(int r);
+static void op_call(uint16_t addr);
+static void op_ret(void);
+static void op_input(int r, uint16_t addr);
+static void op_output(uint16_t addr, int r);
+
+static struct registers regs;
+static int halt;
+
+static void (*runop[16])(uint8_t op) = {
+ runop_main, /* 0000: no prefix */
+ runop_ed, /* 0001: ED prefix */
+ runop_cb, /* 0010: CB prefix */
+ 0, /* 0011: CBED invalid */
+ runop_dd, /* 0100: DD prefix */
+ 0, /* 0101: DDED invalid */
+ runop_ddcb, /* 0110: DDCB prefix */
+ 0, /* 0111: DDCBED invalid */
+ runop_fd, /* 1000: FD prefix */
+ 0, /* 1001: FDED invalid */
+ runop_fdcb, /* 1010: FDCB prefix */
+ 0, 0, 0, 0, 0 /* all the rest combinations are invalid */
+};
+
+static uint8_t *regptr8[8] = {
+ ®s.g.r.b, ®s.g.r.c,
+ ®s.g.r.d, ®s.g.r.e,
+ ®s.g.r.h, ®s.g.r.l,
+ 0, ®s.g.r.a
+};
+
+static uint16_t *regptr16[] = {
+ ®s.g.rr.bc,
+ ®s.g.rr.de,
+ ®s.g.rr.hl,
+ ®s.sp,
+
+ ®s.g.rr.bc,
+ ®s.g.rr.de,
+ ®s.g.rr.hl,
+ ®s.g.rr.af
+};
+
+void cpu_reset(void)
+{
+ regs.iff = 0;
+ regs.pc = 0;
+ regs.i = regs.r = 0;
+ regs.imode = 0;
+}
+
+static uint8_t fetch_byte(void)
+{
+ return emu_mem_read(regs.pc++);
+}
+
+static uint16_t fetch_imm16(void)
+{
+ uint16_t lsb = emu_mem_read(regs.pc++);
+ uint16_t msb = emu_mem_read(regs.pc++);
+ return lsb | (msb << 8);
+}
+
+static unsigned int prefix_bit(uint8_t op)
+{
+ switch(op) {
+ case 0xed: return PREFIX_ED;
+ case 0xcb: return PREFIX_CB;
+ case 0xdd: return PREFIX_DD;
+ case 0xfd: return PREFIX_FD;
+ default:
+ break;
+ }
+ return 0;
+}
+
+void cpu_step(void)
+{
+ unsigned int pbit, prefix = 0;
+ uint8_t op;
+
+ if(halt) return;
+
+ op = fetch_byte();
+ if((pbit = prefix_bit(op))) {
+ prefix = pbit;
+ op = fetch_byte();
+
+ /* only treat the next byte as another prefix if the previous was dd or fd */
+ if((pbit = prefix_bit(op)) && (prefix == 0xdd || prefix == 0xfd)) {
+ prefix |= pbit;
+ op = fetch_byte();
+ }
+ }
+
+ if(runop[prefix]) {
+ runop[prefix](op);
+ }
+}
+
+static int cond(int cc)
+{
+ switch(cc) {
+ case CC_NZ: return ~regs.g.r.f & FLAGS_Z;
+ case CC_Z: return regs.g.r.f & FLAGS_Z;
+ case CC_NC: return ~regs.g.r.f & FLAGS_C;
+ case CC_C: return regs.g.r.f & FLAGS_C;
+ case CC_PO: return ~regs.g.r.f & FLAGS_PV;
+ case CC_PE: return regs.g.r.f & FLAGS_PV;
+ case CC_P: return ~regs.g.r.f & FLAGS_S;
+ case CC_M: return regs.g.r.f & FLAGS_S;
+ default:
+ break;
+ }
+ return 0;
+}
+
+#define ALUOP(x) (((x) >> 3) & 7)
+#define DEST_R(x) (((x) >> 3) & 7)
+#define OPCOND(x) (((x) >> 3) & 7)
+#define SRC_R(x) ((x) & 7)
+#define OP_RR(x) (((x) >> 4) & 3)
+#define RST_ADDR(x) ((x) & 0x38)
+
+#define SWAP_RR(a, b) \
+ do { \
+ uint16_t tmp = a; \
+ a = b; \
+ b = tmp; \
+ } while(0)
+
+static void runop_main(uint8_t op)
+{
+ int b67 = op >> 6;
+ uint8_t disp;
+ uint16_t addr;
+
+ switch(op) {
+ /* 8-bit load group except ld r,r/ld r,imm/ld r,(hl)/ld (hl),r in default */
+ case 0x36:
+ op_store_mem_imm8(regs.g.rr.hl, fetch_byte());
+ break;
+ case 0x0a:
+ op_load_reg8_mem(R_A, regs.g.rr.bc);
+ break;
+ case 0x1a:
+ op_load_reg8_mem(R_A, regs.g.rr.de);
+ break;
+ case 0x3a:
+ op_load_reg8_mem(R_A, fetch_imm16());
+ break;
+ case 0x02:
+ op_store_mem_reg8(regs.g.rr.bc, R_A);
+ break;
+ case 0x12:
+ op_store_mem_reg8(regs.g.rr.de, R_A);
+ break;
+ case 0x32:
+ op_store_mem_reg8(fetch_imm16(), R_A);
+ break;
+
+ /* 16-bit load group */
+ case 0x01:
+ case 0x11:
+ case 0x21:
+ case 0x31:
+ op_load_reg16_imm16(OP_RR(op), fetch_imm16());
+ break;
+ case 0x2a:
+ op_load_reg16_imm16(RR_HL, fetch_imm16());
+ break;
+ case 0x22:
+ op_store_mem_reg16(fetch_imm16(), RR_HL);
+ break;
+ case 0xf9:
+ op_load_reg16_reg16(RR_SP, RR_HL);
+ break;
+ case 0xc5:
+ case 0xd5:
+ case 0xe0:
+ case 0xf5:
+ op_push_reg16(RRSET2 | OP_RR(op));
+ break;
+ case 0xc1:
+ case 0xd1:
+ case 0xe1:
+ case 0xf1:
+ op_pop_reg16(RRSET2 | OP_RR(op));
+ break;
+
+ /* exchange, block transfer, block search groups */
+ case 0xeb:
+ SWAP_RR(regs.g.rr.de, regs.g.rr.hl);
+ break;
+ case 0x08:
+ SWAP_RR(regs.g.rr.af, regs.shadow.rr.af);
+ break;
+ case 0xd9:
+ SWAP_RR(regs.g.rr.bc, regs.shadow.rr.bc);
+ SWAP_RR(regs.g.rr.de, regs.shadow.rr.de);
+ SWAP_RR(regs.g.rr.hl, regs.shadow.rr.hl);
+ break;
+ case 0xe3:
+ op_exch_mem_reg16(regs.sp, RR_HL);
+ break;
+
+ /* general purpose arithmetic and cpu control groups */
+ case 0x27:
+ break; /* TODO implement DAA */
+ case 0x2f: /* cpl a */
+ regs.g.r.a = ~regs.g.r.a;
+ regs.g.r.f |= FLAGS_H | FLAGS_N;
+ break;
+ case 0x3f: /* ccf */
+ regs.g.r.f ^= FLAGS_C;
+ break;
+ case 0x37: /* scf */
+ regs.g.r.f |= FLAGS_C;
+ case 0x00: /* nop */
+ break;
+ case 0x76: /* halt */
+ halt = 1;
+ break;
+ case 0xf3: /* di */
+ regs.iff = 0;
+ break;
+ case 0xfb: /* ei */
+ regs.iff = 1;
+ break;
+
+ /* 16-bit arithmetic group */
+ case 0x09:
+ case 0x19:
+ case 0x29:
+ case 0x39:
+ op_add_reg16_reg16(RR_HL, OP_RR(op));
+ break;
+ case 0x03:
+ case 0x13:
+ case 0x23:
+ case 0x33:
+ op_incdec_reg16(OP_RR(op), 1);
+ break;
+ case 0x0b:
+ case 0x1b:
+ case 0x2b:
+ case 0x3b:
+ op_incdec_reg16(OP_RR(op), -1);
+ break;
+
+ /* rotate and shift group */
+ case 0x07: /* rlca */
+ op_rl_reg8(R_A, 8);
+ break;
+ case 0x17: /* rla */
+ op_rl_reg8(R_A, 9);
+ break;
+ case 0x0f: /* rrca */
+ op_rr_reg8(R_A, 8);
+ break;
+ case 0x1f: /* rra */
+ op_rr_reg8(R_A, 9);
+ break;
+
+ /* jump group except jp cc, imm16 in default */
+ case 0xc3: /* jp imm16 */
+ regs.pc = fetch_imm16();
+ break;
+ case 0x18: /* jr imm8 */
+ regs.pc += fetch_byte();
+ break;
+ case 0x38: /* jr c, imm8 */
+ disp = fetch_byte();
+ if(cond(CC_C)) regs.pc += disp;
+ break;
+ case 0x30: /* jr nc, imm8 */
+ disp = fetch_byte();
+ if(cond(CC_NC)) regs.pc += disp;
+ break;
+ case 0x28: /* jr z, imm8 */
+ disp = fetch_byte();
+ if(cond(CC_Z)) regs.pc += disp;
+ break;
+ case 0x20: /* jr nz, imm8 */
+ disp = fetch_byte();
+ if(cond(CC_NZ)) regs.pc += disp;
+ break;
+ case 0xe9: /* jp (hl) */
+ regs.pc = regs.g.rr.hl;
+ break;
+ case 0x10: /* djnz, imm8 */
+ disp = fetch_byte();
+ if(--regs.g.r.b) regs.pc += disp;
+ break;
+
+ /* call and return group except call cc,imm16/ret cc,imm16/rst */
+ case 0xcd:
+ op_call(fetch_imm16());
+ break;
+ case 0xc9:
+ op_ret();
+ break;
+
+ /* input and output group */
+ case 0xdb:
+ op_input(R_A, ((uint16_t)regs.g.r.a << 8) | fetch_byte());
+ break;
+ case 0xd3:
+ op_output(((uint16_t)regs.g.r.a << 8) | fetch_byte(), R_A);
+ break;
+
+ default:
+ switch(b67) {
+ case 0:
+ if(SRC_R(op) == 6) {
+ op_load_reg8_imm8(DEST_R(op), fetch_byte());
+ } else if(SRC_R(op) == 4 || SRC_R(op) == 5) {
+ int adj = (op & 1) ? -1 : 1;
+ if(DEST_R(op) != 6) {
+ op_incdec_reg8(DEST_R(op), adj);
+ } else {
+ op_incdec_mem(regs.g.rr.hl, adj);
+ }
+ }
+ break;
+
+ case 1:
+ if(DEST_R(op) != 6 && SRC_R(op) != 6) {
+ op_load_reg8_reg8(DEST_R(op), SRC_R(op));
+ } else {
+ if(SRC_R(op) == 6) {
+ op_load_reg8_mem(DEST_R(op), regs.g.rr.hl);
+ } else if(DEST_R(op) == 6) {
+ op_store_mem_reg8(regs.g.rr.hl, SRC_R(op));
+ }
+ }
+ break;
+
+ case 2:
+ if(SRC_R(op) != 6) {
+ op_alu_reg8(ALUOP(op), SRC_R(op));
+ }
+ break;
+
+ case 3:
+ if(SRC_R(op) == 6) { /* alu-op a, imm8 */
+ op_alu_imm8(ALUOP(op), fetch_byte());
+
+ } else if(SRC_R(op) == 2) { /* jp cc, imm16 */
+ addr = fetch_imm16();
+ if(cond(OPCOND(op))) {
+ regs.pc = addr;
+ }
+
+ } else if(SRC_R(op) == 4) { /* call cc, imm16 */
+ addr = fetch_imm16();
+ if(cond(OPCOND(op))) op_call(addr);
+
+ } else if(SRC_R(op) == 0) { /* ret cc, imm16 */
+ addr = fetch_imm16();
+ if(cond(OPCOND(op))) op_ret();
+
+ } else if(SRC_R(op) == 7) { /* rst */
+ op_call(RST_ADDR(op));
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ break; /* treat any unknown opcodes as nops */
+ }
+}
+
+static void runop_ed(uint8_t op)
+{
+}
+
+static void runop_cb(uint8_t op)
+{
+}
+
+static void runop_dd(uint8_t op)
+{
+}
+
+static void runop_fd(uint8_t op)
+{
+}
+
+static void runop_ddcb(uint8_t op)
+{
+}
+
+static void runop_fdcb(uint8_t op)
+{
+}
+
+static void set_reg8(int r, uint8_t val)
+{
+ *regptr8[r] = val;
+}
+
+static void set_reg8s(int r, int8_t val)
+{
+ *regptr8[r] = *(uint8_t*)&val;
+}
+
+static uint8_t get_reg8(int r)
+{
+ return *regptr8[r];
+}
+
+static int8_t get_reg8s(int r)
+{
+ return *(int8_t*)regptr8[r];
+}
+
+static void set_reg16(int r, uint16_t val)
+{
+ *regptr16[r] = val;
+}
+
+static void set_reg16s(int r, int16_t val)
+{
+ *regptr16[r] = *(uint16_t*)&val;
+}
+
+static uint16_t get_reg16(int r)
+{
+ return *regptr16[r];
+}
+
+static int16_t get_reg16s(int r)
+{
+ return *(int16_t*)regptr16[r];
+}
+
+static void set_flag(unsigned int flag, int val)
+{
+ if(val) {
+ regs.g.r.f |= flag;
+ } else {
+ regs.g.r.f &= ~flag;
+ }
+}
+
+static int parity(int x)
+{
+ int i, s = 0;
+ for(i=0; i<8; i++) {
+ s += x & 1;
+ x >>= 1;
+ }
+ return s & 1;
+}
+
+static int overflow(int x)
+{
+ return x > 127 || x < -128;
+}
+
+static int overflow16(int x)
+{
+ return x > 32767 || x < -32768;
+}
+
+static void op_load_reg8_reg8(int rdest, int rsrc)
+{
+ set_reg8(rdest, get_reg8(rsrc));
+}
+
+static void op_load_reg8_imm8(int rdest, uint8_t imm)
+{
+ set_reg8(rdest, imm);
+}
+
+static void op_load_reg8_mem(int rdest, uint16_t addr)
+{
+ set_reg8(rdest, emu_mem_read(addr));
+}
+
+static void op_store_mem_reg8(uint16_t addr, int rsrc)
+{
+ emu_mem_write(addr, get_reg8(rsrc));
+}
+
+static void op_store_mem_imm8(uint16_t addr, uint8_t imm)
+{
+ emu_mem_write(addr, imm);
+}
+
+static void op_store_mem_reg16(uint16_t addr, int rsrc)
+{
+ uint16_t val = get_reg16(rsrc);
+ emu_mem_write(addr, val);
+ emu_mem_write(addr + 1, val >> 8);
+}
+
+static void op_load_reg16_imm16(int rdest, uint16_t imm)
+{
+ set_reg16(rdest, imm);
+}
+
+static void op_load_reg16_reg16(int rdest, int rsrc)
+{
+ set_reg16(rdest, get_reg16(rsrc));
+}
+
+static void op_exch_mem_reg16(uint16_t addr, int rr)
+{
+ uint16_t val = get_reg16(rr);
+ uint16_t lsb = emu_mem_read(addr);
+ uint16_t msb = emu_mem_read(addr + 1);
+ set_reg16(rr, lsb | (msb << 8));
+ emu_mem_write(addr, val);
+ emu_mem_write(addr + 1, val >> 8);
+}
+
+#define CARRY (regs.g.r.f & 1)
+
+static void op_alu_reg8(int op, int r)
+{
+ op_alu_imm8(op, get_reg8(r));
+}
+
+static void op_alu_imm8(int op, uint8_t imm)
+{
+ int c, h, pv, n = 0;
+ int acc = get_reg8s(R_A);
+ int rval = *(int8_t*)&imm;
+ c = CARRY;
+
+ switch(op) {
+ case ALUOP_ADD:
+ if(0) {
+ case ALUOP_SUB:
+ case ALUOP_CP:
+ rval = -rval;
+ c = -c;
+ n = 1;
+ }
+ h = ((acc & 0xf) + (rval + 0xf)) & 0x10;
+ acc += rval;
+ c = acc & 0x100;
+ pv = overflow(acc);
+ break;
+
+ case ALUOP_ADC:
+ if(0) {
+ case ALUOP_SBC:
+ rval = -rval;
+ c = -c;
+ n = 1;
+ }
+ h = ((acc & 0xf) + (rval + 0xf) + CARRY) & 0x10;
+ acc += rval + CARRY;
+ c = acc & 0x100;
+ pv = overflow(acc);
+ break;
+
+ case ALUOP_AND:
+ acc &= rval;
+ c = 0;
+ h = 1;
+ pv = parity(acc);
+ break;
+ case ALUOP_XOR:
+ acc ^= rval;
+ c = 0;
+ h = 0;
+ pv = parity(acc);
+ break;
+ case ALUOP_OR:
+ acc |= rval;
+ c = 0;
+ h = 0;
+ pv = parity(acc);
+ break;
+ default:
+ return;
+ }
+
+ set_flag(FLAGS_S, acc & 0x80);
+ set_flag(FLAGS_Z, acc == 0);
+ set_flag(FLAGS_H, h);
+ set_flag(FLAGS_PV, pv);
+ set_flag(FLAGS_N, n);
+ set_flag(FLAGS_C, c);
+
+ if(op != ALUOP_CP) {
+ set_reg8s(R_A, acc);
+ }
+}
+
+static void op_incdec_reg8(int r, int adj)
+{
+ int prev = get_reg8s(r);
+ int val = prev + adj;
+
+ set_flag(FLAGS_S, val & 0x80);
+ set_flag(FLAGS_Z, val == 0);
+ set_flag(FLAGS_H, ((prev & 0xf) + adj) & 0x10);
+ set_flag(FLAGS_PV, overflow(val));
+ set_flag(FLAGS_N, adj < 0);
+
+ set_reg8s(r, val);
+}
+
+static void op_incdec_reg16(int r, int adj)
+{
+ set_reg16s(r, get_reg16s(r) + adj);
+}
+
+static void op_incdec_mem(uint16_t addr, int adj)
+{
+ uint16_t lsb = emu_mem_read(addr);
+ uint16_t msb = emu_mem_read(addr + 1);
+ int prev, val;
+
+ lsb |= msb << 8;
+ prev = *(int16_t*)&lsb;
+ val = prev + adj;
+
+ set_flag(FLAGS_S, val & 0x8000);
+ set_flag(FLAGS_Z, val == 0);
+ set_flag(FLAGS_H, ((prev & 0xfff) + adj) & 0x1000);
+ set_flag(FLAGS_PV, overflow16(val));
+ set_flag(FLAGS_N, adj < 0);
+
+ lsb = *(uint16_t*)&val;
+ emu_mem_write(addr, lsb);
+ emu_mem_write(addr + 1, lsb >> 8);
+}
+
+static void op_add_reg16_reg16(int rdest, int rsrc)
+{
+}
+
+static void op_rl_reg8(int r, int width)
+{
+}
+
+static void op_rr_reg8(int r, int width)
+{
+}
+
+static void op_push_reg16(int r)
+{
+}
+
+static void op_pop_reg16(int r)
+{
+}
+
+static void op_call(uint16_t addr)
+{
+}
+
+static void op_ret(void)
+{
+}
+
+static void op_input(int r, uint16_t addr)
+{
+ set_reg8(r, emu_io_read(addr));
+}
+
+static void op_output(uint16_t addr, int r)
+{
+ emu_io_write(addr, get_reg8(r));
+}