Skip to content

Commit

Permalink
Ensure the first invalid access of the DMA unit, if any, is handled c…
Browse files Browse the repository at this point in the history
…orrectly.

Fix #31.
  • Loading branch information
Arignir committed Mar 19, 2024
1 parent b122bd3 commit f856b41
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 15 deletions.
5 changes: 4 additions & 1 deletion include/gba/memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,10 @@ struct memory {

// DMA Open Bus
uint32_t dma_bus;
bool is_dma_bus_dirty;

// Set when the last memory access was done by the DMA unit.
// Used to correctly handle invalid memory reads.
bool was_last_access_from_dma;

// Set when the cartridge memory bus is in used
bool gamepak_bus_in_use;
Expand Down
31 changes: 24 additions & 7 deletions source/gba/core/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@

/*
** Fetch, decode and execute the next instruction.
**
** First check if the IRQ line is raised and if IRQs aren't disabled in the CPSR and
** interrupt the core appropriately.
**
** Then, take the appropriate action depending on the core's state:
** - Run: Fetch, decode and execute the next instruction
** - Halt: Idle until the next event
**
** The `Stop` state isn't handled here, hence why it isn't mentioned.
*/
void
core_next(
Expand All @@ -35,7 +44,7 @@ core_next(

core = &gba->core;

/* Fire an interrupt if the IRQ line is set. */
// Fire an interrupt if the IRQ line is set.
if (core->irq_line && !gba->core.cpsr.irq_disable) {
logln(HS_IRQ, "Received new IRQ: 0x%04x.", gba->io.int_enabled.raw & gba->io.int_flag.raw);
core_interrupt(gba, VEC_IRQ, MODE_IRQ, true);
Expand All @@ -48,7 +57,12 @@ core_next(
op = core->prefetch[0];
core->prefetch[0] = core->prefetch[1];
core->prefetch[1] = mem_read16(gba, core->pc, core->prefetch_access_type);
gba->memory.was_last_access_from_dma = false;

// Build a unique index based on the instruction's opcode, which is then used to index
// the Lookup Table (LUT) of Thumb instructions.
//
// NOTE: We need to properly handle unknown instructions instead of crashing.
if (unlikely(thumb_lut[op >> 8] == NULL)) {
panic(HS_CORE, "Unknown Thumb op-code 0x%04x (pc=0x%08x).", op, core->pc);
}
Expand All @@ -61,21 +75,24 @@ core_next(
op = core->prefetch[0];
core->prefetch[0] = core->prefetch[1];
core->prefetch[1] = mem_read32(gba, core->pc, core->prefetch_access_type);
gba->memory.was_last_access_from_dma = false;

/*
** Test if the conditions required to execute the instruction are met
** Ignore instructions where the conditions aren't met.
*/

// Test if the conditions required to execute the instruction are met using a Lookup Table (LUT).
//
// The index of the LUT is both the CPSR and the condition combined in an 8-bit integer
// unique per situation.
idx = (bitfield_get_range(core->cpsr.raw, 28, 32) << 4) | (bitfield_get_range(op, 28, 32));
if (unlikely(!cond_lut[idx])) {
core->pc += 4;
core->prefetch_access_type = SEQUENTIAL;
goto end;
}

// Build a unique index based on the instruction's opcode, which is then used to index
// the Lookup Table (LUT) of ARM instructions.
//
// NOTE: We need to properly handle unknown instructions instead of crashing.
idx = ((op >> 16) & 0xFF0) | ((op >> 4) & 0x00F);

if (unlikely(arm_lut[idx] == NULL)) {
panic(HS_CORE, "Unknown ARM op-code 0x%08x (pc=0x%08x).", op, core->pc);
}
Expand Down
3 changes: 1 addition & 2 deletions source/gba/memory/dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ dma_run_channel(
channel->internal_dst += dst_step;
channel->internal_count -= 1;
gba->memory.dma_bus = channel->latch;
gba->memory.is_dma_bus_dirty = true;
gba->memory.was_last_access_from_dma = true;
access_src = SEQUENTIAL;
access_dst = SEQUENTIAL;
}
Expand Down Expand Up @@ -228,7 +228,6 @@ mem_dma_do_all_pending_transfers(
}

gba->core.is_dma_running = true;
gba->memory.is_dma_bus_dirty = false;
core_idle(gba);

while (gba->core.pending_dma) {
Expand Down
9 changes: 4 additions & 5 deletions source/gba/memory/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -202,10 +202,9 @@ mem_openbus_read(

shift = addr & 0x3;

// On first access, open-bus during DMA transfers returns the
// last prefetched instruction (as one would expect), but on
// subsequent transfers it returns the last transfered data.
if (gba->core.is_dma_running && gba->memory.is_dma_bus_dirty) {
// On first access, open-bus during DMA transfers returns the last prefetched instruction.
// On subsequent transfers it returns the the last transfered data.
if (gba->memory.was_last_access_from_dma) {
return gba->memory.dma_bus >> (8 * shift);
}

Expand Down Expand Up @@ -246,7 +245,7 @@ mem_openbus_read(
default: {
panic(HS_MEMORY, "Reading the open bus from an impossible page: %u", pc >> 24);
break;
}
};
}
} else {
val = gba->core.prefetch[1];
Expand Down

0 comments on commit f856b41

Please sign in to comment.