Merge pull request #48 from PoroCYon/expose-ahbm

Expose AHBM things and add 16- and 32-bit memory accesses
This commit is contained in:
Weiyi Wang 2020-09-13 12:20:24 -04:00 committed by GitHub
commit d4bea3ec3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 159 additions and 36 deletions

View File

@ -10,6 +10,12 @@ namespace Teakra {
struct AHBMCallback {
std::function<std::uint8_t(std::uint32_t address)> read8;
std::function<void(std::uint32_t address, std::uint8_t value)> write8;
std::function<std::uint16_t(std::uint32_t address)> read16;
std::function<void(std::uint32_t address, std::uint16_t value)> write16;
std::function<std::uint32_t(std::uint32_t address)> read32;
std::function<void(std::uint32_t address, std::uint32_t value)> write32;
};
class Teakra {
@ -52,6 +58,15 @@ public:
std::uint16_t DMAChan0GetSrcHigh();
std::uint16_t DMAChan0GetDstHigh();
std::uint16_t AHBMGetUnitSize(std::uint16_t i) const;
std::uint16_t AHBMGetDirection(std::uint16_t i) const;
std::uint16_t AHBMGetDmaChannel(std::uint16_t i) const;
// we need these as AHBM does some weird stuff on unaligned accesses internally
std::uint16_t AHBMRead16(std::uint32_t addr);
void AHBMWrite16(std::uint32_t addr, std::uint16_t value);
std::uint16_t AHBMRead32(std::uint32_t addr);
void AHBMWrite32(std::uint32_t addr, std::uint32_t value);
// core
void Run(unsigned cycle);

View File

@ -12,8 +12,15 @@ typedef struct TeakraObject TeakraContext;
typedef void (*Teakra_InterruptCallback)(void* userdata);
typedef void (*Teakra_AudioCallback)(void* userdata, int16_t samples[2]);
typedef uint8_t (*Teakra_AHBMReadCallback)(void* userdata, uint32_t address);
typedef void (*Teakra_AHBMWriteCallback)(void* userdata, uint32_t address, uint8_t value);
typedef uint8_t (*Teakra_AHBMReadCallback8)(void* userdata, uint32_t address);
typedef void (*Teakra_AHBMWriteCallback8)(void* userdata, uint32_t address, uint8_t value);
typedef uint16_t (*Teakra_AHBMReadCallback16)(void* userdata, uint32_t address);
typedef void (*Teakra_AHBMWriteCallback16)(void* userdata, uint32_t address, uint16_t value);
typedef uint32_t (*Teakra_AHBMReadCallback32)(void* userdata, uint32_t address);
typedef void (*Teakra_AHBMWriteCallback32)(void* userdata, uint32_t address, uint32_t value);
TeakraContext* Teakra_Create();
void Teakra_Destroy(TeakraContext* context);
@ -47,10 +54,24 @@ void Teakra_MMIOWrite(TeakraContext* context, uint16_t address, uint16_t value);
uint16_t Teakra_DMAChan0GetSrcHigh(TeakraContext* context);
uint16_t Teakra_DMAChan0GetDstHigh(TeakraContext* context);
uint16_t Teakra_AHBMGetUnitSize(TeakraContext* context, uint16_t i);
uint16_t Teakra_AHBMGetDirection(TeakraContext* context, uint16_t i);
uint16_t Teakra_AHBMGetDmaChannel(TeakraContext* context, uint16_t i);
uint16_t Teakra_AHBMRead16(TeakraContext* context, uint32_t addr);
void Teakra_AHBMWrite16(TeakraContext* context, uint32_t addr, uint16_t value);
uint16_t Teakra_AHBMRead32(TeakraContext* context, uint32_t addr);
void Teakra_AHBMWrite32(TeakraContext* context, uint32_t addr, uint32_t value);
void Teakra_Run(TeakraContext* context, unsigned cycle);
void Teakra_SetAHBMCallback(TeakraContext* context, Teakra_AHBMReadCallback read,
Teakra_AHBMWriteCallback write, void* userdata);
void Teakra_SetAHBMCallback(TeakraContext* context,
Teakra_AHBMReadCallback8 read8 , Teakra_AHBMWriteCallback8 write8 ,
Teakra_AHBMReadCallback16 read16, Teakra_AHBMWriteCallback16 write16,
Teakra_AHBMReadCallback32 read32, Teakra_AHBMWriteCallback32 write32,
void* userdata);
void Teakra_SetAudioCallback(TeakraContext* context, Teakra_AudioCallback callback, void* userdata);
#ifdef __cplusplus

View File

@ -42,7 +42,7 @@ u32 Ahbm::Read32(u16 channel, u32 address) {
u32 value = 0;
switch (channels[channel].unit_size) {
case UnitSize::U8:
value = read_external(current);
value = read_external8(current);
if ((current & 1) == 1) {
value <<= 8; // this weird bahiviour is hwtested
}
@ -50,17 +50,13 @@ u32 Ahbm::Read32(u16 channel, u32 address) {
break;
case UnitSize::U16: {
u32 current_masked = current & 0xFFFFFFFE;
value =
read_external(current_masked) | ((u32)read_external(current_masked + 1) << 8);
value = read_external16(current_masked);
current += 2;
break;
}
case UnitSize::U32: {
u32 current_masked = current & 0xFFFFFFFC;
value = read_external(current_masked) |
((u32)read_external(current_masked + 1) << 8) |
((u32)read_external(current_masked + 2) << 16) |
((u32)read_external(current_masked + 3) << 24);
value = read_external32(current_masked);
current += 4;
break;
}
@ -82,7 +78,7 @@ void Ahbm::Write16(u16 channel, u32 address, u16 value) {
}
void Ahbm::Write32(u16 channel, u32 address, u32 value) {
if ((address & 1) == 1) {
value >>= 16; // this weird bahiviour is hwtested
value >>= 16; // this weird behaviour is hwtested
}
WriteInternal(channel, address, value);
}
@ -104,9 +100,9 @@ void Ahbm::WriteInternal(u16 channel, u32 address, u32 value) {
channels[channel].burst_queue.pop();
switch (channels[channel].unit_size) {
case UnitSize::U8: {
// this weird bahiviour is hwtested
// this weird behaviour is hwtested
u8 value8 = ((current & 1) == 1) ? (u8)(value32 >> 8) : (u8)value32;
write_external(current, value8);
write_external8(current, value8);
current += 1;
break;
}
@ -114,9 +110,10 @@ void Ahbm::WriteInternal(u16 channel, u32 address, u32 value) {
u32 c0 = current & 0xFFFFFFFE;
u32 c1 = c0 + 1;
if (c0 >= current) {
write_external(c0, (u8)value32);
write_external16(c0, (u16)value32);
} else {
write_external8(c1, (u8)(value32 >> 8));
}
write_external(c1, (u8)(value32 >> 8));
current += 2;
break;
}
@ -125,16 +122,18 @@ void Ahbm::WriteInternal(u16 channel, u32 address, u32 value) {
u32 c1 = c0 + 1;
u32 c2 = c0 + 2;
u32 c3 = c0 + 3;
if (c0 >= current) {
write_external(c0, (u8)value32);
if (c0 >= current && c1 >= current && c2 >= current) {
write_external32(c0, value32);
} else if (c2 >= current) {
if (c1 >= current) {
write_external8(c1, (u8)(value32 >> 8));
}
write_external16(c2, (u16)(value32 >> 16));
} else {
write_external8(c3, (u8)(value32 >> 24));
}
if (c1 >= current) {
write_external(c1, (u8)(value32 >> 8));
}
if (c2 >= current) {
write_external(c2, (u8)(value32 >> 16));
}
write_external(c3, (u8)(value32 >> 24));
current += 4;
break;
}

View File

@ -63,10 +63,17 @@ public:
u16 GetChannelForDma(u16 dma_channel) const;
void SetExternalMemoryCallback(std::function<u8(u32)> read,
std::function<void(u32, u8)> write) {
read_external = std::move(read);
write_external = std::move(write);
void SetExternalMemoryCallback(
std::function<u8 (u32)> read8 , std::function<void(u32, u8 )> write8 ,
std::function<u16(u32)> read16, std::function<void(u32, u16)> write16,
std::function<u32(u32)> read32, std::function<void(u32, u32)> write32) {
read_external8 = std::move(read8);
write_external8 = std::move(write8);
read_external16 = std::move(read16);
write_external16 = std::move(write16);
read_external32 = std::move(read32);
write_external32 = std::move(write32);
}
private:
@ -83,8 +90,12 @@ private:
};
std::array<Channel, 3> channels;
std::function<u8(u32)> read_external;
std::function<void(u32, u8)> write_external;
std::function<u8(u32)> read_external8;
std::function<void(u32, u8)> write_external8;
std::function<u16(u32)> read_external16;
std::function<void(u32, u16)> write_external16;
std::function<u32(u32)> read_external32;
std::function<void(u32, u32)> write_external32;
void WriteInternal(u16 channel, u32 address, u32 value);
};

View File

@ -118,7 +118,32 @@ void Teakra::MaskSemaphore(std::uint16_t value) {
impl->apbp_from_dsp.MaskSemaphore(value);
}
void Teakra::SetAHBMCallback(const AHBMCallback& callback) {
impl->ahbm.SetExternalMemoryCallback(callback.read8, callback.write8);
impl->ahbm.SetExternalMemoryCallback(callback.read8, callback.write8,
callback.read16, callback.write16,
callback.read32, callback.write32);
}
std::uint16_t Teakra::AHBMGetUnitSize(std::uint16_t i) const {
return impl->ahbm.GetUnitSize(i);
}
std::uint16_t Teakra::AHBMGetDirection(std::uint16_t i) const {
return impl->ahbm.GetDirection(i);
}
std::uint16_t Teakra::AHBMGetDmaChannel(std::uint16_t i) const {
return impl->ahbm.GetDmaChannel(i);
}
std::uint16_t Teakra::AHBMRead16(std::uint32_t addr) {
return impl->ahbm.Read16(0, addr);
}
void Teakra::AHBMWrite16(std::uint32_t addr, std::uint16_t value) {
impl->ahbm.Write16(0, addr, value);
}
std::uint16_t Teakra::AHBMRead32(std::uint32_t addr) {
return impl->ahbm.Read32(0, addr);
}
void Teakra::AHBMWrite32(std::uint32_t addr, std::uint32_t value) {
impl->ahbm.Write32(0, addr, value);
}
void Teakra::SetAudioCallback(std::function<void(std::array<s16, 2>)> callback) {

View File

@ -98,18 +98,49 @@ uint16_t Teakra_DMAChan0GetDstHigh(TeakraContext* context){
return context->teakra.DMAChan0GetDstHigh();
}
uint16_t Teakra_AHBMGetUnitSize(TeakraContext* context, uint16_t i) {
return context->teakra.AHBMGetUnitSize(i);
}
uint16_t Teakra_AHBMGetDirection(TeakraContext* context, uint16_t i) {
return context->teakra.AHBMGetDirection(i);
}
uint16_t Teakra_AHBMGetDmaChannel(TeakraContext* context, uint16_t i) {
return context->teakra.AHBMGetDmaChannel(i);
}
uint16_t Teakra_AHBMRead16(TeakraContext* context, uint32_t addr) {
return context->teakra.AHBMRead16(addr);
}
void Teakra_AHBMWrite16(TeakraContext* context, uint32_t addr, uint16_t value) {
context->teakra.AHBMWrite16(addr, value);
}
uint16_t Teakra_AHBMRead32(TeakraContext* context, uint32_t addr) {
return context->teakra.AHBMRead32(addr);
}
void Teakra_AHBMWrite32(TeakraContext* context, uint32_t addr, uint32_t value) {
context->teakra.AHBMWrite32(addr, value);
}
void Teakra_Run(TeakraContext* context, unsigned cycle) {
context->teakra.Run(cycle);
}
void Teakra_SetAHBMCallback(TeakraContext* context, Teakra_AHBMReadCallback read,
Teakra_AHBMWriteCallback write, void* userdata) {
void Teakra_SetAHBMCallback(TeakraContext* context,
Teakra_AHBMReadCallback8 read8 , Teakra_AHBMWriteCallback8 write8 ,
Teakra_AHBMReadCallback16 read16, Teakra_AHBMWriteCallback16 write16,
Teakra_AHBMReadCallback32 read32, Teakra_AHBMWriteCallback32 write32,
void* userdata) {
Teakra::AHBMCallback callback;
callback.read8 = [=](uint32_t address) { return read(userdata, address); };
callback.write8 = [=](uint32_t address, uint8_t value) { write(userdata, address, value); };
callback.read8 = [=](uint32_t address) { return read8(userdata, address); };
callback.write8 = [=](uint32_t address, uint8_t value) { write8(userdata, address, value); };
callback.read16 = [=](uint32_t address) { return read16(userdata, address); };
callback.write16 = [=](uint32_t address, uint16_t value) { write16(userdata, address, value); };
callback.read32 = [=](uint32_t address) { return read32(userdata, address); };
callback.write32 = [=](uint32_t address, uint32_t value) { write32(userdata, address, value); };
context->teakra.SetAHBMCallback(callback);
}
void Teakra_SetAudioCallback(TeakraContext* context, Teakra_AudioCallback callback,
void* userdata) {
context->teakra.SetAudioCallback(

View File

@ -21,6 +21,27 @@ TEST_CASE("DMA + AHBM test", "[dma]") {
[&fcram](u32 address, u8 v) {
REQUIRE(address >= 0x20000000);
fcram[address - 0x20000000] = v;
},
[&fcram](u32 address) -> u16 {
REQUIRE(address >= 0x20000000);
return fcram[address - 0x20000000] | ((u16)fcram[address - 0x20000000 + 1] << 8);
},
[&fcram](u32 address, u16 v) {
REQUIRE(address >= 0x20000000);
fcram[address - 0x20000000 + 0] = (u8)v;
fcram[address - 0x20000000 + 1] = v >> 8;
},
[&fcram](u32 address) -> u32 {
REQUIRE(address >= 0x20000000);
return fcram[address - 0x20000000] | ((u32)fcram[address - 0x20000000 + 1] << 8)
| ((u32)fcram[address - 0x20000000 + 2] << 16) | ((u32)fcram[address - 0x20000000 + 3] << 24);
},
[&fcram](u32 address, u32 v) {
REQUIRE(address >= 0x20000000);
fcram[address - 0x20000000 + 0] = (u8)v;
fcram[address - 0x20000000 + 1] = (u8)(v >> 8);
fcram[address - 0x20000000 + 2] = (u8)(v >> 16);
fcram[address - 0x20000000 + 3] = (u8)(v >> 24);
});
for (u8 i = 0; i < 0x80; ++i) {