Препроцессор
На данный момент работа препроцессора ограничивается убиранием комментов (все, что идет после символа ';' до конца строки), а так же двумя директивами: .GLOBAL (см. раздел создание скриптов, принимающих параметры) и .PROGRESS, при использовании которой во время отработки скрипта будет крутиться диалог ожидания (может оказаться полезным для скриптов, запускаемых из Macro actions).
Переменные глобальные, локальные, их объявление
Объявление переменных неявное. Те вы можете написать
input a
или
a=10
и после этого использовать переменную a. Возможно и явное объявление:
dim a=0
или просто
dim a
Это нужно, например, при использовании локальных переменных с тем же названием, что и у глобальных. В этом случае объявлять локальные переменные необходимо явно.
Переменные могут быть как глобальными (доступными везде на протяжении всего выполнения скрипта), так и локальными (доступными в пределах процедуры, в которой они были объявлены). Переменные Не могут иметь те же названия, что и объявленные или встроенные процедуры, а так же названия "служебных" переменных (попадающих в нэймспэйс изначально), вроде SIZE. Более подробно о нэймспэйсе, что входит туда изначально и тд я напишу ниже.
Размерность переменных, как и адресация в hex едиторе 8 байт.
Массивы
В отличие от обычных переменных, массивы объявляются явно. Делается это следующим образом:
array perem[count]
После объявления можно записывать и получать значения по нужному индексу, например:
a=perem[5]
Индексация начинается с нуля. Размерность массива задается при объявлении. Индекс (как при объявлении, так и при последующей работе) можно задавать выражением. Как и обычные переменные, массивы могут быть локальными (доступными лишь в текущей процедуре), так и глобальными.
Размерность каждого элемента массива, как и переменных, 8 байт.
Процедуры, объявления и вызовы
Вы можете объявить процедуру, чтобы вызывать ее в дальнейшем из кода:
sub название_процедуры [параметр1, параметр2, ...]
....
[return]
end sub
Причем заканчивать можно просто end (как вам удобнее). Вызываются объявленные процедуры так же, как и встроенные. Для преждевременного выхода из процедуры используйте return. Процедуры могут возвращать и принимать значения. Если процедура возвращает некое значение, и ее вызов используется в выражении, передаваемые параметры необходимо заключить в скобки. Скобки рекомендуется использовать и при обычном вызове, так как скоро это будет обязательно (пока что будет работать и без скобок) Небольшой пример:
sub proc a, b
return a+b
end sub
b=proc(5, 6)
print b
Действия
Арифметические:
+ сложение
- вычитание
/ деление
* умножение
| or
& and
^ xor
Логические:
== равно
!= не равно
> больше
< меньше
>= больше либо равно
<= меньше либо равно
| or
& and
Так же можно использовать ключевые слова вместо символов:
ADD
SUB
MUL
DIV
XOR
OR
AND
Действия могут выполняться со скобками, например:
a=b+9*(c+d)
При этом действия в скобках в приоритете, за ними умножение/деление, затем делается все остальное. О логических выражениях более подробно я напишу в разделе "организация условий и циклов".
Организация условий и циклов
Начну с описания условий. Условия можно задавать выражениями, например if a < b | c > d. Кроме того выражениями могут быть и сами сравниваемые величины, например if a < d+0xff. Если в таком выражении вам нужно применить and или or, используйте скобки, например if (a&0x0a) < (b+c|0xff) & b > d.
Цикл с условием:
while условие [do]
...
done
Условие:
if условие [then]
....
[else]
....
fi
Несколько пояснений: do и then не обязательны. Можно писать, можно нет, как вам удобнее. Кол-во вложенностей не ограничено (цикл в цикле, условие в условии и тд).
Встроенные функции
"print param1 [,param2]" - Вывод где param1 - значение, которое нужно вывести, а param2(не обязательный параметр) - система счисления(по умолчанию 10)
"input param1" - Диалог ввода значения, где param1 - переменная, в которую введенное значение запишится
"msg param1 [,param2]" - Диалог вывода значения, где где param1 - значение, которое нужно вывести, а param2(не обязательный параметр) - система счисления(по умолчанию 10)
"exec 'scr_name' [,param1, param2,...]" - Исполнение скрипта из кода, первый параметр - название скрипта в виде строки, следующих параметров может быть сколько угодно. В исполняемом скрипте вы будете принимать эти параметры в переменные, определенные директивой .GLOBAL . Подробнее об этом можно почитать в разделе "распаралеленное исполнение"
"fork [param1, param2, ...]" - тот же exec, только без первого параметра. Запускает нить с интерпретатором, исполняющим тот же исходник, из которого был вызван.
"sleep timeout" - Задержка, длительность задается параметром timeout, в милисекундах
"exit" - Прервать выполнение программы
"random perem" - Записать рандомное число в переменную
"width param1" - Записать ширину экрана(точнее используемой области отображения результата) в переменную param1
"height param1" - Записать высоту экрана(точнее используемой области отображения результата) в переменную param1
"point x, y" - Нарисовать точку по координатам, заданным переменными x и y
"line x1, y1, x2, y2" - Нарисовать линию, проходящую через точки, заданные переменными
"rect x1, y1, x2, y2" - Нарисовать четырехугольник
"circle x, y, radius" - Нарисовать круг
"getVal param1, address, count" - Считать в переменную param1 значение, размерностью count из адреса address
"setVal address, count, value" - Записать по адресу address значение value, размерности count
"select start, stop" - Выделить в редакторе область от start до end
"Служебные" переменные, инициализирующиеся автоматически
SIZE - размер открытого файла. Изменяя размер этой переменной, вы измените размер файла при завершении
С появлением новых раздел будет расширяться
Создание скриптов, принимающих параметры
Для принятия параметров введена директива .GLOBAL. После перечисляются параметры. Фактически, при исполнении это будут обычные глобальные переменные, лишь с тем отличием, что при запуске в них попадут значения, которые были переданы. Пример использования:
.GLOBAL val, count, start, stop
stop=stop+1
while start < stop
setVal start, count, val
start=start+count
done
Данный скрипт при запуске заменит значения в выделенной области значением, которое попадет в val, размерностью count. И не стоит забывать о том, что если какой либо параметр не был задан, в переменную по умолчанию запишится 0. Поэтому будте осторожны с запуском таких скриптов из едитора кода, чтобы не уйти в бесконечный цикл. Так же не забывайте, что графические функции (print, point, line, circle, rect) можно применять только при запуске из едитора. Те вызов любой из этих функций в скрипте, исполняемом из Macro action приведет к ошибке. Однако вы можете использовать input и все остальные функции, не связанные с рисованием в таких скриптах.
Распараллеленное исполнение
Вызывая функцию exec вы фактически запускаете еще один thread с интерпретатором, исполняющим скрипт, указанный первым параметром. В тредах так же можно работать с графикой, вызывать инпуты и мессэджи. Можно редактировать файл и делать все то же, что и в основном треде. Ниже я покажу пару простых примеров:
[script1, скрипт, который мы будем запускать руками]
start=0
stop=SIZE
if stop > 10000
stop=10000
fi
count=stop/4
while start < stop
exec 'threadscr', start, start+count
start=start+count
done
[threadscr, второй скрипт, запускаемый из первого]
.GLOBAL start, end
while start < end
; здесь делается что-то полезное
start=start+1
done
То есть из главного скрипта мы запускаем несколько тредов, выполняющих что-то параллельно, при этом каждый будет работать со своим "куском". Теперь другой подход. Все то же самое, но уже в пределах одного скрипта. Сделать это очень просто - вспомним, что если не передавать параметры скрипту при запуске, в .GLOBAL переменные запишутся нули. Вот и будем проверять на ноль, чтобы понять основной это тред или нет:
.GLOBAL thread, start, end
; это будет происходить в "основном треде"
if thread == 0
start=0
stop=SIZE
if stop > 10000
stop=10000
fi
count=stop/4
while start < stop
exec 'script', 1, start, start+count
start=start+count
done
exit ; после запуска тредов, завершим основной тред
fi
; а здесь пойдет сама обработка, то, что нужно запихать в нити
while start < end
; здесь делается что-то полезное
start=start+1
done
К примеру, так это можно реализовать в пределах одного файла, чтобы не плодить кучу скриптов для одного макроса. В данном случае thread - переменная, которую мы проверяем на ноль, а при вызове exec передаем 1 в качестве соответствующего (первого в данном случае) параметра
Пара примеров
Напоследок приведу несколько скриптов. Можете копировать отсюда, вставлять в редактор кода и жать исполнение. Следующий код выводит значение по указанному адресу в файле:
input addr
getVal val, addr, 1
print val, 16
Теперь завернем этот код в процедуру и вызовем ее:
sub proc
input addr
getVal val, addr, 1
print val, 16
end sub
proc
Теперь давайте заменим значения первых 100 байт(начиная с первого) в файле на значения предыдущих байтов:
i=0
len=SIZE
getVal old, i, 1
i=i+1
if len > 100
len=100
fi
while i < len
setVal i, 1, old
getVal old, i, 1
i=i+1
done
И практическое применение, скрипт, который будет вызываться из Macro action диалога. Сделаем AND с заданным значением над значениями в выделенной области, с указанной размерностью. При этом покрутим диалог ожидания на время выполнения скрипта:
.GLOBAL val, count, start, stop
.PROGRESS
stop=stop+1
while start < stop
getVal a, start, count
a=a & val
setVal start, count, a
start=start+count
done
Напоследок немного порисуем:
a=0
width b
height c
if c < b
b=c
fi
while a < b
point a, a
a=a+1
done
Теперь то же самое, "одной строчкой"
width b
height c
if c < b
b=c
fi
line 0, 0, b, b
И вдогонку - наглядная демонстрация работы с тредами, нарисуем две линии параллельно:
.GLOBAL t
sub paint
a=0
while a < 600 do
point a, a
a=a+1
done
end
sub paintthr
a=600
b=0
while b < 600 do
point a, b
b=b+1
a=a-1
done
end
if t == 0 then
exec 'scr', 1
paint
else
paintthr
fi
Если я что-то упустил или у вас возникли вопросы, пишите в тему