asm-вставки в AS 3.0 (FP 10+)
Вздумалось мне покопаться в алхимии. Бывает.
Скачал cygwin, alchemy-sdk, скурпулезно выполнил замысловатые инструкции со странички Getting Started.
Надо ж.. скомпилился пример stringecho.swc и даже заработал! Тэкс.. Дальше посмотрел примеры либ от адоба.
Ммда.. скучновато, однако. Не,оно, конечно, по уму все вроде. Но...
Не воодушевившись, вобщем, портами сишных либ во флэшовый байткод, но пожалев потраченное время, я решил порыть вокруг.
И нашел.
Один мудрый парень (блин, и как это люди все успевают?) обнаружил занятный факт. Он де тоже копался в алхимии и
установил, что алхимический компилятор asc.jar нормально воспринимает в исходниках АС-классов конструкции вида
Код:
__asm(op(0x43), op(0x32), op(0x68)); //Числа тут я взял из головы.
месте, где они и прописаны в исходнике под эгидой инструкции _asm() и именно в том порядке.
Да-ааа, занятный компилятор!
Во флекc-сдк есть его собрат - asc.jar, но вот инструкцию __asm() он не понимает. Точнее, если отрубить strict-mode,
код скомпилится, но совсем в другой abc. Там будет инструкция типа getproperty :"__asm" у текущего объекта, плеер честно
"оно" поищет, не найдет и пойдет покурить, ну или наподобие, не суть.
Я не смог пройти мимо такого "подарка" и пустился во все тяжкие.
Далее я приведу пример, как это заработало у меня. Я позволил себе опустить некоторые детали своих изысканий, дабы не
быть слишком многословным. Конечно, первым делом я закатил губу на опкоды быстрого доступа к памяти (дух времени, знаете ли!),
которые поддерживаются рантаймом, но не могут быть встроены в байт-код обычным компилятором на основе AS3-шных пассов в исходниках.
Общий ход действий таков:
1. Заготавливаем класс FastMem.as
Код:
package { import flash.system.ApplicationDomain; import flash.utils.ByteArray; public class FastMem extends Object { public function FastMem() { } public function setFastMem(byteArray: ByteArray):void { ApplicationDomain.currentDomain.domainMemory = byteArray; } public function getMinSize():uint { return ApplicationDomain.MIN_DOMAIN_MEMORY_LENGTH; } public function writeByte(position:uint, value:int):void { __asm(op(0xD2),op(0xD1),op(0x3A)); } public function readByte(position:uint):int { __asm(op(0xD1), op(0x35), op(0x48)); } } }
2. Набор файлов под кодовым названием "flasm-sdk" (название мое, не смейтесь):
(пути привожу согласно папкам установки, но нужны только сами файлы, на папки чхать)
- файлы из "алхимического" sdk (alchemy-cygwin-v0.5a):
c:\alchemy\flashlibs\global.abc (это файл-донор buitin типов)
c:\alchemy\flashlibs\playerglobal.abc (это файл-донор intrisinc-классов)
c:\alchemy\bin\asc.jar (это могучий компилятор, понимающий, что такое __asm)
c:\alchemy\bin\GetABC2.pl (ну, а то все служебные утилитки)
c:\alchemy\bin\PutABC2.pl
c:\alchemy\bin\V10SWF.pl
c:\alchemy\bin\zpipe.pl
c:\alchemy\achacks\swctmpl.swf(заготовка для swc)
И для пожимания наших усилий в swc:
- файлы из поставки cygwin (после установки):
c:\cygwin\bin\cygwin1.dll
c:\cygwin\bin\zip.exe
или
- файл из поставки mingw (в составе Code::Blocks):
c:\Program Files\CodeBlocks\mingw\bin\zip.exe
- на компе нужно установить Perl,
тот что с cygwin'ом или обычный виндовый дистрибутив, неважно(см.далее)
- FlashDevelop само-собой со всем флексовым сдк, Java, как полагается.
У меня FD 3.2.2 и FlexSdk 4.1.0 build 16076 (пример скомпилился, заработал и на
более ранней связке, кстати), Java 1.6.0_05.
- хитромудрый catalog.xml ( а это мое чисто "творчество")
Код:
<?xml version="1.0" encoding="utf-8" ?> <swc xmlns="http://www.adobe.com/flash/swccatalog/9"> <versions> <swc version="1.0" /> <flex version="2.0" build="143452" /> </versions> <features> <feature-script-deps /> <feature-files /> </features> <libraries> <library path="library.swf"> <script name="acmodule/memo/FastMem" mod="1177909560000"> <def id="acmodule.memo:FastMem" /> <dep id="flash.utils:ByteArray" type="e" /> <dep id="flash.utils:ByteArray" type="s" /> <dep id="Object" type="i" /> </script> </library> </libraries> <files /> </swc>
Вобщем, если вы не рискнули или не захотели ставить cygwin и конфигурять его с алхимией,
или чего-то там у вас не срослось - не беда. Можно обойтись без этого.
Поскольку я ставил cygwin, следуя инструкции, то с ним перл я поставил тоже. Но, как я выяснил, cygwin для асм-опытов
вместе с его перлом вовсе не нужен, если вы под виндами. Достаточно поставить обычный виндовый перл.
И если вам влом ставить cygwin с перлом и еще с кучей всякого добра впридачу - забейте и элементарно поставьте ActiveState Perl, никаких дополнительных телодвижений при/после установки не требуется (все по дефолту). Конечно, мелькнула мысль - ну его, перл. Написать на сях то же самое (три скрипта) в ехе и делов. Но лень-матушка..
посмотреть очень хотелось на результат. Да и адоб не обидеть бы.. как-никак копирайты в скриптах.
Ну, а zip.exe у меня подошел и другой, из комплекта mingw в поставке Code::Blocks, есть у меня он на компе. Этот не требует дополнительных
длл (cygwin1.dll) для работы, ибо заточен на работу под виндами изначально. И как ни странно, жмет swc как отче наш. По-моему, где-то можно надыбать минг отдельно.
Кстати, пробовал на скорую руку еще жать 7z-ом - не работает потом swc. Но это так, к слову.
Вобщем, cygwin, как таковой, не нужен, по большому счету.
Лирическое отступление 2.
Честно, я не смог понять, как алхимия генерит catalog.xml, что включается потом в swc-либу. Я не смог найти
вразумительных мануалов, как его генерить по-другому или писать в рукопашную.. ну не заморачивался, вод ведь.
Если кто знает этот процесс - обнародуйте, белое место закроем.
А пока я довольствовался на скорую руку написанным catalog.xml, составленным по образу и подобию, как говорится... Что удивительно - сработало.
3. Ну, и конечно вездесущий батник build.bat - последовательность действий для получения вожделенного асматического байт-кода:
Код:
cls java.exe -Xms16M -Xmx1024M -jar .\alchemy\asc.jar -AS3 -import .\alchemy\global.abc -import .\alchemy\playerglobal.abc -config Alchemy::Debugger=false -config Alchemy::NoDebugger=true -config Alchemy::Shell=false -config Alchemy::NoShell=true -config Alchemy::LogLevel=0 -config Alchemy::Vector=true -config Alchemy::NoVector=false -config Alchemy::SetjmpAbuse=false -swf acmodule.memo.FastMem,800,600,60 fastmem.as perl.exe .\alchemy\GetABC2.pl fastmem.swf fastmem.swf.abc perl.exe .\alchemy\PutABC2.pl .\alchemy\swctmpl.swf library.swf fastmem.swf.abc acmodule/memo/FastMem del fastmem.swf.abc del fastmem.abc move library.swf temp.swf perl.exe .\alchemy\V10SWF.pl temp.swf library.swf del temp.swf rem trying cygwin's zip.exe .\alchemy\zip.exe fastmem.swc catalog.xml library.swf rem trying mingw's zip.exe rem .\mingw\bin\zip.exe fastmem.swc catalog.xml library.swf del library.swf
Батник сборки из под cygwin не привожу. Полагаю, что этого будет достаточно.
Не мудрствуя лукаво, я слил все добро в одну папочку "flasm" с соответствующими подпапками, архив с которой вы сможете найти ниже по тексту. Там же находится и скомпиленные .swc с .swf в тестовом FD-проекте.
Собственно, если вы скачали и распаковали, эту папку, или смогли собрать все это хозяйство сами, можно начинать.
Запускаем build.bat из командной строки, чтобы лицезреть все аутпуты.
Код:
D:\flasm>cls D:\flasm>java.exe -Xms16M -Xmx1024M -jar .\alchemy\bin\asc.jar -AS3 -import .\alchemy\flashlibs\global.abc -import .\alchemy\flashlibs\playerglobal.abc -config Alchemy::Debugger=false -config Alchemy::NoDebugger=true -config Alchemy::Shell=false -config Alchemy::NoShell=true -config Alchemy::LogLevel=0 -config Alchemy::Vector=true -config Alchemy::NoVector=false -config Alchemy::SetjmpAbuse=false -swf acmodule.memo.FastMem,800,600,60 fastmem.as D:\flasm>perl.exe .\alchemy\bin\GetABC2.pl fastmem.swf fastmem.swf.abc frame rate: 60 frame count: 1 69 : 4 72 : 508 76 : 26 1 : 0 0 : 0 D:\flasm>perl.exe .\alchemy\bin\PutABC2.pl .\alchemy\achacks\swctmpl.swf library.swf fastmem.swf.abc acmodule/memo/FastMem frame rate: 24 frame count: 1 69 : 4 77 : 506 64 : 31 63 : 16 65 : 4 9 : 3 41 : 26 82 : 471 1 : 0 0 : 0 D:\flasm>del fastmem.swf.abc D:\flasm>del fastmem.abc D:\flasm>move library.swf temp.swf D:\flasm>perl.exe .\alchemy\bin\V10SWF.pl temp.swf library.swf D:\flasm>del temp.swf D:\flasm>.\cygwin\bin\zip.exe fastmem.swc catalog.xml library.swf updating: catalog.xml (deflated 53%) updating: library.swf (deflated 32%) D:\flasm>del fastmem.swf D:\flasm>del library.swf
На стадии изысканий я честно просматривал в hex-редакторе промежуточные файлы и
.. о! чудо! да! опкоды появились. асм-вставки работают! Попутно вдоволь насмотрелся на ABC.
Далее переходим к испытаниям.
FD->New Project(AS3)->Публикация для 10-ого плеера->Подключаем fastmem.swc.
Немудреный класс Main:
(я попытался изобразить что-то типа теста на скорость, не пинайте)
Код:
package { import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.text.TextField; import acmodule.memo.FastMem; import flash.utils.ByteArray; import flash.utils.getTimer; public class Main extends Sprite { private var tf:TextField; private static const CYCLES:uint = 100000; private static const RANGE:uint = 255; private static const SAMPLEPOS:uint = 45; public function Main():void { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); tf = new TextField(); tf.width = 800; tf.height = 600; tf.addEventListener(MouseEvent.CLICK, this.test); addChild(tf); tf.text = "Click me for test."; } private function test(e:Event):void { tf.appendText("\nstarting: cycles " + String(CYCLES) + ", range " + String(RANGE)); var b:ByteArray = new ByteArray(); var fm:FastMem = new FastMem(); b.length = fm.getMinSize(); tf.appendText("\nfast mem size assigned " + String(b.length)); fm.setFastMem(b); var tStart:int = getTimer(); for (var jw:uint = 0; jw < CYCLES; jw++) { for (var iw:uint = 0; iw < RANGE; iw++) { fm.writeByte(iw, iw); } } var tEnd:int = getTimer(); tf.appendText("\n\nwriteByte time:" + String(tEnd - tStart)); tStart = getTimer(); for (var jr:int = 0; jr < CYCLES; jr++) { for (var ir:int = 0; ir < RANGE; ir++) { fm.readByte(ir); } } tEnd = getTimer(); tf.appendText("\nreadByte time:" + String(tEnd - tStart)); tf.appendText("\n\nsample value at address " + String(SAMPLEPOS) + " is " + String(fm.readByte(SAMPLEPOS))); } } }
[qoute]
Click me for test.
starting: cycles 100000, range 0-255
writeByte time:161
readByte time:151
sample value at address 45 is 45
[/quote]
О как. Работает.
Теперь о непонятках.
Ну, про catalog.xml я уже обмолвился.
Еще у меня FD отказывается напрочь автокомплитить методы новоявленного FastMem.
Не знаю в чем тут дело. Может криво таки swc собрался, может еще что..
Далее, опкоды: в "документации" на недокументированные команды сказано черным по белому:
Цитата:
0x35 si8 – store an 8 bit integer to global memory
я понял: сохраняет число в память
0x3a li8 – load an 8 bit unsigned integer from global memory
я понял: считывает число из памяти
я понял: сохраняет число в память
0x3a li8 – load an 8 bit unsigned integer from global memory
я понял: считывает число из памяти
По остальным опкодам:
0xD2, 0xD1 - пихают в стек аргументы метода writeByte(), сначала второй, потом первый;
0x48 - собственно, возвращает результат readByte() - считанное в вершину стека из памяти число.
Если его не ставить, возвращаться ничего не будет. Компилятор тут честно ругается на
синтаксическое отсутствие return'a.
Потому -strict опцию при запуске компилятора я не указал.
Но, это так сказать "отсебятина", знающие как писать на асме поправят

Есть у компилятора еще -optimize опция. Пробовал, реально че-то делает с кодом, размер несколько меньше становится. Но у меня эта опция ругнулась на какой-то неопознанный ... опкод! Не стал разбираться, отрубил.
PS. Так вышло, что несколько ссылок на источники вдохновения, побудивших меня написать весь этот бред, сейчас не под рукой.
В ближайшее время исправлю. И еще, прошу прощения за сумбур. Наверняка, можно все это как-нибудь пригладить.
Архив с примером: http://mdm-zinc.narod.ru/alc/flasm.zip (~2.3 Мб).
UPD
Ссылки:
http://www.unitzeroone.com/blog/2009...and-compilers/
это отправная точка.
http://llvm.org/devmtg/2008-08/Peter...hCCompiler.pdf
великий выдумщик Petersen.
http://pdf.hulufile.com/avm2overview-pdf.html
тут у меня пока дошли руки до "ActionScript Virtual Machine 2 (AVM2) overview". Но есть и другие привлекательные названия). В этом overview перечислены документированные опкоды, в частности.
http://www.ncannasse.fr/blog/virtual_memory_api
http://www.ncannasse.fr/blog/adobe_alchemy
Ну, и то, с чего все и началось, что касается фастмемных фишек.
Всего комментариев 29
Комментарии
![]() ![]() |
|
блин, и как это люди все успевают..
PS поправь теги на as3 |
![]() ![]() |
|
Интересно, только как-то всё запутано и сложновато. С Apparat-м попроще, и strict и optimize вроде работают.
|
![]() ![]() |
|
я пробовал так делать. без inline теряется всякий выигрыш.
|
![]() ![]() |
|
Провел тест против вектора.
Класс: Код:
package { import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.text.TextField; import acmodule.memo.FastMem; import flash.text.TextFieldAutoSize; import flash.utils.ByteArray; import flash.utils.getTimer; public class Main extends Sprite { private var tf:TextField; private var tStart:int; private var tEnd:int; private static const CYCLES:uint = 500; private static const RANGE:uint = 255; public function Main():void { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); tf = new TextField(); tf.width = 800; tf.height = 600; tf.addEventListener(MouseEvent.CLICK, this.test); addChild(tf); tf.text = "Click me for test."; } public function test(e:Event):void { tf.text = "starting: cycles " + String(CYCLES) + ", range " + String(RANGE); tf.appendText("\n--------------------------"); testV(); testFM(); } private function testFM():void { tf.appendText("\n\nFastMem test"); var b:ByteArray = new ByteArray(); var fm:FastMem = new FastMem(); b.length = fm.getMinSize(); fm.setFastMem(b); var i:int = 0; var j:int = 0; tStart = getTimer(); for (j = 0; j < CYCLES; j++) { for (i = 0; i < 255; i++) { fm.writeByte(i, i); } } tEnd = getTimer(); tf.appendText("\nwriteByte time:" + String(tEnd - tStart)); tStart = getTimer(); for (j = 0; j < CYCLES; j++) { for (i = 0; i < 255; i++) { fm.readByte(i); } } tEnd = getTimer(); tf.appendText("\nreadByte time:" + String(tEnd - tStart)); } private function testV():void { tf.appendText("\n\nVector test"); var v:Vector.<int> = new Vector.<int>(1024, true); var j:int = 0; var i:int = 0; tStart = getTimer(); for (j = 0; j < CYCLES; j++) { for (i = 0; i < RANGE; i++) { v[i] = i; } } tEnd = getTimer(); tf.appendText("\nwriteByte time:" + String(tEnd - tStart)); tStart = getTimer(); for (j = 0; j < CYCLES; j++) { for (i = 0; i < RANGE; i++) { var val:int = v[i]; } } tEnd = getTimer(); tf.appendText("\nreadByte time:" + String(tEnd - tStart)); } } } Цитата:
starting: cycles 500000, range 255
-------------------------- Vector test writeByte time:1033 readByte time:887 FastMem test writeByte time:930 readByte time:886 Цитата:
starting: cycles 500, range 255
-------------------------- Vector test writeByte time:2 readByte time:2 FastMem test writeByte time:3 readByte time:2 Цитата:
starting: cycles 500000, range 255
-------------------------- Vector test writeByte time:1018 readByte time:845 FastMem test writeByte time:2052 readByte time:1206 Цитата:
starting: cycles 500, range 255
-------------------------- Vector test writeByte time:3 readByte time:2 FastMem test writeByte time:5 readByte time:3 BlooDHounD, само собой, inline, развороты циклов, рекурсий... Тут я эти несчастные опкоды решил помучить только как пример. Я не собираюсь выставлять все это как некий конкурентоспособный способ оптимизации. Мне интересен больше сам факт возможности asm-вставок. |
|
Обновил(-а) alexcon314 01.11.2010 в 12:46
|
![]() ![]() |
|
Действительно, занятно.
Приведи код сравнения, пожалуйста. |
![]() ![]() |
|
Ошибся в тесте. Исправил. Все ожидаемо. Кроме того, что в 10.1 медленно работает.
|
![]() ![]() |
|
На сколько медленно, хотелось бы знать
![]() UPD: Тьфу, не заметил. Это при выполнении такие результаты или при компиляцию в такую версию? |
|
Обновил(-а) Psycho Tiger 01.11.2010 в 13:09
|
![]() ![]() |
|
При выполнении. Насколько я понял, версия при публикации не влияет. Т.е. независимот версии публикации результаты выполнения одинаковы, что в 10.0, что в 10.1
|
![]() ![]() |
|
В таком случае asm ставки не имеют смысла. Надо ждать 11 фп, смотреть там.
Кстати, мой вопрос тоже - ведь мы сразу abc-код пишем, а не компилируем его. |
![]() ![]() |
|
Цитата:
Надо ждать 11 фп, смотреть там.
|
![]() ![]() |
|
Цитата:
Не воодушевившись, вобщем, портами сишных либ во флэшовый байткод, но пожалев потраченное время, я решил порыть вокруг.
|
![]() ![]() |
|
Если вы меня спрашиваете про компилятор - не знаю. Но было бы неплохо.
Алхимия меня не разочаровала. Мне просто не интересно этим заниматься. |
|
Обновил(-а) alexcon314 01.11.2010 в 14:12
|
![]() ![]() |
|
Про компилятор - это вопрос к тем, кто знает каки-либо подробности о будущем плеере и компиляторе.
А вот это: Цитата:
alexcon314, чем же так алхимия разочаровала?
![]() |
|
Обновил(-а) i.o. 01.11.2010 в 14:21
|
![]() ![]() |
|
Цитата:
Похоже на баг
Но самое интересное - если функцию теста прилепить на клик мыши, то результати очень сильно будут менятся при первых 3-4 кликах. В 10.0+ таких непоняток не наблюдается (у меня по крайней мере) |
|
Обновил(-а) i.o. 01.11.2010 в 14:25
|
![]() ![]() |
|
Я немного покопался в 10.1 в связи с расширеним проектора. Если в 8 ->9 -> 10.0 изменений было немного в том участке, который меня интересовал, и они вобщем-то были логически понятны, то в 10.1 изменения радикальные. Такое ощущение, что кто-то в flash player team решил пойти "своим путем".
|
|
Обновил(-а) alexcon314 01.11.2010 в 14:31
|
![]() ![]() |
|
alexcon314, а что на счет моего вопроса про алхимию?
|
![]() ![]() |
|
Psycho Tiger, я уже написал, что фастмем имеет смысл только при инлайне. если оборачивать всё это методами, тогда что угодно начнёт проигрывать по скорости.
|
![]() ![]() |
|
Так я ж ответил вроде. Нет?
Цитата:
Алхимия меня не разочаровала. Мне просто не интересно этим заниматься.
|
|
Обновил(-а) alexcon314 01.11.2010 в 15:14
|
![]() ![]() |
|
@BlooDHounD, да, действительно. Что-то проворонил.
|
![]() ![]() |
|
не заметил, что сообщение обновилось )
|
![]() ![]() |
|
Чета я не понял!!!
"0xD2, 0xD1 - пихают в стек аргументы метода" Это что же получается я могу в стек закинуть любой адрес и отработать нужный код( прямой доступ к ПК?)? |
![]() ![]() |
|
Не путайте байткод и машинный код.
|
![]() ![]() |
|
Стек в виртуальной машине! Понятно!
|
![]() ![]() |
|
Последние записи от alexcon314
- Пишем свою оболочку для FP под Windows. Шаг 6. (19.05.2011)
- Пишем свою оболочку для FP под Windows. Шаг 5. (18.05.2011)
- Пишем свою оболочку для FP под Windows. Шаг 4. (18.05.2011)
- Пишем свою оболочку для FP под Windows. Шаг 3. (18.05.2011)
- Пишем свою оболочку для FP под Windows. Шаг 2. (17.05.2011)