Skip to content

Conversation

@SuuperW
Copy link
Contributor

@SuuperW SuuperW commented Nov 23, 2025

This PR allows access to 16-bit and 32-bit reads/writes on the system bus domains for melonDS, resolving the issue I had with #3121. This is done in 2 parts:

  1. Give WaterboxMemoryDomainFunc implementations for 16 and 32 bit reads and writes, and a bulk byte write implementation. (This requires giving MemoryDomain a bulk byte write, which was taken from MemoryAPI.)
  2. Change the access function to use 16 and 32 bit methods when array size and alignment are compatible.
    Performance is slightly improved by using larger access sizes inside melon.

This does not give a way to specify to waterbox function hook domains the write size to use. MelonDS will now infer it. Calls to PeekUint and PokeUint will use 16-bit access if the address is 2-byte aligned, similar for 32-bit. This results in different behavior compared to letting the user directly specify it in two ways:

  1. Un-aligned access will still be based on individual bytes, so a ushort write to address 0x7 will write to 0x7 and 0x8. A 16-bit access through the system bus would align the given address, which would write to addresses 0x6 and 0x7. Reads are the same.
  2. Lua users cannot obviously specify access size, and C# users cannot obviously specify write size (as MemoryDomain has no such functions). Access size can be indirectly controlled by (un-)aligning the address or size.

These behaviors seem acceptable to me.

Another option would be to provide new function hook domains for 16 and 32 bit access. This would grant the user the ability to choose the access size by using one of the 4-6 new "ARMX System Bus XX-bit" domains. This would solve both above issues, but I'm not sure if this would be a better or worse user experience. It would at least hint to users that there is a meaningful difference, which most will not be aware of without being told.
This option would mean the updates to C# code are unnecessary.

I did not want to modify the system bus domains to peek/poke currently unsupported addresses for two reasons.

  1. There's likely value in preserving the actual behavior of the emulated system bus. I think it would be better to introduce new memory domain(s) for problematic areas.
  2. I would not be able to make good decisions for how to implement all addresses, due to lack of knowledge regarding both melonDS and the system it emulates. Some are trivial and I could do those, bit it would be an incomplete solution.

Regarding hex editor, I don't think there are any great solutions. With this PR, access size in hex editor can be controlled by changing the viewed data size. This is not intuitive. Using sized system bus domains would require the user to change domains, and also leads to the question of what should be done if the user writes a single byte in 1-byte data size mode, to a 32-bit domain? I guess this would have to first read the relevant 4 bytes, change the one byte, then write 4 bytes again. Fortunately, none of this matters for the vast majority of addresses so it's probably not very important as long as it is possible to perform sized access.

Check if completed:

…mentations, letting the hooked function decide how to handle them.
…and update SafeToPeek. This makes most things readable and writable from BizHawk.

Resolves TASEmulators#3121 except for the problematic addresses listed in SafeToPeek.
@CasualPokePlayer
Copy link
Member

CasualPokePlayer commented Nov 29, 2025

This does not give a way to specify to waterbox function hook domains the write size to use. MelonDS will now infer it.

This is very much the wrong way to do it. If I'm specifying a bulk read of multiple bytes (e.g. memory.read_bytes_as_array), the behavior shouldn't change just because I decided to read a different amount or depending on where I decided to start the read. Byte at address X returns what address X has when you peek that byte with PeekByte.

Lua users cannot obviously specify access size

They can, there are a ton of lua functions specifically for this.

C# users cannot obviously specify write size (as MemoryDomain has no such functions)

They can, MemoryDomain does have such functions (that's the entire point of the different poke functions).

Another option would be to provide new function hook domains for 16 and 32 bit access.

You don't need new domains, the function hook just needs to be modified to specify access size and that access size must be sent over for the core to understand how to read/write things.

Ideally different widths shouldn't be changing results in the first place, but the System Bus is inherently quirky to the console (since it is more or less native bus reads), so if different widths change results those are arguably just system quirks which are inherent to the System Bus domain, so under that logic it's acceptable for System Bus domains.

@SuuperW
Copy link
Contributor Author

SuuperW commented Nov 29, 2025

the behavior shouldn't change just because I decided to read a different amount or depending on where I decided to start the read.

Just to be clear - for the vast majority of addresses, the behavior does not change depending on alignment or number of bytes read.

They can, there are a ton of lua functions specifically for this.

With the current implementation memory.read_u32_le(0x02000001) will internally do 4 single-byte reads while memory.read_u32_le(0x02000000) will do 1 4-byte read. Similar for read_bytes_as_array and for the C# API. This doesn't matter for most addresses. Except, if we do a 4-byte read with read_u32_le then reading from address 0x02000001 would ALWAYS return the same value as reading from address 0x02000000 since the system bus aligns multi-byte reads and writes. Not a bad thing, but surprising to most people.

You don't need new domains, the function hook just needs to be modified to specify access size and that access size must be sent over for the core to understand how to read/write things.

I didn't want to try modifying every other core that uses function hooks. Although I suppose it could instead be a second kind of function hook. I could do that.

And if we do it this way, the Lua bulk read functions could similarly have a parameter for access size. And corresponding C# API functions...? There's already BulkPeekUshort and BulkPeekUint but no multi-byte bulk poke functions. Maybe Lua should follow suit and have multiple functions instead of adding a parameter to the existing one. That probably makes more sense actually, a new read_u32_le_array would return a table with 32-bit numbers instead of separating each byte.

those are arguably just system quirks which are inherent to the System Bus domain, so under that logic it's acceptable for System Bus domains

Yes, that is my view. IMO there should also be a way to perform system bus reads that have side effects. Maybe this could be accomplished with a new API method that enables read side effects. Making read side effects opt-in would protect users from accidentally causing desyncs.

@YoshiRulz
Copy link
Member

Can I veto any new peek or poke APIs until #3472 and/or #3738? At least in ApiHawk; I guess if there's an asymmetry in the Lua API that should be filled.

@SuuperW
Copy link
Contributor Author

SuuperW commented Nov 29, 2025

The first one I will look at later. I don't see the relevance of the second, nor would I wish to attempt to implement it in the near future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MelonDS system bus 16 and 32 bit reads do not work

3 participants