Elite Games - Свобода среди звезд!
.
  » X3TC Моддинг: obj-файлы и маленькие утилитки | страница 4
Конференция предназначена для общения пилотов. Для удобства она разделена на каналы, каждый из которых посвящен определенной игре. Пожалуйста, открывайте темы только в соответствующих каналах и после того, как убедитесь, что данный вопрос не обсуждался ранее.

Поиск | Правила конференции | Фотоальбом | Регистрация | Список пилотов | Профиль | Войти и проверить личные сообщения | Вход

   Страница 4 из 12
На страницу: Пред.  1, 2, 3, 4, 5 ... 10, 11, 12  След.    Перейти:   Все страницы
Поиск в этой теме:
Канал X3: Terran Conflict » Модовый и скриптовый отсек X3: Terran Conflict: «X3TC Моддинг: obj-файлы и маленькие утилитки»
YOYOMAN
 71 EGP

Рейтинг канала: 2(21)
Репутация: 1
Сообщения: 141
Откуда: Украина
Зарегистрирован: 13.07.2010
CheckerTwo :
Эта функция может быть представлена на С-подобном языке примерно вот в таком виде:

честно говоря я не знаю языка С , но я както смог прописать новые типы врат в обж Гы-гы тоесть их тепреь не 25 а 33 Подмигиваю Супер! и фсё прекрассно пашет но там просто копипаста меток и добавление цыферки нового типа. Хы... А то была байда что не ставило символы Сев. Юж. и т.д. перед словом "врата" вместо этого было "???"

Код:
function GetRaceLicenseSubtype(int RaceId)
 {
   switch(RaceId) {
     case 1: return 26; break;
     case 2: return 27; break;
     case 3: return 28; break;
     case 4: return 29; break;
     case 5: return 30; break;
     case 6: return <пофиг какой номер>; break;
     default: return -1;
   };
 }


case 6: return <пофиг какой номер>; break;
Но как мне эту строчку написать в асемблере?? ПРосто настрочить новю метку "с головы"? день сижу на этой секцией Ругаюсь, недоволен! .

А с вратами гораздо проце. Подмигиваю Хы...
_________________
Unreal Engine 4
    Добавлено: 16:38 16-05-2011   
CheckerTwo
 550 EGP


Рейтинг канала: 6(480)
Репутация: 103
Сообщения: 412
Откуда: Tomsk
Зарегистрирован: 18.08.2004
YOYOMAN :

case 6: return <пофиг какой номер>; break;
Но как мне эту строчку написать в асемблере?? ПРосто настрочить новю метку "с головы"? день сижу на этой секцией Ругаюсь, недоволен! .

А с вратами гораздо проце. Подмигиваю Хы...

Примерно так:
Код:

GetRaceLicenseSubtype:
      enter      1, 1
      push       SP[3] ; RaceId
      xjump      6d, 1
      .long      $00 ; default
      .long      $01 ; 1: аргон
      .long      $02 ;
      .long      $03 ;
      .long      $04 ;
      .long      $05 ;
      .long      $06 ;

$00:  pushb      -1d
      ret

$01:  pushb      26d
      ret

$02:  pushb      27d
      ret

$03:  pushb      28d
      ret

$04:  pushb      29d
      ret

$05:  pushb      30d
      ret

$06:  pushb      <новый_ид_1>
      ret
    Добавлено: 18:11 16-05-2011   
YOYOMAN
 71 EGP

Рейтинг канала: 2(21)
Репутация: 1
Сообщения: 141
Откуда: Украина
Зарегистрирован: 13.07.2010
CheckerTwo :
Примерно так:

ЛОЛ!! Вау! Курю... Это просто переписать секцию вот так? но номер 06 это Ксены. АОГ = 17
_________________
Unreal Engine 4
    Добавлено: 18:26 16-05-2011   
Whiskas
 137 EGP


Рейтинг канала: 3(36)
Репутация: 5
Сообщения: 139

Зарегистрирован: 28.04.2011
CheckerTwo :

но полагаю что на ассемблере.

Именно Подмигиваю Например, топливо к кораблям. Как в GTA4. Сам я-то не могу писать на асме. А вот если бы были уже готовые функции, как с десантами(выдрал из time of the truth), то было б круто. Эти примеры, коды и готовые решения и имею ввиду.
CheckerTwo :
Красным цветом - размер секции в байтах

Спасибо! А то я сам дуб дубом Гы-гы

CheckerTwo :
Что-то я не пойму как ты чего правишь...

CheckerTwo :
И все ж, ты про какой редактор? HEX что ли? Не пугай! Не уверен, что HEX-редактором можно что-то написать и не наделать массу ошибок.

Вот небольшой пример. Думаю он тут пригодится. Вставляем код в оригинальный x3story.obj.

Пример 1. Разбрасывание десанта по классам.
 Cкрытый текст   (кликните здесь для просмотра)

Операции проводим на x3story.obj из версии игры 3.1
Итак, сам код функции вставляется непосредственно перед словом SYMB, типа в конец. Длина "функции кол-ва десанта" 6 байт.
Допустим хочется, чтобы на M1 было 33 десанта, на M2 - 45, а на M7 - 70.
Т.е. нужно вставить три функции по 6 байт, итого 18.

Вставляем 18 байт перед словом SYMB.
Цитата:
0018D300 01 83 01 83 6E 00 01 05 06 83 01 83 6E 00 03 05 | .ƒ.ƒn....ƒ.ƒn...
0018D310 21 83 6E 00 03 05 2D 83 6E 00 03 05 46 83 53 59 4D 42 00 00 | Eƒn...%ƒn... ƒSYMB..

Предпоследний байт в каждой функции - это и есть кол-во десов.

Дуем в начало обжа и находим
Цитата:
00000000 53 54 4F 52 00 00 03 21 4D 53 FD 59 43 4F 44 45 | STOR...!MSýYCODE
00000010 00 18 D2 F8 00 6E 00 08 | ..Òø.n..


Мы добавили 18 байт, поэтому вычисляем "новое начало". (Hex)18D2F8+(Dec)18 = (Hex)18D2F8 + (Hex)12 = 18D30A
Вписываем, чтоб было так:
Цитата:
00000000 53 54 4F 52 00 00 03 21 4D 53 FD 59 43 4F 44 45 | STOR...!MSýYCODE
00000010 00 18 D3 0A 00 6E 00 08 | ..Òø.n..


Теперь можно дизассемблить x3story и просмотреть в x3story.out в самом конце, что получилось.
 Cкрытый текст   (кликните здесь для просмотра)
Цитата:

;
; === Select ====================================================
; function Obj_4314.Select(arg1);
;
| Obj_4314.Select:
0018D2F0: 6E 0001 | 0 enter 1, 1
0018D2F3: 05 06 | 0 pushb 6
0018D2F5: 83 | 1 ret

0018D2F6: 01 | push 0
0018D2F7: 83 | ret

0018D2F8: 6E 0003 | enter 1, 3
0018D2FB: 05 21 | pushb 33d ; 21h
0018D2FD: 83 | ret


0018D2FE: 6E 0003 | enter 1, 3
0018D301: 05 2D | pushb 45d ; 2Dh
0018D303: 83 | ret


0018D304: 6E 0003 | enter 1, 3
0018D307: 05 46 | pushb 70d ; 46h
0018D309: 83 | ret


Десов нам пока это не дает. Все просто - у классов M1, M2, M7 нет функции добавления десов. В это можно убедится, просмотрев
SHIP_M1 = class(Obj_2019)
 Cкрытый текст   (кликните здесь для просмотра)
Цитата:
; SHIP_M1 = class(Obj_2019)
; variables
; public
; function Create(arg1, arg2, arg3, arg4, arg5);
; function ShipAttackedBy(arg1, arg2, arg3);
; function StartControlTask();
; function GetNewSquadLeader();
; function StartPatrolSquad(arg1);
; function CreatePlayerOwned(arg1, arg2, arg3, arg4, arg5);
; function AttackedByHandler(arg1, arg2);
; function GetAnswers(arg1, arg2, arg3, arg4);
; end;

SHIP_M2 = class(Obj_2019)
 Cкрытый текст   (кликните здесь для просмотра)
Цитата:
SHIP_M2 = class(Obj_2019)
; variables
; public
; function Create(arg1, arg2, arg3, arg4, arg5);
; function CreatePlayerOwned(arg1, arg2, arg3, arg4, arg5);
; function AttackedByHandler(arg1, arg2);
; function GetAnswers(arg1, arg2, arg3, arg4);
; function GetDockBaySize();
; end;

SHIP_M7 = class(Obj_2019)
 Cкрытый текст   (кликните здесь для просмотра)
Цитата:
; SHIP_M7 = class(Obj_2019)
; variables
; public
; function Create(arg1, arg2, arg3, arg4, arg5);
; function ShipAttackedBy(arg1, arg2, arg3);
; function StartControlTask();
; function GetNewSquadLeader();
; function StartPatrolSquad(arg1);
; function CreatePlayerOwned(arg1, arg2, arg3, arg4, arg5);
; function AttackedByHandler(arg1, arg2);
; function GetAnswers(arg1, arg2, arg3, arg4);
; end;


Почему нет? Потому что у всех этих классов кол-во десанта одинаково и разработчикам не было смысла делать эту функцию для каждого класса. Они сделали одну общую функцию десантов, которая находится здесь:
 Cкрытый текст   (кликните здесь для просмотра)
Цитата:
; type
; Obj_2019 = class(SHIP_CARRIER)
; variables
; public
; function GetMaxMarines();
; end;

Obj_2019 - это парент дя SHIP_M1,SHIP_M2 и SHIP_M7. Он - Босс и командует ими. У него есть ф-ия десов и она добавляет 20 десов всем, кто в его подчинении Гы-гы Ненадолго...

Все, что осталось сделать - это добавить ф-ию GetMaxMarines каждому нашему классу. Разумеется, непосредственно в оригинальный обж(версии 3.1!!!!) и без полной перекомпиляции.

Для начала открываем x3story.sym и находим
07E5 000007E3 | 0 8 | SHIP_M1
07E5 - это наш класс, M1
000007E3 (или просто 07E3) - это парент его, Obj_2019
Цифра 8 - кол-во функций у SHIP_M1.

Теперь нужно найти всё это внутри обжа. Вбиваем в поиск (искать HEX-значения!) 07 E5 00 00 07 E3

 Cкрытый текст   (кликните здесь для просмотра)
Цитата:
0019D390 00 00 00 00 00 00 00 00 07 E5 00 00 07 E3 48 C4 .........å...ãHÄ
0019D3A0 72 D9 00 00 00 08 00 0C 8A 34 00 00 00 81 00 00 rÙ......Š4.....
0019D3B0 00 07 00 00 00 05 00 0C 8A B7 00 00 C9 CD 00 00 ........Š·..ÉÍ..
0019D3C0 00 0E 00 00 00 03 00 0C 8E A2 00 00 F8 B2 00 00 ........Ž¢..ø²..
0019D3D0 00 04 00 00 00 00 00 0C 8F 6C 00 00 F9 14 00 00 ........l..ù...
0019D3E0 00 0A 00 00 00 00 00 0C 91 8E 00 00 F9 03 00 00 ........‘Ž..ù...
0019D3F0 00 04 00 00 00 01 00 0C 91 B8 00 00 F9 26 00 00 ........‘¸..ù&..
0019D400 00 07 00 00 00 05 00 0C 91 E9 00 00 EE 64 00 00 ........‘é..îd..
0019D410 00 08 00 00 00 02 00 0C 94 D5 00 00 39 95 00 00 ........”Õ..9•..
0019D420 00 06 00 00 00 04 00 00 00 00 00 00 07 E6 00 00 .............æ..
0019D430 07 E3 48 C4 72 D9 00 00 .ãHÄrÙ..

08 - кол-во функций Подмигиваю
Всё что серым, это уже идёт другой объект: 07E6 (SHIP_M2). Здесь главное уловить границы, дабы правильно вписать код.

Меняем 08 на 09. Мы же добавляем новую функцию.
Теперь нужно вставить вот такой код:
0018D2F80000A5CE0000000200000000
18D2F8 - указатель(адрес) на начало кода нашей функции. У нас она находится именно там.
00000A5CE - это указатель на название функции. Можно глянуть в x3story.str:
0000A5CE : GetMaxMarines.
А куда вставлять? Сразу после 04.

 Cкрытый текст   (кликните здесь для просмотра)
Цитата:
0019D400 07 E5 00 00 07 E3 48 C4 72 .å...ãHÄr
0019D410 D9 00 00 00 09 00 0C 8A 34 00 00 00 81 00 00 00 Ù......Š4......
0019D420 07 00 00 00 05 00 0C 8A B7 00 00 C9 CD 00 00 00 .......Š·..ÉÍ...
0019D430 0E 00 00 00 03 00 0C 8E A2 00 00 F8 B2 00 00 00 .......Ž¢..ø²...
0019D440 04 00 00 00 00 00 0C 8F 6C 00 00 F9 14 00 00 00 .......l..ù....
0019D450 0A 00 00 00 00 00 0C 91 8E 00 00 F9 03 00 00 00 .......‘Ž..ù....
0019D460 04 00 00 00 01 00 0C 91 B8 00 00 F9 26 00 00 00 .......‘¸..ù&...
0019D470 07 00 00 00 05 00 0C 91 E9 00 00 EE 64 00 00 00 .......‘é..îd...
0019D480 08 00 00 00 02 00 0C 94 D5 00 00 39 95 00 00 00 .......”Õ..9•...
0019D490 06 00 00 00 04 00 18 D2 F8 00 00 A5 CE 00 00 00 .......Òø..¥Î...
0019D4A0 02 00 00 00 00 00 00 00 00 00 00
07 E6 00 00 07 E3 ............æ

После 02 должно быть 10 байтов заполненных нулями. Лишь потом 07 E6 00 00 07 E3

Можно дизасмить. Открываем x3story.out и глядим в конец:
 Cкрытый текст   (кликните здесь для просмотра)
;
; === Select ====================================================
; function Obj_4314.Select(arg1);
;
| Obj_4314.Select:
0018D2F0: 6E 0001 | 0 enter 1, 1
0018D2F3: 05 06 | 0 pushb 6
0018D2F5: 83 | 1 ret

0018D2F6: 01 | push 0
0018D2F7: 83 | ret

;
; === GetMaxMarines =============================================
; function SHIP_M1.GetMaxMarines();
;
| SHIP_M1.GetMaxMarines:
0018D2F8: 6E 0003 | 0 enter 0, 3
0018D2FB: 05 21 | 0 pushb 33d ; 21h
0018D2FD: 83 | 1 ret


0018D2FE: 6E 0003 | enter 0, 3
0018D301: 05 2D | pushb 45d ; 2Dh
0018D303: 83 | ret


0018D304: 6E 0003 | enter 0, 3
0018D307: 05 46 | pushb 70d ; 46h
0018D309: 83 | ret


.end


Для M1 десантов 33 Супер!

Теперь для M2.
1) Открываем x3story.sym и находим:
07E6 000007E3 | 0 5 | SHIP_M2
2) Вбиваем в поиск 07 E6 00 00 07 E3 (или тупо 07E6000007E3, смотря что за HEX-эдитор)
Цитата:
0019D430 07 E6 00 00 .æ..
0019D440 07 E3 48 C4 72 D9 00 00 00 05 00 0C 95 95 00 00 .ãHÄrÙ......••..
0019D450 00 81 00 00 00 07 00 00 00 05 00 0C 96 0D 00 00 ...........–...
0019D460 F9 26 00 00 00 07 00 00 00 05 00 0C 96 3E 00 00 ù&..........–>..
0019D470 EE 64 00 00 00 08 00 00 00 02 00 0C 98 F6 00 00 îd..........˜ö..
0019D480 39 95 00 00 00 06 00 00 00 04 00 0C 99 B6 00 00 9•..........™¶..
0019D490 97 95 00 00 00 02 00 00 00 00 00 00 00 00 00 00 —•..............
0019D4A0 07 EA 00 00 07 F1 48 C4 72 D9 .ê...ñHÄrÙ

Теперь глядим в x3story.out. Функция добавления 45-ти десов для M2 начинается с адреса 0018D2FE.
Нужно вставить вот такой код: 18D2FE0000A5CE0000020000000000 после 020000000000. И изменить кол-во ф-ий с 05 на 06.
Должно быть строго так:
Цитата:
0019D430 07 E6 00 00 .æ..
0019D440 07 E3 48 C4 72 D9 00 00 00 06 00 0C 95 95 00 00 .ãHÄrÙ......••..
0019D450 00 81 00 00 00 07 00 00 00 05 00 0C 96 0D 00 00 ...........–...
0019D460 F9 26 00 00 00 07 00 00 00 05 00 0C 96 3E 00 00 ù&..........–>..
0019D470 EE 64 00 00 00 08 00 00 00 02 00 0C 98 F6 00 00 îd..........˜ö..
0019D480 39 95 00 00 00 06 00 00 00 04 00 0C 99 B6 00 00 9•..........™¶..
0019D490 97 95 00 00 00 02 00 00 00 00 00 18 D2 FE 00 00 —•..........Òþ..
0019D4A0 A5 CE 00 00 00 02 00 00 00 00 00 00 00 00 00 00 ¥Î..............
0019D4B0 07 EA 00 00 07 F1 48 C4 72 D9 .ê...ñHÄrÙ


Для M7. Было так:
 Cкрытый текст   (кликните здесь для просмотра)
Цитата:
0019D550 07 EB 00 00 07 E3 48 C4 72 D9 00 00 .ë...ãHÄrÙ..
0019D560 00 08 00 0C 9E 24 00 00 00 81 00 00 00 07 00 00 ....ž$.........
0019D570 00 05 00 0C 9E A7 00 00 C9 CD 00 00 00 0E 00 00 ....ž§..ÉÍ......
0019D580 00 03 00 0C A2 92 00 00 F8 B2 00 00 00 04 00 00 ....¢’..ø²......
0019D590 00 00 00 0C A3 5C 00 00 F9 14 00 00 00 0A 00 00 ....£\..ù.......
0019D5A0 00 00 00 0C A5 7E 00 00 F9 03 00 00 00 04 00 00 ....¥~..ù.......
0019D5B0 00 01 00 0C A5 A8 00 00 F9 26 00 00 00 07 00 00 ....¥¨..ù&......
0019D5C0 00 05 00 0C A5 D9 00 00 EE 64 00 00 00 08 00 00 ....¥Ù..îd......
0019D5D0 00 02 00 0C A8 C5 00 00 39 95 00 00 00 06 00 00 ....¨Å..9•......
0019D5E0 00 04 00 00 00 00 00 00 07 F2 00 00 08 55 .........ò...U

Должно быть так:
 Cкрытый текст   (кликните здесь для просмотра)
Цитата:
0019D550 07 E3 48 C4 72 D9 00 00 .ãHÄrÙ..
0019D560 00 09 00 0C 9E 24 00 00 00 81 00 00 00 07 00 00 ....ž$.........
0019D570 00 05 00 0C 9E A7 00 00 C9 CD 00 00 00 0E 00 00 ....ž§..ÉÍ......
0019D580 00 03 00 0C A2 92 00 00 F8 B2 00 00 00 04 00 00 ....¢’..ø²......
0019D590 00 00 00 0C A3 5C 00 00 F9 14 00 00 00 0A 00 00 ....£\..ù.......
0019D5A0 00 00 00 0C A5 7E 00 00 F9 03 00 00 00 04 00 00 ....¥~..ù.......
0019D5B0 00 01 00 0C A5 A8 00 00 F9 26 00 00 00 07 00 00 ....¥¨..ù&......
0019D5C0 00 05 00 0C A5 D9 00 00 EE 64 00 00 00 08 00 00 ....¥Ù..îd......
0019D5D0 00 02 00 0C A8 C5 00 00 39 95 00 00 00 06 00 00 ....¨Å..9•......
0019D5E0 00 04 00 18 D3 04 00 00 A5 CE 00 00 00 02 00 00 ....Ó...¥Î......
0019D5F0 00 00 00 00 00 00 00 00
07 F2 00 00 08 55 .........ò...U


Как видно, адреса к функциям должны быть друг под другом, также и ссылки на их названия. Всё должно быть красиво, как в таблице, поэтому-то для M1 добавляется 0018D2F80000A5CE0000000200000000 после 04, а для M2 18D2FE0000A5CE0000020000000000 после 020000000000
Не стал расписывать полностью. Вообщем сравнивайте и понимайте

Могут присутствовать ошибки! Поэтому ниже ссылка на рабочий пример

В качестве домашнего задания сделайте десантов на все классы(кроме истребителей) разное Подмигиваю Для практики и усвоения примера)


Не знаю как прикрепить, придется так:
Оригинальный x3story:
w w w.fayloobmennik.net/624563
Модифицированный пример:
w w w.fayloobmennik.net/624568

Удалять, сохраняться, загружаться можно без последствий. Гарантия 99.9% Гы-гы
Вообщем как-то так. И все hex-эдитором. Проще простого!Супер!

Зы
До strg не добрался пока. Тоже можно в чистый обж hex-эдитором добавлять. Например, для названия своей новой функции.

Кстати, что это за ошибка вылазит, когда ассемблишь?
Цитата:
pass1:(x3story.asm:9) Warn: Obj-file 'x3story.obj' not found. Tables of symbols are not loaded.

Ну положил я рядом в папку x3story.obj, а он мне:
 Cкрытый текст   (кликните здесь для просмотра)
Цитата:
pass1:(x3story.asm:239) Err: Redefined class 'DEBUG'.

pass1:(x3story.asm:241) Err: Redefined variable 'IsProfiling'.

pass1:(x3story.asm:242) Err: Redefined variable 'CheatActive'.

pass1:(x3story.asm:243) Err: Redefined variable 'var_203_2'.

pass1:(x3story.asm:244) Err: Redefined variable 'var_203_3'.

pass1:(x3story.asm:245) Err: Redefined variable 'NextCheatLaser'.

pass1:(x3story.asm:246) Err: Redefined variable 'NextCheatShield'.

pass1:(x3story.asm:247) Err: Redefined variable 'LastGate'.

pass1:(x3story.asm:248) Err: Redefined variable 'var_203_7'.

pass1:(x3story.asm:250) Err: Redefined procedure 'CheatEquip'.

pass1:(x3story.asm:251) Err: Redefined procedure 'MakeTestObj'.


и так каждую функцию Подозрение.


Последний раз редактировалось: Whiskas (18:39 17-05-2011), всего редактировалось 6 раз(а)
    Добавлено: 17:10 17-05-2011   
YOYOMAN
 71 EGP

Рейтинг канала: 2(21)
Репутация: 1
Сообщения: 141
Откуда: Украина
Зарегистрирован: 13.07.2010
Whiskas T-Mech :
Ну положил я рядом в папку x3story.obj, а он мне: Cкрытый текст (кликните здесь для просмотра)

Офигеть просто!!! Вау! Вау! Вау! Вау!
А ты не ложи обж при компиляции в одну папку с асемблером и асмом Подмигиваю НЕ обращяй внимания на ту хрень про загрузку таблиц. Гы-гы
_________________
Unreal Engine 4
    Добавлено: 20:51 17-05-2011   
CheckerTwo
 550 EGP


Рейтинг канала: 6(480)
Репутация: 103
Сообщения: 412
Откуда: Tomsk
Зарегистрирован: 18.08.2004
Whiskas T-Mech :
Сам я-то не могу писать на асме. А вот если бы были уже готовые функции, как с десантами(выдрал из time of the truth), то было б круто. Эти примеры, коды и готовые решения и имею ввиду.

Для того, чтобы писать на ассемблере нужен только текстовый редактор и компилятор/декомпилятор.
Whiskas T-Mech :
Вот небольшой пример. Думаю он тут пригодится. Вставляем код в оригинальный x3story.obj.
Пример 1. Разбрасывание десанта по классам.
...
Вообщем как-то так. И все hex-эдитором. Проще простого!Супер!

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

Компилятор может работать в 2х режимах.
Первый - патч существующего obj-файла. Выбор режима работы осуществляется директивой .loadobj "имя-обж-файла". Если директива .loadobj присутствует в компилируемом asm-файле, то выполняется загрузка указанного obj, разбираются все секции, формируются таблицы функций, переменных и строк во внутренних структурах компилятора. При этом девелоперу будут доступны все классы, функции и переменные, которые уже были в obj-файле и он может ссылаться на них, вызывать.
Во втором режиме - это когда в asm-файле отсутствует директива .loadobj - выполняется просто трансляция asm-файла в obj. Этот режим может быть использован для полной пересборки всего obj-а.

Whiskas T-Mech :
Кстати, что это за ошибка вылазит, когда ассемблишь?
pass1:(x3story.asm:9) Warn: Obj-file 'x3story.obj' not found. Tables of symbols are not loaded.

Ты запустил компиляцию асм-файла, в котором есть директива .loadobj "x3story.obj", но рядом с этим файлом нет файла x3story.obj. Вот компилятор и ругнулся.

Whiskas T-Mech :
Ну положил я рядом в папку x3story.obj, а он мне:
pass1:(x3story.asm:239) Err: Redefined class 'DEBUG'.

Теперь компилятор нашел и загрузил файл x3story.obj, загрузил все таблицы функций. А так как в асм-файле ты пытаешься компилировать уже то что есть и скомпилировано, компилятор ругается - думает, что пытаешься повторно определить уже откомпилированные функции.

Просто закоментируй строку в асм-файле .loadobj ... Компилятор будет собирать новый обж.


Давай, в качестве примера, разберем как можно написать твой патчик маринесов на ассемблере. Текстовым редактором (только не вордом), который может работать с простыми текстовыми файлами в кодировке win-1251 набираем вот такой текст:
Код:

           .title     marine_tst
           .radix     10d
; загружем оригинальный обж, который нужно подправить
           .loadobj   "x3story.obj"

           .section   code, con

; добавляем в описание класса SHIP_M1 новую функцию GetMaxMarines
.classdef  SHIP_M1
  .func      GetMaxMarines;
.endclass  SHIP_M1

; теперь пишем реализацию этой функции
; function  SHIP_M1.GetMaxMarines();
SHIP_M1.GetMaxMarines:
           enter      0, 1
           pushb      33d ; 21h
           ret

; ну и так далее для всех других классов
.classdef  SHIP_M2
  .func      GetMaxMarines;
.endclass  SHIP_M2

; function  SHIP_M2.GetMaxMarines();
SHIP_M2.GetMaxMarines:
           enter      0, 1
           pushb      45d ; 2Dh
           ret


.classdef  SHIP_M7
  .func      GetMaxMarines;
.endclass  SHIP_M7

; function  SHIP_M7.GetMaxMarines();
SHIP_M7.GetMaxMarines:
           enter      0, 1
           pushb      70d ; 46h
           ret

           .end


Сохраняем его в отдельную папочку с именем marines.asm. Копируем в эту папочку оригинальный x3story.obj из версии 3.1. Копируем туда же xa_asm.exe и xa_asm.ini
Запускаем:
C:\test\xa_asm.exe marines.asm
После компиляции в этой же папочке появятся файлы marines.lst, marines.cod
В файле marines.lst можно посмотреть ошибки где и какие.
Файл marines.cod - это полученный OBJ-файл, который содержит все наши изменения.
    Добавлено: 12:57 18-05-2011   
Whiskas
 137 EGP


Рейтинг канала: 3(36)
Репутация: 5
Сообщения: 139

Зарегистрирован: 28.04.2011
CheckerTwo :

Для того, чтобы писать на ассемблере нужен только текстовый редактор и компилятор/декомпилятор.

А ещё мозги и знание самого ассемблера, которых у меня нет...)

CheckerTwo :
Кроме того так как у егософта секция STRG шифрована, добавлять свои функции несколько напряжно.

Я и не знал, что так можно компилятором патчить Ух ты!.. Ух ты!.. Подозрение. . Это реально в 1000 раз быстрее, правда вот меняется эта фигня из 4-ых байт целыми блоками и придется править обратно, как в оригинальном. По поводу strg, то такими патчами теперь все намного проще. Достаточно назвать функцию как нужно, например, GetMaximumMoneyAndFiftyBMWs и набор из 27+1 зашифрованных символов будут ждать тебя в конце обжа. Осталось тока выдрать Супер!

________________________________
Заметил, что плохиш компилятор Гы-гы изменяет размер секции PTCH c
Цитата:
001AF5F0 50 54 43 48 00 02 48 14 00 00 00 15 48 C4 72 D9 PTCH..H.....HÄrÙ

на
Цитата:
001B1B30 50 54 43 48 00 02 48 1C 00 00 00 15 48 PTCH..H.....H
001B1B40 C4 72 D9 ÄrÙ

Оригинальный obj из 13.cat патчил.
Теперь понятно почему в x3story из Time of Truth тож размер 02 48 1C. Точно так же и пропатчили оригинальный, только я тут ручками и с напильником все сижу Гы-гы

Спасибо за пример! Супер! Будем юзать патчи Курю...

 Cкрытый текст   (кликните здесь для просмотра)
Гм... в примере-то ошибка выходит. Если функция начинается с 6E0003, то должно быть так 0018D2F80000A5CE0000000300000000.
Если с 6E0002, то 0018D2F80000A5CE0000000200000000


 Cкрытый текст   (кликните здесь для просмотра)
CheckerTwo :
В твоем патче <максимально_доступный_стек> был равен 3. В принципе не страшно, движек игры с этим справляется. Я поставил 1, потому что больший стек этой функции не нужен. Об этом речь?


Не совсем. Просто если ставить в коде к примеру enter 0, 12d(6E000C), то в обже, в ссылке на новую ф-ию getmaxmarines и должно быть DecToHex 12 = 0C. А я в примере ставил enter 0, 3(6E0003), а сам в обж записывал 02. (хотя должен был именно 03). Не смертельная ошибка, конечно, и все же
Цитата:
0019D550 07 EB 00 00 07 E3 4D D4 38 F1 00 00 .ë...ãMÔ8ñ..
0019D560 00 09 00 0C 9E 24 00 00 00 81 00 00 00 07 00 00 ....ž$.........
0019D570 00 05 00 0C 9E A7 00 00 C9 CD 00 00 00 0E 00 00 ....ž§..ÉÍ......
0019D580 00 03 00 0C A2 92 00 00 F8 B2 00 00 00 04 00 00 ....¢’..ø²......
0019D590 00 00 00 0C A3 5C 00 00 F9 14 00 00 00 0A 00 00 ....£\..ù.......
0019D5A0 00 00 00 0C A5 7E 00 00 F9 03 00 00 00 04 00 00 ....¥~..ù.......
0019D5B0 00 01 00 0C A5 A8 00 00 F9 26 00 00 00 07 00 00 ....¥¨..ù&......
0019D5C0 00 05 00 0C A5 D9 00 00 EE 64 00 00 00 08 00 00 ....¥Ù..îd......
0019D5D0 00 02 00 0C A8 C5 00 00 39 95 00 00 00 06 00 00 ....¨Å..9•......
0019D5E0 00 04 00 18 D3 04 00 00 A5 CE 00 00 00 0C 00 00 ....Ó...¹»......
0019D5F0 00 00 00 00 00 00 00 00 ........


Последний раз редактировалось: Whiskas (20:53 18-05-2011), всего редактировалось 2 раз(а)
    Добавлено: 14:51 18-05-2011   
CheckerTwo
 550 EGP


Рейтинг канала: 6(480)
Репутация: 103
Сообщения: 412
Откуда: Tomsk
Зарегистрирован: 18.08.2004
Whiskas T-Mech :

Заметил, что плохиш компилятор Гы-гы изменяет размер секции PTCH c
Цитата:
001AF5F0 50 54 43 48 00 02 48 14 00 00 00 15 48 C4 72 D9 PTCH..H.....HÄrÙ

на
Цитата:
001B1B30 50 54 43 48 00 02 48 1C 00 00 00 15 48 PTCH..H.....H
001B1B40 C4 72 D9 ÄrÙ


Поправил в новой версии. Действительно косяк. Спасибо.

Whiskas T-Mech :
Гм... в примере-то ошибка выходит. Если функция начинается с 6E0003, то должно быть так 0018D2F80000A5CE0000000300000000.
Если с 6E0002, то 0018D2F80000A5CE0000000200000000


Если правильно тебя понял, то первая команда любой функции - enter <число_аргументов>, <максимально_доступный_стек>
В твоем патче <максимально_доступный_стек> был равен 3. В принципе не страшно, движек игры с этим справляется. Я поставил 1, потому что больший стек этой функции не нужен. Об этом речь?

-------------
Пока проверял и разбирал пример Whiskas T-Mech, обнаружил еще одну небольшую нехорошесть, мелочь но кто-нибудь да налетит. Улыбка.

В первом посте подправленные версии компилятора/декомпилятора.
Ассемблер, версия 4.10b:
- исправлена ошибка в установке размера секции PTCH (спасибо Whiskas T-Mech)
- исправлена ошибка в определении класса для новой функции
- подправлена работа директива .settime (спасибо YOYOMAN)

Дизассемблер, версия 4.13b
- подправлены коментарии для xjump (спасибо vivere)
    Добавлено: 17:51 18-05-2011   
YOYOMAN
 71 EGP

Рейтинг канала: 2(21)
Репутация: 1
Сообщения: 141
Откуда: Украина
Зарегистрирован: 13.07.2010
CheckerTwo :

Примерно так:

Кстати, нифига это код не пашет. Ассемблер ругается на эти переменные вида $XX и не хочет компилить. Да ну ладно. Улыбка Буду чё нибуть другое разбирать. Интересно капец. Гы-гы
Спасибо за обновлени утилиток.
_________________
Unreal Engine 4

Последний раз редактировалось: YOYOMAN (11:29 19-05-2011), всего редактировалось 1 раз
    Добавлено: 11:28 19-05-2011   
CheckerTwo
 550 EGP


Рейтинг канала: 6(480)
Репутация: 103
Сообщения: 412
Откуда: Tomsk
Зарегистрирован: 18.08.2004
YOYOMAN :
CheckerTwo :

Примерно так:

Кстати, нифига это код не пашет. Ассемблер ругается на эти переменные вида $XX и не хочет компилить. Да ну ладно. Улыбка Буду чё нибуть другое разбирать. Интересно капец. Гы-гы
Спасибо за обновлени утилиток.

У меня все скомпилилось
 Cкрытый текст   (кликните здесь для просмотра)

Код:

   1                                               .title     Example 1
   2                                               .radix     10d
   3                                               .section   code, con
   4                                   
   5                                    .func GetRaceLicenseSubtype
   6                                   
   7   00000001:                        GetRaceLicenseSubtype:
   8   00000001: 6E 0001                      enter      1, 1
   9   00000004: 0D 0004                      push       SP[3] ; RaceId
  10   00000007: 78 0006 00000001             xjump      6d, 1
  11   0000000E: 0000002A                     .long      $00 ; default
  12   00000012: 0000002D                     .long      $01 ; 1: рЁуюэ
  13   00000016: 00000030                     .long      $02 ;
  14   0000001A: 00000033                     .long      $03 ;
  15   0000001E: 00000036                     .long      $04 ;
  16   00000022: 00000039                     .long      $05 ;
  17   00000026: 0000003C                     .long      $06 ;
  18                                   
  19   0000002A: 05 FF                  $00:  pushb      -1d
  20   0000002C: 83                           ret
  21                                   
  22   0000002D: 05 1A                  $01:  pushb      26d
  23   0000002F: 83                           ret
  24                                   
  25   00000030: 05 1B                  $02:  pushb      27d
  26   00000032: 83                           ret
  27                                   
  28   00000033: 05 1C                  $03:  pushb      28d
  29   00000035: 83                           ret
  30                                   
  31   00000036: 05 1D                  $04:  pushb      29d
  32   00000038: 83                           ret
  33                                   
  34   00000039: 05 1E                  $05:  pushb      30d
  35   0000003B: 83                           ret
  36                                   
  37   0000003C: 05 01                  $06:  pushb      1
  38   0000003E: 83                           ret
  39   0000003F:                           .end
Errors : 0   Warnings ; 0   Notes : 0

    Добавлено: 19:45 19-05-2011   
YOYOMAN
 71 EGP

Рейтинг канала: 2(21)
Репутация: 1
Сообщения: 141
Откуда: Украина
Зарегистрирован: 13.07.2010
CheckerTwo :
У меня все скомпилилось

Значит я чего то не догнал. Огромное преогромное спасибо!!! Буду пробовать. Улыбка Улыбка Улыбка Супер! Супер! Супер! Супер!
_________________
Unreal Engine 4
    Добавлено: 20:56 19-05-2011   
CheckerTwo
 550 EGP


Рейтинг канала: 6(480)
Репутация: 103
Сообщения: 412
Откуда: Tomsk
Зарегистрирован: 18.08.2004
Уж и не знаю, хватит ли меня до конца Улыбка
Итак, коротенькое описание (Введение)

Виртуальная машина Х3 (ВМ Х3).

ВМ Х3 - классическая стековая машина с совмещенным стеком данных и возвратов. То есть, все команды, выполняющие операции над данными, команды вызова функций и возвраты из них используют единый стек.

Стек - вид памяти, который реализует стек с процедурой LIFO (последним зашел - первым вышел). Стек допускает две операции: push и pop. Операция push выделяет новую ячейку на вершине стека и записывает в неё значение. Операция pop - освобождает ячейку. Для работы со стеком используется так называемый указатель стека SP (stack pointer).

Кроме того ВМ Х3 содержит команды для доступа (чтение/запись) к отдельным выделенным ячейкам памяти вне стека данных.

Арифметический блок ВМ осуществляет арифметико-логические операции над данными, хранящимися в стеке данных. Арифметический блок поддерживает следующий набор операций: пересылки, сложение, вычитание, логические функции, тестирование и сравнение. Арифметика может быть целочисленной или плавающей.

Программа ВМ Х3 представляет собой последовательность команд (инструкций). Эти команды "объясняют" ВМ, что она должна делать, точно описывая последовательность действий.

Реальная программа, которую выполняет ВМ Х3, это последовательность единиц и нулей. Эту строку бит принято называть машинным языком. Машинный язык - это тот язык, который машина понимает. ВМ извлекает команды машинного языка из памяти и затем выполняет команду.

Однако машинный язык мало о чем говорит людям. Если вы хотите сложить два числа 1+2 в ВМ Х3, последовательность команда будет выглядеть таким образом:

00000010 00000011 1000110

Эти три байта точно указывают ВМ, какие операции необходимо выполнить, чтобы получить результат. Если вы являетесь компьютером или ВМ, машинный язык - это действительно прекрасно, но он труден для программистов - людей. К счастью, существует более простой способ программирования. Этим методом, более близким людям, чем машинам, является программирование на языке ассемблера.

Чтобы лучше понять разницу, давайте взглянем на пример еще раз. Ассемблерные команды для сложения двух чисел 1+2 просты:

push 1
push 2
add sp[0], sp[1]

Ассемблер превращает эти предложения в бинарную форму, которую мы видели выше. Компьютер сам управляется с проблемой превращения файла с понятным человеку текстом в программу на машинном языке, которую могла бы выполнить ВМ Х3. Обычно каждое утверждение языка ассемблера генерирует одну команду машинного языка. В некоторых случаях это не так, потому что существуют команды, которые не являются частью выполняемой программы, а предназначены для ассемблера. Они описывают действия ассемблера, который должен выполнять их во время компилирования. Пример директивы ассемблера (предназначенной только для него команды) .radix 10d. Эта инструкция задает систему счисления и сообщает ассемблеру, что все числа в программе, с опущенными квалификаторами, будут рассматриваться как десятичные. Эта инструкция имеет смысл только для ассемблера. В ВМ нет команд, которые могли бы выполнить эту операцию.

Ассемблер Х3ТС

Прежде чем двигаться дальше, обсудим синтаксис команд языка ассемблера. Мы должны выделить основные компоненты языка ассемблера, чтобы можно было затем обозначать эти компоненты с помощью стандартных терминов.

Команда языка ассемблера состоит из четырех частей.

Код:

lab1:    popx    10d        ; вытолкнуть из стека 10 ячеек
Метка    ОпКод   Операнды     Комментарии


Единственная обязательная часть команды языка ассемблера - ОпКод (сокращение от ОПерационный КОД), или код операции.

Поле операндов содержит дополнительную информацию о команде. Оно определяется кодом операции. Каждому коду операции должно соответствовать определенное число операндов. Для команды popx требуется один операнд, для некоторых команд, например, команды pop, операнды вообще не нужны.

Метка и комментарии необязательны в команде. Поле метки позволяет обозначить какое-либо конкретное место в памяти компьютера. Говоря технически, поле метки содержит символический указатель расположения команды. Если мы хотим обратиться к этой команде позднее, то мы делаем это через символьное имя метки и нам не требуется указывать абсолютное расположение данной инструкции в памяти ВМ. Использование меток - одна из причин предпочтительности языка ассемблера перед машинным языком. Превращением же символических имен в реальные адреса занимается ассемблер.

Поле комментариев служит для удобства программиста. Программист может использовать это поле для сообщения дополнительной информации о команде. Комментарий не обязательно жестко связан с командой. Вы можете отвести под комментарий целую строку, поставив в ее начале символ ";". Это позволяет программисту включить в текст программы блок собственной информации, к примеру, описание используемого алгоритма. У каждого есть собственное представление о том, как следует комментировать программы.

Рассмотрим теперь работу ассемблера в целом. Детали будут обсуждены позднее. Ассемблер читает программу, написанную на языке ассемблера, из текстового файла и превращает ее в бинарный файл, содержащий инструкции машинного языка ВМ Х3. Файл, который содержит программу на языке ассемблера, называют исходным файлом. Формируемый ассемблером файл является собственно машинным языком, готовым к исполнению ВМ Х3.

Исполняемый файл для ВМ Х3 x3story.obj, как и другие obj, лежащие в папочке 'X3TC\L', представляют собой некий контейнер, содержащий несколько секций. Основная часть - это секция исполняемого кода - CODE, содержащая инструкции ВМ Х3. Далее идут секции SYMB, VARS, CLAS, PTCH и STRG. Секция SYMB содержит описания глобальных функций, VARS - глобальных переменных, CLAS - описание классовых функций и переменных, PTCH - таблицы замещения для системы патчей, STRG - список строк. Для программиста, разрабатывающего программу для ВМ Х3 ассемблер предоставляет полный доступ лишь к секции CODE и STRG. Информация в секциях SYMB, VARS, CLAS модифицируется ассемблером автоматически. Это сделано по той причине, что структуры этих секций достаточно сложны, а малейшая некорректная правка может привести к нестабильности ВМ и падению. В связи с полным отсутствием информации о работе системы патчей секция PTCH тоже недоступна для модификации.

Всякая программа для ВМ Х3, написанная на ассемблере, содержит несколько обязательных частей. Это заголовок программы, определение секций, в которых производится работа, и завершение программы. Программный модуль начинается с директивы .TITLE. Директива .TITLE <любая строка> предназначена фактически для программиста, ибо напоминает ему - что это за файл, для чего и по какому случаю создавался.

Директива .RADIX <num> задает систему счисления по умолчанию. <num> - число, которое задает систему счисления и может быть равно 2, 8, 10 и 16. Нужно отметить, что при инициализации ассемблера устанавливается 16-ричная система счисления, поэтому, чтобы не было глюков и недопонимания, нужно всегда указывать квалификатор системы счисления для <num>. Например .RADIX 10d. При использовании в программе чисел можно использовать квалификаторы системы счисления - буквы, следующие за числом: b (binary), o (octal), d (decimal) или h (hex). Директива .RADIX может быть в любом месте программы.

 Пример - Использование директивы .RADIX   (кликните здесь для просмотра)

Код:

   1                                  .title     Example RADIX
   2                                 
   3                                  .section   code, con
   4                                 
   5                                  .radix     2d
   6   00000001: 0002 0008 000A 0010  .word    10b, 10o, 10d, 10h, 10, 100, 1000, 10000
                 0002 0004 0008 0010
   7                                 
   8                                  .radix     8d
   9   00000011: 0002 0008 000A 0010  .word    10b, 10o, 10d, 10h, 10, 100, 1000, 10000
                 0008 0040 0200 1000
  10                                 
  11                                  .radix     10d
  12   00000021: 0002 0008 000A 0010  .word    10b, 10o, 10d, 10h, 10, 100, 1000, 10000
                 000A 0064 03E8 2710
  13                                 
  14                                  .radix     16d
  15   00000031: 0002 0008 000A 0010  .word    10b, 10o, 10d, 10h, 10, 100, 1000, 10000
                 0010 0100 1000 0000
 pass2:(07.asm:16) Err: Value of word can be within range 0..FFFF.
  16                                   
  17   00000041:                      .end


                Пример - Использование директивы .RADIX
                 (В строке 16 намеренно сделана ошибка)


Директива определения программной секции .section <name>,
<mod>


Как было сказано ранее, ассемблерной программе доступны для модификации две секции - секция кода (CODE) и секция строк (STRG). В зависимости от того, что будет модифицировать патч (код или строковые ресурсы), вы должны указать в поле <name> - code или strg. Модификатор секции <mod> указывает режим, в котором будет производится правка. Если модификатор секции CON (concatenated), то все изменения, внесенные программой будут дописываться в конец секции. Если модификатор ABS (absolute), то внесенные вами данные будут накладываться на те, которые уже были там. То есть перекрывать.

Например, объявление .section code, abs определяет начало секции, в которой размещается код скрипта. Модификатор abs указывает, что мы будем писать поверх кода, который там уже был размещен. При этом счетчик команд устанавливается на начало программной секции. В Х3TC это адрес 00000001h.

Объявление секции .section code, con дает возможность установить счетчик команд в конец секции. Т.е. код, который будет набираться ниже этого объявления, расположится в конце секции CODE, после самой последней команды, которая уже была в этой секции.

.section strg, con - объявляет начало секции строковых ресурсов. Для секции STRG не рекомендуется устанавливать модификатор ABS, потому как в эту секцию автоматически сливаются все имена функций при их объявлении, а также строки, используемые в программе. При ошибке найти причину будет весьма непросто.

Отсутствие директивы .section в программе приведет к ошибке, так как ассемблер не знает куда запихивать код, который написал программист.

Программный модуль всегда должен заканчиваться директивой .end. .end - не команда ВМ Х3. Это инструкция ассемблеру о том, что нужно прекратить трансляцию. Строка, содержащая .end, не транслируется в машинный код.

Помимо преобразования исходного кода в машинный, ассемблер создает несколько других выходных файлов. Один из них - ассемблерный листинг. Он содержит сообщения о действиях ассемблера. Этот файл содержит исходный код вместе с комментариями, а также машинный код, сформированный ассемблером.

 Пример - Небольшая часть листинга ассемблера   (кликните здесь для просмотра)

Код:

3997                          ;
3998                          ; === Patch21 ===================================================
3999                          ; function  PATCH.Patch21();
4000                          ;
4001   00001DB1:              PATCH.Patch21:
4002   00001DB1: 6E 0002                 enter      0, 2
4003   00001DB4: 0E 000A                 readvar    global.ga_GalaxyMapName ; [10d ; 0Ah]
4004   00001DB7: 0B 0001467C             get_strg   S00002E17 ;  ; "x3_universe"
4005   00001DBC: 5B                      if sp[0]<>sp[1] then push 0 else push 1
4006   00001DBD: 34 00001DCA             if sp[0]=0 then jump L00001DE8
4007   00001DC2: 01                      push       0
4008   00001DC3: 2E                      get_object
4009   00001DC4: 85 0000445E             call85     PATCH.Patch21_FixATFStations ; 0001A7AA
4010   00001DC9: 24                      pop
4011   00001DCA: 01           L00001DE8: push       0
4012   00001DCB: 83                      ret

              Пример - Небольшая часть листинга ассемблера,
                   иногда называемого распечаткой.


Взяв кусок листинга некой программы, рассмотрим результаты работы ассемблера. В правой части распечатки находятся исходные команды. В левой части - информация, сгенерированная ассемблером. Первая колонка содержит номер каждой строки распечатки. Ассемблер устанавливает эти номера для исходного файла. Как правило они соотносятся с номерами строк в исходном файле, сформированном текстовым редактором. Во второй колонке содержатся адреса инструкций. Следующая колонка - код команды на машинном языке. Поскольку команды ВМ имеют длину от 1 до 9 байт, это поле будет изменяться в размере.

В большинстве примеров программ мы будем использовать листинг ассемблера.

Другой создаваемый ассемблером файл - файл ошибок. В этот файл ассемблер складывает только сообщения об ошибках. Для больших исходных файлов в несколько тысяч строк довольно трудно отыскать место ошибки в файле листинга.

При обнаружении ошибки в исходном тексте программы, ассемблер не создает файл с бинарным кодом.

Байты, слова и строки

"Битом" называют двоичную цифру, единичное значение 0 или 1. Группу из 8 бит принято называть "байтом". Как мы увидим позднее, отдельные команды ВМ Х3 могут оперировать с байтами. Кроме того, все команды ВМ Х3 - байтовые, то есть имеют опкод размером один байт. Во время тонкого моддинга и правки кода, необходимо иметь возможность для размещения определенного значения в байт памяти. Для таких случаев ассемблер располагает специальным механизмом - директивой .byte. .byte не является командой ВМ Х3. Это команда ассемблеру поместить в память определенное значение.

Директива .byte 10d дает ассемблеру задание сохранить десятичное значение 10 в текущий байт памяти. А директива .byte 1,2,3,4 - сохраняет значения от 1 до 4 по четырем последовательным адресам в памяти.

Аналогично директиве определения байта памяти, существует инструкция для определения слова. Для ассемблера ВМ Х3 слово составляет 16 бит. Первое утверждение .word в нижеприведенном примере определяет 16-битовое слово со значением 1000d.

Еще один тип данных, который может использоваться в программах на языке ассемблера - это двойное слово, значение длиной в 32 бита. ВМ Х3 пользуется двойными словами для хранения адресов и очень больших чисел. Чтобы определить область, содержащую значение двойного слова, нужно использовать директиву ассемблера .long.

Директивы .byte, .word и .long могут использоваться только в секции CODE. Для определения данных в секции STRG нужно использовать директиву .ascii:

.ascii "string"

Директива .ascii размещает в секции STRG указанную строку как последовательный набор байт. В конце строки автоматически добавляется нулевой байт, символизирующий конец строки.

 Примеры определения байт, слов, длинных слов, строк   (кликните здесь для просмотра)

Код:

   1                                  .title     Example byte,word,long,ascii
   2                                  .radix     10d
   3                                 
   4                                  .section   code, con
   5                                 
   6   00000001: 0A                   .byte    10d
   7   00000002: 01 02 03 04 43       .byte    1, 2, 3, 4, 'C'
   8                                 
   9   00000007: 03E8                 .word    1000d
  10   00000009: 0001 0002 0003 0004  .word    1, 2, 3, 4, 'C'
                 0043
  11                                 
  12   00000013: 000186A0             .long    100000d
  13   00000017: 00000001 00000002    .long    1, 2, 3, 4, 'D'
                 00000003 00000004
                 00000044
  14                                 
  15                                  .section strg, con
  16                                 
  17   00000001: 58 33 20 46 6F 72    .ascii "X3 Force"
                 63 65
  18   0000000A: 4E 65 78 74 20 73    .ascii "Next string"
                 74 72 69 6E 67
  19                                 
  20   00000000:                      .end

          Примеры определения байт, слов, длинных слов, строк


Если атрибут секции STRG установлен ABS, но можно поправить строку. Удобно, если нужно поправить буквально несколько байт в строке без перекомпиляции всего obj-а. Например нужно, чтобы при выводе какой-либо строки вместо маленьких букв выводились заглавные.

 Пример - патч строки   (кликните здесь для просмотра)

Код:

           .title     test
           .radix     16d
           .loadobj   "x3story.obj"

           .section   strg, abs

; была такая строка
; 0001AC7E : \t\t<current_patch value=\"%d\" />\r\n
; слово value заменим

$ = 0001AC7Eh
   .ascii   "\t\t<current_patch VALUE=\"%d\" />\r\n"
           .end


Действуют следующие ограничения:
1. адрес начала строки, задаваемый в примере командой $=0001AC7Eh, должен точно соответствовать началу строки в obj-файле. Адреса всех строк можно посмотреть в файле с расширением .str
2. длина строки в патче должна быть не больше, чем в obj-файле. Если строка короче - дополняется нулевыми байтами. Увеличить длину - нельзя.

Работа ВМ Х3 (продолжение)

Как и любая вычислительная машина, ВМ Х3 имеет счётчик инструкций, который хранит адрес следующей выполняемой инструкции. Счётчик может быть загружен для реализации переходов (по команде перехода), или может быть инкрементирован для выборки следующей по порядку инструкции.

При старте ВМ Х3 движек ищет в исполняемом obj-файле функцию "_Init"
и передает ей управление.


Работа ВМ Х3 состоит в последовательной выборке команд из памяти команд и их выполнении. Каждая команда проходит через этот двухшаговый процесс. Выборкой очередной команды в этом цикле управляет один из регистров ВМ. Этот регистр называют счетчиком команд - PC. Место в памяти команд, на которое указывает этот регистр, содержит очередную команду, которую должна будет выбрать и выполнить ВМ. ВМ читает один или несколько байтов, в зависимости от длины команды, и выполняет ее. Затем ВМ увеличивает PC в соответствии с числом байтов в команде. Нормальное выполнение программы является последовательным, от одной команды к другой, расположенной следом.

Для управления процессом трансляции ассемблерной программы в машинный код ВМ Х3 ассемблер тоже имеет счетчик команд. Идентификатор $ ассемблера предназначен для работы с текущим счетчиком команд. Можно в любой момент определить, запомнить или установить новое значение счетчику команд. Например.

 Пример - работа со счетчиком команд   (кликните здесь для просмотра)

Код:

   1                            .title     Example PC
   2                            .radix     10d
   3                            .section   code, abs
   4                 
   5                 ;... писали что-то
   6   00000001      temp_pc = $   ; запомнили место положение
   7   00000101:     $ = $ + 100h  ; сместились на 100h
   8                 ;... еще что-то написали
   9   00000001:     $ = temp_pc   ; восстановили запомненный счетчик команд
  10                 
  11                 ;Для установки текущего счетчика команд можно использовать директиву .org
  12   000000C9:     .org temp_pc + 200
  13                 ;Это эквивалентно выражению
  14   000000C9:     $ = temp_pc + 200
  15                 
  16   000000C9:                .end

                  Пример - работа со счетчиком команд


Следует заметить, что программный счетчик ассемблера - это указатель на текущую ассемблируемую команду. Это не тоже самое, что и программный счетчик ВМ Х3.

ВМ Х3 может изменить последовательность при выполнении команды, которая помещает в PC новое значение. Такие команды являются командами передачи управления, поскольку выполнение программы переходит в новую область. Инструкция перехода или выбора варианта является самым распространенным способом передачи управления. Команда перехода задает адрес команды, которая должна выполняться следующей.

 Пример - Команда безусловного перехода   (кликните здесь для просмотра)

Код:

   1                                      .title     Example
   2                                      .radix     10d
   3                           
   4                                      .section   code, con
   5                           
   6                           .func ExFunc
   7                           
   8   00000001:               ExFunc:
   9   00000001: 6E 0001             enter      0, 1
  10   00000004: 32 0000000C         jump       $01
  11                           ; эти команды никогда не выполняются
  12   00000009: 05 00         $00:  pushb      0d
  13   0000000B: 83                  ret
  14                           ; а эти - выполняются
  15   0000000C: 05 01         $01:  pushb      1d
  16   0000000E: 83                  ret
  17                           
  18   0000000F:                     .end

                Пример - Команда безусловного перехода


Обратите внимание, что в команде jump для определения следующего выполняемого адреса используется текстовая метка, хотя в машинном языке ВМ требуется абсолютный адрес команды. Ассемблер сам вычисляет адрес и ставит правильное значение в команду машинного языка.

Команда перехода не обязательно должна быть безусловной, как в приведенном выше примере. ВМ Х3 имеется несколько команд условного перехода, которые проверяют значение в стеке данных и выполняют переход.

 Пример - Команда условного перехода   (кликните здесь для просмотра)

Код:

                           .title     Example
                           .radix     10d
                           .section   code, con
; global procedures
;   function  ExFunc(arg1);
;
00000001:             |    .func  ExFunc
00000001: 6E 0001     | 0  ExFunc:    enter      1, 1
00000004:             |    ; проверяем значение аргумента, переданного функции         
00000004: 0D 0004     | 0             push       sp[3] ; arg1
00000007: 33 0000000F | 1             if sp[0]<>0 then jump $01
0000000C:             |    ; значение аргумента == 0
0000000C: 05 00       | 0             pushb      0
0000000E: 83          | 1             ret
0000000F:             |    ; значение аргумента != 0
0000000F: 05 01       | 0  $01:       pushb      1
00000011: 83          | 1             ret
                                      .end

                Пример - Команда условного перехода


Команда условного перехода (if sp[0]<>0 then jump) выполняет переход по указанному адресу, если значение в стеке не равно 0. Если условие не выполняется, ВМ Х3 выполняет команду, следующую за условным переходом, в данном случае команду pushb 0. Команда условного перехода позволяет проверить значения данных в процессе выполнения программы. Ход выполнения программы может меняться в зависимости от результатов этой проверки. Другая команда условного перехода (if sp[0]=0 then jump) выполняет переход по указанному адресу, если значение в стеке равно 0.

Команды выбора варианта (xjump и switch) являются более сложным случаем условного перехода по таблице.

Команда xjump вытаскивает из стека проверяемое значение и имеет два операнда. Первый операнд - число вариантов, не считая варианта по умолчанию (в С эквиволент - "default"). Второй операнд - "начальное значение варианта". Второй операнд придуман ES вероятно для уменьшения размера кода и ускорения работы программы, ибо экономит несколько команд.

 Пример - Команда выбора варианта XJUMP   (кликните здесь для просмотра)

Код:

                                           .title     Example
                                           .radix     10d
                                           .section   code, con
; global procedures
;   function  ExFunc(arg1);
;
00000001:                   |   .func  ExFunc
00000001: 6E 0001           | 0 ExFunc:    enter      1, 1
00000004: 0D 0004           | 0            push       sp[3] ; arg1
00000007: 78 0003 0000000A  | 1            xjump      3, 10d
0000000E:    0000001E       | 0            .long      case_else ; default
00000012:    00000021       | 0            .long      case_10   ; arg1 = 10
00000016:    00000024       | 0            .long      case_11   ; arg1 = 11
0000001A:    00000027       | 0            .long      case_12   ; arg1 = 12

0000001E: 05 FF             | 0 case_else: pushb      -1d ; 0FFh
00000020: 83                | 1            ret

00000021: 05 0A             | 0 case_10:   pushb      10d ; 0Ah
00000023: 83                | 1            ret

00000024: 05 14             | 0 case_11:   pushb      20d ; 14h
00000026: 83                | 1            ret

00000027: 05 1E             | 0 case_12:   pushb      30d ; 1Eh
00000029: 83                | 1            ret

                                           .end

                Пример - Команда выбора варианта XJUMP


Команда switch вытаскивает из стека проверяемое значение и просматривает всю таблицу переходов. Если первый операнд команды не равен 0, то он определяет число вариантов, не считая варианта по-умолчанию. При этом таблица переходов состоит из двух чисел - проверяемого значения варианта и адреса перехода.

 Пример - Команда выбора варианта SWITCH (вариант 1)   (кликните здесь для просмотра)

Код:

                                              .title     Example 04
                                              .radix     10d
                                              .section   code, con
; global procedures
; function  ExFunc(arg1);
;
00000001:                      |      .func  ExFunc
00000001: 6E 0001              |    0 ExFunc:    enter      1, 1
00000004: 0D 0004              |    0            push       sp[3] ; arg - число
00000007: 79 0004 0000         |    1            switch      4, 0
0000000C:    00000030          |    0            .long      case_else      ; default
00000010:    00000261 00000033 |    0            .long      609d, case_609
00000018:    00000263 00000036 |    0            .long      611d, case_611
00000020:    000002F8 00000039 |    0            .long      760d, case_760
00000028:    000002F9 0000003C |    0            .long      761d, case_761

00000030: 05 FF                |    0 case_else: pushb      -1d ; 0FFh
00000032: 83                   |    1            ret

00000033: 05 0A                |    0 case_609:  pushb      10d ; 0Ah
00000035: 83                   |    1            ret

00000036: 05 14                |    0 case_611:  pushb      20d ; 14h
00000038: 83                   |    1            ret

00000039: 05 1E                |    0 case_760:  pushb      30d ; 1Eh
0000003B: 83                   |    1            ret

0000003C: 05 28                |    0 case_761:  pushb      40d ; 28h
0000003E: 83                   |    1            ret

                                              .end

                Пример - Команда выбора варианта SWITCH
                   (Первый вариант - сравнение чисел)


Если второй операнд команды не равен 0, то он определяет число вариантов, не считая варианта по-умолчанию. При этом таблица переходов также состоит из двух чисел, но первым числом будут адреса строк в секции STRG с которыми будет сравниваться проверяеме значение варианта, вторым числом - адреса перехода.

 Пример - Команда выбора варианта SWITCH (вариант 2)   (кликните здесь для просмотра)

Код:

   1                                               .title     Example 1
   2                                               .radix     10d
   3                                   
   4                                               .section   code, con
   5                                   
   6                                    .func ExFunc
   7                                   
   8   00000001:                        ExFunc:
   9   00000001: 6E 0001                      enter      1, 1
  10   00000004: 0D 0004                      push       sp[3] ; argument
  11   00000007: 79 0000 0004                 switch      0, 4
  12   0000000C: 00000030                     .long      $00
  13   00000010: 00000008 00000033            .long      str_center,  $01 ; "center"
  14   00000018: 0000000F 00000036            .long      str_justify, $02 ; "justify"
  15   00000020: 00000017 00000039            .long      str_left,    $03 ; "left"
  16   00000028: 0000001C 0000003C            .long      str_right,   $04 ; "right"
  17                                   
  18   00000030: 05 FF                  $00:  pushb      -1d
  19   00000032: 83                           ret
  20                                   
  21   00000033: 05 0A                  $01:  pushb      10d
  22   00000035: 83                           ret
  23                                   
  24   00000036: 05 14                  $02:  pushb      20d
  25   00000038: 83                           ret
  26                                   
  27   00000039: 05 1E                  $03:  pushb      30d
  28   0000003B: 83                           ret
  29                                   
  30   0000003C: 05 28                  $04:  pushb      40d
  31   0000003E: 83                           ret
  32   0000003F:                        end:
  33                                   
  34                                          .section  strg, con
  35                                   
  36   00000008: 63 65 6E 74 65 72      str_center:  .ascii  "center"
  37   0000000F: 6A 75 73 74 69 66      str_justify: .ascii  "justify"
                 79
  38   00000017: 6C 65 66 74            str_left:    .ascii  "left"
  39   0000001C: 72 69 67 68 74         str_right:   .ascii  "right"
  40                                         
  41   00000000:                              .end

                Пример - Команда выбора варианта SWITCH
                  (Второй вариант - сравнение строк)


Локальные метки.

Сложные программы, включающие большое число команд переходов и подпрограмм, могут служить хорошей тренировкой изобретательности программиста в создании имен. В языке ассемблера ВМ Х3 имеются специальные "повторноиспользуемые" метки. Рассмотрим еще раз предыдущий пример, в котором используется команда switch. Метки $00, $01 и т.д. являются такого рода "локальнымми метками". Локальные метки должны иметь вид - символ '$', за которым следует число. На локальные метки можно ссылаться точно так же, как и на "обычные метки", но только в том случае, если между строкой, содержащей локальную метку, и командой, на нее ссылающейся, нет обычной метки. В нашем примере метки $00, $01, $02, $03, $04 являются локальными по отношению к части программы между метками ExFunc и end. Вне их они вообще восприниматься не будут. Поэтому в командах, расположенных после метки end или до метки ExFunc (или и там, и там), можно использовать другие метки $00 (и пр.) без боязни двусмысленности. Любая подобная ссылка интерпретируется ассемблером как ссылка на метку, расположенную в текущем локальном блоке программы, и поэтому соответствующим образом кодируется.

Взаимодействие ВМ Х3 с внешним миром.

ВМ Х3 взаимодействует с внешним миром исключительно через специально организованное API. С точки зрения ассемблера основой этого интерфейса является специализированная команда ВМ - callasm <name_func>, которая вызывает встроенные в игровой движек служебные функции. Для использования этих специализированных функций программист должен сначала объявить функции специальной директивой .extern.

Например .extern SE_ArrayAlloc заносит имя системной функции SE_ArrayAlloc в таблицы ассемблера и секцию STRG, позволяя в дальнейшем выполнить команду callasm SE_ArrayAlloc

 Пример - Использование функций API   (кликните здесь для просмотра)

Код:

 801                            ;
 802                            ; === IncreaseTimeWarp ==========================================
 803                            ; procedure DEBUG.IncreaseTimeWarp();
 804                            ;
 805                            .extern  TI_SetTimeWarpFactor, TI_GetTimeWarpFactor
 806   00000468:                DEBUG.IncreaseTimeWarp:
 807   00000468: 6E 0002                   setmem     2
 808   0000046B: 01                        push       0
 809   0000046C: 82 00003123               callasm    TI_GetTimeWarpFactor ; 00000270
 810   00000471: 07 00100000               pushd      1048576d ; 00100000h
 811   00000476: 5C                        if SP[0]<=SP[1] then push 0 else push 1
 812   00000477: 34 0000048B               if SP[0]=0 then jump L00000490
 813   0000047C: 01                        push       0
 814   0000047D: 82 00003123               callasm    TI_GetTimeWarpFactor ; 00000270
 815   00000482: 03                        push       2
 816   00000483: 50                        mul        SP[0],SP[1]
 817   00000484: 02                        push       1
 818   00000485: 82 00003145               callasm    TI_SetTimeWarpFactor ; 00000285
 819   0000048A: 24                        pop
 820   0000048B: 01             L00000490: push       0
 821   0000048C: 83                        ret

    Пример - Использование функций API TI_SetTimeWarpFactor, TI_GetTimeWarpFactor


Директивы объявления переменных

ВМ Х3 - стековая машина и бОльшая часть команд этой машины ориентирована на работу с данными, расположенными в стеке данных. Однако хранение и обработка переменных и больших массивов данных, расположенных в стеке не очень эффективна, а доступ к ним затруднен из-за отсутствия в ВМ базовых указателей. Для решения этой проблемы ВМ Х3 имеет ряд специализированных команд для доступа к этой, специально организованной памяти данных. Переменные в ВМ Х3 типизированные. Это означает, что ВМ знает какой тип переменной лежит в стеке, соответственно несколько изменяется и поведение команд в зависимости от ее типа. Например, команда add для целых чисел будет выполнять целочисленное сложение, а для строковых - выполняется конкатенация. В ассемблере присутствуют специальные директивы, которые объявляют переменные в памяти данных:

.var <name> [= <expr>] ; объявление переменной
.vfixed <name> [= <expr>] ; объявление вещественной переменной
.vint <name> [= <expr>] ; целая переменная
.varray <name> [= <expr>] ; указатель на массив
.vtable <name> [= <expr>] ; указатель на таблицу
.vstring <name> [= <label>] ; указатель на строку
.vstring <name> [= <string>] ; строка

Часть, которая определяет значение переменной при инициализации можно опускать. Информация о глобальной переменной, объявленной одной из этих директив, заносится ассемблером в таблицы VARS. Информация о классовой переменной - в таблицы CLAS. Значения строковых переменных будут размещаться в таблицах STRG автоматически.

Работа с переменными.

В ВМ Х3 определены 6 типов переменных - целые, вещественные, строковые, массивы, таблицы и еще какие-то. Эти "еще какие-то" появились в v3.0, но что собой представляют - пока не понятно.

 Пример - использование глобальных и классовых переменных    (кликните здесь для просмотра)

Код:

   1                                 .title     Example VARS
   2                                 .radix     10d
   3                                 .section   code, con
   4                     
   5                      .vint      i1              ; объявляем глобальную переменную
   6                      .vstring   s1 = "string"   ; глобальная строковая переменная
   7                     
   8                      ;
   9                      ; простая функция, которая читает значение глобальной переменной
  10                      ;
  11                      .func GetValue
  12   00000001: 6E 0001  GetValue: enter    0, 1 ;
  13   00000004: 0E 0000            readvar  i1   ; читаем глобальную переменную в стек
  14   00000007: 83                 ret           ; функция возвращает значение i1
  15                      ;
  16                      ; Эта функция записывает число 100 в глобальную переменную
  17                      ; и возвращает его в качестве результата
  18                      ;
  19                      .func SetValue
  20   00000008: 6E 0001  SetValue: enter    0, 1 ;
  21   0000000B: 05 64              push     100d ; sp[0] <- 100d
  22   0000000D: 15 0000            writevar i1   ; i1 <- sp[0], команде не изменяет указатель стека
  23   00000010: 24                 pop           ;   поэтому восстанавливаем указатель стека ручками
  24   00000011: 0E 0000            readvar  i1   ; читаем значение из переменной
  25   00000014: 83                 ret           ; функция возвращает его же
  26                      ;
  27                      ; Теперь рассмотрим как работать с переменными, объявленными в классе
  28                      ;
  29                      .classdef Test, 10000d
  30                        .vint  i1           ; объявили целочисленную переменную
  31                        .func  GetValue     ; эта функция будет читать переменную
  32                        .func  SetValue     ; а эта записывать в нее
  33                      .endclass Test
  34                      ;
  35                      ; функция, принадлежащая классу Test, которая просто читает число,
  36                      ; записанное в классовой переменной Test.i1
  37                      ;
  38   00000015:          Test.GetValue:
  39   00000015: 6E 0001            enter    0, 1
  40   00000018: 0F 0000            read     Test.i1   ; sp[0] <- i1
  41   0000001B: 83                 ret                ; result = Test.i1
  42                      ;
  43                      ; функция записывает 100 в классовую переменную,
  44                      ; но читает глобальную переменную и возвращает ее значение.
  45                      ;
  46   0000001C:          Test.SetValue:
  47   0000001C: 6E 0001            enter    0, 1      ;
  48   0000001F: 05 64              push     100d      ; sp[0] <- 100d
  49   00000021: 16 0000            write    Test.i1   ; Test.i1 <- sp[0]
  50   00000024: 24                 pop                ; восстанавливаем стек
  51   00000025: 0E 0000            readvar  i1        ; читаем глобальную i1 в sp[0]
  52   00000028: 83                 ret                ; result = global.i1
  53                     
  54   00000029:                    .end

         Пример - использование глобальных и классовых переменных


Команды write, writevar не изменяют стек.

Команда сохранения значения в переменной write/writevar не изменяет указатель стека. Это может быть использовано для присвоения одного и того же значения нескольким переменным.

 Пример - быстрая инициализация переменных   (кликните здесь для просмотра)

Код:

   1                       .title     Example MultiInit
   2                       .radix     10d
   3                       .section   code, con
   4                       
   5                       ; обявляем класс TClass, который имеет
   6                       ; уникальный идентификационный номер 10000d
   7                       .classdef TClass, 10000d
   8                         .vint  test1
   9                         .vint  test2
  10                         .vint  test3
  11                         .vint  test4
  12                         .func  prc1  ; объявляем функцию класса TClass.prc1
  13                       .endclass TClass
  14                       
  15   00000001:           TClass.prc1:
  16   00000001: 6E 0001            enter   0, 1
  17   00000004: 01                 push    0h              ; значение 0h засылается в стек
  18   00000005: 16 0000            write   TClass.test1    ; переменные test1, test2, test3, test4
  19   00000008: 16 0001            write   TClass.test2    ;  объекта TClass инициализируются
  20   0000000B: 16 0002            write   TClass.test3    ;  одним значением
  21   0000000E: 16 0003            write   TClass.test4    ;
  22   00000011: 24                 pop                     ; очистить стек
  23                       
  24   00000012: 01                 push    0
  25   00000013: 83                 ret
  26                       
  27   00000014:           .end


Этот же способ может быть использован для инициализации глобальных переменных с помощью команды writevar.

При объявлении переменной ей может быть назначено начальное значение. Начальные значения всех переменных заносятся в таблицы VARS (или CLAS/VARS для классовых переменных). Изменить начальное значение переменной, уже существующей в obj-файле, можно, использовав следующий трюк.

 Пример - правка начальных значений переменных   (кликните здесь для просмотра)

Код:

   1                          .title     Example MultiInit
   2                          .radix     10d
   3                          .loadobj   "x3story.obj"
   4                          ; объявляем секцию code как абсолютную!
   5                          .section   code, abs
   6               
   7               ; меняем начальное значение глобальной переменной   
   8               .vint   TGlobal_3 = 101d
   9               
  10               .classdef X_AUDIO
  11                 ; меняем начальное значение классовой переменной
  12                 .vint   au_ErrorCount = 103d
  13               .endclass X_AUDIO
  14               
  15   00000001:           .end


Секция CODE должна быть с атрибутом ABS. Числовой идентификатор класса задавать не нужно. Он сам вытаскивается из обжа. Имена класса и переменной должны строго соответствовать тем, что нашел дизассемблер. Важно также, что бы у дизассемблера и ассемблера были одинаковые форматы вывода, например DEC - десятичный. Для этого в .ini-файле дизассемблера проверить и установить ключ OutFmt = DEC, а в .ini-файле ассемблера TypeName = DEC.

Функции.

Под функцией понимают некоторую последовательность команд, оформленную в отдельный логический блок. Эта последовательность команд, которая может выполняться в программе неоднократно и в самых разных местах или вызываться единственный раз за все время работы программы. Очень часто большие и сложные программы специально разбивают на небольшие куски кода, которые гораздо легче понять и отладить. Однако, основное достоинство функций заключается в том, что однажды отлаженная функция может многократного использоваться в разных местах программы. Такая часть программы становится подпрограммой или функцией.

В ВМ Х3 процедуры, как таковые отсутствуют, поэтому будем стараться избегать этого термина. В случае, если он встретится в тексте, то можете считать, что речь идет о функции ВМ Х3.

Каждый раз, когда программе потребуется выполнить работу, возложенную на некоторую функцию, она передает ей управление командой вызова. Переход на функцию называется вызовом функции или командой вызова. Вызов функции отличается от команды перехода, рассмотренный нами ранее. Команда вызова функции сохраняет адрес следующей за ней команды в стеке. Этот адрес, называемый адресом возврата, указывает дорогу обратно к исходной последовательности команд.

В ассемблере существует специальная директива объявления функции .func <name>. Эта директива заносит указанный идентификатор (имя) в таблицу STRG. Дополнительные параметры функции (адрес, размер стека, число фактических параметров), которые необходимы для полноценного формирования структур SYMB, ассемблер получает от команды enter <num_args>,<size_stack>. Только после этого считается, что функция полностью определена. Команда enter - первая команда любой функции. Отсутствие команды enter рассматривается ассемблером как ошибка. Кроме того, это единственная команда, которая требует наличия метки. Идетификатор метки команды enter интерпретируется как имя функции, а ее адрес - как адрес функции.

Передача параметров функции: Глобальные функции.

В ВМ Х3 функции бывают разные. И вызов функции зависит от ее специфики. Если функция глобальная, то она должна вызываться командой call84 <addr>.

Давайте посмотрим, как работает вызов функции. Предположим, например, нам надо написать программу, которая к какому-либо числу прибавляет 100 в нескольких местах. Не будем пока задумываться над тем, насколько убога данная функция. Все-таки мы рассматриваем умозрительный пример. А на простых примерах учиться проще. В примере ниже написана глобальная функция "Add100". Число, к которому эта функция будет прибавлять 100, является аргументом этой функции и для его передачи мы используем стек. Стек часто используется для передачи информации в подпрограммы и из них. Для доступа к аргументам функция использует регистр ВМ Х3 sp, как указатель на область стека. Все аргументы доступны как смещения относительно регистра sp[x], где x - смещение.

При выполнении функции Add100 команда push sp[3] извлекает из стека аргумент, следующая команда добавляет в стек число 100, затем команда add извлекает из стека эти два числа и складывает их, полученный результат записывается в стек.

Последняя команда функции ret является специальной командой и называется возвратом. Но прежде чем выполнение вернется к основной программе, команда ret запоминает значение из вершины стека (результат работы функции), находит в стеке число аргументов функции (хранится в sp[2]), удаляет из стека все аргументы и записывает в стек результат работы функции. После этого команда возврата берет адрес, который был сохранен командой вызова функции и помещает его в указатель команд ВМ Х3. Это заставляет программу вернуться к команде, следующей за вызовом процедуры.

 Пример - вызов глобальной функции   (кликните здесь для просмотра)

Код:

;
; Вход в функцию Add100
; Простая функция, которая вытаскивает значение переданного ей параметра
; и увеличивает его на 100
;
.func   Add100
Add100:
        enter     1, 2          ; функция имеет 1 аргумент, использует 2 ячейки стека
;
; вытащим на вершину стека аргумент функции
        push      SP[3]         ; это значение <arg1>, переданное нашей функции
        push      100d          ; число 100
        add       SP[0], SP[1]  ; сложить SP[1] + SP[0] -> SP[0]
;
; число в стеке SP[0] будет возвращено вызывающей программе
        ret                     ; result = arg1 + 100

;
; Вход в функцию Const100
; Простая функция, которая не имеет аргументов
; возвращает число 100
;
.func   Const100
Const100:
        enter     0, 1          ; функция не имеет аргументов, использует 1 ячейку стека
        push      100d          ; число 100
; число в стеке SP[0] будет возвращено вызывающей программе
        ret                     ; result = 100

; основная программа
; вызов объявленной ранее функции, будет выглядеть так:
.....
        push      <arg1>  ; аргумент (параметр) функции
        push      1       ; число фактических аргументов функции
        call84    global.Add100
        pop               ; в стеке был результат функции

; если функция не имеет аргументов, то она должна вызываться вот так:

        push      0       ; у функции нет аргументов
        call84    global.Const100
        pop               ; в стеке был результат функции
.....



Если функция ничего не должна возвращать, все равно перед возвратом из функции нужно в стек записать что-нибудь.

Минимально возможная функция выглядит так:
Код:

.func   SuperSimple
SuperSimple:
        enter     0, 1
; функция всегда возвращает результат, даже когда он никому не нужен.
        push      0             ;
        ret                     ; возврат из функции


Не бывает функций, которые "ничего" не возвращают. Все функции возвращают "что-то" в стеке. Поэтому после вызова функции нужно как минимум извлечь это "что-то" из стека.

Продолжение следует...
---
Edit: Исправлены грамматические ошибки и необоснованные измышлизмы по замечаниям Улыбка
mistaqur :

В SP[0] и SP[1] расположены адрес возврата и указатель на объект, которые имеют собственный тип, с которым нельзя работать.


Последний раз редактировалось: CheckerTwo (20:03 26-01-2012), всего редактировалось 8 раз(а)
    Добавлено: 20:38 20-05-2011   
CheckerTwo
 550 EGP


Рейтинг канала: 6(480)
Репутация: 103
Сообщения: 412
Откуда: Tomsk
Зарегистрирован: 18.08.2004
Передача параметров функции: Глобальные функции. Продолжение.

Для того, чтобы разобраться как передаются аргументы в функцию напишем довольно большую и, может быть, сложноватую для новичков программу. Но она может быть использована как стартовая площадка для экспериментов. Итак, в качестве основы наших исследований возьмем файл "x3config.obj", который лежит в папке "X3TC\L\". Именно этот файл как нельзя лучше подходит для небольших исследований. Причин две. Первая - это файл маленький! Его модификация не требует глубоких знаний устройства ВМ Х3. А вторая - он срабатывает сразу же, как только запустили exe файл. Так что можно тут же выходить из игры! Т.е. все происходит очень быстро. Для отладки небольших функций, которым не нужны ресурсы игры, просто идеально. В этом obj-модуле есть две функции - "_Init" и "LoadText". При старте игры ВМ Х3 загружает этот модуль и запускает функцию "_Init". Добавим в нее вызов своей функции "TestStack". "TestStack" заполняет стек числами и вызывает небольшую подпрограмму, которая аккуратно переписывает значения, хранящиеся в стеке в глобальные переменные t0..t6. После чего содержимое этих переменных уже функцией "SaveFile" записывается в файл на диске, рядом с exe.

 Пример - программа анализа стека в функциях   (кликните здесь для просмотра)

Код:

   1                                  .title     x3config.obj
   2                                  .radix     10d
   3                                  .extern    SE_ReadFile, SE_SPrintf, SE_TableAlloc, SE_XMLImportText
   4                                  .section   code, con
   5                           
   6                            ; global variables
   7                            .vint      FONT_OCR;
   8                            .vtable    FONT_LARGE;
   9                            .vint      gs_TextFileTable = -2147483648d;
  10                            .vint      TGlobal_3 = -2147483648d;
  11                            .vint      TGlobal_4 = -2147483648d;
  12                            .vint      gs_DelayFactor = -2147483648d;
  13                            .vint      ga_Sectors = -2147483648d;
  14                            .vint      ga_Races = -2147483648d;
  15                            .vint      ga_Player = -2147483648d;
  16                            .vint      ga_PlayerShip = -2147483648d;
  17                            .vint      ga_GalaxyMapName = -2147483648d;
  18                            .vint      gs_DebugDump = -2147483648d;
  19                            .vint      gs_DebugInit = -2147483648d;
  20                            .vint      gs_DebugInput = -2147483648d;
  21                            .vint      gs_DebugLogic = -2147483648d;
  22                            .vint      gs_DebugHTML = -2147483648d;
  23                            .vint      gs_DebugMonitors = -2147483648d;
  24                            .vint      gs_DebugComp = -2147483648d;
  25                            .vint      gs_DebugTrade = -2147483648d;
  26                            .vint      gs_DebugCreateObject = -2147483648d;
  27                            .vint      gs_DebugKilled;
  28                            .vint      gs_DebugHangarStart = -2147483648d;
  29                           
  30                            ; global procedures
  31                            .func      _Init;
  32                            .func      LoadText;
  33                            ;
  34                            ; === _Init =====================================================
  35                            ; function  _Init(arg1);
  36                            ;
  37   00000001:                _Init:
  38   00000001: 6E 0003              enter      1, 3
  39   00000004: 01                   push       0
  40   00000005: 82 00000018          callasm    SE_TableAlloc ; 00000007
  41   0000000A: 15 0001              writevar   global.FONT_LARGE ; [1]
  42   0000000D: 24                   pop
  43   0000000E: 03                   push       2
  44   0000000F: 0D 0005              push       SP[4] ; arg1
  45   00000012: 03                   push       2
  46   00000013: 84 00000022          call84     global.LoadText ; 0000001B
  47   00000018: 24                   pop
  48                           
  49   00000019: 01                   push       0
  50   0000001A: 84 00000158          call84     global.TestStack
  51   0000001F: 24                   pop
  52                                 
  53   00000020: 01                   push       0
  54   00000021: 83                   ret
  55                            ;
  56                            ; === LoadText ==================================================
  57                            ; function  LoadText(arg1, arg2);
  58                            ;
  59   00000022:                LoadText:
  60   00000022: 6E 0005              enter      2, 5
  61   00000025: 0D 0005              push       SP[4] ; arg2
  62   00000028: 0B 000000B9          get_strg   S0000001E ; "t/%04d.xml"
  63   0000002D: 03                   push       2
  64   0000002E: 82 0000000D          callasm    SE_SPrintf ; 00000029
  65   00000033: 0D 0001              push       SP[0] ; loc1
  66   00000036: 02                   push       1
  67   00000037: 82 00000001          callasm    SE_ReadFile ; 00000034
  68   0000003C: 0D 0001              push       SP[0] ; loc2
  69   0000003F: 64                   if SP[0]=0 then push 1 else push 0
  70   00000040: 34 0000004D          if SP[0]=0 then jump L00000046
  71   00000045: 01                   push       0
  72   00000046: 14 0003              mov        SP[2],SP[0] ; loc1
  73   00000049: 23 0002              popx       2
  74   0000004C: 83                   ret
  75                           
  76   0000004D:                L00000046:
  77   0000004D: 0D 0001              push       SP[0] ; loc2
  78   00000050: 02                   push       1
  79   00000051: 82 00000026          callasm    SE_XMLImportText ; 00000040
  80   00000056: 24                   pop
  81   00000057: 0D 0002              push       SP[1] ; loc1
  82   0000005A: 0D 0008              push       SP[7] ; arg2
  83   0000005D: 0E 0001              readvar    global.FONT_LARGE ; [1]
  84   00000060: 17                   put_to_array
  85   00000061: 24                   pop
  86   00000062: 02                   push       1
  87   00000063: 14 0003              mov        SP[2],SP[0] ; loc1
  88   00000066: 23 0002              popx       2
  89   00000069: 83                   ret
  90                           
  91                            ;
  92                            ; в эти глобальные переменные мы будем
  93                            ; складывать то, что хранилось в стеке
  94                            ;
  95                            .vint   t0 = -1
  96                            .vint   t1 = -1
  97                            .vint   t2 = -1
  98                            .vint   t3 = -1
  99                            .vint   t4 = -1
 100                            .vint   t5 = -1
 101                            .vint   t6 = -1
 102                            ;
 103                            ; функция сохранения стека в глобальных переменных
 104                            ;
 105                            .func SaveStack
 106   0000006A:                SaveStack:
 107   0000006A: 6E 0001              enter      3, 1
 108                                 
 109   0000006D: 0D 0001              push       SP[0]
 110   00000070: 15 0016              writevar   global.t0
 111   00000073: 24                   pop
 112                                 
 113   00000074: 0D 0002              push       SP[1]
 114   00000077: 15 0017              writevar   global.t1
 115   0000007A: 24                   pop
 116                                 
 117   0000007B: 0D 0003              push       SP[2]       ; число аргументов функции
 118   0000007E: 15 0018              writevar   global.t2
 119   00000081: 24                   pop
 120                           
 121   00000082: 0D 0004              push       SP[3]       ;
 122   00000085: 15 0019              writevar   global.t3
 123   00000088: 24                   pop
 124                                 
 125   00000089: 0D 0005              push       SP[4]       ;
 126   0000008C: 15 001A              writevar   global.t4
 127   0000008F: 24                   pop
 128                                 
 129   00000090: 0D 0006              push       SP[5]       ;
 130   00000093: 15 001B              writevar   global.t5
 131   00000096: 24                   pop
 132                                 
 133   00000097: 0D 0007              push       SP[6]       ; не хорошо читать данные, которые не передавались
 134   0000009A: 15 001C              writevar   global.t6   ; функции - можно налететь на необнаруживаемую ошибку
 135   0000009D: 24                   pop
 136                                 
 137   0000009E: 05 00                pushb      0
 138   000000A0: 83                   ret
 139                            ;
 140                            ; запись значений переменных в файл
 141                            ;
 142                            .extern  SE_WriteFile, SE_SPrintf
 143                            .func SaveFile
 144   000000A1:                SaveFile:
 145   000000A1: 6E 0005              enter      0, 5
 146   000000A4: 0B 00000070          get_strg   S_XC_B_1     ; loc1 <-- ""
 147                                 
 148   000000A9: 0D 0001              push       SP[0]        ; <-- loc1
 149   000000AC: 0E 0016              readvar    t0
 150   000000AF: 0B 00000071          get_strg   S_XC_B_2     ; <-- "t0: %d\r\n"
 151   000000B4: 03                   push       2
 152   000000B5: 82 0000000D          callasm    SE_SPrintf
 153   000000BA: 46                   add        SP[0],SP[1]
 154   000000BB: 14 0002              mov        SP[1],SP[0]  ; --> loc1
 155   000000BE: 24                   pop       
 156                                 
 157   000000BF: 0D 0001              push       SP[0]        ; <-- loc1
 158   000000C2: 0E 0017              readvar    t1
 159   000000C5: 0B 0000007A          get_strg   S_XC_B_3     ; <-- "t1: %d\r\n"
 160   000000CA: 03                   push       2
 161   000000CB: 82 0000000D          callasm    SE_SPrintf
 162   000000D0: 46                   add        SP[0],SP[1]
 163   000000D1: 14 0002              mov        SP[1],SP[0]  ; --> loc1
 164   000000D4: 24                   pop       
 165                                 
 166   000000D5: 0D 0001              push       SP[0]        ; <-- loc1
 167   000000D8: 0E 0018              readvar    t2
 168   000000DB: 0B 00000083          get_strg   S_XC_B_4     ; <-- "t2: %d\r\n"
 169   000000E0: 03                   push       2
 170   000000E1: 82 0000000D          callasm    SE_SPrintf
 171   000000E6: 46                   add        SP[0],SP[1]
 172   000000E7: 14 0002              mov        SP[1],SP[0]  ; --> loc1
 173   000000EA: 24                   pop       
 174                                 
 175   000000EB: 0D 0001              push       SP[0]        ; <-- loc1
 176   000000EE: 0E 0019              readvar    t3
 177   000000F1: 0B 0000008C          get_strg   S_XC_B_5     ; <-- "t3: %d\r\n"
 178   000000F6: 03                   push       2
 179   000000F7: 82 0000000D          callasm    SE_SPrintf
 180   000000FC: 46                   add        SP[0],SP[1]
 181   000000FD: 14 0002              mov        SP[1],SP[0]  ; --> loc1
 182   00000100: 24                   pop       
 183                                 
 184   00000101: 0D 0001              push       SP[0]        ; <-- loc1
 185   00000104: 0E 001A              readvar    t4
 186   00000107: 0B 00000095          get_strg   S_XC_B_6     ; <-- "t4: %d\r\n"
 187   0000010C: 03                   push       2
 188   0000010D: 82 0000000D          callasm    SE_SPrintf
 189   00000112: 46                   add        SP[0],SP[1]
 190   00000113: 14 0002              mov        SP[1],SP[0]  ; --> loc1
 191   00000116: 24                   pop       
 192                                 
 193   00000117: 0D 0001              push       SP[0]        ; <-- loc1
 194   0000011A: 0E 001B              readvar    t5
 195   0000011D: 0B 0000009E          get_strg   S_XC_B_7     ; <-- "t5: %d\r\n"
 196   00000122: 03                   push       2
 197   00000123: 82 0000000D          callasm    SE_SPrintf
 198   00000128: 46                   add        SP[0],SP[1]
 199   00000129: 14 0002              mov        SP[1],SP[0]  ; --> loc1
 200   0000012C: 24                   pop       
 201                           
 202   0000012D: 0D 0001              push       SP[0]        ; <-- loc1
 203   00000130: 0E 001C              readvar    t6
 204   00000133: 0B 000000A7          get_strg   S_XC_B_8     ; <-- "t6: %d\r\n"
 205   00000138: 03                   push       2
 206   00000139: 82 0000000D          callasm    SE_SPrintf
 207   0000013E: 46                   add        SP[0],SP[1]
 208   0000013F: 14 0002              mov        SP[1],SP[0]  ; --> loc1
 209   00000142: 24                   pop       
 210                           
 211   00000143: 0D 0001              push       SP[0]        ; <-- loc1
 212   00000146: 0B 000000B0          get_strg   S_XC_B_9     ; <-- "test.log"
 213   0000014B: 03                   push       2
 214   0000014C: 82 00000050          callasm    SE_WriteFile
 215   00000151: 24                   pop       
 216   00000152: 01                   push       0
 217   00000153: 14 0002              mov        SP[1],SP[0]
 218                           
 219   00000156: 24                   pop       
 220   00000157: 83                   ret       
 221                            ;
 222                            ; тестовая функция, на базе которой мы исследуем
 223                            ; содержимое стека при вызове функции
 224                            ;
 225                            .func TestStack
 226   00000158:                TestStack:
 227   00000158: 6E 000B              enter      0, 11
 228                                 
 229   0000015B: 07 A0B0C0D0          pushd      0A0B0C0D0h
 230   00000160: 07 A1B1C1D1          pushd      0A1B1C1D1h
 231   00000165: 07 A2B2C2D2          pushd      0A2B2C2D2h
 232   0000016A: 07 A3B3C3D3          pushd      0A3B3C3D3h
 233   0000016F: 07 A4B4C4D4          pushd      0A4B4C4D4h
 234   00000174: 07 A5B5C5D5          pushd      0A5B5C5D5h
 235   00000179: 07 A6B6C6D6          pushd      0A6B6C6D6h
 236   0000017E: 23 0002              popx       2
 237   00000181: 07 A7B7C7D7          pushd      0A7B7C7D7h       ; sp[5]
 238   00000186: 07 A8B8C8D8          pushd      0A8B8C8D8h       ; sp[4]
 239   0000018B: 07 A9B9C9D9          pushd      0A9B9C9D9h       ; sp[3]
 240   00000190: 04                   push       3                ; sp[2]
 241   00000191: 84 0000006A          call84     global.SaveStack
 242   00000196: 24                   pop
 243   00000197: 23 0004              popx       4
 244                           
 245   0000019A: 01                   push       0
 246   0000019B: 84 000000A1          call84     global.SaveFile
 247   000001A0: 24                   pop
 248                           
 249   000001A1: 83                   ret
 250                                 
 251                            .section   strg, con
 252                                   
 253   00000070:                        S_XC_B_1:  .ascii ""
 254   00000071: 74 30 3A 20 25 78      S_XC_B_2:  .ascii "t0: %x\r\n"
                 0D 0A
 255   0000007A: 74 31 3A 20 25 78      S_XC_B_3:  .ascii "t1: %x\r\n"
                 0D 0A
 256   00000083: 74 32 3A 20 25 78      S_XC_B_4:  .ascii "t2: %x\r\n"
                 0D 0A
 257   0000008C: 74 33 3A 20 25 78      S_XC_B_5:  .ascii "t3: %x\r\n"
                 0D 0A
 258   00000095: 74 34 3A 20 25 78      S_XC_B_6:  .ascii "t4: %x\r\n"
                 0D 0A
 259   0000009E: 74 35 3A 20 25 78      S_XC_B_7:  .ascii "t5: %x\r\n"
                 0D 0A
 260   000000A7: 74 36 3A 20 25 78      S_XC_B_8:  .ascii "t6: %x\r\n"
                 0D 0A
 261   000000B0: 74 65 73 74 2E 6C      S_XC_B_9:  .ascii "test.log"
                 6F 67
 262                                   
 263   000000B9: 74 2F 25 30 34 64      S0000001E: .ascii "t/%04d.xml"
                 2E 78 6D 6C
 264                                   
 265   00000000:                .end


После запуска exe получаем файл "test.log" вот с таким содержанием:
Код:

t0: 0        = sp[0]
t1: 0        = sp[1]
t2: 3        = sp[2]
t3: a9b9c9d9 = sp[3]
t4: a8b8c8d8 = sp[4]
t5: a7b7c7d7 = sp[5]
t6: a4b4c4d4 = sp[6] - функция не должна обращаться к этому смещению! только для теста

Я немного модифицировал этот файлик, добавив комментарии "sp[n]", для того чтобы было удобно разбираться.

Итак, при вызове функции "SaveStack" для доступа к первому аргументу мы должны использовать смещение указателя стека sp[3], для второго - sp[4] и так далее. В стеке со смещение sp[2] содержится число аргументов функции. sp[1] содержит нечитаемое значение идентификатора класса, sp[0] - адрес возврата, значение которого тоже нельзя прочитать (читаются всегда нулевые значения).

Методы классов.

Директива объявления класса
.CLASSDEF <name> , <num>
.CLASSDEF <name> ( <name_parent> ) , <num>
.ENDCLASS <name>

Директива .CLASSDEF объявляет класс, имя которого задается идентификатором <name>. В скобках может указываться родительский класс - <name_parent>. Кроме того, обязательно должен быть указан уникальный числовой идентификационный номер класса.

 Пример - объявление классовых функций   (кликните здесь для просмотра)

Код:

   1                                 .title     Example FUNC
   2                                 .radix     10d
   3                                 .loadobj  "x3story.obj"
   4                                 .section   code, con
   5                     
   6                      .vint test1  ; объявлена глобальная переменная
   7                     
   8                      ; обявляем класс TClass, который является наследником от TSHIP и имеет
   9                      ; уникальный идентификационный номер 10000d
  10                      .classdef TClass(TSHIP), 10000d
  11                        .vint  var1  ; объявляем переменную класса TClass.var1
  12                        .func  prc1  ; объявляем функцию класса TClass.prc1
  13                      .endclass TClass
  14                     
  15                      ; Теперь обязательно должна быть определена точка входа в функцию
  16                      ; TClass.prc1. Причем первой командой должна стоять enter
  17                      ; Команда enter проверяет наличие метки, имени класса и имени функции
  18   00188EF0:          TClass.prc1:
  19   00188EF0: 6E 0001           enter   0, 1
  20   00188EF3: 01                push    0
  21   00188EF4: 83                ret
  22                     
  23                      ;Для модификации (дополнения) существующих классов новыми переменными и/или
  24                      ;функциями нужно использовать директиву .CLASSDEF без параметров. Например:
  25                     
  26                      .classdef GONER
  27                        .vint   NewVariable
  28                        .func   NewFuction
  29                      .endclass GONER
  30                     
  31                      ; Новая функция в ужесуществующем классе GONER
  32   00188EF5:          GONER.NewFuction:
  33   00188EF5: 6E 0001           enter    0, 1
  34   00188EF8: 0F 000A           read     GONER.NewVariable  ; читаем из классовой переменной
  35   00188EFB: 15 0033           writevar global.test1       ; записываем в глобальную переменную
  36   00188EFE: 83                ret                         ; result = GONER.NewVariable
  37                     
  38   00188EFF:                  .end

            Пример - объявление классовых функций


Вызов функции-метода внутри одного класса немного отличается от вызовов глобальных функций. Для этого испльзуются команды call85 и call86.

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

call86 - вызывает статические методы классов, адреса которых точно известны на момент компиляции и не изменятся.

 Пример - вызов классовых функций   (кликните здесь для просмотра)

Код:

   1                                      .title     Example
   2                                      .radix     10d
   3                                      .loadobj   "x3story.cod"
   4                                      .section   code, con
   5                           
   6                           ;
   7                           ; Простая функция, которая вытаскивает значение переданного ей аргумента
   8                           ; и увеличивает его на 100
   9                           ;
  10                           .func   Add100
  11   00188EF0:               Add100:
  12   00188EF0: 6E 0002               enter     1, 2          ; функция имеет 1 аргумент, использует 2 ячейки стека
  13   00188EF3: 0D 0004               push      SP[3]         ; это значение <arg1>, переданное нашей функции
  14   00188EF6: 05 64                 push      100d          ; число 100
  15   00188EF8: 46                    add       SP[0], SP[1]  ; сложить SP[1] + SP[0] -> SP[0]
  16   00188EF9: 83                    ret                     ; result = arg1 + 100
  17                           ;
  18                           ; пример объявления класса с единственной функцией
  19                           ;
  20                           .classdef TAdd, 10000d
  21                             .func   Addition
  22                           .endclass TAdd
  23                           
  24                           ;
  25                           ; функция, которая складывает аргумент с числом 200 и возвращает его
  26                           ;
  27   00188EFA:               TAdd.Addition:
  28   00188EFA: 6E 0002               enter     1, 2
  29                           ; вытащим на вершину стека аргумент функции
  30   00188EFD: 0D 0004               push      SP[3]         ; это значение <arg1>, переданное нашей функции
  31   00188F00: 06 00C8               push      200d          ; число 200
  32   00188F03: 46                    add       SP[0], SP[1]  ; сложить SP[1] + SP[0] -> SP[0]
  33   00188F04: 83                    ret                     ; result = arg1 + 200
  34                           ;
  35                           ; объявление более сложного класса
  36                           ;
  37                           .classdef TTestClass, 10001d
  38                             .func   Addition
  39                             .func   Test
  40                             .func   TestGlobal
  41                             .func   TestAnother
  42                           .endclass TTestClass
  43                           
  44                           ;
  45                           ; функция, которая складывает аргумент с числом 300 и возвращает его
  46                           ;
  47   00188F05:               TTestClass.Addition:
  48   00188F05: 6E 0002               enter     1, 2
  49   00188F08: 0D 0004               push      SP[3]         ; это значение <arg1>, переданное нашей функции
  50   00188F0B: 06 012C               push      300d          ; число 300
  51   00188F0E: 46                    add       SP[0], SP[1]  ; сложить SP[1] + SP[0] -> SP[0]
  52   00188F0F: 83                    ret                     ; result = arg1 + 300
  53                           ;
  54                           ; функция Test вызывает метод этого же класса
  55                           ;
  56   00188F10:               TTestClass.Test:
  57   00188F10: 6E 0003               enter     1, 3          ;
  58   00188F13: 05 1E                 push      30d           ; передаем нашей функции Addition параметр = 30
  59   00188F15: 02                    push      1             ; один параметр
  60   00188F16: 2E                    get_object              ; записать в стек идентификатор нашего объекта
  61                           ;
  62                           ; при вызове функции-метода имя класса можно не писать, или написать
  63                           ; (TTestClass.Addition), но оно будет игнорироваться.
  64                           ;
  65   00188F17: 85 0001B606           call85    Addition      ; складываем (+300)
  66   00188F1C: 83                    ret                     ; результат в стеке, его и возвращаем
  67                           ;
  68                           ; функция TestGlobal вызывает глобальную функцию
  69                           ;
  70   00188F1D:               TTestClass.TestGlobal:
  71   00188F1D: 6E 0002               enter     1, 2          ;
  72   00188F20: 05 1E                 push      30d           ; передаем нашей функции Addition параметр = 30
  73   00188F22: 02                    push      1             ; один параметр
  74   00188F23: 84 00188EF0           call84    global.Add100 ; складываем (+ 100)
  75   00188F28: 83                    ret                     ; результат в стеке, его и возвращаем
  76                           ;
  77                           ; функция TestAnother вызывает метод чужого класса
  78                           ;
  79   00188F29:               TTestClass.TestAnother:
  80   00188F29: 6E 0003               enter     1, 3          ;
  81   00188F2C: 05 1E                 push      30d           ; передаем нашей функции Addition параметр = 30
  82   00188F2E: 02                    push      1             ; один параметр
  83   00188F2F: 06 2710               push      10000d        ; записать в стек идентификатор чужого класса
  84   00188F32: 86 00188EFA           call86    TAdd.Addition ; складываем (+200)
  85   00188F37: 83                    ret                     ; результат в стеке, его и возвращаем
  86                           
  87   00188F38:                       .end


Дополнительные особенности ассемблера.

Как и в любом ассмблере, в xa_asm существуют директивы, котрые не транслируются непосредственно в машинные команды. Директивы предназначены в первую очередь для управления процессом трансляции и прочих вспомогательных функций.

Условная трансляция.

.IF <boolean_expr>
.ELSE
.ENDIF

Директива .if требует одного аргумента - выражение. Если значение выражения не нулевое, то трансляция продолжается до директивы .else или .endif. Если значение выражения равно нулю - следом идущие команды пропускаются. Директива .if всегда должна иметь свой собственный .endif. Допускается вложенность директив условной трансляции. При написании сложных схем конфигурации исходного текста необходимо проверять граничные значения констант. В случае, если какая-либо константа или условие принимают недопустимое значение, программист может использовать специальную директиву ассемблера, которая принудительно генерирует состояние ошибки .error "Any message". Эта директива может использоваться в любом месте совместно с директивами условной трансляции и макроопределениями. Она выводит в листинг сообщение об ошибке.

Макроопределения.

.MACRO <name> <arg1>,<arg2>,..,<argN>
.ENDM

Функции, которые определены для макросов

.EMPTY(<num>) - возвращает 1, если аргумент под номером <num> не определен во время вызова макрорасширения. Аргументы в макроопределении нумеруются с нуля.

.NARG - возвращает число аргументов, переданных макросу.

Директива загрузки дополнительных файлов .INCLUDE <str_name_file>.

Разделение единого исходника на множество мелких позволяет немного упростить разработку сложных программ.
Например .include "myconst.inc" будет подгружать и ассемблировать дополнительный файл "myconst.inc". Количество директив .include не ограничено для одного файла, но максимальная глубина вложенности include-файлов не может превышать пяти. Макросы, настроечные константы и нечасто используемый код - основные кандидаты на вынос в отдельный файл.

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

 Пример - использование препроцессора ассемблера   (кликните здесь для просмотра)

Код:

   1                           .title     Example - macros
   2                           .radix     10d
   3                           
   4                           .section   code, abs
   5                           
   6                           .macro     je     addr
   7                             .if .empty(0)
   8                               .error "JE: Empty argument"
   9                             .endif
  10                             if sp[0]=sp[1] then push 0 else push 1
  11                             if sp[0]=0 then jump addr
  12                           .endm
  13                           
  14                           .macro     div$
  15                             div      sp[0],sp[1]
  16                           .endm
  17                           
  18                           .macro     add$
  19                             add      sp[0],sp[1]
  20                           .endm
  21                           
  22                           .macro     neg$
  23                             neg      sp[0]
  24                           .endm
  25                           
  26                           ; Задаем значения ПОЛЬЗОВАТЕЛЬСКИХ констант:
  27   00001388                StoreFactor  = 5000d
  28   00000001                fStoreFactor = (StoreFactor * 2) > 2000d
  29                           
  30                           ; Проверка значения StoreFactor на предельно допустимые значения
  31                           .if StoreFactor < 32d
  32                             .error "StoreFactor too small!"
  33                           .endif
  34                           .if StoreFactor > 3FFFFFFFh
  35                             .error "StoreFactor too big!"
  36                           .endif
  37                           
  38                           ; Установим значение максимального размера склада
  39   00029850:               $ = 00029850h
  40   00029850: 06 2710               push       StoreFactor * 2
  41                           ; Проверим необходимость записи в дополнительную область
  42                                   .if fStoreFactor
  43   00029853: 0D 0003                  push       sp[2]
  44   00029856: 0D 0006                  push       sp[5]
  45                                      div$
       00029859: 51                   div        sp[0],sp[1]
  46                                      neg$
       0002985A: 65                   neg        sp[0]
  47                                      je         wrStoreFactor0
                                           .if .empty(0)
                                             .error "JE: Empty argument"
                                           .endif
       0002985B: 5A                        if sp[0]=sp[1] then push 0 else push 1
       0002985C: 34 0002A671               if sp[0]=0 then jump 173681d
  48   00029861: 32 0002B632              jump       wrStoreFactor1
  49                                   .else
  50                                      push       sp[2]
  51                                      push       sp[5]
  52                                      add$
                                      add        sp[0],sp[1]
  53                                      push       sp[0]
  54                                   .endif
  55                           
  56   0002A671:               $ = 0002A671h
  57   0002A671:               wrStoreFactor0:
  58                                     
  59   0002B632:               $ = 0002B632h
  60   0002B632:               wrStoreFactor1:
  61                                     
  62   0002B632:                       .end

            Пример использование препроцессора ассемблера
         (Директивы условной трансляции и макроопределения)


Макроопределения div$, add$, neg$ "доопределяют" систему команд ассмблера, добавляя более короткие в написании псевдокоманды (не нужно писать операнды команд). Более сложное и, возможно, чуть более полезное макроопределение команды je (jump if eq 0) рассмотрим подробнее.

Код:

.macro     je     addr
  .if .empty(0)
    .error "JE: Empty argument"
  .endif
  if sp[0]=sp[1] then push 0 else push 1
  if sp[0]=0 then jump addr
.endm

Это макроопределение заменяет две команды ВМ Х3 и, кроме того, выполняет проверку на допустимость вызова макроса. Директива ассемблера .empty(0) проверяет наличие аргумента макроса. Если нулевой аргумент отсутствует (отсчет аргументов ведется с 0), то теоретически должен сработать .if и выполнится директива .error, ассемблер выведет ошибку. На самом деле, для данной конструкции это все не нужно, так как отсутствие фактического аргумента у макроса сформирует неверную команду if sp[0]=0 then jump, с отсутствующим адресом перехода и ассемблер все равно выдаст ошибку. Но, несмотря на то, что это всего лишь демонстрация возможностей ассемблера, программист может сам придумать для себя более выразительные и удобные команды, чем изначально заложено в ассемблере.

Директива .SETTIME <str_date_time>.

Директива принудительно устанавливает в секциях SYMB, VARS, CLAS obj-файла указанную дату и время. Может быть использована для того, чтобы определенные версии патчей всегда компилировались одинаково, с точностью до байта.

Например: .settime "05-10-2006 00:59:59" ; 5 октября 2006 года

добавлено спустя 15 минут:
Чой-то я выдохся... Улыбка

Последний раз редактировалось: CheckerTwo (20:06 26-01-2012), всего редактировалось 4 раз(а)
    Добавлено: 23:02 20-05-2011   
vivere
 230 EGP


Рейтинг канала: 4(57)
Репутация: 56
Сообщения: 171

Зарегистрирован: 17.02.2011
Огромное спасибо за описание, хотя оно несколько запоздало, так как я уже все эти вещи выяснила на собственном опыте. Но новичкам будет очень полезно.
У меня есть просьба по ассемблеру, вернее даже две:
во первых, сделать возможным написание следующей команды "push sp[arg1]". тоесть чтобы при сборке он сам подставлял нужное число.
во вторых, сделать доступным чтение текущего состояния стека на этапе сборки для передачи этого значения в макрос.

предполагаю, что все это достаточно сложно осуществить ввиду того, что на этапе сборки ассемблер еще не знает уровня стека.
    Добавлено: 15:47 21-05-2011   
Artaazar
 





Господа, у меня есть взрыв мозга (Разрывная - сказал штирлиц пораскинув мозгами)
Я так понимаю класс здесь наследует свойства, параметры, и прочую хрень от своего "родителя". Но до меня не доходит, почему тот же М7 не имеет прав на захват кораблей, ведь:
; Obj_2134 = class(SHIP)
; function CanLaunchMarines();

; SHIP_BIG = class(Obj_2134)

; SHIP_CARRIER = class(SHIP_BIG)

; Obj_2019 = class(SHIP_CARRIER)

; SHIP_M7 = class(Obj_2019)

Объясните пожалуйста, мою ошибку Расстроен
    Добавлено: 05:53 01-07-2011   
XT3_Traktor
 180 EGP


Рейтинг канала: 6(483)
Репутация: 34
Сообщения: 527
Откуда: Украина
Зарегистрирован: 21.11.2008
Тебе наверное проще в Файле Tships назначить твоему
кораблю абордажную капсулу и будет тебе счастье.
Если конечно я тебя правильно понял.

Последний раз редактировалось: XT3_Traktor (09:03 01-07-2011), всего редактировалось 1 раз
    Добавлено: 09:03 01-07-2011   
Artaazar
 





Нет, неправильно. Улыбка
1) Я хочу понять где я ошибся в своих рассуждениях. благодаря этому наследию и м7 и м1 и пр.. должны высаживать десант...
2) ...только в том случае если есть на борту телепортатор. В одной из тем обсуждалось, что логично будет, если корабли которые могут таскать десант, но почему-то лишены возможности высаживать его, благодаря моду смогут все же отправлять людей на абордаж.
    Добавлено: 20:13 01-07-2011   
ULiX
 320 EGP


Репутация: 146
Сообщения: 573
Откуда: Комсомольск на-Амуре
Зарегистрирован: 12.07.2005
CheckerTwo :
Итак, при вызове функции "SaveStack" для доступа к первому аргументу мы должны использовать смещение указателя стека sp[3], для второго - sp[4] и так далее. В стеке со смещение sp[2] содержится число аргументов функции. Для чего нужны sp[0] и sp[1] - это тайна, закрытая ES, факт тот, что в них хранятся нули. Это еще раз подтверждает предположение о двустековости ВМ Х3.


Сделал вывод с недосказкой для непосвященных. Тут прям каждый второй занимался отладкой программ на ассемблере. Для "тех, кто в танке" CheckerTwo хотел видимо сказать, что для "одностековой" структуры в стек помещается адрес возврата из функции в аккурат рядом с аргументами, а мы его там не наблюдаем.

Приведенный пример демонстрирует вызов глобальной функции из глобальной же функции. А проверялось ли значение sp[0] и sp[1] при вызовах классовой функции из глобальной, или глобальной из классовой?

Я что-то устал по форуму рыскать. Дайте кто-нибудь ответ, транслятор XCtoXAsm для Земного конфликта имеется? Или не судьба родиться моим проектом на Земле?
_________________
Новое ещё не значит лучшее
    Добавлено: 08:42 13-09-2011   
CheckerTwo
 550 EGP


Рейтинг канала: 6(480)
Репутация: 103
Сообщения: 412
Откуда: Tomsk
Зарегистрирован: 18.08.2004
О, давно не видно было. Привет Улыбка
ULiX :
Сделал вывод с недосказкой для непосвященных. Тут прям каждый второй занимался отладкой программ на ассемблере. Для "тех, кто в танке" CheckerTwo хотел видимо сказать, что для "одностековой" структуры в стек помещается адрес возврата из функции в аккурат рядом с аргументами, а мы его там не наблюдаем.

Да-да, все верно.
ULiX :
Приведенный пример демонстрирует вызов глобальной функции из глобальной же функции. А проверялось ли значение sp[0] и sp[1] при вызовах классовой функции из глобальной, или глобальной из классовой?

Ага, к сожалению эта мысль пришла уже позже, а времени проверить ее не было. Потом начались с работой заморочки... Расстроен Вполне возможно, что при вызовах классовых функций в стеке есть что-то интересное.
ULiX :
Я что-то устал по форуму рыскать. Дайте кто-нибудь ответ, транслятор XCtoXAsm для Земного конфликта имеется? Или не судьба родиться моим проектом на Земле?

Увы, Darth пропал куда-то. Больше никто не решается...

PS: Поздравляю с сынишкой Улыбка
    Добавлено: 21:22 13-09-2011   
ULiX
 320 EGP


Репутация: 146
Сообщения: 573
Откуда: Комсомольск на-Амуре
Зарегистрирован: 12.07.2005
Не мог бы кто-нибудь выложить оригинальный obj файл.
Дизассемблю, гляну, что изменилось.

Раз некому взяться за компилятор, то возможно мне удастся осилить эту задачу. Сейчас осиливаю теорию. Пока что научился только выполнять простую арифметику. Странное дело, в голове я прекрасно представляю как заменить структуры KC в XASM, но вот чтоб автоматизировать все нюансы и компилировать без ошибок...

В общем я понял, что компилировать в XASM одинаково приятно, что компилировать напрямую в байткод. Но в связи с отсутствием у меня информации о работе с секциями символов неизбежно придётся использовать как промежуточный язык XASM.

to CheckerTwo
Может поделишься вкратце, как находятся границы секций в obj файле и информацией по структуру секций. И о методе шифровки секций. Надеюсь шифровка производится не использованием XOR с фиксированным числом, как в dat файлах Улыбка
_________________
Новое ещё не значит лучшее
    Добавлено: 05:27 23-09-2011   
Канал X3: Terran Conflict -> Модовый и скриптовый отсек X3: Terran Conflict: «X3TC Моддинг: obj-файлы и маленькие утилитки»
На страницу: Пред.  1, 2, 3, 4, 5 ... 10, 11, 12  След.    Перейти:   Все страницы
  
Показать: 
Предыдущая тема | Следующая тема |
К списку каналов | Наверх страницы
Цитата не в тему: Очень редко заходит на ЕГу, сейчас просто вовсю увлекся реалом... (Katana про Shurk'a)

  » X3TC Моддинг: obj-файлы и маленькие утилитки | страница 4
Каналы: Новости | Elite | Elite: Dangerous | Freelancer | Star Citizen | X-Tension/X-BTF | X2: The Threat | X3: Reunion | X3: Terran Conflict | X Rebirth | X4: Foundations | EVE Online | Orbiter | Kerbal Space Program | Evochron | VoidExpanse | Космические Миры | Онлайновые игры | Другие игры | Цифровая дистрибуция | play.elite-games.ru | ЗВ 2: Гражданская война | Творчество | Железо | Игра Мечты | Сайт
   Дизайн Elite Games V5 beta.18