From 030724a9aea346e4a9843d5842fb28c6d6c4cf1a Mon Sep 17 00:00:00 2001 From: Peter Fors Date: Thu, 9 Oct 2025 22:07:52 +0200 Subject: Rearrangement and refactoring and optimizations and more accuracy --- mknes.c | 391 ++++++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 244 insertions(+), 147 deletions(-) (limited to 'mknes.c') diff --git a/mknes.c b/mknes.c index 154ca95..37225ff 100644 --- a/mknes.c +++ b/mknes.c @@ -1,35 +1,61 @@ #define GL_SILENCE_DEPRECATION -#define GLFW_INCLUDE_NONE -#include + +#ifdef _WIN32 +#define NOMINMAX +#undef NOCRYPT +// For mingw-w64, use _aligned_malloc instead of aligned_alloc +#define aligned_alloc(align, size) _aligned_malloc(size, align) +#define aligned_free(ptr) _aligned_free(ptr) +#else +#include +#include /* For mode constants */ +#include /* For O_* constants */ +#define aligned_free(ptr) free(ptr) +#endif + #include +#include #include #include #include #include -#include -#include -#include /* For mode constants */ -#include /* For O_* constants */ + #define BUFFER_WIDTH 256 #define BUFFER_HEIGHT 240 #define WINDOW_WIDTH 320 * 3 #define WINDOW_HEIGHT 240 * 3 -#define PRG_ROM_SIZE (2 * 1024 * 1024) -#define CHR_ROM_SIZE (1 * 1024 * 1024) +// #define PRG_ROM_SIZE (2 * 1024 * 1024) +// #define CHR_ROM_SIZE (1 * 1024 * 1024) +#define PRG_ROM_SIZE (512 * 1024) +#define CHR_ROM_SIZE (256 * 1024) + #define PIXELS_SIZE (256 * 240) #define RAM_SIZE 0x1000 // 0x800 in reality, but for aligned alloc it must be the size of the alignment (4096) #define SRAM_SIZE 0x2000 #define CIRAM_SIZE 0x1000 #define CHR_RAM_SIZE 0x4000 -#ifndef restrict -# if defined(__cplusplus) -# define restrict __restrict -# endif +uint32_t buffer[BUFFER_WIDTH * BUFFER_HEIGHT] __attribute__((section(".bss"), aligned(4096))); +uint32_t display_buffer[BUFFER_WIDTH * BUFFER_HEIGHT] __attribute__((section(".bss"), aligned(4096))); + +static void audio_callback(int16_t *data, size_t frames) { } + +#define FRAME_INTERVAL_NS (1000000000ULL / 60.0988) + +#define DEBUG_PRINT printf +#ifdef _WIN32 +#include "win32_timer.c" +#else +#include "linux_timer.c" #endif +// #include "audio.c" +#include "incbin.h" + +#include "platform_gl_loader.c" + struct main_state { int32_t filter_override; @@ -38,26 +64,68 @@ struct main_state { uint32_t screen_width; uint32_t screen_height; - uint32_t texture; - uint32_t render_width; - uint32_t render_height; - uint32_t shader_program; float contrast; float saturation; float brightness; float tone_data[4]; - int32_t uniform_resolution; - int32_t uniform_src_image_size; - int32_t uniform_brightness; - int32_t uniform_tone; - int32_t uniform_crt_emulation; - int32_t uniform_sampler_location; + // OpenGL Objects + GLuint shader_program; + GLuint persistence_program; + // GLuint upscale_program; + GLuint upscale_warp_program; + GLuint bloom_extract_program; + GLuint bloom_blur_program; + GLuint bloom_warp_program; + GLuint bloom_composite_program; + + GLuint texture; + GLuint persistence_texture; + GLuint persistence_output_texture; + GLuint crt_output_texture; + GLuint bloom_texture; + GLuint bloom_temp_texture; + GLuint bloom_warped_texture; + GLuint upscaled_source_texture; + + GLuint persistence_fbo; + GLuint upscaled_source_fbo; + GLuint crt_fbo; + GLuint bloom_fbo; + GLuint bloom_temp_fbo; + GLuint bloom_warp_fbo; + + GLuint vao; + GLuint vbo; + GLuint ebo; + + // CRT Shader Uniforms + GLuint uniform_resolution; + GLuint uniform_src_image_size; + GLuint uniform_brightness; + GLuint uniform_tone; + GLuint uniform_crt_emulation; + GLuint uniform_apply_mask; + GLuint uniform_sampler_location; + + // Bloom Shader Uniforms + GLuint bloom_uniform_threshold; + GLuint bloom_uniform_sampler; + GLuint blur_uniform_horizontal; + GLuint blur_uniform_sampler; + GLuint composite_uniform_bloom_strength; + GLuint composite_uniform_crt_sampler; + GLuint composite_uniform_bloom_sampler; + + // Bloom settings + float bloom_threshold; + float bloom_strength; + uint32_t bloom_width; + uint32_t bloom_height; + // Phosphor persistence + float persistence_decay; - uint32_t vao; - uint32_t vbo; - uint32_t ebo; struct { int32_t x; @@ -66,69 +134,106 @@ struct main_state { int32_t h; } viewport; - uint8_t fullscreen; + uint32_t render_width; // The actual remake resolution (e.g., 360) + uint32_t render_height; // The actual remake resolution (e.g., 270) + uint32_t frame_number; + uint8_t running; uint8_t toggle_crt_emulation; -}; - -struct main_state state __attribute__((aligned(64))); + uint8_t toggle_bloom; + uint8_t fullscreen; + uint8_t viewport_changed; -uint32_t buffer[BUFFER_WIDTH * BUFFER_HEIGHT] __attribute__((section(".bss"), aligned(4096))); -uint32_t display_buffer[BUFFER_WIDTH * BUFFER_HEIGHT] __attribute__((section(".bss"), aligned(4096))); +}; -static void audio_callback(int16_t *data, size_t frames) { } -#define FRAME_INTERVAL_NS (1000000000ULL / 60.0988) -static GLFWwindow *window; +struct main_state state __attribute__((aligned(64))); -#define DEBUG_PRINT printf -#include "timer.c" -// #include "audio.c" +#include "mkfw.h" +#include "platform_opengl.c" -#include "opengl_loader.c" -#include "opengl.c" -#include "render.c" static uint32_t frames; // debug information +// static int32_t tas_frame_count; + // #include "smb_tas.h" // REMOVE ME + // NES core -#include "mappers/mapper.h" +#include "mknes_mapper.h" #include "mknes.h" -#include "apu.c" -#include "ppu.c" -#include "ppu_registers.c" -#include "memory.c" -#include "cpu.c" -#include "ines2.c" -#include "mappers/mapper.c" - -#include "callbacks.c" +#include "mknes_apu.c" +#include "mknes_ppu.c" +#include "mknes_ppu_registers.c" +#include "mknes_memory.c" +#include "mknes_cpu.c" +#include "mknes_ines2.c" +#include "mknes_mapper.c" // struct nes_state nstate; +static void framebuffer_callback(int32_t width, int32_t height, float aspect_ratio) { + // state.screen_width = width; + // state.screen_height = height; + // state.viewport.x = 0; + // state.viewport.y = 0; + // state.viewport.w = width; + // state.viewport.h = height; + + // float current_aspect = (float)width / (float)height; + + // if(current_aspect > aspect_ratio) { // Window is wider than the desired aspect ratio + // float new_width = height * aspect_ratio; // Compute new width based on the height and the desired aspect ratio + // state.viewport.x = (width - new_width) / 2; + // state.viewport.w = new_width; + // } else if(current_aspect < aspect_ratio) { // Window is taller than the desired aspect ratio + // float new_height = width / aspect_ratio; // Compute new height based on the width and the desired aspect ratio + // state.viewport.y = (height - new_height) / 2; + // state.viewport.h = new_height; + // } + + int32_t viewport_x = 0; + int32_t viewport_y = 0; + int32_t viewport_width = width; + int32_t viewport_height = height; + + + double target_aspect = (aspect_ratio!=0.f) ? (double)aspect_ratio : ((double)width / (double)height); + double current_aspect = (double)width / (double)height; + + if(current_aspect>target_aspect) { + int32_t new_width = (int32_t)((double)height * target_aspect + 0.5); + viewport_x = (width - new_width) / 2; + viewport_width = new_width; + } else if(current_aspectmapper_function.prg_rom_read(nstate, 0xfffc); - uint32_t hi = nstate->mapper_function.prg_rom_read(nstate, 0xfffd); - nstate->cpu.pc = (hi << 8) | lo; + cpu_reset(nstate); #if 1 for(uint32_t i = 0; i < 0x5000; ++i) { @@ -192,107 +300,96 @@ int main(int argc, char **argv) { nstate->ppu.frame_ready = 0; frames++; } + // for(size_t i = 0; i < 256; ++i) { + // printf("instr %2.2x: %lld\n", i, instr_count[i]); + // } return 0; #else - struct timer_handle *timer = timer_new(FRAME_INTERVAL_NS); - if(!timer) { - fprintf(stderr, "Failed to create timer\n"); - return 1; - } +// WINDOW SETUP + mkfw_init(WINDOW_WIDTH, WINDOW_HEIGHT); + mkfw_set_window_title("mknes"); + mkfw_set_framebuffer_size_callback(framebuffer_callback); + mkfw_set_swapinterval(0); - if(glfwInit()) { + gl_loader(); + opengl_setup(); + // setup_render_targets(); + change_resolution(BUFFER_WIDTH, BUFFER_HEIGHT); + mkfw_show_window(); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE); + mkfw_set_window_min_size_and_aspect(WINDOW_WIDTH, WINDOW_HEIGHT, 4.f, 3.f); - window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "NES Emulator", 0, 0); - if(window) { - glfwSetWindowUserPointer(window, (void*)nstate); - glfwSetWindowAspectRatio(window, 320, 240); // Need to set a 4:3 resolution for things to look correct! - glfwSetWindowSizeLimits(window, WINDOW_WIDTH, WINDOW_HEIGHT, GLFW_DONT_CARE, GLFW_DONT_CARE); - glfwSetInputMode(window, GLFW_STICKY_KEYS, GLFW_TRUE); + timer_init(); + struct timer_handle *timer = timer_new(FRAME_INTERVAL_NS); - glfwMakeContextCurrent(window); - opengl_setup(); - glfwSetKeyCallback(window, key_callback); - glfwSetFramebufferSizeCallback(window, framebuffer_callback); - glfwSwapInterval(0); + uint8_t running = true; - framebuffer_callback(window, WINDOW_WIDTH, WINDOW_HEIGHT); + while(running && !mkfw_should_close()) { + mkfw_pump_messages(); - for(int jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) { - if(glfwJoystickPresent(jid)) { - const char *name = glfwGetJoystickName(jid); - printf("Joystick %d detected: %s\n", jid, name); - break; - } - } + // // Check for ESC key + if(mkfw_is_key_pressed(MKS_KEY_ESCAPE)) running = false; - set_decay(20); - - timer_start(timer); - - for(;;) { - timer_wait(timer); - glfwPollEvents(); - - // exit - if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) break; - - // Joypad input - uint8_t input = 0; - if(glfwGetKey(window, GLFW_KEY_X) == GLFW_PRESS) { input |= (1 << 0); } - if(glfwGetKey(window, GLFW_KEY_Z) == GLFW_PRESS) { input |= (1 << 1); } - if(glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS) { input |= (1 << 2); } - if(glfwGetKey(window, GLFW_KEY_ENTER) == GLFW_PRESS) { input |= (1 << 3); } - if(glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS) { input |= (1 << 4); } - if(glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS) { input |= (1 << 5); } - if(glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS) { input |= (1 << 6); } - if(glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS) { input |= (1 << 7); } - nstate->ppu.input[0] = input; - - while(!nstate->ppu.frame_ready) { - // PROFILE_NAMED("nes emulator"); - cpu_tick(nstate); - } - nstate->ppu.frame_ready = 0; - frames++; - - uint32_t * restrict dst = display_buffer; //buffer; - uint8_t * restrict src = nstate->pixels; - for(uint32_t y = 0; y < 240; ++y) { - for(uint32_t x = 0; x < 256; ++x) { - uint8_t val = *src++; - if(val >= 64) val = 0; - dst[x] = nes_palette[val]; - } - dst += BUFFER_WIDTH; - } - // apply_phosphor_decay(); - render_frame(); - glfwSwapBuffers(window); + // Joypad input + uint8_t input = 0; + if(keyboard_state[MKS_KEY_X]) { input |= (1 << 0); } + if(keyboard_state[MKS_KEY_Z]) { input |= (1 << 1); } + if(keyboard_state[MKS_KEY_SPACE]) { input |= (1 << 2); } + if(keyboard_state[MKS_KEY_RETURN]) { input |= (1 << 3); } + if(keyboard_state[MKS_KEY_UP]) { input |= (1 << 4); } + if(keyboard_state[MKS_KEY_DOWN]) { input |= (1 << 5); } + if(keyboard_state[MKS_KEY_LEFT]) { input |= (1 << 6); } + if(keyboard_state[MKS_KEY_RIGHT]) { input |= (1 << 7); } + nstate->ppu.input[0] = input; + + // Run NES emulation for one frame + while(!nstate->ppu.frame_ready) { + cpu_tick(nstate); + } + nstate->ppu.frame_ready = 0; + if(nstate->ppu.open_bus > 0) { + int32_t v = nstate->ppu.open_bus; + v -= 5; + if(v < 0) { + v = 0; } + nstate->ppu.open_bus = v; + } - printf("total frames: %6.6d total cycles: %ld\n", frames, nstate->cpu.cycles); - glfwDestroyWindow(window); - } else { - fprintf(stderr, "Failed to create window\n"); + frames++; + + // Convert NES pixels to display buffer + uint32_t * restrict dst = display_buffer; + uint8_t * restrict src = nstate->pixels; + for(uint32_t y = 0; y < 240; ++y) { + for(uint32_t x = 0; x < 256; ++x) { + uint8_t val = *src++; + if(val >= 64) val = 0; + dst[x] = nes_palette[val]; + } + dst += BUFFER_WIDTH; } - glfwTerminate(); - } else { - fprintf(stderr, "Failed to initialize GLFW\n"); + mkfw_update_keyboard_state(); + mkfw_update_modifier_state(); + mkfw_update_mouse_state(); + + // Render and swap buffers + render_frame(); + mkfw_swap_buffers(); + timer_wait(timer); } + + printf("total frames: %6d total cycles: %12llu\n", frames, (unsigned long long)nstate->cpu.cycles); + timer_destroy(timer); // free_nes_state(&nstate); + timer_shutdown(); + mkfw_cleanup(); #endif -#ifdef _WIN32 - timeEndPeriod(1); -#endif return 0; } -- cgit v1.2.3