#define GL_SILENCE_DEPRECATION #define GLFW_INCLUDE_NONE #include #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 LIKELY(x) __builtin_expect(!!(x), 1) #define UNLIKELY(x) __builtin_expect(!!(x), 0) #ifndef restrict # if defined(__cplusplus) # define restrict __restrict # endif #endif struct main_state { int32_t filter_override; float filter_frequency; 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; uint32_t vao; uint32_t vbo; uint32_t ebo; struct { int32_t x; int32_t y; int32_t w; int32_t h; } viewport; uint8_t fullscreen; uint8_t toggle_crt_emulation; }; struct main_state state; 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; #define DEBUG_PRINT printf #include "timer.c" // #include "audio.c" #include "opengl_loader.c" #include "opengl.c" #include "render.c" // NES core #include "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 "mapper.c" #include "callbacks.c" struct nes_state nstate; static uint32_t frames; #define PRG_ROM_SIZE (512 * 1024) #define CHR_ROM_SIZE (512 * 1024) #define PIXELS_SIZE (256 * 240) #define RAM_SIZE 0x800 #define SRAM_SIZE 0x2000 #define CIRAM_SIZE 0x1000 #define CHR_RAM_SIZE 0x4000 static struct nes_state *allocate_nes_state(void) { struct nes_state *state = (struct nes_state*)calloc(1, sizeof(struct nes_state)); if(!state) return 0; size_t total_size = (PRG_ROM_SIZE + CHR_ROM_SIZE + PIXELS_SIZE + RAM_SIZE + SRAM_SIZE + CIRAM_SIZE + CHR_RAM_SIZE+ 4095) & ~0xfff; uint8_t *m = (uint8_t*)aligned_alloc(4096, total_size); memset(m, 0, total_size); size_t offset = 0; state->prg_rom = m + offset; offset += PRG_ROM_SIZE; // DO NOT MOVE THIS, as this is the pointer we free later. state->chr_rom = m + offset; offset += CHR_ROM_SIZE; state->pixels = m + offset; offset += PIXELS_SIZE; state->ram = m + offset; offset += RAM_SIZE; state->sram = m + offset; offset += SRAM_SIZE; state->ciram = m + offset; offset += CIRAM_SIZE; state->chr_ram = m + offset; offset += CHR_RAM_SIZE; return state; } static void free_nes_state(struct nes_state *s) { free(s->prg_rom); free(s); } int main(int argc, char **argv) { #ifdef _WIN32 timeBeginPeriod(1); #endif state.toggle_crt_emulation = 1; struct nes_state *nstate = allocate_nes_state(); setbuf(stdout, 0); init_opcode_lut(); init_opcode_ud_lut(); // protect_opcode_lut(); ppu_reset(nstate); // ines2_load(nstate, "data/0000/10-Yard Fight (USA, Europe).nes"); // ines2_load(nstate, "data/0000/Balloon Fight (USA).nes"); // ines2_load(nstate, "data/0000/Excitebike (Japan, USA).nes"); // ines2_load(nstate, "data/0000/Ice Climber (USA, Europe, Korea).nes"); // ines2_load(nstate, "data/0000/Kung Fu (Japan, USA).nes"); ines2_load(nstate, "data/0000/Super Mario Bros. (World) (HVC-SM).nes"); // ines2_load(nstate, "data/0000/Urban Champion (World).nes"); // ines2_load(nstate, "data/0000/Wrecking Crew (World).nes"); // ines2_load(nstate, "data/0000/scanline.nes"); // ines2_load(nstate, "data/0000/Sayoonara!.NES"); // ines2_load(nstate, "data/0000/raster_demos/RasterChromaLuma.NES"); // ines2_load(nstate, "data/0000/raster_demos/RasterTest1.NES"); // ines2_load(nstate, "data/0000/raster_demos/RasterTest2.NES"); // ines2_load(nstate, "data/0000/raster_demos/RasterTest3.NES"); // ines2_load(nstate, "data/0000/raster_demos/RasterTest3a.NES"); // ines2_load(nstate, "data/0000/raster_demos/RasterTest3b.NES"); // ines2_load(nstate, "data/0000/raster_demos/RasterTest3c.NES"); // ines2_load(nstate, "data/0000/raster_demos/RasterTest3d.NES"); // ines2_load(nstate, "data/0000/raster_demos/RasterTest3e.NES"); // ines2_load(nstate, "data/0000/NEStress.NES"); // ines2_load(nstate, "data/0000/Super Mario Bros. (World) (HVC-SM).zip"); // ines2_load(nstate, "data/0042/Super Mario Bros. + Duck Hunt (USA).zip"); // ines2_load(nstate, "data/0000/Xevious - The Avenger (USA).zip"); // ines2_load(nstate, "data/tv.nes"); // ines2_load(nstate, "data/0003/Flipull - An Exciting Cube Game (Japan) (En).zip"); // ines2_load(nstate, "data/0003/Friday the 13th (USA).zip"); // ines2_load(nstate, "data/0003/Ghostbusters (Japan).zip"); // ines2_load(nstate, "data/0003/Gradius (USA).zip"); // ines2_load(nstate, "data/0007/Battletoads (USA).zip"); // ines2_load(nstate, "data/0007/Beetlejuice (USA).zip"); // ines2_load(nstate, "data/0007/Cabal (USA).zip"); // ines2_load(nstate, "data/000b/Baby Boomer (USA) (Unl).zip"); // ines2_load(nstate, "data/000b/Captain Comic - The Adventure (USA) (Unl).zip"); // ines2_load(nstate, "data/000b/King Neptune's Adventure (USA) (Unl).zip"); // ines2_load(nstate, "data/2002/Attack Animal Gakuen (Japan).zip"); // ines2_load(nstate, "data/2002/Ballblazer (Japan).zip"); // ines2_load(nstate, "data/2002/Best of the Best - Championship Karate (USA).zip"); mapper_setup(nstate); uint32_t lo = nstate->mapper.prg_read(nstate, 0xfffc); uint32_t hi = nstate->mapper.prg_read(nstate, 0xfffd); nstate->cpu.pc = (hi << 8) | lo; struct timer_handle *timer = timer_new(FRAME_INTERVAL_NS); if(!timer) { fprintf(stderr, "Failed to create timer\n"); return 1; } if(glfwInit()) { glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE); window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "NES Emulator", 0, 0); if(window) { glfwSetWindowUserPointer(window, (void*)nstate); glfwSetWindowAspectRatio(window, 320, 240); glfwSetWindowSizeLimits(window, 320*3, 240*3, GLFW_DONT_CARE, GLFW_DONT_CARE); glfwMakeContextCurrent(window); opengl_setup(); glfwSetKeyCallback(window, key_callback); glfwSetFramebufferSizeCallback(window, framebuffer_callback); glfwSwapInterval(0); framebuffer_callback(window, WINDOW_WIDTH, WINDOW_HEIGHT); 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; } } set_decay(20); timer_start(timer); #if 1 for(uint32_t i = 0; i < 0x5000; ++ i) { while(!nstate->ppu.frame_ready) { // PROFILE_NAMED("nes emulator"); cpu_tick(nstate); } nstate->ppu.frame_ready = 0; frames++; } #else while(!glfwWindowShouldClose(window)) { // timer_wait(timer); glfwPollEvents(); 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); } #endif printf("total frames: %6.6d total cycles: %ld\n", frames, nstate->cycles); glfwDestroyWindow(window); } else { fprintf(stderr, "Failed to create window\n"); } glfwTerminate(); } else { fprintf(stderr, "Failed to initialize GLFW\n"); } timer_destroy(timer); free_nes_state(nstate); #ifdef _WIN32 timeEndPeriod(1); #endif return 0; }