# SDCC 4.5 z80: `return <константа>` из ветки теряется — функция возвращает мусор **Компилятор:** SDCC 4.5.0 (z80), флаги `-mz80 --std-c99 --opt-code-size` (воспроизводится и с `--no-std-crt0`, и в `__banked`, и без него). **Симптом.** У функции, возвращающей `uint8_t`, ветка с `return 1;` НЕ кладёт 1 в A. Вызывающий читает A (`__sdcccall(1)`: uint8 возвращается в A) и получает то, что там осталось от предыдущей операции — то есть мусор. ## Репро `repro.c` (полный текст рядом; собран в `repro.asm`): ```c uint8_t pop_level_guard(uint8_t room, uint8_t *tile, int8_t *dir, uint8_t *color, uint8_t *skill) __banked { if (room < 1 || room > TK_ROOMS) return 0; if (tk_gs_tile[room - 1] < 30) { *tile = tk_gs_tile[room - 1]; *dir = 0; *color = 0; *skill = 1; return 1; /* <-- значение теряется */ } if (room == tk_guard_room) { *tile = 11; *dir = 0; *color = 0; *skill = 1; return 1; /* <-- и здесь */ } return 0; } ``` Сгенерированный хвост второй ветки (`repro.asm`): ``` ;repro.c:15: *tile = 11; *dir = 0; *color = 0; *skill = 1; ld a, #0x0b ld (de), a xor a, a ; A := 0 (это *dir = 0) ld (bc), a pop hl ld (hl), #0x00 ;repro.c:16: return 1; ex de,hl pop hl push hl push de ld (hl), #0x01 ; это *skill = 1, НЕ возврат jr 00108$ 00107$: xor a, a 00108$: ld sp, ix pop ix ret ; A = 0 — вернулась ЛОЖЬ вместо 1 ``` Кода `ld a, #0x01` для самого `return 1` нет ни в одной из двух веток. ## Когда прячется, когда стреляет Баг присутствует и БЕЗ `__banked`, но там часто маскируется: если последним в ветке A случайно оказался ненулевой байт (например `ld a,#0x0b` для `*tile = 11`), вызывающий с проверкой «истина/ложь» получает правильный ответ. `__banked` меняет распределение регистров (указатели уезжают в IX-кадр, `*dir = 0` компилируется в `xor a,a`), A обнуляется — и та же функция начинает возвращать 0 вместо 1. Именно так это и вылезло у нас: набор `tests-host/t_char` годами был зелёным и покраснел ровно в тот момент, когда стаб пометили `__banked` — хотя код стаба не менялся. ## Обход Один выход и явная переменная результата (`workaround.c` / `workaround.asm`): ```c uint8_t res = 0; ... res = 1; ... return res; /* -> ld a, -5 (ix) — корректно */ ``` ## Признак для аудита Грепать функции, у которых `return <константа>` стоит в ветке, где последней операцией была запись по указателю или `xor a,a`. Проверять по сгенерированному `.asm`: перед `jr` на общий выход обязан быть `ld a,#…` (или загрузка результата), иначе возврат — мусор.