Closed hammurabi-mendes closed 2 months ago
More information: it's the call at $8043 to __do_init_array that overwrites addresses 0...6 where the string "test1" was originally present.
Edited -> the problem persists in 19.1.1.
Sorry for taking so long to get to this; just wanted to check in now that I've opened the tin and started looking at it.
Is there something like an emulator that I could use to debug this? It's difficult to directly read the problem out of the code; the snippets of information you've given are definitely helpful, but it's still hard to make sense of from just that.
EDIT: I missed something; it might be possible to diagnose this just by inspection. But a way to run and monitor code would still likely be very helpful.
You can use the Symon emulator here: https://github.com/sethm/symon/releases/tag/v1.4.0
Download the JAR version of the emulator, save the code above as (say) b.cpp and run like this:
Run and you'll see garbled output. Go to view, Memory Window and you'll see that the "test1" string is at address $8000. On the eater target, the rom starts at $8000, so the string is at address "relative 0" from the beginning of ROM.
Now, click on "Hard Reset" to try again, but add a breakpoint at $82a2. You can do that by going into the menu Simulator -> Breakpoints and adding 82a2. RC0 is at $e0 on eater, and RS1 = RC2 | RC3 are zero. So we're calling fputs with a zero address.
Now, if I click on "Hard Reset" and run once again, but this time add a breakpoint at $8043. You'll see that the string is actually at address zero, but it is about to be overritten by _do_init_array. Once you come back from it to $8046, the address zero does not contain "test1" anymore.
BTW, I don't think it's ever a good idea to move variables to the ZP at zero because it may be a problem with C programs that rely that on being a null value. So I think the fix is not only avoiding the overwrite, but it might be nice that the "test1" string is at a non-zero address.
Thanks!
Thanks, this is extremely helpful.
BTW, I don't think it's ever a good idea to move variables to the ZP at zero because it may be a problem with C programs that rely that on being a null value. So I think the fix is not only avoiding the overwrite, but it might be nice that the "test1" string is at a non-zero address.
I think I generally agree; we usually use the low ZP for imaginary registers on systems like this for that reason. I'm not sure why eater
wasn't set up that way; I don't remember from the code review. I'll need to go back and look at the blame/PR when it was checked in; if there wasn't a good reason, I'll change it. But there's may also be an independent problem here, so I'd like to get to the bottom of it.
Spent some time setting this up and running through; the logic in __cxa_atexit
where the destructor is registered on the destructor list is what's clobbering the zero page. The internal pointer that should point to the registration list ends up zeroed out somehow; I need to step through the instructions in more detail and figure out why. (Nothing obvious is amiss.) But I'll get right back to this after I cut the next release.
Finally was able to track this down. The __cxa_atexit
registration list pointer in .data
was zeroed; that's because while Ben Eater is a ROM platform, the SDK didn't register the .data
initializer copy routine to run. (The zero page version of this was registered, confusingly, so it didn't blow up nearly as spectacularly as usual.)
Since the pointer for the registration list was left uninitialized, it was zeroed in the emulator, which caused destructor registration to overwrite the zero page. Adding data copy registration seems to cause the example to produce the correct textual output.
Uisng the v19.1.1 SDK, compile the code attached for the "eater" target:
mos-eater-clang++ -std=c++11 -fnonreentrant -Weverything -flto -Os -o test b.cpp
The compiled code will call fputs with an argument in address 0. The string was at address 0 before being overwritten by __do_init_array, and it looks like there's another copy at $8000. When the PC is $82a2 it calls the first fputs. Registers A and X are both zero at that point, and the soft registers RC2 and RC3 are also both zero. So the first, parameter to fputs is zero, but the string is at $8000, which is the zeroth byte of the rom.
Also, if you comment the lines 24-25 (the ~RCPointer destructor), then the string "test1" is properly at address $0 when calling fputs and everything works.