Finish ppu, and mbc1 mapping
This commit is contained in:
parent
2603916972
commit
89a99b160d
@ -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
22
include/gamepad.h
Normal 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();
|
@ -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)
|
||||
|
||||
|
@ -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;
|
||||
|
160
lib/cart.c
160
lib/cart.c
@ -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);
|
||||
|
||||
}
|
@ -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:
|
||||
|
@ -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
65
lib/gamepad.c
Normal 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;
|
||||
}
|
19
lib/io.c
19
lib/io.c
@ -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);
|
||||
}
|
||||
|
||||
|
14
lib/ppu.c
14
lib/ppu.c
@ -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
271
lib/ppu_pipeline.c
Normal 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;
|
||||
}
|
95
lib/ppu_sm.c
95
lib/ppu_sm.c
@ -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++;
|
||||
|
73
lib/ui.c
73
lib/ui.c
@ -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
BIN
roms/01-read_timing.gb
Normal file
Binary file not shown.
BIN
roms/02-write_timing.gb
Normal file
BIN
roms/02-write_timing.gb
Normal file
Binary file not shown.
BIN
roms/03-modify_timing.gb
Normal file
BIN
roms/03-modify_timing.gb
Normal file
Binary file not shown.
BIN
roms/instr_timing.gb
Normal file
BIN
roms/instr_timing.gb
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user