@jlun2: I fixed the cart RAM domain in GBHawk so your lua script should work now, please retest and let me know if everything is good.
This was unique / troublesome enough that I decided to write down the technically details, anyone interested can read on.
This was actually a pretty weird case that shows how important it is to watch out for garbage collection in managed code in C#.
What went wrong is here:
var CartRam = new MemoryDomainByteArray("CartRAM", MemoryDomain.Endian.Little, cart_RAM, true, 1);
This function takes the whole of cart_RAM as an argument and used in an assignment :
Data = data (where data is cart_RAM in this case.
Data has a setter:
public byte[] Data
{
get => _data;
set
{
_data = value;
Size = _data.LongLength;
}
}
_data is another byte array that is internal to the memory domain.
Internally, this assignment is an assignment by pointer. _data gets a pointer to cart_RAM, even though it doesn't look like it.
In c++, this would be what you want and expect. But in C#, the underlying domain cart_RAM can be moved without you knowing, in an attempt to manage memory for you.
From here on is somewhat speculative, but what I THINK is happening is that memory management moves cart_RAM, but the original copy can't be deleted because their is a pointer to it. So you get left with a stale copy of cart_RAM that the memory domain points to while the actual cart_RAM array getting updated as it should.
This, is super annoying. The solution is to just use the delegate method of defining domains that doesn't rely on a pointer internally. The weird thing is that other cores (ex NESHawk) use this method of defining memory domains by default and I've never seen a case where this has happened before. Probably it should be used for static arrays only (or not at all if it's just going to leave a stale copy clogging up memory.)