From 15d6923327d9932e596a50d9f05252f0d019279a Mon Sep 17 00:00:00 2001 From: Natesh Narain Date: Tue, 22 Aug 2017 22:58:42 -0400 Subject: [PATCH 01/14] update the lcd mode in the register --- src/core/gpu.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 97670533..ea3881aa 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -116,6 +116,8 @@ namespace gb break; } + // update LCDC stat mode + stat_ = (stat_ & 0xFC) | (static_cast(mode_)); } void setRenderCallback(RenderScanlineCallback callback) From 97d56e3824569f4b5196130f8a1db0b39301ac38 Mon Sep 17 00:00:00 2001 From: Natesh Narain Date: Thu, 24 Aug 2017 01:14:08 -0400 Subject: [PATCH 02/14] renamed a constant that clashed with a macro --- src/core/gpu.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 97670533..9145f416 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -18,7 +18,7 @@ namespace gb static constexpr auto LINE_CYCLES = 456; static constexpr auto VBLANK_LINE = 144; - static constexpr auto LINE_MAX = 153; + static constexpr auto MAX_LINES = 153; /* Private Implementation */ @@ -214,7 +214,7 @@ namespace gb void updateLY() { - line_ = (line_ + 1) % LINE_MAX; + line_ = (line_ + 1) % MAX_LINES; mmu_->write((uint8_t)line_, memorymap::LY_REGISTER); } From 6e73d726bfc43c08891ee78392273a07531365b6 Mon Sep 17 00:00:00 2001 From: Natesh Narain Date: Tue, 22 Aug 2017 22:44:52 -0400 Subject: [PATCH 03/14] HDMA at HBlank --- src/core/gpu.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 8 deletions(-) diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index a92cd990..502ddc07 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -33,6 +33,25 @@ namespace gb LCD }; + /** + Data needed for HDMA transfer + */ + struct Hdma + { + Hdma() : + transfer_active(false), + source(0), + destination(0), + length(0) + { + } + + bool transfer_active; + uint16_t source; + uint16_t destination; + uint16_t length; + }; + using CgbPalette = std::array, 8>; Impl(MMU::Ptr& mmu) : @@ -46,8 +65,7 @@ namespace gb vblank_provider_(*mmu.get(), InterruptProvider::Interrupt::VBLANK), stat_provider_(*mmu.get(), InterruptProvider::Interrupt::LCDSTAT), tilemap_(*mmu.get(), palette_), - cgb_enabled_(mmu->cgbEnabled()), - hdma_transfer_start_(false) + cgb_enabled_(mmu->cgbEnabled()) { mmu->addWriteHandler(memorymap::LCDC_REGISTER, std::bind(&Impl::lcdcWriteHandler, this, std::placeholders::_1, std::placeholders::_2)); mmu->addWriteHandler(memorymap::BGPD, std::bind(&Impl::paletteWriteHandler, this, std::placeholders::_1, std::placeholders::_2)); @@ -98,6 +116,8 @@ namespace gb if (hasElapsed(LCD_TRANSFER_CYCLES)) { mode_ = Mode::HBLANK; + // perform an hdma transfer + doHdma(); checkStatInterrupts(ime); } break; @@ -301,22 +321,52 @@ namespace gb void hdma5WriteHandler(uint8_t value, uint16_t addr) { + uint16_t src = WORD(mmu_->read(memorymap::HDMA1), mmu_->read(memorymap::HDMA2)); + uint16_t dest = WORD(mmu_->read(memorymap::HDMA3), mmu_->read(memorymap::HDMA4)); + uint16_t length = ((value & 0x7F) + 1) * 0x10; + + if (IS_BIT_CLR(value, 7)) { - uint16_t src = WORD(mmu_->read(memorymap::HDMA1), mmu_->read(memorymap::HDMA2)); - uint16_t dest = WORD(mmu_->read(memorymap::HDMA3), mmu_->read(memorymap::HDMA4)); - uint16_t length = ((value & 0x7F) + 1) * 0x10; - mmu_->dma(dest, src, length); + // disable an active hdma transfer + hdma_.transfer_active = false; } else { - hdma_transfer_start_ = true; + hdma_.source = src; + hdma_.destination = dest; + hdma_.length = length; + hdma_.transfer_active = true; } hdma5_ = value; } + void doHdma() + { + if (hdma_.transfer_active) + { + // hdma only works between this range + if (line_ >= 0 && line_ <= 143) + { + // transfer $10 bytes + mmu_->dma(hdma_.source, hdma_.destination, 0x10); + // advance source $10 bytes + hdma_.source += 0x10; + // advance destination $10 bytes + hdma_.destination += 0x10; + // count down the length + hdma_.length -= 0x10; + + if (hdma_.length == 0) + { + hdma_.transfer_active = false; + } + } + } + } + void compareLyToLyc(bool ime) { auto lyc = mmu_->read(memorymap::LYC_REGISTER); @@ -361,6 +411,7 @@ namespace gb uint8_t& lcdc_; uint8_t& stat_; uint8_t& hdma5_; + Hdma hdma_; InterruptProvider vblank_provider_; InterruptProvider stat_provider_; @@ -371,7 +422,6 @@ namespace gb RenderScanlineCallback render_scanline_; bool cgb_enabled_; - bool hdma_transfer_start_; CgbPalette cgb_background_palettes_; CgbPalette cgb_sprite_palette_; From 5eb3ab3bec09b1e8098192e1551112a09552e51e Mon Sep 17 00:00:00 2001 From: Natesh Narain Date: Sat, 26 Aug 2017 01:36:54 -0400 Subject: [PATCH 04/14] Allocate internal ram banks for CGB --- include/gameboycore/mbc.h | 12 ++++++-- include/gameboycore/memorymap.h | 4 ++- src/core/gpu.cpp | 40 ++++++++++++++------------- src/core/mbc.cpp | 49 +++++++++++++++++++++------------ src/core/tilemap.cpp | 23 ++++++++++++++-- 5 files changed, 86 insertions(+), 42 deletions(-) diff --git a/include/gameboycore/mbc.h b/include/gameboycore/mbc.h index badf91db..635c338d 100644 --- a/include/gameboycore/mbc.h +++ b/include/gameboycore/mbc.h @@ -135,9 +135,15 @@ namespace gb int getIoIndex(uint16_t addr) const; /** + Get the VRAM offset given the current state of the VBK register */ int getVramOffset() const; + /** + Get the internal ram bank offset given the current state of the SVBK register + */ + int getInternalRamOffset() const; + /** Load memory */ @@ -146,11 +152,13 @@ namespace gb //! number of switchable rom banks int num_rom_banks_; //! number of cartridge ram banks - int num_ram_banks_; + int num_cartridge_ram_banks_; //! CGB enabled bool cgb_enabled_; - //! + //! CGB mode has 2 vram banks for character and map data int vram_banks_; + //! number internal ram banks + int num_internal_ram_banks_; }; } } diff --git a/include/gameboycore/memorymap.h b/include/gameboycore/memorymap.h index 2a410f62..b75d427b 100644 --- a/include/gameboycore/memorymap.h +++ b/include/gameboycore/memorymap.h @@ -139,7 +139,7 @@ namespace gb{ DMA_REGISTER = 0xFF46, - VBK = 0xFF4F, ///< VRAM Bank Selection + VBK_REGISTER = 0xFF4F, ///< VRAM Bank Selection HDMA1 = 0xFF51, ///< New Dread(memorymap::BGP_REGISTER)); // get lcd config - const auto lcdc = mmu_->read(memorymap::LCDC_REGISTER); - const auto background_enabled = IS_SET(lcdc, memorymap::LCDC::BG_DISPLAY_ON) != 0; - const auto window_enabled = IS_SET(lcdc, memorymap::LCDC::WINDOW_ON) != 0; - const auto sprites_enabled = IS_SET(lcdc, memorymap::LCDC::OBJ_ON) != 0; + const auto background_enabled = IS_SET(lcdc_, memorymap::LCDC::BG_DISPLAY_ON) != 0; + const auto window_enabled = IS_SET(lcdc_, memorymap::LCDC::WINDOW_ON) != 0; + const auto sprites_enabled = IS_SET(lcdc_, memorymap::LCDC::OBJ_ON) != 0; // get background tile line const auto background = tilemap_.getBackground(line_, cgb_enabled_); @@ -321,23 +320,26 @@ namespace gb void hdma5WriteHandler(uint8_t value, uint16_t addr) { - uint16_t src = WORD(mmu_->read(memorymap::HDMA1), mmu_->read(memorymap::HDMA2)); - uint16_t dest = WORD(mmu_->read(memorymap::HDMA3), mmu_->read(memorymap::HDMA4)); + uint16_t src = WORD(mmu_->read(memorymap::HDMA1), mmu_->read(memorymap::HDMA2)) & 0xFFF0; + uint16_t dest = WORD(mmu_->read(memorymap::HDMA3), mmu_->read(memorymap::HDMA4)) & 0xFFF0; uint16_t length = ((value & 0x7F) + 1) * 0x10; - - if (IS_BIT_CLR(value, 7)) + // check if the source and destination addresses are valid + if (((src >= 0x0000 && src <= 0x7FF0) || (src >= 0xA000 && src <= 0xDFF0)) && (dest >= 0x8000 && dest <= 0x9FF0)) { - mmu_->dma(dest, src, length); - // disable an active hdma transfer - hdma_.transfer_active = false; - } - else - { - hdma_.source = src; - hdma_.destination = dest; - hdma_.length = length; - hdma_.transfer_active = true; + if (IS_BIT_CLR(value, 7)) + { + mmu_->dma(dest, src, length); + // disable an active hdma transfer + hdma_.transfer_active = false; + } + else + { + hdma_.source = src; + hdma_.destination = dest; + hdma_.length = length; + hdma_.transfer_active = true; + } } hdma5_ = value; @@ -351,7 +353,7 @@ namespace gb if (line_ >= 0 && line_ <= 143) { // transfer $10 bytes - mmu_->dma(hdma_.source, hdma_.destination, 0x10); + mmu_->dma(hdma_.destination, hdma_.source, 0x10); // advance source $10 bytes hdma_.source += 0x10; // advance destination $10 bytes diff --git a/src/core/mbc.cpp b/src/core/mbc.cpp index cf652cca..e9383d22 100644 --- a/src/core/mbc.cpp +++ b/src/core/mbc.cpp @@ -14,8 +14,10 @@ namespace gb rom_bank_(0), ram_bank_(0), num_rom_banks_(0), - num_ram_banks_(0), - cgb_enabled_(cgb_enable) + num_cartridge_ram_banks_(0), + cgb_enabled_(cgb_enable), + vram_banks_(0), + num_internal_ram_banks_(0) { loadMemory(rom, size, rom_size, ram_size); } @@ -90,7 +92,7 @@ namespace gb { // index the points around external RAM to capture all bank auto start = getIndex(memorymap::EXTERNAL_RAM_START, rom_bank_, 0); - auto end = getIndex(memorymap::EXTERNAL_RAM_END, rom_bank_, num_ram_banks_ - 1); + auto end = getIndex(memorymap::EXTERNAL_RAM_END, rom_bank_, num_cartridge_ram_banks_ - 1); return std::vector(memory_.begin() + start, memory_.begin() + end); } @@ -124,18 +126,19 @@ namespace gb case 0x6000: case 0x7000: return (addr) + ((16 * KILO_BYTE) * rom_bank); - break; case 0x8000: case 0x9000: return (addr) + (16 * KILO_BYTE * (num_rom_banks_-1)) + getVramOffset(); case 0xA000: case 0xB000: - return (addr) + (16 * KILO_BYTE * (num_rom_banks_-1)) + ((8 * KILO_BYTE) * (vram_banks_-1)) + (8 * KILO_BYTE * ram_bank); + return (addr) + (16 * KILO_BYTE * (num_rom_banks_ - 1)) + ((8 * KILO_BYTE) * (vram_banks_ - 1)) + (8 * KILO_BYTE * ram_bank); case 0xC000: + return (addr) + (16 * KILO_BYTE * (num_rom_banks_ - 1)) + ((8 * KILO_BYTE) * (vram_banks_ - 1)) + (8 * KILO_BYTE * (num_cartridge_ram_banks_-1)); case 0xD000: + return (addr)+(16 * KILO_BYTE * (num_rom_banks_ - 1)) + ((8 * KILO_BYTE) * (vram_banks_ - 1)) + (8 * KILO_BYTE * (num_cartridge_ram_banks_ - 1)) + getInternalRamOffset(); case 0xE000: case 0xF000: - return (addr) + (16 * KILO_BYTE * (num_rom_banks_-1)) + ((8 * KILO_BYTE) * (vram_banks_ - 1)) + (8 * KILO_BYTE * (num_ram_banks_-1)); + return (addr)+(16 * KILO_BYTE * (num_rom_banks_ - 1)) + ((8 * KILO_BYTE) * (vram_banks_ - 1)) + (8 * KILO_BYTE * (num_cartridge_ram_banks_ - 1)) + (4 * KILO_BYTE * (num_internal_ram_banks_-1)); } return 0; @@ -143,12 +146,21 @@ namespace gb int MBC::getIoIndex(uint16_t addr) const { - return (addr)+(16 * KILO_BYTE * (num_rom_banks_ - 1)) + ((8 * KILO_BYTE) * (vram_banks_ - 1)) + (8 * KILO_BYTE * (num_ram_banks_ - 1)); + return (addr)+ + (16 * KILO_BYTE * (num_rom_banks_ - 1)) + + ((8 * KILO_BYTE) * (vram_banks_ - 1)) + + (8 * KILO_BYTE * (num_cartridge_ram_banks_ - 1)) + + (4 * KILO_BYTE * (num_internal_ram_banks_-1)); } int MBC::getVramOffset() const { - return (8 * KILO_BYTE) * (memory_[getIoIndex(memorymap::VBK)] & 0x01); + return (8 * KILO_BYTE) * (memory_[getIoIndex(memorymap::VBK_REGISTER)] & 0x01); + } + + int MBC::getInternalRamOffset() const + { + return (4 * KILO_BYTE) * 0; } void MBC::loadMemory(uint8_t* rom, std::size_t size, uint8_t rom_size, uint8_t ram_size) @@ -176,20 +188,21 @@ namespace gb num_rom_banks_ = rom_banks2[rom_size] - 1; } - num_ram_banks_ = (static_cast(ram_size) == MBC::XRAM::KB32) ? 4 : 1; + num_cartridge_ram_banks_ = (static_cast(ram_size) == MBC::XRAM::KB32) ? 4 : 1; + num_internal_ram_banks_ = (cgb_enabled_) ? 7 : 1; vram_banks_ = (cgb_enabled_) ? 2 : 1; // memory sizes - const auto rom0 = (16 * KILO_BYTE); - const auto rom_switchable = (16 * KILO_BYTE) * num_rom_banks_; - const auto vram = (8 * KILO_BYTE) * vram_banks_; - const auto ram_switchable1 = (8 * KILO_BYTE) * num_ram_banks_; - const auto ram2 = (8 * KILO_BYTE); - const auto ram_switchable2 = (8 * KILO_BYTE) * 1; - const auto ram3 = (8 * KILO_BYTE); - - const auto memory_size = rom0 + rom_switchable + vram + ram_switchable1 + ram2 + ram_switchable2 + ram3; + const auto rom_bank0_fixed = (16 * KILO_BYTE); // $0000 - $3FFF + const auto rom_switchable = (16 * KILO_BYTE) * num_rom_banks_; // $4000 - $7FFF + const auto vram = (8 * KILO_BYTE) * vram_banks_; // $8000 - $9FFF + const auto ram_cartridge_switchable = (8 * KILO_BYTE) * num_cartridge_ram_banks_; // $A000 - $B000 + const auto ram_bank0_fixed = (4 * KILO_BYTE); // $C000 - $CFFF + const auto ram_internal_switchable = (4 * KILO_BYTE) * num_internal_ram_banks_; // $D000 - $DFFF + const auto high_ram = (8 * KILO_BYTE); // $E000 - $FFFF + + const auto memory_size = rom_bank0_fixed + rom_switchable + vram + ram_cartridge_switchable + ram_bank0_fixed + ram_internal_switchable + high_ram; memory_.resize(memory_size); diff --git a/src/core/tilemap.cpp b/src/core/tilemap.cpp index 4244a5b5..98fa37ba 100644 --- a/src/core/tilemap.cpp +++ b/src/core/tilemap.cpp @@ -33,30 +33,49 @@ namespace gb TileMap::Line tileline; + // scroll x const auto scx = mmu_.read(memorymap::SCX_REGISTER); + // scroll y const auto scy = mmu_.read(memorymap::SCY_REGISTER); + // starting row given the scroll const auto tile_row = ((scy + line) / tile_height); + // starting column given the scroll const auto start_tile_col = scx / tile_width; - const auto pixel_row = (scy + line) % tile_height; + auto pixel_row = (scy + line) % tile_height; auto idx = 0; for (auto tile_col = start_tile_col; tile_col < start_tile_col + 21; ++tile_col) { // calculate tile address const auto tile_offset = start + (tiles_per_row * (tile_row % tiles_per_row)) + (tile_col % tiles_per_col); + // read tile character code from map const auto tilenum = mmu_.readVram(tile_offset, 0); // read tile attributes const auto tileattr = mmu_.readVram(tile_offset, 1); + // extract tile attributes const auto palette_number = (cgb_enable) ? (tileattr & 0x07) : 0; const auto character_bank = (cgb_enable) ? ((tileattr >> 3) & 0x01) : 0; + const auto flip_horizontal = (cgb_enable && (tileattr & 0x20) != 0); + const auto flip_vertical = (cgb_enable && (tileattr & 0x40) != 0); + // TODO: Display priority + + if (flip_vertical) + pixel_row = tile_height - pixel_row - 1; + + // get the row of the tile the current scan line is on. + auto row = tileram_.getRow(pixel_row, tilenum, umode, character_bank); - const auto row = tileram_.getRow(pixel_row, tilenum, umode, character_bank); + // horizontally flip the row if the flag is set + if (flip_horizontal) + std::reverse(row.begin(), row.end()); + // calculate pixel column number auto pixel_col = tile_col * tile_width; + // for (auto i = 0u; i < row.size(); ++i) { if (pixel_col >= scx && pixel_col <= scx + 160 && idx < 160) From a37eb4af05526568ceb30bbb5405be3be381e5e9 Mon Sep 17 00:00:00 2001 From: Natesh Narain Date: Sat, 26 Aug 2017 01:51:22 -0400 Subject: [PATCH 05/14] Added the internal ram offset specified by SVBK --- include/gameboycore/mbc.h | 4 ++++ src/core/mbc.cpp | 46 +++++++++++++++++++++++---------------- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/include/gameboycore/mbc.h b/include/gameboycore/mbc.h index 635c338d..d5ac8c94 100644 --- a/include/gameboycore/mbc.h +++ b/include/gameboycore/mbc.h @@ -144,6 +144,10 @@ namespace gb */ int getInternalRamOffset() const; + /** + */ + unsigned int kilo(unsigned int n) const; + /** Load memory */ diff --git a/src/core/mbc.cpp b/src/core/mbc.cpp index e9383d22..49b9814d 100644 --- a/src/core/mbc.cpp +++ b/src/core/mbc.cpp @@ -125,20 +125,20 @@ namespace gb case 0x5000: case 0x6000: case 0x7000: - return (addr) + ((16 * KILO_BYTE) * rom_bank); + return (addr) + (kilo(16) * rom_bank); case 0x8000: case 0x9000: - return (addr) + (16 * KILO_BYTE * (num_rom_banks_-1)) + getVramOffset(); + return (addr) + (kilo(16) * (num_rom_banks_-1)) + getVramOffset(); case 0xA000: case 0xB000: - return (addr) + (16 * KILO_BYTE * (num_rom_banks_ - 1)) + ((8 * KILO_BYTE) * (vram_banks_ - 1)) + (8 * KILO_BYTE * ram_bank); + return (addr) + (kilo(16) * (num_rom_banks_ - 1)) + (kilo(8) * (vram_banks_ - 1)) + (kilo(8) * ram_bank); case 0xC000: - return (addr) + (16 * KILO_BYTE * (num_rom_banks_ - 1)) + ((8 * KILO_BYTE) * (vram_banks_ - 1)) + (8 * KILO_BYTE * (num_cartridge_ram_banks_-1)); + return (addr) + (kilo(16) * (num_rom_banks_ - 1)) + (kilo(8) * (vram_banks_ - 1)) + (kilo(8) * (num_cartridge_ram_banks_-1)); case 0xD000: - return (addr)+(16 * KILO_BYTE * (num_rom_banks_ - 1)) + ((8 * KILO_BYTE) * (vram_banks_ - 1)) + (8 * KILO_BYTE * (num_cartridge_ram_banks_ - 1)) + getInternalRamOffset(); + return (addr)+(kilo(16) * (num_rom_banks_ - 1)) + (kilo(8) * (vram_banks_ - 1)) + (kilo(8) * (num_cartridge_ram_banks_ - 1)) + getInternalRamOffset(); case 0xE000: case 0xF000: - return (addr)+(16 * KILO_BYTE * (num_rom_banks_ - 1)) + ((8 * KILO_BYTE) * (vram_banks_ - 1)) + (8 * KILO_BYTE * (num_cartridge_ram_banks_ - 1)) + (4 * KILO_BYTE * (num_internal_ram_banks_-1)); + return (addr)+(kilo(16) * (num_rom_banks_ - 1)) + (kilo(8) * (vram_banks_ - 1)) + (kilo(8) * (num_cartridge_ram_banks_ - 1)) + (kilo(4) * (num_internal_ram_banks_-1)); } return 0; @@ -147,20 +147,23 @@ namespace gb int MBC::getIoIndex(uint16_t addr) const { return (addr)+ - (16 * KILO_BYTE * (num_rom_banks_ - 1)) + - ((8 * KILO_BYTE) * (vram_banks_ - 1)) + - (8 * KILO_BYTE * (num_cartridge_ram_banks_ - 1)) + - (4 * KILO_BYTE * (num_internal_ram_banks_-1)); + (kilo(16) * (num_rom_banks_ - 1)) + + (kilo(8) * (vram_banks_ - 1)) + + (kilo(8) * (num_cartridge_ram_banks_ - 1)) + + (kilo(4)* (num_internal_ram_banks_-1)); } int MBC::getVramOffset() const { - return (8 * KILO_BYTE) * (memory_[getIoIndex(memorymap::VBK_REGISTER)] & 0x01); + return kilo(8) * (memory_[getIoIndex(memorymap::VBK_REGISTER)] & 0x01); } int MBC::getInternalRamOffset() const { - return (4 * KILO_BYTE) * 0; + auto bank_number = memory_[getIoIndex(memorymap::SVBK_REGISTER)] & 0x07; + if (bank_number < 2) bank_number = 0; + + return kilo(4) * (bank_number); } void MBC::loadMemory(uint8_t* rom, std::size_t size, uint8_t rom_size, uint8_t ram_size) @@ -194,13 +197,13 @@ namespace gb vram_banks_ = (cgb_enabled_) ? 2 : 1; // memory sizes - const auto rom_bank0_fixed = (16 * KILO_BYTE); // $0000 - $3FFF - const auto rom_switchable = (16 * KILO_BYTE) * num_rom_banks_; // $4000 - $7FFF - const auto vram = (8 * KILO_BYTE) * vram_banks_; // $8000 - $9FFF - const auto ram_cartridge_switchable = (8 * KILO_BYTE) * num_cartridge_ram_banks_; // $A000 - $B000 - const auto ram_bank0_fixed = (4 * KILO_BYTE); // $C000 - $CFFF - const auto ram_internal_switchable = (4 * KILO_BYTE) * num_internal_ram_banks_; // $D000 - $DFFF - const auto high_ram = (8 * KILO_BYTE); // $E000 - $FFFF + const auto rom_bank0_fixed = kilo(16); // $0000 - $3FFF + const auto rom_switchable = kilo(16) * num_rom_banks_; // $4000 - $7FFF + const auto vram = kilo(8) * vram_banks_; // $8000 - $9FFF + const auto ram_cartridge_switchable = kilo(8) * num_cartridge_ram_banks_; // $A000 - $B000 + const auto ram_bank0_fixed = kilo(4); // $C000 - $CFFF + const auto ram_internal_switchable = kilo(4) * num_internal_ram_banks_; // $D000 - $DFFF + const auto high_ram = kilo(8); // $E000 - $FFFF const auto memory_size = rom_bank0_fixed + rom_switchable + vram + ram_cartridge_switchable + ram_bank0_fixed + ram_internal_switchable + high_ram; @@ -209,6 +212,11 @@ namespace gb std::memcpy((char*)&memory_[0], rom, size); } + unsigned int MBC::kilo(unsigned int n) const + { + return KILO_BYTE * n; + } + MBC::~MBC() { } From bef65af6dd65b28b9508d5201c3c28a7a926ab05 Mon Sep 17 00:00:00 2001 From: Natesh Narain Date: Sat, 26 Aug 2017 14:29:20 -0400 Subject: [PATCH 06/14] Added some logic to the hdma transfer code --- src/core/gpu.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 9a975901..8fe4f4bf 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -322,19 +322,24 @@ namespace gb { uint16_t src = WORD(mmu_->read(memorymap::HDMA1), mmu_->read(memorymap::HDMA2)) & 0xFFF0; uint16_t dest = WORD(mmu_->read(memorymap::HDMA3), mmu_->read(memorymap::HDMA4)) & 0xFFF0; - uint16_t length = ((value & 0x7F) + 1) * 0x10; + uint16_t length = (value & 0x7F) * 0x10; // check if the source and destination addresses are valid - if (((src >= 0x0000 && src <= 0x7FF0) || (src >= 0xA000 && src <= 0xDFF0)) && (dest >= 0x8000 && dest <= 0x9FF0)) + if (((src >= 0x0000 && src <= 0x7FF0) || (src >= 0xA000 && src <= 0xDFFF)) && (dest >= 0x8000 && dest <= 0x9FFF)) { - if (IS_BIT_CLR(value, 7)) + if (IS_BIT_CLR(value, 7) && !hdma_.transfer_active) { + // perform a general purpose DMA mmu_->dma(dest, src, length); + } + else if (IS_BIT_CLR(value, 7) && hdma_.transfer_active) + { // disable an active hdma transfer hdma_.transfer_active = false; } else { + // initialize an HDMA transfer hdma_.source = src; hdma_.destination = dest; hdma_.length = length; From 7ffc44399741806922e9e6c5e75a47ac9b234235 Mon Sep 17 00:00:00 2001 From: Natesh Narain Date: Sat, 26 Aug 2017 16:05:41 -0400 Subject: [PATCH 07/14] Fixed HDMA to alway transfer to VRAM --- src/core/gpu.cpp | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 8fe4f4bf..4127f250 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -321,30 +321,26 @@ namespace gb void hdma5WriteHandler(uint8_t value, uint16_t addr) { uint16_t src = WORD(mmu_->read(memorymap::HDMA1), mmu_->read(memorymap::HDMA2)) & 0xFFF0; - uint16_t dest = WORD(mmu_->read(memorymap::HDMA3), mmu_->read(memorymap::HDMA4)) & 0xFFF0; - uint16_t length = (value & 0x7F) * 0x10; + uint16_t dest = WORD(((mmu_->read(memorymap::HDMA3) & 0x1F) | 0x80), mmu_->read(memorymap::HDMA4)) & 0xFFF0; + uint16_t length = ((value & 0x7F) + 1) * 0x10; - // check if the source and destination addresses are valid - if (((src >= 0x0000 && src <= 0x7FF0) || (src >= 0xA000 && src <= 0xDFFF)) && (dest >= 0x8000 && dest <= 0x9FFF)) + if (IS_BIT_CLR(value, 7) && !hdma_.transfer_active) { - if (IS_BIT_CLR(value, 7) && !hdma_.transfer_active) - { - // perform a general purpose DMA - mmu_->dma(dest, src, length); - } - else if (IS_BIT_CLR(value, 7) && hdma_.transfer_active) - { - // disable an active hdma transfer - hdma_.transfer_active = false; - } - else - { - // initialize an HDMA transfer - hdma_.source = src; - hdma_.destination = dest; - hdma_.length = length; - hdma_.transfer_active = true; - } + // perform a general purpose DMA + mmu_->dma(dest, src, length); + } + else if (IS_BIT_CLR(value, 7) && hdma_.transfer_active) + { + // disable an active hdma transfer + hdma_.transfer_active = false; + } + else + { + // initialize an HDMA transfer + hdma_.source = src; + hdma_.destination = dest; + hdma_.length = length; + hdma_.transfer_active = true; } hdma5_ = value; From e9b311bb05568efdaa9950774982bb41e26dd4bf Mon Sep 17 00:00:00 2001 From: Natesh Narain Date: Sat, 26 Aug 2017 17:36:20 -0400 Subject: [PATCH 08/14] Draw sprite from its given character bank --- src/core/gpu.cpp | 5 +++-- src/core/tilemap.cpp | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 4127f250..bb4e7321 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -85,8 +85,6 @@ namespace gb // check if the HBLANK period is over if (hasElapsed(HBLANK_CYCLES)) { - // render the current scan line - renderScanline(); // update the scan line updateLY(); // check if LY matches LYC @@ -115,6 +113,9 @@ namespace gb case Mode::LCD: if (hasElapsed(LCD_TRANSFER_CYCLES)) { + // render the current scan line + renderScanline(); + mode_ = Mode::HBLANK; // perform an hdma transfer doHdma(); diff --git a/src/core/tilemap.cpp b/src/core/tilemap.cpp index 98fa37ba..ad61742d 100644 --- a/src/core/tilemap.cpp +++ b/src/core/tilemap.cpp @@ -160,7 +160,7 @@ namespace gb if (sprite.isVerticallyFlipped()) row = sprite.height - row - 1; - auto pixel_row = tileram_.getRow(row, sprite.tile, true); + auto pixel_row = tileram_.getRow(row, sprite.tile, true, sprite.getCharacterBank()); if (sprite.isHorizontallyFlipped()) std::reverse(pixel_row.begin(), pixel_row.end()); From e3f15a104b9ad2794fbce994c6d54ab5cd30f09f Mon Sep 17 00:00:00 2001 From: Natesh Narain Date: Sun, 27 Aug 2017 01:00:09 -0400 Subject: [PATCH 09/14] Clear apu registers --- src/core/apu.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/apu.cpp b/src/core/apu.cpp index 7f3600c3..933e9bed 100644 --- a/src/core/apu.cpp +++ b/src/core/apu.cpp @@ -293,6 +293,8 @@ namespace gb channel_left_enabled_[1] = (value & 0x20) != 0; channel_left_enabled_[2] = (value & 0x40) != 0; channel_left_enabled_[3] = (value & 0x80) != 0; + + apuWrite(value, addr); } else { @@ -334,9 +336,10 @@ namespace gb void clearRegisters() { - for (auto i = APU_REG_BASE; i < memorymap::WAVE_PATTERN_RAM_START; ++i) + for (auto addr = APU_REG_BASE; addr < memorymap::WAVE_PATTERN_RAM_START; ++addr) { - apuWrite(0, i); + if (addr == memorymap::NR52_REGISTER) continue; + mmu_->write((uint8_t)0, addr); } } From 906d950bd8b027b34bbdd4273ff2bc882df33ae2 Mon Sep 17 00:00:00 2001 From: Natesh Narain Date: Thu, 26 Jan 2017 23:42:09 -0500 Subject: [PATCH 10/14] added MBC5 --- CMakeLists.txt | 2 ++ include/gameboycore/mbc5.h | 24 ++++++++++++++++++++++++ src/core/mbc.cpp | 8 +++++++- src/core/mbc5.cpp | 36 ++++++++++++++++++++++++++++++++++++ src/core/mmu.cpp | 12 +++++++++++- 5 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 include/gameboycore/mbc5.h create mode 100644 src/core/mbc5.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 00006d7a..a0b08948 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,6 +58,7 @@ set(GAMEBOYCORE_HEADERS include/gameboycore/mbc1.h include/gameboycore/mbc2.h include/gameboycore/mbc3.h + include/gameboycore/mbc5.h include/gameboycore/rtc.h include/gameboycore/cartinfo.h include/gameboycore/opcodeinfo.h @@ -79,6 +80,7 @@ set(GAMEBOYCORE src/core/mbc1.cpp src/core/mbc2.cpp src/core/mbc3.cpp + src/core/mbc5.cpp src/core/alu.cpp src/core/cartinfo.cpp src/core/shiftrotate.cpp diff --git a/include/gameboycore/mbc5.h b/include/gameboycore/mbc5.h new file mode 100644 index 00000000..d147fffe --- /dev/null +++ b/include/gameboycore/mbc5.h @@ -0,0 +1,24 @@ +#ifndef GAMEBOYCORE_MBC5_H +#define GAMEBOYCORE_MBC5_H + +#include "gameboycore/mbc.h" + +namespace gb +{ + namespace detail + { + class MBC5 : public MBC + { + public: + + MBC5(uint8_t* rom, uint32_t size, uint8_t rom_size, uint8_t ram_size, bool cgb_enable); + ~MBC5(); + + void control(uint8_t value, uint16_t addr); + + private: + }; + } +} + +#endif // GAMEBOYCORE_MBC5_H diff --git a/src/core/mbc.cpp b/src/core/mbc.cpp index 49b9814d..66ba1f02 100644 --- a/src/core/mbc.cpp +++ b/src/core/mbc.cpp @@ -56,7 +56,13 @@ namespace gb return 0xFF; } - return memory_[getIndex(addr, rom_bank_, ram_bank_)]; + if (addr == 0x6435) + { + int x = 0; + } + + auto idx = getIndex(addr, rom_bank_, ram_bank_); + return memory_[idx]; } uint8_t MBC::readVram(uint16_t addr, uint8_t bank) diff --git a/src/core/mbc5.cpp b/src/core/mbc5.cpp new file mode 100644 index 00000000..280bc5da --- /dev/null +++ b/src/core/mbc5.cpp @@ -0,0 +1,36 @@ +#include "gameboycore/mbc5.h" + +namespace gb +{ + namespace detail + { + MBC5::MBC5(uint8_t* rom, uint32_t size, uint8_t rom_size, uint8_t ram_size, bool cgb_enable) : + MBC(rom, size, rom_size, ram_size, cgb_enable) + { + } + + void MBC5::control(uint8_t value, uint16_t addr) + { + if (addr >= 0x0000 && addr <= 0x1FFF) + { + xram_enable_ = ((value & 0x0F) == 0x0A); + } + else if (addr >= 0x2000 && addr <= 0x2FFF) + { + rom_bank_ |= ((rom_bank_ & ~(0xFF)) | value); + } + else if (addr >= 0x3000 && addr <= 0x3FFF) + { + rom_bank_ |= ((rom_bank_ & ~(0x100)) | ((value & 0xFFFF) << 9)); + } + else if (addr >= 0x4000 && addr <= 0x5FFF) + { + ram_bank_ = value & 0x0F; + } + } + + MBC5::~MBC5() + { + } + } +} \ No newline at end of file diff --git a/src/core/mmu.cpp b/src/core/mmu.cpp index 6a68048a..288cb031 100644 --- a/src/core/mmu.cpp +++ b/src/core/mmu.cpp @@ -8,6 +8,7 @@ #include "gameboycore/mbc1.h" #include "gameboycore/mbc2.h" #include "gameboycore/mbc3.h" +#include "gameboycore/mbc5.h" #include "gameboycore/cartinfo.h" #include "gameboycore/memorymap.h" @@ -67,7 +68,16 @@ namespace gb break; // TODO: MBC4 - // TODO: MBC5 + case detail::MBC::Type::MBC5: + case detail::MBC::Type::MBC5_RAM: + case detail::MBC::Type::MBC5_RAM_BAT: + case detail::MBC::Type::MBC5_RUMBLE: + case detail::MBC::Type::MBC5_RUMBLE_RAM: + case detail::MBC::Type::MBC5_RUMBLE_RAM_BAT: + mbc_.reset(new detail::MBC5(rom, size, header.rom_size, header.ram_size, cgb_enabled_)); + break; + + default: throw std::runtime_error("Unsupported cartridge type :("); From 83fd81f000f71174e56786df7d2e5d3b9ca18157 Mon Sep 17 00:00:00 2001 From: Natesh Narain Date: Mon, 28 Aug 2017 15:38:01 -0400 Subject: [PATCH 11/14] changed an exit code --- src/example/emulator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/example/emulator.cpp b/src/example/emulator.cpp index c0f6343b..4998e1f0 100644 --- a/src/example/emulator.cpp +++ b/src/example/emulator.cpp @@ -61,7 +61,7 @@ int main(int argc, char * argv[]) catch (std::runtime_error& e) { std::cerr << e.what() << std::endl; - return 1; + return 2; } // Exiting save battery RAM From 893a78152af8b0b564e5fa8302cce46b93dabe86 Mon Sep 17 00:00:00 2001 From: Natesh Narain Date: Tue, 29 Aug 2017 18:34:55 -0400 Subject: [PATCH 12/14] Added MBC5 --- include/gameboycore/mbc5.h | 4 ++++ src/core/cpu.cpp | 24 ++++++++++++++++++++++++ src/core/mbc.cpp | 5 ----- src/core/mbc5.cpp | 21 ++++++++++++++++++--- src/example/debug_window.h | 2 ++ 5 files changed, 48 insertions(+), 8 deletions(-) diff --git a/include/gameboycore/mbc5.h b/include/gameboycore/mbc5.h index d147fffe..96a4b8a3 100644 --- a/include/gameboycore/mbc5.h +++ b/include/gameboycore/mbc5.h @@ -17,6 +17,10 @@ namespace gb void control(uint8_t value, uint16_t addr); private: + void selectRomBank(uint8_t lo, uint8_t hi); + + uint8_t rom_bank_lower_bits_; + uint8_t rom_bank_upper_bit_; }; } } diff --git a/src/core/cpu.cpp b/src/core/cpu.cpp index 104346a0..03eb5d1d 100644 --- a/src/core/cpu.cpp +++ b/src/core/cpu.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "bitutil.h" #include "shiftrotate.h" @@ -2052,6 +2053,29 @@ namespace gb std::stringstream ss; ss << std::setfill('0') << std::setw(4) << std::uppercase << std::hex << addr << ": " << str; + const int spaces_before_registers = 13; + std::string padding(spaces_before_registers - std::strlen(str), ' '); + + // print debug info + std::printf("%04X: %s%s| PC: %04X, A: %02X, BC: %02X%02X, DE: %02X%02X, HL: %02X%02X | SP: %04X -> %04X | F: %02X | IF: %02X, IE: %02X\n", + userdata_addr - 1, + str, + padding.c_str(), + pc_.val, + af_.hi, + bc_.hi, + bc_.lo, + de_.hi, + de_.lo, + hl_.hi, + hl_.lo, + sp_.val, + WORD(mmu_->read(sp_.val + 1), mmu_->read(sp_.val)), + af_.lo, + interrupt_flags_, + interrupt_enable_ + ); + if (disassembly_callback_) disassembly_callback_(ss.str()); diff --git a/src/core/mbc.cpp b/src/core/mbc.cpp index 66ba1f02..ba0e20cd 100644 --- a/src/core/mbc.cpp +++ b/src/core/mbc.cpp @@ -56,11 +56,6 @@ namespace gb return 0xFF; } - if (addr == 0x6435) - { - int x = 0; - } - auto idx = getIndex(addr, rom_bank_, ram_bank_); return memory_[idx]; } diff --git a/src/core/mbc5.cpp b/src/core/mbc5.cpp index 280bc5da..ebf4f6b6 100644 --- a/src/core/mbc5.cpp +++ b/src/core/mbc5.cpp @@ -5,7 +5,9 @@ namespace gb namespace detail { MBC5::MBC5(uint8_t* rom, uint32_t size, uint8_t rom_size, uint8_t ram_size, bool cgb_enable) : - MBC(rom, size, rom_size, ram_size, cgb_enable) + MBC(rom, size, rom_size, ram_size, cgb_enable), + rom_bank_lower_bits_(0), + rom_bank_upper_bit_(0) { } @@ -13,22 +15,35 @@ namespace gb { if (addr >= 0x0000 && addr <= 0x1FFF) { + // enable / disable external ram xram_enable_ = ((value & 0x0F) == 0x0A); } else if (addr >= 0x2000 && addr <= 0x2FFF) { - rom_bank_ |= ((rom_bank_ & ~(0xFF)) | value); + // lower 8 bits of rom bank number + rom_bank_lower_bits_ = value; + selectRomBank(rom_bank_lower_bits_, rom_bank_upper_bit_); } else if (addr >= 0x3000 && addr <= 0x3FFF) { - rom_bank_ |= ((rom_bank_ & ~(0x100)) | ((value & 0xFFFF) << 9)); + // 9th bit of rom bank number + rom_bank_upper_bit_ = value; + selectRomBank(rom_bank_lower_bits_, rom_bank_upper_bit_); } else if (addr >= 0x4000 && addr <= 0x5FFF) { + // ram bank number ram_bank_ = value & 0x0F; } } + void MBC5::selectRomBank(uint8_t lo, uint8_t hi) + { + rom_bank_ = ((hi & 0x0001) << 8) | (lo & 0xFFFF); + + if (rom_bank_ > 0) rom_bank_ -= 1; + } + MBC5::~MBC5() { } diff --git a/src/example/debug_window.h b/src/example/debug_window.h index 35b0169e..00d9cbea 100644 --- a/src/example/debug_window.h +++ b/src/example/debug_window.h @@ -4,6 +4,8 @@ #include "imgui.h" #include "gameboycore/gameboycore.h" +#include + class DebugWindow { private: From fc3912e66b52c1596f8be27ba411d237d94dd36e Mon Sep 17 00:00:00 2001 From: Natesh Narain Date: Tue, 29 Aug 2017 21:00:16 -0400 Subject: [PATCH 13/14] added background priority to sraw sprites --- src/core/gpu.cpp | 3 ++- src/core/tilemap.cpp | 33 +++++++++++++++++++-------------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index bb4e7321..68a2f39f 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -203,8 +203,9 @@ namespace gb auto color_number = tileinfo & 0x03; auto color_palette = (tileinfo >> 2) & 0x07; + auto priority = (tileinfo >> 3); - color_line[pixel_idx] = color_number; + color_line[pixel_idx] = color_number | (priority << 2); if (cgb_enabled_) { diff --git a/src/core/tilemap.cpp b/src/core/tilemap.cpp index ad61742d..7ac4e03c 100644 --- a/src/core/tilemap.cpp +++ b/src/core/tilemap.cpp @@ -56,11 +56,11 @@ namespace gb const auto tileattr = mmu_.readVram(tile_offset, 1); // extract tile attributes - const auto palette_number = (cgb_enable) ? (tileattr & 0x07) : 0; - const auto character_bank = (cgb_enable) ? ((tileattr >> 3) & 0x01) : 0; - const auto flip_horizontal = (cgb_enable && (tileattr & 0x20) != 0); - const auto flip_vertical = (cgb_enable && (tileattr & 0x40) != 0); - // TODO: Display priority + const auto palette_number = (cgb_enable) ? (tileattr & 0x07) : 0; + const auto character_bank = (cgb_enable) ? ((tileattr >> 3) & 0x01) : 0; + const auto flip_horizontal = (cgb_enable && (tileattr & 0x20) != 0); + const auto flip_vertical = (cgb_enable && (tileattr & 0x40) != 0); + const auto backgroud_priority = (cgb_enable && (tileattr & 0x80) != 0); if (flip_vertical) pixel_row = tile_height - pixel_row - 1; @@ -79,7 +79,7 @@ namespace gb for (auto i = 0u; i < row.size(); ++i) { if (pixel_col >= scx && pixel_col <= scx + 160 && idx < 160) - tileline[idx++] = row[i] | (palette_number << 2); + tileline[idx++] = row[i] | (palette_number << 2) | (backgroud_priority << 3); pixel_col++; } @@ -122,7 +122,7 @@ namespace gb void TileMap::drawSprites( std::array& scanline, - std::array& color_line, + std::array& info, int line, bool cgb_enable, std::array, 8>& cgb_palette) @@ -160,10 +160,10 @@ namespace gb if (sprite.isVerticallyFlipped()) row = sprite.height - row - 1; - auto pixel_row = tileram_.getRow(row, sprite.tile, true, sprite.getCharacterBank()); + auto sprite_line = tileram_.getRow(row, sprite.tile, true, sprite.getCharacterBank()); if (sprite.isHorizontallyFlipped()) - std::reverse(pixel_row.begin(), pixel_row.end()); + std::reverse(sprite_line.begin(), sprite_line.end()); // get color palette for this sprite @@ -180,19 +180,24 @@ namespace gb for (auto i = 0; i < 8; ++i) { + // skip this pixel if outside the window if ((x + i) < 0 || (x + i) >= 160) continue; + auto color = info[x + i] & 0x03; + auto background_priority = (bool)(info[x + i] >> 2); + if (sprite.hasPriority()) { - if (pixel_row[i] != 0) - scanline[x + i] = palette[pixel_row[i]]; + if (sprite_line[i] != 0) + scanline[x + i] = palette[sprite_line[i]]; } else { - // if priority is to th background the sprite is behind colors 1-3 - if (color_line[x + i] == 0 && pixel_row[i] != 0) - scanline[x + i] = palette[pixel_row[i]]; + // if priority is to the background the sprite is behind colors 1-3 + if (color == 0 && sprite_line[i] != 0) + scanline[x + i] = palette[sprite_line[i]]; } + } count++; From 8dcd27c1abfba3de7678d5371af8957082fc43d3 Mon Sep 17 00:00:00 2001 From: Natesh Narain Date: Tue, 29 Aug 2017 21:12:32 -0400 Subject: [PATCH 14/14] Added obj display priority --- src/core/gpu.cpp | 2 +- src/core/tilemap.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 68a2f39f..9c7c1300 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -203,7 +203,7 @@ namespace gb auto color_number = tileinfo & 0x03; auto color_palette = (tileinfo >> 2) & 0x07; - auto priority = (tileinfo >> 3); + auto priority = (tileinfo >> 5); color_line[pixel_idx] = color_number | (priority << 2); diff --git a/src/core/tilemap.cpp b/src/core/tilemap.cpp index 7ac4e03c..6a0ec8d0 100644 --- a/src/core/tilemap.cpp +++ b/src/core/tilemap.cpp @@ -79,7 +79,7 @@ namespace gb for (auto i = 0u; i < row.size(); ++i) { if (pixel_col >= scx && pixel_col <= scx + 160 && idx < 160) - tileline[idx++] = row[i] | (palette_number << 2) | (backgroud_priority << 3); + tileline[idx++] = row[i] | (palette_number << 2) | (backgroud_priority << 5); pixel_col++; } @@ -188,7 +188,7 @@ namespace gb if (sprite.hasPriority()) { - if (sprite_line[i] != 0) + if (sprite_line[i] != 0 && !background_priority) scanline[x + i] = palette[sprite_line[i]]; } else