summaryrefslogtreecommitdiff
path: root/mknes.c
diff options
context:
space:
mode:
Diffstat (limited to 'mknes.c')
-rw-r--r--mknes.c391
1 files changed, 244 insertions, 147 deletions
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 <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;
}