diff --git a/README.md b/README.md index fdfd82c..7f7b2e2 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ only tested on Linux. Here is how to run the project: ## Controls - `p`: Pauses the emulation - `o`: Go to the next palette in the pattern viewer + - `t`: Show tile IDs ## Dependencies - GCC compiler diff --git a/gui/gui.c b/gui/gui.c index ea9ee77..cb72d43 100644 --- a/gui/gui.c +++ b/gui/gui.c @@ -89,7 +89,12 @@ int gui_input() { system_toggle_pause(); } else { #if DEBUG - pattern_window_key_up(&gui.pattern_window, event.key.keysym.sym); + if (event.key.keysym.sym == SDLK_t) { + PPUDebugFlags *ppu_debug = &ppu_get_state()->debug; + ppu_debug->flags.tile_debugger = !ppu_debug->flags.tile_debugger; + } else { + pattern_window_key_up(&gui.pattern_window, event.key.keysym.sym); + } #endif } } diff --git a/gui/main_window.c b/gui/main_window.c index b255437..0245889 100644 --- a/gui/main_window.c +++ b/gui/main_window.c @@ -34,20 +34,8 @@ void main_window_render_delay(SDL_Renderer *renderer) { } #endif -void main_window_render(NesMainWindow *window, PPUPixel *pixels) { +void main_window_render(NesMainWindow *window, pixel *pixels) { SDL_RenderClear(window->sdl_context.renderer); - -// unsigned int frame_buffer[240 * 256]; -// for (int i = 0; i < 240 * 256; i++) { -// PPUPixel pixel = pixels[i]; -// -// unsigned int *data = &frame_buffer[i]; -// *data = 0xff000000; -// *data |= pixel.r << 16; -// *data |= pixel.g << 8; -// *data |= pixel.b; -// } - SDL_UpdateTexture(window->texture, NULL, pixels, 240 * sizeof(unsigned int)); SDL_RenderCopy(window->sdl_context.renderer, window->texture, NULL, NULL); diff --git a/gui/main_window.h b/gui/main_window.h index 8492476..e421da1 100644 --- a/gui/main_window.h +++ b/gui/main_window.h @@ -11,7 +11,7 @@ #define MAIN_WINDOW_WIDTH 256 #define MAIN_WINDOW_HEIGHT 240 -#define MAIN_WINDOW_SCALE 2 +#define MAIN_WINDOW_SCALE 3 typedef struct nes_main_window { NesSdlContext sdl_context; @@ -22,7 +22,7 @@ typedef struct nes_main_window { void main_window_init(NesMainWindow *window); void main_window_uninit(NesMainWindow *window); -void main_window_render(NesMainWindow *window, PPUPixel* pixels); +void main_window_render(NesMainWindow *window, pixel* pixels); void main_window_present(NesMainWindow *window); #endif //NES_EMULATOR_MAIN_WINDOW_H diff --git a/include/ppu.h b/include/ppu.h index 8d58d7c..193319d 100644 --- a/include/ppu.h +++ b/include/ppu.h @@ -69,46 +69,42 @@ typedef struct ppu_tile_fetch { byte pattern_table_tile_high; } PPUTileFetch; -//typedef struct ppu_pixel { -// byte r; -// byte g; -// byte b; -//} PPUPixel; -typedef unsigned int PPUPixel; - -typedef struct ppu_tile_queue { - PPUTileFetch first_fetch; - PPUTileFetch second_fetch; - PPUTileFetch displayed_fetch; -} PPUTileQueue; +#if DEBUG +typedef union { + struct { + byte tile_debugger: 1; + } flags; + byte flags_byte; +} PPUDebugFlags; +#endif typedef struct ppu { PPUMemory memory; - PPUPixel pixels[256 * 240]; + pixel pixels[256 * 240]; byte registers[8]; byte oam_dma_register; byte oam[PPU_OAM_SIZE]; bool odd_frame; - address v; - address t; - byte x; - bool w; - byte x_scroll; byte fine_x_scroll; byte y_scroll; - byte ppu_addr_increment; + byte x; + bool w; + byte ppu_addr_increment; address ppu_address; address temp_ppu_addr; address bg_pattern_table_addr; PPUTileFetch fetch; - PPUTileQueue tile_queue; unsigned long frame; unsigned int scanline; unsigned int cycle; + +#if DEBUG + PPUDebugFlags debug; +#endif } PPU; PPU *ppu_get_state(); @@ -137,17 +133,6 @@ void ppu_cycle(); */ bool ppu_read_flag(size_t reg, byte mask); -/** - * Read a value from the PPU registers. Does not apply any offset to the value, a mask of 0x20 will either result in 0x20 (true) or 0x0 (false). - * Read a value from the PPU registers. Does not apply any offset to the value, a mask of 0x20 will either result in 0x20 (true) or 0x0 (false). - * - * @param reg The register index - * @param mask The value mask - */ -//void ppu_sig_read_register(byte reg); -// -//void ppu_sig_write_register(byte reg); - byte ppu_read_reg(byte reg); void ppu_write_reg(byte reg, byte data); diff --git a/include/types.h b/include/types.h index 34f5338..33c91e5 100644 --- a/include/types.h +++ b/include/types.h @@ -5,14 +5,9 @@ #ifndef NESEMULATOR_TYPES_H #define NESEMULATOR_TYPES_H -//#define RAM_SIZE 0xffff -//#define VRAM_SIZE 0x4000 - typedef unsigned char byte; typedef unsigned short address; typedef unsigned short word; - -//typedef byte ram[RAM_SIZE]; -//typedef byte vram[VRAM_SIZE]; +typedef unsigned int pixel; #endif //NESEMULATOR_TYPES_H diff --git a/ppu/ppu.c b/ppu/ppu.c index 20dcf32..7381cab 100644 --- a/ppu/ppu.c +++ b/ppu/ppu.c @@ -20,6 +20,7 @@ #include "../cpu/cpu.h" #include "../include/rom.h" #include "colors.h" +#include "tile_debugger.h" #define PPU_VISIBLE_FRAME_END 240 #define PPU_POST_RENDER_LINE_START PPU_VISIBLE_FRAME_END @@ -31,7 +32,7 @@ #define NAMETABLE_TILE_SIZE 8 PPU ppu_state; -PPUPixel color_list[0x40] = COLOR_LIST; +pixel color_list[0x40] = COLOR_LIST; void ppu_init() { memset(&ppu_state, 0, sizeof(PPU)); @@ -95,7 +96,7 @@ static inline byte ppu_pixel_get_palette(byte attribute) { return palette & 0b11; } -static inline void ppu_pixel_set_color(PPUPixel *pixel, byte pt_low, byte pt_high, byte attribute) { +static inline void ppu_pixel_set_color(pixel *pixel, byte pt_low, byte pt_high, byte attribute) { for (int i = 0; i < 8; i++) { byte pixel_offset = 8 - i - 1; @@ -114,7 +115,7 @@ static inline void ppu_pixel_set_color(PPUPixel *pixel, byte pt_low, byte pt_hig } void ppu_draw_tile() { - PPUTileFetch fetch = ppu_state.tile_queue.displayed_fetch; + PPUTileFetch fetch = ppu_state.fetch; unsigned int y = ppu_state.scanline; unsigned int x = ppu_state.cycle; @@ -129,15 +130,20 @@ void ppu_draw_tile() { // } unsigned int pixel_index = y * PPU_VISIBLE_FRAME_END + x; - PPUPixel *pixel = &ppu_state.pixels[pixel_index]; + pixel *pixel = &ppu_state.pixels[pixel_index]; ppu_pixel_set_color(pixel, fetch.pattern_table_tile_low, fetch.pattern_table_tile_high, fetch.attribute_table); } byte ppu_get_pattern(byte tile_index, byte high) { - return tile_index; -// byte tile_row_index = (ppu_state.scanline + ppu_state.y_scroll) % 8; -// address pattern_addr = ppu_state.bg_pattern_table_addr | tile_index << 4 | high << 3 | tile_row_index; -// return ppu_read(pattern_addr); +#if DEBUG + if (ppu_state.debug.flags.tile_debugger) { + return tile_debugger_encode_number_as_pattern(tile_index, ppu_state.scanline % 8); + } +#endif + + byte tile_row_index = (ppu_state.scanline + ppu_state.y_scroll) % 8; + address pattern_addr = ppu_state.bg_pattern_table_addr | tile_index << 4 | high << 3 | tile_row_index; + return ppu_read(pattern_addr); } void ppu_fetch_tile(bool render) { @@ -169,7 +175,6 @@ void ppu_fetch_tile(bool render) { ppu_state.fetch.pattern_table_tile_low = ppu_get_pattern(ppu_state.fetch.nametable, 0); } else if (fetch_cycle == 7) { ppu_state.fetch.pattern_table_tile_high = ppu_get_pattern(ppu_state.fetch.nametable, 1); - ppu_state.tile_queue.displayed_fetch = ppu_state.fetch; if (render) { ppu_draw_tile(); diff --git a/ppu/tile_debugger.c b/ppu/tile_debugger.c index 0976244..e6baf85 100644 --- a/ppu/tile_debugger.c +++ b/ppu/tile_debugger.c @@ -4,7 +4,34 @@ #include "tile_debugger.h" -byte tile_encode_number(byte num) { - // 8 segment display +// Contains the patterns of every hexadecimal digit encoded as pattern data. +// The first dimension of the table represents a row in a tile. +byte hex_pattern_table[5][0x10] = { + {0b111, 0b001, 0b111, 0b111, 0b101, 0b111, 0b111, 0b111, 0b111, 0b111, 0b010, 0b111, 0b111, 0b110, 0b111, 0b111}, + {0b101, 0b001, 0b001, 0b001, 0b101, 0b100, 0b100, 0b001, 0b101, 0b101, 0b101, 0b101, 0b100, 0b101, 0b100, 0b100}, + {0b101, 0b001, 0b111, 0b111, 0b111, 0b111, 0b111, 0b010, 0b111, 0b111, 0b111, 0b110, 0b100, 0b101, 0b111, 0b110}, + {0b101, 0b001, 0b100, 0b001, 0b001, 0b001, 0b101, 0b010, 0b101, 0b001, 0b101, 0b101, 0b100, 0b101, 0b100, 0b100}, + {0b111, 0b001, 0b111, 0b111, 0b001, 0b111, 0b111, 0b010, 0b111, 0b001, 0b101, 0b111, 0b111, 0b110, 0b111, 0b100}, +}; +byte tile_debugger_encode_number_as_pattern(byte num, byte tile_fine_y) { + if (tile_fine_y == 6) { + return 0x7f; // On row 6, a full line is drawn to make it easier to separate tiles + } else if (tile_fine_y == 5 || tile_fine_y == 7) { + return 0; + } + + // The first digit of the hex is encoded + byte remaining = num % 0x10; + byte encoded = hex_pattern_table[tile_fine_y][remaining]; + + if (num > 0xf) { + // If the number is greater than 0xF, we need a second digit + // We encode it, then add it 4 pixels to the left of the already encoded digit + byte tenths = num / 0x10; + byte tenths_encoded = hex_pattern_table[tile_fine_y][tenths]; + encoded = (tenths_encoded << 4) | encoded; + } + + return encoded; } \ No newline at end of file diff --git a/ppu/tile_debugger.h b/ppu/tile_debugger.h index 4ad2c08..38346df 100644 --- a/ppu/tile_debugger.h +++ b/ppu/tile_debugger.h @@ -7,4 +7,13 @@ #include "../include/types.h" +/** + * Encodes a number as pattern data. + * + * @param num The number to encode + * @param tile_fine_y The row of the tile + * @return Pattern data representing the row of the encoded number + */ +byte tile_debugger_encode_number_as_pattern(byte num, byte tile_fine_y); + #endif //NES_EMULATOR_TILE_DEBUGGER_H