Журнал о программированнии на языках Blitz3D, BlitzPlus, BlitzMax

BlitzMax + fasm

Материал из Blitz Et Cetera

Перейти к: навигация, поиск

Делать ассемблерные вставки в код Blitzmax не позволяет. Но это еще не значит, что нельзя написать код на ассемблере для Blitzmax. Дело в том, что сам Blitzmax использует для компиляции Fasm. А это значит что нам надо просто определенным образом скомпилировать модуль для Blitzmax на ассемблере и просто подключить его для использования в программе.

Но это еще не все. Дело в том, что модули компилируются в формате COFF, поэтому мы теоретически можем использовать для написания модулей под Blitzmax ЛЮБОЙ язык, который компилирует в COFF. Просто нужно соблюдать некоторые правила при написании модулей. Я например, подумываю про язык D. Но неизвестно чем это подумывание закончится.

И сразу про недостаток: теряется мультиплатформенность, т.к. для MacOS используется другой компилятор, хотя теоретически возможно что для интеловских версий MacOS-а и подойдет. Но этот вопрос я не исследовал вообще. Ну нету у меня Мака.

Итак, я предполагаю, что и Fasm и Blitzmax уже установлены и настроены и даже работают с полпинка.

Для примера сделаем простую программку, которая переводит градусы в радианы. Не думаю что это очень актуально, но кто знает. И оговорочка: делать будем только релизную версию, дебаг посложнее да и я в ней не разбирался.

Начинаем оформлять код:

format MS COFF ; - задаем формат компиляции. Fasm такой проказник - много форматов знает и умеет.
extrn   ___bb_blitz_blitz ;- так надо!

а вообще это объявление внешней функции Blitzmax для инициализации модуля. Я пробовал ее и не вызывать - все работает. Но надо конечно проверить на крупной программе, а не на примерчиках.

Теперь пришла пора придумать название модуля. Долго думать я не стал и решил назвать его совсем неоригинально, но понятно - degtorad. По этому поводу надо объявить процедуру инициализации модуля:

public  ___bb_degtorad_degtorad

Как видно, название внутри асм-кода имеет свой строгий формат, который, думаю, ясен из строки.

public  _DegToRad ; а это объявление нашей функции. Перед именем обязательно надо поставить подчеркивание!

На этом оформление закончено, пора писать сам код.

Для начала надо проинициализировать модуль, написав функцию инициализации:

___bb_degtorad_degtorad. Ставим директиву начала кода:
 section "code" code.

И пишем код инициализации.

___bb_degtorad_degtorad:

        push    ebp
        mov     ebp,esp
        cmp     dword[_finit],0
        je      _init
        mov     eax,0
        mov     esp,ebp
        pop     ebp
        ret
 _init:
        mov     dword[_finit],1
        call    ___bb_blitz_blitz
        mov     eax,0
        mov     esp,ebp
        pop     ebp

        ret

Кратко что он делает: мы проверяем флаг инициализации _finit. И если инициализации еще не было, то мы ее делаем (код после метки _init). Это нужно для внутренних переменных модуля или глобальных переменных. Конкретно здесь их нет, поэтому просто ставиться флаг что инициализация уже была и вызывается ___bb_blitz_blitz. Для чего - не знаю. Возможно, для какой-то дополнительной внутренней регистрации модулей. Как уже писал, без ее вызова все работает, но лучше его делать, т.к. на больших проектах не тестировалось.

Теперь пишем основной код нашей функции:

_DegToRad:

        push    ebp
        mov     ebp,esp
        push    1016003125
        fld     dword[ebp+8]
        fmul    dword[esp]
        fwait
        add     esp,4
        mov     esp,ebp
        pop     ebp

        ret

Описание: сначала перебрасываем стек на ebp для более удобной работы с аргументами функции. Пихаем в стек число 1016003125. Это представление в формате float числа 0.0174532925199432957692369076848861, которое является коэффициетном преобразования градусов в радианы. Загружаем в ФПУ аргумент функции и перемножаем его с коэффициентом. И все. Дальше просто ждем результата от ФПУ, восстанавливаем стек и возвращаемся. Результирующее значение будет там где ему положено быть. По идее в eax, не помню ни куда ФПУ от этой команды результат возвращает, ни как принимает результаты Blitzmax (или через eax или через стек). Этож больше полугода назад было.

Теперь остается самая малость манипуляций с кодом - описать нашу переменную флага инициализации:

section "data" data writeable align 4

 _finit:

        dd      0

Вот и все с кодом. Полный текст программы:

format MS COFF

 extrn   ___bb_blitz_blitz
 public  ___bb_degtorad_degtorad
 public  _DegToRad
 section "code" code
 ___bb_degtorad_degtorad:
        push    ebp
        mov     ebp,esp
        cmp     dword[_finit],0
        je      _init
        mov     eax,0
        mov     esp,ebp
        pop     ebp
        ret
 _init:
        mov     dword[_finit],1
        call    ___bb_blitz_blitz
        mov     eax,0
        mov     esp,ebp
        pop     ebp
        ret
 _DegToRad:
        push    ebp
        mov     ebp,esp
        push    1016003125
        fld     dword[ebp+8]
        fmul    dword[esp]
        fwait
        add     esp,4
        mov     esp,ebp
        pop     ebp
        ret
 section "data" data writeable align 4
 _finit:

        dd      0

Теперь можно идти двумя путями: или писать батник или компильнуть в самом Фасме. Главное - получить скомпиленый файл программы, degtorad.OBJ с которым нам надо работать дальше.

Для батника все просто. Вот командная строка для компиляции и записи лога:

fasm degtorad.ASM >fasm.log

Для Фасма еще проще - надо нажать Ctrl+F9, что приведет к компиляции нашей программы. Можно еще и через меню это сделать: Run/Compile. В общем способов и подспособов получить скомпиленый модуль у нас хватает.

Но это далеко не все. Теперь нам надо использовать командную строку или батник по-любому, если вы конечно не напишете оконную ГУЙ оболочку или не знаете аналогичной программы в визуальном исполнении. А использовать мы будем программу ar.ехе, которая находится в папочке bin Блицмакса. Надеюсь пути к bin прописаны. И для краткости изложения я приведу сразу батник:

del *.a
ar rc degtorad.release.win32.x86.a degtorad.OBJ
ar rc degtorad.debug.win32.x86.a degtorad.OBJ

Эта прога упакует наш объектный код в свой формат, используемый Blitzmax. Что это за формат - не спрашивайте, я не знаю. Да и вряд ли это надо знать для получения конечного результата.

Итак, после выполнения батника или этих команд из консоли, если все настроено, мы получим два файла:

  1. degtorad.release.win32.x86.a
  2. degtorad.debug.win32.x86.a.

Которые идентичны, а их разное название нужно только для компиляции Blitzmax-ом в разных режимах. Как я говорил выше, с тонкостями написания дебаг-модулей в асме я не разбирался.

И это уже почти все. Но надо еще написать интерфейсные файлы (с расширением *.i) для парсера. В которых надо описать нашу функцию. Примеры их написания можно почерпнуть, посмотрев как это делается у других модулей.

Итак создаем два файла degtorad.release.win32.x86.i и degtorad.debug.win32.x86.i и заполняем их примерно таким содержимым:

ModuleInfo "Version: 1.0"

ModuleInfo "Copyright: Oxid"
import brl.blitz

DegToRad:float(AngleDeg#)="DegToRad"

Параметры ModuleInfo совсем не обязательны. Отдельно хочу обратить внимание, что здесь имя функции пишется без подчеркивания впереди. Сначала идет описание (DegToRad:float(AngleDeg#)) и потом соответствие с функцией в асм-коде (="DegToRad"). Вот import brl.blitz вполне возможно тоже не обязательный параметр - без него все работает на несложных примерах. Может это как-то поможет парсеру, но в эти тонкости я не вникал пока. И вот теперь почти все. Теперь надо только создать папку для модуля в папке для библиотек, которая в папке модулей Blitzmax и скопировать туда 4 файла:

  1. degtorad.debug.win32.x86.a,
  2. degtorad.release.win32.x86.a,
  3. degtorad.release.win32.x86.i
  4. degtorad.debug.win32.x86.i

Название для папки можете придумать любое, а у меня уже есть свои модули в папке либ oxi.mod, поэтому я просто создал там папку с правильным именем DegToRad.mod в ней. Теперь можно подгрузить модуль из программы на БМ простой командой Import oxi.degtorad. Вот пример программы на Blitzmax, его использующей:

Strict

Framework brl.basic
Import oxi.degtorad

Print degtorad(0) + "|" +0*Pi/180
Print degtorad(1) + "|" +1*Pi/180 + "|" +Float(1*Pi/180)
Print degtorad(90) + "|" +90*Pi/180
Print degtorad(180) + "|" +180*Pi/180
Print degtorad(270) + "|" +270*Pi/180
Print degtorad(360) + "|" +360*Pi/180
Print degtorad(500) + "|" +500*Pi/180

Print degtorad(100) + "|" +Float(100*Pi/180)

Как можно увидеть из результатов работы программы, функция дает определенную погрешность из-за приведения к float.


Автор: Oxid

Другие

Друзья