648 lines
12 KiB
C
648 lines
12 KiB
C
#include "gb.h"
|
|
#include <stdio.h>
|
|
|
|
#define BC() ((u16)r[rB] << 8 | r[rC])
|
|
#define DE() ((u16)r[rD] << 8 | r[rE])
|
|
#define HL() ((u16)r[rH] << 8 | r[rL])
|
|
|
|
enum
|
|
{
|
|
FLAGC = 0x10,
|
|
FLAGH = 0x20,
|
|
FLAGN = 0x40,
|
|
FLAGZ = 0x80,
|
|
};
|
|
|
|
enum
|
|
{
|
|
rB,
|
|
rC,
|
|
rD,
|
|
rE,
|
|
rH,
|
|
rL,
|
|
rHL,
|
|
rA,
|
|
rF = rHL,
|
|
};
|
|
|
|
u8 r[8], ime;
|
|
u16 pc, sp, curpc;
|
|
int halt;
|
|
|
|
void
|
|
state(void)
|
|
{
|
|
printf("A:%02X F:%02X B:%02X C:%02X D:%02X E:%02X H:%02X L:%02X SP:%04X "
|
|
"PC:%04X PCMEM:%02X,%02X,%02X,%02X\n",
|
|
r[rA],
|
|
r[rF],
|
|
r[rB],
|
|
r[rC],
|
|
r[rD],
|
|
r[rE],
|
|
r[rH],
|
|
r[rL],
|
|
sp,
|
|
pc - 1,
|
|
memread(pc - 1),
|
|
memread(pc),
|
|
memread(pc + 1),
|
|
memread(pc + 2));
|
|
}
|
|
|
|
static u8
|
|
fetch8(void)
|
|
{
|
|
return memread(pc++);
|
|
}
|
|
|
|
static u16
|
|
fetch16(void)
|
|
{
|
|
u16 u = memread(pc++);
|
|
return u | memread(pc++) << 8;
|
|
}
|
|
|
|
static void
|
|
push8(u8 u)
|
|
{
|
|
memwrite(--sp, u);
|
|
}
|
|
|
|
static void
|
|
push16(u16 u)
|
|
{
|
|
memwrite(--sp, u >> 8);
|
|
memwrite(--sp, u);
|
|
}
|
|
|
|
static u8
|
|
pop8(void)
|
|
{
|
|
return memread(sp++);
|
|
}
|
|
|
|
static u16
|
|
pop16(void)
|
|
{
|
|
u16 v = memread(sp++);
|
|
return v | memread(sp++) << 8;
|
|
}
|
|
|
|
static void
|
|
write16(u16 n, u16 v)
|
|
{
|
|
memwrite(n++, v);
|
|
memwrite(n, v >> 8);
|
|
}
|
|
|
|
static int
|
|
move(u8 dst, u8 src)
|
|
{
|
|
if (dst == rHL) {
|
|
if (src == rHL) {
|
|
halt = 1;
|
|
return 4;
|
|
}
|
|
memwrite(HL(), r[src]);
|
|
return 8;
|
|
}
|
|
if (src == rHL) {
|
|
r[dst] = memread(HL());
|
|
return 8;
|
|
}
|
|
r[dst] = r[src];
|
|
return 4;
|
|
}
|
|
|
|
static int
|
|
alu(u8 op, u8 n)
|
|
{
|
|
u8 v4, u;
|
|
u16 v = 0;
|
|
int t;
|
|
|
|
switch (n) {
|
|
case 8:
|
|
u = fetch8();
|
|
t = 8;
|
|
break;
|
|
case rHL:
|
|
u = memread(HL());
|
|
t = 8;
|
|
break;
|
|
default:
|
|
u = r[n];
|
|
t = 4;
|
|
break;
|
|
}
|
|
switch (op) {
|
|
default:
|
|
v4 = (r[rA] & 0x0F) + (u & 0x0F);
|
|
v = r[rA] + u;
|
|
break;
|
|
case 1:
|
|
v4 = (r[rA] & 0x0F) + (u & 0x0F) + (r[rF] >> 4 & 1);
|
|
v = r[rA] + u + (r[rF] >> 4 & 1);
|
|
break;
|
|
case 2:
|
|
case 7:
|
|
v4 = (r[rA] & 0x0F) + (~u & 0x0F) + 1;
|
|
v = r[rA] + (u ^ 0xFF) + 1;
|
|
break;
|
|
case 3:
|
|
v4 = (r[rA] & 0x0F) + (~u & 0x0F) + (~r[rF] >> 4 & 1);
|
|
v = r[rA] + (u ^ 0xFF) + (~r[rF] >> 4 & 1);
|
|
break;
|
|
case 4:
|
|
v = r[rA] & u;
|
|
break;
|
|
case 5:
|
|
v = r[rA] ^ u;
|
|
break;
|
|
case 6:
|
|
v = r[rA] | u;
|
|
break;
|
|
}
|
|
r[rF] = 0;
|
|
if ((u8)v == 0)
|
|
r[rF] |= FLAGZ;
|
|
if (op < 2) {
|
|
if ((v & 0x100) != 0)
|
|
r[rF] |= FLAGC;
|
|
if ((v4 & 0x10) != 0)
|
|
r[rF] |= FLAGH;
|
|
} else if (op < 4 || op == 7) {
|
|
r[rF] |= FLAGN;
|
|
if ((v & 0x100) == 0)
|
|
r[rF] |= FLAGC;
|
|
if ((v4 & 0x10) == 0)
|
|
r[rF] |= FLAGH;
|
|
} else if (op == 4) {
|
|
r[rF] |= FLAGH;
|
|
}
|
|
if (op != 7)
|
|
r[rA] = v;
|
|
return t;
|
|
}
|
|
|
|
static int
|
|
branch(int cc, int t)
|
|
{
|
|
u16 v = (i8)fetch8();
|
|
if (cc == 0)
|
|
return t + 8;
|
|
pc += v;
|
|
return t + 12;
|
|
}
|
|
|
|
static u8
|
|
inc(u8 v)
|
|
{
|
|
r[rF] &= FLAGC;
|
|
++v;
|
|
if (v == 0)
|
|
r[rF] |= FLAGZ;
|
|
if ((v & 0xF) == 0)
|
|
r[rF] |= FLAGH;
|
|
return v;
|
|
}
|
|
|
|
static u8
|
|
dec(u8 v)
|
|
{
|
|
r[rF] = (r[rF] & FLAGC) | FLAGN;
|
|
--v;
|
|
if (v == 0)
|
|
r[rF] |= FLAGZ;
|
|
if ((v & 0xF) == 0xF)
|
|
r[rF] |= FLAGH;
|
|
return v;
|
|
}
|
|
|
|
static u8
|
|
addhl(u16 u)
|
|
{
|
|
r[rF] &= ~(FLAGN | FLAGC | FLAGH);
|
|
u32 v = HL() + u;
|
|
if ((v & 0x10000) != 0)
|
|
r[rF] |= FLAGC;
|
|
if ((HL() & 0xFFF) + (u & 0xFFF) >= 0x1000)
|
|
r[rF] |= FLAGH;
|
|
r[rL] = v;
|
|
r[rH] = v >> 8;
|
|
return 8;
|
|
}
|
|
|
|
static int
|
|
jump(int cc)
|
|
{
|
|
u16 v = fetch16();
|
|
if (cc == 0)
|
|
return 12;
|
|
pc = v;
|
|
return 16;
|
|
}
|
|
|
|
static int
|
|
call(u16 a, int cc)
|
|
{
|
|
if (cc == 0)
|
|
return 12;
|
|
push16(pc);
|
|
pc = a;
|
|
if (cc < 0)
|
|
return 16;
|
|
return 24;
|
|
}
|
|
|
|
static int
|
|
bits(void)
|
|
{
|
|
u8 op = fetch8();
|
|
u8 n = op & 7;
|
|
u8 m = op >> 3 & 7;
|
|
u16 a = HL();
|
|
u8 v;
|
|
int t;
|
|
if (n == 6) {
|
|
v = memread(a);
|
|
t = 16;
|
|
} else {
|
|
v = r[n];
|
|
t = 8;
|
|
}
|
|
u8 c;
|
|
switch (op >> 6) {
|
|
case 0:
|
|
c = r[rF] >> 4 & 1;
|
|
switch (m) {
|
|
default:
|
|
r[rF] = v >> 3 & 0x10;
|
|
v = v << 1 | v >> 7;
|
|
break;
|
|
case 1:
|
|
r[rF] = v << 4 & 0x10;
|
|
v = v >> 1 | v << 7;
|
|
break;
|
|
case 2:
|
|
r[rF] = v >> 3 & 0x10;
|
|
v = v << 1 | c;
|
|
break;
|
|
case 3:
|
|
r[rF] = v << 4 & 0x10;
|
|
v = v >> 1 | c << 7;
|
|
break;
|
|
case 4:
|
|
r[rF] = v >> 3 & 0x10;
|
|
v = v << 1;
|
|
break;
|
|
case 5:
|
|
r[rF] = v << 4 & 0x10;
|
|
v = (v & 0x80) | v >> 1;
|
|
break;
|
|
case 6:
|
|
r[rF] = v << 0;
|
|
v = v << 4 | v >> 4;
|
|
break;
|
|
case 7:
|
|
r[rF] = v << 4 & 0x10;
|
|
v >>= 1;
|
|
break;
|
|
}
|
|
if (v == 0)
|
|
r[rF] |= FLAGZ;
|
|
break;
|
|
case 1:
|
|
r[rF] = (r[rF] & ~(FLAGN | FLAGZ)) | FLAGH;
|
|
if ((v & 1 << m) == 0)
|
|
r[rF] |= FLAGZ;
|
|
if (n == 6)
|
|
t = 12;
|
|
return t;
|
|
case 2:
|
|
v &= ~(1 << m);
|
|
break;
|
|
case 3:
|
|
v |= (1 << m);
|
|
}
|
|
if (n == 6) {
|
|
memwrite(a, v);
|
|
} else {
|
|
r[n] = v;
|
|
}
|
|
return t;
|
|
}
|
|
|
|
void
|
|
reset(void)
|
|
{
|
|
r[rA] = 0x01;
|
|
r[rF] = 0xb0;
|
|
r[rC] = 0x13;
|
|
r[rE] = 0xd8;
|
|
r[rL] = 0x4d;
|
|
r[rH] = 0x01;
|
|
sp = 0xfffe;
|
|
pc = 0x100;
|
|
}
|
|
|
|
int
|
|
step(void)
|
|
{
|
|
u8 op, v4;
|
|
u16 v, w;
|
|
i8 s;
|
|
|
|
if (halt) {
|
|
if (reg[IF] & reg[IE])
|
|
halt = 0;
|
|
else
|
|
return 4;
|
|
}
|
|
if ((reg[IF] & reg[IE]) != 0 && ime != 0) {
|
|
push16(pc);
|
|
ime = 0;
|
|
v4 = reg[IF] & reg[IE];
|
|
v4 &= -v4;
|
|
reg[IF] &= ~v4;
|
|
for (pc = 0x40; v4 != 1; pc += 8)
|
|
v4 >>= 1;
|
|
/* 2nop + push + changepc */
|
|
return 20;
|
|
}
|
|
curpc = pc;
|
|
op = fetch8();
|
|
switch (op >> 6) {
|
|
case 1:
|
|
return move(op >> 3 & 7, op & 7);
|
|
case 2:
|
|
return alu(op >> 3 & 7, op & 7);
|
|
}
|
|
switch (op) {
|
|
default: state(); panic("Unkown opcode OPCODE:[%X]", op);
|
|
case 0x00: return 4;
|
|
case 0x10: return 4;
|
|
case 0x20: return branch((r[rF] & FLAGZ) == 0, 0);
|
|
case 0x30: return branch((r[rF] & FLAGC) == 0, 0);
|
|
case 0x01:
|
|
r[rC] = fetch8();
|
|
r[rB] = fetch8();
|
|
return 12;
|
|
case 0x11:
|
|
r[rE] = fetch8();
|
|
r[rD] = fetch8();
|
|
return 12;
|
|
case 0x21:
|
|
r[rL] = fetch8();
|
|
r[rH] = fetch8();
|
|
return 12;
|
|
case 0x31: sp = fetch16(); return 12;
|
|
case 0x02: memwrite(BC(), r[rA]); return 8;
|
|
case 0x12: memwrite(DE(), r[rA]); return 8;
|
|
case 0x22:
|
|
memwrite(HL(), r[rA]);
|
|
if (++r[rL] == 0)
|
|
r[rH]++;
|
|
return 8;
|
|
case 0x32:
|
|
memwrite(HL(), r[rA]);
|
|
if (r[rL]-- == 0)
|
|
r[rH]--;
|
|
return 8;
|
|
case 0x03:
|
|
if (++r[rC] == 0)
|
|
r[rB]++;
|
|
return 8;
|
|
case 0x13:
|
|
if (++r[rE] == 0)
|
|
r[rD]++;
|
|
return 8;
|
|
case 0x23:
|
|
if (++r[rL] == 0)
|
|
r[rH]++;
|
|
return 8;
|
|
case 0x33: ++sp; return 8;
|
|
case 0x04: inc(r[rB]++); return 4;
|
|
case 0x14: inc(r[rD]++); return 4;
|
|
case 0x24: inc(r[rH]++); return 4;
|
|
case 0x34: memwrite(HL(), inc(memread(HL()))); return 12;
|
|
case 0x05: dec(r[rB]--); return 4;
|
|
case 0x15: dec(r[rD]--); return 4;
|
|
case 0x25: dec(r[rH]--); return 4;
|
|
case 0x35: memwrite(HL(), dec(memread(HL()))); return 12;
|
|
case 0x06: r[rB] = fetch8(); return 8;
|
|
case 0x16: r[rD] = fetch8(); return 8;
|
|
case 0x26: r[rH] = fetch8(); return 8;
|
|
case 0x36: memwrite(HL(), fetch8()); return 12;
|
|
case 0x07:
|
|
r[rF] = r[rA] >> 3 & 0x10;
|
|
r[rA] = r[rA] << 1 | r[rA] >> 7;
|
|
return 4;
|
|
case 0x17:
|
|
v = r[rF] >> 4 & 1;
|
|
r[rF] = r[rA] >> 3 & 0x10;
|
|
r[rA] = r[rA] << 1 | v;
|
|
return 4;
|
|
case 0x27:
|
|
if ((r[rA] > 0x99 && (r[rF] & FLAGN) == 0) || (r[rF] & FLAGC) != 0) {
|
|
r[rF] |= FLAGC;
|
|
v = 0x60;
|
|
} else {
|
|
r[rF] &= ~FLAGC;
|
|
v = 0;
|
|
}
|
|
if (((r[rA] & 0xF) > 9 && (r[rF] & FLAGN) == 0) || (r[rF] & FLAGH) != 0) {
|
|
v |= 6;
|
|
}
|
|
if ((r[rF] & FLAGN) != 0) {
|
|
r[rA] -= v;
|
|
} else {
|
|
r[rA] += v;
|
|
}
|
|
r[rF] &= ~(FLAGZ | FLAGH);
|
|
if (r[rA] == 0)
|
|
r[rF] |= FLAGZ;
|
|
return 4;
|
|
case 0x37: r[rF] = (r[rF] & ~(FLAGN | FLAGH)) | FLAGC; return 4;
|
|
case 0x08: write16(fetch16(), sp); return 20;
|
|
case 0x18: return branch(1, 0);
|
|
case 0x28: return branch((r[rF] & FLAGZ) != 0, 0);
|
|
case 0x38: return branch((r[rF] & FLAGC) != 0, 0);
|
|
case 0x09: return addhl(BC());
|
|
case 0x19: return addhl(DE());
|
|
case 0x29: return addhl(HL());
|
|
case 0x39: return addhl(sp);
|
|
case 0x0A: r[rA] = memread(BC()); return 8;
|
|
case 0x1A: r[rA] = memread(DE()); return 8;
|
|
case 0x2A:
|
|
r[rA] = memread(HL());
|
|
if (++r[rL] == 0)
|
|
r[rH]++;
|
|
return 8;
|
|
case 0x3A:
|
|
r[rA] = memread(HL());
|
|
if (r[rL]-- == 0)
|
|
r[rH]--;
|
|
return 8;
|
|
case 0x0b:
|
|
if (r[rC]-- == 0)
|
|
r[rB]--;
|
|
return 8;
|
|
case 0x1b:
|
|
if (r[rE]-- == 0)
|
|
r[rD]--;
|
|
return 8;
|
|
case 0x2b:
|
|
if (r[rL]-- == 0)
|
|
r[rH]--;
|
|
return 8;
|
|
case 0x3b: sp--; return 8;
|
|
case 0x0C: inc(r[rC]++); return 4;
|
|
case 0x1C: inc(r[rE]++); return 4;
|
|
case 0x2C: inc(r[rL]++); return 4;
|
|
case 0x3C: inc(r[rA]++); return 4;
|
|
case 0x0d: dec(r[rC]--); return 4;
|
|
case 0x1d: dec(r[rE]--); return 4;
|
|
case 0x2d: dec(r[rL]--); return 4;
|
|
case 0x3d: dec(r[rA]--); return 4;
|
|
case 0x0e: r[rC] = fetch8(); return 8;
|
|
case 0x1e: r[rE] = fetch8(); return 8;
|
|
case 0x2e: r[rL] = fetch8(); return 8;
|
|
case 0x3e: r[rA] = fetch8(); return 8;
|
|
case 0x0F:
|
|
r[rF] = r[rA] << 4 & 0x10;
|
|
r[rA] = r[rA] >> 1 | r[rA] << 7;
|
|
return 4;
|
|
case 0x1F:
|
|
v = r[rF] << 3 & 0x80;
|
|
r[rF] = r[rA] << 4 & 0x10;
|
|
r[rA] = r[rA] >> 1 | v;
|
|
return 4;
|
|
case 0x2F:
|
|
r[rF] |= FLAGN | FLAGH;
|
|
r[rA] ^= 0xFF;
|
|
return 4;
|
|
case 0x3F:
|
|
r[rF] = (r[rF] & ~(FLAGN | FLAGH)) ^ FLAGC;
|
|
return 4;
|
|
case 0xc0:
|
|
if ((r[rF] & FLAGZ) == 0) {
|
|
pc = pop16();
|
|
return 20;
|
|
}
|
|
return 8;
|
|
case 0xD0:
|
|
if ((r[rF] & FLAGC) == 0) {
|
|
pc = pop16();
|
|
return 20;
|
|
}
|
|
return 8;
|
|
case 0xE0: memwrite(0xFF00 | fetch8(), r[rA]); return 12;
|
|
case 0xF0: r[rA] = memread(0xFF00 | fetch8()); return 12;
|
|
case 0xC1:
|
|
r[rC] = pop8();
|
|
r[rB] = pop8();
|
|
return 12;
|
|
case 0xd1:
|
|
r[rE] = pop8();
|
|
r[rD] = pop8();
|
|
return 12;
|
|
case 0xe1:
|
|
r[rL] = pop8();
|
|
r[rH] = pop8();
|
|
return 12;
|
|
case 0xf1:
|
|
r[rF] = pop8() & 0xF0;
|
|
r[rA] = pop8();
|
|
return 12;
|
|
case 0xC2: return jump((r[rF] & FLAGZ) == 0);
|
|
case 0xD2: return jump((r[rF] & FLAGC) == 0);
|
|
case 0xE2: memwrite(0xFF00 | r[rC], r[rA]); return 8;
|
|
case 0xF2: r[rA] = memread(0xFF00 | r[rC]); return 8;
|
|
case 0xC3: return jump(1);
|
|
case 0xF3: ime = 0; return 4;
|
|
case 0xC4: return call(fetch16(), (r[rF] & FLAGZ) == 0);
|
|
case 0xD4: return call(fetch16(), (r[rF] & FLAGC) == 0);
|
|
case 0xC5:
|
|
push8(r[rB]);
|
|
push8(r[rC]);
|
|
return 16;
|
|
case 0xD5:
|
|
push8(r[rD]);
|
|
push8(r[rE]);
|
|
return 16;
|
|
case 0xE5:
|
|
push8(r[rH]);
|
|
push8(r[rL]);
|
|
return 16;
|
|
case 0xF5:
|
|
push8(r[rA]);
|
|
push8(r[rF]);
|
|
return 16;
|
|
case 0xC6: return alu(0, 8);
|
|
case 0xD6: return alu(2, 8);
|
|
case 0xE6: return alu(4, 8);
|
|
case 0xF6: return alu(6, 8);
|
|
case 0xC7: return call(0x00, -1);
|
|
case 0xD7: return call(0x10, -1);
|
|
case 0xE7: return call(0x20, -1);
|
|
case 0xF7: return call(0x30, -1);
|
|
case 0xC8:
|
|
if ((r[rF] & FLAGZ) != 0) {
|
|
pc = pop16();
|
|
return 20;
|
|
}
|
|
return 8;
|
|
case 0xD8:
|
|
if ((r[rF] & FLAGC) != 0) {
|
|
pc = pop16();
|
|
return 20;
|
|
}
|
|
return 8;
|
|
case 0xE8: case 0xF8:
|
|
s = fetch8();
|
|
v = sp + s;
|
|
v4 = (sp & 0xF) + (s & 0xF);
|
|
w = (sp & 0xFF) + (s & 0xFF);
|
|
r[rF] = 0;
|
|
if (v4 >= 0x10)
|
|
r[rF] |= FLAGH;
|
|
if (w >= 0x100)
|
|
r[rF] |= FLAGC;
|
|
if (op == 0xE8) {
|
|
sp = v;
|
|
return 16;
|
|
} else {
|
|
r[rL] = v;
|
|
r[rH] = v >> 8;
|
|
return 12;
|
|
}
|
|
case 0xC9: pc = pop16(); return 16;
|
|
case 0xD9:
|
|
pc = pop16();
|
|
ime = 1;
|
|
return 16;
|
|
case 0xE9: pc = HL(); return 4;
|
|
case 0xF9: sp = HL(); return 8;
|
|
case 0xCA: return jump((r[rF] & FLAGZ) != 0);
|
|
case 0xDA: return jump((r[rF] & FLAGC) != 0);
|
|
case 0xEA: memwrite(fetch16(), r[rA]); return 16;
|
|
case 0xFA: r[rA] = memread(fetch16()); return 16;
|
|
case 0xCB: return bits();
|
|
case 0xFB: ime = 1; return 4;
|
|
case 0xCC: return call(fetch16(), (r[rF] & FLAGZ) != 0);
|
|
case 0xDC: return call(fetch16(), (r[rF] & FLAGC) != 0);
|
|
case 0xCD: return call(fetch16(), 1);
|
|
case 0xCE: return alu(1, 8);
|
|
case 0xDE: return alu(3, 8);
|
|
case 0xEE: return alu(5, 8);
|
|
case 0xFE: return alu(7, 8);
|
|
case 0xCF: return call(0x08, -1);
|
|
case 0xDF: return call(0x18, -1);
|
|
case 0xEF: return call(0x28, -1);
|
|
case 0xFF: return call(0x38, -1);
|
|
}
|
|
}
|