Halt during DMA with objects
This commit is contained in:
parent
0ab7bf7749
commit
aa5a279116
@ -465,34 +465,55 @@ void GB_lcd_off(GB_gameboy_t *gb)
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint8_t oam_read(GB_gameboy_t *gb, uint8_t addr)
|
||||
{
|
||||
if (unlikely(gb->oam_ppu_blocked)) {
|
||||
return 0xFF;
|
||||
}
|
||||
if (unlikely(gb->dma_current_dest <= 0xa0 && gb->dma_current_dest > 0)) { // TODO: what happens in the last and first M cycles?
|
||||
return gb->oam[((gb->dma_current_dest - 1 + (gb->halted || gb->stopped)) & ~1) | (addr & 1)];
|
||||
}
|
||||
return gb->oam[addr];
|
||||
}
|
||||
|
||||
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_is_dma_active(gb)) {
|
||||
if (unlikely(GB_is_dma_active(gb))) {
|
||||
if (!gb->halted && !gb->stopped) {
|
||||
return;
|
||||
}
|
||||
if (gb->model < GB_MODEL_CGB_E) {
|
||||
return;
|
||||
}
|
||||
/* CGB-0 to CGB-D: Halted DMA still blocks Mode 2;
|
||||
Pre-CGB: Unit specific behavior, some units read FFs, some units read using
|
||||
several different corruption pattterns. For simplicity, we emulate
|
||||
FFs. */
|
||||
}
|
||||
|
||||
if (gb->oam_ppu_blocked) {
|
||||
if (unlikely(gb->oam_ppu_blocked)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* This reverse sorts the visible objects by location and priority */
|
||||
object_t *objects = (object_t *) &gb->oam;
|
||||
uint8_t oam_y = oam_read(gb, index * 4);
|
||||
uint8_t oam_x = oam_read(gb, index * 4) + 1;
|
||||
bool height_16 = (gb->io_registers[GB_IO_LCDC] & 4) != 0;
|
||||
signed y = objects[index].y - 16;
|
||||
signed y = oam_y - 16;
|
||||
if (y <= gb->current_line && y + (height_16? 16 : 8) > gb->current_line) {
|
||||
unsigned j = 0;
|
||||
for (; j < gb->n_visible_objs; j++) {
|
||||
if (gb->objects_x[j] <= objects[index].x) break;
|
||||
if (gb->objects_x[j] <= oam_x) break;
|
||||
}
|
||||
memmove(gb->visible_objs + j + 1, gb->visible_objs + j, gb->n_visible_objs - j);
|
||||
memmove(gb->objects_x + j + 1, gb->objects_x + j, gb->n_visible_objs - j);
|
||||
memmove(gb->objects_y + j + 1, gb->objects_y + j, gb->n_visible_objs - j);
|
||||
gb->visible_objs[j] = index;
|
||||
gb->objects_x[j] = objects[index].x;
|
||||
gb->objects_y[j] = objects[index].y;
|
||||
gb->objects_x[j] = oam_y;
|
||||
gb->objects_y[j] = oam_y;
|
||||
gb->n_visible_objs++;
|
||||
}
|
||||
}
|
||||
@ -680,24 +701,25 @@ static inline uint8_t vram_read(GB_gameboy_t *gb, uint16_t addr)
|
||||
/* TODO: AGS has its own, very different pattern, but AGS is not currently a supported model */
|
||||
/* TODO: Research this when researching odd modes */
|
||||
/* TODO: probably not 100% on the first few reads during halt/stop modes*/
|
||||
unsigned offset = 1 - (gb->halted || gb->stopped);
|
||||
if (GB_is_cgb(gb)) {
|
||||
if (gb->dma_ppu_vram_conflict) {
|
||||
addr = (gb->dma_ppu_vram_conflict_addr & 0x1FFF) | (addr & 0x2000);
|
||||
}
|
||||
else if (gb->dma_cycles_modulo && !gb->halted && !gb->stopped) {
|
||||
addr &= 0x2000;
|
||||
addr |= ((gb->dma_current_src - 1) & 0x1FFF);
|
||||
addr |= ((gb->dma_current_src - offset) & 0x1FFF);
|
||||
}
|
||||
else {
|
||||
addr &= 0x2000 | ((gb->dma_current_src - 1) & 0x1FFF);
|
||||
addr &= 0x2000 | ((gb->dma_current_src - offset) & 0x1FFF);
|
||||
gb->dma_ppu_vram_conflict_addr = addr;
|
||||
gb->dma_ppu_vram_conflict = !gb->halted && !gb->stopped;
|
||||
}
|
||||
}
|
||||
else {
|
||||
addr |= ((gb->dma_current_src - 1) & 0x1FFF);
|
||||
addr |= ((gb->dma_current_src - offset) & 0x1FFF);
|
||||
}
|
||||
gb->oam[gb->dma_current_dest - 1] = gb->vram[(addr & 0x1FFF) | (gb->cgb_vram_bank? 0x2000 : 0)];
|
||||
gb->oam[gb->dma_current_dest - offset] = gb->vram[(addr & 0x1FFF) | (gb->cgb_vram_bank? 0x2000 : 0)];
|
||||
}
|
||||
return gb->vram[addr];
|
||||
}
|
||||
@ -875,17 +897,6 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb, unsigned *cycles)
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint8_t oam_read(GB_gameboy_t *gb, uint8_t addr)
|
||||
{
|
||||
if (unlikely(gb->oam_ppu_blocked)) {
|
||||
return 0xFF;
|
||||
}
|
||||
if (unlikely(gb->dma_current_dest <= 0xa0 && gb->dma_current_dest > 0)) { // TODO: what happens in the last and first M cycles?
|
||||
return gb->oam[((gb->dma_current_dest - 1) & ~1) | (addr & 1)];
|
||||
}
|
||||
return gb->oam[addr];
|
||||
}
|
||||
|
||||
static uint16_t get_object_line_address(GB_gameboy_t *gb, uint8_t y, uint8_t tile, uint8_t flags)
|
||||
{
|
||||
bool height_16 = (gb->io_registers[GB_IO_LCDC] & 4) != 0; /* Todo: Which T-cycle actually reads this? */
|
||||
|
@ -354,6 +354,9 @@ static void enter_stop_mode(GB_gameboy_t *gb)
|
||||
static void leave_stop_mode(GB_gameboy_t *gb)
|
||||
{
|
||||
gb->stopped = false;
|
||||
// TODO: verify this
|
||||
gb->dma_cycles = 4;
|
||||
GB_dma_run(gb);
|
||||
gb->oam_ppu_blocked = false;
|
||||
gb->vram_ppu_blocked = false;
|
||||
gb->cgb_palettes_ppu_blocked = false;
|
||||
@ -373,8 +376,6 @@ static void stop(GB_gameboy_t *gb, uint8_t opcode)
|
||||
// When entering with IF&IE, the 2nd byte of STOP is actually executed
|
||||
if (!exit_by_joyp) {
|
||||
if (!immediate_exit) {
|
||||
// TODO: verify this!
|
||||
gb->dma_cycles = 4;
|
||||
GB_dma_run(gb);
|
||||
}
|
||||
enter_stop_mode(gb);
|
||||
@ -419,8 +420,6 @@ static void stop(GB_gameboy_t *gb, uint8_t opcode)
|
||||
if (immediate_exit) {
|
||||
leave_stop_mode(gb);
|
||||
if (!interrupt_pending) {
|
||||
// TODO: verify this!
|
||||
gb->dma_cycles = 4;
|
||||
GB_dma_run(gb);
|
||||
gb->halted = true;
|
||||
gb->just_halted = true;
|
||||
@ -1024,9 +1023,6 @@ static void halt(GB_gameboy_t *gb, uint8_t opcode)
|
||||
}
|
||||
}
|
||||
else {
|
||||
// TODO: verify the timing!
|
||||
gb->dma_cycles = 4;
|
||||
GB_dma_run(gb);
|
||||
gb->halted = true;
|
||||
}
|
||||
gb->just_halted = true;
|
||||
@ -1625,12 +1621,17 @@ void GB_cpu_run(GB_gameboy_t *gb)
|
||||
/* Wake up from HALT mode without calling interrupt code. */
|
||||
if (gb->halted && !effective_ime && interrupt_queue) {
|
||||
gb->halted = false;
|
||||
gb->dma_cycles = 4;
|
||||
GB_dma_run(gb);
|
||||
gb->speed_switch_halt_countdown = 0;
|
||||
}
|
||||
|
||||
/* Call interrupt */
|
||||
else if (effective_ime && interrupt_queue) {
|
||||
gb->halted = false;
|
||||
// TODO: verify the timing!
|
||||
gb->dma_cycles = 4;
|
||||
GB_dma_run(gb);
|
||||
gb->speed_switch_halt_countdown = 0;
|
||||
uint16_t call_addr = gb->pc;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user