Работа с глобальными переменными в Evo SDK
by Hippiman
Итак, ребята, сегодня мы снова будем расширять возможности
Evo SDK. Изначально SDK имел довольно ограниченные возможности
и по функционалу был близок к популярным редакторам на
ZX Spectrum. Тут и невозможность работы с диском и расширенной
памятью, и ужасное ограничение на размер кода в 32K.
В своих предыдущих статьях я описал способы обхождения этих
ограничений. Однако способ увеличения объёма кода путём
разделения его на модули был не очень удобен из-за невозможности
работы с глобальными переменными из модулей. Приходилось
городить монструозные функции-менеджеры, которые передавали
необходимые параметры в модуль или записывали их в расширенную
память. В принципе, с этим можно было жить, однако необходимость
лишних телодвижений сильно снижала скорость вызова модуля, а
значит, и ограничивала варианты их применения. В этой статье я
расскажу, как можно подружить модули и глобальные
(и статические) переменные основной программы.
* * *
Первым делом идём в подпапку_temp_ в директории нашего проекта
(если её нет, читай ACNews #59 ) и открываем файлout.map.
Листаем примерно до середины файла и ищем там вот такие строки:
Area Addr Size Decimal Bytes (Attributes)
---------------------------------------------------------------
_DATA 00005322 000007C9 = 1993. bytes (REL,CON)
Где 00005322 - это начало области данных. (Естественно, в разных
проектах адрес может меняться.)
Закрываемout.map, нам он больше не интересен, и открываем
out.asm.
В начале этого файла идёт список функций, его пролистываем. Ищем
;--------------------------------------------------------
; ram data
;--------------------------------------------------------
.area _DATA
После этих строк идёт список глобальных и статических переменных
такого вида:
_curtime:
.ds 2
_nexttime:
.ds 2
_nextid:
.ds 2
_nextglobid:
.ds 2
_bullet1:
.ds 1
_roadpos:
.ds 1
Как видно, никаких адресов тут нет, что сначала сбивает с толку.
Зато есть вот это:.ds 2.
Эти строки есть не что иное, как смещение в байтах между
переменными.
Путём простых вычислений получаем адреса:
_curtime 0x00005322 (начало области данных)
_nexttime 0x00005322+0x2 = 0x00005324
_nextid 0x00005324+0x2 = 0x00005326
_nextglobid 0x00005326+0x2 = 0x00005328
_bullet1 0x00005328+0x2 = 0x0000532A
_roadpos 0x0000532A+0x1 = 0x0000532B и т.д.
Теперь мы знаем адреса всех глобальных переменных. Осталось
добавить их в модуль.
Например, так:
static u8 *maxhealth;
maxhealth=(u8*)_plmaxhealth;
*maxhealth=15;
На всякий случай напомню, что после любого изменения в основном
модуле и его перекомпиляции все адреса могут поменяться.
Следовательно, каждый раз вручную высчитывать все ссылки - дело
неблагодарное, и лучше его поручить скрипту. Далее я приведу
полный листинг своего perl скрипта, который обрабатывает
исходники основного модуля и генерирует два хедера:
pointers.h - с указателями на функции;
variables.h - с указателями на переменные.
┌──────────────────────────────────────────────────────────────┐
#!/usr/bin/perl
use strict;
my $flag;
my $str;
my @arr;
my $databegin;
my $tmp;
open FIL,"_temp_\out.map";
open OUT,">pointers.h";
open VAR,">variables.h";
$databegin=0;
while($str=<FIL>)
{
chomp $str;
$str=~s/s*//;
if(length($str)>0)
{
@arr=split(/ /,$str);
if(substr($arr[2],0,1) eq "_")
{
print "#define " .$arr[2]." 0x". $arr[0]."n";
print OUT "#define " .$arr[2]." 0x". $arr[0]."n";
}
if($arr[0]eq"_DATA" && $databegin==0)
{
$databegin= hex($arr[31]);#вот тут мы получаем адрес
#области данных и конвертируем его в десятичную систему
#счисления.
}
}
}
close FIL;
open FIL,"_temp_\out.asm"; #открываем основной исходник.
$flag=0;
while($flag==0) #пролистываем до начала описания переменных
{
if(!($str=<FIL>))
{
$flag=1;
}
chomp $str;
#print $str."n";
if($str eq "; ram data")
{
$flag=1;
}
}
$str=<FIL>;
$str=<FIL>;
$flag=0;
while($flag==0)#обрабатываем область с переменными
{
if(!($str=<FIL>))
{
$flag=1;
}
chomp $str;
if($str eq
";--------------------------------------------------------")
#это конец области, дальше нам ничего не нужно.
{
$flag=1;
}
else
{
$str=~s/t//g;#уберём всякий мусор
$str=~s/://g;
$tmp="#define ".$str." ".sprintf ("0x%x",$databegin)."n";
$str=<FIL>;#читаем ещё одну строку, в которой записано
#смещение
chomp $str;
$str=~s/t//g;
@arr=split(/ /,$str);
$databegin+=$arr[1];#получим новое смещение
print $tmp;
print VAR $tmp; #выведем результат в хедер
}
}
└──────────────────────────────────────────────────────────────┘
Вот и всё. Теперь можно добавить вызов этого скрипта к bat-файлу
компиляции основного модуля, и у вас всегда будут верные
сведения об адресах функций и переменных.
Other articles: