Efficient rendering
This commit is contained in:
parent
d9f0c67668
commit
dcb01b4c6a
|
@ -1,9 +1,9 @@
|
|||
set(HEADERS canvas.h gui.h window.h)
|
||||
set(SOURCE canvas.c gui.c window.c)
|
||||
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)
|
||||
|
||||
add_library(nes_gui ${SOURCE} ${HEADERS})
|
||||
|
||||
find_package(SDL2 REQUIRED)
|
||||
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)
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
97
gui/gui.c
97
gui/gui.c
|
@ -2,26 +2,58 @@
|
|||
// Created by william on 16/05/24.
|
||||
//
|
||||
|
||||
#include <assert.h>
|
||||
#include <SDL_ttf.h>
|
||||
#include "log.h"
|
||||
#include "gui.h"
|
||||
#include "window.h"
|
||||
#include "main_window.h"
|
||||
#include "pattern_window.h"
|
||||
#include "../include/system.h"
|
||||
|
||||
typedef struct nes_gui {
|
||||
NesWindow main_window;
|
||||
NesWindow debug_pattern_window;
|
||||
NesMainWindow main_window;
|
||||
NesPatternWindow pattern_window;
|
||||
|
||||
TTF_Font *font;
|
||||
|
||||
// Debug info
|
||||
bool debug_enabled;
|
||||
Uint32 last_frame_tick;
|
||||
Uint32 frame_delay;
|
||||
} NesGui;
|
||||
NesGui gui;
|
||||
|
||||
void gui_init() {
|
||||
gui.main_window = window_init(WINDOW_MAIN_WIDTH, WINDOW_MAIN_HEIGHT, WINDOW_MAIN_SCALING, "NES Emulator");
|
||||
gui.debug_pattern_window = window_init(WINDOW_PATTERN_WIDTH, WINDOW_PATTERN_HEIGHT, WINDOW_PATTERN_SCALING,
|
||||
"Pattern Table");
|
||||
bool gui_init() {
|
||||
TTF_Init();
|
||||
gui.font = TTF_OpenFont("../nintendo-nes-font.ttf", 16);
|
||||
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() {
|
||||
window_uninit(&gui.main_window);
|
||||
window_uninit(&gui.debug_pattern_window);
|
||||
main_window_uninit(&gui.main_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() {
|
||||
|
@ -37,33 +69,38 @@ int gui_input() {
|
|||
}
|
||||
|
||||
void gui_render() {
|
||||
window_render(&gui.main_window);
|
||||
window_render(&gui.debug_pattern_window);
|
||||
main_window_render(&gui.main_window, ppu_get_state()->pixels);
|
||||
|
||||
if (gui.debug_enabled) {
|
||||
pattern_window_render(&gui.pattern_window);
|
||||
}
|
||||
}
|
||||
|
||||
void gui_present() {
|
||||
window_present(&gui.main_window);
|
||||
window_present(&gui.debug_pattern_window);
|
||||
main_window_present(&gui.main_window);
|
||||
|
||||
if (gui.debug_enabled) {
|
||||
pattern_window_present(&gui.pattern_window);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
NesWindow *window;
|
||||
|
||||
switch (win_id) {
|
||||
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);
|
||||
if (gui.frame_delay < 16) {
|
||||
Uint32 additional_delay = 16 - gui.frame_delay;
|
||||
SDL_Delay(additional_delay);
|
||||
gui.frame_delay += additional_delay;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
18
gui/gui.h
18
gui/gui.h
|
@ -5,26 +5,20 @@
|
|||
#ifndef NES_EMULATOR_GUI_H
|
||||
#define NES_EMULATOR_GUI_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "canvas.h"
|
||||
|
||||
#define WINDOW_ID_MAIN 1
|
||||
#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();
|
||||
bool gui_init();
|
||||
void gui_uninit();
|
||||
|
||||
void gui_post_sysinit();
|
||||
|
||||
int gui_input();
|
||||
void gui_render();
|
||||
void gui_present();
|
||||
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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
119
gui/window.c
119
gui/window.c
|
@ -6,12 +6,8 @@
|
|||
#include "window.h"
|
||||
#include "log.h"
|
||||
|
||||
NesWindow window_init(int width, int height, int scaling, char *title) {
|
||||
NesWindow win;
|
||||
win.scaling = scaling;
|
||||
win.width = width * scaling;
|
||||
win.height = height * scaling;
|
||||
win.canvas = canvas_init(width, height);
|
||||
NesSdlContext window_init(char *title, int width, int height, int scale) {
|
||||
NesSdlContext context;
|
||||
|
||||
int renderer_flags = SDL_RENDERER_ACCELERATED;
|
||||
int window_flags = 0;
|
||||
|
@ -21,52 +17,85 @@ NesWindow window_init(int width, int height, int scaling, char *title) {
|
|||
exit(-1);
|
||||
}
|
||||
|
||||
win.window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, win.width, win.height,
|
||||
window_flags);
|
||||
if (!win.window) {
|
||||
log_error("Failed to open %d x %d window: %s", win.width, win.height, SDL_GetError());
|
||||
int actual_width = width * scale;
|
||||
int actual_height = height * scale;
|
||||
context.window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, actual_width,
|
||||
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);
|
||||
}
|
||||
|
||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
|
||||
|
||||
win.renderer = SDL_CreateRenderer(win.window, -1, renderer_flags);
|
||||
if (!win.renderer) {
|
||||
log_error("Failed to create renderer: %s\n", SDL_GetError());
|
||||
context.renderer = SDL_CreateRenderer(context.window, -1, renderer_flags);
|
||||
if (!context.renderer) {
|
||||
log_error("Failed to create renderer: %s", SDL_GetError());
|
||||
SDL_DestroyWindow(context.window);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
return win;
|
||||
return context;
|
||||
}
|
||||
|
||||
void window_uninit(NesWindow *window) {
|
||||
canvas_uninit(&window->canvas);
|
||||
void window_uninit(NesSdlContext context) {
|
||||
SDL_DestroyRenderer(context.renderer);
|
||||
SDL_DestroyWindow(context.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);
|
||||
|
||||
// TODO: Check if this is a good location
|
||||
canvas_reset(&window->canvas);
|
||||
}
|
||||
//NesWindow window_init(int width, int height, char *title) {
|
||||
// NesWindow win;
|
||||
// win.width = width;
|
||||
// win.height = height;
|
||||
//
|
||||
// int renderer_flags = SDL_RENDERER_ACCELERATED;
|
||||
// int window_flags = 0;
|
||||
//
|
||||
// if (SDL_Init(SDL_INIT_VIDEO) < 0) {
|
||||
// log_error("Couldn't initialize SDL: %s", SDL_GetError());
|
||||
// exit(-1);
|
||||
// }
|
||||
//
|
||||
// win.window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, win.width, win.height,
|
||||
// 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);
|
||||
// if (!win.renderer) {
|
||||
// log_error("Failed to create renderer: %s\n", SDL_GetError());
|
||||
// exit(-1);
|
||||
// }
|
||||
//
|
||||
// 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);
|
||||
//}
|
17
gui/window.h
17
gui/window.h
|
@ -6,22 +6,13 @@
|
|||
#define NES_EMULATOR_WINDOW_H
|
||||
|
||||
#include <SDL.h>
|
||||
#include "canvas.h"
|
||||
|
||||
typedef struct nes_window {
|
||||
typedef struct nes_sdl_context {
|
||||
SDL_Renderer *renderer;
|
||||
SDL_Window *window;
|
||||
} NesSdlContext;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
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);
|
||||
NesSdlContext window_init(char *title, int width, int height, int scale);
|
||||
void window_uninit(NesSdlContext context);
|
||||
|
||||
#endif //NES_EMULATOR_WINDOW_H
|
|
@ -69,8 +69,15 @@ typedef struct ppu_tile_fetch {
|
|||
byte pattern_table_tile_high;
|
||||
} PPUTileFetch;
|
||||
|
||||
typedef struct ppu_pixel {
|
||||
byte r;
|
||||
byte g;
|
||||
byte b;
|
||||
} PpuPixel;
|
||||
|
||||
typedef struct ppu {
|
||||
PPUMemory memory;
|
||||
PpuPixel pixels[256 * 240];
|
||||
|
||||
byte registers[8];
|
||||
byte oam_dma_register;
|
||||
|
|
19
main.c
19
main.c
|
@ -23,17 +23,22 @@
|
|||
#include "gui.h"
|
||||
|
||||
int main() {
|
||||
char *rom_path = "../test_roms/dk_japan.nes";
|
||||
log_set_level(LOG_INFO);
|
||||
|
||||
system_init();
|
||||
char *rom_path = "../test_roms/nestest.nes";
|
||||
|
||||
if (!rom_load(rom_path)) {
|
||||
system_uninit();
|
||||
if (!gui_init()) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
gui_init();
|
||||
system_init();
|
||||
|
||||
if (!rom_load(rom_path)) {
|
||||
system_uninit();
|
||||
gui_uninit();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
gui_post_sysinit();
|
||||
system_start();
|
||||
|
||||
bool stop = false;
|
||||
|
@ -45,6 +50,7 @@ int main() {
|
|||
system_next_frame();
|
||||
|
||||
gui_render();
|
||||
|
||||
gui_present();
|
||||
gui_delay();
|
||||
}
|
||||
|
@ -53,6 +59,5 @@ int main() {
|
|||
rom_unload();
|
||||
gui_uninit();
|
||||
|
||||
//// start_debugger();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
Binary file not shown.
|
@ -7,51 +7,55 @@
|
|||
#include "../gui/canvas.h"
|
||||
#include "../gui/gui.h"
|
||||
|
||||
void pt_debug() {
|
||||
Canvas *canvas = gui_get_canvas(WINDOW_ID_PATTERN);
|
||||
inline void test() {
|
||||
|
||||
for (int palette = 0; palette < 2; palette++) {
|
||||
address palette_addr = palette * 0x1000;
|
||||
int palette_canvas_offset = palette * (PATTERN_TABLE_WIDTH * PATTERN_TILE_SIZE);
|
||||
|
||||
for (int y = 0; y < PATTERN_TABLE_WIDTH; y++) {
|
||||
for (int x = 0; x < PATTERN_TABLE_WIDTH; x++) {
|
||||
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++) {
|
||||
byte p1_data = ppu_read(tile_addr + tile_y);
|
||||
byte p2_data = ppu_read(tile_addr + tile_y + PATTERN_TILE_SIZE);
|
||||
|
||||
for (int tile_x = 0; tile_x < PATTERN_TILE_SIZE; tile_x++) {
|
||||
byte bitmask = 1 << (PATTERN_TILE_SIZE - tile_x - 1);
|
||||
byte p1_byte = p1_data & bitmask;
|
||||
byte p2_byte = p2_data & bitmask;
|
||||
|
||||
Pixel pixel;
|
||||
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;
|
||||
}
|
||||
|
||||
int canvas_x = x * PATTERN_TILE_SIZE + tile_x;
|
||||
int canvas_y = y * PATTERN_TILE_SIZE + tile_y + palette_canvas_offset;
|
||||
canvas_draw_pos(canvas, pixel, canvas_x, canvas_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pt_debug() {
|
||||
// Canvas *canvas = gui_get_canvas(WINDOW_ID_PATTERN);
|
||||
//
|
||||
// for (int palette = 0; palette < 2; palette++) {
|
||||
// address palette_addr = palette * 0x1000;
|
||||
// int palette_canvas_offset = palette * (PATTERN_TABLE_WIDTH * PATTERN_TILE_SIZE);
|
||||
//
|
||||
// for (int y = 0; y < PATTERN_TABLE_WIDTH; y++) {
|
||||
// for (int x = 0; x < PATTERN_TABLE_WIDTH; x++) {
|
||||
// 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++) {
|
||||
// byte p1_data = ppu_read(tile_addr + tile_y);
|
||||
// byte p2_data = ppu_read(tile_addr + tile_y + PATTERN_TILE_SIZE);
|
||||
//
|
||||
// for (int tile_x = 0; tile_x < PATTERN_TILE_SIZE; tile_x++) {
|
||||
// byte bitmask = 1 << (PATTERN_TILE_SIZE - tile_x - 1);
|
||||
// byte p1_byte = p1_data & bitmask;
|
||||
// byte p2_byte = p2_data & bitmask;
|
||||
//
|
||||
// Pixel pixel;
|
||||
// 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;
|
||||
// }
|
||||
//
|
||||
// int canvas_x = x * PATTERN_TILE_SIZE + tile_x;
|
||||
// int canvas_y = y * PATTERN_TILE_SIZE + tile_y + palette_canvas_offset;
|
||||
// canvas_draw_pos(canvas, pixel, canvas_x, canvas_y);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
82
ppu/ppu.c
82
ppu/ppu.c
|
@ -56,39 +56,61 @@ void ppu_trigger_vbl_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() {
|
||||
PPUTileFetch tile_fetch = ppu_state.tile_fetch;
|
||||
Canvas *canvas = gui_get_canvas(WINDOW_ID_MAIN);
|
||||
|
||||
byte tile_fine_x = (ppu_state.cycle - 1) % 8;
|
||||
|
||||
byte bitmask = 1 << (PATTERN_TILE_SIZE - tile_fine_x - 1);
|
||||
byte p1_byte = tile_fetch.pattern_table_tile_low & bitmask;
|
||||
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);
|
||||
unsigned int pixel_index = ppu_pixel_get_index(ppu_state.scanline, ppu_state.cycle);
|
||||
PpuPixel *pixel = &ppu_state.pixels[pixel_index];
|
||||
byte pixel_mask = ppu_pixel_get_mask(ppu_state.cycle);
|
||||
ppu_pixel_set_color(pixel, tile_fetch.pattern_table_tile_low, tile_fetch.pattern_table_tile_high, pixel_mask);
|
||||
}
|
||||
|
||||
void ppu_visible_frame(unsigned int cycle) {
|
||||
|
|
Loading…
Reference in New Issue