Skip to content

Commit

Permalink
Improve PPU rendering by accessing VRAM and OAM directly (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
kremi151 committed Aug 15, 2020
1 parent f7ab4f3 commit cc5cb43
Show file tree
Hide file tree
Showing 6 changed files with 33 additions and 26 deletions.
2 changes: 1 addition & 1 deletion core/source/emulator/emulator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Emulator::Emulator(GameBoyType gbType, const Controller::ControllersPtr& control
, ppuMemory()
, memory(new Memory(cartridge, controllers, ioRegisters, ppuMemory))
, cpu(std::make_shared<CPU>(gbType, memory, ioRegisters))
, ppu(memory, cpu, controllers, ioRegisters, ppuMemory)
, ppu(cpu, controllers, ioRegisters, ppuMemory)
{
}

Expand Down
43 changes: 22 additions & 21 deletions core/source/emulator/ppu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,29 +19,30 @@
#include <util/return_codes.h>
#include <emulator/io_registers.h>

#define FB_TILE_DATA_OBJ 0x8000
// Lower tile set lives at 0x8000 in memory
#define FB_TILE_DATA_LOWER 0x0000

// Should be 0x8800, but as tiles are numbered from -127 to 128, we take 0x9000 as the offset
#define FB_TILE_DATA_UPPER 0x9000
// Lower tile set lives at 0x8800 in memory.
// As tiles are numbered from -127 to 128, we take 0x9000 as the offset.
#define FB_TILE_DATA_UPPER 0x1000

#define __fb_lcdc_isOn(lcdc) (lcdc & 0b10000000u)
// Differences here are 1023
#define __fb_lcdc_windowTileMapDisplaySelect(lcdc) ((lcdc & 0b01000000u) ? 0x9C00 : 0x9800)
#define __fb_lcdc_windowTileMapDisplaySelect(lcdc) ((lcdc & 0b01000000u) ? 0x1C00 : 0x1800)
#define __fb_lcdc_isWindowEnabled(lcdc) (lcdc & 0b00100000u)
// Differences here are 4095
#define __fb_lcdc_bgAndWindowTileDataSelect(lcdc) ((lcdc & 0b00010000u) ? FB_TILE_DATA_OBJ : FB_TILE_DATA_UPPER)
#define __fb_lcdc_bgAndWindowTileDataSelect(lcdc) ((lcdc & 0b00010000u) ? FB_TILE_DATA_LOWER : FB_TILE_DATA_UPPER)
// Differences here are 1023
#define __fb_lcdc_bgTileMapDisplaySelect(lcdc) ((lcdc & 0b00001000u) ? 0x9C00 : 0x9800)
#define __fb_lcdc_bgTileMapDisplaySelect(lcdc) ((lcdc & 0b00001000u) ? 0x1C00 : 0x1800)
// Returns the height of objects (16 or 8), width is always 8
#define __fb_lcdc_objSpriteSize(lcdc) ((lcdc & 0b00000100u) ? 16 : 8)
#define __fb_lcdc_objEnabled(lcdc) (lcdc & 0b00000010u)
#define __fb_lcdc_isBGEnabled(lcdc) (lcdc & 0b00000001u)

using namespace FunkyBoy;

PPU::PPU(FunkyBoy::MemoryPtr memory, CPUPtr cpu, Controller::ControllersPtr controllers, const io_registers& ioRegisters, const PPUMemory &ppuMemory)
: memory(std::move(memory))
, cpu(std::move(cpu))
PPU::PPU(CPUPtr cpu, Controller::ControllersPtr controllers, const io_registers& ioRegisters, const PPUMemory &ppuMemory)
: cpu(std::move(cpu))
, controllers(std::move(controllers))
, ioRegisters(ioRegisters)
, ppuMemory(ppuMemory)
Expand Down Expand Up @@ -155,17 +156,17 @@ void PPU::renderScanline(u8 ly) {
memory_address tileMapAddr = __fb_lcdc_bgTileMapDisplaySelect(lcdc);
tileMapAddr += ((y & 255u) / 8) * 32;
palette = ioRegisters.getBGP();
tile = memory->read8BitsAt(tileMapAddr + tileOffsetX);
tile = ppuMemory.getVRAMByte(tileMapAddr + tileOffsetX);
u8 &scanLineX = it; // alias for it
for (scanLineX = 0 ; scanLineX < FB_GB_DISPLAY_WIDTH ; scanLineX++) {
tileLine = memory->read16BitsAt(tileSetAddr + (tile * 16) + (yInTile * 2));
tileLine = ppuMemory.readVRAM16Bits(tileSetAddr + (tile * 16) + (yInTile * 2));
colorIndex = (tileLine >> (15 - (xInTile & 7u))) & 1u;
colorIndex |= ((tileLine >> (7 - (xInTile & 7u))) & 1u) << 1;
scanLineBuffer[scanLineX] = (palette >> (colorIndex * 2u)) & 3u;
if (++xInTile >= 8) {
xInTile = 0;
tileOffsetX = (tileOffsetX + 1) & 31;
tile = memory->read8BitsAt(tileMapAddr + tileOffsetX);
tile = ppuMemory.getVRAMByte(tileMapAddr + tileOffsetX);
}
}
} else {
Expand All @@ -179,16 +180,16 @@ void PPU::renderScanline(u8 ly) {
const u8 objPalette0 = ioRegisters.getOBP0();
const u8 objPalette1 = ioRegisters.getOBP1();
const u8 objHeight = __fb_lcdc_objSpriteSize(lcdc);
memory_address objAddr = 0xFE00;
memory_address objAddr = 0x0000; // 0x0000 ~> 0xFE00 (start of OAM)
u8 objY, objX, objFlags;
bool hide, flipX, flipY;
u8 yInObj;
u8 &objIdx = it; // alias for it
for (objIdx = 0 ; objIdx < 40 ; objIdx++) {
objY = memory->read8BitsAt(objAddr++) - 16;
objX = memory->read8BitsAt(objAddr++);
tile = memory->read8BitsAt(objAddr++);
objFlags = memory->read8BitsAt(objAddr++);
objY = ppuMemory.getOAMByte(objAddr++) - 16;
objX = ppuMemory.getOAMByte(objAddr++);
tile = ppuMemory.getOAMByte(objAddr++);
objFlags = ppuMemory.getOAMByte(objAddr++);
if (ly < objY || ly >= objY + objHeight) {
continue;
}
Expand All @@ -206,7 +207,7 @@ void PPU::renderScanline(u8 ly) {
// In 8x16 mode, the least significant bit of the tile number is treated as '0'
tile &= 0b11111110u;
}
tileLine = memory->read16BitsAt(FB_TILE_DATA_OBJ + (tile * 16) + (yInObj * 2));
tileLine = ppuMemory.readVRAM16Bits(FB_TILE_DATA_LOWER + (tile * 16) + (yInObj * 2));
for (u8 xOnObj = 0 ; xOnObj < 8 ; xOnObj++) {
u8 x = objX + xOnObj - 8;
if (x >= FB_GB_DISPLAY_WIDTH) {
Expand Down Expand Up @@ -239,17 +240,17 @@ void PPU::renderScanline(u8 ly) {
memory_address tileMapAddr = __fb_lcdc_windowTileMapDisplaySelect(lcdc);
tileMapAddr += ((y & 255u) / 8) * 32;
palette = ioRegisters.getBGP();
tile = memory->read8BitsAt(tileMapAddr + tileOffsetX);
tile = ppuMemory.getVRAMByte(tileMapAddr + tileOffsetX);
u8 &scanLineX = it; // alias for it
for (scanLineX = wx ; scanLineX < FB_GB_DISPLAY_WIDTH ; scanLineX++) {
tileLine = memory->read16BitsAt(tileSetAddr + (tile * 16) + (yInTile * 2));
tileLine = ppuMemory.readVRAM16Bits(tileSetAddr + (tile * 16) + (yInTile * 2));
colorIndex = (tileLine >> (15 - (xInTile & 7u))) & 1u;
colorIndex |= ((tileLine >> (7 - (xInTile & 7u))) & 1u) << 1;
scanLineBuffer[scanLineX] = (palette >> (colorIndex * 2u)) & 3u;
if (++xInTile >= 8) {
xInTile = 0;
tileOffsetX = (tileOffsetX + 1) & 31;
tile = memory->read8BitsAt(tileMapAddr + tileOffsetX);
tile = ppuMemory.getVRAMByte(tileMapAddr + tileOffsetX);
}
}
}
Expand Down
4 changes: 1 addition & 3 deletions core/source/emulator/ppu.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
#ifndef FB_CORE_PPU_H
#define FB_CORE_PPU_H

#include <memory/memory.h>
#include <controllers/controllers.h>
#include <emulator/cpu.h>
#include <emulator/io_registers.h>
Expand All @@ -29,7 +28,6 @@ namespace FunkyBoy {

class PPU {
private:
MemoryPtr memory;
CPUPtr cpu;
Controller::ControllersPtr controllers;
io_registers ioRegisters;
Expand All @@ -43,7 +41,7 @@ namespace FunkyBoy {

void renderScanline(u8 ly);
public:
PPU(MemoryPtr memory, CPUPtr cpu, Controller::ControllersPtr controllers, const io_registers& ioRegisters, const PPUMemory &ppuMemory);
PPU(CPUPtr cpu, Controller::ControllersPtr controllers, const io_registers& ioRegisters, const PPUMemory &ppuMemory);
~PPU();

ret_code doClocks(u8 clocks);
Expand Down
6 changes: 6 additions & 0 deletions core/source/memory/ppu_memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

#include "ppu_memory.h"

#include <util/endianness.h>

using namespace FunkyBoy;

PPUMemory::PPUMemory()
Expand Down Expand Up @@ -45,6 +47,10 @@ u8 &PPUMemory::getVRAMByte(memory_address vramOffset) {
return *(vram + vramOffset);
}

u16 PPUMemory::readVRAM16Bits(memory_address vramOffset) {
return Util::compose16Bits(*(vram + vramOffset), *(vram + vramOffset + 1));
}

u8 &PPUMemory::getOAMByte(memory_address oamOffset) {
return *(oam + oamOffset);
}
2 changes: 2 additions & 0 deletions core/source/memory/ppu_memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ namespace FunkyBoy {
PPUMemory &operator=(const PPUMemory &other) = delete;

u8 &getVRAMByte(memory_address vramOffset);
u16 readVRAM16Bits(memory_address vramOffset);

u8 &getOAMByte(memory_address oamOffset);
};

Expand Down
2 changes: 1 addition & 1 deletion platform-3ds/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
This is the implementation of FunkyBoy for the Nintendo 3DS.

This is more like a proof of concept of the porability of FunkyBoy. \
The emulation is currently extremely slow (about 7-8 FPS), mainly due to the fact that the code is not optimized enough for the 3DS' limited resources. \
The emulation is currently extremely slow (about ~10 FPS), mainly due to the fact that the code is not optimized enough for the 3DS' limited resources. \
At a later stage of development, I might consider actively working on improving the emulator's performance on the 3DS.

## Build
Expand Down

0 comments on commit cc5cb43

Please sign in to comment.