Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <stdlib.h>
- #include <stdio.h>
- #include <stdint.h>
- #include <inttypes.h>
- #include <string.h>
- #include <math.h>
- #define WAHE_IMPORTS_IMPL
- #include "wahe_imports.h"
- #include "agnes.c"
- typedef struct
- {
- agnes_t *agnes;
- agnes_input_t player1, player2;
- double frame_time;
- } nes_t;
- nes_t nes = {0};
- int process_nes_frame(uint8_t *fb)
- {
- double frame_dur = 1./60.;
- int frames_done = 0;
- double now = wahe_get_raw_time();
- // Init frame time
- if (nes.frame_time == 0.)
- nes.frame_time = now - frame_dur*0.5;
- // Limit frameskipping
- if (nes.frame_time < now - 2.5*frame_dur)
- nes.frame_time = now - 2.5*frame_dur;
- while (nes.frame_time + frame_dur < now)
- {
- // Step emulation
- agnes_set_input(nes.agnes, &nes.player1, &nes.player2);
- agnes_next_frame(nes.agnes);
- frames_done++;
- nes.frame_time += frame_dur;
- }
- // Convert from palette framebuffer to sRGB framebuffer
- if (frames_done)
- for (int iy = 0; iy < AGNES_SCREEN_HEIGHT; iy++)
- for (int ix = 0; ix < AGNES_SCREEN_WIDTH; ix++)
- ((agnes_color_t *) fb)[iy*AGNES_SCREEN_WIDTH + ix] = agnes_get_screen_pixel(nes.agnes, ix, iy);
- return frames_done;
- }
- EXPORT("module_message_input")
- char *module_message_input(char *message)
- {
- static char *ret_msg = NULL;
- int i, n, n2;
- if (message == NULL)
- return NULL;
- wahe_run_command("Benchmark");
- free(ret_msg);
- ret_msg = NULL;
- // Parse each line
- for (const char *line = message; line; line = find_next_line(line))
- {
- // Receive module index
- sscanf(line, "Module ID %60s", module_id);
- // Receive wahe_run_command() pointer
- #ifndef __wasm__
- sscanf(line, "wahe_run_command() = %zx", (size_t *) &wahe_run_command);
- #endif
- // Load ROM file
- int path_start = 0, path_end = 0;
- sscanf(line, "Load ROM file at path %n%*[^\n]%n", &path_start, &path_end);
- if (path_end)
- {
- if (nes.agnes)
- free(nes.agnes->gamepack.data);
- agnes_destroy(nes.agnes);
- nes.agnes = NULL;
- // Copy ROM path
- char *path = calloc(1+path_end-path_start, sizeof(char));
- memcpy(path, &line[path_start], path_end-path_start);
- // Write command to load ROM data
- char *cmd = calloc(23+path_end-path_start, sizeof(char));
- sprintf(cmd, "Load raw file at path %s", path);
- free(path);
- // Run command and parse
- char *response = wahe_run_command(cmd);
- free(cmd);
- size_t rom_data = 0, rom_size = 0;
- sscanf(response, "Data location: %zu bytes at %zx", &rom_size, &rom_data);
- free(response);
- // Process ROM
- nes.agnes = agnes_make();
- agnes_load_ines_data(nes.agnes, (uint8_t *) rom_data, rom_size);
- }
- // Player input
- int player_number;
- char button[7], state[5];
- if (sscanf(line, "Player %d %6s %4s", &player_number, button, state) == 3)
- {
- agnes_input_t *player = player_number == 1 ? &nes.player1 : &nes.player2;
- int8_t *bp = NULL;
- if (strcmp(button, "A" ) == 0) bp = &player->a;
- if (strcmp(button, "B" ) == 0) bp = &player->b;
- if (strcmp(button, "Select") == 0) bp = &player->select;
- if (strcmp(button, "Start" ) == 0) bp = &player->start;
- if (strcmp(button, "Up" ) == 0) bp = &player->up;
- if (strcmp(button, "Down" ) == 0) bp = &player->down;
- if (strcmp(button, "Left" ) == 0) bp = &player->left;
- if (strcmp(button, "Right" ) == 0) bp = &player->right;
- if (strcmp(state, "down") == 0)
- *bp = 2;
- else
- *bp = -2;
- }
- // Save state
- n = 0;
- sscanf(line, "Save state%n", &n);
- if (n)
- {
- // Print module_message_input commands that can restore the module's state
- size_t state_len = sizeof(agnes_t), rom_len = nes.agnes->gamepack.data_size;
- uint8_t *state_data = (uint8_t *) nes.agnes, *rom_data = nes.agnes->gamepack.data;
- ret_msg = malloc(19 + state_len*2 + 1 + 13 + rom_len*2 + 1);
- // Print RAM
- n = sprintf(ret_msg, "NES RAM state (AP) ");
- for (int i=0; i < state_len; i++)
- {
- uint8_t c = state_data[i];
- ret_msg[n++] = 'A' + (c >> 4);
- ret_msg[n++] = 'A' + (c & 0xF);
- }
- ret_msg[n++] = '\n';
- // Print ROM
- n += sprintf(&ret_msg[n], "NES ROM (AP) ");
- for (int i=0; i < rom_len; i++)
- {
- uint8_t c = rom_data[i];
- ret_msg[n++] = 'A' + (c >> 4);
- ret_msg[n++] = 'A' + (c & 0xF);
- }
- ret_msg[n] = '\0';
- return ret_msg;
- }
- // Restore RAM state
- n = 0;
- n2 = 0;
- sscanf(line, "NES RAM state (AP) %n%*s%n", &n, &n2);
- if (n2)
- {
- uint8_t *gamepack_data = nes.agnes->gamepack.data;
- size_t len = (n2 - n) / 2;
- if (len > sizeof(agnes_t))
- len = sizeof(agnes_t);
- uint8_t *state_data = (uint8_t *) nes.agnes;
- // Translate AP format
- for (int i=0 ; i < len; i++)
- state_data[i] = (line[n+i*2] - 'A' << 4) + line[n+i*2+1] - 'A';
- nes.agnes->gamepack.data = gamepack_data;
- // Set pointers to struct
- nes.agnes->cpu.agnes = nes.agnes;
- nes.agnes->ppu.agnes = nes.agnes;
- switch (nes.agnes->gamepack.mapper)
- {
- case 0: nes.agnes->mapper.m0.agnes = nes.agnes; break;
- case 1: nes.agnes->mapper.m1.agnes = nes.agnes; break;
- case 2: nes.agnes->mapper.m2.agnes = nes.agnes; break;
- case 4: nes.agnes->mapper.m4.agnes = nes.agnes; break;
- }
- }
- // Restore ROM
- n = 0;
- n2 = 0;
- sscanf(line, "NES ROM (AP) %n%*s%n", &n, &n2);
- if (n2)
- {
- free(nes.agnes->gamepack.data);
- size_t len = (n2 - n) / 2;
- nes.agnes->gamepack.data = malloc(len);
- // Translate AP format
- for (int i=0 ; i < len; i++)
- nes.agnes->gamepack.data[i] = (line[n+i*2] - 'A' << 4) + line[n+i*2+1] - 'A';
- }
- // Documentation
- n = 0;
- sscanf(line, "Documentation%n", &n);
- if (n)
- {
- return "module_message_input() takes:\n\n"
- " Load ROM file at path <file path until EOL>\n"
- " Example: Load ROM file at path F:/roms/Duckhunt.nes\n\n"
- " Player <player number> <button name> <button state>\n"
- " Controller input event for each player that represents the change of state of a button\n"
- " <player number> is either 1 or 2\n"
- " <button name> can be A / B / Select / Start / Up / Down / Left / Right\n"
- " <button state> is either 'up' or 'down'\n"
- " Example: Player 1 Start down\n\n"
- " Save state\n"
- " Returns a message containing the 'NES RAM state' and 'NES ROM' commands\n\n"
- " NES RAM state (AP) <data in AP half-byte format>\n"
- " Sets the RAM state to the provided data\n\n"
- " NES ROM (AP) <data in AP half-byte format>\n"
- " Sets the ROM data to the provided data\n\n"
- "module_draw() takes nothing and either returns the description of an image or returns NULL if the frame hasn't been updated";
- }
- }
- //wahe_run_command("Benchmark");
- return NULL;
- }
- EXPORT("module_draw")
- char *module_draw(char *host_msg)
- {
- static uint8_t *fb = NULL;
- if (fb == NULL)
- fb = malloc(AGNES_SCREEN_WIDTH * AGNES_SCREEN_HEIGHT * 4 * sizeof(uint8_t));
- //wahe_run_command("Benchmark");
- // Process NES frame
- if (process_nes_frame(fb) == 0)
- return NULL;
- // Write return message
- static char return_msg[160];
- sprintf(return_msg,
- "Pixel format: RGBA 8 sRGB\n"
- "Framebuffer location: %zu bytes at %#zx (module %s)\n"
- "Framebuffer resolution %dx%d\n",
- AGNES_SCREEN_WIDTH * AGNES_SCREEN_HEIGHT * 4 * sizeof(uint8_t), (size_t) fb, module_id, AGNES_SCREEN_WIDTH, AGNES_SCREEN_HEIGHT);
- //wahe_run_command("Benchmark");
- return return_msg;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement