Advertisement
Guest User

AgNES WASM module core

a guest
Nov 1st, 2023
147
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 7.37 KB | Gaming | 0 0
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <stdint.h>
  4. #include <inttypes.h>
  5. #include <string.h>
  6. #include <math.h>
  7.  
  8. #define WAHE_IMPORTS_IMPL
  9. #include "wahe_imports.h"
  10.  
  11. #include "agnes.c"
  12.  
  13. typedef struct
  14. {
  15.     agnes_t *agnes;
  16.     agnes_input_t player1, player2;
  17.     double frame_time;
  18. } nes_t;
  19.  
  20. nes_t nes = {0};
  21.  
  22. int process_nes_frame(uint8_t *fb)
  23. {
  24.     double frame_dur = 1./60.;
  25.     int frames_done = 0;
  26.  
  27.     double now = wahe_get_raw_time();
  28.  
  29.     // Init frame time
  30.     if (nes.frame_time == 0.)
  31.         nes.frame_time = now - frame_dur*0.5;
  32.  
  33.     // Limit frameskipping
  34.     if (nes.frame_time < now - 2.5*frame_dur)
  35.         nes.frame_time = now - 2.5*frame_dur;
  36.  
  37.     while (nes.frame_time + frame_dur < now)
  38.     {
  39.         // Step emulation
  40.         agnes_set_input(nes.agnes, &nes.player1, &nes.player2);
  41.         agnes_next_frame(nes.agnes);
  42.         frames_done++;
  43.         nes.frame_time += frame_dur;
  44.     }
  45.  
  46.     // Convert from palette framebuffer to sRGB framebuffer
  47.     if (frames_done)
  48.         for (int iy = 0; iy < AGNES_SCREEN_HEIGHT; iy++)
  49.             for (int ix = 0; ix < AGNES_SCREEN_WIDTH; ix++)
  50.                 ((agnes_color_t *) fb)[iy*AGNES_SCREEN_WIDTH + ix] = agnes_get_screen_pixel(nes.agnes, ix, iy);
  51.  
  52.     return frames_done;
  53. }
  54.  
  55. EXPORT("module_message_input")
  56. char *module_message_input(char *message)
  57. {
  58.     static char *ret_msg = NULL;
  59.     int i, n, n2;
  60.  
  61.     if (message == NULL)
  62.         return NULL;
  63.  
  64.     wahe_run_command("Benchmark");
  65.  
  66.     free(ret_msg);
  67.     ret_msg = NULL;
  68.  
  69.     // Parse each line
  70.     for (const char *line = message; line; line = find_next_line(line))
  71.     {
  72.         // Receive module index
  73.         sscanf(line, "Module ID %60s", module_id);
  74.  
  75.         // Receive wahe_run_command() pointer
  76.         #ifndef __wasm__
  77.         sscanf(line, "wahe_run_command() = %zx", (size_t *) &wahe_run_command);
  78.         #endif
  79.  
  80.         // Load ROM file
  81.         int path_start = 0, path_end = 0;
  82.         sscanf(line, "Load ROM file at path %n%*[^\n]%n", &path_start, &path_end);
  83.         if (path_end)
  84.         {
  85.             if (nes.agnes)
  86.                 free(nes.agnes->gamepack.data);
  87.             agnes_destroy(nes.agnes);
  88.             nes.agnes = NULL;
  89.  
  90.             // Copy ROM path
  91.             char *path = calloc(1+path_end-path_start, sizeof(char));
  92.             memcpy(path, &line[path_start], path_end-path_start);
  93.  
  94.             // Write command to load ROM data
  95.             char *cmd = calloc(23+path_end-path_start, sizeof(char));
  96.             sprintf(cmd, "Load raw file at path %s", path);
  97.             free(path);
  98.  
  99.             // Run command and parse
  100.             char *response = wahe_run_command(cmd);
  101.             free(cmd);
  102.             size_t rom_data = 0, rom_size = 0;
  103.             sscanf(response, "Data location: %zu bytes at %zx", &rom_size, &rom_data);
  104.             free(response);
  105.  
  106.             // Process ROM
  107.             nes.agnes = agnes_make();
  108.             agnes_load_ines_data(nes.agnes, (uint8_t *) rom_data, rom_size);
  109.         }
  110.  
  111.         // Player input
  112.         int player_number;
  113.         char button[7], state[5];
  114.         if (sscanf(line, "Player %d %6s %4s", &player_number, button, state) == 3)
  115.         {
  116.             agnes_input_t *player = player_number == 1 ? &nes.player1 : &nes.player2;
  117.             int8_t *bp = NULL;
  118.  
  119.             if (strcmp(button, "A"     ) == 0)   bp = &player->a;
  120.             if (strcmp(button, "B"     ) == 0)   bp = &player->b;
  121.             if (strcmp(button, "Select") == 0)   bp = &player->select;
  122.             if (strcmp(button, "Start" ) == 0)   bp = &player->start;
  123.             if (strcmp(button, "Up"    ) == 0)   bp = &player->up;
  124.             if (strcmp(button, "Down"  ) == 0)   bp = &player->down;
  125.             if (strcmp(button, "Left"  ) == 0)   bp = &player->left;
  126.             if (strcmp(button, "Right" ) == 0)   bp = &player->right;
  127.  
  128.             if (strcmp(state, "down") == 0)
  129.                 *bp = 2;
  130.             else
  131.                 *bp = -2;
  132.         }
  133.  
  134.         // Save state
  135.         n = 0;
  136.         sscanf(line, "Save state%n", &n);
  137.         if (n)
  138.         {
  139.             // Print module_message_input commands that can restore the module's state
  140.             size_t state_len = sizeof(agnes_t), rom_len = nes.agnes->gamepack.data_size;
  141.             uint8_t *state_data = (uint8_t *) nes.agnes, *rom_data = nes.agnes->gamepack.data;
  142.  
  143.             ret_msg = malloc(19 + state_len*2 + 1 + 13 + rom_len*2 + 1);
  144.  
  145.             // Print RAM
  146.             n = sprintf(ret_msg, "NES RAM state (AP) ");
  147.             for (int i=0; i < state_len; i++)
  148.             {
  149.                 uint8_t c = state_data[i];
  150.                 ret_msg[n++] = 'A' + (c >> 4);
  151.                 ret_msg[n++] = 'A' + (c & 0xF);
  152.             }
  153.             ret_msg[n++] = '\n';
  154.  
  155.             // Print ROM
  156.             n += sprintf(&ret_msg[n], "NES ROM (AP) ");
  157.             for (int i=0; i < rom_len; i++)
  158.             {
  159.                 uint8_t c = rom_data[i];
  160.                 ret_msg[n++] = 'A' + (c >> 4);
  161.                 ret_msg[n++] = 'A' + (c & 0xF);
  162.             }
  163.             ret_msg[n] = '\0';
  164.  
  165.             return ret_msg;
  166.         }
  167.  
  168.         // Restore RAM state
  169.         n = 0;
  170.         n2 = 0;
  171.         sscanf(line, "NES RAM state (AP) %n%*s%n", &n, &n2);
  172.         if (n2)
  173.         {
  174.             uint8_t *gamepack_data = nes.agnes->gamepack.data;
  175.  
  176.             size_t len = (n2 - n) / 2;
  177.             if (len > sizeof(agnes_t))
  178.                 len = sizeof(agnes_t);
  179.             uint8_t *state_data = (uint8_t *) nes.agnes;
  180.  
  181.             // Translate AP format
  182.             for (int i=0 ; i < len; i++)
  183.                 state_data[i] = (line[n+i*2] - 'A' << 4) + line[n+i*2+1] - 'A';
  184.  
  185.             nes.agnes->gamepack.data = gamepack_data;
  186.  
  187.             // Set pointers to struct
  188.             nes.agnes->cpu.agnes = nes.agnes;
  189.             nes.agnes->ppu.agnes = nes.agnes;
  190.             switch (nes.agnes->gamepack.mapper)
  191.             {
  192.                 case 0: nes.agnes->mapper.m0.agnes = nes.agnes; break;
  193.                 case 1: nes.agnes->mapper.m1.agnes = nes.agnes; break;
  194.                 case 2: nes.agnes->mapper.m2.agnes = nes.agnes; break;
  195.                 case 4: nes.agnes->mapper.m4.agnes = nes.agnes; break;
  196.             }
  197.         }
  198.  
  199.         // Restore ROM
  200.         n = 0;
  201.         n2 = 0;
  202.         sscanf(line, "NES ROM (AP) %n%*s%n", &n, &n2);
  203.         if (n2)
  204.         {
  205.             free(nes.agnes->gamepack.data);
  206.             size_t len = (n2 - n) / 2;
  207.             nes.agnes->gamepack.data = malloc(len);
  208.  
  209.             // Translate AP format
  210.             for (int i=0 ; i < len; i++)
  211.                 nes.agnes->gamepack.data[i] = (line[n+i*2] - 'A' << 4) + line[n+i*2+1] - 'A';
  212.         }
  213.  
  214.         // Documentation
  215.         n = 0;
  216.         sscanf(line, "Documentation%n", &n);
  217.         if (n)
  218.         {
  219.             return  "module_message_input() takes:\n\n"
  220.                 "   Load ROM file at path <file path until EOL>\n"
  221.                 "       Example: Load ROM file at path F:/roms/Duckhunt.nes\n\n"
  222.                 "   Player <player number> <button name> <button state>\n"
  223.                 "       Controller input event for each player that represents the change of state of a button\n"
  224.                 "       <player number> is either 1 or 2\n"
  225.                 "       <button name> can be A / B / Select / Start / Up / Down / Left / Right\n"
  226.                 "       <button state> is either 'up' or 'down'\n"
  227.                 "       Example: Player 1 Start down\n\n"
  228.                 "   Save state\n"
  229.                 "       Returns a message containing the 'NES RAM state' and 'NES ROM' commands\n\n"
  230.                 "   NES RAM state (AP) <data in AP half-byte format>\n"
  231.                 "       Sets the RAM state to the provided data\n\n"
  232.                 "   NES ROM (AP) <data in AP half-byte format>\n"
  233.                 "       Sets the ROM data to the provided data\n\n"
  234.                 "module_draw() takes nothing and either returns the description of an image or returns NULL if the frame hasn't been updated";
  235.         }
  236.     }
  237.  
  238.     //wahe_run_command("Benchmark");
  239.  
  240.     return NULL;
  241. }
  242.  
  243. EXPORT("module_draw")
  244. char *module_draw(char *host_msg)
  245. {
  246.     static uint8_t *fb = NULL;
  247.  
  248.     if (fb == NULL)
  249.         fb = malloc(AGNES_SCREEN_WIDTH * AGNES_SCREEN_HEIGHT * 4 * sizeof(uint8_t));
  250.  
  251.     //wahe_run_command("Benchmark");
  252.  
  253.     // Process NES frame
  254.     if (process_nes_frame(fb) == 0)
  255.         return NULL;
  256.  
  257.     // Write return message
  258.     static char return_msg[160];
  259.     sprintf(return_msg,
  260.             "Pixel format: RGBA 8 sRGB\n"
  261.             "Framebuffer location: %zu bytes at %#zx (module %s)\n"
  262.             "Framebuffer resolution %dx%d\n",
  263.             AGNES_SCREEN_WIDTH * AGNES_SCREEN_HEIGHT * 4 * sizeof(uint8_t), (size_t) fb, module_id, AGNES_SCREEN_WIDTH, AGNES_SCREEN_HEIGHT);
  264.  
  265.     //wahe_run_command("Benchmark");
  266.  
  267.     return return_msg;
  268. }
  269.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement