# 4. Управление памятью Sprinter Sp2000 ## 4.1 Аппаратная модель памяти Z80 видит 64 КБ, разбитые на **четыре окна (window) по 16 КБ**. Каждое окно независимо маппится на физическую страницу (page) ОЗУ или ПЗУ через порт-регистр страницы: | Окно | Адреса | Порт страницы | Назначение | |------|--------|---------------|------------| | W0 | `#0000..#3FFF` | `#82` (PAGE0, ROM.SLOT0) | RST-обработчики, BIOS/DSS ядро | | W1 | `#4000..#7FFF` | `#A2` (SLOT1, PAGE1) | Приложение | | W2 | `#8000..#BFFF` | `#C2` (SLOT2, PAGE2) | **Стек — обязательно здесь** | | W3 | `#C000..#FFFF` | `#E2` (SLOT3, PAGE3) | Разделяемые страницы DSS, графика, резидентный код | - Запись в порт `#82`/`#A2`/`#C2`/`#E2` меняет физическую страницу окна; чтение возвращает текущую. - Для совместимости со старыми моделями (ZX-Spectrum 128, Scorpion, Pentagon) работают также порты `#7FFD`, `#1FFD`, `#DFFD`, `#EFF7`: | Порт | Описание | |------|----------| | `#7FFD` | Биты 0-2: страница W3; бит 3: выбор ПЗУ (0=DOS, 1=Basic/48) | | `#1FFD` | Биты 0-2: страница W0; бит 3: дополнительная память; бит 4: турбо | | `#DFFD` | Страница W1 (Pentagon, когда бит 5 порта `#7FFD`=0) | | `#EFF7` | Страница W2 (Pentagon) | > **Страница `#FF`:** если окно не выделено программе (образ меньше 16/32 КБ), DSS маппит его на специальную страницу `#FF`. Чтение даёт `#FF`, запись игнорируется. Это ключевая причина «молчаливой» порчи данных — см. §4.9. > **Физические страницы:** порты `#82`/`#A2`/`#C2`/`#E2` адресуют физические 16-КБ страницы в адресном пространстве Sprinter. Диапазон страниц зависит от установленной памяти (обычно 0..127 для 2 МБ, 0..255 для 4 МБ). Старшие страницы заняты видео-ОЗУ и ПЗУ. --- ## 4.2 Окно W0 (#0000..#3FFF) — системное W0 — критическое окно: в нём живут обработчики `RST 08h` (BIOS), `RST 10h` (DSS/ESTEX) и `RST 18h` (BIOS-EXP). Пока в W0 стоит системная страница, вызовы доступны. **ROM/RAM switching:** | Действие | Код | |----------|-----| | Включить ПЗУ в W0 | `OUT (#7C),A` (SYS_PORT.ROM) | | Выключить ПЗУ, вернуть ОЗУ | `OUT (#3C),A` (SYS_PORT.RAM) | При включённом ПЗУ в W0 отображается системная страница BIOS. Для вызова RST-функций W0 должен быть в ПЗУ — BIOS сам переключает его при входе через `RST 08h`. **Требования к прерываниям:** Sprinter работает в режиме **IM1** — вектор прерывания фиксирован на `#0038` (всегда в ПЗУ/ОЗУ W0). Обработчик EXP.asm по адресу `#0038`: 1. Переключает W3 на SYS_PAGE. 2. Проверяет флаг `INT_ID` = `#AA`. 3. Если есть пользовательский обработчик — вызывает его (адрес из SYS_PAGE). 4. Восстанавливает W3, `EI`, `RETI`. Регистр `I` инициализирован в `#3F` (наследие ZX-Spectrum), но при IM1 не используется для диспетчеризации. Если программа переключается в **IM2** — `I` и расположение таблицы векторов определяются программой произвольно, стандартного расположения нет. **DSS и W0:** при вызове `RST 10h` DSS переключает W0 на свою рабочую страницу (COREPAGE = 4), выполняет функцию и восстанавливает W0. Если в W0 до вызова была ОЗУ, DSS восстановит её; если ПЗУ — вернёт ПЗУ. **Практические рекомендации:** - Для вызова системных функций держите W0 в ПЗУ (или не заботьтесь — RST 08h и RST 10h его переключат сами). - При IM1 (режим по умолчанию) прерывание идёт на `#0038` — это ПЗУ, переключение W0 не требуется. - При IM2 программа сама управляет таблицей векторов — не забывайте про `DI`/`EI` при переключении W0. **Размещение своей RAM-страницы в W0:** Если программе нужно дополнительное окно (например, для атласов спрайтов или кода, не требующего системных вызовов), можно временно маппить свою страницу в W0. Для безопасной работы страница должна содержать корректную таблицу RST-векторов в первые `#40` байт: | Адрес | Инструкция | Назначение | |-------|-----------|------------| | `#0000` | `JP boot` или `RST 00h` (обычно не используется) | | | `#0008` | `JP rst08_stub` | RST 08h — BIOS | | `#0010` | `JP rst10_stub` | RST 10h — ESTEX/DSS | | `#0018` | `JP rst18_stub` | RST 18h — BIOS-EXP | | `#0020..#0030` | `JP _w0_unused_stub` | RST 20h-30h — не используются | | `#0038` | `JP isr_stub` | IM1 (RST 38h) | | `#0066` | `RETN` или `JP nmi_stub` | NMI | Каждый стаб (в этой же странице, выше `#40`) выполняет одну задачу: сохранить текущий номер страницы W0, переключить W0 обратно на ПЗУ (`OUT (#7C),A` — SYS_PORT.ROM), выполнить настоящий системный вызов, после чего восстановить страницу программы и вернуться к исходному caller'у. Типовой паттерн для ESTEX (`RST 10h`): ```asm rst10_stub: IN A, (#82) ; сохранить свою страницу W0 PUSH AF OUT (#7C), A ; включить ПЗУ в W0 (SYS_PORT.ROM) ; теперь в W0 — системная страница, RST 10h будет обработан корректно RST #10 ; вызвать настоящий ESTEX (DSS сам переключит W0 ; на COREPAGE и восстановит ПЗУ на выходе) POP BC LD A, B OUT (#82), A ; вернуть свою страницу в W0 RET ; возврат к исходному caller'у ``` Такой же паттерн для RST 08h и RST 18h. Для IM1 (`RST 38h`) — аналогично, но прерывание может прийти в любой момент, поэтому критические секции (своя страница в W0) обязаны быть обёрнуты в `DI`/`EI` (или проверять вложенность через счётчик), иначе повторное прерывание при уже замапленной странице попадёт в стаб, а не в EXP.asm. **Важно:** пока в W0 стоит ваша страница, системные вызовы (`RST 08h`/`10h`/`18h`) проходят через стабы и работают корректно, но с накладными расходами (~10+ тактов на каждый вызов). Код на странице не должен вызывать DSS/BIOS напрямую — только через RST. При этом сам стаб не должен использовать RST-инструкции, так как они адресуют W0, который в момент вызова стаба всё ещё содержит вашу страницу, а не ПЗУ — сначала переключите ПЗУ через `OUT (#7C),A`. Перед первым маппингом своей страницы в W0 сохраните текущее значение порта `#82` (`IN A,(#82)` — оно даёт номер системной/ПЗУ-страницы). Эта страница понадобится стабам для восстановления после вызова во время прерываний, когда ПЗУ уже может быть в W0, а стаб не успел его переключить. Подтверждённая реализация: `tests/w0page` (см. `sprite-api-design.md §9в`). --- ## 4.3 Окно W1 (#4000..#7FFF) — приложение W1 — основное окно для кода и данных приложения. По умолчанию содержит страницу 5 (RAM, инициализируется BIOS). **Использование BIOS/DSS:** BIOS и DSS могут **временно** переключать W1 на свою рабочую страницу. Это происходит: - При файловых операциях DSS: чтении FAT, загрузке секторов — DSS маппит одну из страниц разделяемого пула в W1 (DIRPAGE, FATPAGE, TXTPAGE, DRVPAGE). - При графических операциях BIOS: `PIC_FN2`/`PIC_FN3` (блоковые копии) могут переключать W1 на страницу видео-ОЗУ. - **Важно:** перед возвратом из вызова BIOS/DSS **восстанавливает** предыдущую страницу W1. Типовой паттерн в исходниках DSS: ```asm IN A,(SLOT1) ; сохранить текущую страницу W1 PUSH AF ... работа с разделяемым буфером ... POP BC LD C,SLOT1 OUT (C),B ; восстановить W1 ``` Для вызывающей программы переключение W1 полностью прозрачно — после возврата из любой функции DSS/BIOS содержимое W1 идентично тому, что было до вызова. **Практические рекомендации:** - Размещайте код и данные в W1, рассчитывая, что он остаётся стабильным между вызовами. - При длительных файловых операциях (последовательный `READ` больших объёмов) W1 может быть затронут только в моменты внутреннего обращения DSS к FAT/буферам, но не во время собственно чтения данных в буфер программы. - Если нужно гарантированно сохранить данные через вызов DSS — используйте W2, который BIOS/DSS **никогда не трогают**. --- ## 4.4 Окно W2 (#8000..#BFFF) — стек (критично) W2 — **единственное окно, которое BIOS и DSS гарантированно не трогают**. Поэтому стек ОБЯЗАН находиться здесь: - **SP ≤ #BFFF** при вызовах DSS (`RST 10h`). - **SP ≥ #8000** при вызовах BIOS (`RST 08h`). - Пересечение → стек в `#8000..#BFFF`. - По умолчанию SP инициализируется в `#BFFE` (значение из EXE-заголовка, §4.7). **Почему W2 — единственное стабильное окно:** | Окно | Может быть переключено BIOS/DSS | Почему | |------|--------------------------------|--------| | W0 | Да | DSS переключает на COREPAGE при `RST 10h`; BIOS переключает на ПЗУ | | W1 | Да, временно | DSS маппит разделяемые буферы для FAT/директорий; BIOS при графических операциях — и **восстанавливает** перед возвратом | | W2 | **Нет** | **Никогда. Стек должен быть доступен всегда.** | | W3 | Да, временно | DSS маппит разделяемые буферы; BIOS при графике переключает на видео-ОЗУ; оба **восстанавливают** исходную страницу перед возвратом | **EXEC и W2:** при загрузке новой программы (`EXEC`, функция 40h) DSS сохраняет страницу W2 в EXSTACK (вместе с W1 и W3). После завершения программы (`EXIT`, функция 41h) W2 восстанавливается. **Практические рекомендации:** - Размещайте стек строго в W2. Стандартное значение `SP = #BFFE` — начало стека с запасом ~14 КБ до `#8000`. Можно уменьшить, если программа неглубокая. - W1 и W3 при вызовах DSS и BIOS **сохраняются и восстанавливаются** (см. §4.3, §4.5), но W2 — единственное окно, которое **никогда не трогается** ни DSS, ни BIOS. - Если программе нужно больше памяти, чем даёт W2 (например, для хранения больших массивов), выделяйте блоки через DSS `GETMEM` (функция 3Dh) и мапьте их в W1 или W3. --- ## 4.5 Окно W3 (#C000..#FFFF) — разделяемые страницы W3 — окно, в которое DSS временно маппит страницы своего разделяемого пула для дисковых буферов и служебных данных (адрес `#C000`): | Страница | Назначение | Адрес в W3 | |----------|------------|-------------| | DIRPAGE (0) | Буфер директории | `#C000` | | FATPAGE (1) | Кеш FAT | `#C000` | | TXTPAGE / ENVPAGE (2) | Буфер строки/окружения | `#C000..` (PATH_PNT_ARRAY `#FC80`, ENVTEMP `#FE00`) | | DRVPAGE (3) | Страница драйвера | `#C000` | | COREPAGE (4) | Ядро DSS (маппится в W0 при `RST 10h`) | — | **DSS СОХРАНЯЕТ и ВОССТАНАВЛИВАЕТ W3** после каждой операции. Типовой паттерн в исходниках: ```asm IN A,(SLOT3) ; сохранить текущую страницу W3 PUSH AF ... работа с разделяемым буфером ... POP BC LD C,SLOT3 OUT (C),B ; восстановить W3 ``` Это означает, что для вызывающей программы переключение W3 полностью прозрачно — после возврата из любой функции DSS содержимое W3 идентично тому, что было до вызова. **Какие функции трогают W3:** Функции, обращающиеся к файловой системе, временно маппят разделяемые страницы в W3: поиск файла (`F_FIRST`/`F_NEXT`), чтение FAT (`OPEN`, `READ`, `WRITE`), работа с каталогами (`CHDIR`, `MKDIR`), буферы драйвера диска. Функции без дисковой активности (`VERSION`, `CURDISK`, `WAITKEY`, `LOCATE`, `SYSTIME`, `ENVIRON` и т.п.) W3 не трогают вообще. BIOS при графических операциях также временно переключает W3 на страницы видео-ОЗУ (например, `PIC_SET_PAL`, `PIC_FN2`) — и тоже восстанавливает. **Практические рекомендации:** - W3 можно использовать для данных программы — после возврата из любой функции DSS содержимое W3 не меняется. - Единственное исключение — `EXEC` (функция 40h): она загружает новую программу, и W3 получает страницы загруженного образа. Это ожидаемое поведение. - Если программа не вызывает дисковых и графических функций — W3 полностью стабилен. - **Стек нельзя размещать в W3** — не потому что W3 «теряется», а потому что переключение W3 происходит ВО ВРЕМЯ вызова. Если `SP` указывает на W3, то в момент, когда DSS временно маппит свой буфер, все push/pop внутри DSS будут обращаться к буферной странице, а не к данным программы. Кроме того, при прерывании IM1 обработчик EXP.asm сам переключает W3 на SYS_PAGE — что также сломает стек, если он в W3. --- ## 4.6 Физическая память и страницы Физическая память Sprinter — не сплошной массив RAM. Разные области адресного пространства зарезервированы под видео-ОЗУ, ПЗУ и кеш. Номер страницы — это значение, которое записывается в порт окна (`#82`/`#A2`/`#C2`/`#E2`); 8-битный порт даёт **256 значений** (`#00..#FF`). **Фиксированная карта страниц:** | Диапазон | Страниц | Назначение | |----------|---------|------------| | `#00..#4F` (0..79) | 80 (1,25 МБ) | RAM общего назначения | | `#50..#5F` (80..95) | **16 (256 КБ)** | **Видео-ОЗУ (VRAM) — фиксировано в железе** | | `#60..#DF` (96..223) | 128 (2 МБ) | RAM общего назначения (верхний диапазон) | | `#E0..#EF` (224..239) | **16 (256 КБ)** | **ПЗУ: BIOS + EXP + ZX ROMs + bitstream — фиксировано** | | `#F0..#FF` (240..255) | 16 (256 КБ) | **Fast RAM (кеш) / системные страницы** | **Выделенные страницы:** | Страница | Назначение | |----------|------------| | `4` | **COREPAGE** — ядро DSS (маппится в W0 при `RST 10h`) | | `5` | Стандартная страница приложения W1 (по умолчанию) | | `#41` (65) | **Spec_Page** — сохранение состояния при soft-reset [EXP.asm:859,1019] | | `#FE` (254) | **SYS_PAGE** — системные данные BIOS (INT_ID, INT_ADRESS, буферы) [MAIN.asm:58] | | `#FF` (255) | **Dummy-страница**: чтение → `#FF`, запись игнорируется | **Ключевые моменты:** - **VRAM (`#50..#5F`) фиксирована аппаратно**, независимо от объёма RAM. Эти 16 страниц не являются частью RAM SIMM — это отдельная микросхема 256 КБ. Биты 2-3 номера страницы кодируют режим записи: `#50` normal, `#54` без ОЗУ-тени, `#58` прозрачный (байт `#FF` не пишется), `#5C` оба режима. - **ПЗУ (`#E0..#EF`)** — 256 КБ флеш-памяти с BIOS, EXP, ZX Spectrum 48/128 ROM, TR-DOS, логотипом и битстримом для CPLD. - **Fast RAM (`#F0..#FF`)** — до 512 КБ быстрого SRAM (0 wait-states), доступна через порт `#FB`. `#FE` (SYS_PAGE) и `#FF` (dummy) — часть этого диапазона. При включённом кеше порт `#FB` конфликтует с CBL-звуком. - Для 2 МБ RAM: `#60..` заполнены до `#7F` (32 стр. = 512 КБ); остаток `#80..#DF` — неиспользуемые страницы (отображаются как `#FF`). - Для 4 МБ RAM: `#60..#DF` целиком заполнены RAM (128 стр. = 2 МБ). > **Нумерация:** порты `#82`/`#A2`/`#C2`/`#E2` дают прямой доступ по > физическому номеру (#00..#FF). Порты `#7FFD`/`#1FFD` (совместимость с > ZX-Spectrum 128) используют сквозную нумерацию: номер в `#7FFD` (0..7) > маппится на физические страницы видеоОЗУ `#50..#5F` младшими 3 битами; > для работы с произвольными страницами используйте порты `#82`/`#A2`/`#C2`/`#E2`. --- ## 4.7 EXE-файл: формат и загрузка Загрузка исполняемых файлов выполняется функцией DSS `EXEC` (40h). Формат EXE-файла: **Заголовок (512 байт):** | Смещение | Размер | Поле | Описание | |----------|--------|------|----------| | +0 | 3 | `EXE_EXT` | Сигнатура `"EXE"` | | +3 | 1 | `VERSION` | Минимальная версия DSS (1 = v1.xx, 0 = без пути/аргументов) | | +4 | 2 | `OFFCOD1` | Младшее слово смещения образа в файле | | +6 | 2 | `OFFCOD2` | Старшее слово смещения (OFFCOD2 << 16 + OFFCOD1 = offset) | | +8 | 2 | `LOADER` | Размер первичного загрузчика (0 = весь образ сразу) | | +10 | 6 | `RESERVED` | Зарезервировано | | +16 | 2 | `LD_ADDR` | Адрес загрузки (`#4100..#FFFF`, старший байт маскирован `#3F`) | | +18 | 2 | `PC_REG` | Точка входа (entry point) | | +20 | 2 | `SP_REG` | Начальный SP (по умолчанию `#BFFE`) | | +22 | 1 | `UnUsedPoint` | Не используется | | +23 | 489 | `RESERVED2` | Зарезервировано (обнуляется; используется как временный стек EXEC) | **Процесс загрузки (EXEC, функция 40h):** 1. Открыть файл на чтение. 2. Считать 512-байтный заголовок в буфер ядра. 3. Проверить сигнатуру `"EXE"` и версию (≥ минимальной 1). 4. Если `LOADER=0`: seek на смещение `OFFCOD2:OFFCOD1`, считать весь образ по адресу `LD_ADDR`. 5. Если `LOADER>0`: считать только первичный загрузчик по адресу `LD_ADDR` (он дочитает остальные сегменты). 6. Выделить блок памяти через `GETMEM` (функция 3Dh), зарегистрировать в таблице RAM. 7. Сохранить текущие страницы окон (SLOT1-3) в EXSTACK. 8. Определить, сколько страниц нужно, и замаппить их через `SETWIN1`/`SETWIN2`/`SETWIN3`: - Если `LD_ADDR ≥ #C000`: маппится только W3. - Если `LD_ADDR ≥ #8000`: маппятся W2 и W3. - Иначе: маппятся W1, W2 и W3. 9. Построить **PSP** (Program Segment Prefix) — блок данных сразу ниже `LD_ADDR`: - `LD_ADDR-3`: файловый манипулятор (file handle). - `LD_ADDR-2`: идентификатор блока памяти. - `LD_ADDR-1`: номер задачи. - `LD_ADDR+0`: размер CLP (командной строки). - `LD_ADDR+1..+128`: CLP-буфер. - После CLP: строка пути `"A:\DIR\FILE.EXE",0`. 10. Установить SP из заголовка (`SP_REG`), сохранить IX → адрес PSP. 11. Если `VERSION=0` — установить текущий каталог из пути PSP. 12. Затолкать в стек адрес возврата (RETFAR к DSS), **EI**, JP на точку входа. **Multi-bank EXE:** При наличии нескольких банков (`LOADER>0`) файл устроен так: ``` [512-байт заголовок] [HOME-сегмент] [Банк 1 (16 КБ)] [Банк 2 (16 КБ)] ... ``` - HOME-сегмент: образ, загружаемый DSS по `LD_ADDR`. - Банки: 16-КБ блоки, каждый на своей физической странице. - `LOADER` = размер HOME-сегмента (DSS загружает только его; первичный загрузчик сам дочитывает банки через `READ` из оставшегося открытым файла). --- ## 4.8 Прерывания и память **Обработчик IM1 (`RST 38h`):** Вектор фиксирован на `#0038` (W0). Реализация в EXP.asm: 1. Переключает W3 на SYS_PAGE (BIOS). 2. Проверяет флаг `INT_ID` = `#AA` (установлен при инициализации BIOS). 3. Если флаг совпадает — вызывает пользовательский обработчик, адрес которого хранится в SYS_PAGE (`INT_ADDRESS`, `INT_PAGE`). 4. После возврата восстанавливает W3 и выполняет `EI; RETI`. Это позволяет пользовательским программам устанавливать свои обработчики INT без полного перехвата вектора. **Обработчик IM1 (системный):** При возникновении прерывания Z80 исполняет `RST 38h` → вызов по адресу `#0038`. Реализация в EXP.asm (всегда доступна, т.к. `#0038` в W0): 1. Переключает W3 на SYS_PAGE (BIOS). 2. Проверяет флаг `INT_ID` = `#AA` (установлен при инициализации BIOS). 3. Если флаг совпадает — вызывает пользовательский обработчик, адрес и страница которого хранятся в SYS_PAGE (`INT_ADDRESS`, `INT_PAGE`). Это позволяет программам устанавливать свои ISR без полного перехвата вектора. 4. После возврата восстанавливает W3 и выполняет `EI; RETI`. **Обработчик IM2 (опционально, для продвинутых программ):** Sprinter по умолчанию не использует IM2. Исходники содержат шаблон `IM2_INT.asm` для программ, желающих переключиться на IM2: 1. Сохраняет AF, BC, DE, HL, IX, IY и теневые регистры. 2. Включает ПЗУ (`OUT (#7C),A`) — KEYSCAN требует ПЗУ в W0. 3. Вызывает KEYSCAN (опрос AT-клавиатуры). 4. Восстанавливает все регистры, `EI`, `RETI`. При переходе на IM2 программа сама выбирает значение `I` и размещает таблицу векторов в соответствующем диапазоне памяти. **Рекомендации:** - Если программа не использует прерывания — держите W0 в ПЗУ. - Если программа использует IM1 — можно переопределить обработчик через структуру в SYS_PAGE (флаг `INT_ID`, поля `INT_ADDRESS`/`INT_PAGE`). - Если программа использует IM2 — переключение полностью на ответственности программы; ядро Sprinter не предоставляет стандартного расположения таблицы векторов. --- ## 4.9 Типовые грабли - **Данные читаются как `#FF` / не сохраняются** — код или данные попали в незамапленное окно (страница `#FF`). Причина: образ программы меньше 16 КБ, DSS выделил только одну страницу, а код/данные распределены линкером в незамапленные окна. Лечение: убедитесь, что код и данные укладываются в одно окно, или явно выделяйте и мапьте остальные окна через `GETMEM`/`SETWIN1`-`SETWIN3`. - **Крэш при вызове RST при нестандартном W0** — если перед вызовом W0 содержал произвольную страницу ОЗУ, DSS при `RST 10h` переключит его, но может не восстановить корректно. Решение: перед вызовом RST включите ПЗУ (`OUT (#7C),A`), если не уверены в состоянии W0. - **Стек в W3 (не-рабочий вариант)** — если стек размещён в W3, при любом дисковом вызове W3 будет переключён на разделяемый буфер DSS, и стековые данные будут забиты. Результат: мгновенное зависание при возврате из функции. Единственное рабочее место для стека — W2. - **CBL не работает при включённом кеше** — порт `#FB` используется как кеш-память (Fast RAM) и как порт CBL-звука. Если кеш включён, запись в `#FB` уходит в кеш, а не в CBL. Детали: `11-ports.md`, `12-bugs.md`. - **Прерывание уходит не туда при IM2** — если программа переключилась в IM2 с неинициализированной таблицей векторов, любое прерывание приведёт к исполнению мусора. Решение: используйте IM1 (режим по умолчанию) или инициализируйте таблицу перед переключением. - **Не путать W3 с «теряется после вызова»** — многие источники утверждают, что DSS сбрасывает W3. На самом деле DSS **сохраняет и восстанавливает** W3 после каждой операции. Стек всё равно нельзя размещать в W3 (см. §4.5), но для данных W3 полностью стабилен между вызовами. Если ваша программа полагается на старый миф — проверьте код. --- ## 4.10 Кросс-ссылки - Порты страниц и детали регистров: `11-ports.md` - BIOS (графика, окна): `02-bios.md` - DSS/ESTEX (EXEC, GETMEM, SETWIN): `03-dss.md` - Прерывания: `07-irq.md` - Баги (CBL vs кеш, порт #FB): `12-bugs.md` - EXE-формат и PSР: `memory/exe-format.md`