Compare commits
2 Commits
662d7efa59
...
96af510b19
Author | SHA1 | Date |
---|---|---|
FyloZ | 96af510b19 | |
FyloZ | 54825ecb5a |
|
@ -3,6 +3,10 @@ set(CMAKE_C_COMPILER gcc)
|
||||||
cmake_minimum_required(VERSION 3.10)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
project(nes_emulator LANGUAGES C VERSION 0.1 )
|
project(nes_emulator LANGUAGES C VERSION 0.1 )
|
||||||
|
|
||||||
|
if (NES_DEBUG)
|
||||||
|
add_definitions(-DDEBUG=1)
|
||||||
|
endif(NES_DEBUG)
|
||||||
|
|
||||||
add_subdirectory(libs/log.c)
|
add_subdirectory(libs/log.c)
|
||||||
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
|
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
|
||||||
include_directories(${PROJECT_SOURCE_DIR}/libs/log.c/src ${COMMON_INCLUDES})
|
include_directories(${PROJECT_SOURCE_DIR}/libs/log.c/src ${COMMON_INCLUDES})
|
||||||
|
@ -11,7 +15,6 @@ add_subdirectory(cpu)
|
||||||
add_subdirectory(ppu)
|
add_subdirectory(ppu)
|
||||||
add_subdirectory(mappers)
|
add_subdirectory(mappers)
|
||||||
add_subdirectory(rom)
|
add_subdirectory(rom)
|
||||||
#add_subdirectory(debugger)
|
|
||||||
add_subdirectory(utils)
|
add_subdirectory(utils)
|
||||||
add_subdirectory(gui)
|
add_subdirectory(gui)
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ only tested on Linux. Here is how to run the project:
|
||||||
- Debug
|
- Debug
|
||||||
- Frame Delay: Done
|
- Frame Delay: Done
|
||||||
- Pattern Table Viewer: Done
|
- Pattern Table Viewer: Done
|
||||||
|
- Nametable Viewer: Done
|
||||||
- CPU Debugger: To Do
|
- CPU Debugger: To Do
|
||||||
- Memory Inspector: To Do
|
- Memory Inspector: To Do
|
||||||
- PPU Debugger: To Do
|
- PPU Debugger: To Do
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
set(HEADERS canvas.h gui.h window.h main_window.h char_map.h pattern_display.h pattern_window.h nametable_window.h dbg_pattern_table.h dbg_nametable.h)
|
set(HEADERS gui.h window.h main_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 dbg_pattern_table.c dbg_nametable.c)
|
set(SOURCE gui.c window.c main_window.c)
|
||||||
|
|
||||||
|
if (NES_DEBUG)
|
||||||
|
list(APPEND HEADERS char_map.h pattern_window.h nametable_window.h dbg_pattern_table.h dbg_nametable.h)
|
||||||
|
list(APPEND SOURCE char_map.c pattern_window.c nametable_window.c dbg_pattern_table.c dbg_nametable.c)
|
||||||
|
endif (NES_DEBUG)
|
||||||
|
|
||||||
add_library(nes_gui ${SOURCE} ${HEADERS})
|
add_library(nes_gui ${SOURCE} ${HEADERS})
|
||||||
|
|
||||||
|
|
50
gui/canvas.c
50
gui/canvas.c
|
@ -1,50 +0,0 @@
|
||||||
//
|
|
||||||
// Created by william on 16/05/24.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "canvas.h"
|
|
||||||
|
|
||||||
Canvas canvas_init(int width, int height) {
|
|
||||||
assert(width > 0);
|
|
||||||
assert(height > 0);
|
|
||||||
|
|
||||||
Canvas canvas;
|
|
||||||
canvas.width = width;
|
|
||||||
canvas.height = height;
|
|
||||||
|
|
||||||
int size = width * height;
|
|
||||||
canvas.pixels = malloc(sizeof(Pixel) * size);
|
|
||||||
|
|
||||||
return canvas;
|
|
||||||
}
|
|
||||||
|
|
||||||
void canvas_uninit(Canvas *canvas) {
|
|
||||||
assert(canvas != NULL);
|
|
||||||
assert(canvas->pixels != NULL);
|
|
||||||
|
|
||||||
free(canvas->pixels);
|
|
||||||
}
|
|
||||||
|
|
||||||
void canvas_draw(Canvas *canvas, Pixel pixel, int index) {
|
|
||||||
assert(index >= 0);
|
|
||||||
assert(index < canvas->width * canvas->height);
|
|
||||||
|
|
||||||
canvas->pixels[index] = pixel;
|
|
||||||
}
|
|
||||||
|
|
||||||
void canvas_draw_pos(Canvas *canvas, Pixel pixel, int x, int y) {
|
|
||||||
assert(x >= 0);
|
|
||||||
assert(x < canvas->width);
|
|
||||||
assert(y >= 0);
|
|
||||||
assert(y < canvas->height);
|
|
||||||
|
|
||||||
int index = x + y * canvas->width;
|
|
||||||
canvas_draw(canvas, pixel, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
void canvas_reset(Canvas *canvas) {
|
|
||||||
memset(canvas->pixels, 0, sizeof(Pixel) * (canvas->width * canvas->height));
|
|
||||||
}
|
|
29
gui/canvas.h
29
gui/canvas.h
|
@ -1,29 +0,0 @@
|
||||||
//
|
|
||||||
// Created by william on 16/05/24.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef NES_EMULATOR_CANVAS_H
|
|
||||||
#define NES_EMULATOR_CANVAS_H
|
|
||||||
|
|
||||||
#include "../include/types.h"
|
|
||||||
|
|
||||||
typedef struct pixel {
|
|
||||||
byte r;
|
|
||||||
byte g;
|
|
||||||
byte b;
|
|
||||||
} Pixel;
|
|
||||||
|
|
||||||
typedef struct canvas {
|
|
||||||
int width;
|
|
||||||
int height;
|
|
||||||
Pixel *pixels;
|
|
||||||
} Canvas;
|
|
||||||
|
|
||||||
Canvas canvas_init(int width, int height);
|
|
||||||
void canvas_uninit(Canvas *canvas);
|
|
||||||
|
|
||||||
void canvas_draw(Canvas *canvas, Pixel pixel, int index);
|
|
||||||
void canvas_draw_pos(Canvas *canvas, Pixel pixel, int x, int y);
|
|
||||||
void canvas_reset(Canvas *canvas);
|
|
||||||
|
|
||||||
#endif //NES_EMULATOR_CANVAS_H
|
|
70
gui/gui.c
70
gui/gui.c
|
@ -11,6 +11,7 @@
|
||||||
#include "nametable_window.h"
|
#include "nametable_window.h"
|
||||||
#include "dbg_pattern_table.h"
|
#include "dbg_pattern_table.h"
|
||||||
#include "dbg_nametable.h"
|
#include "dbg_nametable.h"
|
||||||
|
#include "char_map.h"
|
||||||
|
|
||||||
typedef struct nes_gui {
|
typedef struct nes_gui {
|
||||||
NesMainWindow main_window;
|
NesMainWindow main_window;
|
||||||
|
@ -19,11 +20,12 @@ typedef struct nes_gui {
|
||||||
|
|
||||||
TTF_Font *font;
|
TTF_Font *font;
|
||||||
|
|
||||||
// Debug info
|
|
||||||
bool debug_enabled;
|
|
||||||
Uint32 last_frame_tick;
|
Uint32 last_frame_tick;
|
||||||
Uint32 frame_delay;
|
Uint32 frame_delay;
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
unsigned long tick;
|
unsigned long tick;
|
||||||
|
#endif
|
||||||
} NesGui;
|
} NesGui;
|
||||||
NesGui gui;
|
NesGui gui;
|
||||||
|
|
||||||
|
@ -35,15 +37,15 @@ bool gui_init() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
gui.debug_enabled = true;
|
#if DEBUG
|
||||||
gui.tick = 0;
|
gui.tick = 0;
|
||||||
|
pattern_window_init(&gui.pattern_window);
|
||||||
|
nametable_window_init(&gui.nametable_window);
|
||||||
|
|
||||||
main_window_init(&gui.main_window, gui.font);
|
char_map_init(gui.main_window.sdl_context.renderer, gui.font);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (gui.debug_enabled) {
|
main_window_init(&gui.main_window);
|
||||||
pattern_window_init(&gui.pattern_window);
|
|
||||||
nametable_window_init(&gui.nametable_window);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -51,22 +53,24 @@ bool gui_init() {
|
||||||
void gui_uninit() {
|
void gui_uninit() {
|
||||||
main_window_uninit(&gui.main_window);
|
main_window_uninit(&gui.main_window);
|
||||||
|
|
||||||
if (gui.debug_enabled) {
|
#if DEBUG
|
||||||
pattern_window_uninit(&gui.pattern_window);
|
char_map_uninit();
|
||||||
nametable_window_uninit(&gui.nametable_window);
|
|
||||||
}
|
pattern_window_uninit(&gui.pattern_window);
|
||||||
|
nametable_window_uninit(&gui.nametable_window);
|
||||||
|
#endif
|
||||||
|
|
||||||
TTF_CloseFont(gui.font);
|
TTF_CloseFont(gui.font);
|
||||||
}
|
}
|
||||||
|
|
||||||
void gui_post_sysinit() {
|
void gui_post_sysinit() {
|
||||||
if (gui.debug_enabled) {
|
#if DEBUG
|
||||||
dbg_pattern_table_init();
|
dbg_pattern_table_init();
|
||||||
dbg_nametable_init();
|
dbg_nametable_init();
|
||||||
|
|
||||||
pattern_window_build_table(&gui.pattern_window);
|
pattern_window_build_table(&gui.pattern_window);
|
||||||
nametable_window_update(&gui.nametable_window);
|
nametable_window_update(&gui.nametable_window);
|
||||||
}
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int gui_input() {
|
int gui_input() {
|
||||||
|
@ -82,27 +86,25 @@ int gui_input() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void gui_render() {
|
void gui_render() {
|
||||||
main_window_render(&gui.main_window, ppu_get_state()->pixels);
|
#if DEBUG
|
||||||
|
pattern_window_render(&gui.pattern_window);
|
||||||
|
|
||||||
if (gui.debug_enabled) {
|
nametable_window_update(&gui.nametable_window);
|
||||||
pattern_window_render(&gui.pattern_window);
|
nametable_window_render(&gui.nametable_window);
|
||||||
|
|
||||||
// if (gui.tick % 60 == 0) {
|
|
||||||
nametable_window_update(&gui.nametable_window);
|
|
||||||
// }
|
|
||||||
nametable_window_render(&gui.nametable_window);
|
|
||||||
}
|
|
||||||
|
|
||||||
gui.tick++;
|
gui.tick++;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
main_window_render(&gui.main_window, ppu_get_state()->pixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
void gui_present() {
|
void gui_present() {
|
||||||
main_window_present(&gui.main_window);
|
#if DEBUG
|
||||||
|
pattern_window_present(&gui.pattern_window);
|
||||||
|
nametable_window_present(&gui.nametable_window);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (gui.debug_enabled) {
|
main_window_present(&gui.main_window);
|
||||||
pattern_window_present(&gui.pattern_window);
|
|
||||||
nametable_window_present(&gui.nametable_window);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void gui_delay() {
|
void gui_delay() {
|
||||||
|
@ -118,10 +120,6 @@ void gui_delay() {
|
||||||
gui.last_frame_tick = SDL_GetTicks();
|
gui.last_frame_tick = SDL_GetTicks();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool gui_debug_enabled() {
|
|
||||||
return gui.debug_enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int gui_get_frame_delay() {
|
unsigned int gui_get_frame_delay() {
|
||||||
return gui.frame_delay;
|
return gui.frame_delay;
|
||||||
}
|
}
|
|
@ -6,7 +6,6 @@
|
||||||
#define NES_EMULATOR_GUI_H
|
#define NES_EMULATOR_GUI_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "canvas.h"
|
|
||||||
|
|
||||||
bool gui_init();
|
bool gui_init();
|
||||||
void gui_uninit();
|
void gui_uninit();
|
||||||
|
@ -18,7 +17,6 @@ void gui_render();
|
||||||
void gui_present();
|
void gui_present();
|
||||||
void gui_delay();
|
void gui_delay();
|
||||||
|
|
||||||
bool gui_debug_enabled();
|
|
||||||
unsigned int gui_get_frame_delay();
|
unsigned int gui_get_frame_delay();
|
||||||
|
|
||||||
#endif //NES_EMULATOR_GUI_H
|
#endif //NES_EMULATOR_GUI_H
|
||||||
|
|
|
@ -8,20 +8,18 @@
|
||||||
#include "char_map.h"
|
#include "char_map.h"
|
||||||
#include "gui.h"
|
#include "gui.h"
|
||||||
|
|
||||||
void main_window_init(NesMainWindow *window, TTF_Font *font) {
|
void main_window_init(NesMainWindow *window) {
|
||||||
window->sdl_context = window_init("NES Emulator", MAIN_WINDOW_WIDTH, MAIN_WINDOW_HEIGHT, MAIN_WINDOW_SCALE);
|
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,
|
window->texture = SDL_CreateTexture(window->sdl_context.renderer, SDL_PIXELFORMAT_ARGB8888,
|
||||||
SDL_TEXTUREACCESS_STREAMING, MAIN_WINDOW_WIDTH, MAIN_WINDOW_HEIGHT);
|
SDL_TEXTUREACCESS_STREAMING, MAIN_WINDOW_WIDTH, MAIN_WINDOW_HEIGHT);
|
||||||
char_map_init(window->sdl_context.renderer, font);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void main_window_uninit(NesMainWindow *window) {
|
void main_window_uninit(NesMainWindow *window) {
|
||||||
char_map_uninit();
|
|
||||||
|
|
||||||
SDL_DestroyTexture(window->texture);
|
SDL_DestroyTexture(window->texture);
|
||||||
window_uninit(window->sdl_context);
|
window_uninit(window->sdl_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
void main_window_render_delay(SDL_Renderer *renderer) {
|
void main_window_render_delay(SDL_Renderer *renderer) {
|
||||||
Uint32 delay = gui_get_frame_delay();
|
Uint32 delay = gui_get_frame_delay();
|
||||||
|
|
||||||
|
@ -34,12 +32,12 @@ void main_window_render_delay(SDL_Renderer *renderer) {
|
||||||
|
|
||||||
char_map_render(renderer, buffer);
|
char_map_render(renderer, buffer);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void main_window_render(NesMainWindow *window, PPUPixel *pixels) {
|
void main_window_render(NesMainWindow *window, PPUPixel *pixels) {
|
||||||
SDL_RenderClear(window->sdl_context.renderer);
|
SDL_RenderClear(window->sdl_context.renderer);
|
||||||
|
|
||||||
unsigned int frame_buffer[240 * 256];
|
unsigned int frame_buffer[240 * 256];
|
||||||
|
|
||||||
for (int i = 0; i < 240 * 256; i++) {
|
for (int i = 0; i < 240 * 256; i++) {
|
||||||
PPUPixel pixel = pixels[i];
|
PPUPixel pixel = pixels[i];
|
||||||
|
|
||||||
|
@ -53,9 +51,9 @@ void main_window_render(NesMainWindow *window, PPUPixel *pixels) {
|
||||||
SDL_UpdateTexture(window->texture, NULL, &frame_buffer, 240 * sizeof(unsigned int));
|
SDL_UpdateTexture(window->texture, NULL, &frame_buffer, 240 * sizeof(unsigned int));
|
||||||
SDL_RenderCopy(window->sdl_context.renderer, window->texture, NULL, NULL);
|
SDL_RenderCopy(window->sdl_context.renderer, window->texture, NULL, NULL);
|
||||||
|
|
||||||
if (gui_debug_enabled()) {
|
#if DEBUG
|
||||||
main_window_render_delay(window->sdl_context.renderer);
|
main_window_render_delay(window->sdl_context.renderer);
|
||||||
}
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void main_window_present(NesMainWindow *window) {
|
void main_window_present(NesMainWindow *window) {
|
||||||
|
|
|
@ -19,7 +19,7 @@ typedef struct nes_main_window {
|
||||||
SDL_Texture *texture;
|
SDL_Texture *texture;
|
||||||
} NesMainWindow;
|
} NesMainWindow;
|
||||||
|
|
||||||
void main_window_init(NesMainWindow *window, TTF_Font *font);
|
void main_window_init(NesMainWindow *window);
|
||||||
void main_window_uninit(NesMainWindow *window);
|
void main_window_uninit(NesMainWindow *window);
|
||||||
|
|
||||||
void main_window_render(NesMainWindow *window, PPUPixel* pixels);
|
void main_window_render(NesMainWindow *window, PPUPixel* pixels);
|
||||||
|
|
|
@ -10,8 +10,7 @@
|
||||||
#define NW_BUFFER_SIZE (NAMETABLE_ROW_WIDTH * NAMETABLE_COL_HEIGHT * PATTERN_DRAW_SIZE * PATTERN_DRAW_SIZE)
|
#define NW_BUFFER_SIZE (NAMETABLE_ROW_WIDTH * NAMETABLE_COL_HEIGHT * PATTERN_DRAW_SIZE * PATTERN_DRAW_SIZE)
|
||||||
|
|
||||||
void nametable_window_init(NesNametableWindow *window) {
|
void nametable_window_init(NesNametableWindow *window) {
|
||||||
int win_size = pattern_display_get_size(NW_ROW_TILE_COUNT);
|
window->sdl_context = window_init("Nametable", NW_WIDTH, NW_HEIGHT, NW_SCALE);
|
||||||
window->sdl_context = window_init("Nametable", win_size, win_size, NW_SCALE);
|
|
||||||
|
|
||||||
window->texture = SDL_CreateTexture(window->sdl_context.renderer, SDL_PIXELFORMAT_ARGB8888,
|
window->texture = SDL_CreateTexture(window->sdl_context.renderer, SDL_PIXELFORMAT_ARGB8888,
|
||||||
SDL_TEXTUREACCESS_STREAMING, NW_WIDTH, NW_HEIGHT);
|
SDL_TEXTUREACCESS_STREAMING, NW_WIDTH, NW_HEIGHT);
|
||||||
|
@ -22,7 +21,7 @@ void nametable_window_uninit(NesNametableWindow *window) {
|
||||||
window_uninit(window->sdl_context);
|
window_uninit(window->sdl_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nametable_window_update_bank(NesNametableWindow *window, int bank, pixel* buffer) {
|
void nametable_window_update_bank(NesNametableWindow *window, int bank, pixel *buffer) {
|
||||||
dbg_nametable_render_bank(bank, buffer);
|
dbg_nametable_render_bank(bank, buffer);
|
||||||
|
|
||||||
SDL_Rect rect;
|
SDL_Rect rect;
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
|
|
||||||
#include "window.h"
|
#include "window.h"
|
||||||
#include "../include/types.h"
|
#include "../include/types.h"
|
||||||
#include "pattern_display.h"
|
|
||||||
|
|
||||||
#define NW_SCALE 1
|
#define NW_SCALE 1
|
||||||
#define NW_ROW_COUNT 60
|
#define NW_ROW_COUNT 60
|
||||||
|
|
|
@ -1,123 +0,0 @@
|
||||||
//
|
|
||||||
// Created by william on 12/07/24.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#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, int display_type) {
|
|
||||||
assert(tiles_x > 0);
|
|
||||||
assert(tiles_y > 0);
|
|
||||||
|
|
||||||
display->width = tiles_x;
|
|
||||||
display->height = tiles_y;
|
|
||||||
|
|
||||||
display->texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, display_type,
|
|
||||||
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, int tile_count, read_func read_func,
|
|
||||||
void *read_func_data) {
|
|
||||||
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, read_func(tile_addr, read_func_data), 8);
|
|
||||||
memcpy(tile->data_high, read_func(tile_addr + 8, read_func_data), 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, read_func read_func, void *read_func_data) {
|
|
||||||
int tile_count = display->width * display->height;
|
|
||||||
PatternTile *tile_table = malloc(tile_count * sizeof(PatternTile));
|
|
||||||
pattern_display_build_table(tile_table, tile_count, read_func, read_func_data);
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
//
|
|
||||||
// Created by william on 12/07/24.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef NES_EMULATOR_PATTERN_DISPLAY_H
|
|
||||||
#define NES_EMULATOR_PATTERN_DISPLAY_H
|
|
||||||
|
|
||||||
#include <SDL.h>
|
|
||||||
#include "../include/types.h"
|
|
||||||
|
|
||||||
#define PATTERN_DISPLAY_STATIC SDL_TEXTUREACCESS_STATIC
|
|
||||||
#define PATTERN_DISPLAY_DYNAMIC SDL_TEXTUREACCESS_STREAMING
|
|
||||||
|
|
||||||
#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 byte *(*read_func)(address, void *);
|
|
||||||
|
|
||||||
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, int display_type);
|
|
||||||
|
|
||||||
void pattern_display_uninit(PatternDisplay *display);
|
|
||||||
|
|
||||||
void pattern_display_build(PatternDisplay *display, read_func read_func, void *read_func_data);
|
|
||||||
|
|
||||||
void pattern_display_render(PatternDisplay *display, SDL_Renderer *renderer);
|
|
||||||
|
|
||||||
#endif //NES_EMULATOR_PATTERN_DISPLAY_H
|
|
|
@ -10,8 +10,7 @@
|
||||||
#define PW_BUFFER_SIZE (PW_WIDTH * PW_HEIGHT)
|
#define PW_BUFFER_SIZE (PW_WIDTH * PW_HEIGHT)
|
||||||
|
|
||||||
void pattern_window_init(NesPatternWindow *window) {
|
void pattern_window_init(NesPatternWindow *window) {
|
||||||
int win_width = pattern_display_get_size(PW_ROW_TILE_COUNT);
|
window->sdl_context = window_init("Pattern Table", PW_WIDTH, PW_HEIGHT, PW_SCALE);
|
||||||
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,
|
window->texture = SDL_CreateTexture(window->sdl_context.renderer, SDL_PIXELFORMAT_ARGB8888,
|
||||||
SDL_TEXTUREACCESS_STATIC, PW_WIDTH, PW_HEIGHT);
|
SDL_TEXTUREACCESS_STATIC, PW_WIDTH, PW_HEIGHT);
|
||||||
|
|
|
@ -7,14 +7,12 @@
|
||||||
|
|
||||||
#include "../include/types.h"
|
#include "../include/types.h"
|
||||||
#include "window.h"
|
#include "window.h"
|
||||||
#include "pattern_display.h"
|
|
||||||
|
|
||||||
#define PW_SCALE 2
|
#define PW_SCALE 2
|
||||||
#define PW_ROW_TILE_COUNT 16
|
#define PW_ROW_TILE_COUNT 16
|
||||||
|
|
||||||
typedef struct nes_pattern_window {
|
typedef struct nes_pattern_window {
|
||||||
NesSdlContext sdl_context;
|
NesSdlContext sdl_context;
|
||||||
PatternDisplay pattern_display;
|
|
||||||
SDL_Texture *texture;
|
SDL_Texture *texture;
|
||||||
} NesPatternWindow;
|
} NesPatternWindow;
|
||||||
|
|
||||||
|
|
1
main.c
1
main.c
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
char *rom_path = "./test_roms/dk_jp.nes";
|
char *rom_path = "./test_roms/dk_jp.nes";
|
||||||
|
// char *rom_path = "./test_roms/nes-test-roms/other/BLOCKS.NES";
|
||||||
log_set_level(LOG_INFO);
|
log_set_level(LOG_INFO);
|
||||||
|
|
||||||
if (!gui_init()) {
|
if (!gui_init()) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
set(HEADERS pattern_table.h ppu.h palette.h)
|
set(HEADERS ppu.h palette.h)
|
||||||
set(SOURCE pattern_table.c ppu.c palette.c)
|
set(SOURCE ppu.c palette.c)
|
||||||
|
|
||||||
add_library(nes_ppu ${SOURCE} ${HEADERS})
|
add_library(nes_ppu ${SOURCE} ${HEADERS})
|
||||||
|
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
//
|
|
||||||
// Created by william on 17/05/24.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "pattern_table.h"
|
|
||||||
#include "ppu.h"
|
|
||||||
#include "../gui/canvas.h"
|
|
||||||
#include "../gui/gui.h"
|
|
||||||
|
|
||||||
inline void test() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
//
|
|
||||||
// Created by william on 17/05/24.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef NES_EMULATOR_PATTERN_TABLE_H
|
|
||||||
#define NES_EMULATOR_PATTERN_TABLE_H
|
|
||||||
|
|
||||||
#define PATTERN_TABLE_WIDTH 16
|
|
||||||
#define PATTERN_TILE_MEM_SIZE 16
|
|
||||||
#define PATTERN_TILE_SIZE (PATTERN_TILE_MEM_SIZE / 2)
|
|
||||||
|
|
||||||
void pt_debug();
|
|
||||||
|
|
||||||
#endif //NES_EMULATOR_PATTERN_TABLE_H
|
|
63
ppu/ppu.c
63
ppu/ppu.c
|
@ -19,7 +19,6 @@
|
||||||
#include "../include/ppu.h"
|
#include "../include/ppu.h"
|
||||||
#include "../cpu/cpu.h"
|
#include "../cpu/cpu.h"
|
||||||
#include "../include/rom.h"
|
#include "../include/rom.h"
|
||||||
#include "pattern_table.h"
|
|
||||||
|
|
||||||
#define PPU_VISIBLE_FRAME_END 240
|
#define PPU_VISIBLE_FRAME_END 240
|
||||||
#define PPU_POST_RENDER_LINE_START PPU_VISIBLE_FRAME_END
|
#define PPU_POST_RENDER_LINE_START PPU_VISIBLE_FRAME_END
|
||||||
|
@ -76,7 +75,7 @@ static inline unsigned int ppu_pixel_get_index(unsigned int scanline, unsigned i
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline byte ppu_pixel_get_mask(unsigned int tile_fine_x) {
|
static inline byte ppu_pixel_get_mask(unsigned int tile_fine_x) {
|
||||||
return 1 << (PATTERN_TILE_SIZE - tile_fine_x - 1);
|
return 1 << (8 - tile_fine_x - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void ppu_pixel_set_color(PPUPixel *pixel, byte pt_low, byte pt_high) {
|
static inline void ppu_pixel_set_color(PPUPixel *pixel, byte pt_low, byte pt_high) {
|
||||||
|
@ -110,7 +109,19 @@ static inline void ppu_pixel_set_color(PPUPixel *pixel, byte pt_low, byte pt_hig
|
||||||
void ppu_draw_tile() {
|
void ppu_draw_tile() {
|
||||||
PPUTileFetch fetch = ppu_state.tile_queue.displayed_fetch;
|
PPUTileFetch fetch = ppu_state.tile_queue.displayed_fetch;
|
||||||
|
|
||||||
unsigned int pixel_index = ppu_state.scanline * PPU_VISIBLE_FRAME_END + ppu_state.cycle;
|
unsigned int y = ppu_state.scanline;
|
||||||
|
unsigned int x = ppu_state.cycle + 0;
|
||||||
|
|
||||||
|
// if (x > PPU_LINE_WIDTH) {
|
||||||
|
// x -= PPU_LINE_WIDTH;
|
||||||
|
// y++;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (y > PPU_PRE_RENDER_LINE) {
|
||||||
|
// y -= PPU_PRE_RENDER_LINE;
|
||||||
|
// }
|
||||||
|
|
||||||
|
unsigned int pixel_index = y * PPU_VISIBLE_FRAME_END + x;
|
||||||
PPUPixel *pixel = &ppu_state.pixels[pixel_index];
|
PPUPixel *pixel = &ppu_state.pixels[pixel_index];
|
||||||
ppu_pixel_set_color(pixel, fetch.pattern_table_tile_low, fetch.pattern_table_tile_high);
|
ppu_pixel_set_color(pixel, fetch.pattern_table_tile_low, fetch.pattern_table_tile_high);
|
||||||
}
|
}
|
||||||
|
@ -121,7 +132,7 @@ byte ppu_get_pattern(byte tile_index, byte high) {
|
||||||
return ppu_read(pattern_addr);
|
return ppu_read(pattern_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ppu_fetch_tile() {
|
void ppu_fetch_tile(bool render) {
|
||||||
byte fetch_cycle = (ppu_state.cycle - 1) % 8;
|
byte fetch_cycle = (ppu_state.cycle - 1) % 8;
|
||||||
|
|
||||||
if (fetch_cycle == 1) {
|
if (fetch_cycle == 1) {
|
||||||
|
@ -151,7 +162,10 @@ void ppu_fetch_tile() {
|
||||||
} else if (fetch_cycle == 7) {
|
} else if (fetch_cycle == 7) {
|
||||||
ppu_state.fetch.pattern_table_tile_high = ppu_get_pattern(ppu_state.fetch.nametable, 1);
|
ppu_state.fetch.pattern_table_tile_high = ppu_get_pattern(ppu_state.fetch.nametable, 1);
|
||||||
ppu_state.tile_queue.displayed_fetch = ppu_state.fetch;
|
ppu_state.tile_queue.displayed_fetch = ppu_state.fetch;
|
||||||
ppu_draw_tile();
|
|
||||||
|
if (render) {
|
||||||
|
ppu_draw_tile();
|
||||||
|
}
|
||||||
|
|
||||||
if ((ppu_state.ppu_address & 0x1f) == 0x1f) {
|
if ((ppu_state.ppu_address & 0x1f) == 0x1f) {
|
||||||
ppu_state.ppu_address &= ~0x1f;
|
ppu_state.ppu_address &= ~0x1f;
|
||||||
|
@ -160,31 +174,6 @@ void ppu_fetch_tile() {
|
||||||
ppu_state.ppu_address++;
|
ppu_state.ppu_address++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// address read_addr;
|
|
||||||
// switch (fetch_cycle) {
|
|
||||||
// case 1:
|
|
||||||
// read_addr = 0x2000 + ((ppu_state.scanline / 8) * 0x20) + (ppu_state.cycle / 8);
|
|
||||||
// ppu_state.fetch.nametable = ppu_read(read_addr);
|
|
||||||
// break;
|
|
||||||
// case 3:
|
|
||||||
// read_addr = 0x23c0 + (ppu_state.cycle % 8);
|
|
||||||
// ppu_state.fetch.attribute_table = ppu_read(read_addr);
|
|
||||||
// break;
|
|
||||||
// case 5:
|
|
||||||
// read_addr = 0x1000 * ppu_read_flag(PPU_REGISTER_CTRL, PPU_CTRL_BG_PATTERN_TABLE_ADDR);
|
|
||||||
// read_addr += ppu_state.fetch.nametable * 16 + ppu_state.scanline % 8;
|
|
||||||
// ppu_state.fetch.pattern_table_tile_low = ppu_read(read_addr);
|
|
||||||
// break;
|
|
||||||
// case 7:
|
|
||||||
// read_addr = 0x1000 * ppu_read_flag(PPU_REGISTER_CTRL, PPU_CTRL_BG_PATTERN_TABLE_ADDR);
|
|
||||||
// read_addr += ppu_state.fetch.nametable * 16 + ppu_state.scanline % 8 + 8;
|
|
||||||
// ppu_state.fetch.pattern_table_tile_high = ppu_read(read_addr);
|
|
||||||
// ppu_tile_push();
|
|
||||||
// break;
|
|
||||||
// default:
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ppu_visible_frame(unsigned int cycle) {
|
void ppu_visible_frame(unsigned int cycle) {
|
||||||
|
@ -195,8 +184,8 @@ void ppu_visible_frame(unsigned int cycle) {
|
||||||
|
|
||||||
if (cycle == 0) {
|
if (cycle == 0) {
|
||||||
// Idle...
|
// Idle...
|
||||||
} else if (cycle <= 256) {
|
} else if (cycle >= 8 && cycle <= 256) {
|
||||||
ppu_fetch_tile();
|
ppu_fetch_tile(true);
|
||||||
|
|
||||||
if (cycle == 256) {
|
if (cycle == 256) {
|
||||||
if ((ppu_state.ppu_address & 0x7000) != 0x7000) {
|
if ((ppu_state.ppu_address & 0x7000) != 0x7000) {
|
||||||
|
@ -208,7 +197,7 @@ void ppu_visible_frame(unsigned int cycle) {
|
||||||
ppu_state.ppu_address += 0x20;
|
ppu_state.ppu_address += 0x20;
|
||||||
} else {
|
} else {
|
||||||
ppu_state.ppu_address &= ~0x3e0;
|
ppu_state.ppu_address &= ~0x3e0;
|
||||||
// ppu_state.ppu_address ^= 0x0800;
|
ppu_state.ppu_address ^= 0x0800;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -220,8 +209,8 @@ void ppu_visible_frame(unsigned int cycle) {
|
||||||
ppu_state.ppu_address = (ppu_state.ppu_address & 0xfbe0) | (ppu_state.temp_ppu_addr & ~0xfbe0);
|
ppu_state.ppu_address = (ppu_state.ppu_address & 0xfbe0) | (ppu_state.temp_ppu_addr & ~0xfbe0);
|
||||||
ppu_state.x_scroll = 0;
|
ppu_state.x_scroll = 0;
|
||||||
}
|
}
|
||||||
} else if (cycle <= 336) {
|
} else if (cycle <= 328) {
|
||||||
ppu_fetch_tile();
|
ppu_fetch_tile(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,8 +251,8 @@ void ppu_cycle() {
|
||||||
if (rendering_enabled && ppu_state.odd_frame) {
|
if (rendering_enabled && ppu_state.odd_frame) {
|
||||||
// With rendering enabled, the odd frames are shorter
|
// With rendering enabled, the odd frames are shorter
|
||||||
// TODO: and doing the last cycle of the last dummy nametable fetch there instead
|
// TODO: and doing the last cycle of the last dummy nametable fetch there instead
|
||||||
frame_width = PPU_LINE_WIDTH - 2;
|
// frame_width = PPU_LINE_WIDTH - 2;
|
||||||
frame_height = PPU_LINE_END - 1;
|
// frame_height = PPU_LINE_END - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ppu_state.cycle++;
|
ppu_state.cycle++;
|
||||||
|
|
5
system.c
5
system.c
|
@ -5,10 +5,7 @@
|
||||||
#include "include/cpu.h"
|
#include "include/cpu.h"
|
||||||
#include "include/system.h"
|
#include "include/system.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include <unistd.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "pattern_table.h"
|
|
||||||
|
|
||||||
System current_sys;
|
System current_sys;
|
||||||
|
|
||||||
|
@ -33,8 +30,6 @@ void system_next_frame() {
|
||||||
ppu_cycle();
|
ppu_cycle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pt_debug();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void system_uninit() {
|
void system_uninit() {
|
||||||
|
|
Loading…
Reference in New Issue