Тема: LINKER - Cбopщик пepeмeщaeмыx пpoгpaмм
Сюда перенёс всё про раскопки линкера
Немного не по порядку, ну да ладно
Персональный компьютер "Агат" - технические беседы (является частью agatcomp.su / agatcomp.ru) Как зарегистрироваться?
Вы не вошли. Пожалуйста, войдите или зарегистрируйтесь.
ПЭВМ "Агат" 7-9: Форум → Прочие трансляторы → LINKER - Cбopщик пepeмeщaeмыx пpoгpaмм
Страницы 1
Чтобы отправить ответ, вы должны войти или зарегистрироваться
Сюда перенёс всё про раскопки линкера
Немного не по порядку, ну да ладно
Возвращаясь к линкеру.
Его многие видели на разных дисках , но мало кто видел к нему доку (она водится только на полном ИКП-7).
Если читать лень: эта прога обединяет несколько скомпилированных частей в один файл, автоматически согласуя метки между ними.
ПАКЕТ ПРИКЛАДНЫХ ПРОГРАММ "ШКОЛЬНИЦА"
К О М П О Н О В Щ И К П Е Р Е М Е Щ А Е М Ы Х П Р О Г Р А М М
РУКОВОДСТВО ПРОГРАММИСТА
1. ОСНОВНЫЕ СВЕДЕНИЯ
Компоновщик перемещаемых программ (LINKER) предназначен для компоновки объектных
программ, полученных различными способами на ПЭВМ Агат.
1.1. Объектные программы
Объектная (двоичная) программа - это любая последовательность байтов, т.е.
информация, рассматриваемая как содержимое непрерывного участка машинной памяти.
Это может быть как программа в машинных кодах, так и другая двоичная
информация (например, графическая), а также любая их смесь.
Абсолютная объектная программа считается связанной с определенным
участком памяти: она корректна только при размещении с некоторого начального
адреса. Эта связь возникает, когда в объектной программе содержатся абсолютные
адреса ее внутренних частей (например, в командах абсолютного перехода).
При загрузке такой программы с другого адреса ссылки внутри нее нарушаются.
Перемещаемая объектная программа не зависит от места размещения: она либо не
содержит абсолютных ссылок внутрь себя (например, графическая информация), либо
содержит дополнительную информацию, позволяющую при размещении с конкретного
адреса выполнить их пересчет (настройку).
1.2. Создание объектных программ
Абсолютные программы порождаются Ассемблером ДОК "Школьницы", а также при
записи участка памяти командами BSAVE из DOS 3.3 и [SAVE из Отладчика ДОК.
Перемещаемые программы порождаются Ассемблером при указании
в тексте исходной программы псевдокоманды REL. Одновременно для них создаются
таблица перемещения и таблица внешних меток. Формат таблиц приведен в Приложении 1.
На диске абсолютные программы хранятся в К- и В-файлах, перемещаемые - в
П-файлах (DOS 3.3 выдает их в каталоге, как R-файлы). Файлы этих типов тоже
называются абсолютными или перемещаемыми объектными файлами.
1.3. Компоновка объектных программ
Компоновкой называется сборка раздельно подготовленных объектных программ
(модулей, подпрограмм, данных) в единый комплекс. Она сводится к выполнению
одного или нескольких из следующих действий:
- физическое склеивание нескольких программ в одну,
- настройка перекрестных ссылок между ними для взаимодействия,
- настройка программы на другое место размещения в памяти.
LINKER позволяет проводить компоновку в несколько этапов с созданием
промежуточных объектных программ. Окончательный результат компоновки -
абсолютная объектная программа, готовая к запуску.
1.4. Задание перекрестных ссылок в программах
Перекрестная ссылка - это ссылка из одной программы на метку, описанную в
другой программе. Такая метка называется глобальной.
Метка объявляется глобальной с помощью псевдокоманды ENTRY.
Использование глобальной метки в другом файле описывается псевдокомандой
EXTRN.
При создании перемещаемого файла все метки, описанные в данной программе
с помощью ENTRY и EXTRN, собираются в таблицу глобальных меток, которая используется
при компоновке. При создании абсолютного файла таблица не создается.
2. ВХОДНЫЕ И ВЫХОДНЫЕ ДАННЫЕ
Входными данными Компоновщика являются объектные файлы, подлежащие сборке, и
входная строка, в которой при вызове Компоновщика указываются имена этих
файлов со списками атрибутов.
Выходными данными Компоновщика являются:
а) объектные файлы, содержащие:
- абсолютную программу, готовую к загрузке и исполнению;
- перемещаемую программу, таблицу перемещения и таблицу глобальных меток,
пригодные для повторной обработки Компоновщиком;
- отдельную таблицу глобальных меток;
б) сводка всех глобальных меток и их новых адресов на экране монитора;
в) текстовый файл, содержащий ту же сводку.
3. ОБРАЩЕНИЕ К ПРОГРАММЕ
3.1. Вызов Компоновщика
Вызов Компоновщика может осуществляться из Отладчика ДОК,
для чего необходимо запустить программу LINKER:
> [RUN LINKER
После чего на запрос "?" ввести входную строку.
Другой способ запуска - записать команду вызова и входную строку в
текстовом файле (например, с именем LINK):
[RUN LINKER
входная строка
Тогда для запуска достаточно исполнить команду:
> [EXEC LINK
3.2. Формат входной строки
Входная строка имеет вид:
файлР [ атрР ] = файл1 [ атр1 ] , ... , файлN [ атрN ]
где файлР - имя файла-результата, файл1-файлN - имена собираемых файлов,
[ атр ] - необязательные списки атрибутов каждого файла.
Полный список атрибутов файла-результата:
[ Axxxx, К, П, R, B, M, T, Cметка, Sметка ]
где xxxx - шестнадцатеричное число.
Полный список атрибутов собираемых файлов:
[ M, X ]
Порядок атрибутов произволен, однобуквенные атрибуты можно не разделять.
3.3. Атрибуты файла-результата
Axxxx - задать начальный адрес новой объектной программы, по умолчанию - $800.
К,П,R,B - задает тип объектного файла и, соответственно, вид объектной программы.
Атрибуты П и R равнозначны. По умолчанию задан атрибут К.
M - записать таблицу перемещения и таблицу глобальных меток в отдельный перемещаемый
файл, имя которого образуется приписыванием бувы X к имени файла-результата
(см. примеры в 4.).
Т - записать листинг (сводку глобальных меток новой объектной программы и их
адресов) в отдельный текстовый файл, имя которого образуется приписыванием
буквы M спереди к имени файла (** в данной версии не реализовано **).
C,Sметка - задать стартовую метку: первой командой нового файла будет записан
переход на указанную метку (** в данной версии не реализовано **).
3.4. Атрибуты собираемых файлов
M - не включать объектную программу из данного файла в файл-результат. Фактически
используются только определения глобальных меток. По умолчанию включается.
X - не включать глобальные метки данного файла в список глобальных меток файла-
результата. По умолчанию включаются.
4. ПРИМЕРЫ КОМПОНОВКИ ПРОГРАММ
Пусть в файлах ПРИМЕР1, ПРИМЕР2, ПРИМЕР3 находятся, соответственно, следующие
программы:
AAA: LDA BBB BBB: LDA CCC CCC: LDA AAA
STA CCC STA AAA STA BBB
RTS RTS RTS
Задача состоит в том, чтобы объединить их в одну объектную программу.
Вставим в каждую программу какой-нибудь ORG, например, с адреса $800,
а также псевдокоманду REL.
Для обозначения перекрестных ссылок на метки ААА, ВВВ, CCC между файлами
используем псевдокоманды EXTRN и ENTRY.
В окончательном виде файлы будут иметь вид:
ORG $800 ORG $800 ORG $800
REL REL REL
EXTRN BBB EXTRN AAA EXTRN AAA
EXTRN CCC EXTRN CCC EXTRN BBB
ENTRY AAA ENTRY BBB ENTRY CCC
AAA: LDA BBB BBB: LDA CCC CCC: LDA AAA
STA CCC STA AAA STA BBB
RTS RTS RTS
При ассемблировании будут созданы 3 перемещаемых файла (пусть их имена -
ПР1, ПР2, ПР3).
Для сборки их необходимо набрать:
> [RUN LINKER
и на дополнительный запрос ввести следующую входную строку:
?> КОДФАЙЛ [A2000] = ПР1,ПР2,ПР3
По окончании сборки будет выдан такой листинг сборки:
КОДФАЙЛ:2000-2014 - диапазон адресов, в котором лежит новая программа
AAA 2000 - новые адреса всех глобальных меток, которые
BBB 2007 вошли в эту программу
CCC 200E
Рассмотрим еще несколько вариантов сборки той же программы:
1. ПЕРФАЙЛ1 [A2000,П] = ПР1 [X], ПР2 [X], ПР3 [X]
Глобальных меток в новом файле не будет. В отличие от программы КОДФАЙЛ эта
программа перемещаема (атрибут П) и не готова к немедленному запуску. Но зато ее
можно легко настроить на произвольный адрес:
КОД_ИЗ_ПЕР1 [A2015] = ПЕРФАЙЛ1
КОД_ИЗ_ПЕР2 [A202А] = ПЕРФАЙЛ1
а также переписать в формат B-файла:
BIN_ИЗ_ПЕР [A203F,B] = ПЕРФАЙЛ1
2. ПЕРФАЙЛ2 [П] = ПР1, ПР2, ПР3
В этом перемещаемом файле будут глобальные метки AAA,BBB,CCC.
3. КОДФАЙЛ2 [A2000,М] = ПР1, ПР2, ПР3_
Здесь будет создан К-файл КОДФАЙЛ2, содержащий то же, что файл КОДФАЙЛ,
но дополнительно будет создан перемещаемый файл ХКОДФАЙЛ2, в котором нет
никакого кода, но есть таблица перемещения и таблица глобальных меток к
файлу КОДФАЙЛ2. Теперь можно, во-первых, исполнить данную объектную программу
(запустить КОДФАЙЛ2), а во-вторых, использовать ее для дальнейшей сборки:
СБОРНАЯ [A3000,П] = НОВАЯ, ХКОДФАЙЛ2
Здесь, в предположении, что НОВАЯ программа использует метки ААА,ВВВ,CCC,
будет создан файл со ссылками "в никуда", т.к. сами эти программы отсутствуют.
Это может оказаться удобным при ссылке на резидентную программу (например, ОС).
5. Как видно из примеров, только перемещаемые файлы могут при сборке
действительно перемещаться. Компоновщик допускает объединение и абсолютных
файлов (возможно, вперемешку с перемещаемыми), но при этом должно строго
выполняться правило состыковки: конец предыдущего файла совпадает с началом
следующего.
Так, абсолютные файлы КОДФАЙЛ, КОД_ИЗ_ПЕР1, КОД_ИЗ_ПЕР2, BIN_ИЗ_ПЕР из
примеров выше можно скомпоновать именно в таком порядке, и ни в каком ином:
СУПЕРКОД = КОДФАЙЛ, КОД_ИЗ_ПЕР1, КОД_ИЗ_ПЕР2, BIN_ИЗ_ПЕР
Даже простое перемещение их недопустимо: обращение
СУПЕРКОД [A3000] = КОДФАЙЛ, КОД_ИЗ_ПЕР1, КОД_ИЗ_ПЕР2, BIN_ИЗ_ПЕР
даст ошибку.
Приложение 1
ФОРМАТ ПЕРЕМЕЩАЕМЫХ ФАЙЛОВ
Байты файла Содержимое
0-1 Начальный адрес A
2-3 Длина кодовой части K
4-5 Суммарная длина таблиц T
6- 6+K-1 Кодовая часть программы
6+K -... Таблица перемещения
... - ... Таблица глобальных меток
Приложение 2
СООБЩЕНИЯ КОМПОНОВЩИКА
#n в сообщениях ниже означает номер объектного файла во входной строке,
в котором произошла данная ошибка.
ОШИБКА В ПАРАМЕТРАХ - ошибка в задании входной строки.
МАЛО ПАМЯТИ: СВЯЖИТЕ ПО ЧАСТЯМ - не хватает оперативной памяти для хранения
таблиц Компоновщика. Рекомендуется собрать файлы в два приема с использованием
промежуточного файла.
КОНЕЧНЫЙ АДРЕС > FFFF: #n - получившаяся программа выходит за границу памяти.
Рекомендуется собрать ее, указав меньший начальный адрес.
<метка> ПОВТОРЯЕТСЯ В #n - указанная метка определена дважды в разных файлах;
выдается номер файла каждого повторного определения.
НЕ СТЫКУЕТСЯ #n - при компоновке абсолютных программ: начальный адрес файла #n
не совпадает с концом файла #n-1.
МНОГО ИМЕН: #n - больше, чем 256 глобальных меток, при сборке недопустимо.
Для
Че-то приподнял всю эту тему - насчёт разработки крупных прог и программных комплексов.
Вторая задача даже посложнее: к примеру, есть библиотека, хочется иметь её скомпилированной и уже в таком виде подключать к другим программам.
На агате это делалось раньше просто на уровне исходников, причем библиотека либо вкладывалась в цепочку исходников и допиливалась по месту либо ... либо не допиливалась.
Это клёво, конечно, но компиляция идёт долго. Более худший вариант: компилировалась отдельно, а входные точки и переменные в основную прогу передавались в виде фиксированного списка констант (что совсем уж плохо).
В одной из своих старых прог я делал по другому: в начале библиотеки вставлял таблицу входных точек, создавая её командами DW. Это было простое позднее связывание, a'la динамическая линковка. Ни у кого на агате таких решений не видел. Но это требует привязки библиотеки к конкретному адресу. Зато программу и библиотеку можно существенно модифицировать по отдельности друг от друга.
Теоретически (ну прям вот очень теоретически), на агате существовал всем известный (и - одновременно - никому неизвестный) LINKER. Именно он может собирать группу уже компилированных частей программы в один файл, загружающийся в заданную точку, причем согласовав все метки, смещения и всё, что нужно.
Известный - потому что был почти на всех системных дисках с ДОК ("Ассемблер"), неизвестный - потому что ни в одной коллекции я не видел следов его использования. Сам я его тоже не мог использовать, потому что не знал - как? ИКП у меня не было, а инструкция к LINKER была только в ИКП-7-840.
Я даже пытался его декомпилировать и разобраться в логике работы. Но результатов (уже не помню - почему) не было. Наверное, свои велосипеды оказались проще.
При разборе коллекции Лёвина (автор СУБД Паруса/Карата/Эврика и Шахмат/Шашек) обнаружилась как инструкция к Linker так и его исходники.
Некоторое время назад я приступил к его неспешному ковырянию.
Выяснилось, что сам по себе линкер почти работает, но только в старых версиях Школьницы. Судя по всему, те, кто записывал его на диски с ИКП, не пытались проверить работоспособность.
Тут вот какая штука: Цикоза (руководитель группы авторов Школьницы, LINKER родом оттуда же), стремился сохранять совместимость между разными версиями ДОК, в том числе по программному интерфейсу. Но делалось это немного коряво: при компиляции ДОК добивались того, чтобы ДОКУМЕНТИРОВАННЫЕ входные точки и таблицы аргументов драйверов файловой системы и дисководов совпадали. А вот недокументированные - как повезёт. Linker зачем-то использует входную точку процедуры PUTTSL (мне пока не ясно - зачем она ему?), которая в ИКП-шном ДОК уехала на пару кб в сторону.
Но даже подправив этот косяк (а заодно и сравнив работу в старой версии Школьницы) я не смог добиться корректной сборки загружаемого файла. И над этим я ещё буду работать.
А вот компиляция программ в так называемый Перемещаемый формат (который LINKER ожидает на входе) выполняется ДОК успешно. И это весьма радует.
С другой стороны: ассемблер Громова, к сожалению (проверил версии 1.0 и 5.0), при указании параметра R, хотя и собирает Перемещаемый файл, но таблица меток там записывается ... не, не так. Вместо таблицы меток записывается мусор :((. И это весьма огорчает. Тем более исходников громовского ассемблера у меня нет.
Коротко о том, что представляет собой Перемещаемый файл:
это скомпилированный код программы (или библиотки), а также таблицы, в которых указано:
1) По каким смещениям находятся абсолютные адреса (например, аргумент команды JMP). Эти адреса линкер пересчитает при сборке окончательного проекта, подставив их фактические значения.
2) Какие адреса были неопределены и их следует подставить из другой библиотеки.
3) Какие адреса были известны и их следует использовать для настройки другой библиотеки.
Сам механизм импорта/экспорта адресов широчайше используется во всех современных ОС. Причем в двух видах: как для связывания кода на этапе компиляции, так и для связывания на этапе загрузки. Всем известные DLL- файлы - ни что иное, как сильно развившийся аналог R-файлов агата (хотя, конечно, сама эта идея старше как Агата так и всех современных ОС).
На данный момент мне удалось разобрать формат R-файлов, осталось выловить ошибки в LINKER'e.
Неожиданно подоспел очередной акт мерлезонского балета.
Разбирая код LINKER'а обнаружил забавное место:
403 LDA CODE,%N ;нoв.cмeщeниe
404 ADC OFFSETL,X
405 STA OFFSETL+1,X
406 LDA CODELEN+1
407 ADC OFFSETH,X
408 STA OFFSETH+1,X
Так текст выглядит в редакторе ДОК семёрки без расширенного знакогенератора.
Строчка 403 немного странная, не правда ли?
На самом деле ',%' - это мусор (причем символы с D7=0). Почему компилятор не давал на него синтаксической ошибки - не знаю. Из последующих строк видно, что должно быть: LDA CODELEN. К некоторой неудаче в тексте есть определения как CODELEN так и CODE, так что ошибки "метка неопределена" также не возникло.
Судя по всему, эта ошибка зело древняя. Генерируемый код находится по смещению $214 (от начала файла):
2A13- A5 20 .. "%?" LDA $20 <== тут должно быть LDA $DC
2A15- 7D 60 0C "щю?" ADC $0C60, X
2A18- 9D 61 0C "?а?" STA $0C61, X
2A1B- A5 DD .. "%]" LDA $DD
2A1D- 7D 78 0C "щь?" ADC $0C78, X
2A20- 9D 79 0C "?ы?" STA $0C79, X
Я не поленился и убедился, что неверный код есть даже в таких, вполне авторитетных, источниках, как "заводские" ИКП1 с маркировками Фг и указанной на этикетках контрольной суммой.
Будучи исправлен и пересобран, LINKER вполне заработал. К сожалению, сразу обнаружился один его существенный недостаток: у него не предусмотрена диагностика неопределённых меток (т.е. тех, которые объявлены в тексте как "внешние", но для которых не было найдено внешнее значение) при генерации финального (неперемещаемого - К или B) файлов. Ну просто нет такой ошибки.
Продолжим разбор...
(offtop: сегодня впервые в жизни держал в руках плату, изготовленную по собственному проекту. Удивительные ощущения. Производство платы и монтаж - всё промышленное, в Новосибирске заказывали. Около 20 тысяч р обошлись три экземпляра. Предсерийные экземпляры. Несколько USB-устройств + хаб. Вроде работает. К Агату отношения не имеет ... хотя, в жизни, всё ко всему имеет отношение, пусть и косвенное)
Продолжаем жрать вкусный кактус :)
Сегодня я снова обратил внимание на странные, хоть и нечастые косяки LINKER'а. Вроде бы случайные. Но сегодня он вовсе неслучайно зависал. Всего за пару часов удалось выяснить, что линкер ни при чем, при чем совсем другие явления, куда более высокого порядка.
Авторы ИКП-шной сборки ДОК опять что-то забыли. Но теперь не пару команд, а целый цикл инициализации.
На агате было полно всяких игротек и игрозапускалок. Они не очень сложные: прочитал какой нибудь файл в память и передал на него управление. Пусть даже посекторно. Чуть сложнее разработать то, что уже претендует на звание ДОС: это всё таки библиотека разных файловых процедур: чтение, запись, удаление, переименование. Да ещё чтобы командный процессор был. А вы в юности понимали как устроены синтаксические анализаторы комстроки ? Ну и высший пилотаж: это ещё и полноценный набор функций произвольного доступа к части файла.
Так вот единственная популярная ДОС, которая всё это кое как делает - это ДОС3.3. Она - самая сложная (пожалуй, сложнее только спрайт-ОС, ну и ONIX, хотя твёрдо не уверен). Про BTK сказать сложно, Школьница, хотя и может оперировать с фрагментами файлов, но фрагмент - только 1 байт, не больше. Всё остальное, что как-то читает файлы, как правило, может только прочитать весь файл за раз.
Чтобы оперировать с фрагментами файлов никак невозможно обойтись без отдельных операций открытия, чтения/записи и закрытия файла. Иначе дело станет совсем грустным (чтение/запись сами по себе требуют времени, а если ещё и каждый раз заного искать файл на диске - совсем до скорости программируемого калькулятора можно дойти.)
Но для того, чтобы операцию открытия файла как -то связать с последующей операцией чтения, нужен какой-то блок ОЗУ, в котором будет хранится информация об открытом файле.
Такие куски ещё называли когда-то давно FCB (file control block - например, в ранних версиях MS-DOS), потом стали называть по другому. Ну да неважно. Важно, что при старте ДОС эта память должна быть очищена. Ну то есть ДОС должна знать, что сейчас нет открытых файлов.
Вот.
У Школьницы регион FCB находится примерно в адресах $403..$4FF.
У канонично-семёрочного ДОК фрагмент инициализации, занимающийся очисткой FCB, начинается с адреса $2753, где уютно сидит примерно такой код:
275E - A9 07 .. ")." LDA #07
2760 - 8D 00 04 "..." STA 0400
2763 - A9 90 .. ")." LDA #90
2765 - 8D 01 04 "..." STA 0401
2768 - A9 7F .. ")ъ" LDA #7F
276A - 8D 02 04 "..." STA 0402
276D - A9 00 .. ")." LDA #00
276F - A2 07 .. ""." LDX #07
2771 - 9D B0 03 ".0." STA 03B0, X
2774 - CA .. .. "J" DEX
2775 - 10 FA .. ".З" BPL 2771
2777 - A2 03 .. ""." LDX #03
2779 - 9D 00 04 "..." STA 0400, X
277C - E8 .. .. "Х" INX
277D - D0 FA .. "PЗ" BNE
277F - EE B3 03 "Н3." INC 03B3
2782 - A9 43 .. ")c" LDA #43
2784 - 8D E7 1B ".Г." STA 1BE7
2787 - A9 27 .. ")." LDA #27
2789 - 8D E8 1B ".Х." STA 1BE8
278C - A9 21 .. ")." LDA #21
278E - 8D E4 1B ".Д." STA 1BE4
2791 - AE 00 0B "..." LDX 0B00
2794 - 8E 00 B0 "..0" STX B000
2797 - 20 37 0B "..." JSR 0B37
279A - B9 17 0F "9.." LDA 0F17, Y
279D - 20 4B B4 ".k4" JSR B44B
27A0 - A5 4B .. "%k" LDA 4B
27A2 - 8D B6 B4 ".64" STA B4B6
27A5 - 4C 00 1E "l.." JMP 1E00
Тут мы видим сброс всяких структур, в том числе $403..$4FF. Дальше идёт установка текущего дисковода и кой что ещё.
А что у нас в ИКП-7 ?
274F - A5 4D .. "%m" LDA 4D
2751 - 8D 33 20 ".Ё." STA 2033
2754 - 8D 25 20 "..." STA 2025
2757 - 20 64 1B ".д." JSR 1B64
275A - A5 4B .. "%k" LDA 4B
275C - 8D FF AF ".Ъ/" STA AFFF
275F - A9 07 .. ")." LDA #07
2761 - 8D 00 04 "..." STA 0400
2764 - A9 90 .. ")." LDA #90
2766 - 8D 01 04 "..." STA 0401
2769 - A9 7F .. ")ъ" LDA #7F
276B - 8D 02 04 "..." STA 0402
276E - A9 00 .. ")." LDA #00
2770 - 8D EE 04 ".Н." STA 04EE
2773 - AC B3 03 ",3." LDY 03B3
2776 - A2 07 .. ""." LDX #07
2778 - 9D B0 03 ".0." STA 03B0, X
277B - CA .. .. "J" DEX
277C - 10 FA .. ".З" BPL 2778
277E - 8C B3 03 ".3." STY 03B3
2781 - A9 3F .. ")." LDA #3F
2783 - 8D E7 1B ".Г." STA 1BE7
2786 - A9 27 .. ")." LDA #27
2788 - 8D E8 1B ".Х." STA 1BE8
278B - A9 21 .. ")." LDA #21
278D - 8D E4 1B ".Д." STA 1BE4
2790 - 4C 00 1E "l.." JMP 1E00
Похоже ?
Да. Но регион 403..4FF почистить забыли :(
А, не, не забыли:
276E - A9 00 .. ")." LDA #00
2770 - 8D EE 04 ".Н." STA 04EE
Эта парочка команд вообще-то чистит как раз таки первый FCB. Значит не забыли!
Но не подумали о том, что их там больше одного. На самом деле их MAXFILES - содержимое ячейки $400.
Вообще-то тут ещё нет установки B001 - там в ИКП номер дисковода, но это есть в другом месте:
3B69 - A5 55 .. "%u" LDA 55
3B6B - 99 A3 B3 ".#3" STA B3A3, Y
3B6E - C8 .. .. "H" INY
3B6F - 8C 01 B0 "..0" STY B001 <==
3B72 - 84 B4 .. ".4" STY B4
3B74 - 8C B3 03 ".3." STY 03B3
3B77 - C0 04 .. "@." CPY #04
3B79 - 90 1C .. ".." BCC 3B97
3B7B - 4C 37 3B "l.." JMP 3B37
это, конечно, не весь код инициализации. Важно другое - ну тут тоже нет очистки $403..$4FF. И вообще его нигде нет. Заполните этот регион любым мусором и после загрузки ДОК увидите его на месте. Я попал на это в ИКП-7 и позднее убедился в том же для ИКП-9.
В общем-то это всё влияет только на группу операций fopen/fclose (в системе имён Школьницы это OPEN/SHUTFILE), компилятор ассемблера, например, их не использует, равно как и текстовый редактор. Они работают сразу с LOAD/SAVE - чтение/запись файла целиком.
Ну ладно, не инициализировали регион, он забит мусором - значит ДОС на запрос открытия файла скажет "нет свободных буферов" (NOFRBUFS). Есть у неё такой код ошибки. Но это было бы слишком просто. И скучно.
Чтобы не было скучно, авторы LINKER'а подключают к ДОС свой обработчик ошибок. Но они забыли, что при возникновении ошибок не надо суетится и закрывать последний открытый файл. Потому что он может быть и не открыт, если ошибка произошла во время исполнения fopen. И вот в этот момент ДОС и пытается закрыть файл, который никто не открывал. Она бы не пыталась этого делать, если бы $403..$4FF был чистым.
Но там мусор, и снежный ком нарастает: в дескрипторе файла есть поле с номером дисковода, там мусор, драйвер дисковода не проверяет номер дисковода (какая незадача!), а так как драйвер двухстандартный (140+840кб), то неизвестный номер имеет неизвестный обработчик ..... всё заканчивается "программа выполнила недопустимую операцию......." BRK, короче. Хотя, конечно, как повезёт. Зависит от первоначального мусора.
А тут ftp://ftp.apple.asimov.net/pub/apple_II … Manual.pdf на 228 странице описание для ProDOS, с форматом словаря.
Два часа залипал :) СПАСИБО !
Накопал кой что новое для себя, притом весьма замороченное.
("low 8 bit of 16 bit value for an 8-bit field containing upper 8 bits, zero if $40 bit clear in RLD byte one. Or, if the $10 bit is set, than this is the ESD symbol number". И кое что ещё)
Да, формат совпадает, если я что-то не упустил. Только на 229-230 страницах описан байт не то sl не то s1: у него здесь упомянуто два бита (D3 и D4), в то время как ДОК использует почти все биты:
d7 - =d1 ?
d6 - эта переменная не используется (ENTRY считается использованием)
d5 - определена в коде (не EQU или DSECT)
d4 - импорт (вместо адреса в младшем байте будет номер в таблице импорта)
d3 - экспорт
d2 - ?
d1 - 0 - ok, 1 - метка не определена и не имеет EXTRN
d0 - 0 - 1 byte, 1 - 2 byte
Продолжаем жрать вкусный кактус :)
Да... Это просто песня.
Сразу вопросы возникают, как у разработчиков хранились исходники, если из побитых файлов собирались официальные ИКП, и как они тестировали софт, если даже в ДОС такие баги.
Я правильно понимаю, что в какой-то момент разработка системного софта была передана из Новосибирска в Москву?
Может, после передачи от одной команды разработчиков к другой все эти баги полезли?
И интересно про формат R. Вроде на Apple их было 2: один под DOS 3.3, другой под ProDOS. Вот тут кусочек описания для DOS 3.3, но без словаря символов http://apple2.org.za/gswv/a2zine/faqs/Csa2DOSMM.html
Byte Meaning
---- -------
$00-01 Original program load address
$02-03 File length (program image + relocation dictionary)
$04-05 Length of program image alone (not including relocation
dictionary)
$06-xx Program image
$xx-yy Relocation dictionary
А тут ftp://ftp.apple.asimov.net/pub/apple_II … Manual.pdf на 228 странице описание для ProDOS, с форматом словаря.
Что нибудь общее с Агатовским форматом есть?
Я правильно понимаю, что в какой-то момент разработка системного софта была передана из Новосибирска в Москву?
В Новосибе была команда Виктора Цикозы, он работал над Школьницей, сперва для эпла, но быстро перетащили её на появляющийся Агат. Вылизано было всё до блеска - я правда не знал об ошибках в ДОК, просто не сталкивался. Хотя, в отличие от многих агат-программистов, писал многое именно под ДОК либо под ДОК + Бейсик-60 (один исходник, в начале котого делал условную компиляцию блока системных адресов).
В Москве же пилили агат и тот софт, который шел с ним в комплекте (в общем-то всё, кроме Школьницы).
Дальше мои предположения.
Когда появился 840кб флоп и было желание разные разработки объединить на один диск, группа Цикозы сделала релиз для 840кб, но исходники в Москву не отдала (если бы отдала - мы бы где нибудь их уже раскопали). Скорее всего, им уже был неинтересен этот проект, они во всю пилили тогда Спрайт-ОС. Москва особо не пользовалась всевозможными расширенными фичами ДОК, в т.ч. П-файлами. Возможно потому, что основной их проект был ИКП-бейсик, который строился как дизасм эплсофта, а при дизасме было важно сохранить всевозможные входные точки. Так что как раз вся суть ассемблера, как автоматического калькулятора плавающих адресов, была не очень нужна. Отладчик ДОК для отладки Бейсика также был малоинтересен - он не слишком мощнее сисмона, если отпилить от него ДОС, компилятор и редактор. А если не отпилить, то будет драка за использование общих рессурсов (памяти, в первую очередь).
Ну это Москва=ЛЭМЗ (наверное, Кривцов, в первую очередь). А остальные московские разработчики вообще вряд ли имели какой-то выход на Цикозу.
Ну можно было письмо написать.... не знаю, чем бы это закончилось.
По сути, разработчиков высокого класса, которые бы могли задействовать линкер, было не так уж много.
Мне кажется, единственный человек, который мог бы взяться (не только по уровню знаний, но и по подходам и сфере интересов) за исследование/использования линкера - Александр Голов. Ему бы это было полезно при разработке BTK. Почему он этого не сделал - не знаю. Но можно спросить.
А тестирование софта - это ... как бы сказать ... не было тогда такой буквы. Ведь чтобы реально тестировать, нужен мощный штат тестировщиков, сответствующая должность, деньги, время, и проект тестирования. Возможно, разработка каких-то автоматических тестов. Но отношение руководства было примерно такое: "Компьютер ? зачем ? А, это чтобы управлять чем нибудь и считать. О! Причем считать по написанной программе, которую пользователь может написать сам. Ага. Понятно. А зачем нам тестировать программы, если пользователь пишет их сам?"
Т.е. как бы ПО не существовало как отдельного продукта. Вот бейсик, он кажется работает, вот программа же 2+2 может сложить. О чём говорить, если официально разработанное ПО не имело номеров версий ? Кто ставил номера версий на свои работы ? Голов (только на BTK, на ALV Super DOS, ALV Format не было), Цикоза (только на Школьницу в целом и отдельно на РАПИРу), Бадер (и то можно найти сборки MouseGraf с одинаковыми номерами версий, но различных по внутренностям), Лёвин (только на свои СУБД, на шахматах вроде не было). Всё. Я больше не помню. А, вроде ещё Самарцев (Сирин и Маркис).
Что нибудь общее с Агатовским форматом есть?
Очень похоже. Надо почитать ссылки, возможно, всё 1:1.
Маленько крыша едет, но раз уж начал и почти процентов 80 сделал, то стоит всё таки закончить.
Короче: грустно.
LINKER - замороченная прога, но бывает замороченность красивая и/или нужная, а бывает наоборот. Линкер - как раз "наоборот".
Что он делает ? Он читает группу файлов (чуть ли не до 30 штук можно), выдёргивает из них таблицы импорта и имён и затем начинает их тасовать.
Легко ли это? В общем-то не сложно. Если бы слабая определённость задачи: неизвестны ни размеры файлов, ни размеры таблиц (а они, как минимум, зависят как от числа переменных, так и от длин их имён).
Чтобы добится почти 100% универсальности, авторы линкера пошли наикрутейшим путём:
1) Все файлы читаются побайтно. Самое странное, что даже там, где нужно просто пропустить кусок, вместо того чтобы использовать POSITION (fseek), авторы пропускают байты вызовами RDBYTE (fgetc).
2) И потом начинается тусссссня: из каждого файла выдёргиваются таблицы, скучиваются в два массива, один из которых растёт вверх, а вот второй - вниз. И я как-то не заметил проверки на столкновение двух массивов :) Ну, на самом деле буфер довольно большой, так что вряд ли будут проблемы.
Как нетрудно понять, все эти операции требуют 16-битных указателей. Много указателей. Массивы указателей. Поэтому код забит всевозможной арифметикой. Умножение встречается редко, но разнообразные сложения/вычитания - ужас.
Я думаю, это тот случай, когда проще уже было бы написать всё на бейсике: массивы 16-битных там есть, логика программы была бы гораздо более очевидной.
А если сделать какую-то логику фрагментарного чтения (блок-за-обращение) - это бы уже легко компенсировало потери на работе интерпретатора. К сожалению, прочитать произвольный файл (а LINKER читает B, К, R/П-типы) по частям можно только накручивая над стандартными ДОСами свои надстройки, но оно бы того стоило.
Более того, если предположить, что файлы не такие уж здоровые (всё таки это уже не текст), то читая файл-за-обращение, можно было бы и логику не особо усложнять. Свободного ОЗУ в агате не мало, такой подход проиграл бы только в момент сборки какого-то огромного исполняемого файла, занимающего кб 40 (160 блоков).
Через это всё я предполагаю, что линкер не стал практически использоваться из-за крайней тормознутости. Я часто гоняю тестовый пример, состоящий из 4-х файлов, примерно по полблока (128 байт) каждый. Линкеру требуется где-то секунд 15 на их обработку (если не турбировать :). Что будет, если загнать в него несколько Кб кода и таблиц - не знаю. Но попробую.
Чтобы было понятно, в чём заморока с таблицами (откуда столько указателей и суеты) расскажу о формате П-файла.
Он состоит из 4-х частей: сперва идёт 6 байт заголовка, затем исполняемый код и две таблицы: RLD и STB.
1) Таблица реаллокации (RLD). Это список смещений в коде, в которых линкеру что-то нужно изменить.
2) Таблица имён (STB). Любые переменные, через которые код данного файла связан с другими П-файлами.
Как используются таблицы объясню на примерах:
ORG $2800
REL
ST
JMP ST
ST - метка. Она не импортируется (так как объявлена в этом файле). Но в команде JMP она должна быть пересчитана, так как точка загрузки файла заранее неизвестна. В П-файл на её место будет записано число $2800, а адрес ($2801) этого места попадёт в таблицу RLD.
Если линкер разместит код из этого файла по адресу $1000, из таблицы RLD он узнает, что значение по адресу $2801 нужно пересчитать как СТАРОЕ_ЗНАЧЕНИЕ - СТАРЫЙ_ORG + НОВЫЙ_ORG. В таблице STB об этом ничего не будет сказано, так как имя ST не экспортируется.
А вот если в текст будет добавлено "ENTRY ST", то в таблице STB появится запись о том, что существует такая переменная ST и её адрес $2800, но при этом будет стоять пометка, что адрес является перемещаемым и перед использованием в другом файле его сперва нужно пересчитать.
Если теперь команду JMP ST убрать из текста, это приведёт к удалению записи из таблицы RLD, в STB запись останется.
ORG $2800
REL
EXTRN ST
JMP ST
Здесь ST будет задано значение $0000. А таблицы будут заполнены так: в STB будет зарегистрировано имя ST с неизвестным адресом, а в RLD появится запись об адресе $2801, но будет стоять пометка, что адрес является импортируемым и ссылка на запись в таблице STB.
ORG $2800
REL
EXTRN ST
ENTRY ST
В этом примере получится пустой код, запись будет только в таблице STB о неизвестном имени ST, которое отмеченно - одновременно - как импортируемое и экспортируемое.
Это пока были обычные двухбайтовые указатели. Но можно сделать и однобайтовые:
ORG $2800
REL
EXTRN ST
DFB >ST
Получим запись в STB о неизвестном значении ST, а в RLD указатель $0000 со ссылкой на запись в STB, но с флажком, указывающим, что требуется только один младший байт.
Если DFB > заменить на DDB (сохранение значения в порядке старший-младший), оно будет сохранено также, но с отметкой о двух байтах, хранимых в прямом порядке.
ORG $2800
REL
ST:
DFB <ST
Этот пример, хотя и выглядит почти как предыдущий, на самом деле сложнее. Дело в том, что
в RLD указывается, что по смещению $0000 нужно пересчитать значение и сохранить его старший байт. Но чтобы пересчитать правильно, нужно знать и младший байт этого значения (ST). И в этой ситуации он сохраняется в RLD.
Свои особенности начинаются, если переменная объявлена через EQU либо как метка внутри DSECT..DEND. В этом случае она может стать как статичной (независящей от реаллокаций кода), так и динамической (и я пока не понял, в каких случаях это происходит).
К сожалению, следующий пример, хотя и компилируется без ошибок, работает не так, как бы хотелось:
ORG $2800
REL
EXTRN ST
AX EQU ST
JMP ST
Запись в STB о переменной ST появится, в RLD тоже будет ссылка на аргумент команды JMP, но AX будет иметь значение 0 и в таблицы не попадёт.
Тоже происходит, если, например, ST будет использовано внутри DSECT..DEND как аргумент ORG. Компилятор воспримет это как ORG 0 и все переменные внутри DSECT получат фиксированные адреса начиная от 0.
Это плохо, хотя, в "больших" ассемблерах для решения такой проблемы есть понятие именованного сегмента: например, если вы объявляете в любых частях программы сегмент с именем DATA и объявляете переменные разных частей в нём, то на выходе, задав фактический адрес этого сегмента (например, $E0), вы разместите все переменные подряд от адреса $E0 и выше (по порядку их нахождения в частях программы).
Как видно, между всего -то двумя таблицами могут устанавливаться разные связи и нельзя выделить какую-то одну таблицу как более важную или ведущую. Как RLD так и STB могут иметь связанные записи, но могут иметь и самостоятельные. Более того, разные записи RLD могут ссылаться на одну и ту же запись в STB.
Отдельной сложности добавляет то, что так как STB содержит имена переменных, размеры её записей не фиксированы и линкер вынужден пробегать все ячейки памяти таблицы не имея возможности расчитать положение очередной записи.
Всё это приводит к заметной сложности кода линкера.
Закончил разбор кода линкера, решил немного рассказать о его архитектуре и своих впечатлениях.
Прога вызывает неоднозначное впечатление.
С одной стороны - она, несомненно, писалась человеком вполне опытным: это видно не только по использованию всяких приёмов вроде SKIP-COMMAND (добавляем в код байт $2C и следующая двухбайтовая команда становится аргументом почти ничего не делающей команды BIT и, таким образом, как бы пропускается) или многовходовых (в том числе частично-рекурсивных!) подпрограмм:
PROC:
...
JSR PROC2
LDA #1
DFB $2C
PROC2:
LDA #2
...
RTS
Тут есть и почти корректное оперирование с ДОС Школьницы, кой где оптимизация памяти (некоторые рабочие ячейки используются под разными именами в нескольких блоках). Чувствуется, что прога написана не "в лоб", а как бы с неплохой локальной оптимизацией кода. Попадаются и довольно любопытные приёмы программирования, типа такого:
int enable_make_STB_file; // 0 - не надо, 1 - надо
fopen(filename);
{ Запись кода }
{ Запись RLD }
1:
{ Запись STB }
if (--enable_make_STB_file) { fclose(); return; }
fclose();
filename = 'X' + filename;
fopen(filename);
goto 1;
Процедура должна создать полный выходной файл, но если enable_make_STB_file == 1, то также создаётся отдельный файл меток, содержащий только STB и имеющий такое же имя с дополнительным символом 'X' в начале.
Механизм извращённый, конечно; додуматься до него начинающему было бы не просто.
С другой стороны: глобально линкер не красив. Представьте себе почти 1200 строк кода, содержащего 5 (и автор их явно выделил комментариями!) совершенно различных частей, написанных в виде одной большой процедуры. Эта дикая простыня временами прерывается вкраплениями мелких процедур-подпрограмм, которые основной текст "обтекает" JMP-ами. Тут, конечно, в использовании стека не накосячишь, но я посчитал количество команд RTS. Угадайте, сколько их ? Пять штук, Карл ! Ну да, процедур побольше, благодаря вложенности некоторых, близких по смыслу, но .... Это всё было бы отличной работой какого нибудь компилятора С, но не программиста, пишущего, хоть чуть-чуть расчитанный на дальнейшую поддержку или развитие, код.
Это первое. Второе: имена меток. В "больших" ассемблерах существует понятие локальной метки, оно есть даже в ассемблере Громова. Чтобы не придумывать какие-то замысловатые имена вы просто пишете что-то вроде "1:" - это объявление метки и затем ссылаетесь на неё как "1b" или "1a", что понимается компилятором как "метка 1 выше по тексту" или "метка 1 ниже по тексту". Так вы избавляетесь от необходимости изобретать уникальные имена для огромного количества меток, которые отстоят от команд переходов и ветвлений в пределах 5-15 строк (видимости на экране). Заодно в ссылке на метку сразу видно где её искать - выше по тексту или ниже. Подобрать имена для более "глобальных" меток гораздо проще, тем паче, что их гораздо меньше, нежели локальных.
Но так как ДОК не поддерживает локальные метки, можно придумать какую нибудь систему именований, например:
OPEN_NEXT_FILE:
...
ONF.LOOP:
...
BCC ONF.FIN
...
BNE ONF.LOOP
ONF.FIN:
RTS
Т.е. входная точка процедуры имеет максимально звучащее имя, а дальше все метки этой процедуры имеют имена в виде аббривеатуры, точки и уточнящего слова или просто числа.
Вместо этого в линкере метки идут вида: CC, APR, NOIX, RC... Поди, угадай, что L3 - метка инкремента счётчика цикла, после которого будет переход к следующему обрабатываемому элементу таблицы. А к чему относится метка ADDR ? Ага, в программе, которая предназначена для обработки адресов...
Итого:
; LINK : 08.03.88 (c) Группа В. Цикозы
: 13.02.18 (c) Ravodin & ...
В этом тексте исправлено/изменено следующее:
- испорченная строка "LDA CODE??N ;нoв.cмeщeниe";
- изменён адрес процедуры PUTTSL;
- добавил константу выравнивания таблицы меток при выводе на экран (MAXVARLEN=20, было фиксированное значение 9);
- добавлено много комментариев.
Нужно:
- сменить метки на более вменяемые;
- вспомнить про процедуру позиционирования по файлу;
- в обработчике ошибок запретить закрытие файлов, которые мы не открывали !
- поискать '?' и подумать, не следует ли там встроить дополнительные проверки и улучшения;
- подумать об оптимизации парсера сроки параметров и о разделении этого монстра на части;
- возможно, имеет смысл выделить какие-то общие операции, применяемые в каждом из этапов и забить их в процедуры;
- собрать все подпрограммы в кучу ? или наоборот ?
Нужно 2:
- дописать надстройку, которая позволит эффективно работать с файловой системой (поблочно, с произвольной адресацией);
- сделать ветку "STATIC-линкера": который бы читал все или часть файлов "за раз" в память и работал напрямую с большим буфером в ОЗУ.
Я специально вносил изменения только такие, которые не сместят команды. Это сделано для того, чтобы можно было потом двоично сравнить исходный бинарник и обновлённый.
То, что перечислено после слова "Нужно", я предоставляю желающим.
Хотя, если в дальнейшей эксплуатации линкера вылезут ещё какие-то ошибки, буду исправлять их сам.
Теперь про пять блоков:
C - парсинг командной строки
I - первый проход: зaгpузкa RLD и STB, oпpeделение нoвыx бaзoвыx aдpecoв
II - второй проход: нacтpoйкa пepeкpecтныx ccылoк, пocтpoeниe GLOBALS - пpooбpaз нoвoй STB
III - третий проход: запись выходного файла, также запись файла меток (если нужен) и вывод сортированной таблицы GLOBALS
L - вывод (с сортировкой по адресам) глобальной таблицы меток
Здесь есть слово "проход" - это из авторских комментариев и, IMHO, подобрано оно не вполне верно.
Обычно, под "проходом" понимается работа над одним и тем же массивом данных, здесь же:
- первый проход - это чтение файлов и "выкусывание" таблиц из файлов в память.
- второй проход - это анализ и обработка получившихся таблиц.
- третий проход - повторное чтение файлов и "выкусывание" из них кода. Код обрабатывается в соответствии с таблицами и записывается в выходной файл (автор называет его "объектным", но, насколько я помню терминологию, объектными как раз таки являются П-файлы - входные для линкера). А то, что синтезирует линкер - это уже "загружаемые" файлы.
PS И всё же - это код новосибирцев и группы Цикозы (а может и его самого?). Только они всё таки комментировали свои творения. Причем не только в начале файлов. Комментарии часто сокращены всевозможными способами, так что пока сам не поймешь кусок кода, комментарий мало что объясняет. Но уж когда поймешь - сразу становится понятно, что написано в комментарии :)) Значит правильно разобрался.
(Всё на сегодня, пойду пялится в футураму...)
Исправил найденные проблемы, линкер завёлся, в общем-то метки (входные точки) он вполне нормально понимает и обрабатывает.
Вылез мелкий косяк только:
ORG $20
REL
START:
LDA START
Здесь ассемблер сделает LDA с ZP-адресацией и ему без разницы, что, возможно, я затем захочу собрать этот фрагмент на адресе $4000. Ну ладно. Не бага, в общем-то. Скорее, изъян архитектуры транслятор-линкер.
Но дальше полезло другое: любую метку можно объявить через EQU или DSECT. С точки зрения ассемблера, это уже как бы константы, а не метки. Разница в том, что "PI10000 EQU 31415" не должна допускать никакой настройки линкером - как константа, она не зависит от смены адресов. И тут линкер поплыл по полной: он вообще игнорирует флаг EQU, который ему ставит ассемблер. И пытается пересчитывать константы.
Ну это я исправил.
А вот дальше - ещё хуже. Во внутренних структурах линкера, там где собирается глобальная таблица переменных, есть такая дурацкая схема: если старший байт адреса = 0, то вроде как переменная не найдена. И её значение остаётся нулем или чем-то ещё. Ну она может быть не найдена, проблем нет, но ведь может встретится переменная, у которой реально старший байт адреса = 0. Чем думали разработчики - не знаю, но факт: VT EQU $28 не импортируется в прогу, хотя успешно присутствует в таблицах экспорта библиотеки.
Будем ковырять дальше.
(что-то сдаётся мне, что этот линкер немного не был доведён до ума, от того и забыт всеми)
Вчера сходили с женой на концерт Би-2, сегодня покатал немного на веле (где-то км 20-25 всего), но давно не ездил за город, давно пора было. Вес растёт без нагрузок :)
И сегодня добил линкер. В аттаче образ, там пример его использования:
T 040 Koмпoнoвщик:pук.пpoгpaммиcтa
T 065 LINKER.TEKCT.2018A
T 063 LINKER.TEKCT.2018B
К 008 LINKER2018
T 064 LINKER.TEKCT.1988
К 009 LINKER1988
T 011 IOSUB
П 002 IOSUB.KOД1
T 004 HW
П 001 HW.KOД1
T 001 ST
К 001 TI
П 001 XTI
IOSUB - это как бы простенькая перемещаемая библиотека. Там функции (JMPы на функции) IOSUB ДОК и пара процедурок - вывод ASCIZ-строки средствами IOSub.
HW - это пример проги, использующей эту библиотеку.
IOSUB и HW собираются обычным ассемблером. Тип выходного файла не имеет значения, так как ассемблер в любом случае соберёт их как П. Если вы компилировали что нибудь ассемблером ДОК, вы не найдёте почти никаких отличий в исходнике, кроме того, что обязательна ORG с любым адресом, больше $FF и следующая за ней команда REL.
После компиляции библиотеки и программы нужно "выйти в отладчик".
Файл ST содержит вызов линкера, параметры вызова, а также нейтрализует косяки ДОК. Запускаем ST:
[EXEC ST
Увидите несколько команд, затем приглашение ком-строки - всё, финальная программа слинкована.
Линкером создаются два файла: TI и XTI. Первая - это исполняемый в ДОК файл:
[RUN TI
Прога запустится, пройдёт примерно такой диалог:
>[RUN TI
Тест ввода строки:
.VC202JZV
Эхо:
VC202JZV
Адрес начала этой
программы: 4000
>
Также создаётся файл XTI - это П-файл без кода, содержащий только таблицу STB - итоговый список экпортируемых меток. Я сделал его генерацию просто для примера. В дальнейшем добавлю в dos33c2 просмотрщик таких файлов. Будет выглядеть примерно так:
Load ptr = 4000
Import list (пока только безымянные, их нужно модифицировать согласно сдвига):
Names list (эти имена могут где-то пригодится и могут иметь значения):
[29] 406B [--] {--} exp CODE IOS_ENTERSTRING
[29] 406E [--] {--} exp CODE IOS_KEYIN
[29] 4071 [--] {--} exp CODE IOS_KEYIN_KBD
[29] 4074 [--] {--} exp CODE IOS_DIGOUT
[29] 4077 [--] {--} exp CODE IOS_BYTEOUT
[29] 407A [--] {--} exp CODE IOS_CR
[29] 407C [--] {--} exp CODE IOS_COUT
[29] 4082 [--] {--} exp CODE IOS_PRINT
[29] 40C7 [--] {--} exp CODE IOS_PRINT_PTR
[29] 407F [--] {--} exp CODE IOS_COUT_SCR
[08] __E8 [--] {--} exp EQU/D IOS_PTR
[09] 0200 [--] {--} exp EQU/D IOS_STRBUF
[08] __33 [--] {--} exp EQU/D IOS_PROMPT
[08] __24 [--] {--} exp EQU/D IOS_HT
[08] __25 [--] {--} exp EQU/D IOS_VT
Обратите внимание, что в исходнике HW нет ни одной константы, кроме выводимых строк и адреса в нулевой странице (IOS_PTR), который может использовать библиотека.
Пробуйте, расскажите, что получилось.
Страницы 1
Чтобы отправить ответ, вы должны войти или зарегистрироваться
ПЭВМ "Агат" 7-9: Форум → Прочие трансляторы → LINKER - Cбopщик пepeмeщaeмыx пpoгpaмм
Форум работает на PunBB, при поддержке Informer Technologies, Inc