docs: справочники по железу Sprinter + правка gfx_scroll_h
- docs/new/ — сводные справочники (архитектура, BIOS, DSS, память, графика, акселератор, IRQ, порты, ввод, звук, известные баги); - docs/Original/ — первоисточники, из которых они собраны (BIOS, Estex DSS, мануалы, описание акселератора), + Форум.doc/.docx в reference; - libbgi/common/gfx_scroll_h.c — обход бага скролла при ширине >256 (правка автора: шаг банды 255 и продвижение указателей на cw; старый вариант с 256 оставлен закомментированным с TODO); - удалён applications/PoP/roomtest/hang_variants.png — рабочая раскладка из разбора позы виса, в репозитории ей не место. Большие архивы (docs/extra ~568 МБ, docs/sources с вложенными git-репо ~68 МБ) в коммит НЕ включены — см. обсуждение. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,387 @@
|
||||
# 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`
|
||||
Reference in New Issue
Block a user