int main(int argc, char * argv[]) { if (argc != 2) { usage(argv[0]); exit(1); } if (SDL_Init(SDL_INIT_VIDEO) != 0) { printf("Echec init : %s\n", SDL_GetError()); exit(1); } GBContext *cpu = cpu_init(argv[1]); GPUContext *gpu = gpu_init(cpu); JOYPContext *joyp = joyp_init(cpu); uint8_t op; unsigned int start_time, last_time, elapsed_time; while (!cpu->exit) { start_time = SDL_GetTicks(); while (cpu->t_clock < VSYNC_CYCLES) { op = read8(cpu, cpu->PC++); execute(cpu, op); //"realtime" operations must be here.. cpu_ctx_update(cpu); gpu_ctx_update(cpu, gpu); joyp_ctx_update(cpu, joyp); handle_interrupt(cpu); } cpu->t_clock = 0; last_time = SDL_GetTicks(); elapsed_time = last_time - start_time; if(elapsed_time < VSYNC_TIME_MS) {// just a basic frame rate wait loop SDL_Delay(VSYNC_TIME_MS - elapsed_time); } gpu_render(cpu, gpu); // printf("elapsed: %d\n", elapsed_time); } joyp_destroy(joyp); gpu_destroy(gpu); cpu_destroy(cpu); SDL_Quit(); return 0; }
// Timing from http://imrannazar.com/GameBoy-Emulation-in-JavaScript:-GPU-Timings void gpu_process(gpu* gp, interrupts* ir, uint16_t clock) { gp->state_start_clock += clock; switch(GPU_GET_MODE(gp)) { case GPU_HORIZ_BLANK: if (gp->state_start_clock >= GPU_HORIZ_BLANK_TIMING) { gp->state_start_clock = 0; gp->reg.cur_line++; if (gp->reg.cur_line == SCREEN_HEIGHT) { GPU_SET_MODE(gp, GPU_VERT_BLANK); // Redraw surface SDL_Flip(gp->surface); // Raise irq if (gp->reg.control & 0x80) interrupts_raise(ir, IRQ_VBLANK); if ((gp->reg.status & (1 << 4))) interrupts_raise(ir, IRQ_LCD); } else { if ((gp->reg.status & (1 << 5)) && gp->reg.cur_line < SCREEN_HEIGHT) interrupts_raise(ir, IRQ_LCD); GPU_SET_MODE(gp, GPU_SCAN_OAM); } } break; case GPU_VERT_BLANK: if (gp->state_start_clock >= GPU_VERT_BLANK_TIMING) { gp->state_start_clock = 0; gp->reg.cur_line++; if (gp->reg.cur_line > SCREEN_HEIGHT + 10) { GPU_SET_MODE(gp, GPU_HORIZ_BLANK); gp->reg.cur_line = 0; gpu_render(gp); } } break; case GPU_SCAN_OAM: if (gp->state_start_clock >= GPU_SCAN_OAM_TIMING) { gp->state_start_clock = 0; GPU_SET_MODE(gp, GPU_SCAN_VRAM); } break; case GPU_SCAN_VRAM: if (gp->state_start_clock >= GPU_SCAN_VRAM_TIMING) { if ((gp->reg.status & (1 << 3)) && gp->reg.cur_line < SCREEN_HEIGHT) interrupts_raise(ir, IRQ_LCD); gp->state_start_clock = 0; GPU_SET_MODE(gp, GPU_HORIZ_BLANK); // Render one line if (gp->reg.cur_line < SCREEN_HEIGHT) gpu_render(gp); } break; } // Check LY == LYC if (gp->reg.cur_line == gp->reg.check_line) { gp->reg.status |= (1 << 2); if (gp->reg.status & (1 << 6)) interrupts_raise(ir, IRQ_LCD); } else { gp->reg.status &= ~(1 << 2); } }