gbemu/lib/ppu_sm.c

197 lines
5.1 KiB
C
Raw Normal View History

2025-02-01 00:48:49 -07:00
#include <ppu.h>
#include <lcd.h>
#include <cpu.h>
#include <interrupts.h>
2025-02-01 19:05:25 -07:00
#include <string.h>
#include <cart.h>
void pipeline_fifo_reset();
void pipeline_process();
2025-02-01 00:48:49 -07:00
void increment_ly() {
2025-02-01 19:05:25 -07:00
if (window_visible() && lcd_get_context()->ly >= lcd_get_context()->win_y && lcd_get_context()->ly < lcd_get_context()->win_y + YRES) {
ppu_get_context()->window_line++;
}
2025-02-01 00:48:49 -07:00
lcd_get_context()->ly++;
if(lcd_get_context()->ly == lcd_get_context()->ly_compare) {
LCDS_LYC_SET(1);
if(LCDS_STAT_INT(SS_LYC)) {
cpu_request_interrupt(IT_LCD_STAT);
}
} else {
LCDS_LYC_SET(0);
}
}
2025-02-01 19:05:25 -07:00
void load_line_sprites() {
int cur_y = lcd_get_context()->ly;
u8 sprite_height = LCDC_OBJ_HEIGHT;
memset(ppu_get_context()->line_entry_array, 0, sizeof(ppu_get_context()->line_entry_array));
for (int i=0; i<40; i++) {
oam_entry e = ppu_get_context()->oam_ram[i];
if (!e.x) {
//x = 0 means not visible
continue;
}
if (ppu_get_context()->line_sprite_count >= 10) {
//max of 10 sprites per line
break;
}
if(e.y <= cur_y + 16 && e.y + sprite_height > cur_y + 16) {
//this sprite is on the current line
oam_line_entry *entry = &ppu_get_context()->line_entry_array[
ppu_get_context()->line_sprite_count++
];
entry->entry = e;
entry->next = NULL;
if(!ppu_get_context()->line_sprites ||
ppu_get_context()->line_sprites->entry.x > e.x) {
entry->next = ppu_get_context()->line_sprites;
ppu_get_context()->line_sprites = entry;
continue;
}
//do some sorting
oam_line_entry *le = ppu_get_context()->line_sprites;
oam_line_entry *prev = le;
while(le) {
if (le->entry.x > e.x) {
prev->next = entry;
entry->next = le;
break;
}
if(!le->next) {
le->next = entry;
break;
}
prev = le;
le = le->next;
}
}
}
}
2025-02-01 00:48:49 -07:00
void ppu_mode_oam() {
if(ppu_get_context()->line_ticks >= 80) {
LCDS_MODE_SET(MODE_XFER);
2025-02-01 19:05:25 -07:00
ppu_get_context()->pfc.cur_fetch_state = FS_TILE;
ppu_get_context()->pfc.line_x = 0;
ppu_get_context()->pfc.fetch_x = 0;
ppu_get_context()->pfc.pushed_x = 0;
ppu_get_context()->pfc.fifo_x = 0;
}
if(ppu_get_context()->line_ticks == 1) {
//read oam on the first tick only...
ppu_get_context()->line_sprites = 0;
ppu_get_context()->line_sprite_count = 0;
load_line_sprites();
2025-02-01 00:48:49 -07:00
}
}
void ppu_mode_xfer() {
2025-02-01 19:05:25 -07:00
pipeline_process();
if(ppu_get_context()->pfc.pushed_x >= XRES) {
pipeline_fifo_reset();
2025-02-01 00:48:49 -07:00
LCDS_MODE_SET(MODE_HBLANK);
2025-02-01 19:05:25 -07:00
if(LCDS_STAT_INT(SS_HBLANK)) {
cpu_request_interrupt(IT_LCD_STAT);
2025-02-18 22:39:56 -07:00
printf("hblank!\n");
2025-02-01 19:05:25 -07:00
}
2025-02-01 20:53:13 -07:00
ppu_get_context()->rendering_window = false;
2025-02-01 00:48:49 -07:00
}
}
void ppu_mode_vblank() {
if(ppu_get_context()->line_ticks >= TICKS_PER_LINE) {
increment_ly();
if(lcd_get_context()->ly >= LINES_PER_FRAME) {
LCDS_MODE_SET(MODE_OAM);
2025-02-18 22:39:56 -07:00
if(LCDS_STAT_INT(SS_OAM)) {
cpu_request_interrupt(IT_LCD_STAT);
}
2025-02-01 00:48:49 -07:00
lcd_get_context()->ly = 0;
2025-02-01 19:05:25 -07:00
ppu_get_context()->window_line = 0;
2025-02-01 00:48:49 -07:00
}
ppu_get_context()->line_ticks = 0;
}
}
static long prev_frame_time = 0;
static long start_timer = 0;
static long frame_count = 0;
void ppu_mode_hblank() {
if (ppu_get_context()->line_ticks >= TICKS_PER_LINE) {
increment_ly();
if(lcd_get_context()->ly >= YRES) {
LCDS_MODE_SET(MODE_VBLANK);
cpu_request_interrupt(IT_VBLANK);
if(LCDS_STAT_INT(SS_VBLANK)) {
cpu_request_interrupt(IT_LCD_STAT);
}
ppu_get_context()->current_frame++;
//calc FPS...
u32 end = get_ticks();
u32 frame_time = end - prev_frame_time;
if(frame_time < ppu_get_context()->target_frame_time) {
delay((ppu_get_context()->target_frame_time - frame_time));
2025-02-01 00:48:49 -07:00
}
2025-02-17 21:22:13 -07:00
while(ppu_get_context()->paused) {
delay(10);
if(ppu_get_context()->frame){
ppu_get_context()->frame = false;
break;
}
}
2025-02-01 00:48:49 -07:00
if (end - start_timer >= 1000) {
u32 fps = frame_count;
start_timer = end;
frame_count = 0;
2025-02-03 16:25:18 -07:00
printf("FPS: %ld\n", fps);
2025-02-01 19:05:25 -07:00
if(cart_need_save()){
cart_battery_save();
}
2025-02-01 00:48:49 -07:00
}
frame_count++;
prev_frame_time = get_ticks();
} else {
LCDS_MODE_SET(MODE_OAM);
2025-02-18 22:39:56 -07:00
if(LCDS_STAT_INT(SS_OAM)) {
cpu_request_interrupt(IT_LCD_STAT);
}
2025-02-01 00:48:49 -07:00
}
ppu_get_context()->line_ticks = 0;
}
2025-02-08 13:08:34 -07:00
}