diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index 241f190..b81bf5f 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -1,5 +1,5 @@ -set(HEADERS canvas.h gui.h window.h main_window.h char_map.h pattern_window.h) -set(SOURCE canvas.c gui.c window.c main_window.c char_map.c pattern_window.c) +set(HEADERS canvas.h gui.h window.h main_window.h char_map.h pattern_display.h pattern_window.h nametable_window.h) +set(SOURCE canvas.c gui.c window.c main_window.c char_map.c pattern_display.c pattern_window.c nametable_window.c) add_library(nes_gui ${SOURCE} ${HEADERS}) diff --git a/gui/gui.c b/gui/gui.c index 05f45ee..804e2d7 100644 --- a/gui/gui.c +++ b/gui/gui.c @@ -8,10 +8,12 @@ #include "main_window.h" #include "pattern_window.h" #include "../include/system.h" +#include "nametable_window.h" typedef struct nes_gui { NesMainWindow main_window; NesPatternWindow pattern_window; + NesNametableWindow nametable_window; TTF_Font *font; @@ -30,12 +32,13 @@ bool gui_init() { return false; } - gui.debug_enabled = false; + gui.debug_enabled = true; main_window_init(&gui.main_window, gui.font); if (gui.debug_enabled) { pattern_window_init(&gui.pattern_window); + nametable_window_init(&gui.nametable_window); } return true; @@ -46,6 +49,7 @@ void gui_uninit() { if (gui.debug_enabled) { pattern_window_uninit(&gui.pattern_window); + nametable_window_uninit(&gui.nametable_window); } TTF_CloseFont(gui.font); @@ -53,7 +57,15 @@ void gui_uninit() { void gui_post_sysinit() { byte *pattern_memory = system_get_mapper()->ppu_read(0); + byte nametable_memory[0x0400 * 4]; + + memcpy(&nametable_memory, ppu_get_state()->memory.nametable_0, 0x0400); + memcpy(&nametable_memory[0x0400], ppu_get_state()->memory.nametable_0, 0x0400); + memcpy(&nametable_memory[0x0800], ppu_get_state()->memory.nametable_1, 0x0400); + memcpy(&nametable_memory[0x0c00], ppu_get_state()->memory.nametable_1, 0x0400); + pattern_window_build_table(&gui.pattern_window, pattern_memory); + nametable_window_build_table(&gui.nametable_window, &nametable_memory[0]); } int gui_input() { diff --git a/gui/nametable_window.c b/gui/nametable_window.c new file mode 100644 index 0000000..b8551e5 --- /dev/null +++ b/gui/nametable_window.c @@ -0,0 +1,30 @@ +// +// Created by william on 12/07/24. +// + +#include "nametable_window.h" + +void nametable_window_init(NesNametableWindow *window) { + int win_size = pattern_display_get_size(NW_ROW_TILE_COUNT); + window->sdl_context = window_init("Nametable", win_size, win_size, NW_SCALE); + + pattern_display_init(&window->pattern_display, window->sdl_context.renderer, NW_ROW_TILE_COUNT, NW_ROW_TILE_COUNT); +} + +void nametable_window_uninit(NesNametableWindow *window) { + pattern_display_uninit(&window->pattern_display); + window_uninit(window->sdl_context); +} + +void nametable_window_build_table(NesNametableWindow *window, byte *nametable_memory) { + pattern_display_build(&window->pattern_display, nametable_memory); +} + +void nametable_window_render(NesNametableWindow *window) { + SDL_RenderClear(window->sdl_context.renderer); + pattern_display_render(&window->pattern_display, window->sdl_context.renderer); +} + +void nametable_window_present(NesNametableWindow *window) { + SDL_RenderPresent(window->sdl_context.renderer); +} \ No newline at end of file diff --git a/gui/nametable_window.h b/gui/nametable_window.h new file mode 100644 index 0000000..679da10 --- /dev/null +++ b/gui/nametable_window.h @@ -0,0 +1,28 @@ +// +// Created by william on 12/07/24. +// + +#ifndef NES_EMULATOR_NAMETABLE_WINDOW_H +#define NES_EMULATOR_NAMETABLE_WINDOW_H + +#include "window.h" +#include "../include/types.h" +#include "pattern_display.h" + +#define NW_SCALE 2 +#define NW_ROW_TILE_COUNT 32 + +typedef struct nes_nametable_window { + NesSdlContext sdl_context; + PatternDisplay pattern_display; +} NesNametableWindow; + +void nametable_window_init(NesNametableWindow *window); +void nametable_window_uninit(NesNametableWindow *window); + +void nametable_window_build_table(NesNametableWindow *window, byte* nametable_memory); + +void nametable_window_render(NesNametableWindow *window); +void nametable_window_present(NesNametableWindow *window); + +#endif //NES_EMULATOR_NAMETABLE_WINDOW_H diff --git a/gui/pattern_display.c b/gui/pattern_display.c new file mode 100644 index 0000000..5f2840f --- /dev/null +++ b/gui/pattern_display.c @@ -0,0 +1,122 @@ +// +// Created by william on 12/07/24. +// + +#include +#include "pattern_display.h" + +#define PATTERN_BYTES (PATTERN_SIZE * 2) + +void pattern_display_init(PatternDisplay *display, SDL_Renderer *renderer, int tiles_x, int tiles_y) { + assert(tiles_x > 0); + assert(tiles_y > 0); + + display->width = tiles_x; + display->height = tiles_y; + + display->texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, + pattern_display_get_size(tiles_x), + pattern_display_get_size(tiles_y)); +} + +void pattern_display_uninit(PatternDisplay *display) { + SDL_DestroyTexture(display->texture); +} + +void pattern_display_draw_borders(unsigned int *buffer, int win_width, int win_height) { + assert(buffer != NULL); + + for (int x = 0; x < win_width; x++) { + buffer[x] = PATTERN_BORDER_COLOR; + } + + for (int y = 1; y < win_height; y++) { + buffer[y * win_width] = PATTERN_BORDER_COLOR; + } +} + +void pattern_display_build_table(PatternTile *tile_table, byte *memory, int tile_count) { + for (int tile_index = 0; tile_index < tile_count; tile_index++) { + PatternTile *tile = &tile_table[tile_index]; + + address tile_addr = tile_index * PATTERN_BYTES; + memcpy(tile->data_low, &memory[tile_addr], 8); + memcpy(tile->data_high, &memory[tile_addr + 8], 8); + } +} + +void pattern_display_draw_tile_borders(int tile_addr, pixel *buffer, int win_width) { + assert(buffer != NULL); + + for (int by = 0; by < PATTERN_DRAW_SIZE; by++) { + address pixel_addr = tile_addr + (by * win_width) + PATTERN_DRAW_SIZE - 1; + buffer[pixel_addr] = PATTERN_BORDER_COLOR; + } + + for (int bx = 0; bx < PATTERN_DRAW_SIZE; bx++) { + address pixel_addr = tile_addr + ((PATTERN_DRAW_SIZE - 1) * win_width) + bx; + buffer[pixel_addr] = PATTERN_BORDER_COLOR; + } +} + +void pattern_display_draw_tile(PatternTile *tile, pixel *buffer, int tile_addr, int win_width) { + assert(buffer != NULL); + + for (int fine_y = 0; fine_y < PATTERN_SIZE; fine_y++) { + byte data_high = tile->data_high[fine_y]; + byte data_low = tile->data_low[fine_y]; + + for (int fine_x = 0; fine_x < PATTERN_SIZE; fine_x++) { + byte bitmask = 1 << (PATTERN_SIZE - fine_x - 1); + byte bit_high = data_high & bitmask; + byte bit_low = data_low & bitmask; + + int pixel_addr = tile_addr + fine_x + fine_y * win_width; + pixel *pixel = &buffer[pixel_addr]; + + // TODO: Use palette colors + if (bit_high && bit_low) { + *pixel = 0xffffffff; + } else if (bit_low) { + *pixel = 0xffff0000; + } else if (bit_high) { + *pixel = 0xff00ffff; + } else { + *pixel = 0xff000000; + } + } + } + + pattern_display_draw_tile_borders(tile_addr, buffer, win_width); +} + +void pattern_display_build(PatternDisplay *display, byte *memory) { + int tile_count = display->width * display->height; + PatternTile *tile_table = malloc(tile_count * sizeof(PatternTile)); + pattern_display_build_table(tile_table, memory, tile_count); + + int win_width = pattern_display_get_size(display->width); + int win_height = pattern_display_get_size(display->height); + pixel *buffer = malloc(win_width * win_height * sizeof(pixel)); + + pattern_display_draw_borders(buffer, win_width, win_height); + + for (int x = 0; x < display->width; x++) { + for (int y = 0; y < display->height; y++) { + PatternTile *tile = &tile_table[x + y * display->width]; + address row_addr = (y * PATTERN_DRAW_SIZE + PATTERN_BORDER_WIDTH) * win_width; + address tile_addr = row_addr + (x * PATTERN_DRAW_SIZE + PATTERN_BORDER_WIDTH); + + pattern_display_draw_tile(tile, buffer, tile_addr, win_width); + } + } + + SDL_UpdateTexture(display->texture, NULL, buffer, win_width * sizeof(pixel)); + + free(tile_table); + free(buffer); +} + +void pattern_display_render(PatternDisplay *display, SDL_Renderer *renderer) { + SDL_RenderCopy(renderer, display->texture, NULL, NULL); +} \ No newline at end of file diff --git a/gui/pattern_display.h b/gui/pattern_display.h new file mode 100644 index 0000000..005ae83 --- /dev/null +++ b/gui/pattern_display.h @@ -0,0 +1,42 @@ +// +// Created by william on 12/07/24. +// + +#ifndef NES_EMULATOR_PATTERN_DISPLAY_H +#define NES_EMULATOR_PATTERN_DISPLAY_H + +#include +#include "../include/types.h" + +#define PATTERN_SIZE 8 +#define PATTERN_BORDER_WIDTH 1 +#define PATTERN_BORDER_COLOR 0xff2223b2 +#define PATTERN_DRAW_SIZE (PATTERN_SIZE + PATTERN_BORDER_WIDTH) + +typedef unsigned int pixel; + +typedef struct pattern_tile { + byte data_low[8]; + byte data_high[8]; +} PatternTile; + +typedef struct pattern_display { + SDL_Texture *texture; + PatternTile *tiles; + int width; + int height; +} PatternDisplay; + +static inline int pattern_display_get_size(int tile_count) { + return tile_count * PATTERN_DRAW_SIZE + PATTERN_BORDER_WIDTH; +} + +void pattern_display_init(PatternDisplay *display, SDL_Renderer *renderer, int tiles_x, int tiles_y); + +void pattern_display_uninit(PatternDisplay *display); + +void pattern_display_build(PatternDisplay *display, byte *memory); + +void pattern_display_render(PatternDisplay *display, SDL_Renderer *renderer); + +#endif //NES_EMULATOR_PATTERN_DISPLAY_H diff --git a/gui/pattern_window.c b/gui/pattern_window.c index f170773..c0f285e 100644 --- a/gui/pattern_window.c +++ b/gui/pattern_window.c @@ -4,150 +4,28 @@ #include "pattern_window.h" -#define PW_TILE_SIZE 8 -#define PW_TILE_BYTES (PW_TILE_SIZE * 2) -#define PW_BANK_SIZE 0x1000 -#define PW_BANK_TILE_COUNT (PW_ROW_TILE_COUNT * PW_ROW_TILE_COUNT) - -#define PW_TILE_BORDER_WIDTH 1 -#define PW_TILE_BORDER_COLOR 0xff2223b2 -#define PW_TILE_DRAW_SIZE (PW_TILE_SIZE + PW_TILE_BORDER_WIDTH) - -#define PW_WIDTH (PW_ROW_TILE_COUNT * PW_TILE_DRAW_SIZE + PW_TILE_BORDER_WIDTH) -#define PW_HEIGHT (PW_ROW_TILE_COUNT * PW_TILE_DRAW_SIZE * 2 + PW_TILE_BORDER_WIDTH) #define PW_SCALE 2 void pattern_window_init(NesPatternWindow *window) { - window->sdl_context = window_init("Pattern Table", PW_WIDTH, PW_HEIGHT, PW_SCALE); + int win_width = pattern_display_get_size(PW_ROW_TILE_COUNT); + window->sdl_context = window_init("Pattern Table", win_width, win_width * 2, PW_SCALE); - window->texture = SDL_CreateTexture(window->sdl_context.renderer, SDL_PIXELFORMAT_ARGB8888, - SDL_TEXTUREACCESS_STATIC, PW_WIDTH, PW_HEIGHT); + pattern_display_init(&window->pattern_display, window->sdl_context.renderer, PW_ROW_TILE_COUNT, + PW_ROW_TILE_COUNT * 2); } void pattern_window_uninit(NesPatternWindow *window) { + pattern_display_uninit(&window->pattern_display); window_uninit(window->sdl_context); } -/* - * d888888b d888888b db d88888b .d8888. - * `~~88~~' `88' 88 88' 88' YP - * 88 88 88 88ooooo `8bo. - * 88 88 88 88~~~~~ `Y8b. - * 88 .88. 88booo. 88. db 8D - * YP Y888888P Y88888P Y88888P `8888Y' - */ -static PatternTile pattern_window_build_tile(int x, int y, byte *pattern_memory) { - PatternTile tile; - tile.x = x; - tile.y = y; - - address tile_addr = (x + y * PW_ROW_TILE_COUNT) * PW_TILE_BYTES; - memcpy(tile.data_low, &pattern_memory[tile_addr], 8); - memcpy(tile.data_high, &pattern_memory[tile_addr + 8], 8); - - return tile; -} - -static void pattern_window_build_bank(byte *pattern_memory, PatternTile *bank) { - for (int y = 0; y < PW_ROW_TILE_COUNT; y++) { - for (int x = 0; x < PW_ROW_TILE_COUNT; x++) { - int tile_index = y * PW_ROW_TILE_COUNT + x; - PatternTile *tile = &bank[tile_index]; - *tile = pattern_window_build_tile(x, y, pattern_memory); - } - } -} - -void pattern_window_build_tiles(NesPatternWindow *window, byte *pattern_memory) { - pattern_window_build_bank(pattern_memory, window->tiles_bank_0); - pattern_window_build_bank(&pattern_memory[PW_BANK_SIZE], window->tiles_bank_1); -} - -/* - * d8888b. d88888b d8b db d8888b. d88888b d8888b. d888888b d8b db d888b - * 88 `8D 88' 888o 88 88 `8D 88' 88 `8D `88' 888o 88 88' Y8b - * 88oobY' 88ooooo 88V8o 88 88 88 88ooooo 88oobY' 88 88V8o 88 88 - * 88`8b 88~~~~~ 88 V8o88 88 88 88~~~~~ 88`8b 88 88 V8o88 88 ooo - * 88 `88. 88. 88 V888 88 .8D 88. 88 `88. .88. 88 V888 88. ~8~ - * 88 YD Y88888P VP V8P Y8888D' Y88888P 88 YD Y888888P VP V8P Y888P - */ -static void pattern_window_draw_borders(unsigned int *buffer) { - for (int x = 0; x < PW_WIDTH; x++) { - buffer[x] = PW_TILE_BORDER_COLOR; - } - - for (int y = 1; y < PW_HEIGHT; y++) { - buffer[y * PW_WIDTH] = PW_TILE_BORDER_COLOR; - } -} - -static void pattern_window_draw_tile_borders(int tile_addr, unsigned int *buffer) { - for (int by = 0; by < PW_TILE_DRAW_SIZE; by++) { - int pixel_addr = tile_addr + (by * PW_WIDTH) + PW_TILE_DRAW_SIZE - 1; - buffer[pixel_addr] = PW_TILE_BORDER_COLOR; - } - - for (int bx = 0; bx < PW_TILE_DRAW_SIZE; bx++) { - int pixel_addr = tile_addr + ((PW_TILE_DRAW_SIZE - 1) * PW_WIDTH) + bx; - buffer[pixel_addr] = PW_TILE_BORDER_COLOR; - } -} - -static void pattern_window_draw_tile(PatternTile *tile, unsigned int *buffer) { - int row_addr = (tile->y * PW_TILE_DRAW_SIZE + 1) * PW_WIDTH; - int tile_buffer_addr = row_addr + (tile->x * PW_TILE_DRAW_SIZE + 1); - - for (int fine_y = 0; fine_y < PW_TILE_SIZE; fine_y++) { - byte data_high = tile->data_high[fine_y]; - byte data_low = tile->data_low[fine_y]; - - for (int fine_x = 0; fine_x < PW_TILE_SIZE; fine_x++) { - byte bitmask = 1 << (PW_TILE_SIZE - fine_x - 1); - byte bit_high = data_high & bitmask; - byte bit_low = data_low & bitmask; - - int pixel_addr = tile_buffer_addr + fine_x + fine_y * PW_WIDTH; - unsigned int *pixel_data = &buffer[pixel_addr]; - - if (bit_high && bit_low) { - *pixel_data = 0xffffffff; - } else if (bit_low) { - *pixel_data = 0xffff0000; - } else if (bit_high) { - *pixel_data = 0xff00ffff; - } else { - *pixel_data = 0xff000000; - } - } - } - - pattern_window_draw_tile_borders(tile_buffer_addr, buffer); -} - -void pattern_window_draw_bank(PatternTile *tiles, unsigned int *buffer) { - for (int i = 0; i < PW_BANK_TILE_COUNT; i++) { - PatternTile *tile = &tiles[i]; - pattern_window_draw_tile(tile, buffer); - } -} - -void pattern_window_draw_table(NesPatternWindow *window) { - unsigned int tex_buffer[PW_WIDTH * PW_HEIGHT] = {0}; - pattern_window_draw_borders(tex_buffer); - pattern_window_draw_bank(window->tiles_bank_0, tex_buffer); - pattern_window_draw_bank(window->tiles_bank_1, &tex_buffer[PW_WIDTH * (PW_HEIGHT / 2)]); - - SDL_UpdateTexture(window->texture, NULL, tex_buffer, PW_WIDTH * sizeof(unsigned int)); -} - void pattern_window_build_table(NesPatternWindow *window, byte *pattern_memory) { - pattern_window_build_tiles(window, pattern_memory); - pattern_window_draw_table(window); + pattern_display_build(&window->pattern_display, pattern_memory); } void pattern_window_render(NesPatternWindow *window) { SDL_RenderClear(window->sdl_context.renderer); - SDL_RenderCopy(window->sdl_context.renderer, window->texture, NULL, NULL); + pattern_display_render(&window->pattern_display, window->sdl_context.renderer); } void pattern_window_present(NesPatternWindow *window) { diff --git a/gui/pattern_window.h b/gui/pattern_window.h index 8c83838..16e3e4a 100644 --- a/gui/pattern_window.h +++ b/gui/pattern_window.h @@ -5,25 +5,15 @@ #ifndef NES_EMULATOR_PATTERN_WINDOW_H #define NES_EMULATOR_PATTERN_WINDOW_H -#include -#include "window.h" #include "../include/types.h" +#include "window.h" +#include "pattern_display.h" #define PW_ROW_TILE_COUNT 16 -typedef struct pattern_tile { - byte x; - byte y; - byte data_low[8]; - byte data_high[8]; -} PatternTile; - typedef struct nes_pattern_window { NesSdlContext sdl_context; - - PatternTile tiles_bank_0[PW_ROW_TILE_COUNT * PW_ROW_TILE_COUNT]; - PatternTile tiles_bank_1[PW_ROW_TILE_COUNT * PW_ROW_TILE_COUNT]; - SDL_Texture *texture; + PatternDisplay pattern_display; } NesPatternWindow; void pattern_window_init(NesPatternWindow *window); diff --git a/ppu/ppu.c b/ppu/ppu.c index 66fd4ec..3169409 100644 --- a/ppu/ppu.c +++ b/ppu/ppu.c @@ -208,7 +208,7 @@ void ppu_visible_frame(unsigned int cycle) { ppu_state.ppu_address += 0x20; } else { ppu_state.ppu_address &= ~0x3e0; -// ppu_state.ppu_address ^= 0x0800; + ppu_state.ppu_address ^= 0x0800; } } }