#define GL_SILENCE_DEPRECATION #define _GNU_SOURCE #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 #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 (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 static uint32_t buffer[BUFFER_WIDTH * BUFFER_HEIGHT] __attribute__((section(".bss"), aligned(4096))); static 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(fmt, ...) printf(fmt, ##__VA_ARGS__) #ifdef _WIN32 #include "win32_timer.c" #else #include "linux_timer.c" #endif // #include "audio.c" #include "incbin.h" #ifdef BENCHMARK // Embed the ROM for benchmarking to eliminate file I/O overhead // Uncomment the ROM you want to benchmark: // INCBIN_BYTES(benchmark_rom, "data/Life Force (USA).nes"); INCBIN_BYTES(benchmark_rom, "data/0000/Super Mario Bros. (World) (HVC-SM).nes"); // INCBIN_BYTES(benchmark_rom, "data/0003/Gradius (USA).nes"); #endif #include "platform_gl_loader.c" struct main_state { int32_t filter_override; float filter_frequency; uint32_t screen_width; uint32_t screen_height; float contrast; float saturation; float brightness; float tone_data[4]; // 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; struct { int32_t x; int32_t y; int32_t w; int32_t h; } viewport; 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; uint8_t toggle_bloom; uint8_t fullscreen; uint8_t viewport_changed; }; struct main_state state __attribute__((aligned(64))); #include "mkfw.h" #include "platform_opengl.c" static size_t sprite_counts[8]; static uint32_t frames; // debug information // static int32_t tas_frame_count; // #include "smb_tas.h" // REMOVE ME // NES core #include "mknes_mapper.h" #include "mknes.h" #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" #ifdef BENCHMARK #include "mknes_bench.c" #endif // struct nes_state nstate; static void framebuffer_callback(struct mkfw_state *mkfw_window, 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_aspectkeyboard_state[MKS_KEY_X]) { input |= (1 << 0); } if(window->keyboard_state[MKS_KEY_Z]) { input |= (1 << 1); } if(window->keyboard_state[MKS_KEY_SPACE]) { input |= (1 << 2); } if(window->keyboard_state[MKS_KEY_RETURN]) { input |= (1 << 3); } if(window->keyboard_state[MKS_KEY_UP]) { input |= (1 << 4); } if(window->keyboard_state[MKS_KEY_DOWN]) { input |= (1 << 5); } if(window->keyboard_state[MKS_KEY_LEFT]) { input |= (1 << 6); } if(window->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; } 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; } mkfw_update_keyboard_state(window); mkfw_update_modifier_state(window); mkfw_update_mouse_state(window); // Render and swap buffers render_frame(); mkfw_swap_buffers(window); 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(window); return 0; #endif }