summaryrefslogtreecommitdiff
path: root/mknes.c
diff options
context:
space:
mode:
authorPeter Fors <peter.fors@mindkiller.com>2025-04-03 20:02:00 +0200
committerPeter Fors <peter.fors@mindkiller.com>2025-04-03 20:02:00 +0200
commit6274071e3857c1640cc5aef804cb86509ab312f9 (patch)
tree1a4e56b3c3b4bfb4d8f0d2f588487d6e227c3b27 /mknes.c
parent971e51eebbf088f1ac590da1fc57e803eb1ee8cf (diff)
Move to glfw
Diffstat (limited to 'mknes.c')
-rw-r--r--mknes.c290
1 files changed, 166 insertions, 124 deletions
diff --git a/mknes.c b/mknes.c
index d94c868..54da663 100644
--- a/mknes.c
+++ b/mknes.c
@@ -1,6 +1,85 @@
-#include <assert.h>
+#define GL_SILENCE_DEPRECATION
+#define GLFW_INCLUDE_NONE
+#include <GLFW/glfw3.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <math.h>
+#include <string.h>
+#include <immintrin.h>
+
+#define BUFFER_WIDTH 256
+#define BUFFER_HEIGHT 240
+#define WINDOW_WIDTH 320 * 3
+#define WINDOW_HEIGHT 240 * 3
+
+#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)));
+
+void audio_callback(int16_t *data, size_t frames) {
-#include "base.c"
+}
+
+
+#define FRAME_INTERVAL_NS (1000000000ULL / 60.0988)
+
+static GLFWwindow *window;
+
+#define DEBUG_PRINT printf
+#include "timer.c"
+#include "opengl_loader.c"
+#include "opengl.c"
+#include "render.c"
+#include "audio.c"
+#include "callbacks.c"
#include "mapper.h"
@@ -12,65 +91,25 @@
#include "ines2.c"
#include "mapper.c"
-
-
struct nes_state nstate;
static uint32_t frames;
-static void render_callback(void) {
- clear_buffer();
- while(!nstate.ppu.frame_ready) {
- PROFILE_NAMED("nes emulator");
- cpu_tick(&nstate);
- }
- nstate.ppu.frame_ready = 0;
- frames++;
-
- uint32_t *dst = RENDER_START(0,0);
- uint8_t *src = nstate.ppu.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;
- }
-}
-
-static void shutdown_callback() {
- printf("%d\n", frames);
-}
-
-static void audio_callback(int16_t *buffer, size_t frames) {
-
-}
-
-#include <sys/mman.h>
-#include <unistd.h>
-
-void protect_opcode_lut(void) {
- uintptr_t addr = (uintptr_t)opcode_lut;
- size_t page_size = getpagesize();
- uintptr_t page = addr & ~(page_size - 1);
-
- if(mprotect((void*)page, page_size, PROT_READ) != 0) {
- perror("mprotect");
- abort();
- }
-}
+int main(int argc, char **argv) {
+#ifdef _WIN32
+ timeBeginPeriod(1);
+#endif
-static void init_callback(void) {
+ state.toggle_crt_emulation = 1;
setbuf(stdout, 0);
init_opcode_lut();
init_opcode_ud_lut();
// protect_opcode_lut();
ppu_reset(&nstate);
// ines2_load(&nstate, "data/nrom/10-Yard Fight (USA, Europe).nes");
- // ines2_load(&nstate, "data/nrom/Balloon Fight (USA).nes");
- ines2_load(&nstate, "data/nrom/Excitebike (Japan, USA).nes");
+ ines2_load(&nstate, "data/nrom/Balloon Fight (USA).nes");
+ // ines2_load(&nstate, "data/nrom/Excitebike (Japan, USA).nes");
// ines2_load(&nstate, "data/nrom/Ice Climber (USA, Europe, Korea).nes");
// ines2_load(&nstate, "data/nrom/Kung Fu (Japan, USA).nes");
// ines2_load(&nstate, "data/nrom/Super Mario Bros. (World) (HVC-SM).nes");
@@ -93,82 +132,85 @@ static void init_callback(void) {
uint32_t lo = nstate.mapper.read(&nstate, 0xfffc);
uint32_t hi = nstate.mapper.read(&nstate, 0xfffd);
nstate.cpu.pc = (hi << 8) | lo;
-}
-
-// int main(void) {
-// struct nes_state state = {0};
-
-// init_opcode_lut();
-// init_opcode_ud_lut();
-// state.cpu.sp = 0xfd;
-
-// // FILE *f = fopen("nestest.nes", "rb");
-// // fseek(f, 16, SEEK_SET);
-// // fread(&state.memory[0xc000], 1, 0x4000, f);
-// // fclose(f);
-// // state.cpu.pc = 0xc000;
-
-// FILE *f = fopen("6502_functional_test.bin", "rb");
-// fread(state.memory, 1, 0x10000, f);
-// fclose(f);
-// state.cpu.pc = 0x0400;
-
-// size_t i;
-// for(i = 0; i < 70000000; ++i) {
-// cpu_tick(&state);
-// if(state.cpu.pc == 0x3469) break;
-// }
-// // printf("%lld", i);
-// return 0;
-// }
-
-
-
-
-// bool running = true;
-// uint64_t next_update = mkfw_gettime();
-// int64_t frame_duration_ns = 16666667; // NTSC ~60.0988 Hz
-
-// audio_sync_init(next_update); // Initialize sync base time
-// while(running && !mkfw_should_close()) {
-// mkfw_pump_messages();
-// if(key_pressed(MKS_KEY_ESCAPE)) { running = false; }
-
-// #ifdef PROFILER
-// reset_profiling_data();
-// #endif
-
-// render_callback();
-// apply_phosphor_decay();
-// update_keyboard_state();
-// update_modifier_state();
-// update_mouse_state();
-// state.frame_number++;
-
-// #ifndef PERF_TEST
-// render_frame();
-// #ifdef PROFILER
-// debug_render();
-// #endif
-
-// audio_throttle_emulator(state.frame_number, &frame_duration_ns);
-
-// uint64_t now = mkfw_gettime();
-// int64_t remaining = next_update - now;
-
-// if(remaining > 0) {
-// if(remaining > ONE_MILLISECOND_NS) {
-// mkfw_sleep(remaining - ONE_MILLISECOND_NS);
-// }
-// while(mkfw_gettime() < next_update) { /**/ }
-// } else {
-// next_update = now;
-// }
+ struct timer_handle *timer = timer_new(FRAME_INTERVAL_NS);
+ if(!timer) {
+ fprintf(stderr, "Failed to create timer\n");
+ return 1;
+ }
-// next_update += frame_duration_ns;
+ 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) {
+ 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);
+
+ 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 *dst = buffer;
+ uint8_t *src = nstate.ppu.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);
+ }
+
+ glfwDestroyWindow(window);
+ } else {
+ fprintf(stderr, "Failed to create window\n");
+ }
+ glfwTerminate();
-// mkfw_swap_buffers();
-// #endif
-// }
+ } else {
+ fprintf(stderr, "Failed to initialize GLFW\n");
+ }
+ timer_destroy(timer);
+#ifdef _WIN32
+ timeEndPeriod(1);
+#endif
+ return 0;
+}