#include #include #include #include #include #include #include #include #include "lodepng.h" #include "gb.h" #define SKIP_FRAMES 1200 #define NUM_FRAMES 60 // #define SKIP_STEPS 1000000 #define NUM_STEPS 300000 #define UNUSED(a) ((void) (a)) static uint32_t file_size( const char * const path ) { struct stat st; if (stat(path, &st)) { perror("stat():"); exit(EXIT_FAILURE); } return st.st_size; } static const uint8_t * load( const char * const path, uint32_t * const r_file_size ) { const uint32_t rom_size = file_size(path); uint8_t *rom_data = malloc(rom_size); if (!rom_data) { perror("malloc():"); exit(EXIT_FAILURE); } FILE *fh = fopen(path, "rb"); if (!fh) { perror("fopen():"); exit(EXIT_FAILURE); } if (!fread(rom_data, rom_size, 1, fh)) { perror("fread():"); exit(EXIT_FAILURE); } fclose(fh); if (r_file_size) { *r_file_size = (uint32_t) rom_size; } return rom_data; } // buffer for rendered frames static uint8_t frames[NUM_FRAMES * GB_FB_SIZE]; static void on_set_rom_bank( const gb_t * const ctx, const uint16_t bank ) { printf("set rom bank: bank = %u, PC = %04x\n", bank, ctx->cpu.rs[5]); } static void on_set_ram_bank( const gb_t * const ctx, const uint16_t bank ) { printf("set ram bank: bank = %u, PC = %04x\n", bank, ctx->cpu.rs[5]); } static void on_vblank( const gb_t * const ctx ) { printf("vblank: PC = %04x\n", ctx->cpu.rs[5]); } static void on_hblank( const gb_t * const ctx ) { printf("hblank: PC = %04x\n", ctx->cpu.rs[5]); } static void on_timer( const gb_t * const ctx ) { printf("timer: PC = %04x\n", ctx->cpu.rs[5]); } /* * static void * on_gpu_step( * const gb_t * const ctx * ) { * printf("gpu step: gpu clock = 0x%04x\n", ctx->gpu.clock); * } */ static void on_gpu_set_mode( const gb_t * const ctx, const uint8_t mode ) { UNUSED(ctx); printf("gpu mode: %u, line = %u\n", mode, ctx->gpu.line); } static void on_set_cpu_state( const gb_t * const ctx, const gb_cpu_state_t state ) { printf("cpu state: PC = %04x, state = %d\n", ctx->cpu.rs[5], state); } static void on_rst( const gb_t * const ctx, const uint16_t addr ) { UNUSED(ctx); printf("rst: addr = %04x\n", addr); } /* * static void * on_mmu_rb( * const gb_t * const ctx, * const uint16_t addr, * const uint8_t val * ) { * UNUSED(ctx); * printf("mmu_rb: addr = 0x%04X, val = 0x%02X\n", addr, val); * } */ /* * static void * on_mmu_wb( * const gb_t * const ctx, * const uint16_t addr, * const uint8_t val * ) { * UNUSED(ctx); * printf("mmu_wb: addr = 0x%04X, val = 0x%02X\n", addr, val); * } */ static void on_oam_wb( const gb_t * const ctx, const uint16_t addr, const uint8_t val ) { UNUSED(ctx); printf("oam_wb: addr = 0x%04X, val = 0x%02X\n", addr, val); } static const gb_config_t EXECUTE_CONFIG = { .on_set_rom_bank = on_set_rom_bank, .on_set_ram_bank = on_set_ram_bank, .on_vblank = on_vblank, .on_hblank = on_hblank, .on_timer = on_timer, // .on_gpu_step = on_gpu_step, .on_gpu_set_mode = on_gpu_set_mode, .on_set_cpu_state = on_set_cpu_state, .on_rst = on_rst, // .on_mmu_rb = on_mmu_rb, // .on_mmu_wb = on_mmu_wb, .on_oam_wb = on_oam_wb, }; static void test_render_frames( const char * const png_path, const char * const rom_path ) { // load rom data uint32_t rom_size = 0; const uint8_t *rom_data = load(rom_path, &rom_size); fprintf(stderr, "loaded \"%s\" (%u bytes)\n", rom_path, rom_size); // init context gb_t ctx; gb_init(&ctx, NULL, rom_data, rom_size); fprintf(stderr, "gb context initialized\n"); for (size_t i = 0; i < SKIP_FRAMES; i++) { // render frame gb_frame(&ctx); } // render frames for (size_t i = 0; i < NUM_FRAMES; i++) { // render frame gb_frame(&ctx); // copy frame to buffer memcpy(frames + GB_FB_SIZE * i, gb_get_frame(&ctx), GB_FB_SIZE); } // save png if (lodepng_encode24_file(png_path, frames, 160, 144 * NUM_FRAMES)) { fprintf(stderr, "lode_png_encode24_file() failed\n"); exit(EXIT_FAILURE); } fprintf(stderr, "%d frames rendered\n", NUM_FRAMES); // free rom data free((void*) rom_data); } const char * const RWS[] = { "AF", "BC", "DE", "HL", "SP", "PC", NULL }; static void gb_dump_context( gb_t * const ctx ) { for (int i = 0; RWS[i]; i++) { printf("%s:%04X ", RWS[i], ctx->cpu.rs[i]); } char buf[128]; size_t buf_len = sizeof(buf); if (gb_disasm(ctx, buf, &buf_len)) { printf("%s\n", buf); } else { printf("\n"); } } static void test_execute_steps( const char * const rom_path ) { // load rom data uint32_t rom_size = 0; const uint8_t *rom_data = load(rom_path, &rom_size); fprintf(stderr, "loaded \"%s\" (%u bytes)\n", rom_path, rom_size); // init context gb_t ctx; gb_init(&ctx, &EXECUTE_CONFIG, rom_data, rom_size); fprintf(stderr, "gb context initialized\n"); #ifdef SKIP_STEPS for (size_t i = 0; i < SKIP_STEPS; i++) { // step cpu gb_step(&ctx); } #endif /* SKIP_STEPS */ // run cpu for (size_t i = 0; i < NUM_STEPS; i++) { // print context gb_dump_context(&ctx); // step cpu gb_step(&ctx); } // free rom data free((void*) rom_data); } static const uint8_t TEST_IMAGE[] = { 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, }; static void test_write_image( const char * const img_path ) { // save png if (lodepng_encode24_file(img_path, TEST_IMAGE, 3, 3)) { fprintf(stderr, "lode_png_encode24_file() failed\n"); exit(EXIT_FAILURE); } fprintf(stderr, "saved image as \"%s\"\n", img_path); } int main(int argc, char *argv[]) { // render 600 frames for (int i = 2; i < argc; i++) { if (argv[1][0] == 'f') { test_render_frames("out.png", argv[i]); } else if (argv[1][0] == 'e') { test_execute_steps(argv[i]); } else if (argv[1][0] == 'i') { test_write_image(argv[i]); } else { fprintf(stderr, "unknown test: %s\n", argv[i]); exit(EXIT_FAILURE); } } // return success return 0; }