diff options
Diffstat (limited to 'mknes.c')
| -rw-r--r-- | mknes.c | 391 |
1 files changed, 244 insertions, 147 deletions
@@ -1,35 +1,61 @@ #define GL_SILENCE_DEPRECATION -#define GLFW_INCLUDE_NONE -#include <GLFW/glfw3.h> + +#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 <sys/mman.h> +#include <sys/stat.h> /* For mode constants */ +#include <fcntl.h> /* For O_* constants */ +#define aligned_free(ptr) free(ptr) +#endif + #include <stdio.h> +#include <stdlib.h> #include <stdint.h> #include <stdbool.h> #include <math.h> #include <string.h> -#include <immintrin.h> -#include <sys/mman.h> -#include <sys/stat.h> /* For mode constants */ -#include <fcntl.h> /* 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_aspect<target_aspect) { + int32_t new_height = (int32_t)((double)width / target_aspect + 0.5); + viewport_y = (height - new_height) / 2; + viewport_height = new_height; + } else { + } + + viewport_x &= ~1; + viewport_y &= ~1; + + state.viewport.x = viewport_x; + state.viewport.y = viewport_y; + state.viewport.w = viewport_width; + state.viewport.h = viewport_height; + + state.viewport_changed = 1; +} + int main(int argc, char **argv) { -#ifdef _WIN32 - timeBeginPeriod(1); -#endif state.toggle_crt_emulation = 1; - + state.toggle_bloom = 1; setbuf(stdout, 0); init_opcode_lut(); init_opcode_ud_lut(); // protect_opcode_lut(); -#if DEBUG_SHARED_MEM - // int fd = shm_open("/mknes_dbg", O_CREAT | O_RDWR, 0666); - // ftruncate(fd, sizeof(struct nes_state)); - // struct nes_state *nstate = mmap(0, sizeof(struct nes_state), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - // close(fd); -#else - // struct nes_state *nstate = mmap(0, sizeof(struct nes_state), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); struct nes_state *nstate = aligned_alloc(4096, (sizeof(struct nes_state) + 4095) & ~4095); memset(nstate, 0, sizeof(struct nes_state)); -#endif ppu_reset(nstate); // ines2_load(nstate, "data/0000/10-Yard Fight (USA, Europe).nes"); @@ -176,12 +281,15 @@ int main(int argc, char **argv) { // ines2_load(nstate, "data/2002/Ballblazer (Japan).zip"); // ines2_load(nstate, "data/2002/Best of the Best - Championship Karate (USA).zip"); - // ines2_load(nstate, "data/Blaster Master (USA).zip"); + // ines2_load(nstate, "data/0001/Kid Icarus (UE) (V1.1) [!].nes"); + // ines2_load(nstate, "data/0001/Metroid (U) [!].nes"); + // ines2_load(nstate, "data/0001/Legend of Zelda, The (U) (V1.1) [!].nes"); + + // ines2_load(nstate, "data/Blaster Master (USA).zip"); // mapper 1 + // ines2_load(nstate, "AccuracyCoin.nes"); // mapper 1 mapper_setup(nstate); - uint32_t lo = nstate->mapper_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; } |
