gb/gameboy/ops.c
2022-03-14 22:55:51 -04:00

686 lines
10 KiB
C

#include "ops.h"
u8 flag(struct cpu* cpu)
{
switch ((cpu->op >> 3) & 3) {
case 0: return !ISF(ZF);
case 1: return ISF(ZF);
case 2: return !ISF(CF);
case 3: return ISF(CF);
default: return 0;
}
}
u8 high_get(struct cpu* cpu)
{
switch ((cpu->op & 0x38) >> 3) {
case 0: return HI(BC);
case 1: return LO(BC);
case 2: return HI(DE);
case 3: return LO(DE);
case 4: return HI(HL);
case 5: return LO(HL);
case 6: return READ(cpu->HL);
case 7: return HI(AF);
default: return 0;
}
}
void high_set(struct cpu* cpu, u8 dat)
{
switch ((cpu->op & 0x38) >> 3) {
case 0: HI(BC) = dat; break;
case 1: LO(BC) = dat; break;
case 2: HI(DE) = dat; break;
case 3: LO(DE) = dat; break;
case 4: HI(HL) = dat; break;
case 5: LO(HL) = dat; break;
case 6: WRITE(cpu->HL, dat); break;
case 7: HI(AF) = dat; break;
}
}
u8 low_get(struct cpu* cpu)
{
switch (cpu->op & 7) {
case 0: return HI(BC);
case 1: return LO(BC);
case 2: return HI(DE);
case 3: return LO(DE);
case 4: return HI(HL);
case 5: return LO(HL);
case 6: return READ(cpu->HL);
case 7: return HI(AF);
default: return 0;
}
}
void low_set(struct cpu* cpu, u8 dat)
{
switch (cpu->op & 7) {
case 0: HI(BC) = dat; break;
case 1: LO(BC) = dat; break;
case 2: HI(DE) = dat; break;
case 3: LO(DE) = dat; break;
case 4: HI(HL) = dat; break;
case 5: LO(HL) = dat; break;
case 6: WRITE(cpu->HL, dat); break;
case 7: HI(AF) = dat; break;
}
}
void push(struct cpu* cpu, u8 dat)
{
WRITE(--cpu->SP, dat);
}
void push16(struct cpu* cpu, u16 dat)
{
WRITE(--cpu->SP, dat >> 8);
WRITE(--cpu->SP, dat & 0xff);
}
u8 pop(struct cpu* cpu)
{
return READ(cpu->SP++);
}
u16 pop16(struct cpu* cpu)
{
u16 res = READ(cpu->SP++);
res |= (READ(cpu->SP++) << 8);
return res;
}
void rlca(struct cpu* cpu)
{
CLRALL;
TOGIF(CF, HI(AF) >> 7);
HI(AF) = (HI(AF) << 1) | (HI(AF) >> 7);
}
void rrca(struct cpu* cpu)
{
CLRALL;
TOGIF(CF, HI(AF) & 1);
HI(AF) = (HI(AF) >> 1) | (HI(AF) << 7);
}
void rla(struct cpu* cpu)
{
u8 oc = ISF(CF) >> 4;
CLRALL;
TOGIF(CF, HI(AF) >> 7);
HI(AF) = (HI(AF) << 1) | oc;
}
void rra(struct cpu* cpu)
{
u8 ob1 = HI(AF) & 1, oc = ISF(CF) >> 4;
CLRALL;
HI(AF) >>= 1;
if (oc) HI(AF) |= 0x80;
TOGIF(CF, ob1);
}
void rlc(struct cpu* cpu)
{
u8 dat = low_get(cpu), nd;
u8 c = (dat & 0x80) >> 7;
nd = (dat << 1) | c;
CLRALL;
TOGIF(CF, c);
TOGIF(ZF, (!(dat << 1)));
low_set(cpu, nd);
}
void rrc(struct cpu* cpu)
{
u8 dat = low_get(cpu);
u8 c = dat & 1;
dat = (dat >> 1) | (c << 7);
CLRALL;
TOGIF(CF, c);
TOGIF(ZF, (!dat));
low_set(cpu, dat);
}
void rl(struct cpu* cpu)
{
u8 dat = low_get(cpu), c = ISF(CF) >> 4;
u8 ob7 = (dat & 0x80);
dat = (dat << 1) | c;
CLRALL;
TOGIF(CF, ob7);
TOGIF(ZF, (!dat));
low_set(cpu, dat);
}
void rr(struct cpu* cpu)
{
u8 dat = low_get(cpu), c = ISF(CF) >> 4;
u8 ob1 = dat & 1;
dat = (dat >> 1) | (c << 7);
CLRALL;
TOGIF(CF, ob1);
TOGIF(ZF, (!dat));
CLRF(NF | HF);
low_set(cpu, dat);
}
void daa(struct cpu* cpu)
{
if (ISF(NF)) {
if (ISF(CF)) HI(AF) -= 0x60;
if (ISF(HF)) HI(AF) -= 0x06;
}
else {
if (ISF(CF) || (HI(AF) & 0xff) > 0x99) {
HI(AF) += 0x60;
SETF(CF);
}
if (ISF(HF) || (HI(AF) & 0x0f) > 0x09) {
HI(AF) += 0x06;
}
}
TOGIF(ZF, (!HI(AF)));
CLRF(HF);
}
void cpl(struct cpu* cpu)
{
HI(AF) ^= 0xff;
SETF(NF | HF);
}
void scf(struct cpu* cpu)
{
CLRF(NF);
CLRF(HF);
SETF(CF);
}
void ccf(struct cpu* cpu)
{
CLRF(NF);
CLRF(HF);
TOGF(CF);
}
void sla(struct cpu* cpu)
{
u8 dat = low_get(cpu);
CLRALL;
TOGIF(CF, (dat & 0x80));
TOGIF(ZF, (!(dat & 0x7f)));
dat = (u8)(dat << 1);
low_set(cpu, dat);
}
void sra(struct cpu* cpu)
{
u8 dat = low_get(cpu);
u8 ob7 = dat & 0x80;
CLRALL;
TOGIF(CF, (dat & 1));
dat = (dat >> 1) | ob7;
if (dat & 0x80) dat |= 0x80;
TOGIF(ZF, (!dat));
low_set(cpu, dat);
}
void swap(struct cpu* cpu)
{
u8 dat = low_get(cpu), nd;
nd = (dat >> 4) | (dat << 4);
TOGIF(ZF, (!dat));
CLRF(NF | HF | CF);
low_set(cpu, nd);
}
void srl(struct cpu* cpu)
{
u8 dat = low_get(cpu);
CLRALL;
TOGIF(CF, (dat & 1));
dat >>= 1;
TOGIF(ZF, (!dat));
low_set(cpu, dat);
}
void add(struct cpu* cpu, u8 reg)
{
int res = HI(AF) + reg;
CLRF(NF);
TOGIF(ZF, (!((u8)res)));
TOGIF(HF, ((HI(AF) & 0xf) + (reg & 0xf) > 0xf));
TOGIF(CF, ((((unsigned)HI(AF)) + ((unsigned)reg) > 0xff)));
HI(AF) = (u8)res;
}
void adc(struct cpu* cpu, u8 reg)
{
u8 oc = ISF(CF) >> 4, oa = HI(AF);
int res = HI(AF) + reg + oc;
CLRF(NF);
TOGIF(ZF, (!((u8)res)));
TOGIF(HF, ((oa & 0xf) + (reg & 0xf) + oc > 0xf));
TOGIF(CF, ((unsigned)oa + (unsigned)reg + oc > 0xff));
HI(AF) = (u8)res;
}
void sub(struct cpu* cpu, u8 reg)
{
u8 oa = HI(AF);
HI(AF) -= reg;
SETF(NF);
TOGIF(ZF, (oa == reg));
TOGIF(CF, (reg > oa));
TOGIF(HF, ((reg & 0xf) > (oa & 0xf)));
}
void sbc(struct cpu* cpu, u8 reg)
{
u8 c = ISF(CF) >> 4;
u8 res = HI(AF) - reg - c;
SETF(NF);
TOGIF(ZF, (!res));
TOGIF(HF, ((HI(AF) & 0xf) < (reg & 0xf) + c));
TOGIF(CF, (((unsigned)HI(AF) - (unsigned)reg) - c > 0xff));
HI(AF) = res;
}
void and(struct cpu* cpu, u8 reg)
{
HI(AF) &= reg;
SETF(HF);
CLRF(NF | CF);
TOGIF(ZF, (!HI(AF)));
}
void or(struct cpu* cpu, u8 reg)
{
HI(AF) |= reg;
TOGIF(ZF, (!HI(AF)));
CLRF(NF | HF | CF);
}
void xor(struct cpu* cpu, u8 reg)
{
HI(AF) ^= reg;
TOGIF(ZF, (!HI(AF)));
CLRF(NF | HF | CF);
}
void cp(struct cpu* cpu, u8 reg)
{
CLRALL;
SETF(NF);
TOGIF(ZF, (HI(AF) == reg));
TOGIF(HF, ((HI(AF) & 0xf) < (reg & 0xf)));
TOGIF(CF, (HI(AF) < reg));
}
void illegal(struct cpu* cpu)
{
printf("ILLEGAL INSTRUCTION: %02X at $%04X\n", cpu->op, cpu->PC);
}
void nop(struct cpu* cpu)
{
(void)(cpu); /* do nothing */
}
void ld_u16_sp(struct cpu* cpu)
{
u16 a16 = READ16;
WRITE16(a16, cpu->SP);
}
void stop(struct cpu* cpu)
{
/* https://gbdev.io/pandocs/imgs/gb_stop.png */
printf("STOPPED\n");
(void)(cpu);
}
void jr_uncond(struct cpu* cpu)
{
s8 i = (s8)READ8;
cpu->PC += i;
}
void jr_cond(struct cpu* cpu)
{
s8 i = (s8)READ8;
if (flag(cpu)) {
cpu->cyc += 4;
cpu->PC += i;
}
}
void ld_r16_u16(struct cpu* cpu)
{
u16* r16 = cpu->r16g1[(cpu->op & 0x30) >> 4];
*r16 = READ16;
}
void add_hl_r16(struct cpu* cpu)
{
u16* r16 = cpu->r16g1[(cpu->op & 0x30) >> 4];
int res = cpu->HL + *r16;
TOGIF(CF, (res & 0x10000));
TOGIF(HF, ((cpu->HL ^ *r16 ^ (res & 0xffff)) & 0x1000));
CLRF(NF);
cpu->HL = (u16)res;
}
void ld_r16_a(struct cpu* cpu)
{
u16* r16 = cpu->r16g2[(cpu->op & 0x30) >> 4];
if (!r16) {
if ((cpu->op & 0x30) >> 4 == 2) {
ldi_hl_a(cpu);
}
else if ((cpu->op & 0x30) >> 4 == 3) {
ldd_hl_a(cpu);
}
}
else {
WRITE(*r16, HI(AF));
}
}
void ld_a_r16(struct cpu* cpu)
{
u16* r16 = cpu->r16g2[(cpu->op & 0x30) >> 4];
if (!r16) {
if ((cpu->op & 0x30) >> 4 == 2) {
ldi_a_hl(cpu);
}
else {
ldd_a_hl(cpu);
}
}
else {
HI(AF) = READ(*r16);
}
}
void inc_r16(struct cpu* cpu)
{
u16* r16 = cpu->r16g1[(cpu->op & 0x30) >> 4];
*r16 += 1;
}
void dec_r16(struct cpu* cpu)
{
u16* r16 = cpu->r16g1[(cpu->op & 0x30) >> 4];
*r16 -= 1;
}
void inc_r8(struct cpu* cpu)
{
u8 r8 = high_get(cpu);
r8++;
high_set(cpu, r8);
TOGIF(ZF, (!r8));
TOGIF(HF, (!(r8 & 0xf)));
CLRF(NF);
}
void dec_r8(struct cpu* cpu)
{
u8 r8 = high_get(cpu);
r8--;
high_set(cpu, r8);
TOGIF(ZF, (!r8));
TOGIF(HF, ((r8 & 0xf) == 0xf));
SETF(NF);
}
void ld_r8_u8(struct cpu* cpu)
{
high_set(cpu, READ8);
}
void do_op1(struct cpu* cpu)
{
op1[(cpu->op & 0x38) >> 3](cpu);
}
void halt(struct cpu* cpu)
{
/* TODO: do halt */
cpu->halted = 1;
}
void ld_r8_r8(struct cpu* cpu)
{
u8 dat = low_get(cpu);
high_set(cpu, dat);
}
void alu_a_r8(struct cpu* cpu)
{
u8 reg = low_get(cpu);
u8 loc = (cpu->op & 0x38) >> 3;
op2[loc](cpu, reg);
}
void ret_cond(struct cpu* cpu)
{
if (flag(cpu)) {
cpu->cyc += 12;
cpu->PC = pop16(cpu);
}
}
void ld_ff00_u8_a(struct cpu* cpu)
{
WRITE(0xff00 | READ8, HI(AF));
}
void add_sp_i8(struct cpu* cpu)
{
s8 n = (s8)READ8;
CLRF(ZF | NF);
TOGIF(HF, ((cpu->SP & 0xf) + (n & 0xf) > 0xf));
TOGIF(CF, ((cpu->SP & 0xff) + (n & 0xff) > 0xff));
cpu->SP += n;
}
void ld_a_ff00_u8(struct cpu* cpu)
{
u8 a8 = READ8;
HI(AF) = READ(0xff00 + a8);
}
void ld_hl_sp_i8(struct cpu* cpu)
{
s8 n = (s8)READ8;
CLRF(ZF | NF);
TOGIF(HF, ((cpu->SP & 0xf) + (n & 0xf) > 0xf));
TOGIF(CF, ((cpu->SP & 0xff) + (n & 0xff) > 0xff));
cpu->HL = cpu->SP + n;
}
void pop_r16(struct cpu* cpu)
{
u16* r16 = cpu->r16g3[(cpu->op & 0x30) >> 4];
*r16 = pop16(cpu);
cpu->AF &= 0xfff0;
}
void ret(struct cpu* cpu)
{
cpu->PC = pop16(cpu);
}
void reti(struct cpu* cpu)
{
ret(cpu);
cpu->ime = 1;
}
void jp_hl(struct cpu* cpu)
{
cpu->PC = cpu->HL;
}
void ld_sp_hl(struct cpu* cpu)
{
cpu->SP = cpu->HL;
}
void jp_cond(struct cpu* cpu)
{
u16 a16 = READ16;
if (flag(cpu)) {
cpu->cyc += 4;
cpu->PC = a16;
}
}
void jp_uncond(struct cpu* cpu)
{
cpu->PC = READ16;
}
void cb(struct cpu* cpu)
{
cpu->op = READ8;
cpu->cyc += cb_cyc_table[cpu->op] * 4;
op_cb_list[cpu->op].fp(cpu);
}
void ld_ff00_c_a(struct cpu* cpu)
{
WRITE(0xff00 + LO(BC), HI(AF));
}
void ld_u16_a(struct cpu* cpu)
{
WRITE(READ16, HI(AF));
}
void ld_a_ff00_c(struct cpu* cpu)
{
HI(AF) = READ(0xff00 + LO(BC));
}
void ld_a_u16(struct cpu* cpu)
{
HI(AF) = READ(READ16);
}
void ei(struct cpu* cpu)
{
cpu->ime = 1;
}
void di(struct cpu* cpu)
{
cpu->ime = 0;
}
void call_cond(struct cpu* cpu)
{
u16 addr = READ16;
if (flag(cpu)) {
cpu->cyc += 12;
push16(cpu, cpu->PC);
cpu->PC = addr;
}
}
void push_r16(struct cpu* cpu)
{
u16* r16 = cpu->r16g3[(cpu->op & 0x30) >> 4];
push16(cpu, *r16);
}
void call_u16(struct cpu* cpu)
{
u16 addr = READ16;
push16(cpu, cpu->PC);
cpu->PC = addr;
}
void alu_a_u8(struct cpu* cpu)
{
u8 loc = (cpu->op & 0x38) >> 3;
op2[loc](cpu, READ8);
}
void rst(struct cpu* cpu)
{
push16(cpu, cpu->PC);
cpu->PC = cpu->op & 0x38;
}
void ldi_hl_a(struct cpu* cpu)
{
WRITE(cpu->HL++, HI(AF));
}
void ldi_a_hl(struct cpu* cpu)
{
HI(AF) = READ(cpu->HL++);
}
void ldd_hl_a(struct cpu* cpu)
{
WRITE(cpu->HL--, HI(AF));
}
void ldd_a_hl(struct cpu* cpu)
{
HI(AF) = READ(cpu->HL--);
}
void shift_rot(struct cpu* cpu)
{
op3[(cpu->op & 0x38) >> 3](cpu);
}
void bit_res_set(struct cpu* cpu)
{
u8 dat = low_get(cpu),
bit = 1 << ((cpu->op >> 3) & 7);
switch ((cpu->op & 0xc0) >> 6) {
case 1: /*BIT b,r*/
CLRF(NF);
SETF(HF);
TOGIF(ZF, (!(dat & bit)));
return;
case 2: /*RES b,r*/
dat &= ~bit;
break;
case 3: /*SET b,r*/
dat |= bit;
break;
}
low_set(cpu, dat);
}