Efficient rendering

This commit is contained in:
william 2024-06-16 19:22:40 -04:00
parent d9f0c67668
commit dcb01b4c6a
16 changed files with 630 additions and 186 deletions

View File

@ -1,9 +1,9 @@
set(HEADERS canvas.h gui.h window.h) 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) set(SOURCE canvas.c gui.c window.c main_window.c char_map.c pattern_window.c)
add_library(nes_gui ${SOURCE} ${HEADERS}) add_library(nes_gui ${SOURCE} ${HEADERS})
find_package(SDL2 REQUIRED) find_package(SDL2 REQUIRED)
include_directories(nes_gui ${SDL2_INCLUDE_DIRS}) include_directories(nes_gui ${SDL2_INCLUDE_DIRS})
target_link_libraries(nes_gui log.c ${SDL2_LIBRARIES}) target_link_libraries(nes_gui log.c ${SDL2_LIBRARIES} SDL2_ttf)

54
gui/char_map.c Normal file
View File

@ -0,0 +1,54 @@
//
// Created by william on 6/8/24.
//
#include <SDL.h>
#include <SDL_ttf.h>
#include "char_map.h"
SDL_Texture *map[CHAR_MAP_COUNT];
SDL_Texture **char_map_get(char c) {
int char_index = c - CHAR_MAP_MIN;
return &map[char_index];
}
void char_map_init(SDL_Renderer *renderer, TTF_Font *font) {
char buffer[2];
SDL_Color color = CHAR_MAP_COLOR;
SDL_Surface *surface;
SDL_Texture **texture;
for (int i = 0; i <= CHAR_MAP_COUNT; i++) {
buffer[0] = (char) (i + CHAR_MAP_MIN);
buffer[1] = '\0';
surface = TTF_RenderText_Solid(font, buffer, color);
texture = char_map_get(buffer[0]);
*texture = SDL_CreateTextureFromSurface(renderer, surface);
SDL_FreeSurface(surface);
}
}
void char_map_uninit() {
for (int i = 0; i < CHAR_MAP_COUNT; i++) {
SDL_Texture *texture = map[i];
SDL_DestroyTexture(texture);
}
}
void char_map_render(SDL_Renderer *renderer, char *text) {
int x = 0;
for (int i = 0; i < strlen(text); i++) {
char c = text[i];
SDL_Texture *texture = *char_map_get(c);
SDL_Rect rect = {x, 0, 16, 16};
SDL_RenderCopy(renderer, texture, NULL, &rect);
x += 16;
}
}

18
gui/char_map.h Normal file
View File

@ -0,0 +1,18 @@
//
// Created by william on 6/8/24.
//
#ifndef NES_EMULATOR_CHAR_MAP_H
#define NES_EMULATOR_CHAR_MAP_H
#define CHAR_MAP_COLOR {0, 255, 0};
#define CHAR_MAP_MIN 32
#define CHAR_MAP_MAX 126
#define CHAR_MAP_COUNT (CHAR_MAP_MAX - CHAR_MAP_MIN)
void char_map_init(SDL_Renderer *renderer, TTF_Font *font);
void char_map_uninit();
void char_map_render(SDL_Renderer *renderer, char *text);
#endif //NES_EMULATOR_CHAR_MAP_H

View File

@ -2,26 +2,58 @@
// Created by william on 16/05/24. // Created by william on 16/05/24.
// //
#include <assert.h> #include <SDL_ttf.h>
#include "log.h" #include "log.h"
#include "gui.h" #include "gui.h"
#include "window.h" #include "main_window.h"
#include "pattern_window.h"
#include "../include/system.h"
typedef struct nes_gui { typedef struct nes_gui {
NesWindow main_window; NesMainWindow main_window;
NesWindow debug_pattern_window; NesPatternWindow pattern_window;
TTF_Font *font;
// Debug info
bool debug_enabled;
Uint32 last_frame_tick;
Uint32 frame_delay;
} NesGui; } NesGui;
NesGui gui; NesGui gui;
void gui_init() { bool gui_init() {
gui.main_window = window_init(WINDOW_MAIN_WIDTH, WINDOW_MAIN_HEIGHT, WINDOW_MAIN_SCALING, "NES Emulator"); TTF_Init();
gui.debug_pattern_window = window_init(WINDOW_PATTERN_WIDTH, WINDOW_PATTERN_HEIGHT, WINDOW_PATTERN_SCALING, gui.font = TTF_OpenFont("../nintendo-nes-font.ttf", 16);
"Pattern Table"); if (gui.font == NULL) {
log_error("Failed to open TTF font");
return false;
}
gui.debug_enabled = true;
main_window_init(&gui.main_window, gui.font);
if (gui.debug_enabled) {
pattern_window_init(&gui.pattern_window);
}
return true;
} }
void gui_uninit() { void gui_uninit() {
window_uninit(&gui.main_window); main_window_uninit(&gui.main_window);
window_uninit(&gui.debug_pattern_window);
if (gui.debug_enabled) {
pattern_window_uninit(&gui.pattern_window);
}
TTF_CloseFont(gui.font);
}
void gui_post_sysinit() {
byte *pattern_memory = system_get_mapper()->ppu_read(0);
pattern_window_build_table(&gui.pattern_window, pattern_memory);
} }
int gui_input() { int gui_input() {
@ -37,33 +69,38 @@ int gui_input() {
} }
void gui_render() { void gui_render() {
window_render(&gui.main_window); main_window_render(&gui.main_window, ppu_get_state()->pixels);
window_render(&gui.debug_pattern_window);
if (gui.debug_enabled) {
pattern_window_render(&gui.pattern_window);
}
} }
void gui_present() { void gui_present() {
window_present(&gui.main_window); main_window_present(&gui.main_window);
window_present(&gui.debug_pattern_window);
if (gui.debug_enabled) {
pattern_window_present(&gui.pattern_window);
}
} }
void gui_delay() { void gui_delay() {
SDL_Delay(16); Uint32 tick_now = SDL_GetTicks();
} gui.frame_delay = tick_now - gui.last_frame_tick;
Canvas *gui_get_canvas(char win_id) { if (gui.frame_delay < 16) {
NesWindow *window; Uint32 additional_delay = 16 - gui.frame_delay;
SDL_Delay(additional_delay);
switch (win_id) { gui.frame_delay += additional_delay;
case WINDOW_ID_MAIN:
window = &gui.main_window;
break;
case WINDOW_ID_PATTERN:
window = &gui.debug_pattern_window;
break;
default:
log_error("Couldn't get canvas for window ID '%d' because it doesn't exists", win_id);
assert(false);
} }
return &window->canvas; gui.last_frame_tick = SDL_GetTicks();
}
bool gui_debug_enabled() {
return gui.debug_enabled;
}
unsigned int gui_get_frame_delay() {
return gui.frame_delay;
} }

View File

@ -5,26 +5,20 @@
#ifndef NES_EMULATOR_GUI_H #ifndef NES_EMULATOR_GUI_H
#define NES_EMULATOR_GUI_H #define NES_EMULATOR_GUI_H
#include <stdbool.h>
#include "canvas.h" #include "canvas.h"
#define WINDOW_ID_MAIN 1 bool gui_init();
#define WINDOW_MAIN_WIDTH 256
#define WINDOW_MAIN_HEIGHT 240
#define WINDOW_MAIN_SCALING 2
#define WINDOW_ID_PATTERN 2
#define WINDOW_PATTERN_WIDTH 128
#define WINDOW_PATTERN_HEIGHT 256
#define WINDOW_PATTERN_SCALING 2
void gui_init();
void gui_uninit(); void gui_uninit();
void gui_post_sysinit();
int gui_input(); int gui_input();
void gui_render(); void gui_render();
void gui_present(); void gui_present();
void gui_delay(); void gui_delay();
Canvas *gui_get_canvas(char win_id); bool gui_debug_enabled();
unsigned int gui_get_frame_delay();
#endif //NES_EMULATOR_GUI_H #endif //NES_EMULATOR_GUI_H

63
gui/main_window.c Normal file
View File

@ -0,0 +1,63 @@
//
// Created by william on 6/8/24.
//
#include <SDL_ttf.h>
#include "main_window.h"
#include "log.h"
#include "char_map.h"
#include "gui.h"
void main_window_init(NesMainWindow *window, TTF_Font *font) {
window->sdl_context = window_init("NES Emulator", MAIN_WINDOW_WIDTH, MAIN_WINDOW_HEIGHT, MAIN_WINDOW_SCALE);
window->texture = SDL_CreateTexture(window->sdl_context.renderer, SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING, MAIN_WINDOW_WIDTH, MAIN_WINDOW_HEIGHT);
char_map_init(window->sdl_context.renderer, font);
}
void main_window_uninit(NesMainWindow *window) {
char_map_uninit();
SDL_DestroyTexture(window->texture);
window_uninit(window->sdl_context);
}
void main_window_render_delay(SDL_Renderer *renderer) {
Uint32 delay = gui_get_frame_delay();
char buffer[5];
buffer[0] = (char) ((delay / 10) + 48);
buffer[1] = (char) ((delay % 10) + 48);
buffer[2] = ' ';
buffer[3] = 'm';
buffer[4] = 's';
char_map_render(renderer, buffer);
}
void main_window_render(NesMainWindow *window, PpuPixel *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, &frame_buffer, 240 * sizeof(unsigned int));
SDL_RenderCopy(window->sdl_context.renderer, window->texture, NULL, NULL);
if (gui_debug_enabled()) {
main_window_render_delay(window->sdl_context.renderer);
}
}
void main_window_present(NesMainWindow *window) {
SDL_RenderPresent(window->sdl_context.renderer);
}

28
gui/main_window.h Normal file
View File

@ -0,0 +1,28 @@
//
// Created by william on 6/8/24.
//
#ifndef NES_EMULATOR_MAIN_WINDOW_H
#define NES_EMULATOR_MAIN_WINDOW_H
#include <SDL.h>
#include "../include/ppu.h"
#include "window.h"
#define MAIN_WINDOW_WIDTH 240
#define MAIN_WINDOW_HEIGHT 256
#define MAIN_WINDOW_SCALE 2
typedef struct nes_main_window {
NesSdlContext sdl_context;
SDL_Texture *texture;
} NesMainWindow;
void main_window_init(NesMainWindow *window, TTF_Font *font);
void main_window_uninit(NesMainWindow *window);
void main_window_render(NesMainWindow *window, PpuPixel* pixels);
void main_window_present(NesMainWindow *window);
#endif //NES_EMULATOR_MAIN_WINDOW_H

155
gui/pattern_window.c Normal file
View File

@ -0,0 +1,155 @@
//
// Created by william on 6/14/24.
//
#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);
window->texture = SDL_CreateTexture(window->sdl_context.renderer, SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STATIC, PW_WIDTH, PW_HEIGHT);
}
void pattern_window_uninit(NesPatternWindow *window) {
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);
}
void pattern_window_render(NesPatternWindow *window) {
SDL_RenderClear(window->sdl_context.renderer);
SDL_RenderCopy(window->sdl_context.renderer, window->texture, NULL, NULL);
}
void pattern_window_present(NesPatternWindow *window) {
SDL_RenderPresent(window->sdl_context.renderer);
}

37
gui/pattern_window.h Normal file
View File

@ -0,0 +1,37 @@
//
// Created by william on 6/14/24.
//
#ifndef NES_EMULATOR_PATTERN_WINDOW_H
#define NES_EMULATOR_PATTERN_WINDOW_H
#include <SDL.h>
#include "window.h"
#include "../include/types.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;
} NesPatternWindow;
void pattern_window_init(NesPatternWindow *window);
void pattern_window_uninit(NesPatternWindow *window);
void pattern_window_build_table(NesPatternWindow *window, byte* pattern_memory);
void pattern_window_render(NesPatternWindow *window);
void pattern_window_present(NesPatternWindow *window);
#endif //NES_EMULATOR_PATTERN_WINDOW_H

View File

@ -6,12 +6,8 @@
#include "window.h" #include "window.h"
#include "log.h" #include "log.h"
NesWindow window_init(int width, int height, int scaling, char *title) { NesSdlContext window_init(char *title, int width, int height, int scale) {
NesWindow win; NesSdlContext context;
win.scaling = scaling;
win.width = width * scaling;
win.height = height * scaling;
win.canvas = canvas_init(width, height);
int renderer_flags = SDL_RENDERER_ACCELERATED; int renderer_flags = SDL_RENDERER_ACCELERATED;
int window_flags = 0; int window_flags = 0;
@ -21,52 +17,85 @@ NesWindow window_init(int width, int height, int scaling, char *title) {
exit(-1); exit(-1);
} }
win.window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, win.width, win.height, int actual_width = width * scale;
window_flags); int actual_height = height * scale;
if (!win.window) { context.window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, actual_width,
log_error("Failed to open %d x %d window: %s", win.width, win.height, SDL_GetError()); actual_height, window_flags);
if (!context.window) {
log_error("Failed to open %d x %d SDL window: %s", actual_width, actual_height, SDL_GetError());
exit(-1); exit(-1);
} }
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); context.renderer = SDL_CreateRenderer(context.window, -1, renderer_flags);
if (!context.renderer) {
win.renderer = SDL_CreateRenderer(win.window, -1, renderer_flags); log_error("Failed to create renderer: %s", SDL_GetError());
if (!win.renderer) { SDL_DestroyWindow(context.window);
log_error("Failed to create renderer: %s\n", SDL_GetError());
exit(-1); exit(-1);
} }
return win; return context;
} }
void window_uninit(NesWindow *window) { void window_uninit(NesSdlContext context) {
canvas_uninit(&window->canvas); SDL_DestroyRenderer(context.renderer);
SDL_DestroyWindow(context.window);
} }
void window_render(NesWindow *window) { //NesWindow window_init(int width, int height, char *title) {
SDL_RenderClear(window->renderer); // NesWindow win;
// win.width = width;
for (int y = 0; y < window->canvas.height; y++) { // win.height = height;
for (int x = 0; x < window->canvas.width; x++) { //
int pixel_index = x + y * window->canvas.width; // int renderer_flags = SDL_RENDERER_ACCELERATED;
Pixel pixel = window->canvas.pixels[pixel_index]; // int window_flags = 0;
//
SDL_SetRenderDrawColor(window->renderer, pixel.r, pixel.g, pixel.b, 255); // if (SDL_Init(SDL_INIT_VIDEO) < 0) {
// log_error("Couldn't initialize SDL: %s", SDL_GetError());
for (int i = 0; i < window->scaling; i++) { // exit(-1);
for (int j = 0; j < window->scaling; j++) { // }
int scaled_x = x * window->scaling + i; //
int scaled_y = y * window->scaling + j; // win.window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, win.width, win.height,
SDL_RenderDrawPoint(window->renderer, scaled_x, scaled_y); // window_flags);
} // if (!win.window) {
} // log_error("Failed to open %d x %d sdl_window: %s", win.width, win.height, SDL_GetError());
} // exit(-1);
} // }
} //
// win.renderer = SDL_CreateRenderer(win.window, -1, renderer_flags);
void window_present(NesWindow *window) { // if (!win.renderer) {
SDL_RenderPresent(window->renderer); // log_error("Failed to create renderer: %s\n", SDL_GetError());
// exit(-1);
// TODO: Check if this is a good location // }
canvas_reset(&window->canvas); //
} // return win;
//}
//
//void window_uninit(NesWindow *window) {
// SDL_DestroyRenderer(window->renderer);
// SDL_DestroyWindow(window->window);
//}
//
//void window_render(NesWindow *window) {
// SDL_RenderClear(window->renderer);
//
//// for (int y = 0; y < window->canvas.height; y++) {
//// for (int x = 0; x < window->canvas.width; x++) {
//// int pixel_index = x + y * window->canvas.width;
//// Pixel pixel = window->canvas.pixels[pixel_index];
////
//// SDL_SetRenderDrawColor(window->renderer, pixel.r, pixel.g, pixel.b, 255);
////
//// for (int i = 0; i < window->scaling; i++) {
//// for (int j = 0; j < window->scaling; j++) {
//// int scaled_x = x * window->scaling + i;
//// int scaled_y = y * window->scaling + j;
//// SDL_RenderDrawPoint(window->renderer, scaled_x, scaled_y);
//// }
//// }
//// }
//// }
//}
//
//void window_present(NesWindow *window) {
// SDL_RenderPresent(window->renderer);
//}

View File

@ -6,22 +6,13 @@
#define NES_EMULATOR_WINDOW_H #define NES_EMULATOR_WINDOW_H
#include <SDL.h> #include <SDL.h>
#include "canvas.h"
typedef struct nes_window { typedef struct nes_sdl_context {
SDL_Renderer *renderer; SDL_Renderer *renderer;
SDL_Window *window; SDL_Window *window;
} NesSdlContext;
int width; NesSdlContext window_init(char *title, int width, int height, int scale);
int height; void window_uninit(NesSdlContext context);
int scaling;
Canvas canvas;
} NesWindow;
NesWindow window_init(int width, int height, int scaling, char *title);
void window_uninit(NesWindow *window);
void window_render(NesWindow *window);
void window_present(NesWindow *window);
#endif //NES_EMULATOR_WINDOW_H #endif //NES_EMULATOR_WINDOW_H

View File

@ -69,8 +69,15 @@ typedef struct ppu_tile_fetch {
byte pattern_table_tile_high; byte pattern_table_tile_high;
} PPUTileFetch; } PPUTileFetch;
typedef struct ppu_pixel {
byte r;
byte g;
byte b;
} PpuPixel;
typedef struct ppu { typedef struct ppu {
PPUMemory memory; PPUMemory memory;
PpuPixel pixels[256 * 240];
byte registers[8]; byte registers[8];
byte oam_dma_register; byte oam_dma_register;

19
main.c
View File

@ -23,17 +23,22 @@
#include "gui.h" #include "gui.h"
int main() { int main() {
char *rom_path = "../test_roms/dk_japan.nes";
log_set_level(LOG_INFO); log_set_level(LOG_INFO);
system_init(); if (!gui_init()) {
char *rom_path = "../test_roms/nestest.nes";
if (!rom_load(rom_path)) {
system_uninit();
return EXIT_FAILURE; return EXIT_FAILURE;
} }
gui_init(); system_init();
if (!rom_load(rom_path)) {
system_uninit();
gui_uninit();
return EXIT_FAILURE;
}
gui_post_sysinit();
system_start(); system_start();
bool stop = false; bool stop = false;
@ -45,6 +50,7 @@ int main() {
system_next_frame(); system_next_frame();
gui_render(); gui_render();
gui_present(); gui_present();
gui_delay(); gui_delay();
} }
@ -53,6 +59,5 @@ int main() {
rom_unload(); rom_unload();
gui_uninit(); gui_uninit();
//// start_debugger();
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

BIN
nintendo-nes-font.ttf Normal file

Binary file not shown.

View File

@ -7,51 +7,55 @@
#include "../gui/canvas.h" #include "../gui/canvas.h"
#include "../gui/gui.h" #include "../gui/gui.h"
inline void test() {
}
void pt_debug() { void pt_debug() {
Canvas *canvas = gui_get_canvas(WINDOW_ID_PATTERN); // Canvas *canvas = gui_get_canvas(WINDOW_ID_PATTERN);
//
for (int palette = 0; palette < 2; palette++) { // for (int palette = 0; palette < 2; palette++) {
address palette_addr = palette * 0x1000; // address palette_addr = palette * 0x1000;
int palette_canvas_offset = palette * (PATTERN_TABLE_WIDTH * PATTERN_TILE_SIZE); // int palette_canvas_offset = palette * (PATTERN_TABLE_WIDTH * PATTERN_TILE_SIZE);
//
for (int y = 0; y < PATTERN_TABLE_WIDTH; y++) { // for (int y = 0; y < PATTERN_TABLE_WIDTH; y++) {
for (int x = 0; x < PATTERN_TABLE_WIDTH; x++) { // for (int x = 0; x < PATTERN_TABLE_WIDTH; x++) {
address tile_addr = palette_addr + (x + y * PATTERN_TABLE_WIDTH) * PATTERN_TILE_MEM_SIZE; // address tile_addr = palette_addr + (x + y * PATTERN_TABLE_WIDTH) * PATTERN_TILE_MEM_SIZE;
//
for (int tile_y = 0; tile_y < PATTERN_TILE_SIZE; tile_y++) { // for (int tile_y = 0; tile_y < PATTERN_TILE_SIZE; tile_y++) {
byte p1_data = ppu_read(tile_addr + tile_y); // byte p1_data = ppu_read(tile_addr + tile_y);
byte p2_data = ppu_read(tile_addr + tile_y + PATTERN_TILE_SIZE); // byte p2_data = ppu_read(tile_addr + tile_y + PATTERN_TILE_SIZE);
//
for (int tile_x = 0; tile_x < PATTERN_TILE_SIZE; tile_x++) { // for (int tile_x = 0; tile_x < PATTERN_TILE_SIZE; tile_x++) {
byte bitmask = 1 << (PATTERN_TILE_SIZE - tile_x - 1); // byte bitmask = 1 << (PATTERN_TILE_SIZE - tile_x - 1);
byte p1_byte = p1_data & bitmask; // byte p1_byte = p1_data & bitmask;
byte p2_byte = p2_data & bitmask; // byte p2_byte = p2_data & bitmask;
//
Pixel pixel; // Pixel pixel;
if (p1_byte && p2_byte) { // if (p1_byte && p2_byte) {
pixel.r = 255; // pixel.r = 255;
pixel.g = 255; // pixel.g = 255;
pixel.b = 255; // pixel.b = 255;
} else if (p2_byte) { // } else if (p2_byte) {
pixel.r = 255; // pixel.r = 255;
pixel.g = 0; // pixel.g = 0;
pixel.b = 0; // pixel.b = 0;
} else if (p1_byte) { // } else if (p1_byte) {
pixel.r = 0; // pixel.r = 0;
pixel.g = 255; // pixel.g = 255;
pixel.b = 255; // pixel.b = 255;
} else { // } else {
pixel.r = 0; // pixel.r = 0;
pixel.g = 0; // pixel.g = 0;
pixel.b = 0; // pixel.b = 0;
} // }
//
int canvas_x = x * PATTERN_TILE_SIZE + tile_x; // int canvas_x = x * PATTERN_TILE_SIZE + tile_x;
int canvas_y = y * PATTERN_TILE_SIZE + tile_y + palette_canvas_offset; // int canvas_y = y * PATTERN_TILE_SIZE + tile_y + palette_canvas_offset;
canvas_draw_pos(canvas, pixel, canvas_x, canvas_y); // canvas_draw_pos(canvas, pixel, canvas_x, canvas_y);
} // }
} // }
} // }
} // }
} // }
} }

View File

@ -56,39 +56,61 @@ void ppu_trigger_vbl_nmi() {
cpu_trigger_nmi(); cpu_trigger_nmi();
} }
/*
d888888b d888888b db d88888b
`~~88~~' `88' 88 88'
88 88 88 88ooooo
88 88 88 88~~~~~
88 .88. 88booo. 88.
YP Y888888P Y88888P Y88888P
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 inline unsigned int ppu_pixel_get_index(unsigned int scanline, unsigned int cycle) {
return scanline * PPU_VISIBLE_FRAME_END + cycle;
}
static inline byte ppu_pixel_get_mask(unsigned int cycle) {
byte tile_fine_x = (cycle - 1) % 8;
return 1 << (PATTERN_TILE_SIZE - tile_fine_x - 1);
}
static inline void ppu_pixel_set_color(PpuPixel *pixel, byte pt_low, byte pt_high, byte bitmask) {
byte p1_byte = pt_low & bitmask;
byte p2_byte = pt_high & bitmask;
if (p1_byte && p2_byte) {
pixel->r = 255;
pixel->g = 255;
pixel->b = 255;
} else if (p2_byte) {
pixel->r = 255;
pixel->g = 0;
pixel->b = 0;
} else if (p1_byte) {
pixel->r = 0;
pixel->g = 255;
pixel->b = 255;
} else {
pixel->r = 0;
pixel->g = 0;
pixel->b = 0;
}
}
void ppu_draw_tile() { void ppu_draw_tile() {
PPUTileFetch tile_fetch = ppu_state.tile_fetch; PPUTileFetch tile_fetch = ppu_state.tile_fetch;
Canvas *canvas = gui_get_canvas(WINDOW_ID_MAIN);
byte tile_fine_x = (ppu_state.cycle - 1) % 8; unsigned int pixel_index = ppu_pixel_get_index(ppu_state.scanline, ppu_state.cycle);
PpuPixel *pixel = &ppu_state.pixels[pixel_index];
byte bitmask = 1 << (PATTERN_TILE_SIZE - tile_fine_x - 1); byte pixel_mask = ppu_pixel_get_mask(ppu_state.cycle);
byte p1_byte = tile_fetch.pattern_table_tile_low & bitmask; ppu_pixel_set_color(pixel, tile_fetch.pattern_table_tile_low, tile_fetch.pattern_table_tile_high, pixel_mask);
byte p2_byte = tile_fetch.pattern_table_tile_high & bitmask;
Pixel pixel;
// pixel.r = ~tile_fetch.nametable;
// pixel.g = ~tile_fetch.nametable;
// pixel.b = tile_fetch.nametable;
if (p1_byte && p2_byte) {
pixel.r = 255;
pixel.g = 255;
pixel.b = 255;
} else if (p2_byte) {
pixel.r = 255;
pixel.g = 0;
pixel.b = 0;
} else if (p1_byte) {
pixel.r = 0;
pixel.g = 255;
pixel.b = 255;
} else {
pixel.r = 0;
pixel.g = 0;
pixel.b = 0;
}
canvas_draw_pos(canvas, pixel, ppu_state.cycle, ppu_state.scanline);
} }
void ppu_visible_frame(unsigned int cycle) { void ppu_visible_frame(unsigned int cycle) {