Вернуться на ГЛАВНУЮ
страницу
Вернуться к СПИСКУ СТАТЕЙ
Автор: Максим Тимонин aka Максагор
Дата: 23.08.2015 г.
Примечание: опубликовано в электронном ZX-журнале Info Guide #11 за 12.08.2015 г.
Выражаю огромную благодарность создателям iS-DOS - сотрудникам фирмы Iskra-Soft - и автору ядра TASiS Юрию Корсунину за прекрасное семейство ОС для Спектрума и за материалы по ним,которые были использованы при написании данной статьи.
Зачем? Или об устаревшем TR-DOS и iS-DOS/TASiS как его замене
Старожилы Спектрума времён его расцвета в экс-СССР в 90-е годы помнят ту романическую атмосферу стремления к развитию своей любимой машины - создания удобных инструментов работы с данными и разработки программ, служебные утилиты, и прочее, и прочее, и прочее. Делались попытки развития как аппаратной части, так и программной среды, исходя из базового восприятия Спектрума как пусть и не самого мощного, но КОМПЬЮТЕРА, как отдельной платформы, которую можно ВСЕСТОРОННЕ и полноценно ИСПОЛЬЗОВАТЬ. В этой же плоскости лежали и многочисленные попытки написания полноценной операционной системы, графических интерфейсов, многозадачности и прочее.
Последнее - ОСеписание - хочу отметить особо и на нем остановиться. Ведь что получалось? Хочется расширить память? Вот вам схема её напайки. Повысить скорость? Вот турбо-режим. Улучшить графику? Вот схема напайки всяких гигаскринов и прочих аппаратных мультиколоров. Звук? Вот вам аж целый General Sound. И только одно ограничение было не по зубам тогдашним умельцам: какие бы навороты они ни припаивали к своим Спектрумам, всё это продолжало запускаться и работать из-под ОС TR-DOS, являющейся по сути эмулятором ленточного интерфейса на дисководе, с её четырьмя дискетками ёмкостью 640КБ, "которых хватит на всё". А вот стало быстро не хватать, даже с учётом скромных запросов Спекки. И я говорю не о нескольких игрушках на 2-3 дискеты целиком. Просто количество софта (игр, демок, системок) все 90-е годы росло уважающими себя темпами, а пропорционально этим темпам росли и ряды дискет у спекки-пользователей, которые удручённо вздыхали, глядя на "коллег" с других машин (и ладно, если только пользователей IBM PC), удобно загружающихся с винчестеров. А железячники и рады были бы ответить на эти вздохи: "есть решение!" - и даже представляли в ответ IDE-контроллеры, но смысла в этом было никакого, ибо новый носитель требовал новые принципы работы с ним, коренным образом отличаясь от "стандартных" точек входа в ПЗУ TR-DOS. Быстро и неизбежно возникла, в принципе, здравая идея эмулировать (пусть и с частичной потерей совместимости) дискетки в виде фалов-образов. Это на первых порах породило такого монстра, как коллекции виртуальных дискет, выбираемых через сервис-монитор клона Scorpion ZS 256 + SMUC. Но это были почти "те же грабли, вид сбоку" - пользователь все равно был ограничен рамками 640КБ и сменяемыми (пусть и виртуально) дискетами. Остро нужна была среда для полноценной работы с любыми внешними устройствами хранения данных. И такие ОСи тоже появились.
Это, во-первых, CP/M (на разных Спектрумах было порядка 15 реализаций, начиная с 1986 года), прижившаяся полноценно на некоторых клонах и иногда прошиваемая в их ПЗУ; во-вторых, iS-DOS разных модификаций (Classic и Chic). iS-DOS, как менее требовательная, запускаемая практически на всех клонах, более-менее прижилась. По неё был написан ряд серьёзных программных пакетов и множество утилит помельче. Но в жизнь "каждого Спектрумиста" она всё же войти не смогла. И причиной тому оказались гири огромного количества написанного под TR-DOS и идущего только с флопа софта, не подлежавшего какой-либо очевидной адаптации под иную Ось. Плюс сила привычки программировать только под "Тырдосину" оказалось слишком большой - лень переучиваться под требования и соглашения иной Оси сыграла не последнюю роль.
Первое ограничение было преодолено на машинах с 1024 и более КБ ОЗУ, что позволило организовывать виртуальные дискеты в ОЗУ, загружая их с внешних устройств и прежде всего с винта. Получалось удобно – ОС играет роль среды, через которую старый TR-DOS'ный софт (точнее, значительную его часть) можно на ОСевых устройствах хранить и оттуда запускать, а уже новый софт, демки и игры писать под возможностиновой системы.
Но, как уже говорилось, второе ограничение - сила привычки - сыграло роковую роль, и Спектрум в целом остался TRD-зависимым, что, по моему мнению, для него, как для ПОЛНОЦЕННОГО КОМПЬЮТЕРА привело к печальным последствиям в наши дни, несмотря на то, что ОЗУ достигло 4 МБ, освоены HDD, CD-ROM, SD-карточки. На них в современных Спектрумах легко может размещаться практически всё программное обеспечение, написанное за все годы, но, пусть и в файловой системе FAT, всё равно в виде практически исключительно коллекций образов TRD (разве что помимо пары коммандеров и нескольких показательных демок). Никакой программной среды нет и в помине, а работа с FAT и монтирование образов осуществляется через коренным образом развившееся ПЗУ.
Но эта концепция, безусловно имеющая право на существование, привела к тому, что лишённый обязательной универсальной программной и пользовательской среды Спектрум всё больше стал напоминать игровые приставки DENDY, вся "работа" за которыми сводится к "вставил картридж, поиграл, вставил другой". Логичным образом появилось большое количество таких пользователей, для которых Спектрум - только повод запустить демку/игру, поностальгировать и выключить. А если и писать под него, то только кросс-средствами на ПиСи.
Нет, это неплохо. Это очень помогает в работе, и никто не собирается фанатично заставлять отказываться от них. Однако отказ от собственно РАБОТЫ на Спектруме и превращение его в глазах определенной категории Спектрумистов в ностальгическую DENDY-приставку с клавиатурой повлекло за собой деградацию целого ряда направлений софта. Для многих уже не дикими звучат даже не вопросы, а самоуверенные утверждения на форумах типа "Текстовые редакторы? А зачем они нужны? Разве на Спектруме их ещё кто-нибудь использует?" Такие люди правы со своей колокольни в том смысле, что если Спектрум нужен только поностальгировать и поиграть в старую или новую игрушку, то редакторы/конвертеры/языки действительно не нужны. ALASM тоже не нужен - всё можно написать на ПЦ и скомпилировать прямо в TRD-образ, который, в свою очередь, запустить с SD-карточки на реальном Спектруме.
Это нормально. Для Спектрума-ПРИСТАВКИ. Но для Спектрума-КОМПЬЮТЕРА - глухой тупик. И ненормально, что эта концепция "приставки" многими "на автомате" начинает восприниматься единственно верной – что уже породило ряд ранее немыслимых псевдо-религиозных споров типа "ХХХ - это не Спектрум, настоящий Спектрум - это 128K+AY+TR-DOS, а всё, что сверх этого, - ересь!".
Да, Спектрум сам по себе – андеграундная ретро-машина исключительно для любителей старины и ностальгически "вспомнить детство". Однако виды такой ностальгии и увлечения стариной могут быть самыми разными. И что делать тем, для которых предмет ностальгии - это полноценно РАБОТАТЬ за ПОЛНОЦЕННЫМ 8-битным компьютером? Что может предложить им Спектрум-ПРИСТАВКА? Те же коллекции сотен дискет, ограниченных убогой керналью TR-DOS и 640КБ флопика. Выход в свет ZX-Vega - это последняя точка деградации, когда Спекки лишили даже последнего атрибута персонального компьютера - его клавиатуры. И пусть есть достаточное количество "геймеров" - пусть себе радуются, никто не запрещает. Но статья написана для совсем других людей - спектрумистов-компьютерщиков. А для таких людей, которых вполне достаточное количество, вопрос об универсальной программной среде, иными словами - операционной системы - никуда не делся, а только со временем становился все острее. Ведь какой смысл в рамках 640КБ мечтать о разработке графической оконной среды? О многозадачности? Нет, тут нужна полноценная ОС как база, пусть пока и несовершенная, но позволяющая на её основе дальше разрабатывать и развивать софт, а не просто писать для ностальгических утех очередные демки и игрушки.
А между тем, эта операционная система есть. Точнее никуда не девалась. Ибо ОС iS-DOS выжила. Более того, для двух клонов Спектрума она в прошлом и настоящем сумела как раз стать основной, используемой "по умолчанию" - это KAY-1024 + HDD, ныне почти "вымерший", и возрождённый в середине 2000-х годов ATM-turbo 2+, где iS-DOS получила своё второе дыхание в виде новой реинкарнации OS TASiS. KAY как клон уже практически ушёл в прошлое, а вот ATM - жив и производится до сих пор как собственно в классическом варианте, так и в виде дальнейшего развития в рамках ZX Evolution/Baseconf. Правда, стандартный дистрибутив TASiS на ZX Evo пока что можно запустить только с дискеты, так как из-за иного стандарта контроллера IDE и своих особенностей в ПЗУ для установки системы на винт требуется переделка драйверов и автозагрузчика с винта. Но я, как один из разработчиков софта под iS-DOS и соавтор TASiS, могу уверенно сказать, что данная работа в ближайшем будущем будет сделана.
Кстати, раз уж был упомянут ZX Evolution/Baseconf, то там в последнее время наметились положительные сдвиги в сторону "компьютеризации" – адаптирована штатная ATM-версия CP/M, ведутся работы по переносу не много не мало, а самой MSX-DOS 2. И за ОС TASiS дело не заржавеет. К слову, CP/M тоже достойная система, и программированию под неё обязательно надо будет вернуться в одной из следующих статей. А пока замечу, что у iS-DOS/TASiS есть одно неоспоримое преимущество – эта система была написана специально на Спектруме и для Спектрумистов. В ней нет каких-то "международных" устоявшихся стандартов, без нарушения которых невозможно её дальнейшее развитие.
Поэтому в рамках данной статьи я остановлюсь на азах программирования под ОС iS-DOS и прежде всего под её современную версию под ATM-совместимые машины – ОС TASiS, делая при необходимости отдельные уточнения по поводу того, является ли какая-то функция общей для всех iS-DOS или только для TASiS (как более продвинутой системы, совместимой "сверху вниз").
О стереотипах и фобиях написания программ под новую систему
Прежде чем приступить к конкретным примерам, необходимо остановиться на стандартных фобиях и предубеждениях по поводу программирования под Систему тех, кто всю жизнь писал программы исключительное в среде TR-DOS (а таких подавляющее большинство), а теперь ему придется учитывать естественные для любой настоящей Системы "великие и ужасные" Правила и Соглашения. Ниже я постараюсь показать, что не так страшен чёрт, как его малюют.
Какие у нас, если не брать всякие особо вычурные случаи, есть правила при работе в TR-DOS? Да практически никаких. Помимо системных переменных бейсика, которые лучше не затирать, дополнительно надо помнить лишь о 112 байтах системных переменных TR-DOS. Ну, ещё за стеком следить – лучше опустить его прямо в бейсик-загрузчике командой CLEAR перед загружаемым блоком кодов, хотя и это опционально, но если вы используете переключение страниц через порт #7FFD,то весьма желательно. При определённых условиях следует проявлять осторожность в играх с режимами прерываний, регистрами I, IY и HL'. И всё. А в остальном - полная свобода: грузи блоки кода куда угодно и располагай их в каких угодно промежутках адресного пространства, а сама исполняемая программа затем может изменять какие угодно ячейки памяти, используя их как свои переменные, и т.д...
Да, к этой свободе использовать адресное пространство процессора как угодно быстро привыкаешь, и в дальнейшем любое предположение, что придется учитывать наличие в памяти ещё чего то (а ведь совершенно верно - ядро iS-DOS находится именно в основном адресном пространстве), сразу вызывает отторжение и мысли типа "нет, так мы не договаривались!". Это фобия – по-другому её не назовешь. И она порождает ряд стереотипов по iS-DOS, основные из которых, относящиеся к тематике статьи – это то, что "iS-DOS рассчитана на 48К ОЗУ, занимает в нём кучу места, и свободного пространства остается совсем немного, так что ничего серьёзного не напишешь". Разрушению данных стереотипов я и посвящаю данную статью.
Я не буду грузить читателя тоннами информации по всем рестартам и режимам системы. Я разберу лишь несколько примеров, как человеку, привыкшему к "вольнице в ОЗУ" без лишних проблем средствами системы загрузить и запустить... нет, не маленькую системную программку, написанную изначально под iS-DOS (а значит, и с учетом всех правил и соглашений), а исполняемый блок кодов игрушки (написанной как угодно, без учета системы – всё равно наша ОС консольная и с графикой работать не умеет), а потом столь же безболезненно вернуться обратно в оболочку системы, туда, откуда и пришёл - точно так, как на "больших машинах" с MS-DOS или виндой.
Для этого разберём только базовые принципы работы и размещения системы и самые необходимые для загрузки кода и конфигурирования ОС рестарты. Если моя цель будет достигнута и читатель на приведённых примерах избавится от предрассудков в отношении iS-DOS, то опираясь на полученную информацию, он уже сам легко сможет освоить все остальные глубины возможностей работы с этой ОС - благо iS-DOS очень хорошо и подробно документирована.
В процессе описания примеров будет подразумеваться, что у вас современный Спекки с ОЗУ больше 128 КБ - ATM Turbo 2(+) или его реализация в ZX-Evolution/BaseConf, с ОС TASiS или, на худой конец, iS-DOS Chic (что будет оговариваться отдельно).
Распределение памяти в iS-DOS Classic, Chic, Chic-ATM, TASiS
Итак, как устроена любая iS-DOS? Располагается она в основном адресном пространстве. Её ядро растет сверху вниз, начиная от адреса #FFFF и до конкретной конечной точки, которая может сдвигаться выше или ниже, в зависимости от количества загруженных драйверов, уровней и проч. А именно:
В самом адресе #FFFF и десятке байт ниже расположен первичный обработчик прерываний IM 2,вся работа которого заключается в переходе на штатный системный обработчик. Сразу под ним несколько сотен байт свободного пространства, зарезервированного под "свободно плавающий" по нему стек, затем последовательно, один под другим в сторону уменьшения адресов в памяти, следуют 5 (из восьми теоретически возможных) штатных уровней ядра - от нулевого до четвёртого, вместе с их наборами системных переменных. Расположение этих уровней фиксировано для конкретного типа системы и версии её ядра – на какой адрес они скомпилированы, так неизменно и будут располагаться. Три оставшихся возможных пользовательских уровня (используются некоторыми программами, подсоединяющими их на время своей работы) в любом случае будут добавляться ниже. А под этими пятью фиксированными уровнями идёт область с плавающими размерами - сразу под уровнями идёт область резидентов, фоновых задач и драйверов (которые все так или иначе являются подвидами резидентов) и в зависимости от количества подсоединённых резидентов или раздувается дальше вниз, или, при удалении их, растёт вверх. Ниже этой области идёт область каналов, которую также можно увеличить или уменьшить. А под ней - область КЭШа блочных устройств, также колеблющаяся от минимума в 6 блоков по 256 байт (плюс по 4 байта заголовка на каждый блок) до стольких, сколько хватит памяти. А уже от нижней границы КЭШа и дальше вниз идёт область программ пользователя, куда можно грузить всё, что вздумается.
Запускаемая программа пользователя, имеющая, по аналогии с MS-DOS или CP/M, расширение .COM, в отличие от этих систем, у которых есть единый физический адрес запуска #0100, может, как и в TR-DOS, быть откомпилирована и запущена с ЛЮБОГО адреса.
По аналогии с TR-DOS в описателе файлов зарезервирована пара байт под адрес загрузки и запуска (что создаёт много проблем при прямом переносе запускаемых файлов из MS-DOS, так как эти два байта при копировании в FAT теряются, как и байты контрольной суммы - кстати, и их надо восстанавливать вручную в редакторе атрибутов файла). Если при попытке запустить программу пользователя средствами системы из оболочки "хвост" файла залезет в системные области (КЭШ и выше), то система откажется от загрузки и обругает ошибкой 130 (нехватка памяти). И тогда придётся освобождать место, уменьшая или размер КЭШа, или размер области каналов, или удаляя лишние резиденты. Подзагружая данные уже из самой программы, можно средствами системы узнать нижнюю границу КЭШа для принятия решения о выдаче ошибки или продолжении работы, а также регулировать в разумных пределах его размеры. Подробнее это будет описано в соответствующей части статьи. Большинство исполняемых файлов под "обычные" версии iS-DOS, тем не менее, откомпилированы под адрес 24000 (реже ниже, иногда - 25000, выше тоже редко). В таких версиях определённая нижняя граница загрузки запускаемых файлов - это область системных переменных бейсика и TR-DOS (располагающихся с адреса 23552 по 23866). Сама система в переменных по большому счёту не нуждается, кроме двух исключений:
Во-первых, дело в том, что все обращения в систему (любой версии) происходят через единую точку вызова - по команде RST #10, при этом в регистре C содержится номер функции, а в других регистрах - дополнительные параметры. В случае iS-DOS Classic по адресу #0010 располагается подпрограмма ПЗУ с Бейсиком-48, которая передаёт управление подпрограмме вывода в канал (адрес её лежит в описателе, адресованном системной переменной для текущего потока вводавывода). В нашем случае - подпрограмме вызова процедуры из ядра (её номер в рег.C). А во-вторых, некоторые системные переменные TR-DOS могут использоваться драйвером FDD, если он обращается при работе с контроллером ВГ93 к процедурам ПЗУ TR-DOS.
Есть ещё 256 свободных байт в области лежащего сразу под областью переменных бейсика буфера принтера (с адреса 23296 по 23551), который системой не используется. Этот буфер тоже можно использовать под небольшие программки, что иногда и делается. А ниже идут 6912 байт экрана и, в случае с iS-DOS Classic, ПЗУ бейсика.
Сколько же в итоге мы имеем пространства для программы пользователя?
Ядро iS-DOS, как уже упоминалось, может менять свои размеры, однако средние его размеры колеблются в районе 22-24 КБ. Ещё примерно 24 КБ (16 КБ ПЗУ + 8 КБ экрана и системных переменных) недоступно снизу. Итого непрерывного пространства в основной памяти на программу пользователя в iS-DOS Classic остаётся порядка 15-18 КБ. Негусто. Однако Classic – устаревшая система, рассчитанная на машины с 48 КБ памяти, либо версии Спектрума-128К с неотключаемым ПЗУ.
Другая картина - в iS-DOS Chic. Основное различие с Classic в том, что она рассчитана на работу с машинами, которые позволяют вместо ПЗУ включать по адресу #0000 страницу ОЗУ. Для этой цели было переписано и перекомпилировано ядро: все неизменяемые процедуры штатных пяти его уровней перенесены в блок, загружаемый в страницу, включаемую вместо ПЗУ, а вверху остались только системные переменные, да адреса процедур перехода в "нижнюю" страницу ОЗУ. Это, даже с учетом оставшихся областей резидентов, каналов и КЭШа, сократило размер "верхней" части ядра до 8-10 КБ, а это уже даёт свободное пространство почти 30 КБ. Кроме того, для обработки RST #10 уже не нужны системные переменные бейсика - процедура обработки вызова уже присутствует в подключаемой внизу странице ОЗУ.
Ещё лучше со свободной памятью в АТМ-версии iS-DOS Chic - драйвера FDD в этой системе используют возможности железа этого клона по прямому доступу к портам ВГ93, так что отпадает необходимость и в системных переменных TR-DOS, что позволяет грузить полноценные программы сразу после экрана, начиная с 23296,что даёт нам примерно 700 дополнительных байт. В TASiS, основанной на АТМ-Chic, штатно используется не стандартный экран, а текстовая консоль, к тому же располагающаяся в альтернативной видеостранице (в порту #7FFD бит D3=1). В итоге 6912 байт из области стандартного экрана также можно использовать под программы пользователя, начиная их с адреса #4000 - это увеличивает "пользовательскую" область до 38-40 КБ. В перспективе в новых версиях TASiS будет возможно подключать внизу и иные пользовательские страницы ОЗУ, что увеличит пространство ещё примерно на 15 КБ - до 53-55 КБ.
iS-DOS Classic просто грузит себя в неизменное адресное пространство процессора Z80 (на 128К - в стандартную конфигурацию, где с #0000 лежит ПЗУ BASIC48, с #4000 – страница 5, с #8000 - страница 2,а с #C000 для ядра включена страница 0; программы пользователя, например, копировщики или драйвера ОЗУ, могут включать и иные страницы, но вне рамок работы с вызовами системы).
iS-DOS с версии Chic использует переконфигурирование адресного пространства. И эта смена конфигурации различна для разных типов машин в зависимости от использованного в них способа включения ОЗУ в нижней части адресного пространства. Правда, обычно конфигурация устанавливается раз и навсегда и в штатных программах системы не используется. Однако бывают исключения, прежде всего в драйверах - когда надо, к примеру, отключить ОЗУ, чтобы добраться до подпрограмм TR-DOS. И в этом случае надо понимать, на какой машине запущена система и как менять конфигурацию. Система позволяет программно это узнать. Но о способах реконфигурации далее. А сначала о самой структуре адресного пространства в iS-DOS Chic и TASiS.
Если запустить iS-DOS Classic на 128K машинах, то ядро системы в верхней части (от адреса #FFFF) будет располагаться в ОЗУ в странице 0. Но в случае с Chic всё несколько по-другому. На компьютерах KAY-256/1024 при отключении ПЗУ внизу включается как раз страница 0, куда грузится неизменяемая часть ядра. Поэтому оставшаяся часть ядра с переменными, резидентами и КЭШем записывается в страницу 8, которая включается по адресу #C000 – по сути, для порта #7FFD и для софта, в том числе использующего страницу 128K ОЗУ, подмена совершенно "прозрачная". Также эта схема работает и на всех вариантах PROFI. Отдельно стоит тут упомянуть версию Chic для клонов Scorpion. В них в странице RAM8 сидит часть теневого монитора, поэтому ядро перенесено в страницу RAM #0D, что сказалось на совместимости с несколькими 128К-утилитами, потребовавшими коррекции таблиц использующихся в них страниц. Но эту версию следует рассматривать как исключение.
В целом же при такой схеме расположения ядра система Chic рассчитана на машины с минимумом 256КБ ОЗУ. 128КБ ОЗУ достаточно на спектрумах с отдельным напаянным 16КБ статическим ОЗУ-"кэшем" или, как самый экзотический вариант, - с прошитой в ПЗУ неизменяемой частью ядра.
Немного другая схема применена в системах для ATM-turbo 2+ - как в iS-DOS Chic, так и в TASiS. На этом клоне, как правило, минимум ОЗУ - 512КБ, а то и все 1024КБ, и система изначально рассчитывает на их наличие. Также особенностью данных клонов является наличие полноценного диспетчера памяти, позволяющего включать любую страницу ОЗУ или ПЗУ в любую четверть адресного пространства, причём имеется две независимые программируемые карты памяти – для BAS48 (D4 порта #7FFD, далее "ROM2", =1) и для BAS128 (ROM2=0). Другими словами, в этом клоне отключение ПЗУ не ограничивается страницей ОЗУ 0. Мы можем включить в нижнюю область что угодно, причём отдельно вместо ПЗУ BASIC48 и ПЗУ BASIC128. Этим и пользуются Chic с TASiS. Верхняя часть ядра остается в странице ОЗУ 0, а в нижней части включается страница ОЗУ #1D (из области в конце первых 512КБ ОЗУ, в одной из страниц, используемых специальным резидентом из ПЗУ машины). Причем включается он вместо ПЗУ BASIC-128, а BASIC-48 остается на месте. И в итоге оперативная смена конфигурации для доступа, к примеру, к портам TR-DOS (через область #3Dxx бейсика-48) происходит через вывод байта в порт #7FFD: для iS-DOS Chic значение #00 для отключения ПЗУ и #10 для его включения.А в TASiS, где для вывода текста используется альтернативная видеостраница (в порту #7FFD сигнал D3=1 - далее SCR2), эти значения будут #08 и #18 соответственно.
Дополнительно созданными системными функциями можно прозрачно менять страницы ОЗУ и в области #4000 и #8000,а в перспективных разработках по дальнейшему развитию TASiS планируется ещё шире использовать возможности диспетчера памяти и подключать дополнительные модули с библиотеками функций в область #0000 в иные страницы ОЗУ, подменяя временно страницу #1D со штатным ядром системы. Но это тема для отдельной статьи.
В качестве подведения итогов варианты конфигурации ОЗУ в iS-DOS можно представить в виде таблички:
|
Classic |
Chic |
ChicATM |
TASiS |
Порт конфигури- |
нет |
#1FFD |
#7FFD |
#7FFD |
ROM2= |
1(B48) |
1(B48) |
0(B128) |
0(B128) |
SCR2= |
0 |
0 |
0 |
1 |
CPU3= |
RAM#00* |
RAM#08** |
RAM#00 |
RAM#00 |
(ядро системы, и в остатке область прог.пользователя) |
||||
CPU2= |
RAM#02 |
RAM#02 |
RAM#02 |
RAM#02*** |
(область программ пользователя) |
||||
CPU1= |
RAM#05 |
RAM#05 |
RAM#05 |
RAM#05*** |
(обл. прог. польз-ля, сис. переменные BAS48/TR-DOS,экран) |
||||
CPU0= |
BASIC48 |
RAM#00 |
RAM#1D |
RAM#1D*** |
Примечания:
"*" - без использования программ, обращающихся к порту #7FFD (таких немного), страница может быть любая, кроме 2 и 5.
"**" - для скорпионовской версии Chic-ZS это страница RAM #0D.
"***" - средствами системы в программах пользователя временно могут устанавливаться и другие страницы, но штатными при работе из оболочки являются именно эти и только эти значения.
Прерывания и стек в iS-DOS Classic, Chic, Chic-ATM, TASiS. Определение типа системы
Одна из важных процедур в любой системе - это обработка прерываний. В iS-DOS для этого используется режим IM 2. Система его обработки устроена по методу, описанному Николаем Родионовым в книге "ZX Spectrum и TR-DOS для пользователей и программистов": вектор прерывания устанавливается таким образом, чтобы считанный адрес процедуры обработки прерываний был равен #FFFF, т.е. располагался в самом конце адресного пространства. А там вставлен код #18 (команда JR xxxx - безусловный переход). Следующий байт команды считывается из адреса #0000,а там, что в ПЗУ BASIC-48 в iS-DOS Classic, что в странице ОЗУ в Chic/TASiS, располагается байт #F3. В итоге сочетание байтов #18 и #F3 даёт нам команду JR #FFF4, где расположена 11-байтная процедурка первичного сохранения регистров и передачи управления основной процедуре обработки прерываний в теле ядра. Это действительно для всех версий ядра. А вот что различается - так это регистр I, т.е. вектор прерываний. Его значения в зависимости от типа системы следующие:
iS-DOS Classic: I = #3B
iS-DOS Chic (любой): I = #06
TASiS: I = #00
Исходя из этого, опрос значения вектора прерываний является самой простой первичной процедурой определения типа системы (хотя ничего не мешает резидентам и программам пользователя временно записывать туда иное значение и перехватывать обработку прерываний на себя - но это уже из раздела "хитростей", которые не являются темой данной статьи), примерно так:
systyp | LD A,I | |
OR A | ;CP #00 | |
JR Z,tasis | ;мы находимся в ОС TASiS | |
CP #06 | ||
JR Z,chic | ;...в ОС iS-DOS Chic | |
CP #3B | ||
JR Z,class | ;...в ОС iS-DOS Classic | |
JP unknown | ;тип системы неопределён |
Определив, что мы находимся в iS-DOS Chic, нам может потребоваться дополнительное определение типа машины, ибо, как мы уже говорили, в зависимости от этого типа различаются и способы реконфигурирования адресного пространства. А для этого в iS-DOS Chic существует состоящий из 5 байт и расположенный по адресу #00FA т. н. "вектор системы":
#00FA(1 байт) - содержимое страничного порта #7FFD при штатной работе
#00FB(1 байт) - содержимое порта конфигурации для включения ПЗУ
#00FC(1 байт) - содержимое порта конфигурации для включения ОЗУ
#00FD(2 байта) - номер порта конфигурации
Этот "вектор системы" существует и в TASiS, но там его чтение не столь важно, ибо если мы определили, что находимся в этом типе системы, то это подразумевает, что мы находимся на ATM Turbo 2(+),ZX Evo/BaseConf или Pentagon 2.666, где способы реконфигурирования через диспетчер памяти одинаковы. Но для наглядности значения векторов системы для ATM,KAY,Scorpion и PROFI представлены в виде таблички:
#00FA #00FB #00FC #00FD
Chic KAY #10 #10 #11 #1FFD
Chic-ZS Scorp #15 #10 #11 #1FFD
PROFI #10 #01 #11 #DFFD
Chic ATM #00 #10 #00 #7FFD
TASiS #08 #18 #08 #7FFD
Ещё для понимания работы системы необходимо рассмотреть особенности работы со стеком. Но тут всё достаточно просто. Штатно стек находится внутри верхней части ядра системы, где ему выделено несколько сот байт свободного пространства, примерно между #F800 и #F900. При работе "чисто iS-DOS'овских" утилит, работающих только в основном адресном пространстве, переназначать стек вообще не требуется. Но если вам надо будет лезть в страничный порт #7FFD, то может возникнуть необходимость его временного переназначения в тело программы пользователя. Система переживает это переназначение легко - вы также свободно можете использовать системные рестарты. Чтобы вернуться в оболочку системы по команде RET, надо будет восстановить прежнее значение стека. Если же по какой-то причине это невозможно, то и тут не беда – можно будет выйти в оболочку посредством нескольких системных рестартов вызова оболочки SHELL системы. Они сами восстановят необходимое значение стека. Но об этом в следующей части.
Как "отвязываться" от системы и включать её обратно. Принцип загрузки блока кодов и возврата в оболочку
Теперь, на основе вышеизложенного, мы имеем достаточно информации для того, чтобы писать игры под iS-DOS/TASiS, практически не оглядываясь на такие ограничения памяти, как наличие в ОЗУ ядра ОС или системных переменных. Можно даже писать универсальные игры, которые можно запускать хоть под iS-DOS, хоть под TR-DOS. Для этого всего лишь нужно, чтобы итоговые "кодовые" файлы формировались отдельно (как в старых ленточных версиях, причём для основного ОЗУ файлы отдельно и для записи оверлеев в страницы тоже отдельные файлы). Тогда для версий под разные ОС просто надо будет написать свои загрузчики (в случае с TR-DOS - прямо на бейсике через RANDOMIZE USR 15619: REM: LOAD "filename" CODE adress), и дело сделано (для 128К и выше версий игр неплохо бы предусмотреть гибкую таблицу используемых страниц с настройкой портов управления ими). Что же касается iS-DOS, то тут алгоритм таков.
Чтобы отключить систему и перейти к "полноправному" использованию всего доступного адресного пространства, достаточно:
1. Запретить прерывания, сохранив значение используемого системой вектора прерываний для последующего восстановления, а затем переназначить его для использования в прерываниях пользователя.
2. Сохранить где-нибудь указатель стека в системе и переназначить куда нужно пользователю.
3. Сохранить в верхней памяти (путём простого копирования или переключения страниц) ядро системы и при необходимости обеспечить по адресу #C000 либо страницу 0, либо страницу из нижних 128 КБ. Если возможно сохранение ядра, по размерам не превышающего 16 КБ (т.е. вписывающегося в страницу в области с #C000), то перед этим необходимо средствами системы (о которых будет рассказано в соответствующем разделе) организовать проверку значения нижней границы ядра.
4. Если есть необходимость, то используя данные из вектора системы – восстановить стандартную конфигурацию ОЗУ с Бейсиком- 48. Для TASiS может потребоваться переход в экранный режим 6912 и стандартную палитру. Если это осуществлять через систему (для чего есть соответствующие системные вызовы), то делать это надо до отключения ядра.
Соответственно, для включения системы обратно необходимо:
1. Восстановить ядро в необходимой странице по адресу #C000 (для TASiS – это страница 0).
2. Восстановить необходимую для Chic конфигурацию памяти (включить ОЗУ по адресу #0000 в соответствии с вектором системы).
3. Восстановить сохраненные указатель стека и вектор прерываний. Разрешить прерывания.
4. В случае с TASiS при необходимости восстановить прежние экранный режим и палитру (после выполнения пункта 3 это уже можно делать через системные вызовы).
5. При необходимости - выход обратно в оболочку системы (об этом ниже). В случае выхода в оболочку в системе TASiS, прежние системные экранный режим и палитра восстанавливаются автоматически, а значит, п.4 отпадает.
А теперь более конкретно.
Для 48К игрушек, не знающих о существовании порта #7FFD, альтернативного экрана и прочего, всё предельно просто: не используем страницу #00 (или ту страницу, что включена штатно с адреса #C000) вообще. Вместо неё достаточно подставить туда любую другую страницу или с предварительно загруженным туда кодом игры, или со скопированным туда после подстановки. А точнее, если блок кодов игры временно умещается в промежуток между концом загрузчика и адресом #C000, то алгоритм таков:
1. Проверяем, не вылезает ли ядро ниже #C000.
2. Грузим блок кодов в свободное пространство.
3. Отключаем прерывания, переназначаем стек и вектор прерывания.
4. Включаем в #C000 свободную страницу и переносим посредством LDIR или LDDR (или распаковкой) блок кодов игры на нужные адреса, а затем - переконфигурирование адресного пространства, графики и палитры, и CALL game.
Если блок кодов слишком длинный и не влезает в свободное пространство, то тогда делаем по-другому. Средства системы позволяют грузить файлы по частям в любой произвольной последовательности и кусками выбранной длины. Алгоритм в случае с iS-DOS Chic такой:
1. Проверяем, не вылезает ли ядро ниже #C000.
2. Грузим "хвост" блока кодов, который должен располагаться выше #C000 в свободное пространство, а затем копируем его в страницу, которая будет включена вместо страницы с ядром.
3. Грузим остальную часть блока кодов с адреса загрузки до #C000.
4. Отключаем прерывания, переназначаем стек и вектор прерывания.
5. Включаем в #C000 ту свободную страницу, куда грузили "хвост" кодового файла, а затем производим переконфигурирование адресного пространства, графики и палитры, и CALL game.
Очень похожа методика действий и для программ, использующих 128КБ-страницы. Точно так же кодовые блоки в виде оверлейных файлов предварительно загружаются в нужные страницы. При этом надо исключить из использования страницу с ядром, либо временно сохранять его в верхней памяти.
Проиллюстрирую вышеприведённую информацию в кодовых примерах. Как уже упоминалось, система вызывается через единую точку входа RST #10. При этом номер функции содержится в регистре C, а в остальных регистрах при необходимости передаются дополнительные данные. На выходе получаем признак NC – вызов отработан успешно. В остальных регистрах по необходимости могут располагаться возвращаемые данные. Если на выходе признак C, то это признак ошибки, тогда в регистре A - номер ошибки. Если при выходе в оболочку установить флаг C и в регистр A записать любой номер, то выход в оболочку произойдет с сообщением "Ошибка {указанный номер}". Так, например, номер ошибки при нехватке памяти - 130.
Следующий вызов нам необходим для предварительной оценки среды (в данном случае для определения - не опустилось ли ядро ниже #C000):
Рестарт $g_cnfg (регистр C=#10). Входных параметров нет. На выходе в альтернативном регистре HL' адрес вектора конфигурации ядра ОС (другими словами – указатель на расположение системных переменных). Там есть много полезного. Но сейчас нас интересуют данные по смещению 5 - там хранится указатель на нижнюю планку КЭШа (ниже которой начинается область пользователя). Зная это, мы можем организовать проверку свободного места:
freesp | LD C,$g_cnfg | ;или можно прямо LD C,#10 |
RST #10 | ||
EXX | ;"открываем" альтернативные | |
;регистры для доступа к HL | ||
LD BC,5+1 | ;+1 вместо INC HL ниже | |
ADD HL,BC | ;устанавливаем указатель | |
;вектора системы на границу КЭШа | ||
; INC HL | ;адресуем старший байт LD A,(HL) | |
CP #C0 | ;если граница КЭШа < #C000, | |
;то установка флага C | ||
LD A,130 | ;номер сообщения об ошибке | |
;(нехватка памяти) | ||
RET C | ;выход в оболочку с сообщением | |
;об ошибке в случае нехватки | ||
... | ;если всё ОК, продолжаем программу | |
;(если необходимо) | ||
RET | ;необходимо, если оформляем как | |
;подпрограмму |
А само основное тело загрузчика блока кодов (для простоты берём только 48К вариант) и отключение системы будут выглядеть примерно так:
ORG 24000 | ;начало COM-файла | |
CALL freesp | ;проверка ниж.границы ядра | |
RET C | ;если мало, выходим с ошибкой 130 | |
;(устанавливается в подпрограмме) | ||
CALL loadfl | ;грузим кодовый блок в | |
;область пользователя | ||
;(разберём отдельно) | ||
RET C | ;возврат в оболочку, если при | |
;загрузке возникла ошибка | ||
CALL scrpal | ;включаем режим 6912 и | |
;стандартную палитру | ||
DI | ;начинаем процедуру отключения | |
;системы | ||
LD (buffer),SP | ||
LD SP,... | ;переназначаем стек | |
LD A,#3B | ||
LD I,A | ||
IM 1 | ||
LD A,#11 | ||
LD BC,#7FFD | ||
OUT (C),A | ;включаем ПЗУ-48 и страницу | |
;RAM #01 (для примера) | ||
LD HL,... | ||
LD DE,usrbuf | ||
LD BC,... | ||
LDIR | ;или LDDR. Перемещаем загруженный | |
;код игры на "законное" место | ||
EI | ||
CALL usrbuf | ;запуск игры | |
;ну а тут, если игрушка из блока кодов | ||
;выходит обратно по простому RET | ||
;может располагаться процедура возврата | ||
;оболочку TASiS: | ||
DI | ||
XOR A | ||
LD I,A | ||
IM 2 | ||
LD A,#08 | ||
LD BC,#7FFD | ||
OUT (C),A | ;включаем BAS-128,где вместо | |
;него давно сконфигурирована стр. ОЗУ | ||
;альтернативную экранную страницу | ||
;и RAM 0 по адресу #C000 | ||
LD SP,(buffer) | ;восстанавливаем стек | |
EI | ;теперь система вновь работает | |
XOR A | ;устанавливаем флаг Z | |
;(для выполнения оболочкой | ||
;сопутствующей команды) | ||
OR A | ;сбрасываем флаг CY для | |
;"безошибочного" выхода | ||
LD A,#F4 | ;команда оболочке для | |
;перерисовки панели (ведь на | ||
;экране после игры - "мусор") | ||
RET | ;выход в оболочку. Графический | |
;режим и палитра в TASiS | ||
;восстановятся при этом автоматически | ||
buffer | DEFW 0 | |
.. | ||
usrbuf |
Если по каким-то причинам восстановить стек невозможно, то вместо конечного RET можно воспользоваться прямым рестартом вызова оболочки, предварительно проделав вышеприведённые манипуляции с флагами Z,CY и регистром A:
Рестарт $shout (регистр C=#84). Выход в оболочку с выполнением спецкоманды. Входные данные: если флаг CY установлен, то в регистре A - код ошибки (обработку ошибок мы разбирали на примере ошибки нехватки памяти 130). А если флаг CY сброшен, а Z - поднят, то происходит выход в оболочку с выполнением спецкоманды, указанной в регистре A. В нашем случае нам достаточно команды #F4 - перепечатка панели. В этом случае система сама восстановит нужный ей указатель стека. Минус тут только один - нельзя будет запускаться из BAT-файлов. Точнее, можно, но после выхода таким "кривым" способом дальнейшее исполнение BAT-файла просто прервётся, и вы окажетесь в оболочке.
В TASiS есть ещё один способ выхода в оболочку. Его следует применять в тех случаях, когда, к примеру, вы адаптировали под систему обычную ZX-игрушку, которая не знает никаких выходов из самой себя обратно в вызвавшую её блок кодов подпрограмму, а просто "зациклена" в своей игровой среде. В этом случае просто необходимо воспользоваться резидентом - возможностью вызова программы пользователя, размещённой в верхней памяти в странице RAM #1F определённым образом, по нажатии кнопки "RESET". Конкретно структура резидентной страницы следующая.
Чтобы программа, помещённая в страницу #1F, была распознана как резидент, она должна быть оформлена специальным образом. В соответствие с этим, страница имеет следующую структуру (далее указывается смещение от начала страницы):
#0000 - код #C3 (команда JP nnnn)
#0001-#0002 - адрес перехода для JP nnnn (рассчитывается по формуле: #C000+относительный адрес начала программы в странице).
#0003-#3FFC - свободное место непосредственно под программу.
#3FFD - контрольная сумма (КС) всей страницы (то есть с #0000 до #3FFF).
#3FFE - всегда должен быть равен #55.
#3FFF - всегда должен быть равен #AA.
При создании резидента КС рассчитывается путем сложения без учёта переноса (то есть по команде ADD) одного за другим всех байтов страницы, а затем вычитания полученной суммы из нуля (по команде NEG процессора). При этом, так как в процессе расчёта контрольной суммы она ещё не занесена в байт #3FFD, он перед началом подсчёта КС должен быть равен #00(!). И этот момент обязательно должен быть учтён при возникновении необходимости пересчета КС.
Скомпонованная таким образом страница будет успешно распознана как резидентная, и прошивка ПЗУ начнёт процедуру запуска резидента, которая состоит из следующего:
1) По адресу #C000 включается резидентная страница #1F.
2) По адресу #8000 в обеих картах памяти (и при ROM2=0, и при ROM2=1) включается страница RAM #02.
3) Передается управление резиденту на адрес #С000, где должна располагаться команда резидента JP nnnn.
После передачи управления резиденту архитектура выглядит так:
1) Расположение стека (значение SP) - не определено. Остаётся на усмотрение резидента.
2) Карта памяти помимо адреса #8000 - #BFFF (и страницы #1F с запущенным резидентом по адресу #С000) - не определена.Также остается на усмотрение резидента.
3) Состояние палитры - не определено. Также остается на усмотрение резидента.
4) Прерывания находятся в режиме IM 2, включены. Вектор прерывания равен #82FF (I=#82). Таким образом, прерывания надо либо запретить, либо, если работа с ними в короткий период работы резидента (пока он устанавливает основную программу) необходима, например, для установки палитры, то надо установить в #82FF-#8300 адрес подпрограммы обработчика прерываний. В резиденте Honey Commander там стоит указатель на ближайший RET.
Таким образом, программист при разработке резидента не связан никаким правилами и ограничениями, так как резидент не привязан ни к какой операционной системе и не обязан учитывать её особенности. Какова будет конфигурация компьютера после рестарта в резидент, определяется самим резидентом.
В нашем случае будет необходимо в первичную процедуру загрузки игры сохранить в страницу #1F резидентную подпрограмму, данные о значении стека, режима прерываний, конфигурации адресного пространства и просчитать контрольную сумму. А дальше после команды "RESET" эта подпрограмма по сохранённым данным установит ядро на место, переназначит карту памяти. В общем - сделает практически всё то, что описано выше при выходе в оболочку. Остаётся добавить, что стандартно резиденты в TASiS сохраняют область ОЗУ с #C000 по #FFFF в страницу #1C, поэтому без наличия дополнительной необходимости рекомендуется в своих программах при создании резидента также использовать эту страницу.
Теперь для того, чтобы успешно написать и запустить собственную игру под iS-DOS/TASiS, нам осталось разобрать всего несколько системных вызовов.
Открытие и загрузка файлов в iS-DOS
В приведённом выше примере загрузчика мы временно обошли стороной вопросы собственно загрузки данных с диска и переключения графики, оставив их напоследок и обозначив их вызовами подпрограмм типа "CALL loadfl" и "CALL scrpal" соответственно. Теперь же остановимся на них конкретно. Для начала о загрузке с диска.
Существует множество способов и возможностей оперирования файлами, каталогами, подкаталогами и фрагментами файлов на любых логических дисковых устройствах (вне зависимости от физических носителей, так как вся основная работа идёт через соответствующие драйвера), включая поиск, сортировку по шаблону, создание, удаление, переименование, добавление и "отрезание" частей, последовательный и произвольный доступ и многое другое. Но описание всех возможностей - это отдельный труд большого объёма. Желающие, заинтересовавшиеся системой, смогут самостоятельно изучить все рестарты, благо система хорошо документирована и её описание доступно в сети. А приведённые в этой статье примеры позволят понять основные принципы и дальше уже уверенно расширять свои навыки самостоятельно. Поэтому далее будет разбираться загрузка файлов по упрощённой схеме.
А именно - предполагается, что все действия происходят в текущем подкаталоге, откуда был загружен управляющий кодовый COM-файл нашей исполняемой программы (а по умолчанию рестарты работают именно в таком подкаталоге), нам заранее известны имя и размер файла, а также мы знаем, куда его или его части грузить.
Загрузка файла (как и любые иные операции с файлами) состоит в iS-DOS из двух этапов: поиск/открытие файла и собственно операция обмена данными с ним. Соответственно этим этапам приводим системные вызовы:
Рестарт $fopen (регистр C=#25). Поиск и открытие файла по имени и расширению. На входе в HL - указатель на адрес 11-байтового (8 байт имени и 3 байта расширения) описателя файла. Вообще стандартное описание файла составляет 32 байта (их описание ниже), но для входа используются только 11 байт непосредственно имени. Если файл найден, то он открывается. Если найденный файл - подкаталог, то происходит его открытие и переход в него. На выходе: если всё прошло без ошибок (флаг CY сброшен), то в регистре A:
A=#00 - открыт файл;
A=#20 - открыт каталог.
В альтернативном регистре HL' - указатель на адрес системного 32-байтового описателя файла, куда по итогам работы рестарта помещены данные текущего открывшегося файла.
Возможные ошибки (флаг CY установлен):
A=81 - файл не найден;
A=85 - ломаный блок описателя сегментов (текущего или искомого каталога);
A=86 - ломаный каталог.
Ошибки 85 и 86 могут возникнуть в случае порчи файловой системы на устройстве в результате каких-то иных, внешних для программы обстоятельств и в обычных условиях возникать не должны.
Значения 32-байтового описателя файла следующие:
+0 (8 байт) - имя.
+8 (3 байта) - расширение.
+11 (1 байт) - флаговый регистр состояния файла. Биты (0/1):
0 - удалён/существует
2 - защищён от чтения (1)
3 - защищён от записи (1)
4 - видимый/скрытый файл
5 - файл/каталог (корневой файл)
6 - сегментированный/непрерывный
7 - защищён от удаления (1)
+12 (2 байта) - адрес загрузки по умолчанию
+14 (3 байта) - длина в байтах.
+17 (2 байта) - номер блока описателя сегмента (для непрерывного файла – номер нулевого блока файла).
+19 (1 байт) - байт "Special": используется, как правило, в системных файлах для начальной загрузки или реконфигурирования. Как правило, биты 0..2 (диапазон значений 0..7) содержат номер уровня системы в SYS-файлах при подгрузке/замене новых уровней к ядру. В TASiS бит 3 (значение байта=8) - признак вывода не собственного, а внутреннего 38-байтового имени на файловую панель оболочки.
+20 (2 байта) - в обычных файлах не используется.В системном файле ядра ОС (описатель is_dos.sys)содержит используемый загрузчиком адрес установки стека SP.
+22 (1 байт) - в обычных файлах не используется. В системном файле ядра ОС (описатель is_dos.sys )содержит значение вектора прерывания в системе, передаваемое в регистр I загрузчиком при первоначальной установке системы (#3B в Classic, #06 в Chic, #00 в TASiS).
+23 (3 байта) - резерв.
+26 (2 байта) - контрольная сумма файла.
+28 (2 байта) - время.
+30 (2 байта) - дата.
Рестарт $rpart (регистр C=#29). Чтение файла или его фрагмента.
Входные данные:
DE - сколько байт читаем,
AHL - смещение от начала файла в байтах, откуда начинаем чтение,
IX - адрес в ОЗУ, куда читаем.
Возможные ошибки на выходе (флаг CY=1):
A=100 - попытка чтения за концом файла (т.е. AHL+DE больше длины файла, которую, как описано выше, можно достать из описателя в HL'+14).
A=106 - файл не открыт.
A=170 - чтение 0 байт (DE=0).
A=171 - файл защищён от чтения.
A=7 - ошибка чтения/записи (как правило, физическая. Возвращается драйвером низкого уровня).
Отдельно напоминаем, что данный рестарт не проверяет, в какую системную область ОЗУ мы грузим файл. И определять, не затрёт ли файл ядро системы, должен программист. Если же вы уверены в своих действиях и в том, что загружаемый файл ничего лишнего не затрёт, то процедура загрузки файла "с нуля" в ОЗУ (предположим, что его длина не превышает 65535 байт, т. е. занимает не более 16 бит в описателе) будет выглядеть так:
loadfl | LD HL,filename | ;указатель на 11-байто- |
;вый шаблон имени файла | ||
LD C,$fopen | ;или можно прямо LD C,#25 | |
RST #10 | ||
RET C | ;если ошибка, выходим | |
EXX | ;получаем указатель на описатель | |
PUSH HL | ;открытого файла | |
POP IX | ;переносим указатель для | |
;удобства в индексный регистр | ||
;при желании можем организовать | ||
;проверку контрольной суммы файла на | ||
;случай возможной подмены (контрольную | ||
;сумму можно узнать посредством | ||
;системных утилит ОС) | ||
LD A,(filename+26) | ;берём 1-й байт | |
;шаблона контрольной суммы | ||
CP (IX+26) | ;сравниваем с аналогичным | |
;байтом из описателя | ||
;открытого файла | ||
JP NZ,error | ;переходим на процедуру | |
;выхода по ошибке (которая | ||
;установит нужные флаги и | ||
;значения регистров) | ||
LD A,(filename+27) | ||
CP (IX+27) | ||
JP NZ,error | ;аналогично со вторым | |
;байтом контрольной суммы | ||
;подготавливаем данные для загрузки: | ||
LD E,(IX+14) | ;достаем из описателя | |
;длину файла (меньшую, | ||
;чем 65536 байт) | ||
LD D,(IX+15) | ;если мы заранее знаем | |
;длину загружаемого файла, | ||
;то просто делаем LD DE,filesize | ||
XOR A | ;записываем нули в смещение от | |
;начала файла. Если мы | ||
LD HL,0 | ;грузим часть файла (например, | |
;для переброски в страницу ОЗУ), | ||
;то записываем соответствующие | ||
;значения в AHL (смещение) | ||
;и DE (длина). | ||
LD IX,ramadr | ;куда грузим. Если | |
;планируется вызывать | ||
;загрузку по частям | ||
;несколько раз, значение IX | ||
;надо временно сохранять. | ||
LD C,$rpart | ;или можно прямо LD C,#29 | |
RST #10 | ||
RET | ;файл загружен, возвращаемся в | |
;исходную программу из подпрограммы | ||
;загрузки. На выходе должна стоять | ||
;проверка флага CY на предмет ошибки. | ||
;Если же в подпрограмме планируется | ||
;продолжение, например для загрузки | ||
;другого фрагмента файла, то тут надо | ||
;поставить RET C - выход по ошибке | ||
error | ;обработчик выхода по ошибке контр. суммы | |
SCF | ;устанавливаем признак ошибки – | |
;флаг CY=1 | ||
LD A,81 | ;укажем, что "файл не найден" | |
RET | ||
filename | ... | ;11 (минимум) или больше (до 32) |
;байт шаблона имени файла |
Таким образом, зная структуру описателя файла и два выше разобранных системных рестарта, мы можем загружать целиком или по частям любой файл. В конце раздела только для примера приведу аналогичный рестарт на запись в уже существующий файл определенной длины:
Рестарт $wpart (регистр C=#2A) – запись DE байт в текущий открытый файл со смещения AHL байт от начала файла с адреса в IX. Его, как и иные возможности по записи и созданию файлов, предлагается изучить самостоятельно.
Работа с палитрой и переключение графических режимов в TASiS
В предыдущем разделе разобраны примеры, универсальные для всех типов iS-DOS. Этот и следующий разделы будут посвящены дополнительным возможностям TASiS. Так, в системе поддержана работа с графическими режимами и палитрой на уровне системных рестартов, и нам не надо обращаться к портам, чтобы всё это переключать.
Все процедуры работы с дополнительными системными вызовами TASiS можно оформлять в виде подпрограмм с условным переходом по результатам проверки на тип системы.
Например:
LD A,I | ;проверка нахождения в TASiS по | |
OR A | ;CP 0;значению вектора прерываний | |
CALL Z,scrpal | ;переход на"графическую" | |
;подпрограмму, если мы там |
Рестарт $pal (регистр C=#79). Работа с палитрой. Входные данные зависят от регистра A:
A=#00 - установить стандартную палитру.
A=#FF - обновить палитру пользователя. На входе в регистре HL тогда - адрес 16 байт (числа от 0 до 63) палитры пользователя для установки.
Иное значение регистра A палитру не меняет, но на выходе возвращает:
В альтернативном регистре HL' – указатель на адрес системной палитры, используемой в оболочке,а точнее – блока специальных системных переменных TASiS, первые 16 байт которых - системная палитра.
В альтернативном регистре DE' - указатель на адрес стандартной ("спектрумовской") палитры, находящейся вне блока системных переменных TASiS в неизменяемой части системы (в блоке кодов в ОЗУ по адресам #0000-#3FFF).
Системная палитра принудительно установится при выходе из программы пользователя обратно в оболочку, поэтому можно изменять палитру как угодно, не заботясь о сохранении первоначальной. В нашем же случае для игры нам нужно сделать следующее:
XOR A | ;стандартная палитра | |
LD C,$pal | ;или LD C,#79 | |
RST #10 |
С графическими режимами немного сложнее. Отдельного рестарта для управления графикой нет. Но графический режим (а точнее, значение теневого системного порта #FF77 ATM Turbo 2/2+) прописан в упомянутом выше блоке специальных системных переменных TASiS по смещениям:
+54 (1 байт) - переменная sysscr. Байт системного порта для системных нужд. Именно по этому байту восстанавливается экранный режим по умолчанию при выходе из программы пользователя в оболочку. Стандартное значение =#AE(%10101110). Биты 7..4 должны быть всегда такими.
Бит 3 - турборежим включен.
Биты 2..0 - тип экрана. В данном случае тут значение 6 - текстовая консоль 80х25. В текущих версиях TASiS это значение постоянно, однако в дальнейшем, при разработке иных графических сред, может быть изменено.
+55 (1 байт) - переменная usescr.
Биты 2..0 системного порта для использования в программах пользователя. Если тут 0 - использовать sysscr. Все использующие системный порт рестарты ориентируются по этому признаку. При выходе из программы пользователя в оболочку принудительно сбрасывается в 0.
Биты 7..3 игнорируются (берутся из переменной sysscr). Значения для выбора типа экрана следующие:
#08 (установлен игнорируемый бит 3 для ненулевого значения) - EGA экран 320х200,
#02 - аппаратный мультиколор 640х200,
#03 - 256х192 (6912),
#06 - текстовая консоль 80х25.
Другие сочетания битов 2..0 также возможны и приведут к включению нелинейных, т.н. "технологических" экранов. При реализации в железе дополнительных экранных режимов возможен их вызов через эти дополнительные комбинации.
Чтобы сменить графический режим, надо занести соответствующее значение в переменную sysscr или usescr, а затем вызвать один из рестартов TASiS, использующих системный порт. Для наших нужд подходит рестарт SET_PG (#7B) – конфигуратор диспетчера памяти. Вызовем его так, чтобы не менять конфигурацию памяти:
XOR A | ;восстановление системной | |
;страницы ОЗУ TASiS | ||
LD B,A | ;0 ;в области #0000 | |
LD C,#7B | ;реально без дополнительных | |
;данных (в системных | ||
;переменных) ничего не меняется | ||
RST #10 | ;нужно только для смены экрана |
Подводя итог, напишем подпрограмму включения палитры и экрана для использования в загруженной игрушке:
scrpal | XOR A | ;признак стандартной палитры |
LD C,$pal | ;или LD C,#79 | |
RST #10 | ||
XOR A | ||
INC A | ;A!=#00 и !=#FF | |
LD C,$pal | ;для получения | |
RST #10 | ;адреса переменных TASiS в HL' | |
EXX | ||
PUSH HL | ;перебросим адрес | |
POP IX | ;в IX для удобства | |
LD (IX+55),#03 | ;ставим 6912 экран | |
;в переменную usescr | ||
XOR A | ||
LD B,A | ;0 | |
LD C,#7B | ;можно сразу LD BC,#007B | |
RST #10 | ||
RET |
В итоге всё получается не просто, а очень просто.
Управление страницами ОЗУ в TASiS
В предыдущих разделах дана исчерпывающая информация для того, чтобы пользователь мог смело взяться за ассемблер и написать загрузчик для своей игрушки. Здесь же бегло освещается информация сверх этого - ведь в стороне от внимания остались мощнейшие возможности ОС TASiS по управлению диспетчером памяти, а по сути, созданного в этой системе страничного менеджера, открывающего широчайшие перспективы по развитию программных приложений и построения многозадачной среды. Это тема отдельного большого исследования. А пока лишь приведу краткий обзор рестартов с минимумом комментариев, давая читателю возможность самому оценить масштаб открывающихся возможностей.
Рестарт page (регистр C=#7A) позволит пользователю (а в перспективе и системе) работать со всеми страницами ОЗУ.
Параметры на входе:
B - номер страницы,
A=0 - освободить страницу,
A=#FF - занять страницу.
На выходе:
Признак NC - O.K.
Признак C - уже занята (HL' = адрес в таблице, штатно располагающейся в неизменяемой части ядра системы в ОЗУ по адресу #3800)
----------
Иначе на входе:
A - номер страницы (любой, кроме 0 и #FF),
B - смещение в ней (адрес блока) в битах 5..0 и команда в бите 6: 0=читать, 1=писать (бит 7 - резерв для модификаций байт),
HL - откуда/куда,
DE - сколько байт.
Другими словами, можно вообще не переключать страницы, а просто указать блок кодов в основном поле памяти и номер страницы и вызвать рестарт, который сам всё скопирует.
Таблица страниц - 32 байта по биту на страницу (итого 256 страниц) - находится в is_dos.rom и может модифицироваться до перезагрузки. По умолчанию в ней заняты 0,2,3,5,7,#1F,#1E,#1D,#1C,#1B страницы, и при попытке занять их вы получите адрес в таблице (пока #3800) и признак C. Последние 4 страницы зарезервированы за резидентом и нижней страницей системы. Работать с ними можно, но освобождать не надо!
Вообще, на перспективу, желательно использовать хотя бы минимальный "протокол" - проверка занятости страницы, поиск свободной страницы памяти, использование её и освобождение, если занимали. Это позволит корректно работать в будущем, когда система (или проги) будет занимать при необходимости дополнительные страницы или для корректной работы менеджера загруженных приложений в будущей многозадачной среде.
В этой версии доступность страниц ограничена размером ОЗУ в 1024 КБ - 64 страницы, но при адаптации системы под ZX Evolution ничто не мешает организовать поддержку всех 4 МБ его ОЗУ и управление через данный рестарт всеми 256 страницами.
Понятно, что последний рестарт позволит написать новые процедуры для копировщиков, упаковщиков, загрузчиков и т.д. Он освобождает от необходимости контроля за портами. И вообще, с ним идеология работы с памятью может радикально измениться.
И ещё один рестарт работы со страницами ОЗУ:
SET_PG (регистр C=#7B) - установка страницы, восстановление системной страницы, вызов из страницы. Ранее был упомянут в примерах с переключением экранных режимов.
На входе:
A - команда,
B - страница или область,
HL - адрес (если нужен).
Команд всего четыре:
A=#00 - восстановить системную страницу,
B: биты 7 и 6 - область (#4000, #8000). На место ставятся штатно 2-я или 5-я страница ОЗУ из значений, хранящихся в ранее упоминавшемся блоке системных переменных TASiS. Но занеся в эти системные переменные свои значения,можно "назначить" системными иные страницы. Возможно восстанавливать также системную страницу 0-й области (ROM2=0) и системную страницу ПЗУ (ROM2=1). Для этого при команде восстановления системных страниц биты 7 и 6 регистра B должны быть сброшены (восстановление системной ОЗУ) или подняты (восстановление системной ПЗУ). Теперь для переключения на eXtra ПЗУ (например, чтобы войти не в TR-DOS, а в vTR-DOS) достаточно заменить байт системной страницы ПЗУ ( #83 по умолчанию) на #87 и вызвать восстановление её через рестарт (по схеме, приведённой при переключении экранов). Для сторонних кодеров, не знающих тонкостей и аппаратных особенностей компьютера, этот вариант оптимален.
A=#80 - установить страницу в области памяти #8000-#BFFF,
B - номер страницы.
A=#40 - установить страницу в области памяти #4000-#7FFF,
B - номер страницы.
A=#FF - самое интересное: вызвать процедуру из страницы по адресу в HL, в B - номер страницы. Вызывается процедура из страницы и по возврату RET восстанавливается (!) системная страница. Любое другое число в аккумуляторе – возврат с признаком C - ошибка. При вызове процедур из страницы рестарт ориентируется по адресу в HL. При ошибке адреса (#4000-#BFFF) в аккумуляторе возвращается 0 и признак C, иначе - код (и флаг) пользовательской ошибки.
Значения для устанавливаемых страниц лежат в области переменных TASiS в блоке из 8 байт syspg по смещению +32 и достаются через рестарт PAL (#79) точно так же, как и переменные экрана. Структура этого блока:
+32 (8 байт) - переменные syspg. 8 байт номеров страниц в областях системы. Сначала идут 4 байта для режима BASIC-128 (ROM2=0), затем 4 байта для BASIC-48 (ROM2=1). В каждой из этих четвёрок первый байт - копия значения, выдаваемого в страничный порт (т. е. со всеми нужными инвертированиями), остальные - просто номера страниц. Штатные (по умолчанию) значения следующие:
Для BASIC-128:
+32 - байт #62 (ОЗУ #1D) для #0000 - просто значение для порта
+33 - страница 5 для #4000(номер страницы)
+34 - страница 2 для #8000(номер страницы)
+35 - страница 0 для #C000(номер страницы)
Для BASIC-48:
+36 - байт #83 (ПЗУ BAS48) для #0000 - просто значение для порта
+37 - страница 5 для #4000(номер страницы)
+38 - страница 2 для #8000(номер страницы)
+39 - страница 0 для #C000(номер страницы)
Заключение
Некоторые комментарии напоследок:
Как видите, можно ставить страницу и работать с её процедурами и вызывать процедуры в ней, не заботясь о восстановлении на место системной страницы. О занятии и освобождении страниц тоже лучше не забывать. По сути, мы подошли к свободному использованию всей доступной памяти, и всё это - оставаясь в системе!!! А сама система превратилась в удобный "трамплин" для загрузки иной среды. Кто знает, может, в будущем, появится какая-нибудь WINiS, которая, как и первые версии Windows, будет грузиться из-под системы. Но это потом. А для начала, опираясь на данный труд, неплохо было бы перебороть себя, свои стереотипы и фобии и сделать первый шаг в программировании под Систему, чтобы отвязаться, наконец, от проклятия Спектрума - TR-DOS. Зачем? - возвращаясь к началу статьи, спросите вы. Да хотя бы потому, что это просто интересно - это до сих пор ещё неизведанная область на спектруме, где возможно качественное развитие. То самое развитие, которое на том же "любительском" энтузиазме и интересе привело к большому прогрессу в ОСестроительстве - и я не про старичка CP/M и ему подобных. В спину спектрумистам дышит реально многозадачная и графическая ОС SymbOS, написанная для компьютеров с процессорами Z80. Она запускается на MSX2 и Amstrad CPC. Вполне вероятно, что однажды появится версия и для АТМ Turbo 2/2+ - параметры этих клонов Спектрума вполне позволяют это. Но это значит, что опять разработки и стандарты придут на Спектрум извне, со стороны. И не спектрумисты будут определять направление развития программной среды. Что будет очень обидно и стыдно, учитывая, какой огромный задел в 90-е и "нулевые" оставили энтузиасты спектрум-сообщества, в частности, в виде iS-DOS и TASiS. Я считаю, что нам всем вполне по силам сдвинуть дело с мёртвой точки. А пока проект TASiS остаётся открытым для всех заинтересованных кодеров и юзеров.