irq: фикс Phase 1 после отладки — DSS работает в IM 1, чейн всегда на 0x0038

Первая версия висла на первом прерывании. Две причины (verified по
docs/samples/sprinterIntLib.asm, SIO_CTC_KEY.asm и исходникам MAME):

- DSS работает в IM 1 (обработчик 0x0038); I=0x3F — наследие Spectrum
  ROM, НЕ таблица: чтение [I<<8|0xFF] давало мусор (0x00BF) и прыжок в
  никуда. Чейн из трамплина теперь ВСЕГДА jp 0x0038 (interrupted-PC на
  стеке = имитация RST 38); irq_remove безусловно восстанавливает IM 1
- CBL-фильтр по биту 7 порта 0xFE убран: при выключенном CBL бит
  подтянут к 1 (MAME kbd_fe_r: data |= 0xE0) — каждый кадр ложно
  уходил в чейн, user-handler не вызывался бы. Вернуть в Phase 2
  вместе с поддержкой CBL

Попутно подтверждено: порт 0x19 = SIO-A RR0 (Z84C015), бит 0 = Rx
Available; вектора встроенной периферии SIO 0x10..0x1E / CTC 0x06
(заливка 257×H ловит любые); внешний вектор 0xFF.

irqtest: диагностика I до установки + фаза без ESTEX; прогон в MAME:
~49 Гц, клавиатура жива, remove останавливает тики, чистый выход.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-07 09:05:28 +03:00
parent a6fe50e247
commit 5184415fc4
7 changed files with 126 additions and 57 deletions
+43 -13
View File
@@ -1,27 +1,57 @@
# IM2 Interrupt Handlers — Design Document
**Status: Phase 1 РЕАЛИЗОВАН 2026-07-06** (libc/irq: irq_install/
irq_remove + трамплин; тест tests/irqtest). Отличия реализации от
плана ниже:
**Status: Phase 1 РЕАЛИЗОВАН 2026-07-06, ИСПРАВЛЕН после отладки**
(libc/irq: irq_install/irq_remove + трамплин; тест tests/irqtest).
## Результаты verification (2026-07-06, по docs/samples и исходникам MAME)
Первая версия зависала на первом же прерывании после irq_install.
Причины и проверенные факты:
- **DSS работает в IM 1** (обработчик на 0x0038). I=0x3F — наследие
Spectrum ROM, НЕ признак IM 2-таблицы. Доказательства:
`docs/samples/sprinterIntLib.asm` (intRestoreDefaultInterrupt: `ld i,a`
+ `im 1` безусловно, «обязательно перед функциями дос и биос») и
`docs/samples/SIO_CTC_KEY.asm` (выход: `LD I,A` + `IM 1`). Чтение
«таблицы DSS» по [I<<8+0xFF] давало мусор (0x00BF) и было причиной
зависания. Фикс: чейн из трамплина ВСЕГДА на 0x0038 (jp с
interrupted-PC на стеке = имитация RST 38); irq_remove всегда
восстанавливает IM 1 (I — только регистр).
- **CBL-фильтр по биту 7 порта 0xFE убран из трамплина**: в MAME при
выключенном CBL бит 7 всегда = 1 (sprinter.cpp kbd_fe_r:
`data |= 0xE0`), и каждое кадровое прерывание ложно классифицировалось
как CBL — user-handler не вызывался бы никогда. Признак «#fe.bit7=1»
(sprinterIntLib.asm) имеет смысл только при активном CBL — вернуть в
Phase 2 вместе с поддержкой CBL.
- **Порт 0x19 = SIO-A RR0 (Z84C015), бит 0 = «Rx Character Available»** —
семантика клавиатурного пробника верна (подтверждено
sprinterIntLib.asm: `in a,(COM_A); bit 0,a; Z → кадровое`).
- **Вектора встроенной периферии Z84C015**: SIO — 0x10..0x1E, CTC — 0x06
(базовые вектора задаются записью в WR2 SIO-B / CTC ch0). Штатно их
прерывания выключены (сэмплы включают/выключают их сами); наша
заливка 257×H перехватывает любой вектор на трамплин, а чейн на
0x0038 безопасен для любого источника.
- Внешний вектор действительно 0xFF (MAME sprinter.cpp:
`set_irq_acknowledge_callback` → 0xff).
Отличия реализации от плана ниже:
- **отдельный `--memory im2` НЕ понадобился**: таблица — статический
буфер 513 Б в BSS с выравниванием в рантайме; т.к. Sprinter шлёт
только вектор 0xFF, значимы лишь байты [0xFF]/[0x100], и 3-байтовый
буфер 513 Б в BSS с выравниванием в рантайме; т.к. внешний вектор —
только 0xFF, значимы лишь байты [0xFF]/[0x100], и 3-байтовый
`jp _irq_tramp` лежит ВНУТРИ таблицы по смещению H (H = старший
байт её адреса, < 0xC0 — не пересекается). Никаких linker-областей
и правок crt0;
- работает в tiny/big (код и данные в W2); в small/huge irq_install
возвращает EINVAL (проверка адресов трамплина/буфера);
- **чейн к DSS — ВСЕГДА** (и клавиатура, и кадр, и CBL): не нужно
знать, что DSS делает в своём ISR — SYSTIME/клавиатура/мышь живут.
User-handler зовётся только на кадровых (фильтр: бит 0 порта 0x19,
бит 7 порта 0xFE → мимо); финальный jp — SMC-операнд;
- **чейн к DSS — ВСЕГДА и всегда на 0x0038** (и клавиатура, и кадр):
не нужно знать, что DSS делает в своём ISR — SYSTIME/клавиатура/мышь
живут. User-handler зовётся только на кадровых (фильтр: бит 0
порта 0x19 → мимо); финальный jp — SMC-операнд;
- W3-порт в трамплине НЕ сохраняется: gfx держит DI на время свопов,
а user-handler'у banking запрещён; DSS свои окна сохраняет сам;
- irq_remove вешается на atexit (выход без снятия = I указывает в
память умершего процесса = крах шелла); old_I==0 → восстановление IM 1;
- открытые вопросы 1 (что делает DSS ISR) и 2 (ld a,i) сняты
дизайном «всегда чейн» и безусловным EI.
- irq_remove вешается на atexit (выход без снятия = IM 2/I указывают в
память умершего процесса = крах шелла); восстановление — всегда IM 1.
Phase 2 (CBL/COVOX handler, ISA, цепочка нескольких хендлеров) — ниже
по плану, не реализовано.