КАК ДЕЛАТЬ ПРОГРАММЫ С АВТОМАТИЧЕСКОЙ ЗАГРУЗКОЙ
===============================================
Все видели, как красиво загружаются с магнитофона игровые программы ZX-Spectrum. Программы СПЕЦИАЛИСТА могут загружаться также эффектно, тем более, что в СПЕЦИАЛИСТЕ загрузка осуществляется сразу по сбросу, т.е нет необходимости набора команды с клавиатуры: запустил магнитофон нажал на кнопку и любуйся эффектами загрузки. Весьма эффектна загрузка заставки прямо в экран, бегущий счетчик, отсчитывающий время до конца загрузки, двигающиеся спрайты и блоки, вывод текста описания игры во время ее загрузки с ленты и другие подобные эффекты.
Кроме задачи украшения процесса загрузки, блочная автоматическая загрузка позволяет решить задачу защиты от копирования коммерческих программ, т.к при блочной загрузке появляется возможность изменить формат записи на ленту, тем самым затрудняя задачу взлома защиты кракеру (нельзя путать 2 разных термина - кракер и хакер; в то время как хакер, это высококвалифицированный программист-любитель, не занимающийся пиратством, кракер - программист вредитель ломающий защиты с целью прославиться и заработать на пиратстве, т.е понятие кракер равноценно понятию вор).
Данная статья предназначена для начинающих программистов для СПЕЦИАЛИСТА и предполагает то, что читатель уже знает все стандартные подпрограммы ROM-BIOS-а СПЕЦИАЛИСТА. Кроме того необходимо заметить, что эта статья относится только к настоящему СПЕЦИАЛИСТУ, т.е к СПЕЦИАЛИСТУ в базовой стандартной версии. А именно, - аппаратура соответствует 48-ми кило байтной версии (МК,1987,N2), ПЗУ - загрузчик А.Волкова (MK,05.1987) или любой другой полностью совместимый ROM-BIOS (напр: ленинградский МОНИТОР любой версии). Версии аппаратуры и программ КООП SP580, Л.Афанасьева, МП "КОЛЕД" и других, совместимы не 100% и имеют другие "стандартные" подпрограммы.
Для начала вспомним, что происходит при работе загрузчика.
Сразу после нажатия на кнопку СБРОС волковский загрузчик сразу вылетает на адрес C444. Здесь осуществляется инициализация системных переменных BIOS (и МОНИТОРА в версии ПЗУ Ленинград-89). Для этого вызывается стандартная подпрограмма пересылки блока: C42D- переброска блока (HL)...(DE) -> (BC). Далее п/п-ма C438 (MSSG) выводит на экран код очистки экрана и сообщение: "PROGRAMM ?" (в ленинградском мониторе выводится надпись "ЛЕНИНГРАД-1989"), и вот здесь начинается самое интересное.
Запускается п/п-мма C3F9. Смысл её работы в следующем. Сначала на ленте ищется синхробайт Е6. Пока он не найден или нет сигнала, эта п/п-ма крутится на одном месте. Как только синхробайт пойман, то вводятся 4 последующих байта. Два первых определяют с какого адреса загружать вводимый блок в ОЗУ (адрес начала блока), 2 следующих - адрес конца блока в ОЗУ. После этого п/п-ма C3F9 вводит блок и заносит его в ОЗУ по заданным адресам. Ввод заканчивается при заполнении последней ячейки. Адрес начала блока заносится в ячейку 8FE3 (ASTART). И на этом работа п/п-мы C3F9 заканчивается.
Далее загрузчик с помощью команды PCHL передает управление введенной программе (по адресу начала блока). Никакого контроля на правильность ввода не осуществляется. Таким образом блок грузящийся по сбросу имеет вид:
- цепочка нулевых-байт (пилотон),
- синхробайт Е6,
- нач.адрес блока (мл.байт, старший байт),
- конечный адрес блока (мл.б,ст.б)
- и сам блок.
В таком формате работал монитор Волкова (монитор-1985, загружаемый на 8D00). Этот МОНИТОР уже не используется на СПЕЦИАЛИСТАХ и все пользователи с сентября 1988 года используют только орловский МОНИТОР (обычно в комплекте со зверковским загрузчиком МК,04.1989) или гораздо более совершенный ленинградский монитор разработанный В.Ивинских.
Эти мониторы имеют директивы R/W работающие в таком-же формате, за исключением того, что сразу-же за блоком следует контр.сумма: сразу-же, а не как в "уродском" формате РК-86, где "К.С" идет только после дополнительной цепочки нулей и еще одного синхробайта Е6 (кроме того в РК-86 "перепутаны" адреса блока, т.е у них сначала идёт старший, потом младший байты адреса). Кроме директив R/W МОНИТОР имеет директивы I/O - формат с именем файла (имя до 17 символов, запрашивается). Запись имеет следующий вид:
- пилотон (256 нулевых байтов)
- синхробайт E6
- 3 байта D9
- имя файла (до 17 символов считая и завершающий 0)
- длинный пилотон (768 нулевых байтов)
- синхробайт Е6
- нач.адр
- кон.адр
- сам файл
- контрольная сумма.
Т.е по сути к волковскому формату добавлен только заголовок со своим пилотоном. Формат RAMDOS-Commander-а полностью совместим с таким форматом, т.к дополнительные параметры файла (CSUM, SCODE, WBYTE, ADRAM) передаются вслед за именем файла до начала второго пилотона, потому игнорируются орловским монитором.
Это теория. Как же делаются многоблочные автоматически загружающиеся программы? Идея заключается в том, что сначала загружается маленькая программка, для раскрутки. Она может грузиться по сбросу или перехватывать управление при работе директив ввода - R/I/Y/Z. После загрузки этой программки, ей передается управление, а дальше она уже делает, что хочет.
Первый пример. Пусть по сбросу загружается следующая программа:
Код:
.
ORG 0
LXI H,TXT1 ; HL-адрес текста
CALL 0C438H ; выводим текст (C818)
CALL 0C3F9H ; вводим новый блок
LHLD 8FE3H ; и передаем
PCHL ; ему управление
TXT1: DB 1FH,'ПРИВЕТ !'
DB 0DH,0AH,0
Эта программа очистит экран, напишет "ПРИВЕТ !" и будет вводить новый блок, а потом его запустит на исполнение. Пусть второй блок будет такой:
Код:
.
ORG 1000H
LXI H,TXT2
CALL 0C438H ; вывод TXT2
JMP 0C800H ; выход в монитор
TXT2: DB 'КОНЕЦ ВВОДА',0
После загрузки этого второго блока будет выдано сообщение TXT2 и "выйдем" в монитор. Такой ввод возможен только в разные адреса, ведь после ввода второго блока еще продолжает работать первый блок. Однако в BIOS имеется п/программа C453, которая вводит и автоматически запускает на исполнение введенный блок. Если использовать эту подпрограмму, то второй блок можно грузить прямо на место расположения первого (если первый блок уже не нужен).
Это всё для блоков загружаемых по "указанным" адресам. Но BIOS СПЕЦИАЛИСТА позволяет загружать блоки и по адресам, задаваемым программой. Тогда на ленте должны отсутствовать начальный и конечный адреса загрузки блока. Итак C422 - п/п-ма, которая загружает блок по "произвольным" адресам. Ей надо указать, куда загружать блок в регистрах HL,DE. Вот пример. 1-й блок:
Код:
.
LXI H,TXT1
CALL 0C438H ; вывод сообщения
LXI H,1000H ; указываем начальный и
LXI D,17FFH ; конечный адреса
CALL 0C422H ; вводим блок в 2 кбайта
JMP 1000H ; и запускаем
Таким образом BIOS СПЕЦИАЛИСТА предоставляет 4 стандартные подпрограммы ввода блока. Они описаны во всей литературе по СПЕЦИАЛИСТУ и должны безусловно поддерживаться на всех его клонах:
Код:
.
C3F9 - ввод блока с поиском синхробайта, адреса
начала и конца блока указаны на ленте
C422 - ввод блока с поиском синхробайта, адреса
начала и конца блока заданы в HL и DE
C414 - то же самое, но без поиска синхробайта,
позволяет иметь синхробайт не равный E6
C453 - ввод блока с поиском синхробайта и авто-
матическим запуском блока на исполнение
Как видите, благодаря наличию этих удобных п/п-рамм и процедуры загрузки по сбросу делать автоматически загружаемые программы для СПЕЦИАЛИСТА совсем не сложно. Однако теперь теперь перед программистом возникает задача - а как же записать на ленту блоки один за другим, да еще в некоторых не указывать адреса загрузки, а в других указывать. Также встает задача последующего копирования блочных программ. Обе эти задачи были блестяще решены еще в 1988 году программистом В.Ивинских.
Записывать блоки на ленту можно используя совсем ранние версии ленинградского МОНИТОРА (до 2.7), в которых возможности директив работы с лентой существенно расширены (можно вводить/выводить блоки в указанных выше форматах).
Тем не менее, специально для создания и копирования многоблочных программ В.Ивинских в конце 1988 года разработал две программы: компоновщик и копировщик SP-COPY. Эти программы предназначены для работы с блоками в одном стандартном двухфазном формате записи на ленту. Поэтому многие пользователи столкнувшись с тем, что копировщик не позволил им скопировать защищенные форматом записи блочные программы СПЕЦИАЛИСТА, напрасно ругали программу копировщик SP-COPY.
Рассмотрим назначение программы компоновщик. Эта программа позволяет загрузить в ОЗУ компьютера с ленты заранее подготовленные блоки, причем все они должны быть в стандартном формате монитора (с именем по директиве "О"). При вводе компоновщик запоминает параметры введенных блоков: адреса, длины пилотонов, имена, константы ввода. Далее можно, установив требуемые параметры вывода, вновь вывести эти блоки на ленту и получить готовую многоблочную программу. Отличительная особенность компоновщика - автоматическое определение константы чтения (для владельцев ленинградского монитора это не особенность, ибо в нем настройка на скорость осуществляется при работе всех директив ввода, в том числе и ZX !).
После запуска компоновщика внизу экрана появляется главное меню программы. Выбранный режим отображается путем "инверсии". В начале надо запустить режим LOAD (нажатием <ВК>) и ввести все заранее подготовленные блоки. Вводимые блоки укладываются в ОЗУ один за другим (до 8000H), а на экране в виде таблицы отображается информация о введенных блоках. Когда введены все блоки, выходят в меню нажатием <СТР>. Режим EDIT позволяет корректировать длину цепочки нулевых байтов перед блоком при записи на ленту: миним.длина соответствует 01, максим. - 09 и выбирается нажатием на соотвующую цифровую клавишу. Каждая единица дает 85 нулевых байтов (поэтому 03 - стандартная длина 256). Далее нажатием <+> и <-> можно определить выводить адреса начала и конца блока или нет. Клавишами управления курсором влево и вправо можно задать константу записи (25-99). В режиме DELETE можно удалить самый последний введенный блок, нажав на <ПС> (а не <ВК>!), и так можно удалить все блоки (тут надо осторожнее). Режим SAVE. Тут все просто - <ВК> и на ленту пошли блоки один за другим в заданных форматах и с нужными скоростями. EXIT - это выход в МОНИТОР по адресу C800.
Теперь о копировщике SP-COPY. В.Ивинских сделал его множество версий. Наибольшее распространение получили версии 1.3/4 и версии 2.0...2.2 (MSХ). Есть также версия для ОРИОНА (3.0). Эти версии загружаются по сбросу, причем работают прямо из экранной области (как жаловались некоторые пользователи - работает хорошо, но возникает "грязь" у правого края экрана !). Т.е сама программа размещаясь в экране (4К) не занимает места в ОЗУ для программ, оставляя для буфера ввода все 36К ровно. Если бы был цвет как в ОРИОНЕ, то экранные байты можно было бы сделать невидимыми. Эта программа абсолютно не использует никаких п/п-рамм в ПЗУ и даже стек переносит в экранную область. Скопировать SP-COPY можно им самим, но не нужно пытаться что-либо менять в программе, впрочем вряд ли это у вас получится.
Режимов работы всего 3 и всё очень просто. Режим выбирается "стрелками вверх-вниз" и запускается по <ВК>. Отмена режима - <СТР>. Последний введенный блок можно удалить в любом режиме, нажав <НР>/<СТР> (одновременно). Скорость вывода общая для всех блоков - и устанавливается "стрелками вправо-влево". Скорость ввода определяется автоматически и запоминается; "автоматически" вычисляется и устанавливается соответствующая константа вывода. Режим EXIT - выход в монитор.
А теперь рассмотрим какие блоки применялись в некоторых блочных программах СПЕЦИАЛИСТА, сделанных в Ленинграде, на примере простейшего блочного загрузчика игры с заставкой. Игра загружается по сбросу (это наиболее удобно). Первым загружается и сразу запускается маленький стартовый блок (100 байт). Он очищает экран, выводит надпись "НЕ ВЫКЛЮЧАЙТЕ МАГНИТОФОН..." (или на иностранном языке - "DO NOT STOP YOUR TAPE NOW..."). Далее определяется константа чтения (так как пользователь может не иметь ленинградского монитора, где это делается по сбросу) и управление передается подпрограмме C453 - загрузка следующего блока с его автоматическим запуском.
Пока всё просто. То есть первый блок на ленте имеет вид:
- Нач.адрес
- Кон.адрес
- Вывод надписи
- Опред-е конст.чт.
- JMP 0C453H
- Контр.сумма
В этом блоке контр.сумма не используется, но в последующих блоках она имеет значение. Следующий блок - загрузчик всего остального. Его структура:
- Нач.адрес
- Кон.адрес
- Вывод текста "GAME IS LOADING"
- Загрузка программы вывода заставки (бл.3)
- Загрузка картинки-заставки (бл.4)
- CALL программы вывода картинки
- Загрузка блоков игры (бл.5)
- JMP игра
- Контр.сумма
Таким образом 2-й блок по загрузке и запуску не отличается от первого - так же загружается и автоматически запускается (хоть так как константа чтения теперь не важна, этот блок можно для повышения надежности грузить со скоростью вдвое медленнее). Вся дальнейшая загрузка осуществляется под управлением этой программы - загрузчика (блока 2). Заметьте ! - последующие 3 блока уже не имеют начального и конечного адресов. Программа-загрузчик знает их заранее. Этот загрузчик уже не использует п/п-раммы BIOS "СПЕЦИАЛИСТА", а имеет свою собственную, более совершенную программу ввода с магнитофона, - она осуществляет текущий контроль качества ввода, подстраивается под скорость записи, проверяет контр.суммы и может делать различные "чудеса", типа бегущего счетчика, полос по бордюру, двигающихся блоков и т.п.
Впрочем эти эффекты вовсе не являются чудесами, а являются обычным делом для владельцев ленинградского монитора (где бегущий счетчик адреса и поддержка бордюра входят прямо в стандартные п/п-ммы ввода/вывода байта). Текущий контроль ввода сразу-же позволяет определить "останов" магнитофона или провал в записи (попробуйте остановить ленту - сразу же запищит и замигает надпись "TAPE LOADING ERROR !").
Игру полезно грузить маленькими блоками с контр.суммами каждого. Во-первых, это позволяет сразу же обнаружить сбой, а во-вторых, большое количество блочков загружаемых по разным адресам (например: задом наперед) не затрудняя копирование копировщиком, сильно затрудняет работу мерзким кракерам, любящим влезать в чужие программы. Естественно стартовый адрес игры не должен быть очевидным и при работе игра должна проверять флаги, выставленные загрузчиком, для сообщения об отсутствии попытки взлома.
Немного фантазии и изменять вашу программу уже мало кто захочет, ибо время потраченное на взлом сравнится с трудоемкостью создания новой программы. Естественно если ваша программа защищается от копирования, а не только от изменения, то загрузчик должен изменить и формат записи и конечно от копирования защищать программы нужно значительно умнее, так как программу защищаемую от копирования, кракают гнусные пираты, с целью получения наживы.
Чтобы сделать описанную выше блочную загрузку игры можно воспользоваться компоновщиком, а если скорость ввода не меняется то можно обойтись ленинградским монитором и копировщиком. Все блоки готовятся заранее и записываются на ленту причем первые 2 блока обязательно из тех-же адресов, где они будут работать! Остальные - всё равно. Эти блоки записывают на ленту, причем для блоков 1 и 2 нужно выводить адреса загрузки, а для блоков 3, 4 и 5 не надо, и даже нельзя. Иначе весь блок будет считан со сдвижкой на 4 байта! Кроме того между блоком 4 и блоком 5 необходима пауза побольше - т.е длинный пилотон перед блоком 5.
Это необходимо, чтобы успела отработать программа копирования в экран цветной заставки. Тут уж без компоновщика, одним копировщиком не обойтись. Кстати копировщик при копировании всегда считает и запоминает длину пилотона, поэтому если вы будете нажимать <ВК> позже начала пилотона, то пилотон на копии будет короче, чем на оригинале. Чтобы облегчить вам разработку собственных блочных загрузчиков, рассмотрим простую 4-х блочную программку. Проделайте всё сами на компьютере. С помощью редактора и ассемблера подготовьте на ленте 4 файла (уже в машинных кодах разумеется).
Код:
.
БЛОК 1: ORG 0
LXI H,TXT1
CALL 0C438H
JMP 0C453H
TXT1: DB 1FH,'ЗАГРУЖЕН БЛОК 1',0
БЛОК 2: ORG 0
LXI H,TXT2
CALL 0C438H
LXI H,100H
LXI D,KON3-1 ; конечный адрес блока 3
MVI A,0FFH ; подставьте после трансляции
CALL 0C416H ; программы блока 3
JMP 100H
TXT2: DB 13,10,'ЗАГРУЖЕН БЛОК 2',0
БЛОК 3: ORG 100H
LXI H,TXT3
CALL 0C438H
CALL 0C3F9H
LXI H,TXT4:
LHLD 8FE3H
CALL 0CC6CH ; вывод HL в HEX-виде
PCHL
TXT3: DB 13,10,'ЗАГРУЖЕН БЛОК 3',0
TXT4: DB 13,10,'ЗАГРУЖЕН БЛОК 4 С АДРЕСА: ',0
KON3: ; это адрес конца блока 3
БЛОК 4: ORG 200H
LXI H,TXT5
CALL 0C438H
JMP OC800H ; выход в монитор
TXT5: DB 13,10,'БЛОК 4 ОТРАБОТАЛ !',0
Тут применены все виды загрузки. При компоновке для блоков 1,2 и 4 надо указывать их адреса загрузки (т.е "+" в таблице компоновщика), для блока 3 - нет. Но в блоке 2 должны быть заданы его начальный и конечный адреса. Думаю проделать этот простейший пример сможет каждый. Применив немного фантазии и свои несложные програмки вы можете значительно украсить процесс загрузки имеющихся у вас игр (например: сделайте процесс загрузки с двойным дублированием блоков или с выводом сообщения после ввода очередного блока: "до конца загрузки осталось XX мин. XX сек."). Советую также разобрать стартовые блочки-загрузчики, примененные, например, в копировщике или других блочных программах.
Изучив их загрузчики, вы сможете выдрать надёжные п/программы чтения, а из игр вы можете взять подпрограмму ввода заставки прямо в экран (по синклеровски). А изучение загрузчиков защищенных от копирования программ позволит вам и самим защищать свои программы, что позволит вам даже продавать ваши программы, не опасаясь пиратов.
К сожалению нарисовать граф.редактором картинку-заставку - невозможно (если вы не художник). Поэтому выходом здесь может быть использование заставок или копий экрана из программ ZX-Spectrum. Для этого служит программа В.Ивинских "ZX-Picture". Она позволяет считывать с ленты программы в формате СИНКЛЕРА и использовать графику из его программ.
Несколько замечаний. Для СПЕЦИАЛИСТА существует несколько версий BIOS и мониторов (конечно ни один из этих мониторов нельзя даже сравнивать по качеству с ленинградским). Все эти программы большей частью совместимы с BIOS Волкова по системным п/программам в ПЗУ, но служебные ячейки в ОЗУ обычно используют по-разному. Например, в ленинградском мониторе векторизованы все стандартные п/программы, что означает, что любая из них может быть заменена п/программой пользователя (напр: можно загрузить магнитофонный драйвер, который, заменив стандартные п/п-ммы ввода/вывода байта на МГ, изменит низкоуровневый формат работы с магнитофоном на MSX (2-х частотн.с побайтовой синхр-ей) или напр-р "синклеровский"). А также в ОЗУ хранятся адреса важных п/п-м самого монитора (CCP), служебные ячейки RAMDOS и т.д.
Именно благодаря такому расширению количества служебных ячеек BIOS, ленинградский монитор и стал таким гибким и удобным. Поэтому, чтобы обеспечить совместимость со всеми используемыми системными программами не стоит использовать для загрузчиков ОЗУ по адресам служебных ячеек (256 байт ниже экрана), делать блочные программы использующие нестандартные входные точки разных BIOS-ов, а также перехватывающие управление за счет служебных ячеек.
Конечно эффектно, когда программа "вышибает" управление у монитора и Вы при попытке загрузить обычный файл директивой I, вдруг видите на экране разные неожиданные чудеса. Однако это приведет к неудобству других пользователей, ведь для СПЕЦИАЛИСТА столько-же мониторов, сколько крупных городов в стране. Поэтому для трюков следует использовать лишь стандартные ячейки. Некоторые из этих стандартных ячеек приведены ниже:
Код:
.
8FE3/E4 - при работе п/п-мы C3F9 сюда заносится
адрес начала введенной в ОЗУ программы
8FE7/E8 - адрес начала знакогенератора сдвинутый
вправо на 3 разряда
8FEC - признак регистра РУС/ЛАТ (8А ИЛИ 3А)
8FEF/F0 - код нажатой клавиши по верхн. Регистру
8FF1/F2 - частота/длительность звука при работе
станд.подпрограммы вывода звука - C170
8FF6/F7 - ячейка временного хранения стека (SP)
8FFA/FB - "маска" при выводе на экран (XRA)
8FFC - вертикальная координата курсора (0...255)
8FFD - горизонтальн.координата курсора (0...191)
8FFE - константа записи. Зависит от такта про-
цессора. Для 2 МГЦ=28H, 2,5=34H, 4,0=5FH
8FFF - константа ввода. При такте 2МГЦ = 3CH.
Для 2,5 МГЦ = 4CH, для 4 МГЦ = 80H. Для
ленинградского монитора при работе п/п-м
с поиском с/б это значение не играет роли
т.к сюда заносится автоматически опреде-
ленная константа ввода, но при работе п/п
ввода байта, эта ячейка ОЗУ также важна.
И еще один совет. В последнее время многие "специалисты" повышают такт процессора до 2,5-2,75 МГЦ для КР580, и до двойного Турбо в 4,0 МГЦ при замене процессора на Z80. При разработке блочных загрузчиков это необходимо учитывать. Ибо, если например, ваш загрузчик контролирует время до начала очередного блока с целью защиты от копирования, а т.к из-за отсутствия прерываний считать время можно только програмно, то при загрузке на машине с двойным тактом, программа, подождав вдвое меньшее время будет вынуждена уничтожить всё ОЗУ, полагая что мерзкий кракер пытается ломать вашу защиту. Проще всего узнать скорость машины можно "посмотрев" константу записи, которая также инициализируется по сбросу.
Этот текст был написан в 1989 году. Сейчас он был лишь немного отредактирован. Следует также заметить, что все, что здесь описано, было разработано В.Ивинских еще в 1988 году. Тем не менее эти материалы могут помочь начинающим сделать первые шаги в программировании, кого-то подтолкнут к собственным разработкам (возможно и для "ОРИОНА") поэтому этот текст и был предложен для планируемого журнала для любителей ОРИОНА В.Леонтьева из Ижевска.
Чистяков Владимир, С-Петербург, апрель 1993
[свернуть]