Simplify DMA code, fix DMA read timing
This commit is contained in:
parent
b1187919d3
commit
c4a14ac4db
@ -470,7 +470,7 @@ static void add_object_from_index(GB_gameboy_t *gb, unsigned index)
|
||||
if (gb->n_visible_objs == 10) return;
|
||||
|
||||
/* TODO: It appears that DMA blocks PPU access to OAM, but it needs verification. */
|
||||
if (gb->dma_steps_left && (gb->dma_cycles >= 0 || gb->is_dma_restarting)) {
|
||||
if (GB_is_dma_active(gb)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1215,7 +1215,7 @@ static inline uint16_t mode3_batching_length(GB_gameboy_t *gb)
|
||||
{
|
||||
if (gb->model & GB_MODEL_NO_SFC_BIT) return 0;
|
||||
if (gb->hdma_on) return 0;
|
||||
if (gb->dma_steps_left) return 0;
|
||||
if (GB_is_dma_active(gb)) return 0;
|
||||
if (gb->wy_triggered && (gb->io_registers[GB_IO_LCDC] & 0x20) && (gb->io_registers[GB_IO_WX] < 8 || gb->io_registers[GB_IO_WX] == 166)) {
|
||||
return 0;
|
||||
}
|
||||
@ -1406,7 +1406,7 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force)
|
||||
GB_STAT_update(gb);
|
||||
gb->n_visible_objs = 0;
|
||||
|
||||
if (!gb->dma_steps_left && !gb->oam_ppu_blocked) {
|
||||
if (!GB_is_dma_active(gb) && !gb->oam_ppu_blocked) {
|
||||
GB_BATCHPOINT(gb, display, 5, 80);
|
||||
}
|
||||
for (gb->oam_search_index = 0; gb->oam_search_index < 40; gb->oam_search_index++) {
|
||||
|
@ -1639,8 +1639,8 @@ void GB_reset(GB_gameboy_t *gb)
|
||||
gb->io_registers[GB_IO_DMA] = gb->io_registers[GB_IO_OBP0] = gb->io_registers[GB_IO_OBP1] = GB_is_cgb(gb)? 0x00 : 0xFF;
|
||||
|
||||
gb->accessed_oam_row = -1;
|
||||
|
||||
|
||||
gb->dma_current_dest = 0xa1;
|
||||
|
||||
if (GB_is_hle_sgb(gb)) {
|
||||
if (!gb->sgb) {
|
||||
gb->sgb = malloc(sizeof(*gb->sgb));
|
||||
|
@ -431,13 +431,11 @@ struct GB_gameboy_internal_s {
|
||||
int16_t hdma_cycles; // in 8MHz units
|
||||
uint16_t hdma_current_src, hdma_current_dest;
|
||||
|
||||
uint8_t dma_steps_left;
|
||||
uint8_t dma_current_dest;
|
||||
uint8_t last_dma_read;
|
||||
uint16_t dma_current_src;
|
||||
int16_t dma_cycles;
|
||||
bool is_dma_restarting;
|
||||
uint8_t dma_and_pattern;
|
||||
bool dma_skip_write;
|
||||
uint8_t last_opcode_read; /* Required to emulte HDMA reads from Exxx */
|
||||
bool hdma_starting;
|
||||
);
|
||||
|
@ -251,7 +251,7 @@ void GB_trigger_oam_bug_read(GB_gameboy_t *gb, uint16_t address)
|
||||
|
||||
static bool is_addr_in_dma_use(GB_gameboy_t *gb, uint16_t addr)
|
||||
{
|
||||
if (!gb->dma_steps_left || (gb->dma_cycles < 0 && !gb->is_dma_restarting) || addr >= 0xfe00) return false;
|
||||
if (!GB_is_dma_active(gb) || addr >= 0xfe00) return false;
|
||||
if (addr >= 0xfe00) return false;
|
||||
if (gb->dma_current_src == addr) return false; // Shortcut for DMA access flow
|
||||
if (gb->dma_current_src > 0xe000 && (gb->dma_current_src & ~0x2000) == addr) return false;
|
||||
@ -482,7 +482,7 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
if ((gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) {
|
||||
if (GB_is_dma_active(gb)) {
|
||||
/* Todo: Does reading from OAM during DMA causes the OAM bug? */
|
||||
return 0xff;
|
||||
}
|
||||
@ -746,14 +746,14 @@ uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr)
|
||||
|
||||
if (GB_is_cgb(gb) && bus_for_addr(gb, gb->dma_current_src) != GB_BUS_RAM && addr >= 0xc000) {
|
||||
// TODO: this should probably affect the DMA dest as well
|
||||
addr = (gb->dma_current_src & 0x1000) | (addr & 0xFFF) | 0xC000;
|
||||
addr = ((gb->dma_current_src - 1) & 0x1000) | (addr & 0xFFF) | 0xC000;
|
||||
}
|
||||
else if (GB_is_cgb(gb) && gb->dma_current_src >= 0xe000 && addr > 0xc000) {
|
||||
// TODO: this should probably affect the DMA dest as well
|
||||
addr = (gb->dma_current_src & 0x1000) | (addr & 0xFFF) | 0xC000;
|
||||
addr = ((gb->dma_current_src - 1) & 0x1000) | (addr & 0xFFF) | 0xC000;
|
||||
}
|
||||
else {
|
||||
addr = gb->dma_current_src;
|
||||
addr = (gb->dma_current_src - 1);
|
||||
}
|
||||
}
|
||||
uint8_t data = read_map[addr >> 12](gb, addr);
|
||||
@ -1209,7 +1209,7 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
return;
|
||||
}
|
||||
|
||||
if ((gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) {
|
||||
if (GB_is_dma_active(gb)) {
|
||||
/* Todo: Does writing to OAM during DMA causes the OAM bug? */
|
||||
return;
|
||||
}
|
||||
@ -1459,18 +1459,16 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
return;
|
||||
|
||||
case GB_IO_DMA:
|
||||
if (gb->dma_steps_left) {
|
||||
if (GB_is_dma_active(gb)) {
|
||||
/* This is not correct emulation, since we're not really delaying the second DMA.
|
||||
One write that should have happened in the first DMA will not happen. However,
|
||||
since that byte will be overwritten by the second DMA before it can actually be
|
||||
read, it doesn't actually matter. */
|
||||
gb->is_dma_restarting = true;
|
||||
}
|
||||
gb->dma_and_pattern = 0xFF;
|
||||
gb->dma_cycles = -7;
|
||||
gb->dma_cycles = -3;
|
||||
gb->dma_current_dest = 0;
|
||||
gb->dma_current_src = value << 8;
|
||||
gb->dma_steps_left = 0xa0;
|
||||
gb->io_registers[GB_IO_DMA] = value;
|
||||
return;
|
||||
case GB_IO_SVBK:
|
||||
@ -1663,23 +1661,24 @@ void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
|
||||
if (GB_is_cgb(gb) && bus_for_addr(gb, gb->dma_current_src) != GB_BUS_RAM && addr >= 0xc000) {
|
||||
// TODO: this should probably affect the DMA dest as well
|
||||
addr = (gb->dma_current_src & 0x1000) | (addr & 0xFFF) | 0xC000;
|
||||
addr = ((gb->dma_current_src - 1) & 0x1000) | (addr & 0xFFF) | 0xC000;
|
||||
}
|
||||
else if (GB_is_cgb(gb) && gb->dma_current_src >= 0xe000 && addr > 0xc000) {
|
||||
// TODO: this should probably affect the DMA dest as well
|
||||
addr = (gb->dma_current_src & 0x1000) | (addr & 0xFFF) | 0xC000;
|
||||
addr = ((gb->dma_current_src - 1) & 0x1000) | (addr & 0xFFF) | 0xC000;
|
||||
}
|
||||
else {
|
||||
addr = gb->dma_current_src;
|
||||
addr = (gb->dma_current_src - 1);
|
||||
}
|
||||
if (GB_is_cgb(gb) || addr > 0xc000) {
|
||||
gb->dma_and_pattern = addr < 0xc000? 0x00 : 0xFF;
|
||||
if ((gb->model < GB_MODEL_CGB_0 || gb->model == GB_MODEL_CGB_B) && addr > 0xc000) {
|
||||
gb->dma_and_pattern = value;
|
||||
if (addr < 0xc000) {
|
||||
gb->oam[gb->dma_current_dest - 1] = 0;
|
||||
}
|
||||
else if ((gb->model < GB_MODEL_CGB_C || gb->model > GB_MODEL_CGB_E) && addr > 0xc000 && !oam_write) {
|
||||
gb->dma_skip_write = true;
|
||||
gb->oam[gb->dma_current_dest] = value;
|
||||
else if ((gb->model < GB_MODEL_CGB_0 || gb->model == GB_MODEL_CGB_B)) {
|
||||
gb->oam[gb->dma_current_dest - 1] &= value;
|
||||
}
|
||||
else if ((gb->model < GB_MODEL_CGB_C || gb->model > GB_MODEL_CGB_E) && !oam_write) {
|
||||
gb->oam[gb->dma_current_dest - 1] = value;
|
||||
}
|
||||
if (gb->model < GB_MODEL_CGB_E || addr >= 0xc000) return;
|
||||
}
|
||||
@ -1687,34 +1686,38 @@ void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
write_map[addr >> 12](gb, addr, value);
|
||||
}
|
||||
|
||||
bool GB_is_dma_active(GB_gameboy_t *gb)
|
||||
{
|
||||
return gb->dma_current_dest < 0xa1 || gb->is_dma_restarting;
|
||||
}
|
||||
|
||||
void GB_dma_run(GB_gameboy_t *gb)
|
||||
{
|
||||
while (unlikely(gb->dma_cycles >= 4 && gb->dma_steps_left)) {
|
||||
/* Todo: measure this value */
|
||||
if (gb->dma_current_dest >= 0xa1) return;
|
||||
while (unlikely(gb->dma_cycles >= 4)) {
|
||||
gb->dma_cycles -= 4;
|
||||
gb->dma_steps_left--;
|
||||
if (gb->dma_skip_write) {
|
||||
gb->dma_skip_write = false;
|
||||
gb->dma_current_dest++;
|
||||
if (gb->dma_current_dest >= 0xa1) {
|
||||
gb->is_dma_restarting = false;
|
||||
break;
|
||||
}
|
||||
else if (gb->dma_current_src < 0xe000) {
|
||||
gb->oam[gb->dma_current_dest++] = GB_read_memory(gb, gb->dma_current_src) & gb->dma_and_pattern;
|
||||
if (gb->dma_current_dest >= 0xa0) {
|
||||
gb->dma_current_dest++;
|
||||
continue;
|
||||
}
|
||||
if (gb->dma_current_src < 0xe000) {
|
||||
gb->oam[gb->dma_current_dest++] = GB_read_memory(gb, gb->dma_current_src);
|
||||
}
|
||||
else {
|
||||
if (GB_is_cgb(gb)) {
|
||||
gb->oam[gb->dma_current_dest++] = gb->dma_and_pattern;
|
||||
gb->oam[gb->dma_current_dest++] = 0;
|
||||
}
|
||||
else {
|
||||
gb->oam[gb->dma_current_dest++] = GB_read_memory(gb, gb->dma_current_src & ~0x2000) & gb->dma_and_pattern;
|
||||
gb->oam[gb->dma_current_dest++] = GB_read_memory(gb, gb->dma_current_src & ~0x2000);
|
||||
}
|
||||
}
|
||||
gb->dma_and_pattern = 0xFF;
|
||||
|
||||
/* dma_current_src must be the correct value during GB_read_memory */
|
||||
gb->dma_current_src++;
|
||||
if (!gb->dma_steps_left) {
|
||||
gb->is_dma_restarting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ uint8_t GB_safe_read_memory(GB_gameboy_t *gb, uint16_t addr); // Without side ef
|
||||
void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value);
|
||||
#ifdef GB_INTERNAL
|
||||
internal void GB_dma_run(GB_gameboy_t *gb);
|
||||
internal bool GB_is_dma_active(GB_gameboy_t *gb);
|
||||
internal void GB_hdma_run(GB_gameboy_t *gb);
|
||||
internal void GB_trigger_oam_bug(GB_gameboy_t *gb, uint16_t address);
|
||||
#endif
|
||||
|
@ -372,11 +372,6 @@ static void sanitize_state(GB_gameboy_t *gb)
|
||||
if (!GB_is_cgb(gb)) {
|
||||
gb->current_tile_attributes = 0;
|
||||
}
|
||||
|
||||
if ((unsigned)gb->dma_current_dest + (unsigned)gb->dma_steps_left >= 0xa0) {
|
||||
gb->dma_current_dest = 0;
|
||||
gb->dma_steps_left = 0;
|
||||
}
|
||||
|
||||
gb->object_low_line_address &= gb->vram_size & ~1;
|
||||
if (gb->lcd_x > gb->position_in_line) {
|
||||
|
Loading…
Reference in New Issue
Block a user