From 64676529ba1919ac9996aa90344a4fa9c31969e3 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Wed, 13 Jan 2016 23:02:50 -0800 Subject: [PATCH] LR35902, GB: Start work on GB core --- CMakeLists.txt | 10 ++ src/gb/gb.c | 136 +++++++++++++++++++++ src/gb/gb.h | 69 +++++++++++ src/gb/memory.c | 133 +++++++++++++++++++++ src/gb/memory.h | 77 ++++++++++++ src/lr35902/isa-lr35902.c | 227 ++++++++++++++++++++++++++++++++++++ src/lr35902/isa-lr35902.h | 17 +++ src/lr35902/lr35902.c | 116 ++++++++++++++++++ src/lr35902/lr35902.h | 149 +++++++++++++++++++++++ src/platform/test/gb-main.c | 26 +++++ 10 files changed, 960 insertions(+) create mode 100644 src/gb/gb.c create mode 100644 src/gb/gb.h create mode 100644 src/gb/memory.c create mode 100644 src/gb/memory.h create mode 100644 src/lr35902/isa-lr35902.c create mode 100644 src/lr35902/isa-lr35902.h create mode 100644 src/lr35902/lr35902.c create mode 100644 src/lr35902/lr35902.h create mode 100644 src/platform/test/gb-main.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 7dc322dd6..a591a693c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,9 @@ set(BUILD_GLES2 OFF CACHE STRING "Build with OpenGL|ES 2") set(USE_EPOXY ON CACHE STRING "Build with libepoxy") set(DISABLE_DEPS OFF CACHE BOOL "Build without dependencies") file(GLOB ARM_SRC ${CMAKE_SOURCE_DIR}/src/arm/*.c) +file(GLOB LR35902_SRC ${CMAKE_SOURCE_DIR}/src/lr35902/*.c) file(GLOB GBA_SRC ${CMAKE_SOURCE_DIR}/src/gba/*.c) +file(GLOB GB_SRC ${CMAKE_SOURCE_DIR}/src/gb/*.c) file(GLOB GBA_CHEATS_SRC ${CMAKE_SOURCE_DIR}/src/gba/cheats/*.c) file(GLOB GBA_RR_SRC ${CMAKE_SOURCE_DIR}/src/gba/rr/*.c) file(GLOB GBA_SV_SRC ${CMAKE_SOURCE_DIR}/src/gba/supervisor/*.c) @@ -45,8 +47,10 @@ list(APPEND GBA_SV_SRC ${CMAKE_SOURCE_DIR}/src/platform/commandline.c) set(CORE_VFS_SRC ${CMAKE_SOURCE_DIR}/src/util/vfs/vfs-mem.c) set(VFS_SRC) source_group("ARM core" FILES ${ARM_SRC}) +source_group("LR35902 core" FILES ${LR35902_SRC}) source_group("GBA board" FILES ${GBA_SRC} ${RENDERER_SRC} ${SIO_SRC}) source_group("GBA extra" FILES ${GBA_CHEATS_SRC} ${GBA_CTX_SRC} ${GBA_SV_SRC} ${GBA_RR_SRC}) +source_group("GB board" FILES ${GBA_SRC}) source_group("Utilities" FILES ${UTIL_SRC}) include_directories(${CMAKE_SOURCE_DIR}/src/arm) include_directories(${CMAKE_SOURCE_DIR}/src) @@ -492,7 +496,9 @@ endif() # Binaries set(CORE_SRC ${ARM_SRC} + ${LR35902_SRC} ${GBA_SRC} + ${GB_SRC} ${GBA_CHEATS_SRC} ${GBA_CTX_SRC} ${DEBUGGER_SRC} @@ -621,6 +627,10 @@ if(BUILD_TEST) target_link_libraries(${BINARY_NAME}-fuzz ${BINARY_NAME}) set_target_properties(${BINARY_NAME}-fuzz PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}") install(TARGETS ${BINARY_NAME}-fuzz DESTINATION bin COMPONENT ${BINARY_NAME}-test) + + add_executable(${BINARY_NAME}-gb ${CMAKE_SOURCE_DIR}/src/platform/test/gb-main.c) + target_link_libraries(${BINARY_NAME}-gb ${BINARY_NAME}) + set_target_properties(${BINARY_NAME}-gb PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}") endif() # Packaging diff --git a/src/gb/gb.c b/src/gb/gb.c new file mode 100644 index 000000000..ced0a1f01 --- /dev/null +++ b/src/gb/gb.c @@ -0,0 +1,136 @@ +/* Copyright (c) 2013-2016 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "gb.h" + +#include "util/crc32.h" +#include "util/memory.h" +#include "util/math.h" +#include "util/patch.h" +#include "util/vfs.h" + +const uint32_t DMG_LR35902_FREQUENCY = 0x400000; +const uint32_t CGB_LR35902_FREQUENCY = 0x800000; +const uint32_t SGB_LR35902_FREQUENCY = 0x418B1E; + +const uint32_t GB_COMPONENT_MAGIC = 0x400000; + +static void GBInit(struct LR35902Core* cpu, struct LR35902Component* component); +static void GBInterruptHandlerInit(struct LR35902InterruptHandler* irqh); +static void GBProcessEvents(struct LR35902Core* cpu); +static void GBSetInterrupts(struct LR35902Core* cpu, bool enable); +static void GBHitStub(struct LR35902Core* cpu); + +void GBCreate(struct GB* gb) { + gb->d.id = GB_COMPONENT_MAGIC; + gb->d.init = GBInit; + gb->d.deinit = 0; +} + +static void GBInit(struct LR35902Core* cpu, struct LR35902Component* component) { + struct GB* gba = (struct GB*) component; + gba->cpu = cpu; + + GBInterruptHandlerInit(&cpu->irqh); + GBMemoryInit(gba); + + gba->romVf = 0; + + gba->pristineRom = 0; + gba->pristineRomSize = 0; + gba->yankedRomSize = 0; +} + +bool GBLoadROM(struct GB* gb, struct VFile* vf, struct VFile* sav, const char* fname) { + GBUnloadROM(gb); + gb->romVf = vf; + gb->pristineRomSize = vf->size(vf); + vf->seek(vf, 0, SEEK_SET); +#ifdef _3DS + gb->pristineRom = 0; + if (gb->pristineRomSize <= romBufferSize) { + gb->pristineRom = romBuffer; + vf->read(vf, romBuffer, gb->pristineRomSize); + } +#else + gb->pristineRom = vf->map(vf, gb->pristineRomSize, MAP_READ); +#endif + if (!gb->pristineRom) { + return false; + } + gb->yankedRomSize = 0; + gb->memory.rom = gb->pristineRom; + gb->activeFile = fname; + gb->memory.romSize = gb->pristineRomSize; + gb->romCrc32 = doCrc32(gb->memory.rom, gb->memory.romSize); + return true; + // TODO: error check +} + +void GBUnloadROM(struct GB* gb) { + // TODO: Share with GBAUnloadROM + if (gb->memory.rom && gb->pristineRom != gb->memory.rom) { + if (gb->yankedRomSize) { + gb->yankedRomSize = 0; + } + mappedMemoryFree(gb->memory.rom, 0x400000); + } + gb->memory.rom = 0; + + if (gb->romVf) { +#ifndef _3DS + gb->romVf->unmap(gb->romVf, gb->pristineRom, gb->pristineRomSize); +#endif + gb->pristineRom = 0; + gb->romVf = 0; + } +} + +void GBDestroy(struct GB* gb) { + GBUnloadROM(gb); + + GBMemoryDeinit(gb); +} + +void GBInterruptHandlerInit(struct LR35902InterruptHandler* irqh) { + irqh->reset = GBReset; + irqh->processEvents = GBProcessEvents; + irqh->setInterrupts = GBSetInterrupts; + irqh->hitStub = GBHitStub; +} + +void GBReset(struct LR35902Core* cpu) { + cpu->a = 1; + cpu->f.packed = 0xB0; + cpu->b = 0; + cpu->c = 0x13; + cpu->d = 0; + cpu->e = 0xD8; + cpu->h = 1; + cpu->l = 0x4D; + cpu->sp = 0xFFFE; + cpu->pc = 0x100; + + struct GB* gb = (struct GB*) cpu->master; + + if (gb->yankedRomSize) { + gb->memory.romSize = gb->yankedRomSize; + gb->yankedRomSize = 0; + } + GBMemoryReset(gb); +} + +void GBProcessEvents(struct LR35902Core* cpu) { + // TODO +} + +void GBSetInterrupts(struct LR35902Core* cpu, bool enable) { + // TODO +} + +void GBHitStub(struct LR35902Core* cpu) { + // TODO + printf("Hit stub at address %04X\n", cpu->pc); +} diff --git a/src/gb/gb.h b/src/gb/gb.h new file mode 100644 index 000000000..c0a7b91c0 --- /dev/null +++ b/src/gb/gb.h @@ -0,0 +1,69 @@ +/* Copyright (c) 2013-2016 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef GB_H +#define GB_H + +#include "util/common.h" + +#include "lr35902/lr35902.h" + +#include "gb/memory.h" + +extern const uint32_t DMG_LR35902_FREQUENCY; +extern const uint32_t CGB_LR35902_FREQUENCY; +extern const uint32_t SGB_LR35902_FREQUENCY; + +// TODO: Prefix GBAIRQ +enum GBIRQ { + GB_IRQ_VBLANK = 0x0, + GB_IRQ_LCDSTAT = 0x1, + GB_IRQ_TIMER = 0x2, + GB_IRQ_SIO = 0x3, + GB_IRQ_KEYPAD = 0x4, +}; + +struct GB { + struct LR35902Component d; + + struct LR35902Core* cpu; + struct GBMemory memory; + + int* keySource; + + void* pristineRom; + size_t pristineRomSize; + size_t yankedRomSize; + uint32_t romCrc32; + struct VFile* romVf; + + const char* activeFile; +}; + +void GBCreate(struct GB* gb); +void GBDestroy(struct GB* gb); + +void GBReset(struct LR35902Core* cpu); + +void GBWriteIE(struct GB* gb, uint8_t value); +void GBRaiseIRQ(struct GB* gb, enum GBIRQ irq); +void GBTestIRQ(struct LR35902Core* cpu); +void GBHalt(struct GB* gb); +void GBStop(struct GB* gb); + +struct VFile; +bool GBLoadROM(struct GB* gb, struct VFile* vf, struct VFile* sav, const char* fname); +void GBYankROM(struct GB* gb); +void GBUnloadROM(struct GB* gb); + +struct Patch; +void GBApplyPatch(struct GB* gb, struct Patch* patch); + +bool GBIsROM(struct VFile* vf); + +void GBFrameStarted(struct GB* gb); +void GBFrameEnded(struct GB* gb); + +#endif diff --git a/src/gb/memory.c b/src/gb/memory.c new file mode 100644 index 000000000..3c7176007 --- /dev/null +++ b/src/gb/memory.c @@ -0,0 +1,133 @@ +/* Copyright (c) 2013-2016 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "memory.h" + +#include "gb/gb.h" + +#include "util/memory.h" + +static void GBSetActiveRegion(struct LR35902Core* cpu, uint16_t address) { + // TODO +} + +void GBMemoryInit(struct GB* gb) { + struct LR35902Core* cpu = gb->cpu; + cpu->memory.load16 = GBLoad16; + cpu->memory.load8 = GBLoad8; + cpu->memory.store16 = GBStore16; + cpu->memory.store8 = GBStore8; + cpu->memory.setActiveRegion = GBSetActiveRegion; + + gb->memory.wram = 0; + gb->memory.wramBank = 0; + gb->memory.rom = 0; + gb->memory.romBank = 0; + gb->memory.romSize = 0; +} + +void GBMemoryDeinit(struct GB* gb) { + mappedMemoryFree(gb->memory.wram, GB_SIZE_WORKING_RAM); + if (gb->memory.rom) { + mappedMemoryFree(gb->memory.rom, gb->memory.romSize); + } +} + +void GBMemoryReset(struct GB* gb) { + if (gb->memory.wram) { + mappedMemoryFree(gb->memory.wram, GB_SIZE_WORKING_RAM); + } + gb->memory.wram = anonymousMemoryMap(GB_SIZE_WORKING_RAM); + gb->memory.wramBank = &gb->memory.wram[GB_SIZE_WORKING_RAM_BANK0]; + gb->memory.romBank = &gb->memory.rom[GB_BASE_CART_BANK0]; + + if (!gb->memory.wram) { + GBMemoryDeinit(gb); + } +} + +uint16_t GBLoad16(struct LR35902Core* cpu, uint16_t address) { + // TODO +} + +uint8_t GBLoad8(struct LR35902Core* cpu, uint16_t address) { + struct GB* gb = (struct GB*) cpu->master; + struct GBMemory* memory = &gb->memory; + switch (address >> 12) { + case GB_REGION_CART_BANK0: + case GB_REGION_CART_BANK0 + 1: + case GB_REGION_CART_BANK0 + 2: + case GB_REGION_CART_BANK0 + 3: + return memory->rom[address & (GB_SIZE_CART_BANK0 - 1)]; + case GB_REGION_CART_BANK1: + case GB_REGION_CART_BANK1 + 1: + case GB_REGION_CART_BANK1 + 2: + case GB_REGION_CART_BANK1 + 3: + return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)]; + case GB_REGION_VRAM: + case GB_REGION_VRAM + 1: + // TODO + return 0; + case GB_REGION_EXTERNAL_RAM: + case GB_REGION_EXTERNAL_RAM + 1: + // TODO + return 0; + case GB_REGION_WORKING_RAM_BANK0: + case GB_REGION_WORKING_RAM_BANK0 + 2: + return memory->wram[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)]; + case GB_REGION_WORKING_RAM_BANK1: + return memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)]; + default: + // TODO + return 0; + } +} + +void GBStore16(struct LR35902Core* cpu, uint16_t address, int16_t value) { + // TODO +} + +void GBStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) { + struct GB* gb = (struct GB*) cpu->master; + struct GBMemory* memory = &gb->memory; + switch (address >> 12) { + case GB_REGION_CART_BANK0: + case GB_REGION_CART_BANK0 + 1: + case GB_REGION_CART_BANK0 + 2: + case GB_REGION_CART_BANK0 + 3: + // TODO + return; + case GB_REGION_CART_BANK1: + case GB_REGION_CART_BANK1 + 1: + case GB_REGION_CART_BANK1 + 2: + case GB_REGION_CART_BANK1 + 3: + // TODO + return; + case GB_REGION_VRAM: + case GB_REGION_VRAM + 1: + // TODO + return; + case GB_REGION_EXTERNAL_RAM: + case GB_REGION_EXTERNAL_RAM + 1: + // TODO + return; + case GB_REGION_WORKING_RAM_BANK0: + case GB_REGION_WORKING_RAM_BANK0 + 2: + memory->wram[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value; + return; + case GB_REGION_WORKING_RAM_BANK1: + memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value; + return; + default: + // TODO + return; + } +} + +uint16_t GBView16(struct LR35902Core* cpu, uint16_t address); +uint8_t GBView8(struct LR35902Core* cpu, uint16_t address); + +void GBPatch16(struct LR35902Core* cpu, uint16_t address, int16_t value, int16_t* old); +void GBPatch8(struct LR35902Core* cpu, uint16_t address, int8_t value, int8_t* old); diff --git a/src/gb/memory.h b/src/gb/memory.h new file mode 100644 index 000000000..e475c2652 --- /dev/null +++ b/src/gb/memory.h @@ -0,0 +1,77 @@ +/* Copyright (c) 2013-2016 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef GB_MEMORY_H +#define GB_MEMORY_H + +#include "util/common.h" + +#include "lr35902/lr35902.h" + +struct GB; + +enum { + GB_BASE_CART_BANK0 = 0x0000, + GB_BASE_CART_BANK1 = 0x4000, + GB_BASE_VRAM = 0x8000, + GB_BASE_EXTERNAL_RAM = 0xA000, + GB_BASE_WORKING_RAM_BANK0 = 0xC000, + GB_BASE_WORKING_RAM_BANK1 = 0xD000, + GB_BASE_OAM = 0xFE00, + GB_BASE_IO = 0xFF00, + GB_BASE_HRAM = 0xFF80, + GB_BASE_IE = 0xFFFF +}; + +enum { + GB_REGION_CART_BANK0 = 0x0, + GB_REGION_CART_BANK1 = 0x4, + GB_REGION_VRAM = 0x8, + GB_REGION_EXTERNAL_RAM = 0xA, + GB_REGION_WORKING_RAM_BANK0 = 0xC, + GB_REGION_WORKING_RAM_BANK1 = 0xD, + GB_REGION_WORKING_RAM_BANK1_MIRROR = 0xE, + GB_REGION_OTHER = 0xF, +}; + +enum { + GB_SIZE_CART_BANK0 = 0x4000, + GB_SIZE_VRAM = 0x2000, + GB_SIZE_EXTERNAL_RAM = 0x2000, + GB_SIZE_WORKING_RAM = 0x8000, + GB_SIZE_WORKING_RAM_BANK0 = 0x1000, + GB_SIZE_OAM = 0xA0, + GB_SIZE_IO = 0x80, + GB_SIZE_HRAM = 0x7F, +}; + +struct GBMemory { + uint8_t* rom; + uint8_t* romBank; + + uint8_t* wram; + uint8_t* wramBank; + + size_t romSize; +}; + +void GBMemoryInit(struct GB* gb); +void GBMemoryDeinit(struct GB* gb); + +void GBMemoryReset(struct GB* gb); + +uint16_t GBLoad16(struct LR35902Core* cpu, uint16_t address); +uint8_t GBLoad8(struct LR35902Core* cpu, uint16_t address); + +void GBStore16(struct LR35902Core* cpu, uint16_t address, int16_t value); +void GBStore8(struct LR35902Core* cpu, uint16_t address, int8_t value); + +uint16_t GBView16(struct LR35902Core* cpu, uint16_t address); +uint8_t GBView8(struct LR35902Core* cpu, uint16_t address); + +void GBPatch16(struct LR35902Core* cpu, uint16_t address, int16_t value, int16_t* old); +void GBPatch8(struct LR35902Core* cpu, uint16_t address, int8_t value, int8_t* old); + +#endif diff --git a/src/lr35902/isa-lr35902.c b/src/lr35902/isa-lr35902.c new file mode 100644 index 000000000..d89d3fb69 --- /dev/null +++ b/src/lr35902/isa-lr35902.c @@ -0,0 +1,227 @@ +/* Copyright (c) 2013-2016 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "isa-lr35902.h" + +#include "lr35902/emitter-lr35902.h" +#include "lr35902/lr35902.h" + +#define DEFINE_INSTRUCTION_LR35902(NAME, BODY) \ + static void _LR35902Instruction ## NAME (struct LR35902Core* cpu) { \ + UNUSED(cpu); \ + BODY; \ + } + +DEFINE_INSTRUCTION_LR35902(NOP,); + +DEFINE_INSTRUCTION_LR35902(JPFinish, + if (cpu->condition) { + cpu->pc = (cpu->bus << 8) | cpu->index; + cpu->memory.setActiveRegion(cpu, cpu->pc); + // TODO: Stall properly + cpu->cycles += 4; + }) + +DEFINE_INSTRUCTION_LR35902(JPDelay, + cpu->executionState = LR35902_CORE_READ_PC; + cpu->instruction = _LR35902InstructionJPFinish; + cpu->index = cpu->bus;) + +#define DEFINE_JP_INSTRUCTION_LR35902(CONDITION_NAME, CONDITION) \ + DEFINE_INSTRUCTION_LR35902(JP ## CONDITION_NAME, \ + cpu->executionState = LR35902_CORE_READ_PC; \ + cpu->instruction = _LR35902InstructionJPDelay; \ + cpu->condition = CONDITION;) + +DEFINE_JP_INSTRUCTION_LR35902(, true); +DEFINE_JP_INSTRUCTION_LR35902(C, cpu->f.c); +DEFINE_JP_INSTRUCTION_LR35902(Z, cpu->f.z); +DEFINE_JP_INSTRUCTION_LR35902(NC, !cpu->f.c); +DEFINE_JP_INSTRUCTION_LR35902(NZ, !cpu->f.z); + +DEFINE_INSTRUCTION_LR35902(JRFinish, + if (cpu->condition) { + cpu->pc += (int8_t) cpu->bus; + cpu->memory.setActiveRegion(cpu, cpu->pc); + // TODO: Stall properly + cpu->cycles += 4; + }) + +#define DEFINE_JR_INSTRUCTION_LR35902(CONDITION_NAME, CONDITION) \ + DEFINE_INSTRUCTION_LR35902(JR ## CONDITION_NAME, \ + cpu->executionState = LR35902_CORE_READ_PC; \ + cpu->instruction = _LR35902InstructionJRFinish; \ + cpu->condition = CONDITION;) + +DEFINE_JR_INSTRUCTION_LR35902(, true); +DEFINE_JR_INSTRUCTION_LR35902(C, cpu->f.c); +DEFINE_JR_INSTRUCTION_LR35902(Z, cpu->f.z); +DEFINE_JR_INSTRUCTION_LR35902(NC, !cpu->f.c); +DEFINE_JR_INSTRUCTION_LR35902(NZ, !cpu->f.z); + +#define DEFINE_AND_INSTRUCTION_LR35902(NAME, OPERAND) \ + DEFINE_INSTRUCTION_LR35902(AND ## NAME, \ + cpu->a &= OPERAND; \ + cpu->f.z = !cpu->a; \ + cpu->f.n = 0; \ + cpu->f.c = 0; \ + cpu->f.h = 1;) + +#define DEFINE_XOR_INSTRUCTION_LR35902(NAME, OPERAND) \ + DEFINE_INSTRUCTION_LR35902(XOR ## NAME, \ + cpu->a ^= OPERAND; \ + cpu->f.z = !cpu->a; \ + cpu->f.n = 0; \ + cpu->f.c = 0; \ + cpu->f.h = 0;) + +#define DEFINE_OR_INSTRUCTION_LR35902(NAME, OPERAND) \ + DEFINE_INSTRUCTION_LR35902(OR ## NAME, \ + cpu->a |= OPERAND; \ + cpu->f.z = !cpu->a; \ + cpu->f.n = 0; \ + cpu->f.c = 0; \ + cpu->f.h = 0;) + +#define DEFINE_CP_INSTRUCTION_LR35902(NAME, OPERAND) \ + DEFINE_INSTRUCTION_LR35902(CP ## NAME, \ + int diff = cpu->a - OPERAND; \ + cpu->f.n = 1; \ + cpu->f.z = !diff; \ + cpu->f.c = diff < 0; \ + /* TODO: Find explanation of H flag */) + +#define DEFINE_LDB__INSTRUCTION_LR35902(NAME, OPERAND) \ + DEFINE_INSTRUCTION_LR35902(LDB_ ## NAME, \ + cpu->b = OPERAND;) + +#define DEFINE_LDC__INSTRUCTION_LR35902(NAME, OPERAND) \ + DEFINE_INSTRUCTION_LR35902(LDC_ ## NAME, \ + cpu->c = OPERAND;) + +#define DEFINE_LDD__INSTRUCTION_LR35902(NAME, OPERAND) \ + DEFINE_INSTRUCTION_LR35902(LDD_ ## NAME, \ + cpu->d = OPERAND;) + +#define DEFINE_LDE__INSTRUCTION_LR35902(NAME, OPERAND) \ + DEFINE_INSTRUCTION_LR35902(LDE_ ## NAME, \ + cpu->e = OPERAND;) + +#define DEFINE_LDH__INSTRUCTION_LR35902(NAME, OPERAND) \ + DEFINE_INSTRUCTION_LR35902(LDH_ ## NAME, \ + cpu->h = OPERAND;) + +#define DEFINE_LDL__INSTRUCTION_LR35902(NAME, OPERAND) \ + DEFINE_INSTRUCTION_LR35902(LDL_ ## NAME, \ + cpu->l = OPERAND;) + +#define DEFINE_LDHL__INSTRUCTION_LR35902(NAME, OPERAND) \ + DEFINE_INSTRUCTION_LR35902(LDHL_ ## NAME, \ + cpu->bus = OPERAND; \ + cpu->executionState = LR35902_CORE_MEMORY_MOVE_INDEX_STORE; \ + cpu->instruction = _LR35902InstructionLDHL_Bus;) + +#define DEFINE_LDA__INSTRUCTION_LR35902(NAME, OPERAND) \ + DEFINE_INSTRUCTION_LR35902(LDA_ ## NAME, \ + cpu->a = OPERAND;) + +#define DEFINE_LD_INSTRUCTION_LR35902(NAME, OPERAND) \ + DEFINE_INSTRUCTION_LR35902(LD ## NAME, \ + cpu->executionState = LR35902_CORE_READ_PC; \ + cpu->instruction = _LR35902InstructionLD ## NAME ## _Bus;) + +#define DEFINE_ALU_INSTRUCTION_LR35902_NOHL(NAME) \ + DEFINE_ ## NAME ## _INSTRUCTION_LR35902(A, cpu->a); \ + DEFINE_ ## NAME ## _INSTRUCTION_LR35902(B, cpu->b); \ + DEFINE_ ## NAME ## _INSTRUCTION_LR35902(C, cpu->c); \ + DEFINE_ ## NAME ## _INSTRUCTION_LR35902(D, cpu->d); \ + DEFINE_ ## NAME ## _INSTRUCTION_LR35902(E, cpu->e); \ + DEFINE_ ## NAME ## _INSTRUCTION_LR35902(H, cpu->h); \ + DEFINE_ ## NAME ## _INSTRUCTION_LR35902(L, cpu->l); + +DEFINE_INSTRUCTION_LR35902(LDHL_Bus, \ + cpu->index = LR35902ReadHL(cpu); \ + cpu->executionState = LR35902_CORE_MEMORY_MOVE_INDEX_STORE; \ + cpu->instruction = _LR35902InstructionNOP;) + +DEFINE_INSTRUCTION_LR35902(LDHL, \ + cpu->executionState = LR35902_CORE_READ_PC; \ + cpu->instruction = _LR35902InstructionLDHL_Bus;) + +#define DEFINE_ALU_INSTRUCTION_LR35902(NAME) \ + DEFINE_ ## NAME ## _INSTRUCTION_LR35902(Bus, cpu->bus); \ + DEFINE_INSTRUCTION_LR35902(NAME ## HL, \ + cpu->executionState = LR35902_CORE_MEMORY_MOVE_INDEX_LOAD; \ + cpu->index = LR35902ReadHL(cpu); \ + cpu->instruction = _LR35902Instruction ## NAME ## Bus;) \ + DEFINE_INSTRUCTION_LR35902(NAME, \ + cpu->executionState = LR35902_CORE_READ_PC; \ + cpu->instruction = _LR35902Instruction ## NAME ## Bus;) \ + DEFINE_ALU_INSTRUCTION_LR35902_NOHL(NAME) + +DEFINE_ALU_INSTRUCTION_LR35902(AND); +DEFINE_ALU_INSTRUCTION_LR35902(XOR); +DEFINE_ALU_INSTRUCTION_LR35902(OR); +DEFINE_ALU_INSTRUCTION_LR35902(CP); + +static void _LR35902InstructionLDB_Bus(struct LR35902Core*); +static void _LR35902InstructionLDC_Bus(struct LR35902Core*); +static void _LR35902InstructionLDD_Bus(struct LR35902Core*); +static void _LR35902InstructionLDE_Bus(struct LR35902Core*); +static void _LR35902InstructionLDH_Bus(struct LR35902Core*); +static void _LR35902InstructionLDL_Bus(struct LR35902Core*); +static void _LR35902InstructionLDHL_Bus(struct LR35902Core*); +static void _LR35902InstructionLDA_Bus(struct LR35902Core*); + +DEFINE_ALU_INSTRUCTION_LR35902(LDB_); +DEFINE_ALU_INSTRUCTION_LR35902(LDC_); +DEFINE_ALU_INSTRUCTION_LR35902(LDD_); +DEFINE_ALU_INSTRUCTION_LR35902(LDE_); +DEFINE_ALU_INSTRUCTION_LR35902(LDH_); +DEFINE_ALU_INSTRUCTION_LR35902(LDL_); +DEFINE_ALU_INSTRUCTION_LR35902_NOHL(LDHL_); +DEFINE_ALU_INSTRUCTION_LR35902(LDA_); + +DEFINE_INSTRUCTION_LR35902(LDIAFinish, \ + cpu->index |= cpu->bus << 8; + cpu->bus = cpu->a; \ + cpu->executionState = LR35902_CORE_MEMORY_MOVE_INDEX_STORE; \ + cpu->instruction = _LR35902InstructionNOP;) + +DEFINE_INSTRUCTION_LR35902(LDIADelay, \ + cpu->index = cpu->bus; + cpu->executionState = LR35902_CORE_READ_PC; \ + cpu->instruction = _LR35902InstructionLDIAFinish;) + +DEFINE_INSTRUCTION_LR35902(LDIA, \ + cpu->executionState = LR35902_CORE_READ_PC; \ + cpu->instruction = _LR35902InstructionLDIADelay;) + +DEFINE_INSTRUCTION_LR35902(LDAIFinish, \ + cpu->index |= cpu->bus << 8; + cpu->executionState = LR35902_CORE_MEMORY_MOVE_INDEX_LOAD; \ + cpu->instruction = _LR35902InstructionLDA_Bus;) + +DEFINE_INSTRUCTION_LR35902(LDAIDelay, \ + cpu->index = cpu->bus; + cpu->executionState = LR35902_CORE_READ_PC; \ + cpu->instruction = _LR35902InstructionLDAIFinish;) + +DEFINE_INSTRUCTION_LR35902(LDAI, \ + cpu->executionState = LR35902_CORE_READ_PC; \ + cpu->instruction = _LR35902InstructionLDAIDelay;) + +DEFINE_INSTRUCTION_LR35902(DI, cpu->irqh.setInterrupts(cpu, false)); +DEFINE_INSTRUCTION_LR35902(EI, cpu->irqh.setInterrupts(cpu, true)); + +DEFINE_INSTRUCTION_LR35902(STUB, cpu->irqh.hitStub(cpu)); + +const LR35902Instruction _lr35902InstructionTable[0x100] = { + DECLARE_LR35902_EMITTER_BLOCK(_LR35902Instruction) +}; + +const LR35902Instruction _lr35902CBInstructionTable[0x100] = { + DECLARE_LR35902_CB_EMITTER_BLOCK(_LR35902Instruction) +}; diff --git a/src/lr35902/isa-lr35902.h b/src/lr35902/isa-lr35902.h new file mode 100644 index 000000000..66a99c1b6 --- /dev/null +++ b/src/lr35902/isa-lr35902.h @@ -0,0 +1,17 @@ +/* Copyright (c) 2013-2016 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef ISA_LR35902_H +#define ISA_LR35902_H + +#include "util/common.h" + +struct LR35902Core; + +typedef void (*LR35902Instruction)(struct LR35902Core*); +const LR35902Instruction _lr35902InstructionTable[0x100]; +const LR35902Instruction _lr35902CBInstructionTable[0x100]; + +#endif diff --git a/src/lr35902/lr35902.c b/src/lr35902/lr35902.c new file mode 100644 index 000000000..9393ca31f --- /dev/null +++ b/src/lr35902/lr35902.c @@ -0,0 +1,116 @@ +/* Copyright (c) 2013-2016 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "lr35902.h" + +#include "isa-lr35902.h" + +void LR35902Init(struct LR35902Core* cpu) { + cpu->master->init(cpu, cpu->master); + size_t i; + for (i = 0; i < cpu->numComponents; ++i) { + if (cpu->components[i] && cpu->components[i]->init) { + cpu->components[i]->init(cpu, cpu->components[i]); + } + } +} + +void LR35902Deinit(struct LR35902Core* cpu) { + if (cpu->master->deinit) { + cpu->master->deinit(cpu->master); + } + size_t i; + for (i = 0; i < cpu->numComponents; ++i) { + if (cpu->components[i] && cpu->components[i]->deinit) { + cpu->components[i]->deinit(cpu->components[i]); + } + } +} + +void LR35902SetComponents(struct LR35902Core* cpu, struct LR35902Component* master, int extra, struct LR35902Component** extras) { + cpu->master = master; + cpu->numComponents = extra; + cpu->components = extras; +} + + +void LR35902HotplugAttach(struct LR35902Core* cpu, size_t slot) { + if (slot >= cpu->numComponents) { + return; + } + cpu->components[slot]->init(cpu, cpu->components[slot]); +} + +void LR35902HotplugDetach(struct LR35902Core* cpu, size_t slot) { + if (slot >= cpu->numComponents) { + return; + } + cpu->components[slot]->deinit(cpu->components[slot]); +} + +void LR35902Reset(struct LR35902Core* cpu) { + cpu->af = 0; + cpu->bc = 0; + cpu->de = 0; + cpu->hl = 0; + + cpu->sp = 0; + cpu->pc = 0; + + cpu->instruction = 0; + + cpu->cycles = 0; + cpu->nextEvent = 0; + cpu->executionState = LR35902_CORE_FETCH; + cpu->halted = 0; + + cpu->irqh.reset(cpu); +} + +void LR35902RaiseIRQ(struct LR35902Core* cpu) { + // TODO +} + +void LR35902Tick(struct LR35902Core* cpu) { + ++cpu->cycles; + enum LR35902ExecutionState state = cpu->executionState; + ++cpu->executionState; + cpu->executionState &= 3; + switch (state) { + case LR35902_CORE_FETCH: + cpu->bus = cpu->memory.load8(cpu, cpu->pc); + break; + case LR35902_CORE_DECODE: + cpu->instruction = _lr35902InstructionTable[cpu->bus]; + ++cpu->pc; + break; + case LR35902_CORE_EXECUTE: + cpu->instruction(cpu); + break; + case LR35902_CORE_MEMORY_LOAD: + cpu->bus = cpu->memory.load8(cpu, cpu->index); + break; + case LR35902_CORE_MEMORY_STORE: + cpu->memory.store8(cpu, cpu->index, cpu->bus); + break; + case LR35902_CORE_READ_PC: + cpu->bus = cpu->memory.load8(cpu, cpu->pc); + ++cpu->pc; + cpu->executionState = LR35902_CORE_READ_PC_STALL; + break; + case LR35902_CORE_MEMORY_MOVE_INDEX_LOAD: + cpu->executionState = LR35902_CORE_MEMORY_LOAD; + break; + case LR35902_CORE_MEMORY_MOVE_INDEX_STORE: + cpu->executionState = LR35902_CORE_MEMORY_STORE; + break; + case LR35902_CORE_READ_PC_STALL: + case LR35902_CORE_STALL: + break; + } + if (cpu->cycles >= cpu->nextEvent) { + cpu->irqh.processEvents(cpu); + } +} diff --git a/src/lr35902/lr35902.h b/src/lr35902/lr35902.h new file mode 100644 index 000000000..9dc6cc315 --- /dev/null +++ b/src/lr35902/lr35902.h @@ -0,0 +1,149 @@ +/* Copyright (c) 2013-2016 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef LR35902_H +#define LR35902_H + +#include "util/common.h" + +#include "lr35902/isa-lr35902.h" + +struct LR35902Core; + +#pragma pack(push, 1) +union FlagRegister { + struct { +#if defined(__POWERPC__) || defined(__PPC__) + unsigned z : 1; + unsigned n : 1; + unsigned h : 1; + unsigned c : 1; + unsigned : 4; +#else + unsigned : 4; + unsigned c : 1; + unsigned h : 1; + unsigned n : 1; + unsigned z : 1; +#endif + }; + + uint8_t packed; +}; +#pragma pack(pop, 1) + +enum LR35902ExecutionState { + LR35902_CORE_FETCH = 0, + LR35902_CORE_DECODE, + LR35902_CORE_STALL, + LR35902_CORE_EXECUTE, + + LR35902_CORE_MEMORY_LOAD = 5, + LR35902_CORE_MEMORY_STORE = 9, + LR35902_CORE_MEMORY_MOVE_INDEX_LOAD, + LR35902_CORE_MEMORY_MOVE_INDEX_STORE, + LR35902_CORE_READ_PC, + LR35902_CORE_READ_PC_STALL, +}; + +struct LR35902Memory { + uint16_t (*load16)(struct LR35902Core*, uint16_t address); + uint8_t (*load8)(struct LR35902Core*, uint16_t address); + + void (*store16)(struct LR35902Core*, uint16_t address, int16_t value); + void (*store8)(struct LR35902Core*, uint16_t address, int8_t value); + + uint8_t* activeRegion; + uint16_t activeMask; + void (*setActiveRegion)(struct LR35902Core*, uint16_t address); +}; + +struct LR35902InterruptHandler { + void (*reset)(struct LR35902Core* cpu); + void (*processEvents)(struct LR35902Core* cpu); + void (*setInterrupts)(struct LR35902Core* cpu, bool enable); + + void (*hitStub)(struct LR35902Core* cpu); +}; + +// TODO: Merge with ARMComponent? +struct LR35902Component { + uint32_t id; + void (*init)(struct LR35902Core* cpu, struct LR35902Component* component); + void (*deinit)(struct LR35902Component* component); +}; + +struct LR35902Core { +#pragma pack(push, 1) + union { + struct { + union FlagRegister f; + uint8_t a; + }; + uint16_t af; + }; +#pragma pack(pop, 1) + union { + struct { + uint8_t c; + uint8_t b; + }; + uint16_t bc; + }; + union { + struct { + uint8_t e; + uint8_t d; + }; + uint16_t de; + }; + union { + struct { + uint8_t l; + uint8_t h; + }; + uint16_t hl; + }; + uint16_t sp; + uint16_t pc; + + uint16_t index; + + int32_t cycles; + int32_t nextEvent; + enum LR35902ExecutionState executionState; + int halted; + + uint8_t bus; + bool condition; + LR35902Instruction instruction; + + struct LR35902Memory memory; + struct LR35902InterruptHandler irqh; + + struct LR35902Component* master; + + size_t numComponents; + struct LR35902Component** components; +}; + +static inline uint16_t LR35902ReadHL(struct LR35902Core* cpu) { + uint16_t hl; + LOAD_16LE(hl, 0, &cpu->hl); + return hl; +} + +void LR35902Init(struct LR35902Core* cpu); +void LR35902Deinit(struct LR35902Core* cpu); +void LR35902SetComponents(struct LR35902Core* cpu, struct LR35902Component* master, int extra, struct LR35902Component** extras); +void LR35902HotplugAttach(struct LR35902Core* cpu, size_t slot); +void LR35902HotplugDetach(struct LR35902Core* cpu, size_t slot); + +void LR35902Reset(struct LR35902Core* cpu); +void LR35902RaiseIRQ(struct LR35902Core*); + +void LR35902Tick(struct LR35902Core* cpu); + +#endif diff --git a/src/platform/test/gb-main.c b/src/platform/test/gb-main.c new file mode 100644 index 000000000..2701773a4 --- /dev/null +++ b/src/platform/test/gb-main.c @@ -0,0 +1,26 @@ +/* Copyright (c) 2013-2016 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "lr35902/lr35902.h" +#include "gb/gb.h" +#include "util/vfs.h" + +int main(int argc, char* argv[]) { + struct LR35902Core cpu; + struct GB gb; + + GBCreate(&gb); + LR35902SetComponents(&cpu, &gb.d, 0, 0); + LR35902Init(&cpu); + struct VFile* vf = VFileOpen(argv[1], O_RDONLY); + GBLoadROM(&gb, vf, 0, argv[1]); + + LR35902Reset(&cpu); + while (true) { + LR35902Tick(&cpu); + } + vf->close(vf); + return 0; +}