Here is a patch to the behaviour for __sdcc_external_startup
on STM8 and Z80-derivative platforms to unify their behaviour with that documented for the existing MCS51 implementation.
Init code will now check the return value from __sdcc_external_startup
and when it is non-zero, the initialisation of global variables will be skipped.
The following platforms have been changed:
Changes to Z80 platforms have been done in their respective standard library crt0.s
code, and STM8 changes are to init code generated in stm8/main.c
.
The modifications assume the use of the documented default calling convention version when handling the return value from __sdcc_external_startup
. That is, specifically, on Z80 platforms whether the return value is in A or L.
All changes have been tested with uCsim, with the exception of SM83, as uCsim does not appear to support that platform (neither specifically nor using the Z80 emulator).
Feature Requests: #859
Patches: #455
Wiki: NGI0-Entrust-SDCC
Attached are the test program and scripts I used to validate these changes.
Using a version of SDCC installed with the patch applied, build the test program using
build.sh
. A number of sub-folders, one per platform, will be created. Each will contain two compiled test.ihx
programs: one to test1
being returned from__sdcc_external_startup
('skip'); one to test0
being returned ('no_skip').Run the tests using
test.sh
. This will run each compiled test program in turn using the relevant uCsim executable. All the 'skip' programs should printINIT SKIPPED
on the console, and all the 'no_skip' programs should printINIT NOT SKIPPED
.I thought maybe it would be a good idea for similar tests to be added to the regression testing set, if there were any possibility?
Last edit: Basil Hussain 2023-01-14
uCsim supports the GameBoy, which uses the sm83 architecture.
It seems SDCC does not build the gbz80 simulator by default. I don't have it from my own builds on Linux, nor is it in a recent Windows snapshot.
But this confusing, because running
./configure --help
from within the ucsim folder shows that you specifically have to disable ports (e.g.--disable-gbz80-port
), and I can't find any suggestion that SDCC's build process configures or makes uCsim with any of those options.I will try to build uCsim with all ports enabled and test SM83 with ucsim_gbz80.
Wait, I'm an idiot. I was expecting a separate ucsim executable. There is none. I just need to run ucsim_z80 with a
-t GB80
argument.I have stepped through the new init code and can confirm it works properly for SM83.
However, I don't seem to able to get the ucsim-if working to allow printing to the simulator console from test code. Must be something about the GB's memory layout I'm missing. Seems like whatever memory location I choose for the ucsim-if virtual register nothing works, and I get no output.
I suggest to have a look at how the printing is done in the regression tests (support/regression/ports/ucgbz80).
Thank you. I got it working now.
Updated test scripts attached.
Updated patch attached, now with mos6502 port support as well.
I've had a quick look at this patch; it mostly works fine, but I see regressions for mos6502:
I ran the regressions tests for uc6502 myself. All the failures mentioned were linking errors, and all were identical:
The ASxxxx documentation describes that error as "caused by exceeding the pc relative byte branch range". It would appear that is indeed the case in these failures. If I look at the .map file from the first test, the start addresses for the aforementioned sections were as follows:
From what I have found,
bne
instructions using relative addressing can only specify an offset in the range of -128 to +127. And of course, trying to jump +0xFC bytes (or some value approximate to that) doesn't fit in that range!This raises the question: why is GSFINAL so far away from GSINIT? And why only in these particular tests? The GSINIT code in crt0 isn't that large (only 0x33 bytes). According to the .map file there are no other sections inserted between either. Some other stuff must have been appended to GSINIT - but what? How can I find out?
I suppose the solution here is one of two things:
bne
instruction to skip init. Use an absolutejmp
instead.__sdcc_program_startup
in GSFINAL, but simply skip over just the init code, and let execution proceed through to there.Ahh, I think I found out where the extra stuff in GSINIT came from.
These tests have a bunch of data in static/global variables (e.g. arrays of floats) that need to be initialised. But for some reason this is done with additional generated code (e.g. streams of
stx
) rather than letting the init code copy the data. I was not aware that this was how the mos6502 port operated.The only solution will be to use an absolute jump to skip init, because it will need to skip all this potential extra stuff in GSINIT too, whereas skipping to the end of the init code in crt0 will still allow that to run.
I don't believe any of the Z80-based ports should suffer from this problem, as they perform the skip by not making a subroutine call, and it appears anything added to GSINIT falls within that subroutine.
I don't believe the STM8 port ever does any code-based init, so nothing is likely to be added to GSINIT (correct me if I'm wrong), but I may also modify that to use an absolute jump too, just in case. Maybe simply do a jump to
__sdcc_program_startup
in HOME?Updated test scripts also.
I don't really know what to do with this.
Converting it to a regression test would be problematic: It would require the regression test infrastructure to not rely on the initialization of global/static variables (after all the return value from __sdcc_external_startup affects the global/static variables from all files).
No worries, was just an idea. I primarily included the tests to prove that all the changes work as intended. They are incidental to the patch.
Regression test failures are now fixed:
That single remaining failure is same as I got before: a "CPU time limit exceeded" error. Not sure why I'm getting it and you didn't, Philipp.
Updated patch attached.
While I was looking again at the STM8 port, I also fixed a potential deficiency in the way
__sdcc_program_startup
was being jumped to from GSFINAL in the normal course of operations. Ajp
instruction was always used, regardless of memory model. I have changed the code to use ajpf
instruction when--model-large
is used. That will cope with the case of user ever deciding to relocate HOME >32kB in flash.Thanks. Applied in [r13796].
Related
Commit: [r13796]