Оберон-клуб «ВЄДАsoft» https://zx.oberon.org/forum/ |
|
Как создать новую библиотеку для ZXDev https://zx.oberon.org/forum/viewtopic.php?f=10&t=94 |
Страница 1 из 1 |
Автор: | Zorko [ 17 мар 2013, 20:30 ] | |||
Заголовок сообщения: | Как создать новую библиотеку для ZXDev | |||
Сперва — о том, зачем создавать библиотеки именно для ZXDev, ведь есть чудесный кросс-ассемблер SjASMPlus. Во-первых, для быстрого макетирования часто используют ZX-Basic, но это имело больше смысла при разработке на самом Спектруме, которая уже никогда не будет актуальной — мощные хост-платформы сверхдоступны. Но зато возрастает потребность в структурировании методов разработки, поэтому макетирование на Spectrum-Basic выглядит наивно. Нужно использовать другой язык, более подходящий для структуризации данных. В этом свете видится единственно возможное решение — язык высокого уровня с высоким качеством кодогенерации. В ZXDev это есть, и даже больше — это Си и Оберон-1 и 2. Во-вторых, нужен гибкий инструмент для разработки библиотек и эффективного их использования (в идеале с возможностью вызывать процедуры, работая как на языке высокого уровня, так и на ассемблере; чтобы в целевую программу включались только лишь реально использованные фрагменты кода; и, наконец, была бы гибкая конфигурация параметров библиотек). И всё это тоже есть в ZXDev. Вот какой механизм конфигурирования библиотек предлагается. Допустим, у нас есть графическая библиотека, и в ней процедура вывода точки по заданным координатам. При аналогичном случае использования данной процедуры рассмотрим 3 варианта, которые могут быть предусмотрены её конфигуратором:
2. Пишется очень большая игра, использующая всю память; необходим вывод точки максимально быстрый, но ради экономии памяти не использующий заранее рассчитанные таблицы данных. 3. Демка среднего размера, которой нужна максимальная скорость рисования точки. Ввиду наличия свободной памяти можно использовать таблицы данных для ускорения работы. Предлагаемый механизм конфигурирования библиотек основан на препроцессоре языка Си (но может использоваться и для разработки на Обероне), годится для всех трёх случаев и не требует перекомпиляции библиотеки (которая может быть довольно длительной по времени). Файл конфигурации помещается в папку с исходниками и выглядит так: Код: "C" /* Configuration file of the library Graph */ А вот как достигается эффект “без перекомпиляции”. В самом заголовочном файле: Код: "C" #ifdef PLOT_ROM В будущем хотелось бы иметь визуальный редактор свойств модулей и компонентов, подобный дельфийскому. Возможно, такой инструмент появится в среде XDev. Надеюсь, я вас убедил, что накопление библиотек в стиле, задаваемом XDev, имеет плюсы помимо простоты разработки на Обероне. Ведь массив библиотек может быть использован и для разработки на Си, и на ассемблере, и об этом читайте ниже. Приступаем к делу. Нам понадобится базовое знание ассемблера Z80. Адаптируем для ZXDev из YS MegaBasic V4.0 процедуру INVERT без параметров. Запустите MegaBasic и наберите INVERT. Она инвертирует весь экран Спектрума — меняет местами засвеченные и погасшие точки. Примем рабочую гипотезу, что где-то в процедуре должна быть загрузка в регистр начального адреса экранной памяти: #4000. В дебаггере эмулятора EmuZWin вызываем инструмент “Поиск”(Find). Будем искать два байта 00 40 (В Z80 первым хранится младший байт). Опытным взглядом сразу находим участок кода по адресу #C053: Код: "ASM" C053 ; --------------------------------------------------------------------------- Создадим такой Оберон-интерфейс: Код: "OBERON"
ZXDev/Lib/Obj/Mega.h ZXDev/Lib/Sym/Mega.sym Скопируем файлы ZXDev/Lib/Obj/Mega.h и ZXDev/Lib/Obj/Mega.c в ZXDev/Lib/C Это делается затем, что папка Obj содержит автоматически сгенерированные Ofront’ом сишные исходники, а у нас ручная работа. Т.е. нам от интерфейсного пустого модуля нужен лишь символьный файл, своеобразный биндинг между Обероном и Си/ассемблером. Отредактируем исходник ZXDev/Lib/C/Mega.c (для редактирования программ на Обероне, Си и PHP я часто использую редактор Syn Text Editor). Он примет такой вид: Код: "C" /* -------------------- */ ZXDev/Lib/C/Mega.h приведём в такой вид: Код: "C"
Откроем наш интерфейс ZXDev/Lib/Mod/Mega.odc и соберём библиотеку нажатием F12. Если ошибок нет, то консольное окно после трансляции исчезнет. Удобно, не надо заострять на нём лишний раз внимание. Напишем небольшой модуль для тестирования нашей процедуры INVERT: Код: "OBERON"
Вопросы, предложения.
|
Автор: | Zorko [ 18 май 2014, 05:39 ] |
Заголовок сообщения: | Re: DemoTiles.Mod |
Немного о структуре библиотек в ZXDev. Символьный файл модуля GrTiles хранится в ZXDev/Lib/Sym/GrTiles.sym, и посмотреть его содержимое напрямую нельзя, т.к. у него двоичный формат. Но можно с помощью XDev -> Show Definition. Интерфейсный модуль GrTiles хранится в файле ZXDev/Lib/Mod/GrTiles.Mod, но там нет ни строки реализации, только константы, типы и процедуры. Реализация этого модуля написана на встроенном в SDCC ассемблере и находится в файле ZXDev/Lib/C/GrTiles.c, который компилируется (по F11) скриптом ZXDev/Lib/Bin/compile.bat, если чёрненькое окошко мигнуло и исчезло, это значит, что ошибок нет. Иначе оно не исчезнет. Обратите внимание на "линию заголовка" и "линию разреза": /*--------------------------------- Cut here ---------------------------------*/ Это кастомная реализация смартлинковки, которая не предусмотрена во многих компиляторах Си. Самым разумным было бы добавить эту фичу во все сишные компиляторы, но мы-то этого сделать не можем. А такие компиляторы как, например, Turbo C уже вообще никто никогда не доработает. Поэтому остаётся лишь выходить из положения по-своему. Сишный файл режется на кусочки по "линиям разреза", а всё, что расположено до "линии заголовка", включается в каждый разрезанный кусок. Это позволяет упаковывать в библиотеку кусочки кода процедур фрагментами, поэтому прилинковываться к целевому коду будут только реально использованные процедуры. Логика сборки модуля (по F12) в библиотеку (обычно с таким разрезанием на кусочки) описывается скриптом ZXDev/Lib/Bin/build.bat, но, поскольку часто нужно учитывать какие-то специфические особенности (например, то, что модуль GrTiles находится в библиотеке XDev.lib), предусмотрена возможность помимо скрипта сборки по умолчанию использовать скрипт, адаптированный к особенностям сборки каждого модуля и имеющий более высокий приоритет. Такой скрипт нужно назвать одноименно с модулем и поместить в ZXDev/Lib/Obj (GrTiles.bat для GrTiles.Mod). |
Автор: | Reobne [ 19 июн 2014, 04:34 ] |
Заголовок сообщения: | Re: Как создать новую библиотеку для ZXDev |
Вопрос 1. Стоит ли на тратить время на написание таких обёрток? Изучаю ассемблер, и что-то не вижу, как вставить бинарник. Нашёл только директиву include /string/ Которая включает ассемблерный файл. Это получается, что ресурсный файл со спрайтами придётся готовить в виде ассемблерных текстов с кучей .db .dw ![]() И как передать имя файла в ассемлер из оберона? Хотя есть вариант завести фиктивную переменную, а потом её имя в предпроцессоре Си использовать как строку. Вопрос 2. В том ли направлении я копаю по подключению ресурсов? Так-же просмотрел ZXDev\Docu\sdccman.pdf пункт 3.14.2 и далее, там вроде как написано как стыковать сишные данные с ассемблерными вставками, но не впитал смысл с ходу. Много слов с двумя подчёркиваниями впереди, всё по неруски, и ассемблер для примера приводится не Z80, не так-то просто усвоить. А в свете обещания: Цитата: В следующих постах расскажу о процедурах с параметрами, там при сопряжении Си и Оберона есть тонкости. ,жду пока этого. ![]() |
Автор: | Zorko [ 20 июн 2014, 18:36 ] |
Заголовок сообщения: | Re: Как создать новую библиотеку для ZXDev |
Здорово, что есть интерес к этому направлению, значит — продолжаем. ![]() Reobne писал(а): Вопрос 1. Стоит ли на тратить время на написание таких обёрток? После такого замечательного достижения уже очевидно, что нет. ![]() А вот для описания интерфейсов Оберон подходит куда лучше, чем Си. Ведь программисту обычно нужен взгляд на модуль/библиотеку в целом, с большого. А не куча макросов вида #ifdef _Linux ..., характерных для сишных "интерфейсов", являющихся ничем иным как системной реализацией, причём в рамках Си-парадигмы — препроцессор: "это заменяется на это" вместо "функция" и т.п. Соль межмодульного взаимодействия Оберон-программ — это интерфейсы, регламентирующие обмен данными и вызов кода. Возможности по описанию Оберон-интерфейсов ограничены, но я не стал бы считать это большим недостатком — самый скромный по своим возможностям Оберон-интерфейс с лихвой затыкает за пояс традиционные .DLL Windows и .SO Linux, он гибче "ОО"-интерфейсов Java. Доказательный факт — легко прицепиться и к коду на Java и .NET (из GPCP), и к готовым DLL/.SO (из BlackBox или, например, XDS). Речь не о том, что Оберон-интерфейсы во всём "мощнее" сишных хидеров и могут поспособствовать всякому хитрому низкоуровневому трюкачеству. Речь о том, что они дают более адекватную, более высокоуровневую концепцию для описания межмодульных взаимодействий, к тому же и более компактную. Для XDev в схеме трансляции через Ofront нужно описать интерфейс, т.е. все константы, типы, переменные, записи, процедуры (можно без тела). Странслировав этот описатель, устроенный как обычный модуль, мы получим символьный файл .sym (в формате Ofront'а). Также в /Obj (или /Lib/Obj) будет создано два сишных файла "реализации" — заголовок .h и тело .c. Теперь у нас есть возможность писать ассемблерные вставки прямо в теле Оберон-модуля, но здесь есть ограничения, как я уже сказал. Такой пример: Оберон-типы: SHORTINT, INTEGER, LONGINT. Как оказалось, Ofront проверяет константные значения, передаваемые (и присваиваемые) этим типам. Поэтому на Обероне не получится передать типу SHORTINT значение > 127. А так хочется, ведь это байт. Поэтому я иду на такую хитрость: объявляю Оберон-параметр как INTEGER, а Си-параметр — как unsigned char: Код: "OBERON"
Код: "OBERON"
И писать реализацию на Си, только параметр будет уже unsigned char, т.е. однобайтовый. Но поскольку MAX(INTEGER) у нас 32767, то можно будет присваивать ей значения >127, что нам и требуется. Отсюда хитрости, когда тип Coords описывается как SHORTINT, а в параметрах используется INTEGER. Это не снижает качество кода. Это просто трюк, чтобы можно было описать переменные типа Coords как однобайтовые, но смочь передать в процедуру не только эти переменные (значение которых в рантайме конечно же не проверяется), но и числа-константы >127. Корявенько чуть-чуть, но это плата за отсутствие в Оберон-стандарте беззнаковых типов. Мы с Олегом (Saferoll) планировали поработать в этом направлении, да как-то жизнь взяла своё. ![]() Reobne писал(а): Вопрос 2. В том ли направлении я копаю по подключению ресурсов? Вопрос заслуживает отдельной темы. ![]() |
Автор: | Zorko [ 20 июн 2014, 20:33 ] |
Заголовок сообщения: | Re: Как создать новую библиотеку для ZXDev |
Заголовки процедур. __naked, фрейм Enter/Leave. В SDCC можно описывать процедуры с тегом __naked: Код: "C" /*--------------------------------- Cut here ---------------------------------*/ Код: "ASM" _AsmTest_Border_start:: Сишные же процедуры спокойно пишутся ручками, и с помощью __naked можно даже отказаться от генерации в конце функции команды RET. Кстати, эту возможность я обнаружил не так давно благодаря SfS и его библиотечке libspr. А вот ещё симпатичный трючок, который можно применить для эффективности. Имеем такое объявление процедуры: Код: "OBERON"
А вот сишная реализация, которая является фактически не вызовом процедуры, а присваиванием: Код: "C" #define Basic_FONT(fontAddr) (*(unsigned*) (0x5C36) = (fontAddr - 256)) Константы и их подстановка. Ofront всегда вычисляет и подставляет в тело транслируемой Си-программы константы. Это и хорошо, и (местами) плохо. Хорошо потому что быстрее транслирует сишный код. Плохо потому что менее наглядно и иногда такая короткая схема мешает более изощрённой логике работы с константой. Например, если константа в сишном хидере представляет собой сложное условие, зависящее от #ifdef'ов. Предлагаю трюк под названием “подстановка константы”: описываем константу в Оберон-интерфейсе как переменную: Код: "OBERON"
Передача строковых параметров. Ofront всегда имеет в виду длину строки и передаёт её процедурам вторым параметром после указателя на строку (то самое str и str__len). Длина всегда доступна с помощью встроенной функции LEN(str). И это незаменимо для того, чтобы никогда-никогда не выйти за границы памяти, отведённой под строку. Если передаётся указатель на строку, то он указывает на структуру, первый элемент которой — длина строки, дальше сама строка. Я рассматривал возможность для “маленьких” платформ (а также для взаимодействия с WinAPI) сделать какую-то поддержку работы с “опасными” строками в духе Си, но Йозеф Темпл (автор Ofront’а) не одобрил, сказал, что это шаг назад. ![]() Что ещё. Ага. Указатели. Они обычно присваиваются транслятором автоматически в NIL, что может вызвать дополнительные (скрытые) накладные расходы. Остаётся возможность работать с указателями через числовой тип подходящей разрядности и доступ к памяти с помощью Basic.POKE/PEEK или SYSTEM.PUT/GET. “Волшебный” переход адреса от MAX(ADDRESS) к MIN(ADDRESS), т.е. в случае двухбайтного слова — от 32767 (7FFFH) к -32768 (= 8000H == 32768) осуществляется безкровно, хотя обсуждаем вопрос: ловить ли прерыванием (или ASSERT’ом) такое варварство как недопустимое переполнение. Можно и ловить, но Си (и Оберон в варианте Ofront) не ловит, да и многие другие Оберон-реализации не ловят, а которые ловят (BlackBox), даже там эта возможность отключена по умолчанию. Так что имеем знаковый, но как бы беззнаковый адресный тип и им активно пользуемся. ![]() Не всё из задуманного реализовано, но я подошёл к такому устроению универсального батника для сборки библиотеки, опишу его логику. Итак, символьный файл, сишные заголовок и тело сгенерированы. Теперь скрипту надо решить, это Оберон-библиотека, т.е. транслировать ли автоматически сгенерированный Ofront’ом файл, или же альтернативную сишную реализацию написанного Оберон-интерфейса. Автоматически сгенерированные Ofront’ом хедеры/тела хранятся в /Lib/Obj, их сишные реализации — в /Lib/C Так что сперва проверяется, существует ли /Lib/C/Module.c, и если да, то считается, что в Lib/Obj находится “пустая” фиктивная реализация модуля, которая нам не нужна и порождена только как побочный результат генерации нужного нам символьного файла. Поэтому компилируется сишный файл /Lib/C/Module.c. В случае же отсутствия оного компилируется сгенерированный Ofront’ом /Lib/Obj/Module.c. Примерно так же может быть устроен и механизм подключения хидера (из какой папки его брать — /Lib/C/Module.h или /Lib/Obj/Module.h). Ещё надо заметить, что пока что есть смысл Оберон-модули с телом на Обероне компилировать по умолчанию без смартлинковки, потому что её пока можно применять только для простых модулей с процедурами. Если в модуле есть переменные или записи, методы, то разрезание уже не будет работать правильно. Дальнейшее совершенствование этого направления отложено на будущее. |
Страница 1 из 1 | Часовой пояс: UTC + 2 часа |
Powered by phpBB® Forum Software © phpBB Group https://www.phpbb.com/ |