The GBZ80 codegen can generate incorrect code when __sfr registers are used.
Example:
static volatile __sfr __at(0x43) reg;
int f() {
if (reg == 0) { return 1; }
return 0;
}
https://godbolt.org/z/6xna4oovc
Will generate:
_reg = 0x0043
ld hl, #_reg
ld a, (hl)
or a, a
But, for GBZ80, the __sfr registers are at 0xFFxx not at 0x00xx. Trying to use 0xFF43 will result in a warning but "correct" (suboptimal) codegen.
I've also seen hl being optimized away into:
ld a, (#_reg)
or a, a
Tested on release 4.1.0
Actually, looking better at it. I think the warning on
__sfraddresses for GBZ80 is wrong, which causes GBDK to use the range 0x00-0xFF as__atvalues, which then get translated toldhinstructions, which accept a 0x00-0xFF as an 0xFFxx address.that is not a bug, you must use 0xFF43 as address: __at(0xFF43)
So in that case, the bug is a the warning 182. As using the 0xFF43 gives a warning.
yes, in 4.1.6 #12533
__sfrseems to be very broken. several problems were introduced recently.__sfrwithout__at()emits SFR's out of any areas:result:
Last edit: Tony Pavlov 2021-07-12
2.
defined SFR's do not use advantage of
LDHinstruction and also address is either wrong, or generates a warning, as said by Daid.which emits this code:
Last edit: Tony Pavlov 2021-07-12
How about just removing __sfr support for gbz80?
IMO, __sfr only adds unnecessary complexity to gbz80.
The following work fine:
Last edit: Philipp Klaus Krause 2021-11-19
i don't think that is a good idea, because
LDHinstructions are faster thanLD. GB has A LOT OF hardware registers and they are frequently used, so there is much sense in them. that will reduce performance or C-written game boy games.it is actually i/o.
ldhare equivalents toin/outz80 instructions. also there isldh (c), awhich isin (c), aand for out as well. only it is mapped onto CPU address space. maybe that should be handled somehow else, more like i/o?volatile __sfr __at(01) __mapped(0xff01) c1;Last edit: Tony Pavlov 2021-11-19
or maybe:
volatile unsigned char __at(0xff01) __io(0x01) c1;Last edit: Tony Pavlov 2021-11-19
But it is just memory-mapped I/O, so from the compiler perspective, it ist just volatile-qualified accesses to memory. And the SM83 happens to have a special instruction for efficient access to part of that memory.
In the above example code, current SDCC does use ldh instructions for the access to C1, but not for c1.
P.S.: I made a small change in the use of ldh today: Now in some cases, where ldh was previously used by the peephole optimizer, it is emitted by codegen instead. Still, both before and from today, SDCC uses ldh for the access to C1.
Last edit: Philipp Klaus Krause 2021-11-19
if
__sfrwill simply do nothing, but sdcc will emitldhfor volatile variables in the range 0xff00...0xffff, then it is probably ok. but i don't see how that will simplify gbz80 port.also, now
__sfris a hint that allow to emitldhwhen the declaration isexternwithout address. that is important feature, because we have 3 game-boy targets: game boy itself, mega duck and analogue pocket. they have different hardware registers layout. there is compiled sfr.o in the library where addresses come from, and the definition of the hardware registers in headers is the same. that will not be possible anymore.Last edit: Tony Pavlov 2021-11-19
I see the latter point. without __sfr, indeed accesses from a different module, where the address is not known to the compiler would result in ldh not being used (and plain ld is 16 cycles vs. 12 for ldh, so a bit slower).
Anyway, I think we'd want to improve use of ldh in codegen a bit further before making a decision on removing __sfr. And improving use of ldh is IMO worth it even if __sfr stays.
Last edit: Philipp Klaus Krause 2021-11-19
I think requiring the user to specify the __sfr address with full 16 bits is the best solution here.
In [r12833] (SDCC 4.2.13), I changed the documentation and warning accordingly.