Opus Discovery (2)

Итак, приветствую вас во втором продолжении описания дисковой системы Opus Discovery; сегодня посмотрим на управление из ассемблера. Но прежде чем начну, хотел бы поблагодарить всех читателей, которые засыпали редакцию десятками (по словам Logoutа — сотнями) писем. Да, в исходной статье действительно одна ошибка: стандартный размер каталога — 7 секторов, а не 4, как я первоначально упоминал. А теперь уже поехали.

Дискета под лупой

Сначала посмотрим, как собственно данные на дискете хранятся.

Boot-сектор (сторона 1, дорожка 0, сектор 0) начинается меткой длиной 8 байт:

  • Первые два байта всегда 18H 05H.
  • Третий байт — число дорожек, то есть 28H (40 дорожек) или 50H (80 дорожек).
  • Четвёртый байт — число секторов на дорожку, например 12H (18 секторов на дорожку).
  • Пятый байт — плотность; она считается так:

Вес | Размер сектора + Вес | Число сторон дискеты ----+------------- ----+---------------- 0 | 128B 0 | Односторонний формат 1 | 256B 1 | Двусторонний формат 2 | 512B 3 | 1024B (вес64) + (вес16), то есть для формата с 256B на сектор и двумя сторонами получим (164)+(116)=80. Пятый байт так будет 50H.

  • Байты 6 и 7 случайные (чтобы дискета была уникальной).
  • Байт номер 8 всегда 7EH.

Тут на мгновение остановлюсь. Opus Discovery умеет обслуживать разные типы механизмов, но форматировать более чем на одну сторону и 40 дорожек — только если установлена внутренняя RAM. Более того, даже если RAM есть, после включения система всегда настроена на одностороннюю дискету и 40 дорожек. Если хотим форматировать на 800K, нужно выставить правильную конфигурацию диска именно в RAM. Простейший способ — загрузить дискету, у которой формат такой, какой хотим использовать. Достаточно CAT, и система прочитает настройку из BOOT-сектора и сохранит во внутренней RAM. Если затем отформатируем (любую — чистую или уже отформатированную) дискету, система использует настройки именно с последней загруженной дискеты.

Сразу после BOOT-сектора следует КАТАЛОГ. Это, собственно, первый файл на дискете; стандартно он длиной 7 секторов. Отдельные записи каталога всегда длиной 16 байт и выглядят так:

  • Первые два байта — нижние 12 бит: значение, сколько байт занято в последнем секторе данного файла −1. Верхние 4 бита использует DOS.
  • Байты 3 и 4 — номер первого сектора данного файла.
  • Байты 5 и 6 — номер последнего сектора данного файла.
  • Байты 7–16 — имя файла (10 символов; если имя короче — дополнено пробелами).

Первая запись в каталоге всегда содержит информацию о самом каталоге, и имя файла — это название дискеты.

Последняя запись в каталоге всегда конечная метка:

  • Первый байт всегда FFH.
  • Второй байт всегда 00H.
  • Третий и четвёртый байт содержат общее число секторов на дискете.
  • Пятый и шестой байт всегда значение FFH FFH.
  • Последние 10 байт снова — название дискеты.

Здесь должен предупредить о способе, которым система работает с каталогом. Он хоть и занимает 7 секторов, но они заполняются постепенно по мере надобности. Каталог свежеотформатированной дискеты так будет содержать две записи — первую и последнюю — и сразу друг за другом. Остаток каталога затем заполнен значением E5H (229). Посмотрим какой-нибудь пример. Дискета содержит единственный файл, каталог так будет иметь 3 записи:

FF 00 00 00 06 00 RestoreMag 06 00 07 00 22 00 artIIscr FF 00 3F 0B FF FF RestoreMag E5 E5 E5 E5 E5 E5

В этом, собственно, преимущество: не всегда нужно загружать весь каталог — по метке FF FF можем узнать, где кончается его активная часть. В этом случае нам вполне достаточно загрузить первый сектор. Благодаря этому также можем менять размер каталога, не переформатируя дискету. Может пригодиться, если хотим либо увеличить число записей в каталоге, либо, наоборот, добавить немного места для самих данных.

Из этого вытекает ещё одна интересная вещь: файл всегда хранится как один непрерывный блок секторов. Это упрощает чтение; с другой стороны, при частом сохранении и удалении файлов возникает фрагментация свободного места, и может случиться, что система не сможет сохранить файл на дискету, если не найдёт достаточно большой непрерывный участок. Часто используемые дискеты поэтому нужно дефрагментировать (здесь это называется «compacting»), и для этого предназначена команда MOVE "d";1 TO "d";1. Итак, умеем найти файл на дискете — остаётся одно: узнать, что за файл перед нами. Это узнаём в начале самого файла: его первые 7 байт — заголовок:

  • Первый байт — тип файла: 0 — BASIC 1 — Numeric array 2 — Character array 3 — CODE
  • Второй и третий байт — длина файла (у BASIC — и с переменными).
  • Четвёртый и пятый байт — AUTOSTART у BASIC или адрес LOAD у CODE.
  • Шестой и седьмой байт — снова длина; у BASIC — без переменных.

Как в ROM зовут...

...так из ROM и откликается. Суть в том, что выбираем службу, которую хотим использовать (номер записи в правильной таблице), вызываем ROM Opus Discovery, и она возвращает нам адрес данной службы. Преимущество — совместимость разных версий ROM, и полностью отпадает забота о типе дискеты (односторонняя versus двусторонняя и тому подобное).

Но уже наконец загружаем!

Выше показали пример каталога с одним файлом — это вступительная картинка из программы Artist II. Давайте загрузим её в экран (код разделил на секции, комментарий ниже):

org 40000

Start call 5896 ld b,0 rst 48 defb 18

ld (LoadAddr+1),hl

ld hl,7 ld de,16384-7 ld c,0 ld b,27

LoadLoop call LoadSect inc d

inc hl djnz LoadLoop

ld c,7 call LoadSect

End jp 5960

LoadSect push bc push de push hl ld a,1 ld b,2 LoadAddr call 0 pop hl pop de

pop bc ret

Рутина релоцируема, так что адрес трансляции произвольный (все значения в десятичной системе).

CALL 5896 подкачивает ROM Opus Discovery. Регистр B затем заполняем записью в таблице служб, которую хотим использовать; в этом случае — сразу первая (нулевая). RST вызывает службу, а defb за ним содержит номер таблицы, которая нас интересует (в этом случае — запись «Call the hardware» таблицы SYSTEM UTILITY TABLE). Система возвращает адрес службы в HL.

Затем настраиваем счётчики. HL содержит сектор, который хотим читать. DE содержит адрес в памяти, куда данный сектор загрузится (снова напоминаю о 7-байтовом заголовке в начале файла — поэтому грузим несколько байт в ROM). Регистр C говорит системе, сколько байт из сектора хотим загрузить; 0 означает 256 байт. А регистр B используем как счётчик секторов.

Внимательный читатель наверняка заметил, что секторы нумеруем с 0, но при этом речь уже о начале КАТАЛОГА. Так и есть: для загрузки BOOT-сектора служит отдельная служба (и ей посвятим следующий раз).

В цикле LoadLoop постепенно загружаем то, что хотим. Заголовок файла, однако, испортил выравнивание на 256 байт, так что последние 7 загружаем в конце. JP 5960 обеспечивает подкачку ROM ZX Spectrum и возврат в BASIC.

Рутина LoadSect тоже в целом прямолинейна. Сохраняем регистры и вызываем load сектора с параметрами. Регистр A содержит номер механизма, а регистр B ещё уточняет службу; в нашем случае 2 означает READ (0=WRITE, 2=READ, 4=OPEN, 6=CLOSE, 8=LENGTH, 10=POSITION). CALL 0 — правильно: в начале мы ноль переписали правильным адресом из HL.

Изначально у меня был подготовлен и разбор каталога, но из-за нехватки времени (статью пишу уже неприлично поздно после дедлайна) оставляю это уважаемому читателю, либо до следующего номера.

Что дальше?

Итак, умеем загрузить сектор и, собственно, и файл с дискеты. Начал с этого намеренно, потому что это подошло к описанию дискеты как таковой. У Opus Discovery, разумеется, есть службы на загрузку и сохранение файла, чтение и просмотр каталога и тому подобное — на это посмотрим именно в следующей части. Рекомендую до тех пор вспомнить работу со STREAM-ами и каналами: система ими широко пользуется (и это же причина, почему сдвигает BASIC при работе с дискетой).

Так до следующего раза!

ellvis/zeroteam

Поделитесь вашим мнением о статье