Глава
6
ОРГАНИЗАЦИЯ ЗАГРУЗЧИКОВ В
МАШИННЫХ КОДАХ
В
этой главе я расскажу, как на практике можно организовать кодовый загрузчик.
Приемы, описанные здесь, можно использовать также для внедрения в Ваши
бейсик-программы небольших подпрограмм в машинных кодах.
Вообще
говоря, об адаптации программ на диск в этой главе говориться не будет, однако
у человека, способного перемешать Бейсик с кодом, поубавится проблем с
адаптацией.
Самый
простой, понятный и системный, но глупый и неинтересный способ организации
загрузчика в машинных кодах был описан в четвертой главе (внесу ясность: под
организацией я подразумеваю то, как загрузчик загружается и запускается, а не
то, как он работает). На практике встречи с таким загрузчиком вызывают изумление,
настолько они редки. Способов организации загрузчиков так много, что использовать
«самый простой, понятный и системный» приходит в голову редким индивидуумам.
Наиболее
распространенным методом организации кодового загрузчика является загрузка его
вместе с бейсик-программой. Попробую объяснить, как можно перемешать Бейсик с
машинным кодом, чтобы подобный симбиоз работал так, как хотелось бы.
В
первом приближении можно выделить три способа внедрения машинного кода в
Бейсик:
1. в
виде комментария;
2. в
виде программы на Бейсике;
3. в
виде переменных.
Эти
фразы могут вызвать законное удивление и ряд недоброжелательных эпитетов в мой
адрес, поэтому попробую реабилитироваться и объяснить, что они значат.
Первый
способ заключается в следующем: загрузчик помещается на место комментария,
следующего за оператором REM. Это не значит, что требуется долго и
мучительно изобретать комментарий на языках народов - Крайнего Севера. Можно
просто набить необходимое количество пробелов. Если Вы так же тщеславны, как и
я, то можете набивать свои паспортные данные — так или иначе, вся введенная
информация будет впоследствии замещена машинными кодами.
Если
Ваши загрузчики или подпрограммы в машинных кодах достаточно велики, то способы
выделения места под них должны отличаться от вышеприведенного. Ведь если на
бумаге написать комментарий, состоящий из трех тысяч пробелов, достаточно
просто (как впрочем и из любого другого количества пробелов), то для того,
чтобы ввести этот текст с бумаги в машину, необходимо нанимать опытную
машинистку.
Мне
кажется, что человек, способный написать загрузчик длиной в несколько килобайт,
и без моих наставлений сможет грамотно выделить место для своей программы. Тем
не менее, могу посоветовать воспользоваться для этого одной из программ пакета
Supercode либо функцией CREATE системного монитора Моn2 (не путайте с
MONS), если он у Вас есть, и Вы умеете им пользоваться.
Второй
способ практически не отличается от первого, разница лишь в том, что программа
в машинном коде занимает не поле комментария, а поле операторов, то есть
отсутствует оператор REM. Поскольку интерпретатору абсолютно безразлично,
что находится в строке вслед за REM, то первым способом можно располагать машинный
код в произвольном месте программы. При использовании второго способа
необходимо проследить все возможные «ходы» интерпретатора: нельзя допускать,
чтобы в процессе работы интерпретатор «набрел» на строку, в которой находится
машинный код, иначе его может стошнить.
Третий
способ более сложен и интересен. Машинный код записывается в область переменных
Бейсика. Этого можно добиться, например, задав массив и записав в
зарезервированную под него область памяти свою программу в кодах.
Вообще
говоря, все три способа очень похожи друг на друга, разница состоит в том,
каким образом рассчитать адреса, по которым будет размещаться машинный код.
Если Вы используете первый или второй способ, то машинный код можно разместить
по фиксированным адресам, записав его в первую строку бейсик-программы (не
лишним будет придать этой строке нулевой номер). В этом случае Вы сможете
практически беспрепятственно редактировать бейсик-программу: адрес запуска
программы в машинных кодах не изменится. Он будет равен 33760 до инициализации
TR-DOS и 23872 после инициализации. Откуда взялись эти значения, легко
подсчитать. Вы уже знаете, что в обычных условиях программа на Бейсике
начинается с адреса 23755. Байты, расположенные по этому и последующим адресам,
определяют номер строки (23755 и 23756), длину строки (23757 и 23758), затем
следует поле операторов. В нашем случае— это оператор REM (адрес 23759),
после которого, начиная с адреса 23760, следует комментарий (если Вы уже
поместили туда какую-нибудь гадость, то комментарий может быть весьма
трудночитаемым).
Если
же Вы захотите поместить программу в машинных кодах где-нибудь в середине или в
конце бейсик-программы, то рассчитать адрес ее расположения будет несколько
сложнее. Для этого придется вычислить адрес начала строки, в которой программа
записана, и к нему прибавить 5. Дополнительным недостатком подобного способа размещения
кодов является то, что после редактирования или добавления в бейсик-программу
строк с номерами меньшими, чем номер строки с машинным кодом, адрес начала
машинного кода будет смещаться. То же самое будет происходить при размещении
машинного кода в области переменных.
Третий
способ наиболее трудоемок, и пользоваться им рекомендуется только тогда, когда
нужно посильнее запудрить мозги тому, кто захочет в этой программе разобраться.
Адрес машинного кода, размещенного в области переменных, рассчитать сложней,
чем в уже рассмотренных случаях, так как необходимо знать распределение памяти,
выделенной для переменных Бейсика. До сих пор мне встречался только следующий
способ: машинный код размещался, начиная с самой первой ячейки, отводимой под
переменные. Ее адрес можно определить оператором
PRINT PEEK 23627+256*РЕЕК 23628
Если
машинный код размещается подобным образом, то использование каких-либо
переменных в бейсик-программе полностью исключается: Вы можете выполнить
последовательность операторов
LET a=100: PRINT a
и,
если не вылетите куда-нибудь, то, скорее всего, получите сообщение
2 Variable not found, 0:2
При
использовании переменных бейсик-программы для размещения машинного кода
возникает один интересный эффект: такую бейсик-программу, записанную с
автостартом на кассету или диск,
невозможно
загрузить оператором MERGE.
Для
того чтобы программа не загружалась оператором MERGE, можно также
посоветовать указать неправильную длину одной или нескольких строк
бейсик-программы. Это, во-первых, помешает их редактировать, а во-вторых,
оператор MERGE при загрузке, после некоторых раздумий, покажет
загружающему фигу. Сбросьте компьютер и наберите любую программку из одной
строчки, а затем выполните оператор РОКЕ 23757,255 и попробуйте отредактировать
эту строчку. Эффекты могут быть самыми неожиданными.
Если
Вы все же решили использовать переменные для хранения программы в машинных
кодах, то выделить для этого область можно следующим образом:
CLEAR: DIM a$(x)
— где х — длина
Вашей программы в байтах (на самом деле памяти выделяется несколько больше).
Оператор
CLEAR очищает область переменных. Это необходимо для того, чтобы массив а$
размещался в самом начале этой области, и можно было легко вычислить адрес его
начала. Размещая программу в машинных кодах в области переменных, будьте
осторожны. К каким эффектам при этом может привести выполнение операторов CLEAR
или RUN, не знают ни сэр Клайв Синклер, ни доктор Логан ни, тем
более, я. Определенно сказать можно только то, что программа работать не будет.
Что касается меня, то я ни разу не использовал область переменных в таких целях
и, тем не менее, не чувствую себя в чем-то ущербным.
_________________
В
предыдущей главе Вашему вниманию предлагались подпрограммы в машинных кодах,
помогающие быстро перемещать большие области памяти и запускать программы.
Значительно удобнее организовывать эти подпрограммы не так, как было
предложено, то есть дописывая их к основному файлу, а внедрять их в Бейсик
одним из приведенных способов.
Для
примера внедрим в Бейсик программу в кодах, приведенную на стр. 23. В загрузчик
на Бейсике необходимо первой строкой вставить оператор REM и вслед за
ним «комментарий», состоящий из 12 пробелов или других символов. Затем нужно
заменить «комментарий» на машинный код с помощью оператора РОКЕ (система
TR-DOS должна быть инициализирована):
РОКЕ 23872,33
РОКЕ 23873,160
РОКЕ 23874,96
РОКЕ 23875,17
и т. д., до
полного удовлетворения.
Адрес
запуска этой подпрограммы будет равен адресу ее начала, то есть 23872. В
результате Ваших действий новый вариант загрузчика будет выглядеть так:
5 REM < Здесь будет всякая гадость. Возможно даже,
что Вы не увидите 10-й строки, в этом случае
выполните LIST 10>
10 BORDER 1: PAPER 1: INK 1
20 CLEAR 24735: REM Обратите внимание!!!
30 RANDOMIZE USR 15619: REM : LOAD "name"CODE
40 CLEAR 24199
50 RANDOMIZE USR 23872
60 RANDOMIZE USR 49152
Таким
образом Вы избавляетесь от необходимости дописывать к файлу какие-либо куски и
оставляете за собой возможность не трогать файл вообще, а загружать его в
память оператором LOAD "name"CODE 24736 системы TR-DOS.
Я
полагаю, сейчас Вам стало ясно, что оператор РОКЕ — хоть и универсальное
средство для программирования в машинном коде, но его применение — довольно
занудное занятие, а результат работы плохо поддается проверке. Если Вы где-то
ошиблись, то найти ошибку будет довольно сложно, даже если использовать функцию
РЕЕК. Так что мой Вам совет: немедленно бегите к друзьям, спекулянтам, к кому угодно;
просите, покупайте, воруйте (воровать нехорошо!) описания языка ассемблера,
программ ассемблера и отладчика*; садитесь к машине и изучайте все это. Если я
когда-нибудь напишу продолжение этой книжки, то наверняка буду подразумевать,
что Вы уже имеете хоть какое-то представление об ассемблере.