|
nedoPC.orgElectronics hobbyists community established in 2002 |
|
DLL для Спринтера (LIBMAN)
Author |
Message |
Shaos
Admin
Joined: 08 Jan 2003 23:22 Posts: 22757 Location: Silicon Valley
|
http://nedopc.org/nedopc/sprinter/download/libman.zip (32K) | | | | Code: LIBMAN - Менеджер библиотек для компьютера Sprinter =================================================== Version 1.2 (August 1, 2002)
Модуль LIBMAN.A предназначен для использования динамически подгружаемых библиотек (DLL) формата L0 в пользовательских программах для компьютера Sprinter. При компиляции модуль занимает около 1К памяти и позволяет загружать, запускать и выгружать библиотеки. Единственное требование при компиляции - расположение кода модуля LIBMAN в области #4000-#BFFF.
Формат библиотеки L0: #00:(2) "L0" #02:(2) размер загружаемой части библиотеки #04:(2) смещение до таблицы (размер кода) #06:(2) размер таблицы #08:(2) контрольная сумма #0A:(1) день \ #0B:(1) месяц } - дата создания библиотеки #0C:(2) год / #0E:(2) версия библиотеки (1.0 = #0100) #10:(16) имя библиотеки (заканчивается нулем) #20: jp init ; подпрограмма инициализации #23: jp fini ; подпрограмма деинициализации #26: jp ... ... перемещающая таблица ...
Со смещения #10 код библиотеки является скомпрессированным. После 32-байтового заголовка идет таблица переходов на функции библиотеки. Количество функций никак не ограничивается. Нумеруются функции с 0. Первые две функции имеют особое назначение - функция 0 для инициализации (вызывается при загрузке) и функция 1 для деинициализации (вызывается при закрытии) - и не предназначены для явного вызова из пользовательской задачи. Кроме того функция 0 должна устанавливать регистр A в 0 и обнулять флаг C (проще всего это сделать с помощью команды XOR A). При вызове этой функции через регистр А ей будет передан идентификатор открытого файла библиотеки для того, чтобы библиотека, при необходимости, могла загрузить из файла дополнительные данные (менеджер загружает столько байт, сколько указано в поле #02). В случае неудачи, функция 0 должна взвести флаг C и указать в регистре A код ошибки - в этом случае менеджер выгрузит библиотеку и вернет код ошибки.
Количество одновременно загруженных библиотек в этой версии - 64. Библиотеки подгружаются по имени файла с помощью подпрограммы L_LOAD. Перед вызовом требуется в регистровой паре HL установить адрес имени файла библиотеки, а в регистре A - требуемую страницу памяти:
ld hl,filename ld a,wind ; 1,2,3 call l_load jp c,error ld (handle),hl ... handle dw 0
Здесь, filename - адрес имени файла, заканчивающегося нулем; wind - номер окна, в котором желательно исполнять подгруженный код (для 1: #4000-#7FFF, для 2: #8000-#BFFF, для 3: #C000-#FFFF); handle - двухбайтовая переменная для хранения идентификатора библиотеки для дальнейшего использования; в случае ошибки функция возвращает флаг переноса. При загрузке код библиотек переносится в памяти так, чтобы расположиться максимально компактно. После успешной загрузки и привязки кода библиотеки к конкретным адресам, производится запуск функции 0 библиотеки. Размер одной библиотеки не может превышать 16К.
Информацию о загруженной библиотеке (32 байта заголовка) можно получить с помощью подпрограммы L_INFO. Идентификатор передается через регистровую пару HL. Адрес 32-байтного буфера - через регистровую пару DE:
ld hl,(handle) ld de,buffer32 call l_info jp c,error
Если ошибки не произошло, то в буфере будет располагаться 32-байтовый заголовок библиотеки (см.выше) из которого можно получить информацию о версии спецификации, размере файла библиотеки, размере кода, дате создания, версии библиотеки и полном имени библиотеки.
Вызов функций библиотеки производится с помощью подпрограммы L_CALL. Эта подпрограмма самостоятельно подключает необходимый код в нужное окно (программист должен корректно указать его при загрузке библиотеки, чтобы не закрыть открываемой страницей кода менеджера, данных, стека), передает управление на код соответствующей функции библиотеки, а затем возвращает обратно ту страницу, которая была там до вызова функции библиотеки. Перед вызовом в HL устанавливается идентификатор библиотеки, а в регистре B - функция библиотеки, которую нужно вызвать. Регистр C - зарезервирован на будущее:
ld hl,(handle) ld b,function call l_call jp c,error
Об ошибке свидетельствует взведеный флаг переноса. Данные можно передавать и получать в регистрах A,DE,IX,IY, а также через второй набор регистров.
Для закрытия библиотеки и особождения захваченной памяти используется подпрограмма L_FREE, которой нужно передать идентификатор библиотеки:
ld hl,(handle) call l_free jp c,error
Ошибка возвращается во флаге переноса. Перед освобождением памяти, подпрограмма производит запуск функции 1 выгружаемой библиотеки.
Для идентификации загруженной библиотеки используется двухбайтовое число специально для будущих реализаций (возможность использования одной и той же библиотеки разными процессами, которым будут выданы разные идентификаторы этой библиотеки).
Для создания библиотек можно использовать программу MK_DLL.CPP (MK_DLL.EXE откомпилирован для консоли Windows). Пример простой библиотеки есть в LIB.A. А в TEST.A можно посмотреть как эта библиотека используется. Файлы TEST.EXE и TEST.DLL являются бинарными файлами для компьютера Sprinter и соответствуют исходным текстам TEST.A и LIB.A. Менеджер библиотек LIBMAN.A подключен внутри исходника TEST.A. Пакетный файл ZM.BAT производит сборку тестового примера на персональном компьютере под управлением Windows и при наличии кросс-ассемблера ZMAC.
Copyright (c) 2002, Александр Шабаршин (shaos@mail.ru)
http://shaos.ru/nedopc/sprinter/
Благодарности (в алфавитном порядке): AcidRain/PowerAmiga^mOOds Anton Enin Garry Lancaster PetersPlus Ltd. Stels/Myth corp. Vasil Ivanov
| | | | |
P.S. Что-то похоже в какой-то момент у меня потерялся вот этот функционал при вызове функции 0: Надо будет исправить т.к. наличие библиотек, подгружающих свой хвост, который длиннее отведённых библиотеке 16кб, было бы очень неплохой фичей...
|
13 Dec 2020 00:21 |
|
|
Shaos
Admin
Joined: 08 Jan 2003 23:22 Posts: 22757 Location: Silicon Valley
|
В сентябре 20004 года Василий Иванов сделал свой обновлённый вариант LIBMAN v1.3: http://www.nedopc.org/forum/viewtopic.php?p=68967#p68967 | | | | Vasil Ivanov wrote: Вчера немного поправил твой LIBMAN для DLL-ек. Теперь он нормально работает с библами, у которых таблица перемещений идет сразу за "своим" кодом (т.е. в начале рел-таблицы не требуется 4 нулевых байта (32 бита, для прохода dll-заголовка)). Этот формат обозвал как "L1". Его будет (уже выдает) выдавать линкер от SOLID C. LIBMAN теперь версии 1.3. Надеюсь ты не против? ... Фактически все изменения LIBMAN-а - это прибавка 32 байт (размер заголовка) к началу корректируемого кода, для формата L1. Вот и все. Да еще малька соптимизил код менеджера - сейчас он на 48 байт короче оригинала. Грузил им твою тестовую библиотечку (упакованную, не упакованную), созданную mk_dll.exe и эту же библиотеку, скомпиленную моим линкером. Все грузилось без проблем. | | | | |
http://nedopc.org/nedopc/sprinter/download/libman13.zip (171K) Причём линковка DLL была уже поддержана линкером его Solid C: | | | | Code: Создание DLL-библиотек
Для создания динамических библиотек служит опция "t". Обратите внимание, что эта опция отменяет вставку exe-заголовка. Отслеживается превышение максимального размера dll-библиотеки в 16 kB. В этом случае линкер выдает предупреждающее сообщение, но линковка библи- отеки не прерывается.
Создаваемые dll-библиотеки имеют сигнатуру заголовка "L1". Библиотека этого формата отличается от формата "L0" только тем, что таблица перемещений начи- нается сразу за концом "своего" кода. Заголовок dll-библиотеки создается и прилинковывается автоматически, поэто- му не надо резервировать для него место при написании ассемблерных текстов. Более подробную информацию по dll-библиотекам смотрите в документации менед- жера dll-библиотек.
Пример строки вызова линкера:
LD /TSymple library:0001 test.rel
где: "Symple library" - Строка внутреннего описания библиотеки. Строка описания может содержать пробелы. Длина описания не должна превышать 16 символов, иначе линкер выдаст ошибку.
"0001" - Внутренний номер версии библиотеки. Может содержать символы 0..9 и A..F (a..f).
"test.rel" - объектный файл, служащий для создания библиотеки.
Можно не указывать один из параметров (или все) опции, но символ ":" разде- лителя параметров должен стоять всегда. В случае отсутствия описания - в заголовке библиотеки будет стоять пустая строка. В случае отсутствия номера версии - в заголовке будет стоять нулевой номер. Примеры вызова опции:
LD /Tsymple library: test.rel LD /tМоя библиотека: test LD /T:010B test.rel LD /t:7 test LD /t: test.rel
Обратите внимание, что строка внутреннего описания библиотеки начинается сразу же за буквой "t" опции. Например, если написать
LD /T Example:0001 test.rel
то строка описания в заголовке библиотеки будет иметь вид " Example", т.е. начинаться с пробела.
| | | | |
В ноябре 2004 Василий выкатил апдейт для Solid C с поддержкой загрузки нового формата DLL: http://nedopc.org/nedopc/sprinter/download/sc_dll.zip (18K) Подробнее про сишный вызов DLL было в форуме тут: http://www.nedopc.org/forum/viewtopic.php?p=70452#p70452
|
13 Dec 2020 00:25 |
|
|
Shaos
Admin
Joined: 08 Jan 2003 23:22 Posts: 22757 Location: Silicon Valley
|
Вызов функции DLL может быть и выглядит несколько длинноватым (см. ниже), однако например если мы хотим поработать с плавающей точкой или сделать заливку полигона или GIF скажем подекодить на экран, вызвав соответствующую функцию уже загруженной DLL либы, то время вызова этой функции DLL будет пренебрежимо мало по сравнению со временем выполнения этой самой функции DLL (это 490 тактов до передачи управления в функцию и ещё 154 такта после - т.е. всего 644 такта оверхеда, что есть 92 мкс при 7 МГц процыке): | | | | Code: ; <><><><><><><><><><><><><><><><><> ; user data in a,de,ix,iy ; ld hl,(handle) ; ld b,function ; call l_call ; jp c,error
_L_CALL: push hl push de push bc push af xor a ld (lcflag),a ld a,b ld (lc_fun),a ld a,l rla rla ld d,0 ld e,a ld hl,LIBR add hl,de ld a,(hl) or a jp z,lc_er2 inc hl ld a,(hl) push af inc hl ld h,(hl) ld l,#20 ld (lcstart),hl ld a,h and #C0 ld (lcoldp),a cp #40 jr nz,lc1 in a,(#A2) jr lc3 lc1: cp #80 jr nz,lc2 in a,(#C2) jr lc3 lc2: cp #C0 jr nz,lc_er3 in a,(#E2) lc3: ld (lcoldw),a pop af push de ld bc,#0038 ; SETWIN rst 10h pop de pop af pop bc ld hl,(lcstart) ld d,0 ld e,b add hl,de add hl,de add hl,de pop de ld bc,lc_ push bc jp (hl) ; CALL FUNC <<<<<<<<<<<<<<<<<<<<<<<<<<<< lc_: pop hl ; restore handle ld c,a ; save A to C jr nc,lc4_ ld a,(lc_fun) or a ; test for FUN0 jr nz,lc4_ ld a,#FF ld (lcflag),a lc4_: ld a,(lcoldw) ld b,a ld a,(lcoldp) cp #40 jr nz,lc1_ ld a,b out (#A2),a jr lc3_ lc1_: cp #80 jr nz,lc2_ ld a,b out (#C2),a jr lc3_ lc2_: cp #C0 jr nz,lc_er0 ld a,b out (#E2),a lc3_: ld a,(lcflag) rlca ; set c-flag ld a,c ; restore A ret lc_er3: pop af lc_er2: pop af pop bc pop de lc_er1: pop hl lc_er0: scf ret
| | | | |
Однако если у нас будут либы поменьше с более лёгкими функциями (типа 32-битная арифметика), то можно придумать способ работы с ними, который был бы чуток побыстрее. Например, вместо можно вызвать другую функцию (которую ещё надо написать) скажем l_freeze: которая будет оставлять окно с либой подключенным, а в регистровой паре DE вернёт указатель на ряд JMP-ов, который пользователь может использовать далее для непосредственного вызова пользовательских функций библиотеки используя формулу frozen+N*3 для вызова функции N, причём функции 0 и 1 не должны вызываться таким способом т.к. это функции INIT и FINIT, вызываемые только самим менеджером. После того как непосредственная работа с библиотекой закончена, программа может разморозить либу с помощью функции l_unfreeze (которой также пока нету): После этого либа остаётся загруженной, но спрятанной, и как и раньше будет доступна через вызов l_call. Если программа до вызова l_unfreeze вызвала l_freeze для другой либы, то l_unfreeze для предыдущей либы выполняется автоматически. P.S. Чтобы исключить возможность ошибочного вызова пользователем функций 0 или 1 можно frozen сразу "отмотать" на запрошенную функцию, передав скажем в регистре B номер первой желаемой функции, например 2 (первая пользовательская функция), тогда DE будет сразу указывать на JMP FUNCTION2, хотя это всё может ещё более запутать программиста... P.P.S. Также в будущем можно поддержать "умные" либы, которые не надо грузить каждый раз когда вызывается l_load - скажем если первым символом в названии такой либы будет $, то если она уже была загружена в нужное окно, то грузить её снова ненадо - просто выдаём обратно тот же идентификатор либы (возможно с инкрементом старшей части хэндлера чтобы отличить инстансы друг от друга), но для работы с данными, уникальными для каждой программы-пользователя, у таких либ должен быть иной механизм, например через функцию 2 каждая программа будет давать ей указатель на буфер, с которым надо работать и либа будет этот буфер запоминать для данного пользователя. Такой функционал потребуется тогда, когда LIBMAN переедет в DSS либо будет висеть резидентом и откликаться на RST #20 (тогда и регистр C пригодится) - в этом случае максимальное количество одновременно подгруженных DLL-ей можно увеличить с 64 до 256, что потребует буфера для хранения информации о библиотеках в 1024 байта.
|
13 Dec 2020 02:21 |
|
|
Sayman
Maniac
Joined: 05 Oct 2009 19:44 Posts: 223 Location: 212.164.105.5
|
первую половину функции нужно явно переделать. таблицу переделать в 512 байт (младший, старший байты адреса) с процедурой декодирования аля dss. + к этому размещение только в 0м окне, без необходимости вычислять окно. всё это ускорит вызов раза в 2. сейчас RST #20 и RST#28 свободны (вплоть до адреса 0x002f). 16 байт для размещения "переключалки" 0го окна туда и сюда более чем должно хватить. можно уже сейчас эксперементировать и тестировать функционал библиотек в текущей версии доса. например, программа libman.exe установит диспетчер на рст 0х20, запросит у доса страничку для размещения своего функционала и страничку для размещения либ. solid c я не пользуюсь, оно не интересно. нет ansi и нет 32 бита. и ещё момент, что-то запамятовал я, а текущая реализация либмана поддерживает погрузку сразу нескольких либ с возможностью вызова нужной функции нужной либы? а то во всех примерах всегда одна либа грузится. если либман не умеет работать с кучами, то тоже нужно дорабатывать. в либмана я сам не полезу...
|
13 Dec 2020 21:51 |
|
|
Shaos
Admin
Joined: 08 Jan 2003 23:22 Posts: 22757 Location: Silicon Valley
|
не - таблица SIZE/8 перемещает только с шагом 256 байт (учитывается только старший байт всех адресов) - насколько я помню MSX-DOS себя так перемещал в верхнюю память - это общепринятый метод или ты про какую таблицу? таблица релокации используется только при загрузке если речь идёт про вызовы функций, то их обычно несколько - зачем отводить под них 512 байт если в либе скажем только 3 точки входа? окно 0 не подходит т.к. для прерывания должен стоять хук и обработчик для биоса, потом либа сама может вызывать функции DSS - поэтому 0 окно у меня используется только для первичной загрузки естественно поддерживается несколько либ - в этой версии либмана до 64 либ одновременно а Solid C таки можно считать стандартом де факто - других сей на Спринтере нету
|
13 Dec 2020 23:06 |
|
|
Sayman
Maniac
Joined: 05 Oct 2009 19:44 Posts: 223 Location: 212.164.105.5
|
ну тогда на Спринтере нету сей. солидом мало что можно сделать. за всё время так никто ничего не не сделал. прерывания на момент переключения можно запретить со стороны либмана, на случай стоковых прерываний первой же командаой DI либо, на +1 байт смещение, если пользователь имеет свой обработчик и прерывания запрещать не требуется. да и с 0й страницей по части вызова доса тоже можно придумать вариант. раскидывать либы по окнам в условиях, когда и так окошек мало, как бы расточительно. может случится так что все 4 окна уйдут под либы. опять таки, ты заставляешь "юзера" шарить, какая либа в каком окне, изворачиваться. а если все либы в 0м окне, т.е. в одном месте, это сильно снизит требования по входу для юзера.
|
14 Dec 2020 01:53 |
|
|
Shaos
Admin
Joined: 08 Jan 2003 23:22 Posts: 22757 Location: Silicon Valley
|
либа в окно подключается только тогда, когда её используют т.е. варианта когда во всех окнах либы - не будет и юзер не будет "шарить" по окнам - он сам желаемое окно задал, когда либу грузил - соответственно он должен понимать, что делает
P.S. LIBMAN 1.2 был четвёртой итерацией менеджера (версия от Василия - уже пятая итерация) и на каждой итерации функционал обсуждался на форуме петерсов и с самими петерсами (например возможность подключения либы в любое окно кроме нулевого специально запрашивалась, насколько я помню) - никаких лишних усложнений небыло - всё обговорено и обсуждено так что я буду цеплять то что есть на RST #20 и никаких фундаментальных переделок делать небуду - все старые либы должны работать как есть...
|
14 Dec 2020 02:59 |
|
|
Shaos
Admin
Joined: 08 Jan 2003 23:22 Posts: 22757 Location: Silicon Valley
|
Это твое личное мнение - каждый имеет право на своё лично мнение и моё личное мнение отличается от твоего
|
14 Dec 2020 03:12 |
|
|
Sayman
Maniac
Joined: 05 Oct 2009 19:44 Posts: 223 Location: 212.164.105.5
|
Мнение, то мнение, только вот попробуй написать на этом солиде что-то толковое. можно взять и попробовать мой старый фдиск на нём собрать. или попробовать написать что-то графическое. гляделку бмп файлов (ну просто так, чтобы проверить). сразу упрёшься в ограничения этого компилятора. т.е. практического применения не видно для него. даже банально парсер ini файлов мой древний тут не взлетит. переделать может и можно, но проще уже под асм переложить будет. если так хочется нативный си компилятор, то можно как то декомпилить хайтех си и адаптировать под спринтер. или взять исходники BDS C и допилить до уровня ANSI.
|
14 Dec 2020 03:40 |
|
|
Shaos
Admin
Joined: 08 Jan 2003 23:22 Posts: 22757 Location: Silicon Valley
|
ну адаптируй если очень хочется но труд других людей не надо приуменьшать, а тем более словами разбрасываться конфликтными...
|
14 Dec 2020 03:58 |
|
|
Sayman
Maniac
Joined: 05 Oct 2009 19:44 Posts: 223 Location: 212.164.105.5
|
в общем проехали тему сей. про либмана. законным способом, ничего не ломая прямо сейчас, в рамках 1.62.хх можно доработать system.exe, чтобы функция либмана заехала в него. тогда, при запуске шелла, будет инициализирована точка входа рст20, будет корректно работать всё остальное и либы в том числе. при этом странички занятые либами освобождаться не будут т.к.. первичный шел не закрываем. при попытке закрыться он будет перезапускать себя по новой. таким образом даже если юзер по ошибке сделает exit в первичном шелле и вся его память, включая либы будет освобождена, то он сам всё перезапросит и переставит. законного, в рамках 1.62 способа не существует. остальные варианты обсужденные в группе - костыльные.
при этом, при повторном запуске ещё одного шелла, он должен увидеть, что уже либман был ранее инициализирован и не станет переустанавливать свой функционал.
|
15 Dec 2020 00:58 |
|
|
Shaos
Admin
Joined: 08 Jan 2003 23:22 Posts: 22757 Location: Silicon Valley
|
согласен
вопрос какой system.exe взять за основу? по идее в официальном есть комментарии и вменяемые названия для меток
с другой стороны в версии Василия добавлена достаточно продвинутая история и исходник порезан на тематические подсистемы
может как-то можно совместить это всё и сделать единый суперисходник?...
|
15 Dec 2020 01:19 |
|
|
Sayman
Maniac
Joined: 05 Oct 2009 19:44 Posts: 223 Location: 212.164.105.5
|
можно, разрешаю
|
15 Dec 2020 01:47 |
|
|
Shaos
Admin
Joined: 08 Jan 2003 23:22 Posts: 22757 Location: Silicon Valley
|
ну я тогда по ходу пьесы воткну туда copy и move (пока тупой с копированием и удалением) и сделаю алиасы ls, cp и mv прогресс будет в топике про шелл с памятью: http://www.nedopc.org/forum/viewtopic.php?f=60&t=8473(но всё что касается либмана будет тут)
|
15 Dec 2020 02:42 |
|
|
Shaos
Admin
Joined: 08 Jan 2003 23:22 Posts: 22757 Location: Silicon Valley
|
Итак, шелл стартует как приложение, работающее в странице 2 (#8000...#BF00), однако при работе с либами там может находиться программа пользователя их вызывающая или сама вызываемая либа т.к. она может запускаться в одном из трёх окон 1,2,3 (нулевое окно отпадает, т.к. либы могут пользоваться точками входа DSS и BIOS, а также предполагать, что у нас работают прерывания). Пользователь при загрузке либы сам устанавливает в каком окне он её ожидает видеть, соответственно там не должно быть его стека и данных, на которые могут указывать аргументы, однако сама точка вызова RST #20 в программе пользователя может находиться в том же окне, куда потом откроется либа, потому что скорее всего либман будет пускаться из нулевого окна (временно запретив прерывания и перещёлкнув нулевое окно с DSS на страницу шелла и вернув всё обратно после отработки функции) и возврат также будет делаться через нулевое окно через код прихаканный в DSS. Однако, загружающий код в функции либмана L_LOAD активно использует функции DSS - это значит, что в нулевое окно этот код вставлять нельзя и у либмана должна быть ещё ретранслируемая часть, которая будет оттранслирована для всех трёх окон 1,2,3 и включена в нужное окно при загрузке и выгрузке очередной либы (например как часть буфера). Вызовы L_CALL, L_INFO и L_FREE можно полностью реализовать только через вызов функций биоса (повторив реализацию DSS-ных SETWIN[n] и FREEMEM в коде шелла), соответственно их можно вызывать в нулевом окне убрав DSS в тень.
|
15 Dec 2020 23:50 |
|
|
Who is online |
Users browsing this forum: No registered users and 2 guests |
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot post attachments in this forum
|
|