#define GL_SILENCE_DEPRECATION #define GLFW_INCLUDE_NONE #include #include #include #include #include #include #include #define printf(...) #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 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 #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" static uint32_t frames; // debug information // #include "smb_tas.h" // REMOVE ME // NES core #include "mappers/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" // struct nes_state nstate; int main(int argc, char **argv) { #ifdef _WIN32 timeBeginPeriod(1); #endif state.toggle_crt_emulation = 1; setbuf(stdout, 0); init_opcode_lut(); init_opcode_ud_lut(); // protect_opcode_lut(); struct nes_state *nstate = aligned_alloc(4096, (sizeof(struct nes_state) + 4095) & ~4096); 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/Super Mario Bros. (W) (V1.0) [!].nes"); // ines2_load(&nstate, "data/Super Mario Bros. (JU) [!].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/Life Force (USA).zip"); // 2002 // 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"); // ines2_load(&nstate, "data/Blaster Master (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; #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++; } return 0; #else 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); // 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); 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); while(!glfwWindowShouldClose(window)) { timer_wait(timer); glfwPollEvents(); 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); } printf("total frames: %6.6d total cycles: %ld\n", frames, nstate->cpu.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); #endif #ifdef _WIN32 timeEndPeriod(1); #endif return 0; }