Files
Sprinter-SDCC/docs/bugs/sdcc-z80-cmp-store-a/REPORT-ru.md
T
snark13 138e783e72 mdview2: горизонтальный скролл по типу блока + фиксы fence/HR/SDCC
Горизонтальный скролл (Фаза 5, финал):
- Скроллим по ТИПУ, а не по длине: новый флаг IF_HSCROLL ставится только
  на код и таблицы; HR и границы fence (IF_NOWRAP без IF_HSCROLL) не
  двигаются. Блок едет целиком, включая строки короче 80.
- Обход бага кодогенерации SDCC z80: `if (n!=g) g=n;` пишет (n-g) вместо n
  (SUB сравнения затирает A, store переиспользует испорченный A). Лечится
  записью viewport_x ДО сравнения. Минимальный репродьюсер и оба описания
  для трекера — в docs/bugs/sdcc-z80-cmp-store-a/ (воспроизводится на чистом
  sdcc 4.5, в т.ч. с --no-peep → это кодогенератор, не peephole).

Рендеринг:
- Отступленный fence (```c внутри списка) теперь распознаётся: is_fence_raw
  пропускает ведущие пробелы/табы; то же в рендере прячет строку-границу.
- Строки-разделители (HR, ровно 80) больше не участвуют в скролле.

Чистка: удалён мёртвый код (is_fence_delim, get_init_style[_raw], is_cont,
seg_flags). Makefile (mdview/mdview2): iconv UTF-8→CP866 завершается ненулевым
кодом при отбрасывании символов — игнорируем (|| true).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 10:11:27 +03:00

128 lines
4.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# [z80] `global = local` после `if (local != global)` пишет `local - global` (A затирается сравнением)
## Кратко
На бэкенде z80 последовательность
```c
if (n != g) { g = n; }
```
где `n` лежит в регистре `A`, а `g` — глобальная переменная, компилируется
неверно. Сравнение `!=` вычисляется через `SUB A,(HL)`, который **разрушает `A`**,
после чего присваивание `g = n` выполняется записью `A`а там уже `n - g`, а не
`n`. В итоге в глобальную переменную попадает `(unsigned char)(n - старое_g)`
вместо `n`.
Корректно было бы использовать для сравнения `CP (HL)` (он не меняет `A`) либо
перезагрузить `n` перед записью.
## Версия
SDCC 4.5.0 #15242 (Mac OS X x86_64). Опции по умолчанию; также воспроизводится с
`--opt-code-speed` и с `--no-peep` (то есть это баг кодогенератора, а не
peephole-оптимизатора).
## Минимальный пример
```c
unsigned char vx;
void update(unsigned char n)
{
if (n != vx) {
vx = n;
}
}
```
Сборка:
```
sdcc -mz80 -S repro.c
```
## Сгенерированный ассемблер (неверный)
```asm
_update::
;repro.c: if (n != vx) {
ld hl, #_vx
sub a, (hl) ; A (= n) разрушается: A = n - vx
ret Z
;repro.c: vx = n;
ld (_vx+0), a ; пишет (n - vx) вместо n
;repro.c: }
ret
```
С `--no-peep` дефект тот же (отличается лишь форма ветвления):
```asm
_update::
ld iy, #_vx
sub a, 0 (iy) ; A (= n) разрушен
jp NZ, 00112$
jp 00103$
00112$:
ld (_vx+0), a ; пишет (n - vx)
00103$:
ret
```
## Почему так происходит
`n` приходит в `A` (sdcccall). Кодогенератор выбирает `SUB A,(HL)` для вычисления
отношения `n != vx`. `SUB` перезаписывает `A` разностью. Далее генератор считает,
что ещё «живое» значение `n` по-прежнему в `A`, и для присваивания выдаёт голую
запись `LD (_vx),A`, не перезагрузив `n`. Поскольку дефект сохраняется при
`--no-peep`, он находится в кодогенерации (учёт регистров/времён жизни через
сравнение), а не в peephole-оптимизаторе.
Правильное преобразование сравнения — `CP (HL)`: он выставляет флаги ровно как
`SUB`, но сохраняет `A`, поэтому последующая запись была бы корректной без единой
лишней инструкции.
## Варианты, которые тоже воспроизводят
- `if (n == vx) return; vx = n;` (форма с ранним выходом)
- `n` как результат вызова функции, а не как параметр
- и `-mz80` по умолчанию, и `--opt-code-speed`
## Обходной путь (workaround)
Записывать в глобальную переменную **до** сравнения, чтобы разрушающий `SUB` не
оказался на пути записи; сравнивать сохранённую копию:
```c
void update(unsigned char n)
{
unsigned char old = vx;
vx = n; /* запись первой, A ещё держит n */
if (n != old) {
/* побочный эффект */
}
}
```
даёт корректное:
```asm
_update::
ld (_vx+0), a
ret
```
## Файлы в этом каталоге
- `repro.c` — минимальный пример
- `repro.asm` — вывод с опциями по умолчанию (дефект виден)
- `repro.nopeep.asm` — вывод с `--no-peep` (дефект сохраняется)
- `workaround.c` / `workaround.asm` — обход «запись до сравнения» (корректно)
## Поиск по трекеру
Поиск по баг-трекеру SDCC точного дубликата не нашёл. Ближайший по версии отчёт
#3834 «[Z80][SDCC 4.5] Compiler bug» — это **другой** дефект (`genPointerSet`,
переставленный порядок push/pop), а не данный случай «сравнение затирает A».