From 7649714c7383ef471a1265229495af24da0811f1 Mon Sep 17 00:00:00 2001 From: slack Date: Fri, 13 Jul 2007 18:33:48 +0000 Subject: [PATCH] - Mas opcodes - Clase GBMemory para controlar todos los accesos a memoria y redirigirlos a memoria/IO/MBC - Clase abstracta MBC: De ahi deben heredar NoMBC, MBC1, MBC2, etc - Renombrado gbrom a GBRom git-svn-id: http://slack.codemaniacs.com/wenboi@4 0666ae3d-8926-0410-aeff-ae84559ff337 --- GBMemory.h | 31 +++++ gbrom.h => GBRom.h | 0 MBC.h | 12 ++ gbcore.cc | 333 ++++++++++++++++++++++++++++++++++++++++++--- opcodes.h | 151 +++++++++++++++++++- 5 files changed, 503 insertions(+), 24 deletions(-) create mode 100644 GBMemory.h rename gbrom.h => GBRom.h (100%) create mode 100644 MBC.h diff --git a/GBMemory.h b/GBMemory.h new file mode 100644 index 0000000..8119c47 --- /dev/null +++ b/GBMemory.h @@ -0,0 +1,31 @@ +#ifndef GBMEMORY_H +#define GBMEMORY_H + +#include "sized_types.h" +#include "MBC.h" + +class GBMemory +{ + MBC *mbc; + // 0000-3FFF: ROM Bank 0 (in cart) + // 4000-7FFF: Switchable ROM Bank (in cart) + u8 VRAM[8192]; // 8000-9FFF: Video RAM + // A000-BFFF: External RAM (in cart, switchable) + u8 WRAM0[4096]; // C000-CFFF: Work RAM Bank 0 + u8 WRAM1[4096]; // D000-DFFF: Work RAM Bank 1 (TODO: In GBC mode switchable bank 1-7) + // E000-FDFF: ECHO: Same as C000-DDFF + u8 OAM[160]; // FE00-FE9F: Sprite Attribute Table + u8 HRAM[126]; // FF80-FFFE: High RAM + + public: + GBMemory(): mbc(0) {} + void init(MBC *mbc) { this->mbc = mbc; } + + + int& operator[](int index)=0; + int operator[](int index) const=0; + +}; + + +#endif // GBMEMORY_H diff --git a/gbrom.h b/GBRom.h similarity index 100% rename from gbrom.h rename to GBRom.h diff --git a/MBC.h b/MBC.h new file mode 100644 index 0000000..0d4baba --- /dev/null +++ b/MBC.h @@ -0,0 +1,12 @@ +#ifndef MBC_H +#define MBC_H + +#include "sized_types.h" + +class MBC +{ + virtual u8 read_byte() const=0; + virtual void write_byte(u8)=0; +}; + +#endif // MBC_H diff --git a/gbcore.cc b/gbcore.cc index 252a63a..a54fc8e 100644 --- a/gbcore.cc +++ b/gbcore.cc @@ -1,13 +1,44 @@ #include "gbcore.h" #include "sized_types.h" -#include "gbrom.h" +#include "GBRom.h" +#include "GBMemory.h" +#include "MBC.h" #include #include +class MBC +{ + virtual u8 read_byte() const=0; + virtual void write_byte(u8)=0; +}; + +class GBMemory +{ + MBC *mbc; + // 0000-3FFF: ROM Bank 0 (in cart) + // 4000-7FFF: Switchable ROM Bank (in cart) + u8 VRAM[8192]; // 8000-9FFF: Video RAM + // A000-BFFF: External RAM (in cart, switchable) + u8 WRAM0[4096]; // C000-CFFF: Work RAM Bank 0 + u8 WRAM1[4096]; // D000-DFFF: Work RAM Bank 1 (TODO: In GBC mode switchable bank 1-7) + // E000-FDFF: ECHO: Same as C000-DDFF + u8 OAM[160]; // FE00-FE9F: Sprite Attribute Table + u8 HRAM[126]; // FF80-FFFE: High RAM + + public: + GBMemory(): mbc(0) {} + void init(MBC *mbc) { this->mbc = mbc; } + + + int& operator[](int index)=0; + int operator[](int index) const=0; + +}; + class GameBoy { - u8 memory[65536]; + GBMemory memory; GBRom *rom; enum flags_enum @@ -47,6 +78,9 @@ class GameBoy } __attribute__((packed)) regs; + u8 IME; // Interrupt master enable flag + u8 HALT; // Is the CPU halted waiting for an interrupt? + void set_flag(const u8 f) { regs.flags |= f; } void reset_flag(const u8 f) { regs.flags &= (~f); } bool check_flag(const u8 f) { return (regs.flags & f != 0); } @@ -61,7 +95,7 @@ class GameBoy }; GameBoy::GameBoy(std::string rom_name): - rom(0), regs() + rom(0), regs(), IME(1), HALT(0) { rom = read_gbrom(rom_name); reset(); @@ -89,7 +123,7 @@ void GameBoy::run_cycle() switch(opcode) { // LD n, nn - for_each_register(0x76, 0x06, 0x0E, 0x16, 0x1E, 0x26, 0x2E, LD_reg_nn) + for_each_register(0x3E, 0x06, 0x0E, 0x16, 0x1E, 0x26, 0x2E, LD_reg_nn) // LD r1,r2 for_each_register(0x7F, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, LD_A_reg) @@ -202,8 +236,7 @@ void GameBoy::run_cycle() int res = regs.SP + offset; // TODO: Verificar si los flags van asi - if (res > 0xFFFF) set_flag(CARRY_FLAG); - else reset_flag(CARRY_FLAG); + set_flag_if (res > 0xFFFF, CARRY_FLAG); // TODO: hacer lo apropiado con el half-carry flag reset_flag(ADD_SUB_FLAG); @@ -243,9 +276,9 @@ void GameBoy::run_cycle() regs.A = static_cast(res); reset_flag(ADD_SUB_FLAG); - if (res > 0xFF) set_flag(CARRY_FLAG); - if (regs.A == 0) set_flag(ZERO_FLAG); - if (half_res > 0x0F) set_flag(HALF_CARRY_FLAG); + set_flag_if (res > 0xFF, CARRY_FLAG); + set_flag_if (regs.A == 0, ZERO_FLAG); + set_flag_if (half_res > 0x0F, HALF_CARRY_FLAG); break; } case 0xC6: {//ADD A, # @@ -255,9 +288,9 @@ void GameBoy::run_cycle() regs.A = static_cast(res); reset_flag(ADD_SUB_FLAG); - if (res > 0xFF) set_flag(CARRY_FLAG); - if (regs.A == 0) set_flag(ZERO_FLAG); - if (half_res > 0x0F) set_flag(HALF_CARRY_FLAG); + set_flag_if (res > 0xFF, CARRY_FLAG); + set_flag_if (regs.A == 0, ZERO_FLAG); + set_flag_if (half_res > 0x0F, HALF_CARRY_FLAG); break; } @@ -271,9 +304,9 @@ void GameBoy::run_cycle() regs.A = static_cast(res); reset_flag(ADD_SUB_FLAG); - if (res > 0xFF) set_flag(CARRY_FLAG); - if (regs.A == 0) set_flag(ZERO_FLAG); - if (half_res > 0x0F) set_flag(HALF_CARRY_FLAG); + set_flag_if (res > 0xFF, CARRY_FLAG); + set_flag_if (regs.A == 0, ZERO_FLAG); + set_flag_if (half_res > 0x0F, HALF_CARRY_FLAG); break; } case 0xCE: {//ADC A, # @@ -284,12 +317,276 @@ void GameBoy::run_cycle() regs.A = static_cast(res); reset_flag(ADD_SUB_FLAG); - if (res > 0xFF) set_flag(CARRY_FLAG); - if (regs.A == 0) set_flag(ZERO_FLAG); - if (half_res > 0x0F) set_flag(HALF_CARRY_FLAG); + set_flag_if (res > 0xFF, CARRY_FLAG); + set_flag_if (regs.A == 0, ZERO_FLAG); + set_flag_if (half_res > 0x0F, HALF_CARRY_FLAG); + break; + } + + // SUB n + for_each_register(0x97, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, SUB_reg) + + case 0x96: {//SUB (HL) + int res = regs.A - memory[regs.HL]; + int half_res = (regs.A & 0x0F) - (memory[regs.HL] & 0x0F); + regs.A = static_cast(res); + + set_flag(ADD_SUB_FLAG); + set_flag_if (res < 0, CARRY_FLAG); + set_flag_if (res == 0, ZERO_FLAG); + set_flag_if (half_res < 0, HALF_CARRY_FLAG); + break; + } + + case 0xD6: {//SUB # + int inm = memory[regs.PC++]; + int res = regs.A - inm; + int half_res = (regs.A & 0x0F) - (inm & 0x0F); + regs.A = static_cast(res); + + set_flag(ADD_SUB_FLAG); + set_flag_if (res < 0, CARRY_FLAG); + set_flag_if (res == 0, ZERO_FLAG); + set_flag_if (half_res < 0, HALF_CARRY_FLAG); + break; + } + + // SBC n + for_each_register(0x9F, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, SBC_reg) + + case 0x9E: {//SBC (HL) + int carry = (check_flag(CARRY_FLAG)? 1 : 0); + int res = regs.A - memory[regs.HL] - carry; + int half_res = (regs.A & 0x0F) - (memory[regs.HL] & 0x0F) - carry; + regs.A = static_cast(res); + + set_flag(ADD_SUB_FLAG); + set_flag_if (res < 0, CARRY_FLAG); + set_flag_if (res == 0, ZERO_FLAG); + set_flag_if (half_res < 0, HALF_CARRY_FLAG); + break; + } + + // There is no SBC inm + + // AND n + for_each_register(0xA7, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, AND_reg) + + case 0xA6: //AND (HL) + regs.A &= memory[regs.HL]; + if (regs.A == 0) set_flag(ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + set_flag(HALF_CARRY_FLAG); + reset_flag(CARRY_FLAG); + break; + + case 0xE6: //AND inm + regs.A &= memory[regs.PC++]; + if (regs.A == 0) set_flag(ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + set_flag(HALF_CARRY_FLAG); + reset_flag(CARRY_FLAG); + break; + + // OR n + for_each_register(0xB7, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, OR_reg) + + case 0xB6: //OR (HL) + regs.A |= memory[regs.HL]; + if (regs.A == 0) set_flag(ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + reset_flag(HALF_CARRY_FLAG); + reset_flag(CARRY_FLAG); + break; + + case 0xF6: //OR inm + regs.A |= memory[regs.PC++]; + if (regs.A == 0) set_flag(ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + reset_flag(HALF_CARRY_FLAG); + reset_flag(CARRY_FLAG); + break; + + // XOR n + for_each_register(0xAF, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, XOR_reg) + + case 0xAE: //XOR (HL) + regs.A ^= memory[regs.HL]; + if (regs.A == 0) set_flag(ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + reset_flag(HALF_CARRY_FLAG); + reset_flag(CARRY_FLAG); + break; + + case 0xEE: //XOR inm + regs.A ^= memory[regs.PC++]; + if (regs.A == 0) set_flag(ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + reset_flag(HALF_CARRY_FLAG); + reset_flag(CARRY_FLAG); + break; + + // CP n + for_each_register(0xBF, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, CP_reg) + + case 0xBE: {//SUB (HL) + int res = regs.A - memory[regs.HL]; + int half_res = (regs.A & 0x0F) - (memory[regs.HL] & 0x0F); + regs.A = static_cast(res); + + set_flag(ADD_SUB_FLAG); + set_flag_if (res < 0, CARRY_FLAG); + set_flag_if (res == 0, ZERO_FLAG); + set_flag_if (half_res < 0, HALF_CARRY_FLAG); + break; + } + + case 0xFE: {//SUB # + int inm = memory[regs.PC++]; + int res = regs.A - inm; + int half_res = (regs.A & 0x0F) - (inm & 0x0F); + regs.A = static_cast(res); + + set_flag(ADD_SUB_FLAG); + set_flag_if (res < 0, CARRY_FLAG); + set_flag_if (res == 0, ZERO_FLAG); + set_flag_if (half_res < 0, HALF_CARRY_FLAG); + break; + } + + // INC n + for_each_register(0x3C, 0x04, 0x0C, 0x14, 0x1C, 0x24, 0x2C, INC_reg) + + case 0x34: {//INC (HL) + int half_res = (memory[regs.HL] & 0x0F) + 1; + ++memory[regs.HL]; + reset_flag(ADD_SUB_FLAG); + set_flag_if (memory[regs.HL] == 0, ZERO_FLAG); + set_flag_if (half_res > 0x0F, HALF_CARRY_FLAG); + break; + } + + // DEC n + for_each_register(0x3D, 0x05, 0x0D, 0x15, 0x1D, 0x25, 0x2D, DEC_reg) + + case 0x35: {//DEC (HL) + int half_res = (memory[regs.HL] & 0x0F) - 1; + --memory[regs.HL]; + set_flag(ADD_SUB_FLAG); + set_flag_if (memory[regs.HL] == 0, ZERO_FLAG); + set_flag_if (half_res < 0, HALF_CARRY_FLAG); + break; + } + + // 16-bit ALU + // ADD HL, n + for_each_register16(0x09, 0x19, 0x29, 0x39, ADD_HL_reg16) + + // ADD SP, # + case 0xE8: { + // FIXME: No se que hacer con el half carry, en 4 o en 11? + int n = *(reinterpret_cast(memory+regs.PC++)); + int res = regs.SP + n; + regs.SP = static_cast(res); + reset_flag(ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + set_flag_if(res > 0xFFFF, CARRY_FLAG); break; + } + + // INC nn + for_each_register16(0x03, 0x13, 0x23, 0x33, INC_reg16) + + // DEC nn + for_each_register16(0x0B, 0x1B, 0x2B, 0x3B, DEC_reg16) + + // Miscellaneous instructions + // SWAP n + case 0xCB: { + int sub_opcode = memory[regs.PC++]; + switch(sub_opcode) + { + for_each_register(0x37, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, SWAP_reg) + + // SWAP (HL) + case 0x36: { + u8 tmp = memory[regs.HL]; + tmp = ((tmp & 0x0F) << 4) | ((tmp & 0xF0)>>4); + memory[regs.HL] = tmp; + + set_flag_if(tmp==0, ZERO_FLAG); + reset_flag(CARRY_FLAG); + reset_flag(HALF_CARRY_FLAG); + reset_flag(ADD_SUB_FLAG); + break; + } + } + break; + } + + // DAA http://www.worldofspectrum.org/faq/reference/z80reference.htm#DAA + case 0x27: { + u8 corr_factor = 0; + if (regs.A > 0x99 || check_flag(CARRY_FLAG)) { + corr_factor = 0x60; + set_flag(CARRY_FLAG); + } else { + reset_flag(CARRY_FLAG); + } + + if (regs.A & 0x0F > 9 || check_flag(HALF_CARRY_FLAG)) { + corr_factor |= 0x06; + } + + if (!check_flag(ADD_SUB_FLAG)) { + regs.A += corr_factor; + } else { + regs.A -= corr_factor; } + set_flag_if(regs.A==0, ZERO_FLAG); + reset_flag(HALF_CARRY_FLAG); // GBCPUman.pdf contradicts previous reference :P + break; + } + + // CPL + case 0x2F: + regs.A = ~regs.A; + set_flag(HALF_CARRY_FLAG); + set_flag(ADD_SUB_FLAG); + break; + + // CCF + case 0x3F: + if (check_flag(CARRY_FLAG)) + reset_flag(CARRY_FLAG); + else + set_flag(CARRY_FLAG); + + reset_flag(HALF_CARRY_FLAG); + reset_flag(ADD_SUB_FLAG); + break; + + // SCF + case 0x37: + set_flag(CARRY_FLAG); + reset_flag(HALF_CARRY_FLAG); + reset_flag(ADD_SUB_FLAG); + break; + + // NOP + case 0x00: + break; + + // HALT + case 0x76: + HALT = true; + break; + + + + + diff --git a/opcodes.h b/opcodes.h index 6b13c41..370afc4 100644 --- a/opcodes.h +++ b/opcodes.h @@ -1,3 +1,11 @@ +#define set_flag_if(cond, flag) \ + if (cond) set_flag(flag); \ + else reset_flag(flag) + +#define reset_flag_if(cond, flag) \ + if (cond) reset_flag(flag); \ + else set_flag(flag) + #define for_each_register(opA, opB, opC, opD, opE, opH, opL, macro) \ macro(opA, A) \ macro(opB, B) \ @@ -7,6 +15,12 @@ macro(opH, H) \ macro(opL, L) +#define for_each_register16(opBC, opDE, opHL, opSP, macro) \ + macro(opBC, BC) \ + macro(opDE, DE) \ + macro(opHL, HL) \ + macro(opSP, SP) + #define LD_reg_nn(opcode, reg) \ case opcode: \ regs.reg = memory[regs.PC++]; \ @@ -58,9 +72,9 @@ regs.A = static_cast(res); \ \ reset_flag(ADD_SUB_FLAG); \ - if (res > 0xFF) set_flag(CARRY_FLAG); \ - if (regs.A == 0) set_flag(ZERO_FLAG); \ - if (half_res > 0x0F) set_flag(HALF_CARRY_FLAG); \ + set_flag_if (res > 0xFF, CARRY_FLAG); \ + set_flag_if (regs.A == 0, ZERO_FLAG); \ + set_flag_if (half_res > 0x0F, HALF_CARRY_FLAG); \ break; \ } @@ -72,10 +86,135 @@ regs.A = static_cast(res); \ \ reset_flag(ADD_SUB_FLAG); \ - if (res > 0xFF) set_flag(CARRY_FLAG); \ - if (regs.A == 0) set_flag(ZERO_FLAG); \ - if (half_res > 0x0F) set_flag(HALF_CARRY_FLAG); \ + set_flag_if (res > 0xFF, CARRY_FLAG); \ + set_flag_if (regs.A == 0, ZERO_FLAG); \ + set_flag_if (half_res > 0x0F, HALF_CARRY_FLAG); \ + break; \ + } + +#define SUB_reg(opcode, reg) \ + case opcode: { \ + int res = regs.A - regs.reg; \ + int half_res = (regs.A & 0x0F) - (regs.reg & 0x0F); \ + regs.A = static_cast(res); \ + set_flag(ADD_SUB_FLAG); \ + set_flag_if (res < 0, CARRY_FLAG); \ + set_flag_if (res == 0, ZERO_FLAG); \ + set_flag_if (half_res < 0, HALF_CARRY_FLAG); \ break; \ } +#define SBC_reg(opcode, reg) \ + case opcode: { \ + int carry = (check_flag(CARRY_FLAG)? 1 : 0); \ + int res = regs.A - regs.reg - carry; \ + int half_res = (regs.A & 0x0F) - (regs.reg & 0x0F) - carry; \ + regs.A = static_cast(res); \ + set_flag(ADD_SUB_FLAG); \ + set_flag_if (res < 0, CARRY_FLAG); \ + set_flag_if (res == 0, ZERO_FLAG); \ + set_flag_if (half_res < 0, HALF_CARRY_FLAG); \ + break; \ + } + +#define AND_reg(opcode, reg) \ + case opcode: { \ + regs.A &= regs.reg; \ + if (regs.A == 0) set_flag(ZERO_FLAG); \ + reset_flag(ADD_SUB_FLAG); \ + set_flag(HALF_CARRY_FLAG); \ + reset_flag(CARRY_FLAG); \ + break; \ + } + +#define OR_reg(opcode, reg) \ + case opcode: { \ + regs.A |= regs.reg; \ + if (regs.A == 0) set_flag(ZERO_FLAG); \ + reset_flag(ADD_SUB_FLAG); \ + reset_flag(HALF_CARRY_FLAG); \ + reset_flag(CARRY_FLAG); \ + break; \ + } + +#define XOR_reg(opcode, reg) \ + case opcode: { \ + regs.A ^= regs.reg; \ + if (regs.A == 0) set_flag(ZERO_FLAG); \ + reset_flag(ADD_SUB_FLAG); \ + reset_flag(HALF_CARRY_FLAG); \ + reset_flag(CARRY_FLAG); \ + break; \ + } + +#define CP_reg(opcode, reg) \ + case opcode: { \ + int res = regs.A - regs.reg; \ + int half_res = (regs.A & 0x0F) - (regs.reg & 0x0F); \ + set_flag(ADD_SUB_FLAG); \ + set_flag_if (res < 0, CARRY_FLAG); \ + set_flag_if (res == 0, ZERO_FLAG); \ + set_flag_if (half_res < 0, HALF_CARRY_FLAG); \ + break; \ + } + +#define INC_reg(opcode, reg) \ + case opcode: {\ + int half_res = (regs.reg & 0x0F) + 1; \ + ++regs.reg; \ + reset_flag(ADD_SUB_FLAG); \ + set_flag_if (regs.reg == 0, ZERO_FLAG); \ + set_flag_if (half_res > 0x0F, HALF_CARRY_FLAG); \ + break; \ + } + +#define DEC_reg(opcode, reg) \ + case opcode: {\ + int half_res = (regs.reg & 0x0F) - 1; \ + --regs.reg; \ + set_flag(ADD_SUB_FLAG); \ + set_flag_if (regs.reg == 0, ZERO_FLAG); \ + set_flag_if (half_res < 0, HALF_CARRY_FLAG); \ + break; \ + } + + +#define ADD_HL_reg16(opcode, reg16) \ + case opcode: {\ + int res = regs.HL + regs.reg16; \ + int half_res = (regs.HL & 0xFFF) + (regs.reg16 & 0xFFF); \ + regs.HL = static_cast(res); \ + reset_flag(ADD_SUB_FLAG); \ + set_flag_if (res == 0, ZERO_FLAG); \ + set_flag_if (half_res > 0xFFF, HALF_CARRY_FLAG); \ + set_flag_if (res > 0xFFFF, CARRY_FLAG); \ + break; \ + } + +#define INC_reg16(opcode, reg16) \ + case opcode: \ + ++regs.reg16; \ + break; + +#define DEC_reg16(opcode, reg16) \ + case opcode: \ + --regs.reg16; \ + break; + +#define SWAP_reg(opcode, reg) \ + case opcode: \ + regs.reg = ((regs.reg & 0x0F)<<4) | ((regs.reg & 0xF0)>> 4); \ + set_flag_if (regs.reg == 0, ZERO_FLAG); \ + reset_flag(CARRY_FLAG); \ + reset_flag(HALF_CARRY_FLAG); \ + reset_flag(ADD_SUB_FLAG); \ + break; + + + + + + + + -- 2.34.1