commit 335839205a0d65767cc267f484b7a7aaed1e9aac Author: Patrick Date: Tue Nov 25 19:01:03 2025 +0100 upload of the files for pgb diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..c04e5e1 --- /dev/null +++ b/.clangd @@ -0,0 +1,10 @@ +CompileFlags: + Add: + - -x + - c + - -Wall + - -Wextra + - -Werror + - -Wpedantic + - -std=c99 + - -Wno-empty-translation-unit diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f236ec6 --- /dev/null +++ b/Makefile @@ -0,0 +1,31 @@ +CC = cc +CFLAGS = -Wall -Wextra -Wpedantic -Werror -std=c99 -lX11 + +SRCDIR = src +OBJDIR = obj +SRC = $(wildcard $(SRCDIR)/*.c) +OBJ = $(SRC:$(SRCDIR)/%.c=$(OBJDIR)/%.o) + +TARGET = pgb + +debug: CFLAGS += -g3 -fsanitize=address +debug: all + +release: CFLAGS += -O3 -s +release: all + +all: $(OBJDIR) $(TARGET) + +$(TARGET): $(OBJ) + $(CC) $(CFLAGS) -o $@ $^ + +$(OBJDIR): + mkdir -p $(OBJDIR) + +$(OBJDIR)/%.o: $(SRCDIR)/%.c + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + rm -rf $(OBJDIR) $(TARGET) + +.PHONY: clean all debug release diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/pgb b/pgb new file mode 100755 index 0000000..ed42e0f Binary files /dev/null and b/pgb differ diff --git a/src/cpu.c b/src/cpu.c new file mode 100644 index 0000000..c8b6b00 --- /dev/null +++ b/src/cpu.c @@ -0,0 +1,2615 @@ +#include "pgb.h" +#include +#include + +#define USE_BOOTROM 0 + +static u8 memory[0x10000]; +static u8 registers[12]; +static u8 clock_queue = 0; +static u8 ime = 0; +static u8 halted = 0; + +static u8 oam_lock = 0; +static u8 vram_lock = 0; + +static FILE *log; + +#pragma region Abstracted Memory Access +static inline u8 rread8(const R8 reg) { return registers[reg]; } +static inline u16 rread16(const R16 reg) { + return ((const u16)rread8(reg * 2 + 1) & 0xFF) + + ((const u16)rread8((reg * 2) & 0xFF) << 8); +} + +static inline void rwrite8(const R8 reg, const u8 val) { registers[reg] = val; } +static inline void rwrite16(const R16 reg, const u16 val) { + rwrite8((reg * 2) + 1, val & 0xFF); + rwrite8(reg * 2, (val >> 8) & 0xFF); +} + +static inline u8 mread8(const u16 addr) { + if (addr >= 0x8000 && addr <= 0x9FFF && vram_lock) { + return 0xFF; + } else if (addr >= 0xFE00 && addr <= 0xFE9F && oam_lock) { + return 0xFF; + } else if (addr == 0xFF44) { + return 0x90; + } + return memory[addr]; +} +static inline u16 mread16(const u16 addr) { + return ((const u16)mread8(addr) & 0xFF) + + (((const u16)mread8(addr + 1) & 0xFF) << 8); +} + +static inline void mwrite8(const u16 addr, const u8 val) { + if (addr <= 0x7FFF) { + return; + } else if (addr >= 0x8000 && addr <= 0x9FFF && vram_lock) { + return; + } else if (addr >= 0xFE00 && addr <= 0xFE9F && oam_lock) { + return; + } + memory[addr] = val; +} +static inline void mwrite16(const u16 addr, const u16 val) { + mwrite8(addr, val & 0xFF); + mwrite8(addr + 1, (val >> 8) & 0xFF); +} + +static inline u8 rget(const R8 reg, const u8 bit) { + return (rread8(reg) >> bit) & 1; +} +static inline void rset(const R8 reg, const u8 bit) { + rwrite8(reg, rread8(reg) | 1 << bit); +} +static inline void rclear(const R8 reg, const u8 bit) { + rwrite8(reg, rread8(reg) & ~(1 << bit)); +} + +static inline u8 mget(const u16 addr, const u8 bit) { + return (mread8(addr) >> bit) & 1; +} +static inline void mset(const u16 addr, const u8 bit) { + mwrite8(addr, mread8(addr) | (1 << bit)); +} +static inline void mclear(const u16 addr, const u8 bit) { + mwrite8(addr, mread8(addr) & ~(1 << bit)); +} + +#pragma region Other Utilities +static inline void instruction_cost(const u8 size, const u8 clocks) { + rwrite16(R16_PC, rread16(R16_PC) + (size)); + clock_queue += (clocks); +} + +static inline void error_handler(void) { + puts("\nUnimplemented/Invalid Instruction!"); + printf("AF: 0x%X, BC: 0x%X, DE: 0x%X, HL: 0x%X, SP: 0x%X, PC: 0x%X\n", + rread16(R16_AF), rread16(R16_BC), rread16(R16_DE), rread16(R16_HL), + rread16(R16_SP), rread16(R16_PC)); + printf("Hex: 0x%X\n", mread8(rread16(R16_PC))); + printf("Oct: %o\n", mread8(rread16(R16_PC))); +} + +#pragma region Flag Helpers +#define zflag(val) (val) == 0 ? rset(R8_F, FLAG_Z) : rclear(R8_F, FLAG_Z); + +#define hflag_add(result, operand) \ + ((result) & 0x0F) < ((operand) & 0x0F) ? rset(R8_F, FLAG_H) \ + : rclear(R8_F, FLAG_H); + +#define cflag_add(result, operand) \ + (result) < (operand) ? rset(R8_F, FLAG_C) : rclear(R8_F, FLAG_C); + +#define hflag_sub(original, result) \ + ((original) & 0x0F) < ((result) & 0x0F) ? rset(R8_F, FLAG_H) \ + : rclear(R8_F, FLAG_H); + +#define cflag_sub(original, result) \ + (original) < (result) ? rset(R8_F, FLAG_C) : rclear(R8_F, FLAG_C); + +#pragma region 8 Bit Arithmetic +static inline void add(const u8 val) { + rwrite8(R8_A, rread8(R8_A) + val); + zflag(rread8(R8_A)); + rclear(R8_F, FLAG_N); + hflag_add(rread8(R8_A), val); + cflag_add(rread8(R8_A), val); +} + +static inline void adc(const u8 val) { + u8 f_save; + u8 flag = rget(R8_F, FLAG_C); + add(val); + f_save = rread8(R8_F); + add(flag); + rwrite8(R8_F, rread8(R8_F) | f_save); + zflag(rread8(R8_A)); +} + +static inline void sub(const u8 val) { + u8 original = rread8(R8_A); + rwrite8(R8_A, rread8(R8_A) - val); + zflag(rread8(R8_A)); + rset(R8_F, FLAG_N); + hflag_sub(original, rread8(R8_A)); + cflag_sub(original, rread8(R8_A)); +} + +static inline void sbc(const u8 val) { + u8 f_save; + u8 flag = rget(R8_F, FLAG_C); + sub(val); + f_save = rread8(R8_F); + sub(flag); + rwrite8(R8_F, rread8(R8_F) | f_save); + zflag(rread8(R8_A)); +} + +static inline void and (const u8 val) { + rwrite8(R8_A, rread8(R8_A) & val); + zflag(rread8(R8_A)); + rclear(R8_F, FLAG_N); + rset(R8_F, FLAG_H); + rclear(R8_F, FLAG_C); +} + +static inline void xor + (const u8 val) { + rwrite8(R8_A, rread8(R8_A) ^ val); + zflag(rread8(R8_A)); + rclear(R8_F, FLAG_N); + rclear(R8_F, FLAG_H); + rclear(R8_F, FLAG_C); + } + + static inline void or + (const u8 val) { + rwrite8(R8_A, rread8(R8_A) | val); + zflag(rread8(R8_A)); + rclear(R8_F, FLAG_N); + rclear(R8_F, FLAG_H); + rclear(R8_F, FLAG_C); +} + +static inline void cp(const u8 val) { + u8 result = rread8(R8_A) - val; + zflag(result); + rset(R8_F, FLAG_N); + hflag_sub(rread8(R8_A), result); + cflag_sub(rread8(R8_A), result); +} + +static inline void inc(const R8 reg) { + rwrite8(reg, rread8(reg) + 1); + zflag(rread8(reg)); + rclear(R8_F, FLAG_N); + hflag_add(rread8(reg), 1); +} + +static inline void dec(const R8 reg) { + u8 original = rread8(reg); + rwrite8(reg, rread8(reg) - 1); + zflag(rread8(reg)); + rset(R8_F, FLAG_N); + hflag_sub(original, rread8(reg)); +} + +#pragma region 16 Bit Arithmetic & Stack +static inline void add16(R16 dst, R16 reg) { + u16 o1 = rread16(dst); + u16 o2 = rread16(reg); + rwrite16(dst, o1 + o2); + rclear(R8_F, FLAG_N); + if (o1 > rread16(dst)) { + rset(R8_F, FLAG_C); + } else { + rclear(R8_F, FLAG_C); + } + if ((o1 & 0xFFF) + (o2 & 0xFFF) > 0xFFF) { + rset(R8_F, FLAG_H); + } else { + rclear(R8_F, FLAG_H); + } +} + +static inline void push(R16 reg) { + rwrite16(R16_SP, rread16(R16_SP) - 2); + mwrite16(rread16(R16_SP), rread16(reg)); +} + +static inline void pop(R16 reg) { + rwrite16(reg, mread16(rread16(R16_SP))); + rwrite16(R16_SP, rread16(R16_SP) + 2); + if (reg == R16_AF) { + rwrite8(R8_F, (rread8(R8_F) >> 4) << 4); + } +} + +#pragma region Shift, Rotate & Bit Instruction Helpers +static inline u8 rl(u8 val) { + u8 result; + result = (val << 1) | rget(R8_F, FLAG_C); + (val >> 7) & 1 ? rset(R8_F, FLAG_C) : rclear(R8_F, FLAG_C); + zflag(result); + rclear(R8_F, FLAG_N); + rclear(R8_F, FLAG_H); + return result; +} +static inline u8 rr(u8 val) { + u8 result; + result = (rget(R8_F, FLAG_C) << 7) | (val >> 1); + val & 1 ? rset(R8_F, FLAG_C) : rclear(R8_F, FLAG_C); + zflag(result); + rclear(R8_F, FLAG_N); + rclear(R8_F, FLAG_H); + return result; +} +static inline u8 rlc(u8 val) { + u8 result; + (val >> 7) & 1 ? rset(R8_F, FLAG_C) : rclear(R8_F, FLAG_C); + result = (val << 1) | (val >> 7); + zflag(result); + rclear(R8_F, FLAG_N); + rclear(R8_F, FLAG_H); + return result; +} +static inline u8 rrc(u8 val) { + u8 result; + val & 1 ? rset(R8_F, FLAG_C) : rclear(R8_F, FLAG_C); + result = (val << 7) | (val >> 1); + zflag(result); + rclear(R8_F, FLAG_N); + rclear(R8_F, FLAG_H); + return result; +} +static inline u8 sla(u8 val) { + u8 result; + (val >> 7) & 1 ? rset(R8_F, FLAG_C) : rclear(R8_F, FLAG_C); + result = val << 1; + zflag(result); + rclear(R8_F, FLAG_N); + rclear(R8_F, FLAG_H); + return result; +} +static inline u8 sra(u8 val) { + u8 result; + val & 1 ? rset(R8_F, FLAG_C) : rclear(R8_F, FLAG_C); + result = (val & (1 << 7)) | (val >> 1); + zflag(result); + rclear(R8_F, FLAG_N); + rclear(R8_F, FLAG_H); + return result; +} +static inline u8 swap(u8 val) { + u8 result; + result = (val >> 4) | (val << 4); + zflag(result); + rclear(R8_F, FLAG_N); + rclear(R8_F, FLAG_H); + rclear(R8_F, FLAG_C); + return result; +} +static inline u8 srl(u8 val) { + u8 result; + val & 1 ? rset(R8_F, FLAG_C) : rclear(R8_F, FLAG_C); + result = val >> 1; + zflag(result); + rclear(R8_F, FLAG_N); + rclear(R8_F, FLAG_H); + return result; +} +static inline void bit(u8 val, u8 bit) { + zflag((val >> bit) & 1); + rclear(R8_F, FLAG_N); + rset(R8_F, FLAG_H); +} + +#pragma region Interrupt Helpers + +static inline void interrupt_check(void) { + if (ime) { + ime = 0; + if (mget(0xFF0F, INT_VBLANK) && mget(0xFFFF, INT_VBLANK)) { + mclear(0xFF0F, INT_VBLANK); + push(R16_PC); + rwrite16(R16_PC, 0x40); + instruction_cost(0, 20); + halted = 0; + } else if (mget(0xFF0F, INT_LCD) && mget(0xFFFF, INT_LCD)) { + mclear(0xFF0F, INT_LCD); + push(R16_PC); + rwrite16(R16_PC, 0x48); + instruction_cost(0, 20); + halted = 0; + } else if (mget(0xFF0F, INT_TIMER) && mget(0xFFFF, INT_TIMER)) { + mclear(0xFF0F, INT_TIMER); + push(R16_PC); + rwrite16(R16_PC, 0x50); + instruction_cost(0, 20); + halted = 0; + } else if (mget(0xFF0F, INT_SERIAL) && mget(0xFFFF, INT_SERIAL)) { + mclear(0xFF0F, INT_SERIAL); + push(R16_PC); + rwrite16(R16_PC, 0x58); + instruction_cost(0, 20); + halted = 0; + } else if (mget(0xFF0F, INT_JOYPAD) && mget(0xFFFF, INT_JOYPAD)) { + mclear(0xFF0F, INT_JOYPAD); + push(R16_PC); + rwrite16(R16_PC, 0x60); + instruction_cost(0, 20); + halted = 0; + } + } +} + +#pragma region External Access +void lock_oam(void) { oam_lock = 1; } +void unlock_oam(void) { oam_lock = 0; } +void lock_vram(void) { vram_lock = 1; } +void unlock_vram(void) { vram_lock = 0; } + +void create_interrupt(const INTERRUPT interrupt) { mset(0xFF0F, interrupt); } + +u8 mread8_external(const u16 addr) { return mread8(addr); } +u16 mread16_external(const u16 addr) { return mread16(addr); } +void mwrite8_external(const u16 addr, const u8 val) { mwrite8(addr, val); } +void mwrite16_external(const u16 addr, const u16 val) { mwrite16(addr, val); } + +#pragma region CPU Initialization +void cpu_init(const char *gamerom, const char *logfile) { + clock_queue = 0; + fread(memory, 0x8000, 1, fopen(gamerom, "rb")); + log = fopen(logfile, "w"); + + switch (mread8(0x147)) { + case 0: + break; + default: + puts("(Possibly) Unsupported Cartridge Type!"); + break; + } + + rwrite8(R8_A, 0x01); + rwrite8(R8_F, 0xB0); + rwrite8(R8_B, 0x00); + rwrite8(R8_C, 0x13); + rwrite8(R8_D, 0x00); + rwrite8(R8_E, 0xD8); + rwrite8(R8_H, 0x01); + rwrite8(R8_L, 0x4D); + rwrite16(R16_SP, 0xFFFE); + rwrite16(R16_PC, 0x0100); +} + +static inline void prefix_run(const u8 instr) { + switch (instr) { +#pragma region Prefix 0x00 + case 0x00: + rwrite8(R8_B, rlc(rread8(R8_B))); + instruction_cost(2, 8); + break; + case 0x01: + rwrite8(R8_C, rlc(rread8(R8_C))); + instruction_cost(2, 8); + break; + case 0x02: + rwrite8(R8_D, rlc(rread8(R8_D))); + instruction_cost(2, 8); + break; + case 0x03: + rwrite8(R8_E, rlc(rread8(R8_E))); + instruction_cost(2, 8); + break; + case 0x04: + rwrite8(R8_H, rlc(rread8(R8_H))); + instruction_cost(2, 8); + break; + case 0x05: + rwrite8(R8_L, rlc(rread8(R8_L))); + instruction_cost(2, 8); + break; + case 0x06: + mwrite8(rread16(R16_HL), rlc(mread8(rread16(R16_HL)))); + instruction_cost(2, 16); + break; + case 0x07: + rwrite8(R8_A, rlc(rread8(R8_A))); + instruction_cost(2, 8); + break; + case 0x08: + rwrite8(R8_B, rrc(rread8(R8_B))); + instruction_cost(2, 8); + break; + case 0x09: + rwrite8(R8_C, rrc(rread8(R8_C))); + instruction_cost(2, 8); + break; + case 0x0A: + rwrite8(R8_D, rrc(rread8(R8_D))); + instruction_cost(2, 8); + break; + case 0x0B: + rwrite8(R8_E, rrc(rread8(R8_E))); + instruction_cost(2, 8); + break; + case 0x0C: + rwrite8(R8_H, rrc(rread8(R8_H))); + instruction_cost(2, 8); + break; + case 0x0D: + rwrite8(R8_L, rrc(rread8(R8_L))); + instruction_cost(2, 8); + break; + case 0x0E: + mwrite8(rread16(R16_HL), rrc(mread8(rread16(R16_HL)))); + instruction_cost(2, 16); + break; + case 0x0F: + rwrite8(R8_A, rrc(rread8(R8_A))); + instruction_cost(2, 8); + break; +#pragma region Prefix 0x10 + case 0x10: + rwrite8(R8_B, rl(rread8(R8_B))); + instruction_cost(2, 8); + break; + case 0x11: + rwrite8(R8_C, rl(rread8(R8_C))); + instruction_cost(2, 8); + break; + case 0x12: + rwrite8(R8_D, rl(rread8(R8_D))); + instruction_cost(2, 8); + break; + case 0x13: + rwrite8(R8_E, rl(rread8(R8_E))); + instruction_cost(2, 8); + break; + case 0x14: + rwrite8(R8_H, rl(rread8(R8_H))); + instruction_cost(2, 8); + break; + case 0x15: + rwrite8(R8_L, rl(rread8(R8_L))); + instruction_cost(2, 8); + break; + case 0x16: + mwrite8(rread16(R16_HL), rl(mread8(rread16(R16_HL)))); + instruction_cost(2, 16); + break; + case 0x17: + rwrite8(R8_A, rl(rread8(R8_A))); + instruction_cost(2, 8); + break; + case 0x18: + rwrite8(R8_B, rr(rread8(R8_B))); + instruction_cost(2, 8); + break; + case 0x19: + rwrite8(R8_C, rr(rread8(R8_C))); + instruction_cost(2, 8); + break; + case 0x1A: + rwrite8(R8_D, rr(rread8(R8_D))); + instruction_cost(2, 8); + break; + case 0x1B: + rwrite8(R8_E, rr(rread8(R8_E))); + instruction_cost(2, 8); + break; + case 0x1C: + rwrite8(R8_H, rr(rread8(R8_H))); + instruction_cost(2, 8); + break; + case 0x1D: + rwrite8(R8_L, rr(rread8(R8_L))); + instruction_cost(2, 8); + break; + case 0x1E: + mwrite8(rread16(R16_HL), rr(mread8(rread16(R16_HL)))); + instruction_cost(2, 16); + break; + case 0x1F: + rwrite8(R8_A, rr(rread8(R8_A))); + instruction_cost(2, 8); + break; +#pragma region Prefix 0x20 + case 0x20: + rwrite8(R8_B, sla(rread8(R8_B))); + instruction_cost(2, 8); + break; + case 0x21: + rwrite8(R8_C, sla(rread8(R8_C))); + instruction_cost(2, 8); + break; + case 0x22: + rwrite8(R8_D, sla(rread8(R8_D))); + instruction_cost(2, 8); + break; + case 0x23: + rwrite8(R8_E, sla(rread8(R8_E))); + instruction_cost(2, 8); + break; + case 0x24: + rwrite8(R8_H, sla(rread8(R8_H))); + instruction_cost(2, 8); + break; + case 0x25: + rwrite8(R8_L, sla(rread8(R8_L))); + instruction_cost(2, 8); + break; + case 0x26: + mwrite8(rread16(R16_HL), sla(mread8(rread16(R16_HL)))); + instruction_cost(2, 16); + break; + case 0x27: + rwrite8(R8_A, sla(rread8(R8_A))); + instruction_cost(2, 8); + break; + case 0x28: + rwrite8(R8_B, sra(rread8(R8_B))); + instruction_cost(2, 8); + break; + case 0x29: + rwrite8(R8_C, sra(rread8(R8_C))); + instruction_cost(2, 8); + break; + case 0x2A: + rwrite8(R8_D, sra(rread8(R8_D))); + instruction_cost(2, 8); + break; + case 0x2B: + rwrite8(R8_E, sra(rread8(R8_E))); + instruction_cost(2, 8); + break; + case 0x2C: + rwrite8(R8_H, sra(rread8(R8_H))); + instruction_cost(2, 8); + break; + case 0x2D: + rwrite8(R8_L, sra(rread8(R8_L))); + instruction_cost(2, 8); + break; + case 0x2E: + mwrite8(rread16(R16_HL), sra(mread8(rread16(R16_HL)))); + instruction_cost(2, 16); + break; + case 0x2F: + rwrite8(R8_A, sra(rread8(R8_A))); + instruction_cost(2, 8); + break; +#pragma region Prefix 0x30 + case 0x30: + rwrite8(R8_B, swap(rread8(R8_B))); + instruction_cost(2, 8); + break; + case 0x31: + rwrite8(R8_C, swap(rread8(R8_C))); + instruction_cost(2, 8); + break; + case 0x32: + rwrite8(R8_D, swap(rread8(R8_D))); + instruction_cost(2, 8); + break; + case 0x33: + rwrite8(R8_E, swap(rread8(R8_E))); + instruction_cost(2, 8); + break; + case 0x34: + rwrite8(R8_H, swap(rread8(R8_H))); + instruction_cost(2, 8); + break; + case 0x35: + rwrite8(R8_L, swap(rread8(R8_L))); + instruction_cost(2, 8); + break; + case 0x36: + mwrite8(rread16(R16_HL), swap(mread8(rread16(R16_HL)))); + instruction_cost(2, 16); + break; + case 0x37: + rwrite8(R8_A, swap(rread8(R8_A))); + instruction_cost(2, 8); + break; + case 0x38: + rwrite8(R8_B, srl(rread8(R8_B))); + instruction_cost(2, 8); + break; + case 0x39: + rwrite8(R8_C, srl(rread8(R8_C))); + instruction_cost(2, 8); + break; + case 0x3A: + rwrite8(R8_D, srl(rread8(R8_D))); + instruction_cost(2, 8); + break; + case 0x3B: + rwrite8(R8_E, srl(rread8(R8_E))); + instruction_cost(2, 8); + break; + case 0x3C: + rwrite8(R8_H, srl(rread8(R8_H))); + instruction_cost(2, 8); + break; + case 0x3D: + rwrite8(R8_L, srl(rread8(R8_L))); + instruction_cost(2, 8); + break; + case 0x3E: + mwrite8(rread16(R16_HL), srl(mread8(rread16(R16_HL)))); + instruction_cost(2, 16); + break; + case 0x3F: + rwrite8(R8_A, srl(rread8(R8_A))); + instruction_cost(2, 8); + break; +#pragma region Prefix 0x40 + case 0x40: + bit(rread8(R8_B), 0); + instruction_cost(2, 8); + break; + case 0x41: + bit(rread8(R8_C), 0); + instruction_cost(2, 8); + break; + case 0x42: + bit(rread8(R8_D), 0); + instruction_cost(2, 8); + break; + case 0x43: + bit(rread8(R8_E), 0); + instruction_cost(2, 8); + break; + case 0x44: + bit(rread8(R8_H), 0); + instruction_cost(2, 8); + break; + case 0x45: + bit(rread8(R8_L), 0); + instruction_cost(2, 8); + break; + case 0x46: + bit(mread8(rread16(R16_HL)), 0); + instruction_cost(2, 12); + break; + case 0x47: + bit(rread8(R8_A), 0); + instruction_cost(2, 8); + break; + case 0x48: + bit(rread8(R8_B), 1); + instruction_cost(2, 8); + break; + case 0x49: + bit(rread8(R8_C), 1); + instruction_cost(2, 8); + break; + case 0x4A: + bit(rread8(R8_D), 1); + instruction_cost(2, 8); + break; + case 0x4B: + bit(rread8(R8_E), 1); + instruction_cost(2, 8); + break; + case 0x4C: + bit(rread8(R8_H), 1); + instruction_cost(2, 8); + break; + case 0x4D: + bit(rread8(R8_L), 1); + instruction_cost(2, 8); + break; + case 0x4E: + bit(mread8(rread16(R16_HL)), 1); + instruction_cost(2, 12); + break; + case 0x4F: + bit(rread8(R8_A), 1); + instruction_cost(2, 8); + break; +#pragma region Prefix 0x50 + case 0x50: + bit(rread8(R8_B), 2); + instruction_cost(2, 8); + break; + case 0x51: + bit(rread8(R8_C), 2); + instruction_cost(2, 8); + break; + case 0x52: + bit(rread8(R8_D), 2); + instruction_cost(2, 8); + break; + case 0x53: + bit(rread8(R8_E), 2); + instruction_cost(2, 8); + break; + case 0x54: + bit(rread8(R8_H), 2); + instruction_cost(2, 8); + break; + case 0x55: + bit(rread8(R8_L), 2); + instruction_cost(2, 8); + break; + case 0x56: + bit(mread8(rread16(R16_HL)), 2); + instruction_cost(2, 12); + break; + case 0x57: + bit(rread8(R8_A), 2); + instruction_cost(2, 8); + break; + case 0x58: + bit(rread8(R8_B), 3); + instruction_cost(2, 8); + break; + case 0x59: + bit(rread8(R8_C), 3); + instruction_cost(2, 8); + break; + case 0x5A: + bit(rread8(R8_D), 3); + instruction_cost(2, 8); + break; + case 0x5B: + bit(rread8(R8_E), 3); + instruction_cost(2, 8); + break; + case 0x5C: + bit(rread8(R8_H), 3); + instruction_cost(2, 8); + break; + case 0x5D: + bit(rread8(R8_L), 3); + instruction_cost(2, 8); + break; + case 0x5E: + bit(mread8(rread16(R16_HL)), 3); + instruction_cost(2, 12); + break; + case 0x5F: + bit(rread8(R8_A), 3); + instruction_cost(2, 8); + break; +#pragma region Prefix 0x60 + case 0x60: + bit(rread8(R8_B), 4); + instruction_cost(2, 8); + break; + case 0x61: + bit(rread8(R8_C), 4); + instruction_cost(2, 8); + break; + case 0x62: + bit(rread8(R8_D), 4); + instruction_cost(2, 8); + break; + case 0x63: + bit(rread8(R8_E), 4); + instruction_cost(2, 8); + break; + case 0x64: + bit(rread8(R8_H), 4); + instruction_cost(2, 8); + break; + case 0x65: + bit(rread8(R8_L), 4); + instruction_cost(2, 8); + break; + case 0x66: + bit(mread8(rread16(R16_HL)), 4); + instruction_cost(2, 12); + break; + case 0x67: + bit(rread8(R8_A), 4); + instruction_cost(2, 8); + break; + case 0x68: + bit(rread8(R8_B), 5); + instruction_cost(2, 8); + break; + case 0x69: + bit(rread8(R8_C), 5); + instruction_cost(2, 8); + break; + case 0x6A: + bit(rread8(R8_D), 5); + instruction_cost(2, 8); + break; + case 0x6B: + bit(rread8(R8_E), 5); + instruction_cost(2, 8); + break; + case 0x6C: + bit(rread8(R8_H), 5); + instruction_cost(2, 8); + break; + case 0x6D: + bit(rread8(R8_L), 5); + instruction_cost(2, 8); + break; + case 0x6E: + bit(mread8(rread16(R16_HL)), 5); + instruction_cost(2, 12); + break; + case 0x6F: + bit(rread8(R8_A), 5); + instruction_cost(2, 8); + break; +#pragma region Prefix 0x70 + case 0x70: + bit(rread8(R8_B), 6); + instruction_cost(2, 8); + break; + case 0x71: + bit(rread8(R8_C), 6); + instruction_cost(2, 8); + break; + case 0x72: + bit(rread8(R8_D), 6); + instruction_cost(2, 8); + break; + case 0x73: + bit(rread8(R8_E), 6); + instruction_cost(2, 8); + break; + case 0x74: + bit(rread8(R8_H), 6); + instruction_cost(2, 8); + break; + case 0x75: + bit(rread8(R8_L), 6); + instruction_cost(2, 8); + break; + case 0x76: + bit(mread8(rread16(R16_HL)), 6); + instruction_cost(2, 12); + break; + case 0x77: + bit(rread8(R8_A), 6); + instruction_cost(2, 8); + break; + case 0x78: + bit(rread8(R8_B), 7); + instruction_cost(2, 8); + break; + case 0x79: + bit(rread8(R8_C), 7); + instruction_cost(2, 8); + break; + case 0x7A: + bit(rread8(R8_D), 7); + instruction_cost(2, 8); + break; + case 0x7B: + bit(rread8(R8_E), 7); + instruction_cost(2, 8); + break; + case 0x7C: + bit(rread8(R8_H), 7); + instruction_cost(2, 8); + break; + case 0x7D: + bit(rread8(R8_L), 7); + instruction_cost(2, 8); + break; + case 0x7E: + bit(mread8(rread16(R16_HL)), 7); + instruction_cost(2, 12); + break; + case 0x7F: + bit(rread8(R8_A), 7); + instruction_cost(2, 8); + break; +#pragma region Prefix 0x80 + case 0x80: + rclear(R8_B, 0); + instruction_cost(2, 8); + break; + case 0x81: + rclear(R8_C, 0); + instruction_cost(2, 8); + break; + case 0x82: + rclear(R8_D, 0); + instruction_cost(2, 8); + break; + case 0x83: + rclear(R8_E, 0); + instruction_cost(2, 8); + break; + case 0x84: + rclear(R8_H, 0); + instruction_cost(2, 8); + break; + case 0x85: + rclear(R8_L, 0); + instruction_cost(2, 8); + break; + case 0x86: + mclear(rread16(R16_HL), 0); + instruction_cost(2, 16); + break; + case 0x87: + rclear(R8_A, 0); + instruction_cost(2, 8); + break; + case 0x88: + rclear(R8_B, 1); + instruction_cost(2, 8); + break; + case 0x89: + rclear(R8_C, 1); + instruction_cost(2, 8); + break; + case 0x8A: + rclear(R8_D, 1); + instruction_cost(2, 8); + break; + case 0x8B: + rclear(R8_E, 1); + instruction_cost(2, 8); + break; + case 0x8C: + rclear(R8_H, 1); + instruction_cost(2, 8); + break; + case 0x8D: + rclear(R8_L, 1); + instruction_cost(2, 8); + break; + case 0x8E: + mclear(rread16(R16_HL), 1); + instruction_cost(2, 16); + break; + case 0x8F: + rclear(R8_A, 1); + instruction_cost(2, 8); + break; +#pragma region Prefix 0x90 + case 0x90: + rclear(R8_B, 2); + instruction_cost(2, 8); + break; + case 0x91: + rclear(R8_C, 2); + instruction_cost(2, 8); + break; + case 0x92: + rclear(R8_D, 2); + instruction_cost(2, 8); + break; + case 0x93: + rclear(R8_E, 2); + instruction_cost(2, 8); + break; + case 0x94: + rclear(R8_H, 2); + instruction_cost(2, 8); + break; + case 0x95: + rclear(R8_L, 2); + instruction_cost(2, 8); + break; + case 0x96: + mclear(rread16(R16_HL), 2); + instruction_cost(2, 16); + break; + case 0x97: + rclear(R8_A, 2); + instruction_cost(2, 8); + break; + case 0x98: + rclear(R8_B, 3); + instruction_cost(2, 8); + break; + case 0x99: + rclear(R8_C, 3); + instruction_cost(2, 8); + break; + case 0x9A: + rclear(R8_D, 3); + instruction_cost(2, 8); + break; + case 0x9B: + rclear(R8_E, 3); + instruction_cost(2, 8); + break; + case 0x9C: + rclear(R8_H, 3); + instruction_cost(2, 8); + break; + case 0x9D: + rclear(R8_L, 3); + instruction_cost(2, 8); + break; + case 0x9E: + mclear(rread16(R16_HL), 3); + instruction_cost(2, 16); + break; + case 0x9F: + rclear(R8_A, 3); + instruction_cost(2, 8); + break; +#pragma region Prefix 0xA0 + case 0xA0: + rclear(R8_B, 4); + instruction_cost(2, 8); + break; + case 0xA1: + rclear(R8_C, 4); + instruction_cost(2, 8); + break; + case 0xA2: + rclear(R8_D, 4); + instruction_cost(2, 8); + break; + case 0xA3: + rclear(R8_E, 4); + instruction_cost(2, 8); + break; + case 0xA4: + rclear(R8_H, 4); + instruction_cost(2, 8); + break; + case 0xA5: + rclear(R8_L, 4); + instruction_cost(2, 8); + break; + case 0xA6: + mclear(rread16(R16_HL), 4); + instruction_cost(2, 16); + break; + case 0xA7: + rclear(R8_A, 4); + instruction_cost(2, 8); + break; + case 0xA8: + rclear(R8_B, 5); + instruction_cost(2, 8); + break; + case 0xA9: + rclear(R8_C, 5); + instruction_cost(2, 8); + break; + case 0xAA: + rclear(R8_D, 5); + instruction_cost(2, 8); + break; + case 0xAB: + rclear(R8_E, 5); + instruction_cost(2, 8); + break; + case 0xAC: + rclear(R8_H, 5); + instruction_cost(2, 8); + break; + case 0xAD: + rclear(R8_L, 5); + instruction_cost(2, 8); + break; + case 0xAE: + mclear(rread16(R16_HL), 5); + instruction_cost(2, 16); + break; + case 0xAF: + rclear(R8_A, 5); + instruction_cost(2, 8); + break; +#pragma region Prefix 0xB0 + case 0xB0: + rclear(R8_B, 6); + instruction_cost(2, 8); + break; + case 0xB1: + rclear(R8_C, 6); + instruction_cost(2, 8); + break; + case 0xB2: + rclear(R8_D, 6); + instruction_cost(2, 8); + break; + case 0xB3: + rclear(R8_E, 6); + instruction_cost(2, 8); + break; + case 0xB4: + rclear(R8_H, 6); + instruction_cost(2, 8); + break; + case 0xB5: + rclear(R8_L, 6); + instruction_cost(2, 8); + break; + case 0xB6: + mclear(rread16(R16_HL), 6); + instruction_cost(2, 16); + break; + case 0xB7: + rclear(R8_A, 6); + instruction_cost(2, 8); + break; + case 0xB8: + rclear(R8_B, 7); + instruction_cost(2, 8); + break; + case 0xB9: + rclear(R8_C, 7); + instruction_cost(2, 8); + break; + case 0xBA: + rclear(R8_D, 7); + instruction_cost(2, 8); + break; + case 0xBB: + rclear(R8_E, 7); + instruction_cost(2, 8); + break; + case 0xBC: + rclear(R8_H, 7); + instruction_cost(2, 8); + break; + case 0xBD: + rclear(R8_L, 7); + instruction_cost(2, 8); + break; + case 0xBE: + mclear(rread16(R16_HL), 7); + instruction_cost(2, 16); + break; + case 0xBF: + rclear(R8_A, 7); + instruction_cost(2, 8); + break; +#pragma region Prefix 0xC0 + case 0xC0: + rset(R8_B, 0); + instruction_cost(2, 8); + break; + case 0xC1: + rset(R8_C, 0); + instruction_cost(2, 8); + break; + case 0xC2: + rset(R8_D, 0); + instruction_cost(2, 8); + break; + case 0xC3: + rset(R8_E, 0); + instruction_cost(2, 8); + break; + case 0xC4: + rset(R8_H, 0); + instruction_cost(2, 8); + break; + case 0xC5: + rset(R8_L, 0); + instruction_cost(2, 8); + break; + case 0xC6: + mset(rread16(R16_HL), 0); + instruction_cost(2, 16); + break; + case 0xC7: + rset(R8_A, 0); + instruction_cost(2, 8); + break; + case 0xC8: + rset(R8_B, 1); + instruction_cost(2, 8); + break; + case 0xC9: + rset(R8_C, 1); + instruction_cost(2, 8); + break; + case 0xCA: + rset(R8_D, 1); + instruction_cost(2, 8); + break; + case 0xCB: + rset(R8_E, 1); + instruction_cost(2, 8); + break; + case 0xCC: + rset(R8_H, 1); + instruction_cost(2, 8); + break; + case 0xCD: + rset(R8_L, 1); + instruction_cost(2, 8); + break; + case 0xCE: + mset(rread16(R16_HL), 1); + instruction_cost(2, 16); + break; + case 0xCF: + rset(R8_A, 1); + instruction_cost(2, 8); + break; +#pragma region Prefix 0xD0 + case 0xD0: + rset(R8_B, 2); + instruction_cost(2, 8); + break; + case 0xD1: + rset(R8_C, 2); + instruction_cost(2, 8); + break; + case 0xD2: + rset(R8_D, 2); + instruction_cost(2, 8); + break; + case 0xD3: + rset(R8_E, 2); + instruction_cost(2, 8); + break; + case 0xD4: + rset(R8_H, 2); + instruction_cost(2, 8); + break; + case 0xD5: + rset(R8_L, 2); + instruction_cost(2, 8); + break; + case 0xD6: + mset(rread16(R16_HL), 2); + instruction_cost(2, 16); + break; + case 0xD7: + rset(R8_A, 2); + instruction_cost(2, 8); + break; + case 0xD8: + rset(R8_B, 3); + instruction_cost(2, 8); + break; + case 0xD9: + rset(R8_C, 3); + instruction_cost(2, 8); + break; + case 0xDA: + rset(R8_D, 3); + instruction_cost(2, 8); + break; + case 0xDB: + rset(R8_E, 3); + instruction_cost(2, 8); + break; + case 0xDC: + rset(R8_H, 3); + instruction_cost(2, 8); + break; + case 0xDD: + rset(R8_L, 3); + instruction_cost(2, 8); + break; + case 0xDE: + mset(rread16(R16_HL), 3); + instruction_cost(2, 16); + break; + case 0xDF: + rset(R8_A, 3); + instruction_cost(2, 8); + break; +#pragma region Prefix 0xE0 + case 0xE0: + rset(R8_B, 4); + instruction_cost(2, 8); + break; + case 0xE1: + rset(R8_C, 4); + instruction_cost(2, 8); + break; + case 0xE2: + rset(R8_D, 4); + instruction_cost(2, 8); + break; + case 0xE3: + rset(R8_E, 4); + instruction_cost(2, 8); + break; + case 0xE4: + rset(R8_H, 4); + instruction_cost(2, 8); + break; + case 0xE5: + rset(R8_L, 4); + instruction_cost(2, 8); + break; + case 0xE6: + mset(rread16(R16_HL), 4); + instruction_cost(2, 16); + break; + case 0xE7: + rset(R8_A, 4); + instruction_cost(2, 8); + break; + case 0xE8: + rset(R8_B, 5); + instruction_cost(2, 8); + break; + case 0xE9: + rset(R8_C, 5); + instruction_cost(2, 8); + break; + case 0xEA: + rset(R8_D, 5); + instruction_cost(2, 8); + break; + case 0xEB: + rset(R8_E, 5); + instruction_cost(2, 8); + break; + case 0xEC: + rset(R8_H, 5); + instruction_cost(2, 8); + break; + case 0xED: + rset(R8_L, 5); + instruction_cost(2, 8); + break; + case 0xEE: + mset(rread16(R16_HL), 5); + instruction_cost(2, 16); + break; + case 0xEF: + rset(R8_A, 5); + instruction_cost(2, 8); + break; +#pragma region Prefix 0xF0 + case 0xF0: + rset(R8_B, 6); + instruction_cost(2, 8); + break; + case 0xF1: + rset(R8_C, 6); + instruction_cost(2, 8); + break; + case 0xF2: + rset(R8_D, 6); + instruction_cost(2, 8); + break; + case 0xF3: + rset(R8_E, 6); + instruction_cost(2, 8); + break; + case 0xF4: + rset(R8_H, 6); + instruction_cost(2, 8); + break; + case 0xF5: + rset(R8_L, 6); + instruction_cost(2, 8); + break; + case 0xF6: + mset(rread16(R16_HL), 6); + instruction_cost(2, 16); + break; + case 0xF7: + rset(R8_A, 6); + instruction_cost(2, 8); + break; + case 0xF8: + rset(R8_B, 7); + instruction_cost(2, 8); + break; + case 0xF9: + rset(R8_C, 7); + instruction_cost(2, 8); + break; + case 0xFA: + rset(R8_D, 7); + instruction_cost(2, 8); + break; + case 0xFB: + rset(R8_E, 7); + instruction_cost(2, 8); + break; + case 0xFC: + rset(R8_H, 7); + instruction_cost(2, 8); + break; + case 0xFD: + rset(R8_L, 7); + instruction_cost(2, 8); + break; + case 0xFE: + mset(rread16(R16_HL), 7); + instruction_cost(2, 16); + break; + case 0xFF: + rset(R8_A, 7); + instruction_cost(2, 8); + break; + default: + error_handler(); + puts("From the prefixed instructions."); + exit(EXIT_FAILURE); + break; + } +} + +void cpu_cycle(void) { + interrupt_check(); + if (clock_queue == 0) { + if (!halted) { + if (log != NULL) { + fprintf( + log, + "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", + rread8(R8_A), rread8(R8_F), rread8(R8_B), rread8(R8_C), + rread8(R8_D), rread8(R8_E), rread8(R8_H), rread8(R8_L), + rread16(R16_SP), rread16(R16_PC), mread8(rread16(R16_PC)), + mread8(rread16(R16_PC) + 1), mread8(rread16(R16_PC) + 2), + mread8(rread16(R16_PC) + 3)); + } + switch (mread8(rread16(R16_PC))) { +#pragma region 0x00 + case 0x00: + instruction_cost(1, 4); + break; + case 0x01: + rwrite16(R16_BC, mread16(rread16(R16_PC) + 1)); + instruction_cost(3, 12); + break; + case 0x02: + mwrite8(rread16(R16_BC), rread8(R8_A)); + instruction_cost(1, 8); + break; + case 0x03: + rwrite16(R16_BC, rread16(R16_BC) + 1); + instruction_cost(1, 8); + break; + case 0x04: + inc(R8_B); + instruction_cost(1, 4); + break; + case 0x05: + dec(R8_B); + instruction_cost(1, 4); + break; + case 0x06: + rwrite8(R8_B, mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0x07: + rwrite8(R8_A, rlc(rread8(R8_A))); + rclear(R8_F, FLAG_Z); + instruction_cost(1, 4); + break; + case 0x08: + mwrite16(mread16(rread16(R16_PC) + 1), rread16(R16_SP)); + instruction_cost(3, 20); + break; + case 0x09: + add16(R16_HL, R16_BC); + instruction_cost(1, 8); + break; + case 0x0A: + rwrite8(R8_A, mread8(rread16(R16_BC))); + instruction_cost(1, 8); + break; + case 0x0B: + rwrite16(R16_BC, rread16(R16_BC) - 1); + instruction_cost(1, 8); + break; + case 0x0C: + inc(R8_C); + instruction_cost(1, 4); + break; + case 0x0D: + dec(R8_C); + instruction_cost(1, 4); + break; + case 0x0E: + rwrite8(R8_C, mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0x0F: + rwrite8(R8_A, rrc(rread8(R8_A))); + rclear(R8_F, FLAG_Z); + instruction_cost(1, 4); + break; +#pragma region 0x10 + case 0x10: + halted = 1; + instruction_cost(2, 4); + break; + case 0x11: + rwrite16(R16_DE, mread16(rread16(R16_PC) + 1)); + instruction_cost(3, 12); + break; + case 0x12: + mwrite8(rread16(R16_DE), rread8(R8_A)); + instruction_cost(1, 8); + break; + case 0x13: + rwrite16(R16_DE, rread16(R16_DE) + 1); + instruction_cost(1, 8); + break; + case 0x14: + inc(R8_D); + instruction_cost(1, 4); + break; + case 0x15: + dec(R8_D); + instruction_cost(1, 4); + break; + case 0x16: + rwrite8(R8_D, mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0x17: + rwrite8(R8_A, rl(rread8(R8_A))); + rclear(R8_F, FLAG_Z); + instruction_cost(1, 4); + break; + case 0x18: + rwrite16(R16_PC, rread16(R16_PC) + (s8)mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 12); + break; + case 0x19: + add16(R16_HL, R16_DE); + instruction_cost(1, 8); + break; + case 0x1A: + rwrite8(R8_A, mread8(rread16(R16_DE))); + instruction_cost(1, 8); + break; + case 0x1B: + rwrite16(R16_DE, rread16(R16_DE) - 1); + instruction_cost(1, 8); + break; + case 0x1C: + inc(R8_E); + instruction_cost(1, 4); + break; + case 0x1D: + dec(R8_E); + instruction_cost(1, 4); + break; + case 0x1E: + rwrite8(R8_E, mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0x1F: + rwrite8(R8_A, rr(rread8(R8_A))); + rclear(R8_F, FLAG_Z); + instruction_cost(1, 4); + break; +#pragma region 0x20 + case 0x20: + if (!rget(R8_F, FLAG_Z)) { + rwrite16(R16_PC, rread16(R16_PC) + (s8)mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 12); + } else { + instruction_cost(2, 8); + } + break; + case 0x21: + rwrite16(R16_HL, mread16(rread16(R16_PC) + 1)); + instruction_cost(3, 12); + break; + case 0x22: + mwrite8(rread16(R16_HL), rread8(R8_A)); + rwrite16(R16_HL, rread16(R16_HL) + 1); + instruction_cost(1, 8); + break; + case 0x23: + rwrite16(R16_HL, rread16(R16_HL) + 1); + instruction_cost(1, 8); + break; + case 0x24: + inc(R8_H); + instruction_cost(1, 4); + break; + case 0x25: + dec(R8_H); + instruction_cost(1, 4); + break; + case 0x26: + rwrite8(R8_H, mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0x27: + if (rget(R8_F, FLAG_N)) { + u8 adjustment = 0; + if (rget(R8_F, FLAG_H)) { + adjustment += 0x6; + } + if (rget(R8_F, FLAG_C)) { + adjustment += 0x60; + } else { + rclear(R8_F, FLAG_C); + } + rwrite8(R8_A, rread8(R8_A) - adjustment); + } else { + u8 adjustment = 0; + if (rget(R8_F, FLAG_H) || (rread8(R8_A) & 0xF) > 0x9) { + adjustment += 0x6; + } + if (rget(R8_F, FLAG_C) || rread8(R8_A) > 0x99) { + adjustment += 0x60; + rset(R8_F, FLAG_C); + } else { + rclear(R8_F, FLAG_C); + } + rwrite8(R8_A, rread8(R8_A) + adjustment); + } + zflag(rread8(R8_A)); + rclear(R8_F, FLAG_H); + instruction_cost(1, 4); + break; + case 0x28: + if (rget(R8_F, FLAG_Z)) { + rwrite16(R16_PC, rread16(R16_PC) + (s8)mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 12); + } else { + instruction_cost(2, 8); + } + break; + case 0x29: + add16(R16_HL, R16_HL); + instruction_cost(1, 8); + break; + case 0x2A: + rwrite8(R8_A, mread8(rread16(R16_HL))); + rwrite16(R16_HL, rread16(R16_HL) + 1); + instruction_cost(1, 8); + break; + case 0x2B: + rwrite16(R16_HL, rread16(R16_HL) - 1); + instruction_cost(1, 8); + break; + case 0x2C: + inc(R8_L); + instruction_cost(1, 4); + break; + case 0x2D: + dec(R8_L); + instruction_cost(1, 4); + break; + case 0x2E: + rwrite8(R8_L, mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0x2F: + rwrite8(R8_A, ~rread8(R8_A)); + rset(R8_F, FLAG_N); + rset(R8_F, FLAG_H); + instruction_cost(1, 4); + break; +#pragma region 0x30 + case 0x30: + if (!rget(R8_F, FLAG_C)) { + rwrite16(R16_PC, rread16(R16_PC) + (s8)mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 12); + } else { + instruction_cost(2, 8); + } + break; + case 0x31: + rwrite16(R16_SP, mread16(rread16(R16_PC) + 1)); + instruction_cost(3, 12); + break; + case 0x32: + mwrite8(rread16(R16_HL), rread8(R8_A)); + rwrite16(R16_HL, rread16(R16_HL) - 1); + instruction_cost(1, 8); + break; + case 0x33: + rwrite16(R16_SP, rread16(R16_SP) + 1); + instruction_cost(1, 8); + break; + case 0x34: + mwrite8(rread16(R16_HL), mread8(rread16(R16_HL)) + 1); + zflag(mread8(rread16(R16_HL))); + rclear(R8_F, FLAG_N); + hflag_add(mread8(rread16(R16_HL)), 1); + instruction_cost(1, 4); + break; + case 0x35: + mwrite8(rread16(R16_HL), mread8(rread16(R16_HL)) - 1); + zflag(mread8(rread16(R16_HL))); + rset(R8_F, FLAG_N); + hflag_sub(mread8(rread16(R16_HL)) + 1, mread8(rread16(R16_HL))); + instruction_cost(1, 4); + break; + case 0x36: + mwrite8(rread16(R16_HL), mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 12); + break; + case 0x37: + rset(R8_F, FLAG_C); + rclear(R8_F, FLAG_H); + rclear(R8_F, FLAG_N); + instruction_cost(1, 4); + break; + case 0x38: + if (rget(R8_F, FLAG_C)) { + rwrite16(R16_PC, rread16(R16_PC) + (s8)mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 12); + } else { + instruction_cost(2, 8); + } + break; + case 0x39: + add16(R16_HL, R16_SP); + instruction_cost(1, 8); + break; + case 0x3A: + rwrite8(R8_A, mread8(rread16(R16_HL))); + rwrite16(R16_HL, rread16(R16_HL) - 1); + instruction_cost(1, 8); + break; + case 0x3B: + rwrite16(R16_SP, rread16(R16_SP) - 1); + instruction_cost(1, 8); + break; + case 0x3C: + inc(R8_A); + instruction_cost(1, 4); + break; + case 0x3D: + dec(R8_A); + instruction_cost(1, 4); + break; + case 0x3E: + rwrite8(R8_A, mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0x3F: + rget(R8_F, FLAG_C) ? rclear(R8_F, FLAG_C) : rset(R8_F, FLAG_C); + rclear(R8_F, FLAG_N); + rclear(R8_F, FLAG_H); + instruction_cost(1, 4); + break; +#pragma region 0x40 + case 0x40: + rwrite8(R8_B, rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0x41: + rwrite8(R8_B, rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0x42: + rwrite8(R8_B, rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0x43: + rwrite8(R8_B, rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0x44: + rwrite8(R8_B, rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0x45: + rwrite8(R8_B, rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0x46: + rwrite8(R8_B, mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0x47: + rwrite8(R8_B, rread8(R8_A)); + instruction_cost(1, 4); + break; + case 0x48: + rwrite8(R8_C, rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0x49: + rwrite8(R8_C, rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0x4A: + rwrite8(R8_C, rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0x4B: + rwrite8(R8_C, rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0x4C: + rwrite8(R8_C, rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0x4D: + rwrite8(R8_C, rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0x4E: + rwrite8(R8_C, mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0x4F: + rwrite8(R8_C, rread8(R8_A)); + instruction_cost(1, 4); + break; +#pragma region 0x50 + case 0x50: + rwrite8(R8_D, rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0x51: + rwrite8(R8_D, rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0x52: + rwrite8(R8_D, rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0x53: + rwrite8(R8_D, rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0x54: + rwrite8(R8_D, rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0x55: + rwrite8(R8_D, rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0x56: + rwrite8(R8_D, mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0x57: + rwrite8(R8_D, rread8(R8_A)); + instruction_cost(1, 4); + break; + case 0x58: + rwrite8(R8_E, rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0x59: + rwrite8(R8_E, rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0x5A: + rwrite8(R8_E, rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0x5B: + rwrite8(R8_E, rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0x5C: + rwrite8(R8_E, rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0x5D: + rwrite8(R8_E, rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0x5E: + rwrite8(R8_E, mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0x5F: + rwrite8(R8_E, rread8(R8_A)); + instruction_cost(1, 4); + break; +#pragma region 0x60 + case 0x60: + rwrite8(R8_H, rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0x61: + rwrite8(R8_H, rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0x62: + rwrite8(R8_H, rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0x63: + rwrite8(R8_H, rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0x64: + rwrite8(R8_H, rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0x65: + rwrite8(R8_H, rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0x66: + rwrite8(R8_H, mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0x67: + rwrite8(R8_H, rread8(R8_A)); + instruction_cost(1, 4); + break; + case 0x68: + rwrite8(R8_L, rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0x69: + rwrite8(R8_L, rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0x6A: + rwrite8(R8_L, rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0x6B: + rwrite8(R8_L, rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0x6C: + rwrite8(R8_L, rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0x6D: + rwrite8(R8_L, rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0x6E: + rwrite8(R8_L, mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0x6F: + rwrite8(R8_L, rread8(R8_A)); + instruction_cost(1, 4); + break; +#pragma region 0x70 + case 0x70: + mwrite8(rread16(R16_HL), rread8(R8_B)); + instruction_cost(1, 8); + break; + case 0x71: + mwrite8(rread16(R16_HL), rread8(R8_C)); + instruction_cost(1, 8); + break; + case 0x72: + mwrite8(rread16(R16_HL), rread8(R8_D)); + instruction_cost(1, 8); + break; + case 0x73: + mwrite8(rread16(R16_HL), rread8(R8_E)); + instruction_cost(1, 8); + break; + case 0x74: + mwrite8(rread16(R16_HL), rread8(R8_H)); + instruction_cost(1, 8); + break; + case 0x75: + mwrite8(rread16(R16_HL), rread8(R8_L)); + instruction_cost(1, 8); + break; + case 0x76: + halted = 1; + instruction_cost(1, 4); + break; + case 0x77: + mwrite8(rread16(R16_HL), rread8(R8_A)); + instruction_cost(1, 8); + break; + case 0x78: + rwrite8(R8_A, rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0x79: + rwrite8(R8_A, rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0x7A: + rwrite8(R8_A, rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0x7B: + rwrite8(R8_A, rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0x7C: + rwrite8(R8_A, rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0x7D: + rwrite8(R8_A, rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0x7E: + rwrite8(R8_A, mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0x7F: + rwrite8(R8_A, rread8(R8_A)); + instruction_cost(1, 4); + break; +#pragma region 0x80 + case 0x80: + add(rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0x81: + add(rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0x82: + add(rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0x83: + add(rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0x84: + add(rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0x85: + add(rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0x86: + add(mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0x87: + add(rread8(R8_A)); + instruction_cost(1, 4); + break; + case 0x88: + adc(rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0x89: + adc(rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0x8A: + adc(rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0x8B: + adc(rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0x8C: + adc(rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0x8D: + adc(rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0x8E: + adc(mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0x8F: + adc(rread8(R8_A)); + instruction_cost(1, 4); + break; +#pragma region 0x90 + case 0x90: + sub(rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0x91: + sub(rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0x92: + sub(rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0x93: + sub(rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0x94: + sub(rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0x95: + sub(rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0x96: + sub(mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0x97: + sub(rread8(R8_A)); + instruction_cost(1, 4); + break; + case 0x98: + sbc(rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0x99: + sbc(rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0x9A: + sbc(rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0x9B: + sbc(rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0x9C: + sbc(rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0x9D: + sbc(rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0x9E: + sbc(mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0x9F: + sbc(rread8(R8_A)); + instruction_cost(1, 4); + break; +#pragma region 0xA0 + case 0xA0: + and(rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0xA1: + and(rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0xA2: + and(rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0xA3: + and(rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0xA4: + and(rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0xA5: + and(rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0xA6: + and(mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0xA7: + and(rread8(R8_A)); + instruction_cost(1, 4); + break; + case 0xA8: + xor(rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0xA9: + xor(rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0xAA: + xor(rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0xAB: + xor(rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0xAC: + xor(rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0xAD: + xor(rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0xAE: + xor(mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0xAF: + xor(rread8(R8_A)); + instruction_cost(1, 4); + break; +#pragma region 0xB0 + case 0xB0: + or (rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0xB1: + or (rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0xB2: + or (rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0xB3: + or (rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0xB4: + or (rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0xB5: + or (rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0xB6: + or (mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0xB7: + or (rread8(R8_A)); + instruction_cost(1, 4); + break; + case 0xB8: + cp(rread8(R8_B)); + instruction_cost(1, 4); + break; + case 0xB9: + cp(rread8(R8_C)); + instruction_cost(1, 4); + break; + case 0xBA: + cp(rread8(R8_D)); + instruction_cost(1, 4); + break; + case 0xBB: + cp(rread8(R8_E)); + instruction_cost(1, 4); + break; + case 0xBC: + cp(rread8(R8_H)); + instruction_cost(1, 4); + break; + case 0xBD: + cp(rread8(R8_L)); + instruction_cost(1, 4); + break; + case 0xBE: + cp(mread8(rread16(R16_HL))); + instruction_cost(1, 8); + break; + case 0xBF: + cp(rread8(R8_A)); + instruction_cost(1, 4); + break; +#pragma region 0xC0 + case 0xC0: + if (!rget(R8_F, FLAG_Z)) { + pop(R16_PC); + instruction_cost(0, 20); + } else { + instruction_cost(1, 8); + } + break; + case 0xC1: + pop(R16_BC); + instruction_cost(1, 12); + break; + case 0xC2: + if (!rget(R8_F, FLAG_Z)) { + rwrite16(R16_PC, mread16(rread16(R16_PC) + 1)); + instruction_cost(0, 16); + } else { + instruction_cost(3, 12); + } + break; + case 0xC3: + rwrite16(R16_PC, mread16(rread16(R16_PC) + 1)); + instruction_cost(0, 16); + break; + case 0xC4: + if (!rget(R8_F, FLAG_Z)) { + rwrite16(R16_PC, rread16(R16_PC) + 3); + push(R16_PC); + rwrite16(R16_PC, rread16(R16_PC) - 3); + rwrite16(R16_PC, mread16(rread16(R16_PC) + 1)); + instruction_cost(0, 24); + } else { + instruction_cost(3, 12); + } + break; + case 0xC5: + push(R16_BC); + instruction_cost(1, 16); + break; + case 0xC6: + add(mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0xC7: + rwrite16(R16_PC, rread16(R16_PC) + 1); + push(R16_PC); + rwrite16(R16_PC, rread16(R16_PC) - 1); + rwrite16(R16_PC, 0x00); + instruction_cost(0, 16); + break; + case 0xC8: + if (rget(R8_F, FLAG_Z)) { + pop(R16_PC); + instruction_cost(0, 20); + } else { + instruction_cost(1, 8); + } + break; + case 0xC9: + pop(R16_PC); + instruction_cost(0, 16); + break; + case 0xCA: + if (rget(R8_F, FLAG_Z)) { + rwrite16(R16_PC, mread16(rread16(R16_PC) + 1)); + instruction_cost(0, 16); + } else { + instruction_cost(3, 12); + } + break; + case 0xCB: + prefix_run(mread8(rread16(R16_PC) + 1)); + break; + case 0xCC: + if (rget(R8_F, FLAG_Z)) { + rwrite16(R16_PC, rread16(R16_PC) + 3); + push(R16_PC); + rwrite16(R16_PC, rread16(R16_PC) - 3); + rwrite16(R16_PC, mread16(rread16(R16_PC) + 1)); + instruction_cost(0, 24); + } else { + instruction_cost(3, 12); + } + break; + case 0xCD: + rwrite16(R16_PC, rread16(R16_PC) + 3); + push(R16_PC); + rwrite16(R16_PC, rread16(R16_PC) - 3); + rwrite16(R16_PC, mread16(rread16(R16_PC) + 1)); + instruction_cost(0, 24); + break; + case 0xCE: + adc(mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0xCF: + rwrite16(R16_PC, rread16(R16_PC) + 1); + push(R16_PC); + rwrite16(R16_PC, rread16(R16_PC) - 1); + rwrite16(R16_PC, 0x08); + instruction_cost(0, 16); + break; +#pragma region 0xD0 + case 0xD0: + if (!rget(R8_F, FLAG_C)) { + pop(R16_PC); + instruction_cost(0, 20); + } else { + instruction_cost(1, 8); + } + break; + case 0xD1: + pop(R16_DE); + instruction_cost(1, 12); + break; + case 0xD2: + if (!rget(R8_F, FLAG_C)) { + rwrite16(R16_PC, mread16(rread16(R16_PC) + 1)); + instruction_cost(0, 16); + } else { + instruction_cost(3, 12); + } + break; + case 0xD4: + if (!rget(R8_F, FLAG_C)) { + rwrite16(R16_PC, rread16(R16_PC) + 3); + push(R16_PC); + rwrite16(R16_PC, rread16(R16_PC) - 3); + rwrite16(R16_PC, mread16(rread16(R16_PC) + 1)); + instruction_cost(0, 24); + } else { + instruction_cost(3, 12); + } + break; + case 0xD5: + push(R16_DE); + instruction_cost(1, 16); + break; + case 0xD6: + sub(mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0xD7: + rwrite16(R16_PC, rread16(R16_PC) + 1); + push(R16_PC); + rwrite16(R16_PC, rread16(R16_PC) - 1); + rwrite16(R16_PC, 0x10); + instruction_cost(0, 16); + break; + case 0xD8: + if (rget(R8_F, FLAG_C)) { + pop(R16_PC); + instruction_cost(0, 20); + } else { + instruction_cost(1, 8); + } + break; + case 0xD9: + pop(R16_PC); + ime = 1; + instruction_cost(0, 16); + break; + case 0xDA: + if (rget(R8_F, FLAG_C)) { + rwrite16(R16_PC, mread16(rread16(R16_PC) + 1)); + instruction_cost(0, 16); + } else { + instruction_cost(3, 12); + } + break; + case 0xDC: + if (rget(R8_F, FLAG_C)) { + rwrite16(R16_PC, rread16(R16_PC) + 3); + push(R16_PC); + rwrite16(R16_PC, rread16(R16_PC) - 3); + rwrite16(R16_PC, mread16(rread16(R16_PC) + 1)); + instruction_cost(0, 24); + } else { + instruction_cost(3, 12); + } + break; + case 0xDE: + sbc(mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0xDF: + rwrite16(R16_PC, rread16(R16_PC) + 1); + push(R16_PC); + rwrite16(R16_PC, rread16(R16_PC) - 1); + rwrite16(R16_PC, 0x18); + instruction_cost(0, 16); + break; +#pragma region 0xE0 + case 0xE0: + mwrite8(0xFF00 + mread8(rread16(R16_PC) + 1), rread8(R8_A)); + instruction_cost(2, 12); + break; + case 0xE1: + pop(R16_HL); + instruction_cost(1, 12); + break; + case 0xE2: + mwrite8(0xFF00 + rread8(R8_C), rread8(R8_A)); + instruction_cost(1, 8); + break; + case 0xE5: + push(R16_HL); + instruction_cost(1, 16); + break; + case 0xE6: + and(mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0xE7: + rwrite16(R16_PC, rread16(R16_PC) + 1); + push(R16_PC); + rwrite16(R16_PC, rread16(R16_PC) - 1); + rwrite16(R16_PC, 0x20); + instruction_cost(0, 16); + break; + case 0xE8: + rwrite16(R16_SP, rread16(R16_SP) + (s8)mread8(rread16(R16_PC) + 1)); + rclear(R8_F, FLAG_Z); + rclear(R8_F, FLAG_N); + cflag_add((u8)(rread16(R16_SP) & 0xFF), mread8(rread16(R16_PC) + 1)); + hflag_add((u8)(rread16(R16_SP) & 0xFF), mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 16); + break; + case 0xE9: + rwrite16(R16_PC, rread16(R16_HL)); + instruction_cost(0, 4); + break; + case 0xEA: + mwrite8(mread16(rread16(R16_PC) + 1), rread8(R8_A)); + instruction_cost(3, 16); + break; + case 0xEE: + xor(mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0xEF: + rwrite16(R16_PC, rread16(R16_PC) + 1); + push(R16_PC); + rwrite16(R16_PC, rread16(R16_PC) - 1); + rwrite16(R16_PC, 0x28); + instruction_cost(0, 16); + break; +#pragma region 0xF0 + case 0xF0: + rwrite8(R8_A, mread8(0xFF00 + mread8(rread16(R16_PC) + 1))); + instruction_cost(2, 12); + break; + case 0xF1: + pop(R16_AF); + instruction_cost(1, 12); + break; + case 0xF2: + rwrite8(R8_A, mread8(0xFF00 + rread8(R8_C))); + instruction_cost(1, 8); + break; + case 0xF3: + ime = 0; + instruction_cost(1, 4); + break; + case 0xF5: + push(R16_AF); + instruction_cost(1, 16); + break; + case 0xF6: + or (mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0xF7: + rwrite16(R16_PC, rread16(R16_PC) + 1); + push(R16_PC); + rwrite16(R16_PC, rread16(R16_PC) - 1); + rwrite16(R16_PC, 0x30); + instruction_cost(0, 16); + break; + case 0xF8: + rwrite16(R16_HL, rread16(R16_SP) + (s8)mread8(rread16(R16_PC) + 1)); + rclear(R8_F, FLAG_Z); + rclear(R8_F, FLAG_N); + hflag_add(rread8(R8_L), mread8(rread16(R16_PC) + 1)); + cflag_add(rread8(R8_L), mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 12); + break; + case 0xF9: + rwrite16(R16_SP, rread16(R16_HL)); + instruction_cost(1, 8); + break; + case 0xFA: + rwrite8(R8_A, mread8(mread16(rread16(R16_PC) + 1))); + instruction_cost(3, 16); + break; + case 0xFB: + ime = 1; + instruction_cost(1, 4); + break; + case 0xFE: + cp(mread8(rread16(R16_PC) + 1)); + instruction_cost(2, 8); + break; + case 0xFF: + rwrite16(R16_PC, rread16(R16_PC) + 1); + push(R16_PC); + rwrite16(R16_PC, rread16(R16_PC) - 1); + rwrite16(R16_PC, 0x38); + instruction_cost(0, 16); + break; + default: + error_handler(); + exit(EXIT_FAILURE); + break; + } + } + } + if (clock_queue > 0) { + clock_queue--; + } +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..7975f6e --- /dev/null +++ b/src/main.c @@ -0,0 +1,45 @@ +#include "pgb.h" +#include +#include +#include +#include + +#define GB_RATE 59.73 +#define CLOCKS_PER_FRAME 70224 +#define TIME_TO_SLEEP (CLOCKS_PER_SEC / GB_RATE) + +int main(const int argc, const char **argv) { + create_window(); + if (argc > 1) { + const char *gamerom = NULL; + const char *logfile = NULL; + int i; + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], "--file") == 0) { + gamerom = argv[++i]; + } else if (strcmp(argv[i], "--log") == 0) { + logfile = argv[++i]; + } + } + if (gamerom != NULL) { + cpu_init(gamerom, logfile); + int timer = time(NULL); + while (1) { + int i; + int start = clock(); + for (i = 0; i < CLOCKS_PER_FRAME; i++) { + cpu_cycle(); + ppu_dot(); + timer_cycle(); + } + while ((clock() - start) < TIME_TO_SLEEP) { + } + if (time(NULL) - timer >= 1) { + timer = time(NULL); + } + } + } + } + close_window(); + return EXIT_SUCCESS; +} diff --git a/src/pgb.h b/src/pgb.h new file mode 100644 index 0000000..1ce05be --- /dev/null +++ b/src/pgb.h @@ -0,0 +1,47 @@ +typedef unsigned char u8; +typedef signed char s8; +typedef unsigned short u16; +typedef signed char s16; + +typedef enum R8 { R8_A, R8_F, R8_B, R8_C, R8_D, R8_E, R8_H, R8_L } R8; +typedef enum R16 { R16_AF, R16_BC, R16_DE, R16_HL, R16_SP, R16_PC } R16; +typedef enum RFLAG { + NOFLAG0, + NOFLAG1, + NOFLAG2, + NOFLAG3, + FLAG_C, + FLAG_H, + FLAG_N, + FLAG_Z +} RFLAG; + +typedef enum INTERRUPT { + INT_VBLANK, + INT_LCD, + INT_TIMER, + INT_SERIAL, + INT_JOYPAD +} INTERRUPT; + +void lock_oam(void); +void unlock_oam(void); +void lock_vram(void); +void unlock_vram(void); + +u8 mread8_external(const u16 addr); +u16 mread16_external(const u16 addr); +void mwrite8_external(const u16 addr, const u8 val); +void mwrite16_external(const u16 addr, const u16 val); + +void cpu_init(const char *gamerom, const char *logfile); +void cpu_cycle(void); +void create_interrupt(const INTERRUPT interrupt); + +void ppu_dot(void); + +void timer_cycle(void); + +void create_window(void); +void draw_pixel(u8 x, u8 y, u8 color); +void close_window(void); diff --git a/src/ppu.c b/src/ppu.c new file mode 100644 index 0000000..2f6967f --- /dev/null +++ b/src/ppu.c @@ -0,0 +1,67 @@ +#include "pgb.h" +#include +#include + +static u16 dot; +static u8 scanline; +static u8 x; +static u8 delay; + +#define get_lcdc(bit) ((mread8_external(0xFF40) >> bit) & 1) + +void fetch_bg_pixel(u8 display_x, u8 display_y) { + u8 internal_x = mread8_external(0xFF43) + display_x; + u8 internal_y = mread8_external(0xFF42) + display_y; + u16 tile_addr_offset = + mread8_external((get_lcdc(3) ? 0x9C00 : 0x9800) + (u16)(internal_x / 8) + + ((u16)(internal_y / 8)) * 32) * + 16; + u16 tile_addr = get_lcdc(4) ? (0x8000 + tile_addr_offset) + : (0x9000 + (s16)tile_addr_offset); + u8 tile_x = internal_x % 8; + u8 tile_y = internal_y % 8; + u16 tile_data = mread16_external(tile_addr + tile_y * 2); + if (tile_y < 3) { + u16 addr = 0x8000 + (mread8_external((get_lcdc(3) ? 0x9C00 : 0x9800) + + (u16)internal_x / 8 + + (u16)((internal_y + 3) / 8) * 32) * + 16); + if (mread16_external(addr + (tile_y + 3) * 2) != tile_data && get_lcdc(4)) { + printf("(u16)(%d) (u16)(%d)\n", (internal_y) / 8, (internal_y + 3) / 8); + } + } + u8 color = (tile_data >> abs(tile_x - 7)) & 1; + color += ((tile_data >> abs(tile_x - 15)) & 1) * 2; + draw_pixel(display_x, display_y, color); +} + +void ppu_dot(void) { + if (dot == 0) { + lock_oam(); + x = 0; + delay = 0; + } else if (dot == 456) { + dot = -1; + scanline++; + if (scanline == 144) { + create_interrupt(INT_VBLANK); + } + if (scanline == 154) { + scanline = 0; + } + } + if (dot >= 80 && x < 160) { + if (delay == 0) { + unlock_vram(); + fetch_bg_pixel(x, scanline); + x++; + lock_vram(); + } else { + delay--; + } + } else if (x == 160) { + unlock_vram(); + unlock_oam(); + } + dot++; +} diff --git a/src/timer.c b/src/timer.c new file mode 100644 index 0000000..fda1ae0 --- /dev/null +++ b/src/timer.c @@ -0,0 +1,37 @@ +#include "pgb.h" + +static u16 counter = 0; + +static u16 get_cycles_to_count(void) { + switch (mread8_external(0xFF07) & 3) { + case 0: + return 1024; + break; + case 1: + return 16; + break; + case 2: + return 64; + break; + case 3: + return 256; + break; + } + return 1024; +} + +void timer_cycle(void) { + if (mread8_external(0xFF07) & 4) { + if (counter < get_cycles_to_count()) { + counter++; + } else { + counter = 0; + if (mread8_external(0xFF05) == 0xFF) { + mwrite8_external(0xFF05, mread8_external(0xFF06)); + create_interrupt(INT_TIMER); + } else { + mwrite8_external(0xFF05, mread8_external(0xFF05) + 1); + } + } + } +} diff --git a/src/window.c b/src/window.c new file mode 100644 index 0000000..18c0481 --- /dev/null +++ b/src/window.c @@ -0,0 +1,53 @@ +#include "pgb.h" +#include +#include +#include +#include + +static Display *display; +static Window window; +static int screen; + +void create_window(void) { + display = XOpenDisplay(NULL); + if (display == NULL) { + fprintf(stderr, "Cannot open display\n"); + exit(1); + } + + screen = DefaultScreen(display); + + window = XCreateSimpleWindow(display, RootWindow(display, screen), 10, 10, + 320, 288, 1, BlackPixel(display, screen), + WhitePixel(display, screen)); + + XStoreName(display, window, "PGB"); + XSelectInput(display, window, ExposureMask | KeyPressMask); + XMapWindow(display, window); +} + +void draw_pixel(u8 x, u8 y, u8 color) { + unsigned long real_color = 0xFFFFFFFF; + switch (color) { + case 0: + real_color = 0xFFFFFFFF; + break; + case 1: + real_color = 0xAAAAAAAA; + break; + case 2: + real_color = 0x55555555; + break; + case 3: + real_color = 0x00000000; + break; + } + XSetForeground(display, DefaultGC(display, screen), real_color); + XFillRectangle(display, window, DefaultGC(display, screen), x * 2, y * 2, 2, + 2); +} + +void close_window(void) { + XDestroyWindow(display, window); + XCloseDisplay(display); +}