From f42a2bbb4048fe60b875379d71567676486d7851 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Tue, 21 Mar 2023 22:56:02 +0530 Subject: [PATCH 01/34] Render Background --- src/CMakeLists.txt | 2 + src/cpu.cpp | 2 +- src/cpu.h | 6 ++ src/gameBoy.cpp | 16 ++++- src/gameBoy.h | 4 ++ src/graphics.cpp | 143 +++++++++++++++++++++++++++++++++++++++++++++ src/graphics.h | 46 +++++++++++++++ src/main.cpp | 5 +- src/mmap.cpp | 14 ++++- src/mmap.h | 28 +++++++++ src/types.h | 1 + 11 files changed, 259 insertions(+), 8 deletions(-) create mode 100644 src/graphics.cpp create mode 100644 src/graphics.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8da94ae..7784932 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,6 +4,7 @@ set(SOURCES cpu.cpp gameBoy.cpp mmap.cpp + graphics.cpp # ------- # Header Files @@ -11,6 +12,7 @@ set(SOURCES gameBoy.h mmap.h types.h + graphics.h ) target_sources(${PROJECT_NAME} PRIVATE ${SOURCES}) diff --git a/src/cpu.cpp b/src/cpu.cpp index cb483b7..aef7d51 100644 --- a/src/cpu.cpp +++ b/src/cpu.cpp @@ -3980,7 +3980,7 @@ int CPU::executeNextInstruction() // Turn off logging // If reached infinite loop - if (reg_PC.dat == 0xCC62) + if (reg_PC.dat > 0xCC62) { fclose(outfile); } diff --git a/src/cpu.h b/src/cpu.h index 7538054..7b9119a 100644 --- a/src/cpu.h +++ b/src/cpu.h @@ -1,6 +1,7 @@ #pragma once #include "types.h" #include "mmap.h" +#include "graphics.h" // CPU Register // Pulled from https://gbdev.io/pandocs/CPU_Registers_and_Flags.html @@ -104,6 +105,8 @@ class CPU // Memory Map MemoryMap* mMap; + PPU* ppu; + // ISA // Pulled from https://izik1.github.io/gbops/index.html typedef int (CPU::*method_function)(); @@ -1144,6 +1147,9 @@ class CPU // set the memory map void setMemory(MemoryMap* memory) { mMap = memory; } + // set the PPU + void setPPU(PPU* ppu_arg) { ppu = ppu_arg; } + // set the Accumulator void set_reg_A(Byte value) { reg_AF.hi = value; } diff --git a/src/gameBoy.cpp b/src/gameBoy.cpp index b3e52c7..97a440c 100644 --- a/src/gameBoy.cpp +++ b/src/gameBoy.cpp @@ -12,15 +12,26 @@ GBE::GBE() // Initialize the MemoryMap gbe_mMap = new MemoryMap(); + // Initialize the Graphics + gbe_graphics = new PPU(); + // Unify the CPU and MemoryMap gbe_cpu->setMemory(gbe_mMap); + // Unify the CPU and PPU + gbe_cpu->setPPU(gbe_graphics); + + // Unify the PPU and MmeoryMap + gbe_graphics->setMemoryMap(gbe_mMap); + + gbe_graphics->init(); + // Open the Boot ROM if ((bootROM = fopen("../src/dmg_boot.gb", "rb")) == NULL) printf("boot rom file not opened"); // Open the Game ROM - if ((gameROM = fopen("../tests/instr_timing.gb", "rb")) == NULL) + if ((gameROM = fopen("../tests/cpu_instrs/cpu_instrs.gb", "rb")) == NULL) printf("game rom file not opened"); // Load the Boot ROM @@ -555,15 +566,16 @@ void GBE::update() s_Cycles += gbe_cpu->executeNextInstruction(); if ((*gbe_mMap)[0xFF02] == 0x81) { + gbe_graphics->load(); printf("%c", (*gbe_mMap)[0xFF01]); gbe_mMap->writeMemory(0xFF02, 0x00); } // update the DIV and TIMA timers gbe_cpu->updateTimers(s_Cycles); - // updateGraphics() s_Cycles = 0; s_Cycles += gbe_cpu->performInterrupt(); + gbe_graphics->pollEvents(); } // renderGraphics() } diff --git a/src/gameBoy.h b/src/gameBoy.h index d785049..35c22f8 100644 --- a/src/gameBoy.h +++ b/src/gameBoy.h @@ -2,6 +2,7 @@ #include "types.h" #include "cpu.h" #include "mmap.h" +#include "graphics.h" // GBE stands for GameBoyEmulator @@ -24,6 +25,9 @@ class GBE // Pointer to the MemoryMap MemoryMap* gbe_mMap; + // Pointer to the Graphics + PPU* gbe_graphics; + // File pointer for Boot ROM FILE* bootROM; diff --git a/src/graphics.cpp b/src/graphics.cpp new file mode 100644 index 0000000..889cf15 --- /dev/null +++ b/src/graphics.cpp @@ -0,0 +1,143 @@ +#include "types.h" +#include "graphics.h" + +PPU::PPU() +{ + // Initialize members + window = nullptr; + renderer = nullptr; + texture = nullptr; + isEnabled = false; + event = new SDL_Event(); + + // Fill renderArray initially with white (lightest color in palette + std::fill(renderArray, renderArray + (256 * 256), bg_colors[0]); + + // Copy the same array as a null array (or flush array) + std::copy(renderArray, renderArray + (256 * 256), nullArray); +} + +bool PPU::init() +{ + // Initialize SDL + if (SDL_Init(SDL_INIT_VIDEO) < 0) + { + printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError()); + return false; + } + + // Set hint to use hardware acceleration + if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1")) + { + printf("Hardware Acceleration not enabled! SDL_Error: %s\n", SDL_GetError()); + return false; + } + + // Set hint for VSync + if (!SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1")) + { + printf("VSync not enabled! SDL_Error: %s\n", SDL_GetError()); + return false; + } + + // Create window and renderer + if (!(window = SDL_CreateWindow("GameBoy Emulator", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 256, 256, SDL_WINDOW_SHOWN))) + { + printf("Window could not be created! SDL_Error: %s\n", SDL_GetError()); + return false; + } + + if (!(renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC))) + { + printf("Renderer could not be created! SDL_Error: %s\n", SDL_GetError()); + return false; + } + + // Evaluate LCDC register + Byte LCDC = mMap->getRegLCDC(); + + isEnabled = (LCDC & 0x80); + bgTileMapAddr = (LCDC & 0x04) ? 0x9C00 : 0x9800; + bgTileDataAddr = (LCDC & 0x08) ? 0x8000 : 0x8800; + + // Evaluate Background Palette register + bgPalette = mMap->getRegBGP(); + + // Create a placeholder texture + texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC, 160, 144); + + // Render the texture + SDL_UpdateTexture(texture, NULL, renderArray, 160 * 4); + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, texture, NULL, NULL); + SDL_RenderPresent(renderer); + return true; +} + +// Poll Events to check for inputs +// And process them +bool PPU::pollEvents() +{ + while (SDL_PollEvent(&(*event))) + { + if (event->type == SDL_KEYDOWN) + { + printf("Key pressed: %c\n", event->key.keysym.sym); + if (event->key.keysym.sym == SDLK_ESCAPE) + exit(0); + } + } + return false; +} + +void PPU::load() +{ + // Read background palette register + bgPalette = mMap->getRegBGP(); + + Word tilenum; + Byte pixelCol; + + // Filling pixel array + // I am sorry, this is a lot for me to explain + // I will break it down later + // Till then, you can attempt to understand this, or give up + // I suggest you give up + for (int i = 0; i < 256; i++) + { + for (int j = 0; j < 256; j++) + { + tilenum = (*mMap)[bgTileMapAddr + ((i / 8) * 32) + (j / 8)]; + if (bgTileDataAddr == 0x8800) + { + pixelCol = ((*mMap)[bgTileDataAddr + (tilenum * 0x10) + (i % 8 * 2)] >> (7 - (j % 8)) & 0x1) + ((*mMap)[bgTileDataAddr + (tilenum * 0x10) + (i % 8 * 2) + 1] >> (7 - (j % 8)) & 0x1) * 2; + } + else + { + pixelCol = ((*mMap)[bgTileDataAddr + 0x800 + ((SWord)tilenum * 0x10) + (i % 8 * 2)] >> (7 - (j % 8)) & 0x1) + (((*mMap)[bgTileDataAddr + 0x800 + ((SWord)tilenum * 0x10) + (i % 8 * 2) + 1] >> (7 - (j % 8)) & 0x1) * 2); + } + + renderArray[i * 256 + j] = bg_colors[(bgPalette >> (pixelCol * 2)) & 0x3]; + } + } + + SDL_UpdateTexture(texture, NULL, renderArray, 256 * 4); + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, texture, NULL, NULL); + SDL_RenderPresent(renderer); +} + +void PPU::close() +{ + // Destroy texture + SDL_DestroyTexture(texture); + + // Destroy renderer + SDL_DestroyRenderer(renderer); + + // Destroy window + SDL_DestroyWindow(window); + + // Quit SDL subsystems + SDL_Quit(); +} \ No newline at end of file diff --git a/src/graphics.h b/src/graphics.h new file mode 100644 index 0000000..03751ca --- /dev/null +++ b/src/graphics.h @@ -0,0 +1,46 @@ +#pragma once +#include "types.h" +#include "mmap.h" +#include +#include +#include + +class PPU +{ +private: + SDL_Window* window; + SDL_Renderer* renderer; + SDL_Texture* texture; + SDL_Event* event; + color renderArray[256 * 256]; + color nullArray[256 * 256]; + + MemoryMap* mMap; + bool isEnabled; + Word bgTileMapAddr; + Word bgTileDataAddr; + Byte bgPalette; + + // The GameBoy screen + // 160x144 screen resolution withing a 256x224 border + // The original GameBoy supported 4 colors + // Pulled from https://gbdev.io/pandocs/Specifications.html + const int SCREEN_WIDTH = 160; + const int SCREEN_HEIGHT = 144; + + // Color Mapping for background + color bg_colors[4] = { 0x9BBC0FFF, 0x8BAC0FFF, 0x306230FF, 0x0F380FFF }; + + // Color Mapping for objects + // NOTE: 0 is transparent + // indices 1, 2, 3 are the actual colors and will be populated later + color obj_colors[4] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; + +public: + PPU(); + bool init(); + bool pollEvents(); + void load(); + void close(); + void setMemoryMap(MemoryMap* m) { mMap = m; } +}; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 0fdf031..e29a31a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,10 +1,7 @@ -#include #include "gameBoy.h" -int main() +int main(int argv, char** argc) { - std::cout << "Hello World!"; - GBE* gbe = new GBE(); return 0; diff --git a/src/mmap.cpp b/src/mmap.cpp index d0f4731..aa708e1 100644 --- a/src/mmap.cpp +++ b/src/mmap.cpp @@ -64,6 +64,18 @@ MemoryMap::MemoryMap() // IF at 0xFF0F reg_IF = ioPorts + 0x0F; + + // LCDC at 0xFF40 + reg_LCDC = ioPorts + 0x40; + + // SCX at 0xFF43 + reg_SCX = ioPorts + 0x43; + + // SCY at 0xFF42 + reg_SCY = ioPorts + 0x42; + + // BGP at 0xFF47 + reg_BGP = ioPorts + 0x47; } // Write to memory @@ -72,7 +84,7 @@ bool MemoryMap::writeMemory(Word address, Byte value) { if (address < 0x8000) { - printf("Writing to ROM is not allowed"); + printf("Writing to ROM is not allowed! Write attempted at %04X", address); return false; } else if (address < 0xA000) diff --git a/src/mmap.h b/src/mmap.h index c04790e..9149895 100644 --- a/src/mmap.h +++ b/src/mmap.h @@ -88,6 +88,22 @@ class MemoryMap // Signals which interrupt must take place Byte* reg_IF; + // The LCD Control Register + // Stays in the I/O Ports at 0xFF40 + Byte* reg_LCDC; + + // The SCX Register + // Stays in the I/O Ports at 0xFF43 + Byte* reg_SCX; + + // The SCY Register + // Stays in the I/O Ports at 0xFF42 + Byte* reg_SCY; + + // The BGP Register + // Stays in the I/O Ports at 0xFF47 + Byte* reg_BGP; + public: // Constructor MemoryMap(); @@ -153,6 +169,18 @@ class MemoryMap // gets the reg_IE Byte getRegIE() { return *interruptEnableRegister; } + // gets the reg_LCDC + Byte getRegLCDC() { return *reg_LCDC; } + + // gets the reg_SCX + Byte getRegSCX() { return *reg_SCX; } + + // gets the reg_SCY + Byte getRegSCY() { return *reg_SCY; } + + // gets the reg_BGP + Byte getRegBGP() { return *reg_BGP; } + // sets the reg_TIMA void setRegTIMA(Byte value) { *reg_TIMA = value; } diff --git a/src/types.h b/src/types.h index 097a7ac..edd42d2 100644 --- a/src/types.h +++ b/src/types.h @@ -7,3 +7,4 @@ typedef unsigned char Byte; typedef char SByte; typedef unsigned short Word; typedef signed short SWord; +typedef unsigned int color; \ No newline at end of file From 95fe9f714cdb620d3662fbf16538ccbefd4f9af3 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 22 Mar 2023 19:26:11 +0530 Subject: [PATCH 02/34] Add explanation for rendering --- src/graphics.cpp | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/graphics.cpp b/src/graphics.cpp index 889cf15..ab75651 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -99,10 +99,31 @@ void PPU::load() Byte pixelCol; // Filling pixel array - // I am sorry, this is a lot for me to explain - // I will break it down later - // Till then, you can attempt to understand this, or give up - // I suggest you give up + // Going over each pixel on screen + // And filling it with the correct color + // If LCDC.4 is set, then the background tile map uses $8000 method and unsigned addressing + // If LCDC.4 is not set, then the background tile map uses $8800 method and signed addressing + // Each tile has 8x8 pixels, and each pixel has a color ID of 0 to 3 + // Each tile occupies 16 bytes, where each line is represented by 2 bytes + // For each line, the 1st byte specifies the LSB of the color ID of each pixel, and the 2nd byte specifies the MSB. + // The color numbers are translated into gray shades depending on the current palette + + // First we calculate the tile number of the tile that the pixel is in + // To do that, we divide the pixel's x and y coordinates by 8 (floor division) + // Then we multiply the resultant y by 32 (the number of tiles in a row) (256 pixels / 8 pixels per tile = 32 tiles) + // Then we add the resultant x which gives us the tile number we must check for data + // Here i is y and j is x + + // Now, using the tile number, we can calculate the address of the tile data by multiplying the tile number by 16 and adding it to the tile data address + // The tile data address is either 0x8000 or 0x8800 depending on LCDC.4 for Background + // If the tile data address is 0x8000, then the tile number is unsigned, else signed at 0x8800 + // Now, depending on the row of pixel, we find which 2 bytes of data to use out of the 16 in pixel data, by taking remainder from 8 and choosing the pair of bytes + // Then, we find the color ID of the pixel by taking the bit at the position of the pixel in the row (7 - (j % 8)) and shifting it to the LSB for both the bytes in the pair + // 7-(j % 8) will give us the 7th bit for j = 0, 6th bit for j = 1, and so on, and the last bit for j = 8. Exactly the bit we need from both bytes in pair + // Adding these LSBs will give us the pixel color we want + + // Source: https://gbdev.io/pandocs/Tile_Data.html + for (int i = 0; i < 256; i++) { for (int j = 0; j < 256; j++) From 6d9a99ed9b866f31ce5694e127ee07bfc4afcc2f Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Fri, 7 Apr 2023 04:27:38 +0530 Subject: [PATCH 03/34] Enable scrolling of window --- src/gameBoy.cpp | 9 +++++++++ src/graphics.cpp | 47 ++++++++++++++++++++++++++++++++++------------- src/graphics.h | 36 +++++++++++++++++++++++++++++++++--- 3 files changed, 76 insertions(+), 16 deletions(-) diff --git a/src/gameBoy.cpp b/src/gameBoy.cpp index 97a440c..ea87f27 100644 --- a/src/gameBoy.cpp +++ b/src/gameBoy.cpp @@ -585,6 +585,15 @@ void GBE::executeBootROM() while (gbe_mMap->readMemory(0xFF50) == 0x00) { s_Cycles += gbe_cpu->executeNextInstruction(); + if ((*gbe_mMap)[0xFF02] == 0x81) + { + gbe_graphics->load(); + printf("%c", (*gbe_mMap)[0xFF01]); + gbe_mMap->writeMemory(0xFF02, 0x00); + } + gbe_cpu->updateTimers(s_Cycles); + s_Cycles = 0; + s_Cycles += gbe_cpu->performInterrupt(); } // Overwrite the boot ROM with first 256 bytes of game ROM diff --git a/src/graphics.cpp b/src/graphics.cpp index ab75651..2b84730 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -8,13 +8,18 @@ PPU::PPU() renderer = nullptr; texture = nullptr; isEnabled = false; + mMap = nullptr; + bgTileDataAddr = 0x0000; + bgTileMapAddr = 0x0000; + bgPalette = 0x00; + currentLine = 0x00; + ppuMode = 0x02; event = new SDL_Event(); + source = new SDL_Rect({ 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT }); + dest = new SDL_Rect({ 0, 0, SCREEN_WIDTH * 2, SCREEN_HEIGHT * 2 }); - // Fill renderArray initially with white (lightest color in palette - std::fill(renderArray, renderArray + (256 * 256), bg_colors[0]); - - // Copy the same array as a null array (or flush array) - std::copy(renderArray, renderArray + (256 * 256), nullArray); + // Fill renderArray initially with white (lightest color in palette) + std::fill(renderArray, renderArray + (256 * 256 * 4), bg_colors[0]); } bool PPU::init() @@ -41,7 +46,7 @@ bool PPU::init() } // Create window and renderer - if (!(window = SDL_CreateWindow("GameBoy Emulator", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 256, 256, SDL_WINDOW_SHOWN))) + if (!(window = SDL_CreateWindow("GameBoy Emulator", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH * 2, SCREEN_HEIGHT * 2, SDL_WINDOW_SHOWN))) { printf("Window could not be created! SDL_Error: %s\n", SDL_GetError()); return false; @@ -64,12 +69,13 @@ bool PPU::init() bgPalette = mMap->getRegBGP(); // Create a placeholder texture - texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC, 160, 144); + // 512x512 to have 4 copies of tilemap + texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC, 256 * 2, 256 * 2); // Render the texture - SDL_UpdateTexture(texture, NULL, renderArray, 160 * 4); + SDL_UpdateTexture(texture, NULL, renderArray, 512 * 4); SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, texture, NULL, NULL); + SDL_RenderCopy(renderer, texture, source, dest); SDL_RenderPresent(renderer); return true; } @@ -78,10 +84,11 @@ bool PPU::init() // And process them bool PPU::pollEvents() { - while (SDL_PollEvent(&(*event))) + while (SDL_PollEvent(event)) { if (event->type == SDL_KEYDOWN) { + mMap->writeMemory(0xFF02, 0x81); printf("Key pressed: %c\n", event->key.keysym.sym); if (event->key.keysym.sym == SDLK_ESCAPE) exit(0); @@ -138,14 +145,28 @@ void PPU::load() pixelCol = ((*mMap)[bgTileDataAddr + 0x800 + ((SWord)tilenum * 0x10) + (i % 8 * 2)] >> (7 - (j % 8)) & 0x1) + (((*mMap)[bgTileDataAddr + 0x800 + ((SWord)tilenum * 0x10) + (i % 8 * 2) + 1] >> (7 - (j % 8)) & 0x1) * 2); } - renderArray[i * 256 + j] = bg_colors[(bgPalette >> (pixelCol * 2)) & 0x3]; + renderArray[(i * 512) + j] = bg_colors[(bgPalette >> (pixelCol * 2)) & 0x3]; + renderArray[(i * 512) + (256 + j)] = bg_colors[(bgPalette >> (pixelCol * 2)) & 0x3]; } } - SDL_UpdateTexture(texture, NULL, renderArray, 256 * 4); + // As SDL2 does not have texture warping + // We need to keep 4 copies of the tilemap on the texture side by side + // so the scroll window can warp around + std::copy(renderArray, renderArray + (512 * 256), renderArray + (512 * 256)); + + source->y = mMap->getRegSCY(); + source->x = mMap->getRegSCX(); + + SDL_UpdateTexture(texture, NULL, renderArray, 256 * 4 * 2); SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, texture, NULL, NULL); + SDL_RenderCopy(renderer, texture, source, dest); SDL_RenderPresent(renderer); + printf("\n%02X, %02X\n", mMap->getRegSCX(), mMap->getRegSCY()); +} + +void PPU::executePPU(int cycles) +{ } void PPU::close() diff --git a/src/graphics.h b/src/graphics.h index 03751ca..41f7c0d 100644 --- a/src/graphics.h +++ b/src/graphics.h @@ -12,13 +12,26 @@ class PPU SDL_Renderer* renderer; SDL_Texture* texture; SDL_Event* event; - color renderArray[256 * 256]; - color nullArray[256 * 256]; + + // Source and Destination rects to enable scrolling over texture + SDL_Rect* source; + SDL_Rect* dest; + + // renderArray to be converted to texture + color renderArray[256 * 256 * 4]; MemoryMap* mMap; + + // LCDC 0th bit is the LCD enable flag bool isEnabled; - Word bgTileMapAddr; + + // LCDC 3rd bit is the BG and Window Tile Data Select flag Word bgTileDataAddr; + + // LCDC 4th bit is the BG Tile Map Display Select flag + Word bgTileMapAddr; + + // BGP register is the BG Palette Data Byte bgPalette; // The GameBoy screen @@ -36,6 +49,22 @@ class PPU // indices 1, 2, 3 are the actual colors and will be populated later color obj_colors[4] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; + // Current line being rendered + int currentLine; + + // PPU Mode + Byte ppuMode; + + // PPU Mode Clocks + // Mode 0: 204 cycles + // Mode 1: 456 cycles + // Mode 2: 80 cycles + // Mode 3: 172 cycles + int modeClocks[4] = { 204, 456, 80, 172 }; + + // Current PPU Mode Clock + int currentClock; + public: PPU(); bool init(); @@ -43,4 +72,5 @@ class PPU void load(); void close(); void setMemoryMap(MemoryMap* m) { mMap = m; } + void executePPU(int cycles); }; \ No newline at end of file From d5465de17df3a35800bc3a9ea77b8c12eea46bb8 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Mon, 10 Apr 2023 19:04:55 +0530 Subject: [PATCH 04/34] Add PPU execution barebones --- src/gameBoy.cpp | 6 ++-- src/graphics.cpp | 73 +++++++++++++++++++++++++++++++++++++++++++++++- src/graphics.h | 15 +++++++++- src/mmap.cpp | 3 ++ src/mmap.h | 10 +++++++ 5 files changed, 103 insertions(+), 4 deletions(-) diff --git a/src/gameBoy.cpp b/src/gameBoy.cpp index ea87f27..a701b6a 100644 --- a/src/gameBoy.cpp +++ b/src/gameBoy.cpp @@ -566,13 +566,14 @@ void GBE::update() s_Cycles += gbe_cpu->executeNextInstruction(); if ((*gbe_mMap)[0xFF02] == 0x81) { - gbe_graphics->load(); + //gbe_graphics->load(); printf("%c", (*gbe_mMap)[0xFF01]); gbe_mMap->writeMemory(0xFF02, 0x00); } // update the DIV and TIMA timers gbe_cpu->updateTimers(s_Cycles); + gbe_graphics->executePPU(s_Cycles); s_Cycles = 0; s_Cycles += gbe_cpu->performInterrupt(); gbe_graphics->pollEvents(); @@ -587,11 +588,12 @@ void GBE::executeBootROM() s_Cycles += gbe_cpu->executeNextInstruction(); if ((*gbe_mMap)[0xFF02] == 0x81) { - gbe_graphics->load(); + //gbe_graphics->load(); printf("%c", (*gbe_mMap)[0xFF01]); gbe_mMap->writeMemory(0xFF02, 0x00); } gbe_cpu->updateTimers(s_Cycles); + gbe_graphics->executePPU(s_Cycles); s_Cycles = 0; s_Cycles += gbe_cpu->performInterrupt(); } diff --git a/src/graphics.cpp b/src/graphics.cpp index 2b84730..29f953c 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -18,6 +18,10 @@ PPU::PPU() source = new SDL_Rect({ 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT }); dest = new SDL_Rect({ 0, 0, SCREEN_WIDTH * 2, SCREEN_HEIGHT * 2 }); + ppuMode = 0; + currentClock = modeClocks[ppuMode]; + scanlineRendered = false; + // Fill renderArray initially with white (lightest color in palette) std::fill(renderArray, renderArray + (256 * 256 * 4), bg_colors[0]); } @@ -162,11 +166,78 @@ void PPU::load() SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture, source, dest); SDL_RenderPresent(renderer); - printf("\n%02X, %02X\n", mMap->getRegSCX(), mMap->getRegSCY()); } void PPU::executePPU(int cycles) { + currentClock -= cycles; + switch (ppuMode) + { + case HBLANK: + { + if (currentClock < 0) + { + Byte LY = mMap->getRegLY(); + mMap->setRegLY(LY + 1); + if (LY == 0x8F) + { + ppuMode = 1; + } + else + { + ppuMode = 2; + } + currentClock += modeClocks[ppuMode]; + } + } + return; + case VBLANK: + { + if (currentClock < 0) + { + Byte LY = mMap->getRegLY(); + mMap->setRegLY(LY + 1); + if (LY == 0x99) + { + ppuMode = 2; + scanlineRendered = false; + mMap->setRegLY(0); + } + currentClock += modeClocks[ppuMode]; + } + } + return; + case OAM: + { + if (currentClock < 0) + { + // TODO: Implement OAM memory restriction + ppuMode = 3; + currentClock += modeClocks[ppuMode]; + } + } + return; + case TRANSFER: + { + // TODO: Implement scanline rendering + if (!scanlineRendered) + { + load(); + scanlineRendered = true; + } + + if (currentClock < 0) + { + // TODO: Implement All memory restriction + ppuMode = 0; + currentClock += modeClocks[ppuMode]; + } + } + return; + default: + printf("Unknown PPU Mode %d\n", ppuMode); + return; + } } void PPU::close() diff --git a/src/graphics.h b/src/graphics.h index 41f7c0d..bfd8c14 100644 --- a/src/graphics.h +++ b/src/graphics.h @@ -18,7 +18,8 @@ class PPU SDL_Rect* dest; // renderArray to be converted to texture - color renderArray[256 * 256 * 4]; + // stores 4 copies of texture for wrapping of screen + color renderArray[512 * 512]; MemoryMap* mMap; @@ -65,6 +66,17 @@ class PPU // Current PPU Mode Clock int currentClock; + // Scanline Rendered Flag + bool scanlineRendered; + + enum PPU_MODES + { + HBLANK, + VBLANK, + OAM, + TRANSFER + }; + public: PPU(); bool init(); @@ -73,4 +85,5 @@ class PPU void close(); void setMemoryMap(MemoryMap* m) { mMap = m; } void executePPU(int cycles); + Byte getPPUMode() { return ppuMode; } }; \ No newline at end of file diff --git a/src/mmap.cpp b/src/mmap.cpp index aa708e1..8c87e20 100644 --- a/src/mmap.cpp +++ b/src/mmap.cpp @@ -76,6 +76,9 @@ MemoryMap::MemoryMap() // BGP at 0xFF47 reg_BGP = ioPorts + 0x47; + + // LY at 0xFF44 + reg_LY = ioPorts + 0x44; } // Write to memory diff --git a/src/mmap.h b/src/mmap.h index 9149895..75d2d51 100644 --- a/src/mmap.h +++ b/src/mmap.h @@ -104,6 +104,10 @@ class MemoryMap // Stays in the I/O Ports at 0xFF47 Byte* reg_BGP; + // The LY Register + // Stays in the I/O Ports at 0xFF44 + Byte* reg_LY; + public: // Constructor MemoryMap(); @@ -181,9 +185,15 @@ class MemoryMap // gets the reg_BGP Byte getRegBGP() { return *reg_BGP; } + // gets the reg_LY + Byte getRegLY() { return *reg_LY; } + // sets the reg_TIMA void setRegTIMA(Byte value) { *reg_TIMA = value; } // sets the reg_IF to request an interrupt void setRegIF(Byte value) { *reg_IF |= value; } + + // sets the reg_LY + void setRegLY(Byte value) { *reg_LY = value; } }; \ No newline at end of file From 97a7be022438c25322ec454f4f471e3ffc17eb48 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi <76248539+r41k0u@users.noreply.github.com> Date: Mon, 10 Apr 2023 23:00:02 +0530 Subject: [PATCH 05/34] Add SDL2 to cpp.yml --- .github/workflows/cpp.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/cpp.yml b/.github/workflows/cpp.yml index 332f3bc..3e6d7b5 100644 --- a/.github/workflows/cpp.yml +++ b/.github/workflows/cpp.yml @@ -14,6 +14,12 @@ jobs: - uses: actions/checkout@v3 with: submodules: recursive + + - name: install_dependencies + run: | + sudo add-apt-repository -y "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse" + sudo apt-get update -y -qq + sudo apt-get install libsdl2-dev - name: build run: | From a402217d1a7653b12066579d0ee455774f4a43c2 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Tue, 11 Apr 2023 06:22:09 +0530 Subject: [PATCH 06/34] Fix rendering algo to scroll Nintendo logo --- src/cpu.cpp | 1 + src/gameBoy.cpp | 2 +- src/graphics.cpp | 12 +++++++++++- src/mmap.h | 2 ++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/cpu.cpp b/src/cpu.cpp index aef7d51..f9b1227 100644 --- a/src/cpu.cpp +++ b/src/cpu.cpp @@ -3987,6 +3987,7 @@ int CPU::executeNextInstruction() // Get the opcode Byte opcode = (*mMap)[reg_PC.dat]; + mMap->writeMemory(0xFF00, 0xFF); return (this->*method_pointer[opcode])(); } diff --git a/src/gameBoy.cpp b/src/gameBoy.cpp index a701b6a..63ef168 100644 --- a/src/gameBoy.cpp +++ b/src/gameBoy.cpp @@ -31,7 +31,7 @@ GBE::GBE() printf("boot rom file not opened"); // Open the Game ROM - if ((gameROM = fopen("../tests/cpu_instrs/cpu_instrs.gb", "rb")) == NULL) + if ((gameROM = fopen("../tests/Tetris.gb", "rb")) == NULL) printf("game rom file not opened"); // Load the Boot ROM diff --git a/src/graphics.cpp b/src/graphics.cpp index 29f953c..3af6f91 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -103,8 +103,18 @@ bool PPU::pollEvents() void PPU::load() { + // Evaluate LCDC register + Byte LCDC = mMap->getRegLCDC(); + + isEnabled = (LCDC & 0x80); + // bgTileMapAddr = (LCDC & 0x04) ? 0x9C00 : 0x9800; + bgTileMapAddr = 0x9800; + // bgTileDataAddr = (LCDC & 0x08) ? 0x8000 : 0x8800; + bgTileDataAddr = 0x8000; + // Read background palette register bgPalette = mMap->getRegBGP(); + // bgPalette = 0xE4; Word tilenum; Byte pixelCol; @@ -146,7 +156,7 @@ void PPU::load() } else { - pixelCol = ((*mMap)[bgTileDataAddr + 0x800 + ((SWord)tilenum * 0x10) + (i % 8 * 2)] >> (7 - (j % 8)) & 0x1) + (((*mMap)[bgTileDataAddr + 0x800 + ((SWord)tilenum * 0x10) + (i % 8 * 2) + 1] >> (7 - (j % 8)) & 0x1) * 2); + pixelCol = ((*mMap)[bgTileDataAddr + ((SWord)tilenum * 0x10) + (i % 8 * 2)] >> (7 - (j % 8)) & 0x1) + (((*mMap)[bgTileDataAddr + ((SWord)tilenum * 0x10) + (i % 8 * 2) + 1] >> (7 - (j % 8)) & 0x1) * 2); } renderArray[(i * 512) + j] = bg_colors[(bgPalette >> (pixelCol * 2)) & 0x3]; diff --git a/src/mmap.h b/src/mmap.h index 75d2d51..b4d667b 100644 --- a/src/mmap.h +++ b/src/mmap.h @@ -8,6 +8,8 @@ class MemoryMap { private: + Byte mbcMode; + // First ROM Bank // 16 KB 0x0000 - 0x3FFF // Contains the first 16 KB of the ROM From b4433163938583ba2f6967ae5346ea385e040ca3 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Tue, 11 Apr 2023 07:16:49 +0530 Subject: [PATCH 07/34] Add VBLANK interrupt, boot Tetris till demo --- src/graphics.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/graphics.cpp b/src/graphics.cpp index 3af6f91..b9962a5 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -191,6 +191,7 @@ void PPU::executePPU(int cycles) mMap->setRegLY(LY + 1); if (LY == 0x8F) { + mMap->setRegIF(mMap->getRegIF() | 0x1); ppuMode = 1; } else From f27c58d699aa976a51208cf8eea9beb1c0350c73 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Thu, 13 Apr 2023 11:45:14 +0530 Subject: [PATCH 08/34] Replace frame based rendering with scanline based rendering --- src/gameBoy.cpp | 2 +- src/graphics.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++++---- src/graphics.h | 3 ++- 3 files changed, 60 insertions(+), 7 deletions(-) diff --git a/src/gameBoy.cpp b/src/gameBoy.cpp index 63ef168..18e5cc2 100644 --- a/src/gameBoy.cpp +++ b/src/gameBoy.cpp @@ -31,7 +31,7 @@ GBE::GBE() printf("boot rom file not opened"); // Open the Game ROM - if ((gameROM = fopen("../tests/Tetris.gb", "rb")) == NULL) + if ((gameROM = fopen("../tests/instr_timing/instr_timing.gb", "rb")) == NULL) printf("game rom file not opened"); // Load the Boot ROM diff --git a/src/graphics.cpp b/src/graphics.cpp index b9962a5..5b18fe3 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -23,7 +23,7 @@ PPU::PPU() scanlineRendered = false; // Fill renderArray initially with white (lightest color in palette) - std::fill(renderArray, renderArray + (256 * 256 * 4), bg_colors[0]); + std::fill(renderArray, renderArray + (160 * 144), bg_colors[0]); } bool PPU::init() @@ -74,10 +74,10 @@ bool PPU::init() // Create a placeholder texture // 512x512 to have 4 copies of tilemap - texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC, 256 * 2, 256 * 2); + texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC, 160, 144); // Render the texture - SDL_UpdateTexture(texture, NULL, renderArray, 512 * 4); + SDL_UpdateTexture(texture, NULL, renderArray, 160 * 4); SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture, source, dest); SDL_RenderPresent(renderer); @@ -101,6 +101,48 @@ bool PPU::pollEvents() return false; } +void PPU::renderScanline(Byte line) +{ + // Evaluate LCDC register + Byte LCDC = mMap->getRegLCDC(); + + isEnabled = (LCDC & 0x80); + + if (!isEnabled) + return; + + // bgTileMapAddr = (LCDC & 0x04) ? 0x9C00 : 0x9800; + bgTileMapAddr = 0x9800; + // bgTileDataAddr = (LCDC & 0x08) ? 0x8000 : 0x8800; + bgTileDataAddr = 0x8000; + + // Read background palette register + bgPalette = mMap->getRegBGP(); + // bgPalette = 0xE4; + + Byte pixel_y = line + mMap->getRegSCY(); + Byte scrollX = mMap->getRegSCX(); + Byte pixel_x, pixel_col; + Word tilenum; + + for (Byte j = 0; j < 160; j++) + { + pixel_x = scrollX + j; + tilenum = (*mMap)[bgTileMapAddr + ((pixel_y / 8) * 32) + (pixel_x / 8)]; + + if (bgTileDataAddr == 0x8800) + { + pixel_col = ((*mMap)[bgTileDataAddr + (tilenum * 0x10) + (pixel_y % 8 * 2)] >> (7 - (pixel_x % 8)) & 0x1) + ((*mMap)[bgTileDataAddr + (tilenum * 0x10) + (pixel_y % 8 * 2) + 1] >> (7 - (pixel_x % 8)) & 0x1) * 2; + } + else + { + pixel_col = ((*mMap)[bgTileDataAddr + ((SWord)tilenum * 0x10) + (pixel_y % 8 * 2)] >> (7 - (pixel_x % 8)) & 0x1) + (((*mMap)[bgTileDataAddr + ((SWord)tilenum * 0x10) + (pixel_y % 8 * 2) + 1] >> (7 - (pixel_x % 8)) & 0x1) * 2); + } + + renderArray[(line * 160) + j] = bg_colors[(bgPalette >> (pixel_col * 2)) & 0x3]; + } +} + void PPU::load() { // Evaluate LCDC register @@ -204,6 +246,14 @@ void PPU::executePPU(int cycles) return; case VBLANK: { + if (!scanlineRendered) + { + SDL_UpdateTexture(texture, NULL, renderArray, 160 * 4); + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, texture, NULL, NULL); + SDL_RenderPresent(renderer); + scanlineRendered = true; + } if (currentClock < 0) { Byte LY = mMap->getRegLY(); @@ -231,11 +281,13 @@ void PPU::executePPU(int cycles) case TRANSFER: { // TODO: Implement scanline rendering - if (!scanlineRendered) + /*if (!scanlineRendered) { load(); scanlineRendered = true; - } + }*/ + + renderScanline(mMap->getRegLY()); if (currentClock < 0) { diff --git a/src/graphics.h b/src/graphics.h index bfd8c14..4fc78d4 100644 --- a/src/graphics.h +++ b/src/graphics.h @@ -19,7 +19,7 @@ class PPU // renderArray to be converted to texture // stores 4 copies of texture for wrapping of screen - color renderArray[512 * 512]; + color renderArray[160 * 144]; MemoryMap* mMap; @@ -82,6 +82,7 @@ class PPU bool init(); bool pollEvents(); void load(); + void renderScanline(Byte line); void close(); void setMemoryMap(MemoryMap* m) { mMap = m; } void executePPU(int cycles); From c09dbb6c232bbe638cb90153053b05ccb4168142 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Thu, 13 Apr 2023 17:43:56 +0530 Subject: [PATCH 09/34] Add STAT interrupts --- src/gameBoy.cpp | 2 +- src/graphics.cpp | 255 ++++++++++++++++++++++++++++------------------- src/graphics.h | 11 +- src/mmap.cpp | 6 ++ src/mmap.h | 17 ++++ 5 files changed, 186 insertions(+), 105 deletions(-) diff --git a/src/gameBoy.cpp b/src/gameBoy.cpp index 18e5cc2..5efdcac 100644 --- a/src/gameBoy.cpp +++ b/src/gameBoy.cpp @@ -31,7 +31,7 @@ GBE::GBE() printf("boot rom file not opened"); // Open the Game ROM - if ((gameROM = fopen("../tests/instr_timing/instr_timing.gb", "rb")) == NULL) + if ((gameROM = fopen("../tests/dmg-acid2.gb", "rb")) == NULL) printf("game rom file not opened"); // Load the Boot ROM diff --git a/src/graphics.cpp b/src/graphics.cpp index 5b18fe3..62e9a22 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -8,19 +8,20 @@ PPU::PPU() renderer = nullptr; texture = nullptr; isEnabled = false; + showBGWin = false; mMap = nullptr; bgTileDataAddr = 0x0000; bgTileMapAddr = 0x0000; + winTileMapAddr = 0x0000; bgPalette = 0x00; currentLine = 0x00; ppuMode = 0x02; event = new SDL_Event(); - source = new SDL_Rect({ 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT }); - dest = new SDL_Rect({ 0, 0, SCREEN_WIDTH * 2, SCREEN_HEIGHT * 2 }); ppuMode = 0; currentClock = modeClocks[ppuMode]; scanlineRendered = false; + frameRendered = false; // Fill renderArray initially with white (lightest color in palette) std::fill(renderArray, renderArray + (160 * 144), bg_colors[0]); @@ -68,6 +69,7 @@ bool PPU::init() isEnabled = (LCDC & 0x80); bgTileMapAddr = (LCDC & 0x04) ? 0x9C00 : 0x9800; bgTileDataAddr = (LCDC & 0x08) ? 0x8000 : 0x8800; + winTileMapAddr = (LCDC & 0x40) ? 0x9C00 : 0x9800; // Evaluate Background Palette register bgPalette = mMap->getRegBGP(); @@ -79,7 +81,7 @@ bool PPU::init() // Render the texture SDL_UpdateTexture(texture, NULL, renderArray, 160 * 4); SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, texture, source, dest); + SDL_RenderCopy(renderer, texture, NULL, NULL); SDL_RenderPresent(renderer); return true; } @@ -107,118 +109,111 @@ void PPU::renderScanline(Byte line) Byte LCDC = mMap->getRegLCDC(); isEnabled = (LCDC & 0x80); + showBGWin = (LCDC & 0x1); if (!isEnabled) return; - // bgTileMapAddr = (LCDC & 0x04) ? 0x9C00 : 0x9800; - bgTileMapAddr = 0x9800; - // bgTileDataAddr = (LCDC & 0x08) ? 0x8000 : 0x8800; - bgTileDataAddr = 0x8000; + bgTileMapAddr = (LCDC & 0x08) ? 0x9C00 : 0x9800; + bgTileDataAddr = (LCDC & 0x10) ? 0x8000 : 0x8800; // Read background palette register bgPalette = mMap->getRegBGP(); // bgPalette = 0xE4; - Byte pixel_y = line + mMap->getRegSCY(); - Byte scrollX = mMap->getRegSCX(); - Byte pixel_x, pixel_col; - Word tilenum; + Byte bg_pixel_y = line + mMap->getRegSCY(); + Byte scroll_x = mMap->getRegSCX(); + Byte bg_pixel_x, bg_pixel_col; + Word bg_tilenum; for (Byte j = 0; j < 160; j++) { - pixel_x = scrollX + j; - tilenum = (*mMap)[bgTileMapAddr + ((pixel_y / 8) * 32) + (pixel_x / 8)]; + bg_pixel_x = scroll_x + j; + bg_tilenum = (*mMap)[bgTileMapAddr + ((bg_pixel_y / 8) * 32) + (bg_pixel_x / 8)]; if (bgTileDataAddr == 0x8800) { - pixel_col = ((*mMap)[bgTileDataAddr + (tilenum * 0x10) + (pixel_y % 8 * 2)] >> (7 - (pixel_x % 8)) & 0x1) + ((*mMap)[bgTileDataAddr + (tilenum * 0x10) + (pixel_y % 8 * 2) + 1] >> (7 - (pixel_x % 8)) & 0x1) * 2; + bg_pixel_col = ((*mMap)[bgTileDataAddr + (bg_tilenum * 0x10) + (bg_pixel_y % 8 * 2)] >> (7 - (bg_pixel_x % 8)) & 0x1) + ((*mMap)[bgTileDataAddr + (bg_tilenum * 0x10) + (bg_pixel_y % 8 * 2) + 1] >> (7 - (bg_pixel_x % 8)) & 0x1) * 2; } else { - pixel_col = ((*mMap)[bgTileDataAddr + ((SWord)tilenum * 0x10) + (pixel_y % 8 * 2)] >> (7 - (pixel_x % 8)) & 0x1) + (((*mMap)[bgTileDataAddr + ((SWord)tilenum * 0x10) + (pixel_y % 8 * 2) + 1] >> (7 - (pixel_x % 8)) & 0x1) * 2); + bg_pixel_col = ((*mMap)[bgTileDataAddr + ((SWord)bg_tilenum * 0x10) + (bg_pixel_y % 8 * 2)] >> (7 - (bg_pixel_x % 8)) & 0x1) + (((*mMap)[bgTileDataAddr + ((SWord)bg_tilenum * 0x10) + (bg_pixel_y % 8 * 2) + 1] >> (7 - (bg_pixel_x % 8)) & 0x1) * 2); } - renderArray[(line * 160) + j] = bg_colors[(bgPalette >> (pixel_col * 2)) & 0x3]; + if (showBGWin) + renderArray[(line * 160) + j] = bg_colors[(bgPalette >> (bg_pixel_col * 2)) & 0x3]; + else + renderArray[(line * 160) + j] = bg_colors[0]; } } -void PPU::load() -{ - // Evaluate LCDC register - Byte LCDC = mMap->getRegLCDC(); - - isEnabled = (LCDC & 0x80); - // bgTileMapAddr = (LCDC & 0x04) ? 0x9C00 : 0x9800; - bgTileMapAddr = 0x9800; - // bgTileDataAddr = (LCDC & 0x08) ? 0x8000 : 0x8800; - bgTileDataAddr = 0x8000; - - // Read background palette register - bgPalette = mMap->getRegBGP(); - // bgPalette = 0xE4; - - Word tilenum; - Byte pixelCol; - - // Filling pixel array - // Going over each pixel on screen - // And filling it with the correct color - // If LCDC.4 is set, then the background tile map uses $8000 method and unsigned addressing - // If LCDC.4 is not set, then the background tile map uses $8800 method and signed addressing - // Each tile has 8x8 pixels, and each pixel has a color ID of 0 to 3 - // Each tile occupies 16 bytes, where each line is represented by 2 bytes - // For each line, the 1st byte specifies the LSB of the color ID of each pixel, and the 2nd byte specifies the MSB. - // The color numbers are translated into gray shades depending on the current palette - - // First we calculate the tile number of the tile that the pixel is in - // To do that, we divide the pixel's x and y coordinates by 8 (floor division) - // Then we multiply the resultant y by 32 (the number of tiles in a row) (256 pixels / 8 pixels per tile = 32 tiles) - // Then we add the resultant x which gives us the tile number we must check for data - // Here i is y and j is x - - // Now, using the tile number, we can calculate the address of the tile data by multiplying the tile number by 16 and adding it to the tile data address - // The tile data address is either 0x8000 or 0x8800 depending on LCDC.4 for Background - // If the tile data address is 0x8000, then the tile number is unsigned, else signed at 0x8800 - // Now, depending on the row of pixel, we find which 2 bytes of data to use out of the 16 in pixel data, by taking remainder from 8 and choosing the pair of bytes - // Then, we find the color ID of the pixel by taking the bit at the position of the pixel in the row (7 - (j % 8)) and shifting it to the LSB for both the bytes in the pair - // 7-(j % 8) will give us the 7th bit for j = 0, 6th bit for j = 1, and so on, and the last bit for j = 8. Exactly the bit we need from both bytes in pair - // Adding these LSBs will give us the pixel color we want - - // Source: https://gbdev.io/pandocs/Tile_Data.html - - for (int i = 0; i < 256; i++) - { - for (int j = 0; j < 256; j++) - { - tilenum = (*mMap)[bgTileMapAddr + ((i / 8) * 32) + (j / 8)]; - if (bgTileDataAddr == 0x8800) - { - pixelCol = ((*mMap)[bgTileDataAddr + (tilenum * 0x10) + (i % 8 * 2)] >> (7 - (j % 8)) & 0x1) + ((*mMap)[bgTileDataAddr + (tilenum * 0x10) + (i % 8 * 2) + 1] >> (7 - (j % 8)) & 0x1) * 2; - } - else - { - pixelCol = ((*mMap)[bgTileDataAddr + ((SWord)tilenum * 0x10) + (i % 8 * 2)] >> (7 - (j % 8)) & 0x1) + (((*mMap)[bgTileDataAddr + ((SWord)tilenum * 0x10) + (i % 8 * 2) + 1] >> (7 - (j % 8)) & 0x1) * 2); - } - - renderArray[(i * 512) + j] = bg_colors[(bgPalette >> (pixelCol * 2)) & 0x3]; - renderArray[(i * 512) + (256 + j)] = bg_colors[(bgPalette >> (pixelCol * 2)) & 0x3]; - } - } - - // As SDL2 does not have texture warping - // We need to keep 4 copies of the tilemap on the texture side by side - // so the scroll window can warp around - std::copy(renderArray, renderArray + (512 * 256), renderArray + (512 * 256)); - - source->y = mMap->getRegSCY(); - source->x = mMap->getRegSCX(); - - SDL_UpdateTexture(texture, NULL, renderArray, 256 * 4 * 2); - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, texture, source, dest); - SDL_RenderPresent(renderer); -} +//void PPU::load() +//{ +// // Evaluate LCDC register +// Byte LCDC = mMap->getRegLCDC(); +// +// isEnabled = (LCDC & 0x80); +// // bgTileMapAddr = (LCDC & 0x04) ? 0x9C00 : 0x9800; +// bgTileMapAddr = 0x9800; +// // bgTileDataAddr = (LCDC & 0x08) ? 0x8000 : 0x8800; +// bgTileDataAddr = 0x8000; +// +// // Read background palette register +// bgPalette = mMap->getRegBGP(); +// // bgPalette = 0xE4; +// +// Word bg_tilenum; +// Byte pixelCol; +// +// // Filling pixel array +// // Going over each pixel on screen +// // And filling it with the correct color +// // If LCDC.4 is set, then the background tile map uses $8000 method and unsigned addressing +// // If LCDC.4 is not set, then the background tile map uses $8800 method and signed addressing +// // Each tile has 8x8 pixels, and each pixel has a color ID of 0 to 3 +// // Each tile occupies 16 bytes, where each line is represented by 2 bytes +// // For each line, the 1st byte specifies the LSB of the color ID of each pixel, and the 2nd byte specifies the MSB. +// // The color numbers are translated into gray shades depending on the current palette +// +// // First we calculate the tile number of the tile that the pixel is in +// // To do that, we divide the pixel's x and y coordinates by 8 (floor division) +// // Then we multiply the resultant y by 32 (the number of tiles in a row) (256 pixels / 8 pixels per tile = 32 tiles) +// // Then we add the resultant x which gives us the tile number we must check for data +// // Here i is y and j is x +// +// // Now, using the tile number, we can calculate the address of the tile data by multiplying the tile number by 16 and adding it to the tile data address +// // The tile data address is either 0x8000 or 0x8800 depending on LCDC.4 for Background +// // If the tile data address is 0x8000, then the tile number is unsigned, else signed at 0x8800 +// // Now, depending on the row of pixel, we find which 2 bytes of data to use out of the 16 in pixel data, by taking remainder from 8 and choosing the pair of bytes +// // Then, we find the color ID of the pixel by taking the bit at the position of the pixel in the row (7 - (j % 8)) and shifting it to the LSB for both the bytes in the pair +// // 7-(j % 8) will give us the 7th bit for j = 0, 6th bit for j = 1, and so on, and the last bit for j = 8. Exactly the bit we need from both bytes in pair +// // Adding these LSBs will give us the pixel color we want +// +// // Source: https://gbdev.io/pandocs/Tile_Data.html +// +// for (int i = 0; i < 256; i++) +// { +// for (int j = 0; j < 256; j++) +// { +// bg_tilenum = (*mMap)[bgTileMapAddr + ((i / 8) * 32) + (j / 8)]; +// if (bgTileDataAddr == 0x8800) +// { +// pixelCol = ((*mMap)[bgTileDataAddr + (bg_tilenum * 0x10) + (i % 8 * 2)] >> (7 - (j % 8)) & 0x1) + ((*mMap)[bgTileDataAddr + (bg_tilenum * 0x10) + (i % 8 * 2) + 1] >> (7 - (j % 8)) & 0x1) * 2; +// } +// else +// { +// pixelCol = ((*mMap)[bgTileDataAddr + ((SWord)bg_tilenum * 0x10) + (i % 8 * 2)] >> (7 - (j % 8)) & 0x1) + (((*mMap)[bgTileDataAddr + ((SWord)bg_tilenum * 0x10) + (i % 8 * 2) + 1] >> (7 - (j % 8)) & 0x1) * 2); +// } +// +// renderArray[(i * 512) + j] = bg_colors[(bgPalette >> (pixelCol * 2)) & 0x3]; +// renderArray[(i * 512) + (256 + j)] = bg_colors[(bgPalette >> (pixelCol * 2)) & 0x3]; +// } +// } +// +// // As SDL2 does not have texture warping +// // We need to keep 4 copies of the tilemap on the texture side by side +// // so the scroll window can warp around +//} void PPU::executePPU(int cycles) { @@ -227,57 +222,113 @@ void PPU::executePPU(int cycles) { case HBLANK: { + if (!scanlineRendered) + { + renderScanline(mMap->getRegLY()); + scanlineRendered = true; + } + if (currentClock < 0) { Byte LY = mMap->getRegLY(); + Byte LYC = mMap->getRegLYC(); + Byte STAT = mMap->getRegSTAT(); mMap->setRegLY(LY + 1); + + if (LY + 1 == LYC) + { + mMap->setRegSTAT(STAT | 0x4); + if (STAT & 0x40) + mMap->setRegIF(mMap->getRegIF() | 0x2); + } + else + { + mMap->setRegSTAT(STAT & 0xFB); + } + if (LY == 0x8F) { mMap->setRegIF(mMap->getRegIF() | 0x1); + mMap->setRegSTAT((STAT & 0xFC) | 0x1); + if (STAT & 0x10) + mMap->setRegIF(mMap->getRegIF() | 0x2); ppuMode = 1; } else { + mMap->setRegSTAT((STAT & 0xFC) | 0x2); + if (STAT & 0x20) + mMap->setRegIF(mMap->getRegIF() | 0x2); ppuMode = 2; } currentClock += modeClocks[ppuMode]; } } - return; + break; case VBLANK: { - if (!scanlineRendered) + if (!frameRendered) { SDL_UpdateTexture(texture, NULL, renderArray, 160 * 4); SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture, NULL, NULL); SDL_RenderPresent(renderer); - scanlineRendered = true; + frameRendered = true; } if (currentClock < 0) { Byte LY = mMap->getRegLY(); + Byte LYC = mMap->getRegLYC(); + Byte STAT = mMap->getRegSTAT(); mMap->setRegLY(LY + 1); + + if (LYC == LY + 1) + { + mMap->setRegSTAT(STAT & 0x4); + if (STAT & 0x40) + mMap->setRegIF(mMap->getRegIF() | 0x2); + } + else + { + mMap->setRegSTAT(STAT & 0xFB); + } + if (LY == 0x99) { + mMap->setRegSTAT((STAT & 0xFC) | 0x2); + if (STAT & 0x20) + mMap->setRegIF(mMap->getRegIF() | 0x2); ppuMode = 2; scanlineRendered = false; mMap->setRegLY(0); + if (LYC == 0) + { + mMap->setRegSTAT(STAT & 0x4); + if (STAT & 0x40) + mMap->setRegIF(mMap->getRegIF() | 0x2); + } + else + { + mMap->setRegSTAT(STAT & 0xFB); + } } currentClock += modeClocks[ppuMode]; } } - return; + break; case OAM: { + frameRendered = false; if (currentClock < 0) { // TODO: Implement OAM memory restriction + Byte STAT = mMap->getRegSTAT(); + mMap->setRegSTAT((STAT & 0xFC) | 0x3); ppuMode = 3; currentClock += modeClocks[ppuMode]; } } - return; + break; case TRANSFER: { // TODO: Implement scanline rendering @@ -287,19 +338,23 @@ void PPU::executePPU(int cycles) scanlineRendered = true; }*/ - renderScanline(mMap->getRegLY()); + scanlineRendered = false; if (currentClock < 0) { // TODO: Implement All memory restriction + Byte STAT = mMap->getRegSTAT(); + mMap->setRegSTAT(STAT & 0xFC); + if (STAT & 0x8) + mMap->setRegIF(mMap->getRegIF() | 0x2); ppuMode = 0; currentClock += modeClocks[ppuMode]; } } - return; + break; default: printf("Unknown PPU Mode %d\n", ppuMode); - return; + break; } } diff --git a/src/graphics.h b/src/graphics.h index 4fc78d4..ed7d38b 100644 --- a/src/graphics.h +++ b/src/graphics.h @@ -13,10 +13,6 @@ class PPU SDL_Texture* texture; SDL_Event* event; - // Source and Destination rects to enable scrolling over texture - SDL_Rect* source; - SDL_Rect* dest; - // renderArray to be converted to texture // stores 4 copies of texture for wrapping of screen color renderArray[160 * 144]; @@ -26,12 +22,17 @@ class PPU // LCDC 0th bit is the LCD enable flag bool isEnabled; + // + bool showBGWin; + // LCDC 3rd bit is the BG and Window Tile Data Select flag Word bgTileDataAddr; // LCDC 4th bit is the BG Tile Map Display Select flag Word bgTileMapAddr; + Word winTileMapAddr; + // BGP register is the BG Palette Data Byte bgPalette; @@ -69,6 +70,8 @@ class PPU // Scanline Rendered Flag bool scanlineRendered; + bool frameRendered; + enum PPU_MODES { HBLANK, diff --git a/src/mmap.cpp b/src/mmap.cpp index 8c87e20..6e42a18 100644 --- a/src/mmap.cpp +++ b/src/mmap.cpp @@ -79,6 +79,12 @@ MemoryMap::MemoryMap() // LY at 0xFF44 reg_LY = ioPorts + 0x44; + + // LYC at 0xFF45 + reg_LYC = ioPorts + 0x45; + + // STAT at 0xFF41 + reg_STAT = ioPorts + 0x41; } // Write to memory diff --git a/src/mmap.h b/src/mmap.h index b4d667b..3c49de2 100644 --- a/src/mmap.h +++ b/src/mmap.h @@ -110,6 +110,14 @@ class MemoryMap // Stays in the I/O Ports at 0xFF44 Byte* reg_LY; + // The LYC Register + // Stays in the I/O Ports at 0xFF45 + Byte* reg_LYC; + + // The STAT Register + // Stays in the I/O Ports at 0xFF41 + Byte* reg_STAT; + public: // Constructor MemoryMap(); @@ -190,6 +198,12 @@ class MemoryMap // gets the reg_LY Byte getRegLY() { return *reg_LY; } + // gets the reg_LYC + Byte getRegLYC() { return *reg_LYC; } + + // gets the reg_STAT + Byte getRegSTAT() { return *reg_STAT; } + // sets the reg_TIMA void setRegTIMA(Byte value) { *reg_TIMA = value; } @@ -198,4 +212,7 @@ class MemoryMap // sets the reg_LY void setRegLY(Byte value) { *reg_LY = value; } + + // sets the reg_STAT + void setRegSTAT(Byte value) { *reg_STAT = value; } }; \ No newline at end of file From a59bd89a77e947807a2032f683e38c1db97eb676 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Thu, 13 Apr 2023 17:49:30 +0530 Subject: [PATCH 10/34] Add WY, WX registers --- src/mmap.cpp | 6 ++++++ src/mmap.h | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/mmap.cpp b/src/mmap.cpp index 6e42a18..a248839 100644 --- a/src/mmap.cpp +++ b/src/mmap.cpp @@ -85,6 +85,12 @@ MemoryMap::MemoryMap() // STAT at 0xFF41 reg_STAT = ioPorts + 0x41; + + // WY at 0xFF4A + reg_WY = ioPorts + 0x4A; + + // WX at 0xFF4B + reg_WX = ioPorts + 0x4B; } // Write to memory diff --git a/src/mmap.h b/src/mmap.h index 3c49de2..f661c1b 100644 --- a/src/mmap.h +++ b/src/mmap.h @@ -118,6 +118,14 @@ class MemoryMap // Stays in the I/O Ports at 0xFF41 Byte* reg_STAT; + // The WY Register + // Stays in the I/O Ports at 0xFF4A + Byte* reg_WY; + + // The WX Register + // Stays in the I/O Ports at 0xFF4B + Byte* reg_WX; + public: // Constructor MemoryMap(); @@ -204,6 +212,12 @@ class MemoryMap // gets the reg_STAT Byte getRegSTAT() { return *reg_STAT; } + // gets the reg_WY + Byte getRegWY() { return *reg_WY; } + + // gets the reg_WX + Byte getRegWX() { return *reg_WX; } + // sets the reg_TIMA void setRegTIMA(Byte value) { *reg_TIMA = value; } From e8dc51a2e0275ba9f956a42a6065a83d5c15330e Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Thu, 13 Apr 2023 17:52:19 +0530 Subject: [PATCH 11/34] Remove PPU::load() --- src/gameBoy.cpp | 2 -- src/graphics.cpp | 94 ++++++++++++++---------------------------------- src/graphics.h | 1 - 3 files changed, 26 insertions(+), 71 deletions(-) diff --git a/src/gameBoy.cpp b/src/gameBoy.cpp index 5efdcac..6c989ab 100644 --- a/src/gameBoy.cpp +++ b/src/gameBoy.cpp @@ -566,7 +566,6 @@ void GBE::update() s_Cycles += gbe_cpu->executeNextInstruction(); if ((*gbe_mMap)[0xFF02] == 0x81) { - //gbe_graphics->load(); printf("%c", (*gbe_mMap)[0xFF01]); gbe_mMap->writeMemory(0xFF02, 0x00); } @@ -588,7 +587,6 @@ void GBE::executeBootROM() s_Cycles += gbe_cpu->executeNextInstruction(); if ((*gbe_mMap)[0xFF02] == 0x81) { - //gbe_graphics->load(); printf("%c", (*gbe_mMap)[0xFF01]); gbe_mMap->writeMemory(0xFF02, 0x00); } diff --git a/src/graphics.cpp b/src/graphics.cpp index 62e9a22..d427e06 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -126,6 +126,32 @@ void PPU::renderScanline(Byte line) Byte bg_pixel_x, bg_pixel_col; Word bg_tilenum; + // Filling pixel array + // Going over each pixel on screen + // And filling it with the correct color + // If LCDC.4 is set, then the background tile map uses $8000 method and unsigned addressing + // If LCDC.4 is not set, then the background tile map uses $8800 method and signed addressing + // Each tile has 8x8 pixels, and each pixel has a color ID of 0 to 3 + // Each tile occupies 16 bytes, where each line is represented by 2 bytes + // For each line, the 1st byte specifies the LSB of the color ID of each pixel, and the 2nd byte specifies the MSB. + // The color numbers are translated into gray shades depending on the current palette + + // First we calculate the tile number of the tile that the pixel is in + // To do that, we divide the pixel's x and y coordinates by 8 (floor division) + // Then we multiply the resultant y by 32 (the number of tiles in a row) (256 pixels / 8 pixels per tile = 32 tiles) + // Then we add the resultant x which gives us the tile number we must check for data + // Here i is y and j is x + + // Now, using the tile number, we can calculate the address of the tile data by multiplying the tile number by 16 and adding it to the tile data address + // The tile data address is either 0x8000 or 0x8800 depending on LCDC.4 for Background + // If the tile data address is 0x8000, then the tile number is unsigned, else signed at 0x8800 + // Now, depending on the row of pixel, we find which 2 bytes of data to use out of the 16 in pixel data, by taking remainder from 8 and choosing the pair of bytes + // Then, we find the color ID of the pixel by taking the bit at the position of the pixel in the row (7 - (j % 8)) and shifting it to the LSB for both the bytes in the pair + // 7-(j % 8) will give us the 7th bit for j = 0, 6th bit for j = 1, and so on, and the last bit for j = 8. Exactly the bit we need from both bytes in pair + // Adding these LSBs will give us the pixel color we want + + // Source: https://gbdev.io/pandocs/Tile_Data.html + for (Byte j = 0; j < 160; j++) { bg_pixel_x = scroll_x + j; @@ -147,74 +173,6 @@ void PPU::renderScanline(Byte line) } } -//void PPU::load() -//{ -// // Evaluate LCDC register -// Byte LCDC = mMap->getRegLCDC(); -// -// isEnabled = (LCDC & 0x80); -// // bgTileMapAddr = (LCDC & 0x04) ? 0x9C00 : 0x9800; -// bgTileMapAddr = 0x9800; -// // bgTileDataAddr = (LCDC & 0x08) ? 0x8000 : 0x8800; -// bgTileDataAddr = 0x8000; -// -// // Read background palette register -// bgPalette = mMap->getRegBGP(); -// // bgPalette = 0xE4; -// -// Word bg_tilenum; -// Byte pixelCol; -// -// // Filling pixel array -// // Going over each pixel on screen -// // And filling it with the correct color -// // If LCDC.4 is set, then the background tile map uses $8000 method and unsigned addressing -// // If LCDC.4 is not set, then the background tile map uses $8800 method and signed addressing -// // Each tile has 8x8 pixels, and each pixel has a color ID of 0 to 3 -// // Each tile occupies 16 bytes, where each line is represented by 2 bytes -// // For each line, the 1st byte specifies the LSB of the color ID of each pixel, and the 2nd byte specifies the MSB. -// // The color numbers are translated into gray shades depending on the current palette -// -// // First we calculate the tile number of the tile that the pixel is in -// // To do that, we divide the pixel's x and y coordinates by 8 (floor division) -// // Then we multiply the resultant y by 32 (the number of tiles in a row) (256 pixels / 8 pixels per tile = 32 tiles) -// // Then we add the resultant x which gives us the tile number we must check for data -// // Here i is y and j is x -// -// // Now, using the tile number, we can calculate the address of the tile data by multiplying the tile number by 16 and adding it to the tile data address -// // The tile data address is either 0x8000 or 0x8800 depending on LCDC.4 for Background -// // If the tile data address is 0x8000, then the tile number is unsigned, else signed at 0x8800 -// // Now, depending on the row of pixel, we find which 2 bytes of data to use out of the 16 in pixel data, by taking remainder from 8 and choosing the pair of bytes -// // Then, we find the color ID of the pixel by taking the bit at the position of the pixel in the row (7 - (j % 8)) and shifting it to the LSB for both the bytes in the pair -// // 7-(j % 8) will give us the 7th bit for j = 0, 6th bit for j = 1, and so on, and the last bit for j = 8. Exactly the bit we need from both bytes in pair -// // Adding these LSBs will give us the pixel color we want -// -// // Source: https://gbdev.io/pandocs/Tile_Data.html -// -// for (int i = 0; i < 256; i++) -// { -// for (int j = 0; j < 256; j++) -// { -// bg_tilenum = (*mMap)[bgTileMapAddr + ((i / 8) * 32) + (j / 8)]; -// if (bgTileDataAddr == 0x8800) -// { -// pixelCol = ((*mMap)[bgTileDataAddr + (bg_tilenum * 0x10) + (i % 8 * 2)] >> (7 - (j % 8)) & 0x1) + ((*mMap)[bgTileDataAddr + (bg_tilenum * 0x10) + (i % 8 * 2) + 1] >> (7 - (j % 8)) & 0x1) * 2; -// } -// else -// { -// pixelCol = ((*mMap)[bgTileDataAddr + ((SWord)bg_tilenum * 0x10) + (i % 8 * 2)] >> (7 - (j % 8)) & 0x1) + (((*mMap)[bgTileDataAddr + ((SWord)bg_tilenum * 0x10) + (i % 8 * 2) + 1] >> (7 - (j % 8)) & 0x1) * 2); -// } -// -// renderArray[(i * 512) + j] = bg_colors[(bgPalette >> (pixelCol * 2)) & 0x3]; -// renderArray[(i * 512) + (256 + j)] = bg_colors[(bgPalette >> (pixelCol * 2)) & 0x3]; -// } -// } -// -// // As SDL2 does not have texture warping -// // We need to keep 4 copies of the tilemap on the texture side by side -// // so the scroll window can warp around -//} - void PPU::executePPU(int cycles) { currentClock -= cycles; diff --git a/src/graphics.h b/src/graphics.h index ed7d38b..c5c5916 100644 --- a/src/graphics.h +++ b/src/graphics.h @@ -84,7 +84,6 @@ class PPU PPU(); bool init(); bool pollEvents(); - void load(); void renderScanline(Byte line); void close(); void setMemoryMap(MemoryMap* m) { mMap = m; } From 715a6b855aebd0f6baafc660725e52057a13ad22 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Thu, 13 Apr 2023 22:39:15 +0530 Subject: [PATCH 12/34] Complete Background rendering --- src/graphics.cpp | 6 ++++-- src/graphics.h | 7 +++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/graphics.cpp b/src/graphics.cpp index d427e06..4221af4 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -9,6 +9,7 @@ PPU::PPU() texture = nullptr; isEnabled = false; showBGWin = false; + showWindow = false; mMap = nullptr; bgTileDataAddr = 0x0000; bgTileMapAddr = 0x0000; @@ -110,6 +111,7 @@ void PPU::renderScanline(Byte line) isEnabled = (LCDC & 0x80); showBGWin = (LCDC & 0x1); + showWindow = (LCDC & 0x20); if (!isEnabled) return; @@ -159,11 +161,11 @@ void PPU::renderScanline(Byte line) if (bgTileDataAddr == 0x8800) { - bg_pixel_col = ((*mMap)[bgTileDataAddr + (bg_tilenum * 0x10) + (bg_pixel_y % 8 * 2)] >> (7 - (bg_pixel_x % 8)) & 0x1) + ((*mMap)[bgTileDataAddr + (bg_tilenum * 0x10) + (bg_pixel_y % 8 * 2) + 1] >> (7 - (bg_pixel_x % 8)) & 0x1) * 2; + bg_pixel_col = ((*mMap)[bgTileDataAddr + 0x800 + ((SByte)bg_tilenum * 0x10) + (bg_pixel_y % 8 * 2)] >> (7 - (bg_pixel_x % 8)) & 0x1) + ((*mMap)[bgTileDataAddr + 0x800 + ((SByte)bg_tilenum * 0x10) + (bg_pixel_y % 8 * 2) + 1] >> (7 - (bg_pixel_x % 8)) & 0x1) * 2; } else { - bg_pixel_col = ((*mMap)[bgTileDataAddr + ((SWord)bg_tilenum * 0x10) + (bg_pixel_y % 8 * 2)] >> (7 - (bg_pixel_x % 8)) & 0x1) + (((*mMap)[bgTileDataAddr + ((SWord)bg_tilenum * 0x10) + (bg_pixel_y % 8 * 2) + 1] >> (7 - (bg_pixel_x % 8)) & 0x1) * 2); + bg_pixel_col = ((*mMap)[bgTileDataAddr + (bg_tilenum * 0x10) + (bg_pixel_y % 8 * 2)] >> (7 - (bg_pixel_x % 8)) & 0x1) + (((*mMap)[bgTileDataAddr + (bg_tilenum * 0x10) + (bg_pixel_y % 8 * 2) + 1] >> (7 - (bg_pixel_x % 8)) & 0x1) * 2); } if (showBGWin) diff --git a/src/graphics.h b/src/graphics.h index c5c5916..d3bcd59 100644 --- a/src/graphics.h +++ b/src/graphics.h @@ -19,12 +19,15 @@ class PPU MemoryMap* mMap; - // LCDC 0th bit is the LCD enable flag + // LCDC 7th bit is the LCD enable flag bool isEnabled; - // + // LCDC 0th bit is the BG and Window Display Enable flag bool showBGWin; + // LCDC 5th bit is the Window Display Enable flag + bool showWindow; + // LCDC 3rd bit is the BG and Window Tile Data Select flag Word bgTileDataAddr; From 3679dba5a58d5bd7e5fcb692f8735c9fc8d4270e Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Fri, 14 Apr 2023 00:08:27 +0530 Subject: [PATCH 13/34] Add Window rendering, but WY misbehaves --- src/graphics.cpp | 39 ++++++++++++++++++++++++++++++--------- src/graphics.h | 1 + src/mmap.h | 3 +++ 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/graphics.cpp b/src/graphics.cpp index 4221af4..e5cfce2 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -118,15 +118,19 @@ void PPU::renderScanline(Byte line) bgTileMapAddr = (LCDC & 0x08) ? 0x9C00 : 0x9800; bgTileDataAddr = (LCDC & 0x10) ? 0x8000 : 0x8800; + winTileMapAddr = (LCDC & 0x40) ? 0x9C00 : 0x9800; // Read background palette register bgPalette = mMap->getRegBGP(); // bgPalette = 0xE4; + Byte win_y = mMap->getRegWY(); + Byte win_x = mMap->getRegWX() - 7; + Byte win_pixel_y = line - win_y; Byte bg_pixel_y = line + mMap->getRegSCY(); Byte scroll_x = mMap->getRegSCX(); - Byte bg_pixel_x, bg_pixel_col; - Word bg_tilenum; + Byte bg_pixel_x, bg_pixel_col, win_pixel_x, win_pixel_col; + Byte bg_tilenum, win_tilenum; // Filling pixel array // Going over each pixel on screen @@ -156,6 +160,7 @@ void PPU::renderScanline(Byte line) for (Byte j = 0; j < 160; j++) { + // Background rendering bg_pixel_x = scroll_x + j; bg_tilenum = (*mMap)[bgTileMapAddr + ((bg_pixel_y / 8) * 32) + (bg_pixel_x / 8)]; @@ -172,6 +177,26 @@ void PPU::renderScanline(Byte line) renderArray[(line * 160) + j] = bg_colors[(bgPalette >> (bg_pixel_col * 2)) & 0x3]; else renderArray[(line * 160) + j] = bg_colors[0]; + + + // Window rendering + if (showWindow && (win_x <= j) && (j <= (win_x + 160)) && (win_y <= line) && (line <= (win_y + 143))) + { + win_pixel_x = j - win_x; + win_tilenum = (*mMap)[winTileMapAddr + ((win_pixel_y / 8) * 32) + (win_pixel_x / 8)]; + + if (bgTileDataAddr == 0x8800) + { + win_pixel_col = ((*mMap)[bgTileDataAddr + 0x800 + ((SByte)win_tilenum * 0x10) + (win_pixel_y % 8 * 2)] >> (7 - (win_pixel_x % 8)) & 0x1) + ((*mMap)[bgTileDataAddr + 0x800 + ((SByte)win_tilenum * 0x10) + (win_pixel_y % 8 * 2) + 1] >> (7 - (win_pixel_x % 8)) & 0x1) * 2; + } + else + { + win_pixel_col = ((*mMap)[bgTileDataAddr + (win_tilenum * 0x10) + (win_pixel_y % 8 * 2)] >> (7 - (win_pixel_x % 8)) & 0x1) + (((*mMap)[bgTileDataAddr + (win_tilenum * 0x10) + (win_pixel_y % 8 * 2) + 1] >> (7 - (win_pixel_x % 8)) & 0x1) * 2); + } + + if (win_pixel_col != 0) + renderArray[(line * 160) + j] = bg_colors[(bgPalette >> (win_pixel_col * 2)) & 0x3]; + } } } @@ -186,6 +211,8 @@ void PPU::executePPU(int cycles) { renderScanline(mMap->getRegLY()); scanlineRendered = true; + // if ((mMap->getRegLCDC() & 0x20) && (mMap->getRegLCDC() & 0x1) && (0 <= mMap->getRegWY() < 144) && (0 <= mMap->getRegWX() < 167)) + // mMap->setRegWY(mMap->getRegWY() + 1); } if (currentClock < 0) @@ -261,6 +288,7 @@ void PPU::executePPU(int cycles) ppuMode = 2; scanlineRendered = false; mMap->setRegLY(0); + // mMap->setRegWY(0); if (LYC == 0) { mMap->setRegSTAT(STAT & 0x4); @@ -291,13 +319,6 @@ void PPU::executePPU(int cycles) break; case TRANSFER: { - // TODO: Implement scanline rendering - /*if (!scanlineRendered) - { - load(); - scanlineRendered = true; - }*/ - scanlineRendered = false; if (currentClock < 0) diff --git a/src/graphics.h b/src/graphics.h index d3bcd59..11c5fe5 100644 --- a/src/graphics.h +++ b/src/graphics.h @@ -34,6 +34,7 @@ class PPU // LCDC 4th bit is the BG Tile Map Display Select flag Word bgTileMapAddr; + // LCDC 6th bit is the Window Tile Map Display Select flag Word winTileMapAddr; // BGP register is the BG Palette Data diff --git a/src/mmap.h b/src/mmap.h index f661c1b..17c17e0 100644 --- a/src/mmap.h +++ b/src/mmap.h @@ -229,4 +229,7 @@ class MemoryMap // sets the reg_STAT void setRegSTAT(Byte value) { *reg_STAT = value; } + + // sets the reg_WY + void setRegWY(Byte value) { *reg_WY = value; } }; \ No newline at end of file From 75e8eeb5066bc1b8573eac45754d19c89c57c6ad Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Tue, 18 Apr 2023 01:32:21 +0530 Subject: [PATCH 14/34] Finish window internal line counter and window rendering --- src/graphics.cpp | 17 +++++++++-------- src/graphics.h | 3 +++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/graphics.cpp b/src/graphics.cpp index e5cfce2..a06b4f2 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -10,12 +10,14 @@ PPU::PPU() isEnabled = false; showBGWin = false; showWindow = false; + //renderWindow = false; mMap = nullptr; bgTileDataAddr = 0x0000; bgTileMapAddr = 0x0000; winTileMapAddr = 0x0000; bgPalette = 0x00; currentLine = 0x00; + hiddenWindowLineCounter = 0x00; ppuMode = 0x02; event = new SDL_Event(); @@ -95,7 +97,6 @@ bool PPU::pollEvents() { if (event->type == SDL_KEYDOWN) { - mMap->writeMemory(0xFF02, 0x81); printf("Key pressed: %c\n", event->key.keysym.sym); if (event->key.keysym.sym == SDLK_ESCAPE) exit(0); @@ -122,11 +123,10 @@ void PPU::renderScanline(Byte line) // Read background palette register bgPalette = mMap->getRegBGP(); - // bgPalette = 0xE4; Byte win_y = mMap->getRegWY(); Byte win_x = mMap->getRegWX() - 7; - Byte win_pixel_y = line - win_y; + Byte win_pixel_y = hiddenWindowLineCounter; Byte bg_pixel_y = line + mMap->getRegSCY(); Byte scroll_x = mMap->getRegSCX(); Byte bg_pixel_x, bg_pixel_col, win_pixel_x, win_pixel_col; @@ -180,7 +180,7 @@ void PPU::renderScanline(Byte line) // Window rendering - if (showWindow && (win_x <= j) && (j <= (win_x + 160)) && (win_y <= line) && (line <= (win_y + 143))) + if (showBGWin && showWindow && ((win_y <= line) && (win_y < 144)) && (win_x < 160) && (hiddenWindowLineCounter < 144) && (j >= win_x)) { win_pixel_x = j - win_x; win_tilenum = (*mMap)[winTileMapAddr + ((win_pixel_y / 8) * 32) + (win_pixel_x / 8)]; @@ -194,10 +194,13 @@ void PPU::renderScanline(Byte line) win_pixel_col = ((*mMap)[bgTileDataAddr + (win_tilenum * 0x10) + (win_pixel_y % 8 * 2)] >> (7 - (win_pixel_x % 8)) & 0x1) + (((*mMap)[bgTileDataAddr + (win_tilenum * 0x10) + (win_pixel_y % 8 * 2) + 1] >> (7 - (win_pixel_x % 8)) & 0x1) * 2); } - if (win_pixel_col != 0) + if ((win_pixel_col != 0) || (win_x)) renderArray[(line * 160) + j] = bg_colors[(bgPalette >> (win_pixel_col * 2)) & 0x3]; } } + + if (showBGWin && showWindow && ((win_y <= line) && (win_y < 144)) && (win_x < 160) && (hiddenWindowLineCounter < 144)) + hiddenWindowLineCounter++; } void PPU::executePPU(int cycles) @@ -211,8 +214,6 @@ void PPU::executePPU(int cycles) { renderScanline(mMap->getRegLY()); scanlineRendered = true; - // if ((mMap->getRegLCDC() & 0x20) && (mMap->getRegLCDC() & 0x1) && (0 <= mMap->getRegWY() < 144) && (0 <= mMap->getRegWX() < 167)) - // mMap->setRegWY(mMap->getRegWY() + 1); } if (currentClock < 0) @@ -240,6 +241,7 @@ void PPU::executePPU(int cycles) if (STAT & 0x10) mMap->setRegIF(mMap->getRegIF() | 0x2); ppuMode = 1; + hiddenWindowLineCounter = 0; } else { @@ -288,7 +290,6 @@ void PPU::executePPU(int cycles) ppuMode = 2; scanlineRendered = false; mMap->setRegLY(0); - // mMap->setRegWY(0); if (LYC == 0) { mMap->setRegSTAT(STAT & 0x4); diff --git a/src/graphics.h b/src/graphics.h index 11c5fe5..7e23b6e 100644 --- a/src/graphics.h +++ b/src/graphics.h @@ -40,6 +40,9 @@ class PPU // BGP register is the BG Palette Data Byte bgPalette; + // Internal window line counter + Byte hiddenWindowLineCounter; + // The GameBoy screen // 160x144 screen resolution withing a 256x224 border // The original GameBoy supported 4 colors From 0719607330f0629691bd0466e157c69f57fb8506 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Tue, 18 Apr 2023 06:10:13 +0530 Subject: [PATCH 15/34] Implement DMA Transfer --- src/gameBoy.cpp | 2 +- src/graphics.cpp | 43 ++++++++++++++++++++++++++++++++++++++++--- src/graphics.h | 21 +++++++++++++++++++++ src/mmap.cpp | 18 ++++++++++++++++++ src/mmap.h | 14 ++++++++++++++ 5 files changed, 94 insertions(+), 4 deletions(-) diff --git a/src/gameBoy.cpp b/src/gameBoy.cpp index 6c989ab..521ce95 100644 --- a/src/gameBoy.cpp +++ b/src/gameBoy.cpp @@ -31,7 +31,7 @@ GBE::GBE() printf("boot rom file not opened"); // Open the Game ROM - if ((gameROM = fopen("../tests/dmg-acid2.gb", "rb")) == NULL) + if ((gameROM = fopen("../tests/Tetris.gb", "rb")) == NULL) printf("game rom file not opened"); // Load the Boot ROM diff --git a/src/graphics.cpp b/src/graphics.cpp index a06b4f2..0e7636b 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -16,6 +16,8 @@ PPU::PPU() bgTileMapAddr = 0x0000; winTileMapAddr = 0x0000; bgPalette = 0x00; + objPalette0 = 0x00; + objPalette1 = 0x00; currentLine = 0x00; hiddenWindowLineCounter = 0x00; ppuMode = 0x02; @@ -77,6 +79,12 @@ bool PPU::init() // Evaluate Background Palette register bgPalette = mMap->getRegBGP(); + // Evaluate Sprite Palette 0 register + objPalette0 = mMap->getRegOBP0(); + + // Evaluate Sprite Palette 1 register + objPalette1 = mMap->getRegOBP1(); + // Create a placeholder texture // 512x512 to have 4 copies of tilemap texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC, 160, 144); @@ -113,6 +121,7 @@ void PPU::renderScanline(Byte line) isEnabled = (LCDC & 0x80); showBGWin = (LCDC & 0x1); showWindow = (LCDC & 0x20); + showSprites = (LCDC & 0x2); if (!isEnabled) return; @@ -121,16 +130,19 @@ void PPU::renderScanline(Byte line) bgTileDataAddr = (LCDC & 0x10) ? 0x8000 : 0x8800; winTileMapAddr = (LCDC & 0x40) ? 0x9C00 : 0x9800; - // Read background palette register + // Read palette registers bgPalette = mMap->getRegBGP(); + objPalette0 = mMap->getRegOBP0(); + objPalette1 = mMap->getRegOBP1(); Byte win_y = mMap->getRegWY(); Byte win_x = mMap->getRegWX() - 7; Byte win_pixel_y = hiddenWindowLineCounter; Byte bg_pixel_y = line + mMap->getRegSCY(); Byte scroll_x = mMap->getRegSCX(); - Byte bg_pixel_x, bg_pixel_col, win_pixel_x, win_pixel_col; + Byte bg_pixel_x, bg_pixel_col, win_pixel_x, win_pixel_col, sprite_y; Byte bg_tilenum, win_tilenum; + Byte sprite_height = (LCDC & 0x4) ? 16 : 8; // Filling pixel array // Going over each pixel on screen @@ -177,7 +189,6 @@ void PPU::renderScanline(Byte line) renderArray[(line * 160) + j] = bg_colors[(bgPalette >> (bg_pixel_col * 2)) & 0x3]; else renderArray[(line * 160) + j] = bg_colors[0]; - // Window rendering if (showBGWin && showWindow && ((win_y <= line) && (win_y < 144)) && (win_x < 160) && (hiddenWindowLineCounter < 144) && (j >= win_x)) @@ -201,6 +212,32 @@ void PPU::renderScanline(Byte line) if (showBGWin && showWindow && ((win_y <= line) && (win_y < 144)) && (win_x < 160) && (hiddenWindowLineCounter < 144)) hiddenWindowLineCounter++; + + // Sprite rendering + if (showSprites) + { + sprites.clear(); + for (Word i = 0xFE00; i < 0xFEA0; i += 4) + { + if (sprites.size() >= 10) + break; + sprite_y = (*mMap)[i]; + if ((line < (sprite_y - 16) || line > (sprite_y - 16 + sprite_height))) + continue; + + Sprite* sprite = new Sprite(); + sprite->address = i; + sprite->y = sprite_y; + sprite->x = (*mMap)[i + 1]; + sprite->tile = (*mMap)[i + 2]; + sprite->flags = (*mMap)[i + 3]; + sprites.push_back(*sprite); + } + + if (sprites.size()) + std::sort(sprites.begin(), sprites.end(), [](Sprite& a, Sprite& b) { return (((a.x == b.x) && (a.address < b.address)) || (a.x > b.x)); }); + Byte j = 0; + } } void PPU::executePPU(int cycles) diff --git a/src/graphics.h b/src/graphics.h index 7e23b6e..706f696 100644 --- a/src/graphics.h +++ b/src/graphics.h @@ -4,6 +4,16 @@ #include #include #include +#include + +struct Sprite +{ + Word address; + Byte y; + Byte x; + Byte tile; + Byte flags; +}; class PPU { @@ -28,6 +38,9 @@ class PPU // LCDC 5th bit is the Window Display Enable flag bool showWindow; + // LCDC 1st bit is the OBJ (Sprite) Display Enable flag + bool showSprites; + // LCDC 3rd bit is the BG and Window Tile Data Select flag Word bgTileDataAddr; @@ -40,6 +53,12 @@ class PPU // BGP register is the BG Palette Data Byte bgPalette; + // OBP0 register is the OBJ Palette 0 Data + Byte objPalette0; + + // OBP1 register is the OBJ Palette 1 Data + Byte objPalette1; + // Internal window line counter Byte hiddenWindowLineCounter; @@ -87,6 +106,8 @@ class PPU TRANSFER }; + std::vector sprites; + public: PPU(); bool init(); diff --git a/src/mmap.cpp b/src/mmap.cpp index a248839..1bb3b5c 100644 --- a/src/mmap.cpp +++ b/src/mmap.cpp @@ -77,6 +77,12 @@ MemoryMap::MemoryMap() // BGP at 0xFF47 reg_BGP = ioPorts + 0x47; + // OBP0 at 0xFF48 + reg_OBP0 = ioPorts + 0x48; + + // OBP1 at 0xFF49 + reg_OBP1 = ioPorts + 0x49; + // LY at 0xFF44 reg_LY = ioPorts + 0x44; @@ -140,6 +146,18 @@ bool MemoryMap::writeMemory(Word address, Byte value) // else write to I/O ports if (address == 0xFF04) *reg_DIV = 0x00; + // Check for DMA transfer + // Writing a loop instead of std::copy + // as memoury is not a single unit + // in our architecture + else if (address == 0xFF46) + { + for (Word i = 0; i < 0xA0; i++) + oamTable[i] = readMemory(((Word)value << 8) + i); + ioPorts[address - 0xFF00] = value; + } + else if (address == 0xFF44) + *reg_LY = 0x00; else ioPorts[address - 0xFF00] = value; } diff --git a/src/mmap.h b/src/mmap.h index 17c17e0..424b971 100644 --- a/src/mmap.h +++ b/src/mmap.h @@ -106,6 +106,14 @@ class MemoryMap // Stays in the I/O Ports at 0xFF47 Byte* reg_BGP; + // The OBP0 Register + // Stays in the I/O Ports at 0xFF48 + Byte* reg_OBP0; + + // The OBP1 Register + // Stays in the I/O Ports at 0xFF49 + Byte* reg_OBP1; + // The LY Register // Stays in the I/O Ports at 0xFF44 Byte* reg_LY; @@ -203,6 +211,12 @@ class MemoryMap // gets the reg_BGP Byte getRegBGP() { return *reg_BGP; } + // gets the reg_OBP0 + Byte getRegOBP0() { return *reg_OBP0; } + + // gets the reg_OBP1 + Byte getRegOBP1() { return *reg_OBP1; } + // gets the reg_LY Byte getRegLY() { return *reg_LY; } From d50f21edc999fe57f44b2fe934dce09cadc50cb1 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Tue, 18 Apr 2023 08:52:04 +0530 Subject: [PATCH 16/34] Get sprites working, passes half of dmg-acid2's sprite tests --- src/graphics.cpp | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/src/graphics.cpp b/src/graphics.cpp index 0e7636b..b86c330 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -140,8 +140,8 @@ void PPU::renderScanline(Byte line) Byte win_pixel_y = hiddenWindowLineCounter; Byte bg_pixel_y = line + mMap->getRegSCY(); Byte scroll_x = mMap->getRegSCX(); - Byte bg_pixel_x, bg_pixel_col, win_pixel_x, win_pixel_col, sprite_y; - Byte bg_tilenum, win_tilenum; + Byte bg_pixel_x, bg_pixel_col, win_pixel_x, win_pixel_col, sprite_y, sprite_pixel_col; + Byte bg_tilenum, win_tilenum, sprite_palette; Byte sprite_height = (LCDC & 0x4) ? 16 : 8; // Filling pixel array @@ -236,7 +236,37 @@ void PPU::renderScanline(Byte line) if (sprites.size()) std::sort(sprites.begin(), sprites.end(), [](Sprite& a, Sprite& b) { return (((a.x == b.x) && (a.address < b.address)) || (a.x > b.x)); }); - Byte j = 0; + + for (auto it = sprites.begin(); it != sprites.end(); ++it) + { + sprite_palette = (it->flags & 0x10) ? objPalette1 : objPalette0; + for (int i = 0; i < 8; i++) + { + switch (it->flags & 0x60) + { + case 0x00: // Normal + sprite_pixel_col = ((*mMap)[0x8000 + (it->tile * 0x10) + ((line - (it->y - 16)) * 2)] >> (7 - i) & 0x1) + (((*mMap)[0x8000 + (it->tile * 0x10) + ((line - (it->y - 16)) * 2) + 1] >> (7 - i) & 0x1) * 2); + break; + case 0x20: // Flip X + sprite_pixel_col = ((*mMap)[0x8000 + (it->tile * 0x10) + ((line - (it->y - 16)) * 2)] >> i & 0x1) + (((*mMap)[0x8000 + (it->tile * 0x10) + ((line - (it->y - 16)) * 2) + 1] >> i & 0x1) * 2); + break; + case 0x40: // Flip Y + sprite_pixel_col = ((*mMap)[0x8000 + (it->tile * 0x10) + ((sprite_height - (line - (it->y - 16)) - 1) * 2)] >> (7 - i) & 0x1) + (((*mMap)[0x8000 + (it->tile * 0x10) + ((sprite_height - (line - (it->y - 16)) - 1) * 2) + 1] >> (7 - i) & 0x1) * 2); + break; + case 0x60: // Flip X and Y + sprite_pixel_col = ((*mMap)[0x8000 + (it->tile * 0x10) + ((sprite_height - (line - (it->y - 16)) - 1) * 2)] >> i & 0x1) + (((*mMap)[0x8000 + (it->tile * 0x10) + ((sprite_height - (line - (it->y - 16)) - 1) * 2) + 1] >> i & 0x1) * 2); + break; + default: + break; + } + + if (sprite_pixel_col != 0) + { + if (((it->x + i - 8) < 160) && !(it->flags & 0x80)) + renderArray[(line * 160) + (it->x + i - 8)] = bg_colors[(sprite_palette >> (sprite_pixel_col * 2)) & 0x3]; + } + } + } } } From 6ed7da5271d3b16714b301531af555c5d42ddb92 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Tue, 18 Apr 2023 11:50:20 +0530 Subject: [PATCH 17/34] Finish DMG-Acid2 test with one sprite artifact, run Dr. Mario --- src/gameBoy.cpp | 2 +- src/graphics.cpp | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/gameBoy.cpp b/src/gameBoy.cpp index 521ce95..521a269 100644 --- a/src/gameBoy.cpp +++ b/src/gameBoy.cpp @@ -31,7 +31,7 @@ GBE::GBE() printf("boot rom file not opened"); // Open the Game ROM - if ((gameROM = fopen("../tests/Tetris.gb", "rb")) == NULL) + if ((gameROM = fopen("../tests/drmario.gb", "rb")) == NULL) printf("game rom file not opened"); // Load the Boot ROM diff --git a/src/graphics.cpp b/src/graphics.cpp index b86c330..a91751d 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -235,13 +235,15 @@ void PPU::renderScanline(Byte line) } if (sprites.size()) - std::sort(sprites.begin(), sprites.end(), [](Sprite& a, Sprite& b) { return (((a.x == b.x) && (a.address < b.address)) || (a.x > b.x)); }); + std::sort(sprites.begin(), sprites.end(), [](Sprite& a, Sprite& b) { return (((a.x == b.x) && (a.address > b.address)) || (a.x > b.x)); }); for (auto it = sprites.begin(); it != sprites.end(); ++it) { sprite_palette = (it->flags & 0x10) ? objPalette1 : objPalette0; for (int i = 0; i < 8; i++) { + if (sprite_height == 16) + it->tile &= 0xFE; switch (it->flags & 0x60) { case 0x00: // Normal From a1d1da5d176e16605fc1285cf73adddafbbc48ec Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 26 Apr 2023 03:49:07 +0530 Subject: [PATCH 18/34] Game playable now --- src/cpu.cpp | 2 +- src/graphics.cpp | 96 +++++++++++++++++++++++++++++++++++++++--------- src/mmap.cpp | 41 ++++++++++++++++++++- src/mmap.h | 6 +++ 4 files changed, 125 insertions(+), 20 deletions(-) diff --git a/src/cpu.cpp b/src/cpu.cpp index f9b1227..d5fa79f 100644 --- a/src/cpu.cpp +++ b/src/cpu.cpp @@ -3987,7 +3987,7 @@ int CPU::executeNextInstruction() // Get the opcode Byte opcode = (*mMap)[reg_PC.dat]; - mMap->writeMemory(0xFF00, 0xFF); + //mMap->writeMemory(0xFF00, 0xFF); return (this->*method_pointer[opcode])(); } diff --git a/src/graphics.cpp b/src/graphics.cpp index a91751d..26ab27c 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -103,11 +103,71 @@ bool PPU::pollEvents() { while (SDL_PollEvent(event)) { - if (event->type == SDL_KEYDOWN) + if (event->key.type == SDL_KEYDOWN) { - printf("Key pressed: %c\n", event->key.keysym.sym); - if (event->key.keysym.sym == SDLK_ESCAPE) + switch (event->key.keysym.sym) + { + case SDLK_LEFT: + *(mMap->joyPadState) &= 0xFD; + break; + case SDLK_RIGHT: + *(mMap->joyPadState) &= 0xFE; + break; + case SDLK_UP: + *(mMap->joyPadState) &= 0xFB; + break; + case SDLK_DOWN: + *(mMap->joyPadState) &= 0xF7; + break; + case SDLK_a: + *(mMap->joyPadState) &= 0xEF; + break; + case SDLK_s: + *(mMap->joyPadState) &= 0xDF; + break; + case SDLK_LSHIFT: + *(mMap->joyPadState) &= 0xBF; + break; + case SDLK_SPACE: + *(mMap->joyPadState) &= 0x7F; + break; + case SDLK_ESCAPE: exit(0); + default: + break; + } + } + else if (event->key.type == SDL_KEYUP) + { + switch (event->key.keysym.sym) + { + case SDLK_LEFT: + *(mMap->joyPadState) |= 0x02; + break; + case SDLK_RIGHT: + *(mMap->joyPadState) |= 0x01; + break; + case SDLK_UP: + *(mMap->joyPadState) |= 0x04; + break; + case SDLK_DOWN: + *(mMap->joyPadState) |= 0x08; + break; + case SDLK_a: + *(mMap->joyPadState) |= 0x10; + break; + case SDLK_s: + *(mMap->joyPadState) |= 0x20; + break; + case SDLK_LSHIFT: + *(mMap->joyPadState) |= 0x40; + break; + case SDLK_SPACE: + *(mMap->joyPadState) |= 0x80; + break; + default: + break; + } } } return false; @@ -236,7 +296,7 @@ void PPU::renderScanline(Byte line) if (sprites.size()) std::sort(sprites.begin(), sprites.end(), [](Sprite& a, Sprite& b) { return (((a.x == b.x) && (a.address > b.address)) || (a.x > b.x)); }); - + for (auto it = sprites.begin(); it != sprites.end(); ++it) { sprite_palette = (it->flags & 0x10) ? objPalette1 : objPalette0; @@ -246,20 +306,20 @@ void PPU::renderScanline(Byte line) it->tile &= 0xFE; switch (it->flags & 0x60) { - case 0x00: // Normal - sprite_pixel_col = ((*mMap)[0x8000 + (it->tile * 0x10) + ((line - (it->y - 16)) * 2)] >> (7 - i) & 0x1) + (((*mMap)[0x8000 + (it->tile * 0x10) + ((line - (it->y - 16)) * 2) + 1] >> (7 - i) & 0x1) * 2); - break; - case 0x20: // Flip X - sprite_pixel_col = ((*mMap)[0x8000 + (it->tile * 0x10) + ((line - (it->y - 16)) * 2)] >> i & 0x1) + (((*mMap)[0x8000 + (it->tile * 0x10) + ((line - (it->y - 16)) * 2) + 1] >> i & 0x1) * 2); - break; - case 0x40: // Flip Y - sprite_pixel_col = ((*mMap)[0x8000 + (it->tile * 0x10) + ((sprite_height - (line - (it->y - 16)) - 1) * 2)] >> (7 - i) & 0x1) + (((*mMap)[0x8000 + (it->tile * 0x10) + ((sprite_height - (line - (it->y - 16)) - 1) * 2) + 1] >> (7 - i) & 0x1) * 2); - break; - case 0x60: // Flip X and Y - sprite_pixel_col = ((*mMap)[0x8000 + (it->tile * 0x10) + ((sprite_height - (line - (it->y - 16)) - 1) * 2)] >> i & 0x1) + (((*mMap)[0x8000 + (it->tile * 0x10) + ((sprite_height - (line - (it->y - 16)) - 1) * 2) + 1] >> i & 0x1) * 2); - break; - default: - break; + case 0x00: // Normal + sprite_pixel_col = ((*mMap)[0x8000 + (it->tile * 0x10) + ((line - (it->y - 16)) * 2)] >> (7 - i) & 0x1) + (((*mMap)[0x8000 + (it->tile * 0x10) + ((line - (it->y - 16)) * 2) + 1] >> (7 - i) & 0x1) * 2); + break; + case 0x20: // Flip X + sprite_pixel_col = ((*mMap)[0x8000 + (it->tile * 0x10) + ((line - (it->y - 16)) * 2)] >> i & 0x1) + (((*mMap)[0x8000 + (it->tile * 0x10) + ((line - (it->y - 16)) * 2) + 1] >> i & 0x1) * 2); + break; + case 0x40: // Flip Y + sprite_pixel_col = ((*mMap)[0x8000 + (it->tile * 0x10) + ((sprite_height - (line - (it->y - 16)) - 1) * 2)] >> (7 - i) & 0x1) + (((*mMap)[0x8000 + (it->tile * 0x10) + ((sprite_height - (line - (it->y - 16)) - 1) * 2) + 1] >> (7 - i) & 0x1) * 2); + break; + case 0x60: // Flip X and Y + sprite_pixel_col = ((*mMap)[0x8000 + (it->tile * 0x10) + ((sprite_height - (line - (it->y - 16)) - 1) * 2)] >> i & 0x1) + (((*mMap)[0x8000 + (it->tile * 0x10) + ((sprite_height - (line - (it->y - 16)) - 1) * 2) + 1] >> i & 0x1) * 2); + break; + default: + break; } if (sprite_pixel_col != 0) diff --git a/src/mmap.cpp b/src/mmap.cpp index 1bb3b5c..1e91f1a 100644 --- a/src/mmap.cpp +++ b/src/mmap.cpp @@ -97,6 +97,9 @@ MemoryMap::MemoryMap() // WX at 0xFF4B reg_WX = ioPorts + 0x4B; + + joyPadState = new Byte; + *joyPadState = 0xFF; } // Write to memory @@ -152,12 +155,20 @@ bool MemoryMap::writeMemory(Word address, Byte value) // in our architecture else if (address == 0xFF46) { + Word val = value; + val = val << 8; for (Word i = 0; i < 0xA0; i++) - oamTable[i] = readMemory(((Word)value << 8) + i); + oamTable[i] = readMemory(val + i); ioPorts[address - 0xFF00] = value; } else if (address == 0xFF44) *reg_LY = 0x00; + else if (address == 0xFF00) + { + readInput(value); + } + //if (value != 0xFF) + //printf("0x%02x\n", ioPorts[0]);} else ioPorts[address - 0xFF00] = value; } @@ -253,4 +264,32 @@ Byte MemoryMap::readMemory(Word address) Byte MemoryMap::operator[](Word address) { return MemoryMap::readMemory(address); +} + +void MemoryMap::readInput(Byte value) +{ + ioPorts[0] = (ioPorts[0] & 0xCF) | (value & 0x30); + + Byte current = ioPorts[0] & 0xF0; + + switch (current & 0x30) + { + case 0x10: + current = 0xD0; + current |= (((*joyPadState) >> 4) & 0x0F); + break; + case 0x20: + current = 0xE0; + current |= ((*joyPadState) & 0x0F); + break; + case 0x30: + current = 0xF0; + current |= 0x0F; + break; + } + + if ((ioPorts[0] & (~current) & 0x0F) != 0) + (*reg_IF) |= 0x10; + + ioPorts[0] = current; } \ No newline at end of file diff --git a/src/mmap.h b/src/mmap.h index 424b971..442476e 100644 --- a/src/mmap.h +++ b/src/mmap.h @@ -135,12 +135,16 @@ class MemoryMap Byte* reg_WX; public: + Byte* joyPadState; + // Constructor MemoryMap(); // Destructor ~MemoryMap(); + void readInput(Byte value); + // Returns the ROM Bank 0 Byte* getRomBank0() const { return romBank0; } @@ -246,4 +250,6 @@ class MemoryMap // sets the reg_WY void setRegWY(Byte value) { *reg_WY = value; } + + //void setPPU(PPU* ppu) { gbe_ppu = ppu; } }; \ No newline at end of file From 1503dd085fac90bfafa06edcd4d8b8c3cb6cdf21 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Mon, 1 May 2023 19:22:38 +0530 Subject: [PATCH 19/34] Remove VBLANK stub --- src/gameBoy.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/gameBoy.cpp b/src/gameBoy.cpp index 521a269..cb3fc9c 100644 --- a/src/gameBoy.cpp +++ b/src/gameBoy.cpp @@ -31,7 +31,7 @@ GBE::GBE() printf("boot rom file not opened"); // Open the Game ROM - if ((gameROM = fopen("../tests/drmario.gb", "rb")) == NULL) + if ((gameROM = fopen("../tests/dmg-acid2.gb", "rb")) == NULL) printf("game rom file not opened"); // Load the Boot ROM @@ -47,14 +47,6 @@ GBE::GBE() s_Cycles = 0; - // STUB: Fooling the emulator - // Into thinking the frame is ready - // Helps us get out of the loop at - // 0x0064 in the boot ROM - // Needs to be removed once Timers - // and PPU is implemented - gbe_mMap->writeMemory(0xff44, 0x90); - // Adding the Nintendo Logo to ROM // to pass the Boot check From 61ec724621ffc1b07f7ba64c4b6452c9c6db5ecb Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Mon, 1 May 2023 20:51:04 +0530 Subject: [PATCH 20/34] Remove sprite overscan artifact --- src/graphics.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graphics.cpp b/src/graphics.cpp index 26ab27c..29094b1 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -282,7 +282,7 @@ void PPU::renderScanline(Byte line) if (sprites.size() >= 10) break; sprite_y = (*mMap)[i]; - if ((line < (sprite_y - 16) || line > (sprite_y - 16 + sprite_height))) + if ((line < (sprite_y - 16) || line > (sprite_y - 16 + sprite_height - 1))) continue; Sprite* sprite = new Sprite(); From 00ce12f84e7be5ed5b3343ebba4b5c67baf472df Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Tue, 2 May 2023 10:33:11 +0530 Subject: [PATCH 21/34] Fix OBJ priority, run tetris --- src/gameBoy.cpp | 2 +- src/graphics.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gameBoy.cpp b/src/gameBoy.cpp index cb3fc9c..921cdbc 100644 --- a/src/gameBoy.cpp +++ b/src/gameBoy.cpp @@ -31,7 +31,7 @@ GBE::GBE() printf("boot rom file not opened"); // Open the Game ROM - if ((gameROM = fopen("../tests/dmg-acid2.gb", "rb")) == NULL) + if ((gameROM = fopen("../tests/Tetris.gb", "rb")) == NULL) printf("game rom file not opened"); // Load the Boot ROM diff --git a/src/graphics.cpp b/src/graphics.cpp index 29094b1..e1030bb 100644 --- a/src/graphics.cpp +++ b/src/graphics.cpp @@ -324,7 +324,7 @@ void PPU::renderScanline(Byte line) if (sprite_pixel_col != 0) { - if (((it->x + i - 8) < 160) && !(it->flags & 0x80)) + if (((it->x + i - 8) < 160) && (!(it->flags & 0x80) || (renderArray[(line * 160) + (it->x + i - 8)] == bg_colors[0]))) renderArray[(line * 160) + (it->x + i - 8)] = bg_colors[(sprite_palette >> (sprite_pixel_col * 2)) & 0x3]; } } From 8be6c0b8ed8227b38ec836ffd1562f8b2355ffa2 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Tue, 2 May 2023 11:19:01 +0530 Subject: [PATCH 22/34] Cleanup gameboy.cpp --- src/gameBoy.cpp | 445 +----------------------------------------------- src/mmap.cpp | 3 + src/mmap.h | 3 + 3 files changed, 7 insertions(+), 444 deletions(-) diff --git a/src/gameBoy.cpp b/src/gameBoy.cpp index 921cdbc..d95f774 100644 --- a/src/gameBoy.cpp +++ b/src/gameBoy.cpp @@ -31,7 +31,7 @@ GBE::GBE() printf("boot rom file not opened"); // Open the Game ROM - if ((gameROM = fopen("../tests/Tetris.gb", "rb")) == NULL) + if ((gameROM = fopen("../tests/pkmnred.gb", "rb")) == NULL) printf("game rom file not opened"); // Load the Boot ROM @@ -99,449 +99,6 @@ GBE::GBE() gbe_mMap->debugWriteMemory(0x132, 0x33); gbe_mMap->debugWriteMemory(0x133, 0x3E); - // Using the Tetris header to pass the checksum test - // Unnecessary if loading a valid rom - - /*gbe_mMap->debugWriteMemory(0x134, 0x54); - gbe_mMap->debugWriteMemory(0x135, 0x45); - gbe_mMap->debugWriteMemory(0x136, 0x54); - gbe_mMap->debugWriteMemory(0x137, 0x52); - gbe_mMap->debugWriteMemory(0x138, 0x49); - gbe_mMap->debugWriteMemory(0x139, 0x53); - gbe_mMap->debugWriteMemory(0x13A, 0x00); - gbe_mMap->debugWriteMemory(0x13B, 0x00); - gbe_mMap->debugWriteMemory(0x13C, 0x00); - gbe_mMap->debugWriteMemory(0x13D, 0x00); - gbe_mMap->debugWriteMemory(0x13E, 0x00); - gbe_mMap->debugWriteMemory(0x13F, 0x00); - gbe_mMap->debugWriteMemory(0x140, 0x00); - gbe_mMap->debugWriteMemory(0x141, 0x00); - gbe_mMap->debugWriteMemory(0x142, 0x00); - gbe_mMap->debugWriteMemory(0x143, 0x00); - gbe_mMap->debugWriteMemory(0x144, 0x00); - gbe_mMap->debugWriteMemory(0x145, 0x00); - gbe_mMap->debugWriteMemory(0x146, 0x00); - gbe_mMap->debugWriteMemory(0x147, 0x00); - gbe_mMap->debugWriteMemory(0x148, 0x00); - gbe_mMap->debugWriteMemory(0x149, 0x00); - gbe_mMap->debugWriteMemory(0x14A, 0x00); - gbe_mMap->debugWriteMemory(0x14B, 0x01); - gbe_mMap->debugWriteMemory(0x14C, 0x01); - gbe_mMap->debugWriteMemory(0x14D, 0x0A);*/ - - // I have commented my tests for now as the Nintendo logo now sits there. - // We can test using blargg's test rom then. - - //// Current State: AF = 0x01B0, BC = 0x0013, DE = 0x00D8, HL = 0x014D, SP = 0xFFFE, PC = 0x0100 - - //// NOP - //// Does nothing - //// Final State: No change - // gbe_mMap->debugWriteMemory(0x0100, 0x00); - - //// LD BC, u16 - //// Loads a 16 bit immediate into the register BC - //// Final State: BC = 0XE001 - // gbe_mMap->debugWriteMemory(0x0101, 0x01); - // gbe_mMap->debugWriteMemory(0x0102, 0x01); - // gbe_mMap->debugWriteMemory(0x0103, 0xE0); - - //// LD (BC), A - //// Loads the value of the accumulator into the memory address pointed to by BC - //// Final State: Value 0x01 at address 0xE001 (2nd byte of Work RAM) - // gbe_mMap->debugWriteMemory(0x0104, 0x02); - - //// INC BC - //// Increments the value of BC by 1 - //// Final State: BC = 0xE002 - // gbe_mMap->debugWriteMemory(0x0105, 0x03); - - //// INC B - //// Increments the value of B by 1 - //// Final State: BC = 0xE102, Flag_N = 0, Flag_H = 0, Flag_Z = 0, AF = 0110 - // gbe_mMap->debugWriteMemory(0x0106, 0x04); - - //// Set BC = 0XFFFF to test INC B for Z flag - // gbe_mMap->debugWriteMemory(0x0107, 0x01); - // gbe_mMap->debugWriteMemory(0x0108, 0xFF); - // gbe_mMap->debugWriteMemory(0x0109, 0xFF); - - //// INC B - //// Increments the value of B by 1 - //// Final State: BC = 0x00FF, Flag_N = 0, Flag_H = 1, Flag_Z = 1, AF = 01B0 - // gbe_mMap->debugWriteMemory(0x010A, 0x04); - - //// Set BC = 0X0FFF to test INC B for H flag - // gbe_mMap->debugWriteMemory(0x010B, 0x01); - // gbe_mMap->debugWriteMemory(0x010C, 0xFF); - // gbe_mMap->debugWriteMemory(0x010D, 0x0F); - - //// INC B - //// Increments the value of B by 1 - //// Final State: BC = 0x10FF, Flag_N = 0, Flag_H = 1, Flag_Z = 0, AF = 0130 - // gbe_mMap->debugWriteMemory(0x010E, 0x04); - - //// DEC B - //// Decrements the value of B by 1 - //// Final State: BC = 0x0FFF, Flag_N = 1, Flag_H = 1, Flag_Z = 0, AF = 0170 - // gbe_mMap->debugWriteMemory(0x010F, 0x05); - - //// Set BC = 0X01FF to test DEC B for Z flag - // gbe_mMap->debugWriteMemory(0x0110, 0x01); - // gbe_mMap->debugWriteMemory(0x0111, 0xFF); - // gbe_mMap->debugWriteMemory(0x0112, 0x01); - - //// DEC B - //// Decrements the value of B by 1 - //// Final State: BC = 0x00FF, Flag_N = 1, Flag_H = 0, Flag_Z = 1, AF = 01D0 - // gbe_mMap->debugWriteMemory(0x0113, 0x05); - - //// Set BC = 0X020F to test DEC B - // gbe_mMap->debugWriteMemory(0x0114, 0x01); - // gbe_mMap->debugWriteMemory(0x0115, 0x0F); - // gbe_mMap->debugWriteMemory(0x0116, 0x02); - - //// DEC B - //// Decrements the value of B by 1 - //// Final State: BC = 0x010F, Flag_N = 1, Flag_H = 0, Flag_Z = 0, AF = 0150 - // gbe_mMap->debugWriteMemory(0x0117, 0x05); - - //// LD B, u8 - //// Loads an 8 bit immediate into the register B - //// Final State: BC = 0x690F - // gbe_mMap->debugWriteMemory(0x0118, 0x06); - // gbe_mMap->debugWriteMemory(0x0119, 0x69); - - //// RLCA - //// Rotates the value of the accumulator to the left - //// Final State: AF = 0x0200, Flag_C = 0 - // gbe_mMap->debugWriteMemory(0x011A, 0x07); - - //// Set A = 0x80 to test RLCA for C Flag - - //// RLCA - //// TODO: Will write later when LD A instructions have been tested - //// as that is needed to test this instruction more for flags - - //// LD (u16), SP - //// Loads the value of the stack pointer into the memory address pointed to by the immediate - //// Final State: Value 0xFFFE at address 0XE002 - // gbe_mMap->debugWriteMemory(0x011B, 0x08); - // gbe_mMap->debugWriteMemory(0x011C, 0x02); - // gbe_mMap->debugWriteMemory(0x011D, 0xE0); - - //// ADD HL, BC - //// Adds the value of BC to HL - //// Final State: HL = 0x6BFC - // gbe_mMap->debugWriteMemory(0x011E, 0x09); - - //// LD BC u16 - //// Loading address 0xE003 in BC to test next instruction - // gbe_mMap->debugWriteMemory(0x011F, 0x01); - // gbe_mMap->debugWriteMemory(0x0120, 0x03); - // gbe_mMap->debugWriteMemory(0x0121, 0xE0); - - //// LD A, (BC) - //// Loads the value of the memory address pointed to by BC into the accumulator - //// Final State: A = 0xFE, AF = 0xFE00 - // gbe_mMap->debugWriteMemory(0x0122, 0x0A); - - //// DEC BC - //// Decrements the value of BC by 1 - //// Final State: BC = 0xE002 - // gbe_mMap->debugWriteMemory(0x0123, 0x0B); - - //// INC C - //// Increments the value of C by 1 - //// Final State: BC = 0xE003, Flag_N = 0, Flag_H = 0, Flag_Z = 0, AF = 0xFE00 - // gbe_mMap->debugWriteMemory(0x0124, 0x0C); - - //// LD BC u16 - //// Loading address 0xE00F in BC to test H flag of INC C - // gbe_mMap->debugWriteMemory(0x0125, 0x01); - // gbe_mMap->debugWriteMemory(0x0126, 0x0F); - // gbe_mMap->debugWriteMemory(0x0127, 0xE0); - - //// INC C - //// Increments the value of C by 1 - //// Final State: BC = 0xE010, Flag_N = 0, Flag_H = 1, Flag_Z = 0, AF = 0xFE20 - // gbe_mMap->debugWriteMemory(0x0128, 0x0C); - - //// LD BC u16 - //// Loading value 0xE0FF in BC to test Z flag of INC C - // gbe_mMap->debugWriteMemory(0x0129, 0x01); - // gbe_mMap->debugWriteMemory(0x012A, 0xFF); - // gbe_mMap->debugWriteMemory(0x012B, 0xE0); - - //// INC C - //// Increments the value of C by 1 - //// Final State: BC = 0xE010, Flag_N = 0, Flag_H = 1, Flag_Z = 0, AF = 0xFE20 - // gbe_mMap->debugWriteMemory(0x012C, 0x0C); - - //// DEC C - //// Decrements the value of C by 1 - //// Final State: BC = 0xE0FF, Flag_N = 1, Flag_H = 1, Flag_Z = 0, AF = 0xFE60 - // gbe_mMap->debugWriteMemory(0x012D, 0x0D); - - //// LD BC u16 - //// Loading value 0xE001 in BC to test H flag of DEC C - // gbe_mMap->debugWriteMemory(0x012E, 0x01); - // gbe_mMap->debugWriteMemory(0x012F, 0x01); - // gbe_mMap->debugWriteMemory(0x0130, 0xE0); - - //// DEC C - //// Decrements the value of C by 1 - //// Final State: BC = 0xE000, Flag_N = 1, Flag_H = 0, Flag_Z = 1, AF = 0xFEB0 - // gbe_mMap->debugWriteMemory(0x0131, 0x0D); - - //// LD C, u8 - //// Loads an 8 bit immediate into the register C - //// Final State: BC = 0xE069 - // gbe_mMap->debugWriteMemory(0x0132, 0x0E); - // gbe_mMap->debugWriteMemory(0x0133, 0x69); - - //// RRCA - //// Rotates the value of the accumulator to the right - //// Final State: AF = 0x7F00, Flag_C = 0 - // gbe_mMap->debugWriteMemory(0x0134, 0x0F); - - //// STOP - //// Stops the CPU until an interrupt occurs - //// Did a NOP for now, as this stops execution of code. - //// TODO: Find a way to resume execution - // gbe_mMap->debugWriteMemory(0x0135, 0x00); - - //// STOP does an unnecessary byte read - // gbe_mMap->debugWriteMemory(0x0136, 0x00); - - //// LD DE, u16 - //// Loads an 16 bit immediate into the register DE - //// Final State: DE = 0xFFEC - // gbe_mMap->debugWriteMemory(0x0137, 0x11); - // gbe_mMap->debugWriteMemory(0x0138, 0xEC); - // gbe_mMap->debugWriteMemory(0x0139, 0xFF); - - //// LD (DE), A - //// Loads the value of the accumulator into the memory address pointed to by DE - //// Final State: Value 0x7F at address 0x69E0 - // gbe_mMap->debugWriteMemory(0x013A, 0x12); - - //// INC DE - //// Increments the value of DE by 1 - //// Final State: DE = 0x69E1 - // gbe_mMap->debugWriteMemory(0x013B, 0x13); - - //// INC D - //// Increments the value of D by 1 - //// Final State: DE = 0x6AE1, Flag_N = 0, Flag_H = 0, Flag_Z = 0, AF = 0x7F00 - // gbe_mMap->debugWriteMemory(0x013C, 0x14); - - //// DEC D - //// Decrements the value of D by 1 - //// Final State: DE = 0x69E1, Flag_N = 1, Flag_H = 0, Flag_Z = 0, AF = 0x7F40 - // gbe_mMap->debugWriteMemory(0x013D, 0x15); - - //// LD D, u8 - //// Loads an 8 bit immediate into the register D - //// Final State: DE = 0xE0E1 - // gbe_mMap->debugWriteMemory(0x013E, 0x16); - // gbe_mMap->debugWriteMemory(0x013F, 0xE0); - - //// RLA - //// Rotates the value of the accumulator to the left - //// Final State: AF = 0xFE00, Flag_C = 0 - // gbe_mMap->debugWriteMemory(0x0140, 0x17); - - //// RLA - //// Rotates the value of the accumulator to the left - //// Final State: AF = 0xFC10, Flag_C = 1 - // gbe_mMap->debugWriteMemory(0x0141, 0x17); - - //// JR i8 - //// Jumps to the address at PC + i8 + 2 - //// Final State: Next instruction is selected - //// i8 is a signed 8 bit value - // gbe_mMap->debugWriteMemory(0x0142, 0x18); - // gbe_mMap->debugWriteMemory(0x0143, 0x00); - - //// JR i8 - //// This one must go in an infinite loop - // gbe_mMap->debugWriteMemory(0x0144, 0x18); - // gbe_mMap->debugWriteMemory(0x0145, 0xFE); - - //// TODO: ADD HL, DE test after implementing and testing LD HL, u16 - - //// Loading 0x0100 into DE to test LD (DE), A - //// Final State: DE = 0x0100 - // gbe_mMap->debugWriteMemory(0x0146, 0x11); - // gbe_mMap->debugWriteMemory(0x0147, 0x00); - // gbe_mMap->debugWriteMemory(0x0148, 0x01); - - //// LD A, (DE) - //// Loads the value of the memory address pointed to by DE into the accumulator - //// Final State: AF = 0x0010 - // gbe_mMap->debugWriteMemory(0x0149, 0x1A); - - //// DEC DE - //// Decrements the value of DE by 1 - //// Final State: DE = 0x00FF - // gbe_mMap->debugWriteMemory(0x014A, 0x1B); - - //// INC E - //// Increments the value of E by 1 - //// Final State: DE = 0x0100, Flag_N = 0, Flag_H = 1, Flag_Z = 1, AF = 0x00A0 - // gbe_mMap->debugWriteMemory(0x014B, 0x1C); - - //// DEC E - //// Decrements the value of E by 1 - //// Final State: DE = 0x00FF, Flag_N = 1, Flag_H = 1, Flag_Z = 0, AF = 0x0060 - // gbe_mMap->debugWriteMemory(0x014C, 0x1D); - - //// LD E, u8 - //// Loads an 8 bit immediate into the register E - //// Final State: DE = 0x00E0 - // gbe_mMap->debugWriteMemory(0x014D, 0x1E); - // gbe_mMap->debugWriteMemory(0x014E, 0xE0); - - //// Loading a value into the accumulator to test RRA - //// Final State: DE = 0x014E, AF = 0xE000 - // gbe_mMap->debugWriteMemory(0x014F, 0x11); - // gbe_mMap->debugWriteMemory(0x0150, 0x4E); - // gbe_mMap->debugWriteMemory(0x0151, 0x01); - // gbe_mMap->debugWriteMemory(0x0152, 0x1A); - - //// RRA - //// Rotates the value of the accumulator to the right - //// Final State: AF = 0x7000, Flag_C = 0 - // gbe_mMap->debugWriteMemory(0x0153, 0x1F); - - //// Loading a value into the accumulator to test RRA - //// Final State: DE = 0x0151, AF = 0x0100 - // gbe_mMap->debugWriteMemory(0x0154, 0x11); - // gbe_mMap->debugWriteMemory(0x0155, 0x51); - // gbe_mMap->debugWriteMemory(0x0156, 0x01); - // gbe_mMap->debugWriteMemory(0x0157, 0x1A); - - //// RRA - //// Rotates the value of the accumulator to the right - //// Final State: AF = 0x0010, Flag_C = 1 - // gbe_mMap->debugWriteMemory(0x0158, 0x1F); - - //// JR NZ, i8 - //// Jumps to the address at PC + i8 + 2 if the zero flag is not set - //// Final State: Next instruction is not selected as the zero flag is not set - //// PC = 0x015C - // gbe_mMap->debugWriteMemory(0x0159, 0x20); - // gbe_mMap->debugWriteMemory(0x015A, 0x01); - - //// NOP - //// This must be skipped - // gbe_mMap->debugWriteMemory(0x015B, 0x00); - - //// Setting the zero flag to test JR NZ, i8 - //// Load 0xFF in B and INC B - //// Final State: BC = 0x0069, AF = 0x00B0, Flag_Z = 1 - // gbe_mMap->debugWriteMemory(0x015C, 0x06); - // gbe_mMap->debugWriteMemory(0x015D, 0xFF); - // gbe_mMap->debugWriteMemory(0x015E, 0x04); - - //// JR NZ, i8 - //// Jumps to the address at PC + i8 + 2 if the zero flag is not set - //// Final State: Next instruction is selected as the zero flag is set - //// PC = 0x0161 - // gbe_mMap->debugWriteMemory(0x015F, 0x20); - // gbe_mMap->debugWriteMemory(0x0160, 0x01); - - //// NOP - //// This must be executed - // gbe_mMap->debugWriteMemory(0x0161, 0x00); - - //// LD HL, u16 - //// Loads a 16 bit immediate into the register HL - //// Final State: HL = 0xC010 - // gbe_mMap->debugWriteMemory(0x0162, 0x21); - // gbe_mMap->debugWriteMemory(0x0163, 0x10); - // gbe_mMap->debugWriteMemory(0x0164, 0xC0); - - //// LD (HL+), A - //// Loads the value of the accumulator into the memory address pointed to by HL - //// and then increments HL - //// Final State: HL = 0xC011, AF = 0x0010, 0X00 at 0XC010 - // gbe_mMap->debugWriteMemory(0x0165, 0x22); - - //// INC HL - //// Increments the value of HL by 1 - //// Final State: HL = 0xC012 - // gbe_mMap->debugWriteMemory(0x0166, 0x23); - - //// INC H - //// Increments the value of H by 1 - //// Final State: HL = 0xC113, Flag_N = 0, Flag_H = 0, Flag_Z = 0, AF = 0x0010 - // gbe_mMap->debugWriteMemory(0x0167, 0x24); - - //// DEC H - //// Decrements the value of H by 1 - //// Final State: HL = 0xC112, Flag_N = 1, Flag_H = 0, Flag_Z = 0, AF = 0x0050 - // gbe_mMap->debugWriteMemory(0x0168, 0x25); - - //// LD H, u8 - //// Loads an 8 bit immediate into the register H - //// Final State: HL = 0xA012 - // gbe_mMap->debugWriteMemory(0x0169, 0x26); - // gbe_mMap->debugWriteMemory(0x016A, 0xA0); - - //// DAA - //// Adjusts the value of the accumulator to be a valid BCD value - //// Final State: AF = 0x0010 - //// TODO: Test this. High risk opcode - // gbe_mMap->debugWriteMemory(0x016B, 0x27); - - //// JR Z, i8 - //// Jumps to the address at PC + i8 + 2 if the zero flag is set - //// Final State: Next instruction is selected as the zero flag is not set - //// PC = 0x016E - // gbe_mMap->debugWriteMemory(0x016C, 0x28); - // gbe_mMap->debugWriteMemory(0x016D, 0x01); - - //// NOP - //// This must be not skipped - // gbe_mMap->debugWriteMemory(0x016E, 0x00); - - //// Setting the zero flag to test JR Z, i8 - //// Load 0xFF in B and INC B - // gbe_mMap->debugWriteMemory(0x016F, 0x06); - // gbe_mMap->debugWriteMemory(0x0170, 0xFF); - // gbe_mMap->debugWriteMemory(0x0171, 0x04); - - //// JR Z, i8 - //// Jumps to the address at PC + i8 + 2 if the zero flag is set - //// Final State: Next instruction is not selected as the zero flag is set - //// PC = 0x0175 - // gbe_mMap->debugWriteMemory(0x0172, 0x28); - // gbe_mMap->debugWriteMemory(0x0173, 0x01); - - //// NOP - //// This must be skipped - // gbe_mMap->debugWriteMemory(0x0174, 0x00); - - //// ADD HL, HL - //// Adds the value of HL to HL - //// Final State: HL = 0x4024, Flag_N = 0, Flag_H = 0, Flag_C = 0, AF = 0x0010 - // gbe_mMap->debugWriteMemory(0x0175, 0x29); - - //// Loading value in HL to test next opcode - // gbe_mMap->debugWriteMemory(0x0162, 0x21); - // gbe_mMap->debugWriteMemory(0x0163, 0x10); - // gbe_mMap->debugWriteMemory(0x0164, 0xC0); - - //// LD A, (HL+) - //// Loads the value of the memory address pointed to by HL into the accumulator - //// and then increments HL - //// Final State: HL = 0xC013, AF = 0x0010, 0X00 at 0XC011 - - //// Seg fault to end using UNKOWN - // gbe_mMap->debugWriteMemory(0x0146, 0xEB); - executeBootROM(); update(); diff --git a/src/mmap.cpp b/src/mmap.cpp index 1e91f1a..3a35040 100644 --- a/src/mmap.cpp +++ b/src/mmap.cpp @@ -100,6 +100,9 @@ MemoryMap::MemoryMap() joyPadState = new Byte; *joyPadState = 0xFF; + + bootRomFile = nullptr; + romFile = nullptr; } // Write to memory diff --git a/src/mmap.h b/src/mmap.h index 442476e..3725252 100644 --- a/src/mmap.h +++ b/src/mmap.h @@ -10,6 +10,9 @@ class MemoryMap private: Byte mbcMode; + FILE *bootRomFile; + FILE *romFile; + // First ROM Bank // 16 KB 0x0000 - 0x3FFF // Contains the first 16 KB of the ROM From 3366ccdcf91a096af1e4e53cdd524f279cf58239 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Tue, 2 May 2023 11:21:20 +0530 Subject: [PATCH 23/34] Remove CPU Debug framework --- src/cpu.cpp | 23 ----------------------- src/cpu.h | 6 ------ src/gameBoy.cpp | 6 ------ 3 files changed, 35 deletions(-) diff --git a/src/cpu.cpp b/src/cpu.cpp index d5fa79f..d2a6872 100644 --- a/src/cpu.cpp +++ b/src/cpu.cpp @@ -58,9 +58,6 @@ CPU::CPU() // Set isHalted to false isHalted = false; - // The debug logging file - outfile = fopen("logfile.txt", "w"); - // TODO: check the initial state of IME IMEFlag = -1; @@ -3971,23 +3968,8 @@ int CPU::executeInstruction(Byte opcode) int CPU::executeNextInstruction() { - // Check if boot execution is complete - // If yes, we can do logging in debug log outfile - if (mMap->readMemory(0xFF50) == 0x01) - { - dumpState(); - } - - // Turn off logging - // If reached infinite loop - if (reg_PC.dat > 0xCC62) - { - fclose(outfile); - } - // Get the opcode Byte opcode = (*mMap)[reg_PC.dat]; - //mMap->writeMemory(0xFF00, 0xFF); return (this->*method_pointer[opcode])(); } @@ -7790,11 +7772,6 @@ int CPU::SET_7_A() return 4; } -void CPU::dumpState() -{ - //fprintf(outfile, "A:%02X F:%02X B:%02X C:%02X D:%02X E:%02X H:%02X L:%02X SP:%04X PC:%04X PCMEM:%02X,%02X,%02X,%02X\n", reg_AF.hi, reg_AF.lo, reg_BC.hi, reg_BC.lo, reg_DE.hi, reg_DE.lo, reg_HL.hi, reg_HL.lo, reg_SP.dat, reg_PC.dat, (*mMap)[reg_PC.dat], (*mMap)[reg_PC.dat + 1], (*mMap)[reg_PC.dat + 2], (*mMap)[reg_PC.dat + 3]); -} - // Checks for interrupts and services them if needed // Behaviour source: https://gbdev.io/pandocs/Interrupts.html int CPU::performInterrupt() diff --git a/src/cpu.h b/src/cpu.h index 7b9119a..b7f3e48 100644 --- a/src/cpu.h +++ b/src/cpu.h @@ -1132,12 +1132,6 @@ class CPU int SET_7_HLp(); int SET_7_A(); - // Dump CPU state in logfile - // Useful for debugging - void dumpState(); - - FILE* outfile; - public: const int clockSpeed = 4194304; // 4.194304 MHz CPU const int clockSpeedPerFrame = 70224; // 4194304 / 59.73fps diff --git a/src/gameBoy.cpp b/src/gameBoy.cpp index d95f774..48941d7 100644 --- a/src/gameBoy.cpp +++ b/src/gameBoy.cpp @@ -113,11 +113,6 @@ void GBE::update() { // Execute the next instruction s_Cycles += gbe_cpu->executeNextInstruction(); - if ((*gbe_mMap)[0xFF02] == 0x81) - { - printf("%c", (*gbe_mMap)[0xFF01]); - gbe_mMap->writeMemory(0xFF02, 0x00); - } // update the DIV and TIMA timers gbe_cpu->updateTimers(s_Cycles); @@ -126,7 +121,6 @@ void GBE::update() s_Cycles += gbe_cpu->performInterrupt(); gbe_graphics->pollEvents(); } - // renderGraphics() } void GBE::executeBootROM() From 3f9cbed96d93d815120bfd246fbed37f08622aff Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Tue, 2 May 2023 11:48:28 +0530 Subject: [PATCH 24/34] Move ROM load logic to MemoryMap --- src/gameBoy.cpp | 25 ++++++++----------------- src/mmap.cpp | 18 ++++++++++++++++++ src/mmap.h | 12 +++++++++++- 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/gameBoy.cpp b/src/gameBoy.cpp index 48941d7..9f565db 100644 --- a/src/gameBoy.cpp +++ b/src/gameBoy.cpp @@ -31,19 +31,17 @@ GBE::GBE() printf("boot rom file not opened"); // Open the Game ROM - if ((gameROM = fopen("../tests/pkmnred.gb", "rb")) == NULL) + if ((gameROM = fopen("../tests/Tetris.gb", "rb")) == NULL) printf("game rom file not opened"); - // Load the Boot ROM - // Into the first 0x100 bytes - fread(gbe_mMap->getRomBank0(), 1, 256, bootROM); + // Set the Boot ROM + gbe_mMap->setBootRomFile(bootROM); - // Load Game ROM in Bank 0 - // After offsetting for Boot ROM first - fseek(gameROM, 0x100, SEEK_SET); + // Set the Game ROM + gbe_mMap->setRomFile(gameROM); - fread(gbe_mMap->getRomBank0() + 0x100, 1, 16128, gameROM); - fread(gbe_mMap->getRomBank1(), 1, 16384, gameROM); + // Map to ROMs to mMap + gbe_mMap->mapRom(); s_Cycles = 0; @@ -128,18 +126,11 @@ void GBE::executeBootROM() while (gbe_mMap->readMemory(0xFF50) == 0x00) { s_Cycles += gbe_cpu->executeNextInstruction(); - if ((*gbe_mMap)[0xFF02] == 0x81) - { - printf("%c", (*gbe_mMap)[0xFF01]); - gbe_mMap->writeMemory(0xFF02, 0x00); - } gbe_cpu->updateTimers(s_Cycles); gbe_graphics->executePPU(s_Cycles); s_Cycles = 0; s_Cycles += gbe_cpu->performInterrupt(); } - // Overwrite the boot ROM with first 256 bytes of game ROM - fseek(gameROM, 0x00, SEEK_SET); - fread(gbe_mMap->getRomBank0(), 1, 256, gameROM); + gbe_mMap->unloadBootRom(); } \ No newline at end of file diff --git a/src/mmap.cpp b/src/mmap.cpp index 3a35040..237b398 100644 --- a/src/mmap.cpp +++ b/src/mmap.cpp @@ -295,4 +295,22 @@ void MemoryMap::readInput(Byte value) (*reg_IF) |= 0x10; ioPorts[0] = current; +} + +void MemoryMap::mapRom() { + // Load the Boot ROM + // Into the first 0x100 bytes + fread(romBank0, 1, 256, bootRomFile); + + // Load Game ROM in Bank 0 + // After offsetting for Boot ROM first + fseek(romFile, 0x100, SEEK_SET); + + fread(romBank0 + 0x100, 1, 16128, romFile); + fread(romBank1, 1, 16384, romFile); +} + +void MemoryMap::unloadBootRom() { + fseek(romFile, 0x00, SEEK_SET); + fread(romBank0, 1, 256, romFile); } \ No newline at end of file diff --git a/src/mmap.h b/src/mmap.h index 3725252..5c2f4e5 100644 --- a/src/mmap.h +++ b/src/mmap.h @@ -191,6 +191,12 @@ class MemoryMap // increments the divider register void updateDividerRegister() { (*reg_DIV)++; } + // Map the boot and game to memory4 + void mapRom(); + + // Unload boot ROM after boot execution + void unloadBootRom(); + // gets the reg_TAC Byte getRegTAC() { return *reg_TAC; } @@ -254,5 +260,9 @@ class MemoryMap // sets the reg_WY void setRegWY(Byte value) { *reg_WY = value; } - //void setPPU(PPU* ppu) { gbe_ppu = ppu; } + // sets the boot ROM + void setBootRomFile(FILE* file) { bootRomFile = file; } + + // sets the ROM file + void setRomFile(FILE* file) { romFile = file; } }; \ No newline at end of file From e09a146a5fa00fe718c7db7a07775c091a940c02 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Tue, 2 May 2023 12:06:04 +0530 Subject: [PATCH 25/34] Init MBC --- src/gameBoy.cpp | 2 +- src/mmap.cpp | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/gameBoy.cpp b/src/gameBoy.cpp index 9f565db..3041768 100644 --- a/src/gameBoy.cpp +++ b/src/gameBoy.cpp @@ -31,7 +31,7 @@ GBE::GBE() printf("boot rom file not opened"); // Open the Game ROM - if ((gameROM = fopen("../tests/Tetris.gb", "rb")) == NULL) + if ((gameROM = fopen("../tests/pkmnred.gb", "rb")) == NULL) printf("game rom file not opened"); // Set the Boot ROM diff --git a/src/mmap.cpp b/src/mmap.cpp index 237b398..207b5eb 100644 --- a/src/mmap.cpp +++ b/src/mmap.cpp @@ -103,6 +103,8 @@ MemoryMap::MemoryMap() bootRomFile = nullptr; romFile = nullptr; + + mbcMode = 0x0; } // Write to memory @@ -308,6 +310,9 @@ void MemoryMap::mapRom() { fread(romBank0 + 0x100, 1, 16128, romFile); fread(romBank1, 1, 16384, romFile); + + // Check 0x147 for MBC mode + mbcMode = romBank0[0x147]; } void MemoryMap::unloadBootRom() { From 741db22b04afb9234906907d74665b016fcc7c56 Mon Sep 17 00:00:00 2001 From: Parth-Nebula Date: Mon, 5 Jun 2023 00:15:37 +0530 Subject: [PATCH 26/34] Create Banks, Registers and Masks for MBC1 --- src/mmap.cpp | 26 +++++++++++++++++++++-- src/mmap.h | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 80 insertions(+), 4 deletions(-) diff --git a/src/mmap.cpp b/src/mmap.cpp index 207b5eb..b2c947f 100644 --- a/src/mmap.cpp +++ b/src/mmap.cpp @@ -105,6 +105,26 @@ MemoryMap::MemoryMap() romFile = nullptr; mbcMode = 0x0; + + romSize = 0x0; + + ramSize = 0x0; + + ramEnable = 0; + + romBankNumber = 0; + + ramBankNumber = 0; + + bankingModeSelect = 0; + + ramExistenceMask = 0; + + romBankNumberMask = 0; + + ramBankNumberMaskForRom = 0; + + ramBankNumberMaskForRam = 0; } // Write to memory @@ -299,7 +319,8 @@ void MemoryMap::readInput(Byte value) ioPorts[0] = current; } -void MemoryMap::mapRom() { +void MemoryMap::mapRom() +{ // Load the Boot ROM // Into the first 0x100 bytes fread(romBank0, 1, 256, bootRomFile); @@ -315,7 +336,8 @@ void MemoryMap::mapRom() { mbcMode = romBank0[0x147]; } -void MemoryMap::unloadBootRom() { +void MemoryMap::unloadBootRom() +{ fseek(romFile, 0x00, SEEK_SET); fread(romBank0, 1, 256, romFile); } \ No newline at end of file diff --git a/src/mmap.h b/src/mmap.h index 5c2f4e5..8b0bf4e 100644 --- a/src/mmap.h +++ b/src/mmap.h @@ -10,8 +10,8 @@ class MemoryMap private: Byte mbcMode; - FILE *bootRomFile; - FILE *romFile; + FILE* bootRomFile; + FILE* romFile; // First ROM Bank // 16 KB 0x0000 - 0x3FFF @@ -137,6 +137,60 @@ class MemoryMap // Stays in the I/O Ports at 0xFF4B Byte* reg_WX; + // Number indicating number of ROM Banks available + // Number of ROM Banks = ( 2 << romSize ) + Byte romSize; + + // Number of RAM Banks available + // Number of ramSize RAM Banks + // 0 0 + // 2 1 + // 3 4 + // 4 16 + // 5 8 + Byte ramSize; + + // All ROM Banks + // 16KiB each + Byte (*romBankList)[0x4000]; + + // All RAM Banks + // 8KiB each + Byte (*ramBankList)[0x2000]; + + // 1 bit MBC register + // Tells whether External RAM is enabled for read and write + bool ramEnable; + + // 5 bit MBC register + // Tells which ROM Bank to use + Byte romBankNumber; + + // 2 bit MBC register + // Tells which RAM Bank to use + // Might also tell which ROM Bank to use in case of ROM > 512KiB + Byte ramBankNumber; + + // 1 bit MBC register + // Selects the banking mode + bool bankingModeSelect; + + // 1 bit mask + // Tells if External RAM is available in the Cartridge + bool ramExistenceMask; + + // 5 bit mask + // Tells the number of bits of ROM Bank Number that are useful + Byte romBankNumberMask; + + // 2 bit mask + // Tells the number of bits of RAM Bank Number that are useful for ROM + Byte ramBankNumberMaskForRom; + + // 2 bit mask + // Tells the number of bits of RAM Bank Number that are useful for RAM + Byte ramBankNumberMaskForRam; + public: Byte* joyPadState; From 84f8e5dc65546c851a179f14f91a6af110d1c972 Mon Sep 17 00:00:00 2001 From: Parth-Nebula Date: Mon, 5 Jun 2023 00:19:29 +0530 Subject: [PATCH 27/34] Read ROM banks from the game rom file --- src/mmap.cpp | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/src/mmap.cpp b/src/mmap.cpp index b2c947f..b1a14a5 100644 --- a/src/mmap.cpp +++ b/src/mmap.cpp @@ -332,8 +332,88 @@ void MemoryMap::mapRom() fread(romBank0 + 0x100, 1, 16128, romFile); fread(romBank1, 1, 16384, romFile); + // MBC + // Check 0x147 for MBC mode mbcMode = romBank0[0x147]; + + // Check 0x148 for ROM size + romSize = romBank0[0x148]; + + // Check 0x149 for RAM size + ramSize = romBank0[0x149]; + + // Seek to begining of ROM + fseek(romFile, 0x00, SEEK_SET); + + // Get the ROM banks + romBankList = new Byte[2 << romSize][0x4000]; + for (int i = 0; i < (2 << romSize); ++i) + { + memset(romBankList[i], 0x00, 0x4000); + } + for (int i = 0; i < (2 << romSize); ++i) + { + fread(romBankList[i], 1, 16384, romFile); + } + + // Set RAM banks + int ramBanks = 0; + switch (ramSize) + { + case 0: + ramBanks = 0; + break; + case 2: + ramBanks = 1; + break; + case 3: + ramBanks = 4; + break; + case 4: + ramBanks = 16; + break; + case 5: + ramBanks = 8; + break; + } + ramBankList = new Byte[ramBanks][0x2000]; + for (int i = 0; i < ramBanks; ++i) + { + memset(ramBankList[i], 0x00, 0x2000); + } + if (ramBanks > 0) + { + externalRam = ramBankList[0]; + } + + // Set the RAM Existence Mask + // Tells if External RAM is available in the Cartridge + if ((mbcMode == 2 or mbcMode == 3) and ramBanks > 0) + { + ramExistenceMask = 1; + } + + // Set the ROM Bank Number Mask + // Tells the useful bits in the ROM Bank number depending on ROM size + romBankNumberMask = ((2 << romSize) < 0b100000) ? (2 << romSize) : 0b100000; + romBankNumberMask -= 1; + + // Set the RAM Bank Number Mask for ROM + // Tells the useful bits in the RAM Bank number for ROM depending on ROM size + if (romSize > 4) + { + ramBankNumberMaskForRom = ((1 << (romSize - 4)) < 0b100) ? (1 << (romSize - 4)) : 0b100; + ramBankNumberMaskForRom -= 1; + } + + // Set the RAM Bank Number Mask for RAM + // Tells the useful bits in the RAM Bank number for RAM depending on RAM size + if (ramBanks > 0) + { + ramBankNumberMaskForRam = (ramBanks < 0b100) ? ramBanks : 0b100; + ramBankNumberMaskForRam -= 1; + } } void MemoryMap::unloadBootRom() From 95a3c047b6b7d2ba69bef0b68d060fbde0086fef Mon Sep 17 00:00:00 2001 From: NehaGujar1 Date: Mon, 5 Jun 2023 01:49:26 +0530 Subject: [PATCH 28/34] Add banking functions --- src/mmap.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/mmap.h b/src/mmap.h index 8b0bf4e..5b0a0aa 100644 --- a/src/mmap.h +++ b/src/mmap.h @@ -319,4 +319,10 @@ class MemoryMap // sets the ROM file void setRomFile(FILE* file) { romFile = file; } + + // Change ROM Banking according to the set registers + void bankRom(); + + // Change RAM Banking according to the set registers + void bankRam(); }; \ No newline at end of file From 7253d917c54a25fb06086e0e89ffb9ef1bc69905 Mon Sep 17 00:00:00 2001 From: NehaGujar1 Date: Mon, 5 Jun 2023 01:51:39 +0530 Subject: [PATCH 29/34] Make read changes in ROM Bank 0 and ROM Bank 1 --- src/mmap.cpp | 65 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/src/mmap.cpp b/src/mmap.cpp index b1a14a5..874b9b1 100644 --- a/src/mmap.cpp +++ b/src/mmap.cpp @@ -131,10 +131,37 @@ MemoryMap::MemoryMap() // TODO: Make emulation memory secure bool MemoryMap::writeMemory(Word address, Byte value) { - if (address < 0x8000) + if (address < 0x2000) { - printf("Writing to ROM is not allowed! Write attempted at %04X", address); - return false; + // If values is 0x0A then external RAM is enabled, else it is disabled + if (value == 0x0A) + { + ramEnable = true; + } + else + { + ramEnable = false; + } + } + else if (address < 0x4000) + { + // Decide ROM Bank Number + romBankNumber = (value & 0b11111); + bankRom(); + } + else if (address < 0x6000) + { + // Decide RAM Bank Number + ramBankNumber = (value & 0b11); + bankRom(); + bankRam(); + } + else if (address < 0x8000) + { + // Decide Banking Mode Select + bankingModeSelect = (value & 0b1); + bankRom(); + bankRam(); } else if (address < 0xA000) { @@ -143,8 +170,11 @@ bool MemoryMap::writeMemory(Word address, Byte value) } else if (address < 0xC000) { - // Write to External RAM - externalRam[address - 0xA000] = value; + // Write to External RAM if external RAM has been enabled + if (ramEnable) + { + externalRam[address - 0xA000] = value; + } } else if (address < 0xE000) { @@ -420,4 +450,29 @@ void MemoryMap::unloadBootRom() { fseek(romFile, 0x00, SEEK_SET); fread(romBank0, 1, 256, romFile); +} + +void MemoryMap::bankRom() +{ + + Byte completeBankNumber = romBankNumber; + + if (completeBankNumber == 0) + { + completeBankNumber += 1; + } + + completeBankNumber &= romBankNumberMask; + completeBankNumber |= ((ramBankNumber & ramBankNumberMaskForRom) << 5); + + romBank0 = romBankList[((ramBankNumber & ramBankNumberMaskForRom) << 5) & (bankingModeSelect * 0b1100000)]; + romBank1 = romBankList[completeBankNumber]; +} + +void MemoryMap::bankRam() +{ + if (ramExistenceMask) + { + externalRam = ramBankList[(ramBankNumber & ramBankNumberMaskForRam) & (bankingModeSelect * 0b11)]; + } } \ No newline at end of file From 74de4f2eecf4d279ea6c14ba3b0e2998ec18fb22 Mon Sep 17 00:00:00 2001 From: NehaGujar1 Date: Sun, 11 Jun 2023 14:43:24 +0530 Subject: [PATCH 30/34] Cycle count issue --- src/gameBoy.cpp | 5 +- src/mmap.cpp | 131 +++++++++++++++++++++++++++++++++++++++++++++--- src/mmap.h | 28 ++++++++++- 3 files changed, 153 insertions(+), 11 deletions(-) diff --git a/src/gameBoy.cpp b/src/gameBoy.cpp index 3041768..0f3101a 100644 --- a/src/gameBoy.cpp +++ b/src/gameBoy.cpp @@ -31,7 +31,7 @@ GBE::GBE() printf("boot rom file not opened"); // Open the Game ROM - if ((gameROM = fopen("../tests/pkmnred.gb", "rb")) == NULL) + if ((gameROM = fopen("../tests/rtc3test.gb", "rb")) == NULL) printf("game rom file not opened"); // Set the Boot ROM @@ -111,7 +111,7 @@ void GBE::update() { // Execute the next instruction s_Cycles += gbe_cpu->executeNextInstruction(); - + gbe_mMap->updateRTCReg(s_Cycles); // update the DIV and TIMA timers gbe_cpu->updateTimers(s_Cycles); gbe_graphics->executePPU(s_Cycles); @@ -126,6 +126,7 @@ void GBE::executeBootROM() while (gbe_mMap->readMemory(0xFF50) == 0x00) { s_Cycles += gbe_cpu->executeNextInstruction(); + gbe_mMap->updateRTCReg(s_Cycles); gbe_cpu->updateTimers(s_Cycles); gbe_graphics->executePPU(s_Cycles); s_Cycles = 0; diff --git a/src/mmap.cpp b/src/mmap.cpp index 874b9b1..5741823 100644 --- a/src/mmap.cpp +++ b/src/mmap.cpp @@ -1,5 +1,6 @@ #include "mmap.h" #include +#include // Constructor MemoryMap::MemoryMap() @@ -125,6 +126,22 @@ MemoryMap::MemoryMap() ramBankNumberMaskForRom = 0; ramBankNumberMaskForRam = 0; + + RTCmax[0]=60; + RTCmax[1]=60; + RTCmax[2]=24; + RTCmax[3]=0xFF; + RTC[0]=0; + RTC[1]=0; + RTC[2]=0; + RTC[3]=0; + RTCmax[0]=60; + RTC[4]=0; + latch = false; + setRTC = false; + numberOfYearsCount = 0; + numberofCyclescount=0; + totalNumberofCyclescount = 0; } // Write to memory @@ -133,7 +150,7 @@ bool MemoryMap::writeMemory(Word address, Byte value) { if (address < 0x2000) { - // If values is 0x0A then external RAM is enabled, else it is disabled + // If values is 0x0A then external RAM and/or writing to RTC registers is enabled, else it is disabled if (value == 0x0A) { ramEnable = true; @@ -151,14 +168,29 @@ bool MemoryMap::writeMemory(Word address, Byte value) } else if (address < 0x6000) { - // Decide RAM Bank Number - ramBankNumber = (value & 0b11); - bankRom(); - bankRam(); + // Decide RAM Bank Number - if(val<=1b11) else if(08-0C) - RTC register -> read/written A000-BFFF mainly A000 - setRTC=true + if (value<=0b11) + { + ramBankNumber = (value & 0b11); + bankRom(); + bankRam(); + setRTC=false; + } + else if (value<=0b1100&&value>=0b100) + { + setRTC=true; + RTCval = value-8; + } + } else if (address < 0x8000) { - // Decide Banking Mode Select + // Decide Banking Mode Select - + latch=false; + if(bankingModeSelect==0&&(value&0b1==1)) + { + latch=true; + } bankingModeSelect = (value & 0b1); bankRom(); bankRam(); @@ -173,7 +205,17 @@ bool MemoryMap::writeMemory(Word address, Byte value) // Write to External RAM if external RAM has been enabled if (ramEnable) { - externalRam[address - 0xA000] = value; + if(setRTC) + { + RTC[4] = 0b10111111&RTC[4]; + RTC[4]+=0b01000000; + //overflow handle?? + // RTC[RTCval] = value; + // if(latch) + setRTCRegisters(value); + RTC[4]-=0b01000000; + } + else externalRam[address - 0xA000] = value; } } else if (address < 0xE000) @@ -271,6 +313,10 @@ Byte MemoryMap::readMemory(Word address) else if (address < 0xC000) { // Read from External RAM + if(setRTC) { + while ((RTC[4]&&0b01000000)) ; + return RTC[RTCval]; + } return externalRam[address - 0xA000]; } else if (address < 0xE000) @@ -459,7 +505,7 @@ void MemoryMap::bankRom() if (completeBankNumber == 0) { - completeBankNumber += 1; + //completeBankNumber += 1; //remove this for MBC3 } completeBankNumber &= romBankNumberMask; @@ -475,4 +521,73 @@ void MemoryMap::bankRam() { externalRam = ramBankList[(ramBankNumber & ramBankNumberMaskForRam) & (bankingModeSelect * 0b11)]; } +} + +void MemoryMap::setRTCRegisters(int value) +{ + printf("hi"); + int curr_set_time = RTCval; + while (curr_set_time<4&&value>0) + { + RTC[curr_set_time] = value%RTCmax[curr_set_time]; + value = value/RTCmax[curr_set_time]; + curr_set_time++; + } + if (value<=1) + { + RTC[4] = 0b11111110&RTC[4]; + RTC[4] += value; + numberOfYearsCount+=value; + } + else + { + /*not really handled*/ + RTC[4] = 0b01111111&RTC[4]; + RTC[4] = 0b10000000; + } + +} + +void MemoryMap::updateRTCReg(int cycles) +{ + totalNumberofCyclescount += cycles; + //printf("in update: %d\n",cycles); + if (!latch) + { + return; + } + int value = (int)(totalNumberofCyclescount) / 4194304; + if ((totalNumberofCyclescount) < 4194304) + { + // return; + } + printf("testing : %d\n",totalNumberofCyclescount); + //struct tm *gmtime(const time_t *time); + // time_t now = time(0); + // tm* gmtm = gmtime(&now); + // RTC[0]=gmtm->tm_sec; + // RTC[1]=gmtm->tm_min; + //RTC[2]= + int curr_set_time = 0; + while (curr_set_time<4&&value>0) + { + RTC[curr_set_time] += value; + RTC[curr_set_time]%=RTCmax[curr_set_time]; + value = value/RTCmax[curr_set_time]; + curr_set_time++; + } + if (value<=1) + { + RTC[4] = 0b11111110&RTC[4]; + RTC[4] += value; + numberOfYearsCount+=value; + } + else + { + /*not really handled*/ + RTC[4] = 0b01111111&RTC[4]; + RTC[4] = 0b10000000; + } + totalNumberofCyclescount=0; + latch = false; } \ No newline at end of file diff --git a/src/mmap.h b/src/mmap.h index 5b0a0aa..03d9cf1 100644 --- a/src/mmap.h +++ b/src/mmap.h @@ -160,7 +160,7 @@ class MemoryMap // 1 bit MBC register // Tells whether External RAM is enabled for read and write - bool ramEnable; + bool ramEnable; //will also enable RTC register // 5 bit MBC register // Tells which ROM Bank to use @@ -191,6 +191,28 @@ class MemoryMap // Tells the number of bits of RAM Bank Number that are useful for RAM Byte ramBankNumberMaskForRam; + bool setRTC; + Byte RTCval; + Byte RTC[5]; + Byte RTCmax[5]; + Byte dayCounter; + bool latch; + Byte numberOfYearsCount; + long long int numberofCyclescount; + long long int totalNumberofCyclescount; + /*struct tm + { + int tm_sec; // seconds of minutes from 0 to 61 + int tm_min; // minutes of hour from 0 to 59 + int tm_hour; // hours of day from 0 to 24 + int tm_mday; // day of month from 1 to 31 + int tm_mon; // month of year from 0 to 11 + int tm_year; // year since 1900 + int tm_wday; // days since sunday + int tm_yday; // days since January 1st + int tm_isdst; // hours of daylight savings time + };*/ + public: Byte* joyPadState; @@ -325,4 +347,8 @@ class MemoryMap // Change RAM Banking according to the set registers void bankRam(); + + void setRTCRegisters(int value); + + void updateRTCReg(int cycles); }; \ No newline at end of file From ec8d6cddc1d6211583a85dae24ef1e92e24aac84 Mon Sep 17 00:00:00 2001 From: NehaGujar1 Date: Sun, 11 Jun 2023 14:48:10 +0530 Subject: [PATCH 31/34] Minor modification in updateRTCReg --- src/mmap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mmap.cpp b/src/mmap.cpp index 5741823..e93cdff 100644 --- a/src/mmap.cpp +++ b/src/mmap.cpp @@ -559,7 +559,7 @@ void MemoryMap::updateRTCReg(int cycles) int value = (int)(totalNumberofCyclescount) / 4194304; if ((totalNumberofCyclescount) < 4194304) { - // return; + return; } printf("testing : %d\n",totalNumberofCyclescount); //struct tm *gmtime(const time_t *time); From 703e4e620b5e0419c062d37b3b012d81b4723d02 Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi Date: Wed, 14 Jun 2023 23:00:04 +0530 Subject: [PATCH 32/34] Add SDL2 to CMakeLists.txt --- README.md | 12 ++++++++++++ src/CMakeLists.txt | 8 +++++++- src/gameBoy.cpp | 2 +- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d964ef2..6370537 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,15 @@ +# Dependencies +## SDL +### MacOS +`brew install sdl2` +### Linux +`sudo apt install libsdl2-dev` + +### Windows +Download the development pack `SDL2-devel-2.0.5-VC.zip` from [here](https://github.com/libsdl-org/SDL/releases/tag/release-2.26.2) + +Or use winget or choco + # Build ## Release ``` diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7784932..66701f6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,7 +5,6 @@ set(SOURCES gameBoy.cpp mmap.cpp graphics.cpp - # ------- # Header Files cpu.h @@ -17,8 +16,15 @@ set(SOURCES target_sources(${PROJECT_NAME} PRIVATE ${SOURCES}) +find_package(SDL2 REQUIRED) +include_directories(${SDL2_INCLUDE_DIRS}) + if (MSVC) set_target_properties( ${PROJECT_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/") endif () + +target_link_libraries(${PROJECT_NAME} ${SDL2_LIBRARIES}) + +set(SDL2_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/include") \ No newline at end of file diff --git a/src/gameBoy.cpp b/src/gameBoy.cpp index 3041768..9f565db 100644 --- a/src/gameBoy.cpp +++ b/src/gameBoy.cpp @@ -31,7 +31,7 @@ GBE::GBE() printf("boot rom file not opened"); // Open the Game ROM - if ((gameROM = fopen("../tests/pkmnred.gb", "rb")) == NULL) + if ((gameROM = fopen("../tests/Tetris.gb", "rb")) == NULL) printf("game rom file not opened"); // Set the Boot ROM From 563c78ddeb7b8f1f546e75e5a9ef036c3a682ffc Mon Sep 17 00:00:00 2001 From: Pragyansh Chaturvedi <76248539+r41k0u@users.noreply.github.com> Date: Wed, 14 Jun 2023 23:10:34 +0530 Subject: [PATCH 33/34] Update cpp.yml - Add OS Check --- .github/workflows/cpp.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cpp.yml b/.github/workflows/cpp.yml index 3e6d7b5..4ca3717 100644 --- a/.github/workflows/cpp.yml +++ b/.github/workflows/cpp.yml @@ -17,9 +17,11 @@ jobs: - name: install_dependencies run: | - sudo add-apt-repository -y "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse" - sudo apt-get update -y -qq - sudo apt-get install libsdl2-dev + if [ "$RUNNER_OS" == "Linux" ]; then + sudo add-apt-repository -y "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse" + sudo apt-get update -y -qq + sudo apt-get install libsdl2-dev + fi - name: build run: | From 99cc8e3bbbcab5ccb30c853448a9c22896572c81 Mon Sep 17 00:00:00 2001 From: NehaGujar1 Date: Sat, 17 Jun 2023 19:26:37 +0530 Subject: [PATCH 34/34] Update RTC write functions --- src/gameBoy.cpp | 6 +- src/mmap.cpp | 192 +++++++++++++++++++++++++++++------------------- src/mmap.h | 36 +++++---- 3 files changed, 143 insertions(+), 91 deletions(-) diff --git a/src/gameBoy.cpp b/src/gameBoy.cpp index 0f3101a..6240580 100644 --- a/src/gameBoy.cpp +++ b/src/gameBoy.cpp @@ -31,7 +31,7 @@ GBE::GBE() printf("boot rom file not opened"); // Open the Game ROM - if ((gameROM = fopen("../tests/rtc3test.gb", "rb")) == NULL) + if ((gameROM = fopen("../tests/instr_timing.gb", "rb")) == NULL) printf("game rom file not opened"); // Set the Boot ROM @@ -111,10 +111,10 @@ void GBE::update() { // Execute the next instruction s_Cycles += gbe_cpu->executeNextInstruction(); - gbe_mMap->updateRTCReg(s_Cycles); // update the DIV and TIMA timers gbe_cpu->updateTimers(s_Cycles); gbe_graphics->executePPU(s_Cycles); + gbe_mMap->updateRTCReg(s_Cycles); s_Cycles = 0; s_Cycles += gbe_cpu->performInterrupt(); gbe_graphics->pollEvents(); @@ -126,9 +126,9 @@ void GBE::executeBootROM() while (gbe_mMap->readMemory(0xFF50) == 0x00) { s_Cycles += gbe_cpu->executeNextInstruction(); - gbe_mMap->updateRTCReg(s_Cycles); gbe_cpu->updateTimers(s_Cycles); gbe_graphics->executePPU(s_Cycles); + gbe_mMap->updateRTCReg(s_Cycles); s_Cycles = 0; s_Cycles += gbe_cpu->performInterrupt(); } diff --git a/src/mmap.cpp b/src/mmap.cpp index e93cdff..5f9d2b0 100644 --- a/src/mmap.cpp +++ b/src/mmap.cpp @@ -127,21 +127,22 @@ MemoryMap::MemoryMap() ramBankNumberMaskForRam = 0; - RTCmax[0]=60; - RTCmax[1]=60; - RTCmax[2]=24; - RTCmax[3]=0xFF; - RTC[0]=0; - RTC[1]=0; - RTC[2]=0; - RTC[3]=0; - RTCmax[0]=60; - RTC[4]=0; + RTCmax[0] = 60; + RTCmax[1] = 60; + RTCmax[2] = 24; + RTCmax[3] = 0xFF; + RTC[0] = 0; + RTC[1] = 0; + RTC[2] = 0; + RTC[3] = 0; + RTCmax[0] = 60; + RTC[4] = 0; latch = false; setRTC = false; numberOfYearsCount = 0; - numberofCyclescount=0; + //numberofCyclescount = 0; totalNumberofCyclescount = 0; + halt = false; } // Write to memory @@ -169,28 +170,29 @@ bool MemoryMap::writeMemory(Word address, Byte value) else if (address < 0x6000) { // Decide RAM Bank Number - if(val<=1b11) else if(08-0C) - RTC register -> read/written A000-BFFF mainly A000 - setRTC=true - if (value<=0b11) + if (value <= 0b11) { ramBankNumber = (value & 0b11); bankRom(); bankRam(); - setRTC=false; + setRTC = false; } - else if (value<=0b1100&&value>=0b100) + else if (value <= 0b1100 && value >= 0b100) { - setRTC=true; - RTCval = value-8; + setRTC = true; + RTCval = value - 8; } - } else if (address < 0x8000) { - // Decide Banking Mode Select - - latch=false; - if(bankingModeSelect==0&&(value&0b1==1)) + // Decide Banking Mode Select + // Checks for update registers call + if (bankingModeSelect == 0 && (value & 0b1 == 1)) { - latch=true; + latch = true; + updateRTCReg(0); } + latch = false; bankingModeSelect = (value & 0b1); bankRom(); bankRam(); @@ -205,17 +207,14 @@ bool MemoryMap::writeMemory(Word address, Byte value) // Write to External RAM if external RAM has been enabled if (ramEnable) { - if(setRTC) + if (setRTC) { - RTC[4] = 0b10111111&RTC[4]; - RTC[4]+=0b01000000; - //overflow handle?? - // RTC[RTCval] = value; - // if(latch) - setRTCRegisters(value); - RTC[4]-=0b01000000; + halt = true; + setRTCRegisters(value); + halt = false; } - else externalRam[address - 0xA000] = value; + else + externalRam[address - 0xA000] = value; } } else if (address < 0xE000) @@ -264,8 +263,8 @@ bool MemoryMap::writeMemory(Word address, Byte value) { readInput(value); } - //if (value != 0xFF) - //printf("0x%02x\n", ioPorts[0]);} + // if (value != 0xFF) + // printf("0x%02x\n", ioPorts[0]);} else ioPorts[address - 0xFF00] = value; } @@ -313,8 +312,11 @@ Byte MemoryMap::readMemory(Word address) else if (address < 0xC000) { // Read from External RAM - if(setRTC) { - while ((RTC[4]&&0b01000000)) ; + if (setRTC) + { + // printf("RTC[4]:%d",RTC[4]); + while ((halt)) + ; return RTC[RTCval]; } return externalRam[address - 0xA000]; @@ -505,7 +507,7 @@ void MemoryMap::bankRom() if (completeBankNumber == 0) { - //completeBankNumber += 1; //remove this for MBC3 + // completeBankNumber += 1; //remove this for MBC3 } completeBankNumber &= romBankNumberMask; @@ -525,69 +527,111 @@ void MemoryMap::bankRam() void MemoryMap::setRTCRegisters(int value) { - printf("hi"); - int curr_set_time = RTCval; - while (curr_set_time<4&&value>0) + int currentSetIndex = RTCval; + while (latch) + { + /* wait till RTC completes getting updated */ + } + + if (currentSetIndex <= 1 && value >= 0) { - RTC[curr_set_time] = value%RTCmax[curr_set_time]; - value = value/RTCmax[curr_set_time]; - curr_set_time++; + RTC[currentSetIndex] = value & 0b00111111; } - if (value<=1) + else if (currentSetIndex == 2) { - RTC[4] = 0b11111110&RTC[4]; - RTC[4] += value; - numberOfYearsCount+=value; + RTC[currentSetIndex] = value & 0b00011111; + } + else if (currentSetIndex == 3) + { + RTC[currentSetIndex] = value; } else { - /*not really handled*/ - RTC[4] = 0b01111111&RTC[4]; - RTC[4] = 0b10000000; + RTC[currentSetIndex] = value & 0b11000001; } - } void MemoryMap::updateRTCReg(int cycles) { + if (RTC[4] & 0b01000000) + return; totalNumberofCyclescount += cycles; - //printf("in update: %d\n",cycles); - if (!latch) + if (!latch || halt) { return; } - int value = (int)(totalNumberofCyclescount) / 4194304; - if ((totalNumberofCyclescount) < 4194304) + int value = (int)(totalNumberofCyclescount) / 3035700; + if ((totalNumberofCyclescount) < 3035700) { + latch = false; return; } - printf("testing : %d\n",totalNumberofCyclescount); - //struct tm *gmtime(const time_t *time); - // time_t now = time(0); - // tm* gmtm = gmtime(&now); - // RTC[0]=gmtm->tm_sec; - // RTC[1]=gmtm->tm_min; - //RTC[2]= - int curr_set_time = 0; - while (curr_set_time<4&&value>0) + int currentSetIndex = 0; + while (currentSetIndex < 3 && value > 0) + { + if (RTC[currentSetIndex] < RTCmax[currentSetIndex]) + { + RTC[currentSetIndex] += value; + value = RTC[currentSetIndex] / RTCmax[currentSetIndex]; + RTC[currentSetIndex] %= RTCmax[currentSetIndex]; + currentSetIndex++; + } + else + { + RTC[currentSetIndex] += value; + if (currentSetIndex < 2) + { + value = 0; + RTC[currentSetIndex] &= 0b00111111; + } + else + { + value = 0; + RTC[currentSetIndex] &= 0b00011111; + } + currentSetIndex++; + } + } + if (value == 0) { - RTC[curr_set_time] += value; - RTC[curr_set_time]%=RTCmax[curr_set_time]; - value = value/RTCmax[curr_set_time]; - curr_set_time++; + totalNumberofCyclescount = 0; + latch = false; + return; } - if (value<=1) + long long int totalNumberOfDays = RTC[currentSetIndex] + 256 * ((RTC[4] & 1) ? 1 : 0) + value; + if (totalNumberOfDays > 0x1ff) { - RTC[4] = 0b11111110&RTC[4]; - RTC[4] += value; - numberOfYearsCount+=value; + totalNumberOfDays = totalNumberOfDays % (0x1ff); + if (totalNumberOfDays < 256) + { + RTC[3] = totalNumberOfDays; + RTC[4] = RTC[4] & (0b11111110); + } + else + { + RTC[3] = totalNumberOfDays & (0b11111111); + RTC[4] = RTC[4] & (0b11111110); + if (totalNumberOfDays & 0b100000000) + RTC[4] += 1; + } + RTC[4] = RTC[4] & (0b01111111); + RTC[4] += (0b10000000); } else { - /*not really handled*/ - RTC[4] = 0b01111111&RTC[4]; - RTC[4] = 0b10000000; + if (totalNumberOfDays < 256) + { + RTC[3] = totalNumberOfDays; + RTC[4] = RTC[4] & (0b11111110); + } + else + { + RTC[3] = totalNumberOfDays & (0b11111111); + RTC[4] = RTC[4] & (0b11111110); + if (totalNumberOfDays & 0b100000000) + RTC[4] += 1; + } } - totalNumberofCyclescount=0; + totalNumberofCyclescount = 0; latch = false; } \ No newline at end of file diff --git a/src/mmap.h b/src/mmap.h index 03d9cf1..f9daf3f 100644 --- a/src/mmap.h +++ b/src/mmap.h @@ -191,27 +191,33 @@ class MemoryMap // Tells the number of bits of RAM Bank Number that are useful for RAM Byte ramBankNumberMaskForRam; + //1 bit boolean value + // Selects RAM banking (if false), RTC registers otherwise bool setRTC; + + // 4 bit Register number + // Specifies the register number amongst RTC registers Byte RTCval; + + // 5 RTC registers to hierachially store time + // RTC(0) S RTC(1) M RTC(2) H RTC(3) DL RTC(4) DH Byte RTC[5]; + + // Stores maximum allowed value for each RTC register like 59 for RTC(0) S Byte RTCmax[5]; - Byte dayCounter; + + // 1 bit boolean value + // To update the timer bool latch; + + // To disallow simultaneous writes + bool halt; + + // Counts total number of years Byte numberOfYearsCount; - long long int numberofCyclescount; + + // Stores total number of new cycles before latching long long int totalNumberofCyclescount; - /*struct tm - { - int tm_sec; // seconds of minutes from 0 to 61 - int tm_min; // minutes of hour from 0 to 59 - int tm_hour; // hours of day from 0 to 24 - int tm_mday; // day of month from 1 to 31 - int tm_mon; // month of year from 0 to 11 - int tm_year; // year since 1900 - int tm_wday; // days since sunday - int tm_yday; // days since January 1st - int tm_isdst; // hours of daylight savings time - };*/ public: Byte* joyPadState; @@ -348,7 +354,9 @@ class MemoryMap // Change RAM Banking according to the set registers void bankRam(); + // Sets the value of RTC registers upon write call void setRTCRegisters(int value); + // Updates the RTC registers with new time to be latched void updateRTCReg(int cycles); }; \ No newline at end of file