#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 #include static size_t state_dump_count; static FILE *state_dump_file; #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 uint32_t frames; // debug information static int32_t tas_frame; // #include "helpers/battletoads_tas.h" // REMOVE ME #include "helpers/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 dump_state(struct nes_state *state) { size_t state_size = offsetof(struct nes_state, ram); if(!state_dump_file) { state_dump_file = fopen("state_dump.bin", "wb"); if(!state_dump_file) { fprintf(stderr, "Failed to open state_dump.bin for writing\n"); return; } } fwrite(state, 1, state_size, state_dump_file); state_dump_count++; } 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_aspectciram + n * 960; for(size_t i = 0; i < 30; ++i, d += 32) { for(size_t j = 0; j < 32; ++j) { uint8_t tile = d[j]; if(tile == 0x00) printf(" "); else if(tile == 0xff) printf("##"); else printf("%02d", tile % 100); } printf("\n"); } } // Joypad input uint8_t input = 0; if(window->keyboard_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; // nstate->ppu.input[0] = tas_input[tas_frame++]; // 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++; // Dump state every frame starting from 2400 // if(frames >= 2400 && frames <= 3100) { // dump_state(nstate); // } // 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); if(!window->keyboard_state[MKS_KEY_F]) { timer_wait(timer); } } // printf("total frames: %6d total cycles: %12llu\n", frames, (unsigned long long)nstate->cpu.cycles); // printf("state dumps created: %zu\n", state_dump_count); if(state_dump_file) { fclose(state_dump_file); } timer_destroy(timer); munmap(nstate, nstate_size); timer_shutdown(); mkfw_cleanup(window); return 0; #endif }