Глава 1
КАК БОРОТЬСЯ С
БЕЙСИКОМ
Загрузчики
программ, с которыми Вы можете встретиться, подразделяются на три категории:
загрузчики на Бейсике (загрузка производится оператором LOAD),
загрузчики в машинных кодах и смешанные загрузчики.
Проще
и приятнее, безусловно, иметь дело с загрузчиками на Бейсике. Их можно
разделить на открытые и закрытые.
Открытые
загрузчики — это когда после загрузки первого файла программы (то есть
собственно загрузчика) нажимаешь Break, затем Enter, и на
приятном голубом фоне появляется:
10 INK 1: PAPER 5: BORDER 5: CLEAR 25199
20 LOAD ""SCREEN$: LOAD ""CODE
30 RANDOMIZE USR 32768
или
что-то в этом роде.
Надеюсь,
что не ущемлю Ваше самолюбие, если все же покажу, как адаптировать к диску
такую программу:
10 INK 1: PAPER 5: BORDER 5: CLEAR 25199
20 RANDOMIZE USR 15619: REM :LOAD "name1"CODE 16384
25 RANDOMIZE USR 15619: REM :LOAD "name2"CODE
30 RANDOMIZE USR 32768
— где
name1 и name2 — имена, которые Вы присвоите файлам, следующим за
загрузчиком. Иногда эти имена можно оставить прежними, но чаще их приходится
изменять. Во-первых, имена файлов в кассетной версии могут содержать символы
или комбинации символов, которые будут восприниматься системой TR-DOS неправильно.
Например, встретив оператор LOAD "B:SCREEN", TR-DOS будет загружать
файл "SCREEN" с дисковода "В", а отнюдь не файл с именем
"В:SCREEN", как хотелось бы. Во-вторых, в отличие от кассеты, на
которой файлов с именем "SCREEN" может находиться сколько
угодно, на диске файл с таким именем должен быть только один. Поэтому обычно
файлы программы называют именами, производными от имени самой программы (например,
"rambo$", "rambo cd" и т. п.). Кроме того,
считается хорошим тоном в имени основного файла (загрузчика) использовать прописные
буквы, а в именах остальных — строчные*.
Изменить
имена файлов на желаемые можно еще до перенесения программы на диск,
воспользовавшись обыкновенным ленточным копировщиком (TF COPY, COPY DE LUXE,
COPY COPY и др.), после чего останется лишь перебросить файлы на диск
специальным копировщиком «лента—диск». Удобнее всего пользоваться копировщиками
Programmable Copier v. 2.0 (PCopier), Pcopier Plus или AMCopier. При работе с
* На
самом деле, последнее придумал я сам, но, по-моему, это можно считать хорошим
тоном (прим, авт.).
перечисленными
копировщиками, во-первых, можно поменять имена файлов уже после копирования
файлов на диск, а во-вторых, есть уверенность, что файлы и диск не будут
испорчены (при использовании же копировщика Дерещука такой уверенности нет).
Но
вот Вы, самым тщательным образом просмотрев все свои диски, убедились, что
«специального копировщика» на них нет. Не отчаивайтесь, еще не все потеряно.
Загрузите программу в обыкновенный копировщик и внимательно изучите все, что он
расскажет о ее файлах. Запишите длины и адреса загрузки каждого файла и смело
нажимайте кнопку сброса. Теперь, обладая столь ценной информацией, можно действовать
дальше.
Пусть,
например, копировщик сообщил следующее:
файл "SCREEN" — адрес загрузки 49152, длина 6912;
файл "CODE" — адрес загрузки 25200, длина 40335.
Используя
эти данные, можно на основе приведенного варианта загрузчика написать
специальный копировщик:
10 INK 1: PAPER 5: BORDER 5: CLEAR 25199
20 LOAD ""SCREEN$
21 RANDOMIZE USR 15619:
REM : SAVE "shaky sc"CODE 16384,6912
30 LOAD ""CODE
31 RANDOMIZE USR 15619:
REM : SAVE "shaky mn"CODE 25200,40335
40 STOP
— то
есть после каждого оператора загрузки файла с ленты нужно поставить оператор
записи на диск, указав адрес и длину файла. Если в ленточном загрузчике не
упоминается адрес загрузки, то используйте адрес, который любезно сообщил
копировщик. Но будьте внимательны, не всегда адрес в загрузчике указан в явном
виде: в приведенном примере копировщик указал адрес первого файла 49152, но загрузка
производится оператором LOAD ""SCREEN$, который является
эквивалентом LOAD ""CODE 16384,6912. Поэтому файл необходимо
записывать с адреса 16384, а не 49152.
На
этом, как правило, Ваши мучения должны закончиться. Единственной «серьезной»
проблемой при переделке подобного загрузчика может стать то, что при выводе его
листинга цвет фона и тона совпадут, и Вам придется изменить один из цветов
оператором INK или PAPER. Но даже этого можно избежать, подгрузив
файл загрузчика оператором MERGE, а не LOAD. В этом случае
программа не запустится, а появится сообщение 0 ОК, 0:1. Единственно, не
следует забывать, что делать это можно только на чистой машине, то есть после
нажатия клавиши сброса.
Надеюсь,
что в вопросах, касающихся открытых загрузчиков, наступила полная ясность даже
у того, кто ничего не понял.
_____________________
Ваше
счастье, если просмотрев листинг загрузчика, Вы увидите нечто похожее на
предыдущий пример, то есть на нормальную бейсик-программу, но на практике
гораздо чаще появляется что-нибудь вроде:
0 REM Look here, little bozo, why don't you go fuck yourself?
0 DRAW USR VAL "24500",deBillGilbert'89 0 REM FLASH
DRAW ???Aw COPY M
или
еще более страшное. Подобный ужас можно назвать закрытым загрузчиком. Попытаюсь научить Вас «открывать» его.
Бейсик-программу
защищают от просмотра и редактирования несколькими способами. Рассмотрим самые
популярные из них.
Вы,
вероятно, уже сталкивались с таким фокусом, как нулевая строка, которую нельзя
ни удалить, ни вызвать на редактирование. Справиться с такой защитой можно
двумя путями. Первый заключается в перенумерации строк с помощью одной из многочисленных
сервисных программ (ZXED, например). После чего обычно получается вполне доброкачественный
листинг (отсутствуют нулевые строки и т. п.).
Второй
путь более изящен, но эффективен только в том случае, когда нулевая строка одна
и располагается первой в программе. Для его реализации нужно иметь
представление о формате хранения строк бейсик-программы в памяти ZX Spectrum*:
номер строки (2 байта)
|
длина строки (2 байта)
|
операторы Бейсика
|
перевод строки
|
Вы
уже, вероятно, поняли, что изменить номер строки проще простого, записав
оператором РОКЕ требуемое число в первые два байта программы. Но куда именно
записывать? Вот этим вопросом мы сейчас и займемся.
Определить
начало бейсик-программы поможет системная переменная PROG**. Она находится по
адресу 23635 и занимает два байта. Таким образом, выяснить адрес первого байта
первой строки бейсик-программы можно, выполнив оператор
PRINT PEEK 23635+256*РЕЕК 23636
Если
к Вашему Speccy не подключено никакой периферии, то после выполнения этого
оператора на экране появится число 23755. Если подключен и инициализирован
Beta-Disk, то появится число 23867, поскольку TR-DOS резервирует 112 ячеек для
своих нужд.
Итак,
адрес, по которому хранится номер начальной строки бейсик-программы, обнаружен:
пусть, к примеру, он равен 23755. Теперь можно менять номер первой строки:
POKE 23755,N-256*INT (N/256):POKE 23756,INT (N/256)
—
где N — требуемый номер. Это идеальный способ переопределения
*
Более подробно об этом см.[1].
**
Подробнее о системных переменных читайте в [1].
номера строки.
Можете, кстати, поэкспериментировать, задавая номера строк более 9999:
получаются весьма любопытные результаты. Как показывает практика, необходимость
изменять номера более 255 возникает редко. Поэтому обычно достаточно выполнить
всего один оператор:
POKE 23756,N
— где
N — номер строки не более 255. Этим оператором изменяется только младший байт
номера строки (еще раз хочу напомнить, что последние два примера приведены,
-исходя из того, что бейсик-программа начинается с адреса 23755).
Выше
было сказано, что такой способ борьбы с нулевой строкой эффективен только,
когда нулевая строка находится в начале программы. На самом же деле, ничто не
мешает изменить номер второй и последующих строк. Для этого пригодится информация
о длине строки, содержащаяся в двух следующих за номером строки байтах.
Прибавив длину строки к адресу ее начала, получим адрес следующей строки.
__________________________
Не
менее распространенным приемом защиты бейсик-программ является добавление в
строки бейсик-программы различных управляющих кодов: INK, PAPER, AT, TAB и
других. Полную таблицу контрольных кодов Вы сможете найти в [1, 2].
Для
примера поясню разницу между контрольным кодом INK и оператором INK.
Если при выводе листинга программы бейсик-система встречает оператор INK,
она честно представляет его на экране, не производя никаких действий. Если же
встречается контрольный код INK, то следующий за ним байт бейсик-система
воспринимает как код цвета тона и продолжает вывод листинга этим цветом. Контрольный
код INK и оператор INK различаются своими кодами в таблице символов
(см. [1, 2]), и их нельзя путать. Все сказанное справедливо для PAPER,
FLASH, TAB и др. Необходимо учесть только, что позиция вывода на экран
задается двумя байтами, следующими за контрольным кодом AT и TAB,
а не одним, как для других контрольных кодов.
Большинство
контрольных кодов можно удалить клавишей Delete, вызвав бейсик-строку на
редактирование. Поочередно нажимайте клавиши «курсор вправо» и Delete, и если
авторы защиты использовали корректные значения контрольных кодов, то Вы без
осложнений рано или поздно получите на экране вполне осмысленную информацию. Но
бывает и так: когда, например, бейсик-интерпретатор просят вывести листинг
программы 121-м цветом в знакоместо с координатами (240,86), тогда он
просто-напросто прекращает вывод листинга на экран, а при попытке
редактирования строки начинает истошно орать. В этом случае нужно сначала
«заткнуть ему рот» оператором РОКЕ 23608,0 и далее действовать по
приведенной схеме.
___________________
Еще
одна крайне неприятная вещь, которая может помешать справиться с программой при
адаптации ее на диск, — это нехватка памяти. Она может возникнуть из-за того,
что, во-первых, TR-DOS резервирует 112 байт для своих системных переменных;
во-вторых, операторы загрузки с диска могут занимать в памяти значительно больше
места, чем аналогичные инструкции для ленты (если Вы привыкли исчислять память
мегабайтами, слово «значительно» беру назад); в-третьих, системе TR-DOS
необходим буфер не менее 256 байт для записи/чтения файлов, вывода каталога и
т. п. и еще больше для распечатки каталога дискеты командой LIST. Этого
дополнительного расхода памяти бывает достаточно, чтобы компьютер выдал сообщение:
Out of memory, No room for line или еще что-нибудь в этом роде.
Не
печальтесь, в этом случае (как, впрочем, и во всех остальных)
компьютер
можно обмануть. Наиболее простой и доступный способ
— это
разделение загрузчика на две или более части.
Допустим, исходный ленточный загрузчик выглядел так:
10 CLEAR 24250
20 LOAD ""SCREEN$
30 LOAD ""CODE
40 LOAD ""CODE 23296,256
50 RANDOMIZE USR 23296
60 LOAD ""CODE
70 RANDOMIZE USR 24576
Без
толики сомнения Вы написали ди-
сковый загрузчик
для этой программы:
10 CLEAR 24250
20 RANDOMIZE USR 15619:
REM : LOAD "bomb sc"CODE 16384
30 RANDOMIZE USR 15619: REM :LOAD "bomb mn"CODE
40 RANDOMIZE USR 15619:
REM : LOAD "bomb pb"CODE 23296
50 RANDOMIZE USR 23296
60 RANDOMIZE USR 15619: REM : LOAD "bomb ls"CODE
70 RANDOMIZE USR 24576
и, запустив
программу, вместо игры получили сообщение 4 Out of memory, 20:1.
Только не вздумайте тянуться к @безьяне (так некоторые именуют кнопку Magic, поскольку в
простонародье символ @, которым TR-DOS помечает названия файлов, «сброшенных» кнопкой Magic,
часто называют «обезьяной» или «собакой». Однако, в целом, звучит неплохо и
соответствует истинному положению вещей). Попробуйте разбить загрузчик на две
части. Например, в нашем случае первая часть загрузчика (назовем этот файл
"BOMB") будет выглядеть так:
10 CLEAR 24250
20 RANDOMIZE USR 15619:
REM :LOAD "bomb sc"CODE 16384
30 RANDOMIZE USR 15619:REM :LOAD "bomb mn"CODE
40 RANDOMIZE USR 15619:REM :LOAD "bomb bs"
Вторую
часть назовем "bomb bs":
10 RANDOMIZE USR 23296
20 RANDOMIZE USR 15619:REM :LOAD "bomb ls"CODE
30 RANDOMIZE USR 24576
Компьютер
загрузит сначала первый загрузчик "BOMB", которому места в
памяти вполне достаточно. Затем, после выполнения заданных операций, первый
загрузчик загрузит второй ("bomb bs"), ему, в свою очередь, места
также хватает. Второй загрузчик закончит эпопею с загрузкой и запустит
программу.
Могут
возникать ситуации, когда приходится «распиливать» загрузчик на три или более
части, однако это бывает крайне редко. Точнее, крайне редко такой прием
помогает.