Еще в 80-х годах неизвестная компания Binary Systems выпустила игру Starflight. В игре игроку предстоит взять на себя роль капитана космического корабля, отправленного исследовать галактику. Не существует определенного пути, позволяющего игрокам свободно переключаться между добычей полезных ископаемых, боем между кораблями и дипломатией пришельцев. Более широкий сюжет игры раскрывается постепенно, когда игрок обнаруживает, что древняя раса существ заставляет звезды вспыхивать и уничтожать все живые существа. Игра получила широкую оценку как современных, так и современных критиков и является одним из первых примеров игры-песочницы. Игра повлияла на дизайн множества других игр на протяжении десятилетий после ее выпуска.
Чтобы узнать больше об игре, перейдите по следующим ссылкам:
Вы можете купить игру на GoG.
Когда я впервые услышал об игре, мне захотелось в нее поиграть. Однако я был слишком молод и не мог говорить по-английски. Спустя 20 я попробовал еще раз, и это был очень приятный опыт. Исследование увлекательное, сюжетная линия эпическая и заканчивается сюрпризом — один из лучших, которые я когда-либо испытывал. Конечно, игра не устарела, но преданность разработчиков игре чувствуется. В этой игре есть художественный аспект, а также внимание мастера к деталям.
Играть в эту поистине потрясающую игру доставляет столько же удовольствия, сколько и ее реверс-инжиниринг. Вы следуете по стопам разработчиков и ощущаете их мыслительный процесс так, как будто это снова 1985 год. От этой игры ожидайте неожиданностей. Обычно, когда вы реконструируете такую старую игру, вы должны получить десять тысяч строк чистого ассемблерного кода, который вы можете проанализировать с помощью обычных инструментов, таких как IDA Pro. Но не в этот раз. Собственно для этой игры можно выбросить обычные инструменты. Они бесполезны. Вы сами по себе. Причина в том, что Starflight была написана на Форте, языке, о котором я едва знал.
Форт — это язык с максимальным минимализмом в отношении синтаксиса. Синтаксиса не больше, чем пространство между «словами». Вы можете написать программу чтения и интерпретатор Forth, используя всего несколько строк кода.
На современном языке вы пишете что-то вроде
print ( 2 + 3 )
чтобы напечатать результат 2+3. Однако в Форте это выглядит так.
2 3 + .
Форт — это стековая машина с обратной польской записью. Интерпретация следующая
Синтаксис прост и интерпретатор прост. «2», «3», «+» и «." называются просто «словами». Синтаксического различия между данными и кодом нет. Определенно, язык, который соответствовал ограничениям первых домашних компьютеров.
Когда вы анализируете исполняемый файл STARFLT.COM, вы обнаруживаете некоторые фантастические внутренности.
Как объяснялось выше, Форт — это стековая машина. В качестве механики кодирования он использует непрямую многопоточность — очень эффективный метод хранения скомпилированного кода. Потоковый код имеет форму, которая по существу полностью состоит из вызовов подпрограмм. Косвенная обработка потоков использует указатели на места, которые, в свою очередь, указывают на машинный код.
Допустим, указатель вашей инструкции указывает на адрес 0x1000 и содержит 16-битное значение Read16(0x1000)=0x0f72.
0x1000 : dw 0x0f72
Значение 0x0f72 является закодированным эквивалентом слова Форта «+». Помните описание выше. Слово «+» извлекает две последние записи стека, складывает их и помещает результат обратно на вершину стека. Согласно косвенной многопоточности, это 16-битное значение 0x0f72 является указателем на место, которое, в свою очередь, указывает на машинный код. Когда вы читаете содержимое памяти Read16(0x0f72), вы получаете указатель на 0x0f74. И действительно, когда вы посмотрите на эту ячейку памяти и разберете ее, вы получите следующее
0x0f72 : dw 0x0f74
0x0f74 : pop ax
0x0f75 : pop bx
0x0f76 : add ax , bx
0x0f78 : push ax
0x0f79 : lodsw
0x0f7a : mov bx , ax
0x0f7c : jmp word ptr [ bx ]
Первые четыре инструкции выполняют именно те операции, которые должно выполнять слово «+». Последние три инструкции ассемблера, начиная с «lodsw», увеличивают указатель инструкций и переходят к следующему коду.
Давайте продолжим. Теперь указатель инструкции указывает на 0x1002.
0x1002 : dw 0x53a3
Чтение адреса 0x53a3 показывает
0x53a3 : dw 0x1d29
0x53a5 : dw 0x0001
и соответствующий код
0x1d29 : inc bx
0x1d2a : inc bx
0x1d2b : push bx
0x1d2c : lodsw
0x1d2d : mov bx , ax
0x1d2f : jmp word ptr [ bx ]
В это время регистр bx содержит слово с адресом 0x53a3. Итак, этот код просто помещает адрес 0x53a5 на вершину стека. Мы предоставили программе указатель на переменную. Переменная имеет содержимое 0x0001. Форт-слово «@» извлекает адрес из стека, читает его содержимое и помещает его обратно в стек.
На данный момент мне удалось идентифицировать 6256 слов, содержащих либо код, либо данные.
И это, собственно, все, что вам нужно знать о структуре кода. Как вы можете видеть, это может быть кодирование с эффективным использованием пространства, но с точки зрения скорости это катастрофа. Каждые несколько инструкций машинного кода вам приходится переходить к другому блоку кода.
Эквивалент косвенной многопоточности в C будет выглядеть так.
uint16_t instruction_pointer = start_of_program_pointer ;
void Call ( uint16_t word_adress )
{
// the first two byte of the word's address contain
// the address of the corresponding code, which must be executed for this word
uint16_t code_address = Read16 ( word_address );
switch ( code_address )
{
.
.
.
case 0x0f74 : // word '+'
Push16 ( Pop16 () + Pop16 ());
break ;
.
.
.
}
}
void Run ()
{
while ( 1 )
{
uint16_t word_address = Read16 ( instruction_pointer );
instruction_pointer += 2 ;
Call ( word_address );
}
}
Код, выполняемый для определенного слова, имеет доступ к 5 основным переменным (16-бит).
Дизассемблер транспилирует код FORTH в код в стиле C. Большая часть транспилированного кода компилируется. Чтобы понять, что делает программа, взгляните на следующую таблицу. Он принимает «байт-код» (который в основном представляет собой 16-битные указатели) в качестве входных данных и преобразует его в C.
Четвертый код:
: .C ( -- )
Display context stack contents.
CR CDEPTH IF CXSP @ 3 + END-CX
DO I 1.5@ .DRJ -3 +LOOP
ELSE ." MT STK"
THEN CR ;
EXIT
Трансформация:
16-битные указатели | ФОРТ | С |
---|---|---|
: .C ( -- ) | void DrawC() { | |
unsigned short int i, imax; | ||
0x0642 | ЧР | Exec("CR"); |
0x75d5 | ГЛУБИНА | CDEPTH(); |
0x15fa 0x0020 | ЕСЛИ | if (Pop() != 0) { |
0x54ae | CXSP | Push(Read16(pp_CXSP) + 3); |
0xbae | @ | |
0x3b73 | 3 | |
0x0f72 | + | |
0x4ffd | КОНЕЦ-CX | Push(Read16(cc_END_dash_CX)); |
0x15b8 | ДЕЛАТЬ | i = Pop(); |
imax = Pop(); | ||
do { | ||
0x50e0 | я | Push(i); |
0x4995 | 1,5@ | _1_dot_5_at_(); |
0x81d5 | .DRJ | DrawDRJ(); |
0x175d 0xfffd | -3 | Push(-3); |
0x155c 0xffff | +ПЕТЛЯ | int step = Pop(); |
i += step; | ||
if (((step>=0) && (i>=imax)) || ((step<0) && (i<=imax))) break; | ||
} while(1); | ||
0x1660 0x000b | ЕЩЕ | } else { |
0x1bdc | "МТ СТК" | PRINT("MT STK", 6); |
0x06 | ||
0x4d | 'М' | |
0x54 | 'Т' | |
0x20 | '' | |
0x53 | 'С' | |
0x54 | 'Т' | |
0x4b | 'К' | |
ЗАТЕМ | } | |
0x0642 | ЧР | Exec("CR"); |
0x1690 | ВЫХОД | } |
Игра поставляется в 3-х файлах.
Контент STARA.com
вход | размер | описание |
---|---|---|
СПРАВОЧНИК | 4096 | содержит каталог STARA и STARB |
ЭЛО-CPIC | 4816 | |
ГАЗ-ЦПИК | 3120 | |
MEC-CPIC | 2848 | |
МИС-ЦПИК | 6064 | |
НОМ-КПИК | 1136 | |
SPE-CPIC | 1888 г. | |
THR-CPIC | 2480 | |
ВЭЛ-КПИК | 4672 | |
ВПР-CPIC | 1248 | |
МИН-CPIC | 2096 | |
ВСПЛЕСК | 16384 | Картина |
МЕД-ПИК | 2048 | Картина |
ФАЗЫ | 6144 | |
ХУМ-ПИК | 480 | Картина |
ВЭЛ-ПИК | 432 | Картина |
ТР-ПИК | 272 | Картина |
ЭЛО-ПИК | 608 | Картина |
И-ПИК | 640 | Картина |
СОХРАНЯТЬ | 124000 | |
МУЗЫКА | 4960 | Наложение кода |
ЗЕМЛЯ | 1152 | Карта планеты Земля |
ГАЛАКТИКА | 6304 | |
КРЕДИТЫ | 16384 | картина |
КС-КПИК | 2928 | |
ШРИФТЫ | 768 | |
CGA | 3600 | Подпрограммы машинного кода для видеокарты CGA |
ЕГА | 3600 | Подпрограммы машинного кода для видеокарты EGA |
Содержание STARB.COM
вход | размер | описание |
---|---|---|
СПРАВОЧНИК | 4096 | содержит каталог STARA и STARB |
ПРИМЕР | 150528 | Древовидная структура с большей частью контента игры |
КОРОБКА | 1024 | Стол |
БАНК-ТРАНС | 144 | Стол |
ЧЛЕН ЭКИПАЖА | 128 | Стол |
СУДНО | 1936 год | Стол |
ЭЛЕМЕНТ | 544 | Стол |
АРТЕФАКТ | 1584 г. | Стол |
ПЛАНЕТА | 1360 | Стол |
ОБРАЗЕЦ | 448 | Стол |
БИО-ДАННЫЕ | 448 | Стол |
ТПОРТ-ПИК | 2416 | Картина |
БПОРТ-ПИК | 3984 | Картина |
АНАЛИЗ-ТЕКСТ | 3200 | Стол |
КНОПКИ | 944 | Стол |
ЗНАЧОК1:1 | 912 | |
ЗНАЧОК1:2 | 912 | |
ЗНАЧОК1:4 | 912 | |
ЗНАЧОК-ИМЯ | 736 | |
ДПАРТ-ОВ | 1552 г. | Наложение кода |
РЕГИОНЫ | 176 | Стол |
СУЩЕСТВО | 17024 | Стол |
ЧКФЛАЙТ-ОВ | 960 | Наложение кода |
ФРАКТ-ОВ | 4640 | Наложение кода |
ИКОНП-ОВ | 832 | Наложение кода |
САЙТ-ОВ | 1888 г. | Наложение кода |
ГИПЕРМСГ-ОВ | 4112 | Наложение кода |
ГПОЛИ | 368 | |
ФАСЕТ | 288 | |
ВЕРТЕКС | 416 | |
БЛТ-ОВ | 864 | Наложение кода |
РАЗНОЕ-ОВ | 1440 | Наложение кода |
БАНК-ОВ | 1520 г. | Наложение кода |
ВИНТ-OV | 2800 | Наложение кода |
ПЕРСОНАЛ-ОВ | 4192 | Наложение кода |
ШИПГРФ-ОВ | 2112 | Наложение кода |
КОНФИГ-ОВ | 3072 | Наложение кода |
ТДЕПОТ-ОВ | 4800 | Наложение кода |
ПОРТМЕНЮ-ОВ | 3120 | Наложение кода |
ВИТА-ОВ | 3552 | Наложение кода |
HP-ОВ | 4832 | Наложение кода |
ЛП-ОВ | 5280 | Наложение кода |
СЕНТ-ОВ | 4784 | Наложение кода |
ТВ-ОВ | 3472 | Наложение кода |
КОММ-ОВ | 7232 | Наложение кода |
КОММССПЕЦ-ОВ | 2864 | Наложение кода |
СЕИД-ОВ | 2400 | Наложение кода |
СПИСКИ | 720 | Наложение кода |
ДВИЖЕНИЕ-ОВ | 3808 | Наложение кода |
ИНЖЕНЕР | 2320 | Наложение кода |
ДОКТОР | 1280 | Наложение кода |
ОРБИТА-ОВ | 6640 | Наложение кода |
КАПИТАН | 5952 | Наложение кода |
НАУКА | 3952 | Наложение кода |
НАВИГАТР | 880 | Наложение кода |
КНОПКИ КОРАБЛЯ | 1984 г. | |
МАП-ОВ | 4160 | Наложение кода |
ГИПЕР-ОВ | 7168 | Наложение кода |
АНАЛИЗ-ОВ | 2560 | Наложение кода |
LAUNCH-ОВ | 1360 | Наложение кода |
ПОТОК-ЭФФЕКТ | 464 | |
ОП-ОВ | 4400 | Наложение кода |
ПУНКТЫ-ОВ | 6016 | Наложение кода |
ЛСИСИКОН | 752 | |
МСИСИКОН | 448 | |
ССИСИКОН | 176 | |
ПОВЕДЕНИЕ-ОВ | 5360 | |
CMAP | 1008 | |
УСТАНОВИТЬ | 800 | |
ИСЦЕЛЕНИЕ-ОВ | 1232 | Наложение кода |
РЕМОНТ-ОВ | 1696 г. | Наложение кода |
ИГРА-ОВ | 5920 | Наложение кода |
ПЛСЕТ-ОВ | 2400 | Наложение кода |
КАРТ-ОВ | 2240 | Наложение кода |
ВЕС-БЛТ | 4528 | |
ШТОРМ-ОВ | 1232 | Наложение кода |
СОЕДИНЕНИЯ | 176 | Стол |
ИТ-ОВ | 1936 год | Наложение кода |
КОМБАТ-ОВ | 6192 | Наложение кода |
ПОВРЕЖДЕНИЕ-ОВ | 2752 | Наложение кода |
ЛЭНД-ОВ | 1088 | Наложение кода |
ПСТАТС | 64 | Стол |
СТП-ОВ | 1440 | Наложение кода |
Поместите файлы оригинальной игры Starflight в папки starflt1-in
и starflt2-in
и запустите make
. Вы должны получить два исполняемых файла ( disasOV1
и disasOV2
), которые создают содержимое в папках starflt1-out
и starflt2-out
. Сгенерированный вывод является частью этого репозитория.