Finish ppu, and mbc1 mapping

This commit is contained in:
Samuel Walker 2025-02-01 19:05:25 -07:00
parent 2603916972
commit 89a99b160d
Signed by: piwalker
GPG Key ID: 616B1928705EA4C9
17 changed files with 779 additions and 18 deletions

View File

@ -24,4 +24,8 @@ bool cart_load(char *cart);
rom_header *get_rom_header();
u8 cart_read(u16 address);
void cart_write(u16 address, u8 value);
void cart_write(u16 address, u8 value);
bool cart_need_save();
bool cart_battery_load();
bool cart_battery_save();

22
include/gamepad.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include <common.h>
typedef struct {
bool start;
bool select;
bool a;
bool b;
bool up;
bool down;
bool left;
bool right;
} gamepad_state;
void gamepad_init();
bool gamepad_button_sel();
bool gamepad_dir_sel();
void gamepad_set_sel(u8 value);
gamepad_state *gamepad_get_state();
u8 gamepad_get_output();

View File

@ -41,7 +41,7 @@ lcd_context *lcd_get_context();
#define LCDC_LCD_ENABLE (BIT(lcd_get_context()->lcdc, 7))
#define LCDS_MODE ((lcd_mode)(lcd_get_context()->lcds & 0b11))
#define LCDS_MODE_SET(mode) { lcd_get_context()->lcds &= ~0b11; lcd_get_context()->lcdc |= mode; }
#define LCDS_MODE_SET(mode) { lcd_get_context()->lcds &= ~0b11; lcd_get_context()->lcds |= mode; }
#define LCDS_LYC (BIT(lcd_get_context()->lcds, 2))
#define LCDS_LYC_SET(b) BIT_SET(lcd_get_context()->lcds, 2, b)

View File

@ -7,23 +7,73 @@ static const int TICKS_PER_LINE = 456;
static const int YRES = 144;
static const int XRES = 160;
u32 target_frame_time;
typedef enum {
FS_TILE,
FS_DATA0,
FS_DATA1,
FS_IDLE,
FS_PUSH
} fetch_state;
typedef struct _fifo_entry {
struct _fifo_entry *next;
u32 value;
} fifo_entry;
typedef struct {
fifo_entry *head;
fifo_entry *tail;
u32 size; //32 bit color
} fifo;
typedef struct {
fetch_state cur_fetch_state;
fifo pixel_fifo;
u8 line_x;
u8 pushed_x;
u8 fetch_x;
u8 bgw_fetch_data[3];
u8 fetch_entry_data[6]; //oam data
u8 map_y;
u8 map_x;
u8 tile_y;
u8 fifo_x;
} pixel_fifo_context;
typedef struct {
u8 y;
u8 x;
u8 tile;
unsigned f_cgb_pn : 3;
unsigned f_cgb_vram_bank : 1;
unsigned f_pn : 1;
unsigned f_x_flip : 1;
unsigned f_y_flip : 1;
unsigned f_bgp : 1;
u8 f_cgb_pn : 3;
u8 f_cgb_vram_bank : 1;
u8 f_pn : 1;
u8 f_x_flip : 1;
u8 f_y_flip : 1;
u8 f_bgp : 1;
} oam_entry;
typedef struct _oam_line_entry {
oam_entry entry;
struct _oam_line_entry *next;
} oam_line_entry;
typedef struct {
oam_entry oam_ram[40];
u8 vram[0x2000];
u8 line_sprite_count; // 0 to 10 sprites
oam_line_entry *line_sprites; //linked list of sprites
oam_line_entry line_entry_array[10]; //memory to use for list
u8 fetched_entry_count;
oam_entry fetched_entries[3]; //entries fetched during pipeline
u8 window_line;
pixel_fifo_context pfc;
u32 current_frame;
u32 line_ticks;
u32 *video_buffer;

View File

@ -6,10 +6,39 @@ typedef struct {
u32 rom_size;
u8 *rom_data;
rom_header *header;
//mbc1 data
bool ram_enabled;
bool ram_banking;
u8 *rom_bank_x;
u8 banking_mode;
u8 rom_bank_value;
u8 ram_bank_value;
u8 *ram_bank;
u8 *ram_banks[16];
//battery
bool battery;
bool need_save;
} cart_context;
static cart_context ctx;
bool cart_need_save () {
return ctx.need_save;
}
bool cart_mbc1() {
return BETWEEN(ctx.header->type, 1, 3);
}
bool cart_battery() {
return ctx.header->type == 3;
}
static const char *ROM_TYPES[] = {
"ROM ONLY",
"MBC1",
@ -132,6 +161,23 @@ const char *cart_lic_name() {
return "UNKNOWN";
}
void cart_setup_bamking() {
for (int i=0; i<16; i++) {
ctx.ram_banks[1] = 0;
if((ctx.header->ram_size == 2 && i == 0) ||
(ctx.header->ram_size == 3 && i < 4) ||
(ctx.header->ram_size == 5 && i < 8) ||
(ctx.header->ram_size == 4)) {
ctx.ram_banks[i] = malloc(0x2000);
memset(ctx.ram_banks[i], 0, 0x2000);
}
}
ctx.ram_bank = ctx.ram_banks[0];
ctx.rom_bank_x = ctx.rom_data + 0x4000;
}
bool cart_load(char *cart) {
snprintf(ctx.filename, sizeof(ctx.filename), "%s", cart);
FILE *fp = fopen(cart, "r");
@ -153,6 +199,8 @@ bool cart_load(char *cart) {
ctx.header = (rom_header *)(ctx.rom_data + 0x100);
ctx.header->title[15] = 0;
ctx.battery = cart_battery();
ctx.need_save = false;
printf("Cartridge Loaded:\n");
printf("\t Title : %s\n", ctx.header->title);
@ -162,6 +210,8 @@ bool cart_load(char *cart) {
printf("\t LIC Code : %2.2X (%s)\n", ctx.header->lic_code, cart_lic_name());
printf("\t ROM Vers : %2.2X\n", ctx.header->version);
cart_setup_bamking();
u16 x = 0;
for (u16 i=0X0134; i<=0X014C; i++) {
x = x - ctx.rom_data[i] -1;
@ -172,17 +222,119 @@ bool cart_load(char *cart) {
if((x & 0xFF) != ctx.header->checksum) {
fprintf(stderr, "WARNING!!! The header checksum does not match! ROM may be corrupt or invalid!\n");
}
if(ctx.battery) {
cart_battery_load();
}
return true;
}
u8 cart_read(u16 address){
//for now ROM ONLY type supported...
if(!cart_mbc1() || address < 0x4000) {
return ctx.rom_data[address];
}
if((address & 0xE000) == 0xA000) {
if (!ctx.ram_enabled) {
return 0xFF;
}
if(!ctx.ram_bank) {
return 0xFF;
}
return ctx.ram_bank[address - 0xA000];
}
return ctx.rom_bank_x[address - 0x4000];
return ctx.rom_data[address];
}
void cart_write(u16 address, u8 value){
printf("UNSUPPORTED cart_write(%04X)\n", address);
//NO_IMPL
if(!cart_mbc1()) {
return;
}
if(address < 0x2000) {
ctx.ram_enabled = ((value & 0xF) == 0xA);
}
if((address & 0xE000) == 0x2000) {
//rom bank
if(value == 0) {
value = 1;
}
value &= 0b11111;
ctx.rom_bank_value = value;
ctx.rom_bank_x = ctx.rom_data + (0x4000 * ctx.rom_bank_value);
}
if((address & 0xE000) == 0x4000) {
//ram bank number
ctx.ram_bank_value = value & 0xb11;
if(ctx.ram_banking) {
if(cart_need_save) {
cart_battery_save();
}
ctx.ram_bank = ctx.ram_banks[ctx.ram_bank_value];
}
}
if((address & 0xE000) == 0x6000) {
//bamking mode select
ctx.banking_mode = value & 1;
ctx.ram_banking = ctx.banking_mode;
if (ctx.ram_banking) {
if(cart_need_save) {
cart_battery_save();
}
ctx.ram_bank = ctx.ram_banks[ctx.ram_bank_value];
}
}
if((address & 0xE000) == 0xA000) {
if(!ctx.ram_enabled)
return;
if(!ctx.ram_bank)
return;
ctx.ram_bank[address - 0xA000] = value;
if(ctx.battery) {
ctx.need_save = true;
}
}
}
bool cart_battery_load(){
char fn[1048];
sprintf(fn, "%s.battery", ctx.filename);
FILE *fp = fopen(fn, "rb");
if(!fp) {
fprintf(stderr, "unable to open: %s\n", fn);
}
fread(ctx.ram_bank, 0x2000, 1, fp);
fclose(fp);
}
bool cart_battery_save(){
char fn[1048];
sprintf(fn, "%s.battery", ctx.filename);
FILE *fp = fopen(fn, "wb");
if(!fp) {
fprintf(stderr, "unable to open: %s\n", fn);
}
fwrite(ctx.ram_bank, 0x2000, 1, fp);
fclose(fp);
}

View File

@ -75,7 +75,7 @@ void fetch_data() {
return;
case AM_R_A8:
ctx.fetched_data = bus_read(ctx.regs.pc);
emu_cycles(2);
emu_cycles(1);
ctx.regs.pc++;
return;
case AM_A8_R:

View File

@ -70,7 +70,6 @@ static void proc_halt(cpu_context *ctx) {
static void proc_stop(cpu_context *ctx) {
printf("CPU STOP!\n");
NO_IMPL
}
static void proc_rlca(cpu_context *ctx) {
@ -384,7 +383,6 @@ static void proc_pop(cpu_context *ctx) {
u16 n = (hi << 8) | lo;
cpu_set_reg(ctx->cur_inst->reg_1, n);
emu_cycles(2);
if (ctx->cur_inst->reg_1 == RT_AF) {
cpu_set_reg(ctx->cur_inst->reg_1, n & 0xFFF0);
@ -397,6 +395,8 @@ static void proc_push(cpu_context *ctx) {
emu_cycles(1);
stack_push((val & 0xFF));
emu_cycles(1);
emu_cycles(1);
}
static void proc_ldh(cpu_context *ctx) {
@ -423,8 +423,8 @@ static void proc_ld(cpu_context *ctx) {
emu_cycles(1);
} else {
bus_write(ctx->mem_dest, ctx->fetched_data);
emu_cycles(1);
}
emu_cycles(1);
return;
}

65
lib/gamepad.c Normal file
View File

@ -0,0 +1,65 @@
#include <gamepad.h>
#include <string.h>
typedef struct {
bool button_sel;
bool dir_sel;
gamepad_state controller;
} gamepad_context;
static gamepad_context ctx = {0};
void gamepad_init();
bool gamepad_button_sel(){
return ctx.button_sel;
}
bool gamepad_dir_sel(){
return ctx.dir_sel;
}
void gamepad_set_sel(u8 value){
ctx.button_sel = value & 0x20;
ctx.dir_sel = value & 0x10;
}
gamepad_state *gamepad_get_state(){
return &ctx.controller;
}
u8 gamepad_get_output() {
u8 output = 0xCF;
if(!gamepad_button_sel()) {
if (gamepad_get_state()->start) {
output &= ~(1 << 3);
}
if (gamepad_get_state()->select) {
output &= ~(1 << 2);
}
if (gamepad_get_state()->a) {
output &= ~(1 << 0);
}
if (gamepad_get_state()->b) {
output &= ~(1 << 1);
}
}
if(!gamepad_dir_sel()) {
if (gamepad_get_state()->left) {
output &= ~(1 << 1);
}
if (gamepad_get_state()->right) {
output &= ~(1 << 0);
}
if (gamepad_get_state()->up) {
output &= ~(1 << 2);
}
if (gamepad_get_state()->down) {
output &= ~(1 << 3);
}
}
return output;
}

View File

@ -3,10 +3,14 @@
#include <cpu.h>
#include <dma.h>
#include <lcd.h>
#include <gamepad.h>
static char serial_data[2];
u8 io_read(u16 address){
if(address == 0xFF00) {
return gamepad_get_output();
}
if(address == 0xFF01) {
return serial_data[0];
}
@ -27,14 +31,24 @@ u8 io_read(u16 address){
return cpu_get_int_flags();
}
if(BETWEEN(address, 0xFF10, 0xFF3F)) {
//ignore sound
return 0;
}
printf("UNSUPPORTED io_read(%04X)\n", address);
return 0;
}
void io_write(u16 address, u8 value){
if(address == 0xFF00) {
gamepad_set_sel(value);
return;
}
if(address == 0xFF01) {
serial_data[0] = value;
printf("%c", value);
return;
}
@ -58,6 +72,11 @@ void io_write(u16 address, u8 value){
return;
}
if(BETWEEN(address, 0xFF10, 0xFF3F)) {
//ignore sound
return;
}
printf("UNSUPPORTED io_write(%04X)\n", address);
}

View File

@ -3,6 +3,9 @@
#include <string.h>
#include <ppu_sm.h>
void pipeline_fifo_reset();
void pipeline_process();
static ppu_context ctx;
ppu_context *ppu_get_context() {
@ -14,6 +17,17 @@ void ppu_init() {
ctx.line_ticks = 0;
ctx.video_buffer = malloc(YRES * XRES * sizeof(u32));
ctx.pfc.line_x = 0;
ctx.pfc.pushed_x = 0;
ctx.pfc.fetch_x = 0;
ctx.pfc.pixel_fifo.size = 0;
ctx.pfc.pixel_fifo.head = ctx.pfc.pixel_fifo.tail = NULL;
ctx.pfc.cur_fetch_state = FS_TILE;
ctx.line_sprites = 0;
ctx.fetched_entry_count = 0;
ctx.window_line = 0;
lcd_init();
LCDS_MODE_SET(MODE_OAM);

271
lib/ppu_pipeline.c Normal file
View File

@ -0,0 +1,271 @@
#include <ppu.h>
#include <lcd.h>
#include <bus.h>
bool window_visible() {
return LCDC_WIN_ENABLE && lcd_get_context()->win_x >= 0 &&
lcd_get_context()->win_x <= 166 && lcd_get_context()->win_y >= 0 &&
lcd_get_context()->win_y < YRES;
}
void pixel_fifo_push(u32 value) {
fifo_entry *next = malloc(sizeof(fifo_entry));
next->next = NULL;
next->value = value;
if(!ppu_get_context()->pfc.pixel_fifo.head) {
//first entry
ppu_get_context()->pfc.pixel_fifo.head = ppu_get_context()->pfc.pixel_fifo.tail = next;
} else {
ppu_get_context()->pfc.pixel_fifo.tail->next = next;
ppu_get_context()->pfc.pixel_fifo.tail = next;
}
ppu_get_context()->pfc.pixel_fifo.size++;
}
u32 pixel_fifo_pop() {
if(ppu_get_context()->pfc.pixel_fifo.size <= 0) {
fprintf(stderr, "ERR in pixel FIFO\n");
}
fifo_entry *popped = ppu_get_context()->pfc.pixel_fifo.head;
ppu_get_context()->pfc.pixel_fifo.head = popped->next;
ppu_get_context()->pfc.pixel_fifo.size--;
u32 val = popped->value;
free(popped);
return val;
}
u32 fetch_sprite_pixels(int bit, u32 color, u8 bg_color) {
for (int i = 0; i < ppu_get_context()->fetched_entry_count; i++) {
int sp_x = (ppu_get_context()->fetched_entries[i].x - 8) +
((lcd_get_context()->scroll_x % 8));
if (sp_x + 8 < ppu_get_context()->pfc.fifo_x) {
//past this pixel already
continue;
}
int offset = ppu_get_context()->pfc.fifo_x - sp_x;
if (offset < 0 || offset > 7) {
continue;
}
bit = 7 - offset;
if (ppu_get_context()->fetched_entries[i].f_x_flip) {
bit = offset;
}
u8 lo = !!(ppu_get_context()->pfc.fetch_entry_data[i * 2] & (1 << bit));
u8 hi = !!(ppu_get_context()->pfc.fetch_entry_data[(i * 2)+1] & (1 << bit)) << 1;
bool bg_priority = ppu_get_context()->fetched_entries[i].f_bgp;
if (!(hi|lo)) {
//transparant
continue;
}
if(!bg_priority || bg_color == 0) {
color = (ppu_get_context()->fetched_entries[i].f_pn) ?
lcd_get_context()->sp2_colors[hi|lo] : lcd_get_context()->sp1_colors[hi|lo];
if(hi | lo) {
break;
}
}
}
return color;
}
bool pipeline_fifo_add() {
if(ppu_get_context()->pfc.pixel_fifo.size > 8) {
//fifo is full!
return false;
}
int x = ppu_get_context()->pfc.fetch_x - (8 - lcd_get_context()->scroll_x % 8);
for (int i=0; i<8; i++) {
int bit = 7 - i;
u8 lo = !!(ppu_get_context()->pfc.bgw_fetch_data[1] & (1 << bit));
u8 hi = !!(ppu_get_context()->pfc.bgw_fetch_data[2] & (1 << bit)) << 1;
u32 color = lcd_get_context()->bg_colors[hi | lo];
if (!LCDC_BGW_ENABLE) {
color = lcd_get_context()->bg_colors[0];
}
if(LCDC_OBJ_ENABLE) {
color = fetch_sprite_pixels(bit, color, hi | lo);
}
if (x >= 0) {
pixel_fifo_push(color);
ppu_get_context()->pfc.fifo_x++;
}
}
return true;
}
void pipeline_load_sprite_tile() {
oam_line_entry *le = ppu_get_context()->line_sprites;
while(le) {
int sp_x = (le->entry.x - 8) + (lcd_get_context()->scroll_x % 8);
if ((sp_x >= ppu_get_context()->pfc.fetch_x && sp_x < ppu_get_context()->pfc.fetch_x + 8) ||
((sp_x + 8) >= ppu_get_context()->pfc.fetch_x && (sp_x + 8) < ppu_get_context()->pfc.fetch_x + 8)) {
ppu_get_context()->fetched_entries[ppu_get_context()->fetched_entry_count++] = le->entry;
}
le = le->next;
if (!le || ppu_get_context()->fetched_entry_count >= 3) {
//max checking 3 sprites on pixels
break;
}
}
}
void pipeline_load_sprite_data(u8 offset) {
int cur_y = lcd_get_context()->ly;
u8 sprite_height = LCDC_OBJ_HEIGHT;
for (int i = 0; i < ppu_get_context()->fetched_entry_count; i++) {
u8 ty = ((cur_y + 16) - ppu_get_context()->fetched_entries[i].y) * 2;
if (ppu_get_context()->fetched_entries[i].f_y_flip) {
ty = ((sprite_height * 2) - 2) - ty;
}
u8 tile_index = ppu_get_context()->fetched_entries[i].tile;
if (sprite_height == 16) {
tile_index &= ~(1);
}
ppu_get_context()->pfc.fetch_entry_data[(i * 2) + offset] =
bus_read(0x8000 + (tile_index * 16) + ty + offset);
}
}
void pipeline_load_window_tile() {
if(!window_visible())
return;
u8 window_y = lcd_get_context()->win_y;
if (ppu_get_context()->pfc.fetch_x + 7 >= lcd_get_context()->win_x &&
ppu_get_context()->pfc.fetch_x + 7 < lcd_get_context()->win_x + YRES + 14) {
if(lcd_get_context()->ly >= window_y && lcd_get_context()->ly < window_y + XRES) {
u8 w_tile_y = ppu_get_context()->window_line / 8;
ppu_get_context()->pfc.bgw_fetch_data[0] = bus_read(LCDC_WIN_MAP_AREA +
((ppu_get_context()->pfc.fetch_x + 7 - lcd_get_context()->win_x) / 8) +
(w_tile_y * 32));
if(LCDC_BGW_DATA_AREA == 0x8800){
ppu_get_context()->pfc.bgw_fetch_data[0] += 128;
}
}
}
}
void pipeline_fetch() {
switch(ppu_get_context()->pfc.cur_fetch_state) {
case FS_TILE: {
ppu_get_context()->fetched_entry_count = 0;
if (LCDC_BGW_ENABLE) {
ppu_get_context()->pfc.bgw_fetch_data[0] = bus_read(LCDC_BG_MAP_AREA +
(ppu_get_context()->pfc.map_x / 8) +
(((ppu_get_context()->pfc.map_y / 8)) * 32));
if(LCDC_BGW_DATA_AREA == 0x8800) {
ppu_get_context()->pfc.bgw_fetch_data[0] += 128;
}
pipeline_load_window_tile();
}
if(LCDC_OBJ_ENABLE && ppu_get_context()->line_sprite_count) {
pipeline_load_sprite_tile();
}
ppu_get_context()->pfc.cur_fetch_state = FS_DATA0;
ppu_get_context()->pfc.fetch_x += 8;
} break;
case FS_DATA0: {
ppu_get_context()->pfc.bgw_fetch_data[1] = bus_read(LCDC_BGW_DATA_AREA +
(ppu_get_context()->pfc.bgw_fetch_data[0] * 16) +
ppu_get_context()->pfc.tile_y);
pipeline_load_sprite_data(0);
ppu_get_context()->pfc.cur_fetch_state = FS_DATA1;
} break;
case FS_DATA1: {
ppu_get_context()->pfc.bgw_fetch_data[2] = bus_read(LCDC_BGW_DATA_AREA +
(ppu_get_context()->pfc.bgw_fetch_data[0] * 16) +
ppu_get_context()->pfc.tile_y + 1);
pipeline_load_sprite_data(1);
ppu_get_context()->pfc.cur_fetch_state = FS_IDLE;
} break;
case FS_IDLE: {
ppu_get_context()->pfc.cur_fetch_state = FS_PUSH;
} break;
case FS_PUSH: {
if (pipeline_fifo_add()) {
ppu_get_context()->pfc.cur_fetch_state = FS_TILE;
}
} break;
}
}
void pipeline_push_pixel() {
if (ppu_get_context()->pfc.pixel_fifo.size > 8) {
u32 pixel_data = pixel_fifo_pop();
if(ppu_get_context()->pfc.line_x >= (lcd_get_context()->scroll_x % 8)) {
ppu_get_context()->video_buffer[ppu_get_context()->pfc.pushed_x +
(lcd_get_context()->ly * XRES)] = pixel_data;
ppu_get_context()->pfc.pushed_x++;
}
ppu_get_context()->pfc.line_x++;
}
}
void pipeline_process() {
ppu_get_context()->pfc.map_y = (lcd_get_context()->ly + lcd_get_context()->scroll_y);
ppu_get_context()->pfc.map_x = (ppu_get_context()->pfc.fetch_x + lcd_get_context()->scroll_x);
ppu_get_context()->pfc.tile_y = ((lcd_get_context()->ly + lcd_get_context()->scroll_y) % 8) * 2;
if(!(ppu_get_context()->line_ticks & 1)) {
pipeline_fetch();
}
pipeline_push_pixel();
}
void pipeline_fifo_reset() {
while(ppu_get_context()->pfc.pixel_fifo.size) {
pixel_fifo_pop();
}
ppu_get_context()->pfc.pixel_fifo.head = 0;
}

View File

@ -2,8 +2,17 @@
#include <lcd.h>
#include <cpu.h>
#include <interrupts.h>
#include <string.h>
#include <cart.h>
void pipeline_fifo_reset();
void pipeline_process();
void increment_ly() {
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++;
}
lcd_get_context()->ly++;
if(lcd_get_context()->ly == lcd_get_context()->ly_compare) {
@ -17,15 +26,93 @@ void increment_ly() {
}
}
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;
}
}
}
}
void ppu_mode_oam() {
if(ppu_get_context()->line_ticks >= 80) {
LCDS_MODE_SET(MODE_XFER);
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();
}
}
void ppu_mode_xfer() {
if(ppu_get_context()->line_ticks >= 80 + 172) {
pipeline_process();
if(ppu_get_context()->pfc.pushed_x >= XRES) {
pipeline_fifo_reset();
LCDS_MODE_SET(MODE_HBLANK);
if(LCDS_STAT_INT(SS_HBLANK)) {
cpu_request_interrupt(IT_LCD_STAT);
}
}
}
@ -36,13 +123,14 @@ void ppu_mode_vblank() {
if(lcd_get_context()->ly >= LINES_PER_FRAME) {
LCDS_MODE_SET(MODE_OAM);
lcd_get_context()->ly = 0;
ppu_get_context()->window_line = 0;
}
ppu_get_context()->line_ticks = 0;
}
}
static u32 target_frame_time = 1000/60;
u32 target_frame_time = 1000/60;
static long prev_frame_time = 0;
static long start_timer = 0;
static long frame_count = 0;
@ -75,6 +163,9 @@ void ppu_mode_hblank() {
start_timer = end;
frame_count = 0;
printf("FPS: %ld\n", fps);
if(cart_need_save()){
cart_battery_save();
}
}
frame_count++;

View File

@ -1,5 +1,7 @@
#include <ui.h>
#include <emu.h>
#include <ppu.h>
#include <gamepad.h>
#include <SDL.h>
#include <SDL_ttf.h>
@ -23,6 +25,19 @@ void ui_init(){
printf("TTF INIT\n");
SDL_CreateWindowAndRenderer(SCREEN_WIDTH, SCREEN_HEIGHT, 0, &sdlWindow, &sdlRenderer);
screen = SDL_CreateRGBSurface(0, SCREEN_WIDTH,
SCREEN_HEIGHT, 32,
0x00FF0000,
0x0000FF00,
0x000000FF,
0xFF000000);
sdlTexture = SDL_CreateTexture(sdlRenderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
SCREEN_WIDTH,
SCREEN_HEIGHT);
SDL_CreateWindowAndRenderer(16 * 8 * scale, 32 * 8 * scale, 0, &sdlDebugWindow, &sdlDebugRenderer);
debugScreen = SDL_CreateRGBSurface(0, (16 * 8 * scale) + (16 * scale),
(32 * 8 * scale) + (64 * scale), 32,
@ -95,15 +110,73 @@ void update_debug_window() {
}
void ui_update() {
SDL_Rect rc;
rc.x = rc.y = 0;
rc.w = rc.h = 2048;
u32 *video_buffer = ppu_get_context()->video_buffer;
for(int line_num = 0; line_num < YRES; line_num++) {
for(int x = 0; x < XRES; x++) {
rc.x = x * scale;
rc.y = line_num * scale;
rc.w = scale;
rc.h = scale;
SDL_FillRect(screen, &rc, video_buffer[x + (line_num * XRES)]);
}
}
SDL_UpdateTexture(sdlTexture, NULL, screen->pixels, screen->pitch);
SDL_RenderClear(sdlRenderer);
SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);
SDL_RenderPresent(sdlRenderer);
update_debug_window();
}
static bool ff = false;
void ui_on_key(bool down, u32 key_code) {
if(key_code == SDLK_SPACE && down == true) {
ff = !ff;
if(ff){
target_frame_time = 1000/300;
} else {
target_frame_time = 1000/60;
}
printf("target frame time: %d\n", target_frame_time);
}
switch(key_code){
case SDLK_z: gamepad_get_state()->b = down; break;
case SDLK_x: gamepad_get_state()->a = down; break;
case SDLK_RETURN: gamepad_get_state()->start = down; break;
case SDLK_TAB: gamepad_get_state()->select = down; break;
case SDLK_UP: gamepad_get_state()->up = down; break;
case SDLK_DOWN: gamepad_get_state()->down = down; break;
case SDLK_LEFT: gamepad_get_state()->left = down; break;
case SDLK_RIGHT: gamepad_get_state()->right = down; break;
}
}
void ui_handle_events(){
SDL_Event e;
while(SDL_PollEvent(&e) > 0) {
if(e.type == SDL_KEYDOWN) {
ui_on_key(true, e.key.keysym.sym);
}
if(e.type == SDL_KEYUP) {
ui_on_key(false, e.key.keysym.sym);
}
if(e.type == SDL_WINDOWEVENT && e.window.event == SDL_WINDOWEVENT_CLOSE) {
emu_get_context()->die = true;
}
}
}

BIN
roms/01-read_timing.gb Normal file

Binary file not shown.

BIN
roms/02-write_timing.gb Normal file

Binary file not shown.

BIN
roms/03-modify_timing.gb Normal file

Binary file not shown.

BIN
roms/instr_timing.gb Normal file

Binary file not shown.