Разработка и ромхакинг > Ромхакинг и программирование
[NES] Ассемблер перевести в понятный код
<< < (2/3) > >>
Ti_:

--- Цитата: teremochek от 07 Март 2013, 09:40:25 ---как перевести

как перевести и понять

--- Конец цитаты ---
Подскажи как мне перевести испанского на немецкий и понять (знаю русский, английский)  ...  :ohmy:
KABAL:
sergi, полностью с тобой согласен... Машинный код - по мне так проще всего...
yur:
Впринципе суть вопроса ясна, а по поводу икса в скобках это может быть и присваивание переменной  "х" функции,ну а что бы реализовать твою идею эт надо ассемблер 6502 знать и какую нибудь среду ооп 
teremochek:

--- Цитата: Ti_ от 07 Март 2013, 10:30:30 ---Подскажи как мне перевести испанского на немецкий и понять (знаю русский, английский)  ...  :ohmy:

--- Конец цитаты ---

Нужно было вначале сказать что это Assembler x816.
И на скорее всего что его никто не знает...

***

Все равно, спасибо за помощь. Кое что в ассемблере я теперь понимаю ^_^
MetalliC:
teremochek, вообще существуют более-менее нормальные "переводчики" машинного кода в C, но только для x86 (пк-шных) процов и ARM (и кстати стоят хрен знает сколько тысяч долларов)

вот пример работы:
процедурка на асме http://pastebin.com/ahNDrd65
результат на С http://pastebin.com/8ikUSL83
будет ли тебе понятнее такой код на С ? сомневаюсь ;)

но большое НО - это декомпил кода, который изначально был написан на С, если бы на входе был рукописный асм - белиберда бы получилась еще та
gepar:

--- Цитата: KABAL ---ergi, полностью с тобой согласен... Машинный код - по мне так проще всего...
--- Конец цитаты ---

Он проще всего, всё просто и понятно, только надо прочитать 500 строчек предыдущего кода чтобы отследить что же там в том регистре лежит-то и откуда оно взялось, а так да, всё хорошо и просто. Ато видишь ли мода пошла со всеми этими классами и наследованием ...  :)

--- Цитата: teremochek ---И на скорее всего что его никто не знает...
--- Конец цитаты ---

А как можно написать что-то на том, что никто не знает ?  :wacko:
teremochek:

--- Цитата: MetalliC от 08 Март 2013, 13:41:20 ---teremochek, вообще существуют более-менее нормальные "переводчики" машинного кода в C, но только для x86 (пк-шных) процов и ARM (и кстати стоят хрен знает сколько тысяч долларов)

--- Конец цитаты ---
Приходилось иметь дело с таким.
Первое что обломило, это куча переменных с неопознанным типом.
Ну а когда дошло до этих - "*(_DWORD *)(a1 + 20) = 7;", я понял что ничего не получится..


--- Цитата: MetalliC от 08 Март 2013, 13:41:20 ---вот пример работы:
процедурка на асме http://pastebin.com/ahNDrd65
результат на С http://pastebin.com/8ikUSL83
будет ли тебе понятнее такой код на С ? сомневаюсь ;)

--- Конец цитаты ---
Такой код я хотя-бы могу разобрать. Если выяснить что делает данная функция и проименовать ее, то даже вероятно не будет нужды переименовывать весь код функции..
Другое дело - эти "(*DWORD)*(a1+21)",
с которыми неясно что делать.. Как я понял это ссылка на память стека, или что-то вроде того...
Но мне нужен Высокоуровневый код, который легко модифицировать и портировать..
 

--- Цитата: MetalliC от 08 Март 2013, 13:41:20 ---но большое НО - это декомпил кода, который изначально был написан на С, если бы на входе был рукописный асм - белиберда бы получилась еще та

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

Вообще, как я понимаю, толковых декомпиляторов ассемблера в высокоуровневый код - не существует.
MetalliC:

--- Цитата: teremochek ---Ну а когда дошло до этих - "*(_DWORD *)(a1 + 20) = 7;", я понял что ничего не получится..
--- Конец цитаты ---

я понял, C ты нифига не знаешь, иначе сразу было бы понятно что это


--- Цитата: teremochek ---Вообще, как я понимаю, толковых декомпиляторов ассемблера в высокоуровневый код - не существует.
--- Конец цитаты ---

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

другой вопрос что ты ни ассемблеров не знаешь, ни высокоуровневых языков, и для тебя надо переводить в почти человеческий язык, типа "языка Ершова" (если .... то... тогда; повторить), при этом с вычисенными переменными (что и зачем), и соответственно названными, а такого не будет, по крайней мере пока не появятся вычислительные машины с искуственным интелектом (без шуток)

ну а специалисты при помощи IDA и HexRays за денек-другой вполне реверсят программки небольшого-среднего объема
teremochek:

--- Цитата: teremochek от 07 Март 2013, 09:40:25 ---как перевести
x = FireballCounter & #%00000001

--- Конец цитаты ---
Здесь нечего переводить.Число справа в бинарном формате.
Можно записать так:
x = FireballCounter AND %00000001

Не совсем понимаю как это работает. Может оператор AND заменить плюсом "+" ?


--- Цитата: teremochek от 07 Март 2013, 09:40:25 ---как перевести и понять
y = Player_Y_HighPos - 1 == zero

--- Конец цитаты ---
Здесь тоже нечего переводить. В принципе понять можно так.
 IF  y = (Player_Y_HighPos - 1)  AND (Player_Y_HighPos - 1) = zero


--- Цитата: teremochek от 07 Март 2013, 09:40:25 ---как перевести
y, FireballThrowingTimer = PlayerAnimTimerSet

--- Конец цитаты ---
Предположу, что так.

y = PlayerAnimTimerSet         
FireballThrowingTimer = y



--- Цитата: teremochek от 07 Март 2013, 09:40:25 ---как перевести
if lda AreaType == zero

--- Конец цитаты ---
Здесь возможно так:

a = AreaType
if a == zero


Добавлено позже:

--- Цитата: MetalliC от 20 Март 2013, 15:03:11 ---я понял, C ты нифига не знаешь, иначе сразу было бы понятно что это

--- Конец цитаты ---
Может объяснишь вкратце, если знаешь..  o_0


--- Цитата: MetalliC от 20 Март 2013, 15:03:11 ---это и есть толковый декомпилятор, в нем все реверсеры в мире и работают, т.е. антивирусные компании, хакеры/крякеры, спецы по поиску уязвимостей в ПО, и просто желающие скоммуниздить чужой код.

другой вопрос что ты ни ассемблеров не знаешь, ни высокоуровневых языков, и для тебя надо переводить в почти человеческий язык, типа "языка Ершова" (если .... то... тогда; повторить), при этом с вычисенными переменными (что и зачем), и соответственно названными, а такого не будет, по крайней мере пока не появятся вычислительные машины с искуственным интелектом (без шуток)

ну а специалисты при помощи IDA и HexRays за денек-другой вполне реверсят программки небольшого-среднего объема

--- Конец цитаты ---
Я нечего против "IDA и HexRays" не имею. И кстати я его не называл, ты сам сказал  ^_^
Тот-же дизассемблер NES, SEGA в нем делается. Знаю там даже специальные плагины для этого есть.
Другое дело что это больше Анализатор кода чем декомпилятор.
Mr2:

--- Цитата: teremochek от 20 Март 2013, 13:24:40 ---Но мне нужен Высокоуровневый код, который легко модифицировать и портировать..
--- Конец цитаты ---
Хватит смешить народ. <_<

--- Цитата: teremochek от 20 Март 2013, 13:24:40 ---Вообще, как я понимаю, толковых декомпиляторов ассемблера в высокоуровневый код - не существует.

--- Конец цитаты ---
Для кого-то и высокоуровневый язык не толковый... :cool:
JAM:
Даю реально дельный совет.
Вбей в Google фразу типа "6502 opcode list" или "NES ASM opcodes". Наверняка ты найдёшь или голый список команд, или гид с описанием комманд, что тоже хорошо. Скорее всего, на английском. Команд всего 256, но есть много похожих команд, которые можно сгуппировать, например: LDA, LDA,X, LDA,Y или PHA, PHX, PHY и т.п. Всего групп около 30, это не так много. Не обязательно учить команды, достаточно их понимать. Без понимания команд даже код, который ты привёл, даже с комментариями -- как перевод с японского на китайский. Научись понимать команды, и ты будешь знать китайский =)

Но даже зная команды, придётся попотеть, чтобы понять, какой смысл в определённой команде. Например, ты будешь понимать, что одна команда считывает значение из памяти по определённому адресу в аккумулятор. Другая команда сравнивает значение с определённым байтом в коде. Третья команда выполняет прыжок при определённом условии, четвёртая команда модифицирует значение в аккумуляторе, а пятая команда сохраняет значение аккумулятора обратно в память по тому же адресу. Вот только, не зная, что находится в RAM памяти по адресу $0123, так и не поймёшь, зачем всё это. Благо, Марио игра изученная, и для неё наверняка есть ROMMap и RAMMap. Для неизученных игр придётся самому отслежить с помощью Debugger'а, что же такое по адресу $0123 и когда оно меняется.

Не нужно бояться ASM, как огня. Уж поверь мне. Лет 8 назад я скачал одну дизассемблированную процедуру, долго в неё втыкал, но так ничего и не понял. Прям, как ты сейчас. 4 года назад я стал изучать ассемблер в одиночку, по списку команд. 3 года назад я всё-таки осилил ту процедуру. Даже зная все команды, я месяц втыкал в 27kb текста, пока до меня не начало доходить, что к чему, путём проб и ошибок. С тех пор научился патчи делать для некоторых игр, понимать код в целом, и даже видеть его в HEX-редакторе. Сейчас я как Сайфер из Матрицы, только блондинок и брюнеток не различаю. =) Смотрю на ROM в Hex-редакторе, и вижу где код, где таблицы, где спрайтовая графика, а где всё остальное...

Но это только для одной системы. Ассемблер для Сеги для меня до сих пор -- тёмный лес, хотя там есть некоторые успехи. Для одного хака, например, чтобы изменить 20 байт в разных частях ROM'а, мне лично потребовалось 15 часов, а ты хочешь, чтобы всё и сразу одним нажатием кнопки =) И хотя та игра сильно изученная и даже дизассемблирвоанная, в хаке какие-то данные изменены, где-то удлиннены или укорочены массивы и т.п. В итоге, часть данных перенесена, какие-то данные сдвинуты на X байт, какие-то на Y байт, а все остальные -- на Й байт.
teremochek:
Спасибо. Хороший совет.
teremochek:
Давайте оставим x816, т.к. он сложнее, и посмотрим простой дизассемблер.
Вот еще один пример. На этот раз - "Galaxian".

--- Код: ---ProcessPlayerBullet:
LDA spr_buf(32,ycoo)
BEQ skp021
CMP #200
BEQ skp022
tail05: LDA spr_buf(32,ycoo)
SEC
SBC #4
STA spr_buf(32,ycoo)
BCS ret06
skp021: LDA #200
STA spr_buf(32,ycoo)
skp022: LDA PlayerDiedThisVsync
BEQ PinBulletToPlayer
LDA #CHR_Blank
STA spr_buf(32,tile)
ret06: RTS

--- Конец кода ---

Подпрограмма - "ProcessPlayerBullet:". В конце стоит "RTS". Т.е. возврат. Похоже на Функцию, как во многих языках.
Можно записать так:

--- Код: ---Function ProcessPlayerBullet()
--- Конец кода ---
За место "RTS" -
--- Код: ---End Function
--- Конец кода ---

LDA spr_buf(32,ycoo)
Загрузить память в аккамулятор. Это не самый лучший пример, потому что "spr_buf(32,ycoo)" это массив.
Очевидно это координата Y "спрайта" - пули игрока.
записываем просто:

--- Код: ---a = spr_buf(32,ycoo)
--- Конец кода ---
следующая

--- Код: ---BEQ skp021
--- Конец кода ---
Я понимаю так. Что-то в роде IF ... GOTO ...
Поскольку Compare команды не видно. Наверноо сравниваем с нулем "0"

--- Код: ---If a == 0 Then Goto skp021
--- Конец кода ---
дальше

--- Код: ---CMP #200
BEQ skp022
--- Конец кода ---
Сравниваем a и 200

--- Код: ---If a==200 Then Goto skp022
--- Конец кода ---
Далее Лабель tail05: Записываются везде по разному. У меня так.
#tail05

LDA spr_buf(32,ycoo)

--- Код: ---a = spr_buf(32,ycoo)
--- Конец кода ---

SEC

--- Код: ---I=1
--- Конец кода ---

SBC #4  'Вычитание с займом ?
a = a - 4  ?

STA spr_buf(32,ycoo)  'Сохраняем А в память.

--- Код: ---spr_buf(32,ycoo) = a
--- Конец кода ---

BCS ret06   'Условный переход, если перенос. Не понимаю.

Пока хватит...
vladikcomper:

--- Код: --- SEC
SBC #4
STA spr_buf(32,ycoo)
BCS ret06
--- Конец кода ---

SEC - Задать Carry флаг.

SBC отнимает из аккумулятора (А) значение 4 и инвертированное значение Carry-флага. Т.е., если Carry=1, ничего не отнимается, если Carry=0, дополнительно отнимается 1. В процессоре 6502 не предусмотрено отнимание без учета Carry-флага, поэтому флаг Carry специально задается инструкцией SEC, чтобы гарантировать, что SBC отнимает именно 4.

SBC очищает флаг Carry, если при выполнении вычитая случилось заимствование (borrow), т.е. результат пересек границу 00-FF. Например, если отнять из 01 число 04, получится FD, тогда флаг Carry будет очищен. Таким образом...

BCS - Branch if carry set - переходит, если заимствования не произошло (Carry=1).

На языках высокого уровня эту логику можно заменить следующей: "переход, если значение осталось положительным".
JAM:

--- Цитата: teremochek от 28 Март 2013, 20:22:50 ---Я понимаю так. Что-то в роде IF ... GOTO ...
Поскольку Compare команды не видно. Наверноо сравниваем с нулем "0"

--- Конец цитаты ---
Да, это короткий вариант сравнения. Если нужно сравнить с нулём, то просто
LDA $1234
BEQ Метка

Если нужно сравнить с чем-то ещё:
LDA $1234
CMP #$56
BEQ Метка
teremochek:
vladikcomper, JAM, Спасибо за полезную информацию.

Итак, в целом  получилась такая функция.

--- Код: ---Function ProcessPlayerBullet()
    a = spr_buf(32,ycoo)
    If a == 0 Then Goto skp021
    If a == 200 Then Goto skp022
#tail05
    a = spr_buf(32,ycoo)
C_FLAG = 1
a = a - 4
spr_buf(32,ycoo) = a
#skp021
a = 200
spr_buf(32,ycoo) = a
#skp022
a = PlayerDiedThisVsync
If a == 0 Then Goto PinBulletToPlayer
a = CHR_Blank
spr_buf(32,tile) = a
#ret06
End Function 'RTS
--- Конец кода ---

Теперь можно еще упростить, убрав загрузку в регистры и сохранение из регистров.

--- Код: ---Function ProcessPlayerBullet() 
    If spr_buf(32,ycoo) == 0 Then Goto skp021
    If spr_buf(32,ycoo) == 200 Then Goto skp022
#tail05
spr_buf(32,ycoo) = spr_buf(32,ycoo) - 4
if spr_buf(32,ycoo) > 0 Then Goto #ret06
#skp021
spr_buf(32,ycoo) = 200
#skp022
If PlayerDiedThisVsync == 0 Then Goto PinBulletToPlayer
spr_buf(32,tile) = CHR_Blank
#ret06
End Function
--- Конец кода ---
Возможно хорошая идея, избавиться от Меток ? (label).

Получилось вот так.

--- Код: ---Function ProcessPlayerBullet()
    If spr_buf(32,ycoo) == 0 or spr_buf(32,ycoo) == 200
    spr_buf(32,ycoo) = 200
    If PlayerDiedThisVsync == 0 Then Goto PinBulletToPlayer
    spr_buf(32,tile) = CHR_Blank
    Return
Endif
#tail05
spr_buf(32,ycoo) = spr_buf(32,ycoo) - 4
if spr_buf(32,ycoo) > 0 Then Return
spr_buf(32,ycoo) = 200
If PlayerDiedThisVsync == 0 Then Goto PinBulletToPlayer
spr_buf(32,tile) = CHR_Blank
End Function
--- Конец кода ---

Метка "#tail05" осталась, т.к. ссылка на нее находится вне.
Как оказалось, нужно добавить еще одну часть кода, для ясности картины.
Вот эту

PinBulletToPlayer:
   LDA player_x_pos   
   CLC
   ADC #124
   STA spr_buf(32,xcoo)   
   LDX #0
   JSR FireHoldoff      
   JSR StartPlayerShootSound
   JMP tail05      

Упрощаем:

     #PinBulletToPlayer
     spr_buf(32,xcoo) = player_x_pos +124
     FireHoldoff()      
     StartPlayerShootSound()
     GOTO tail05   

Теперь смотрим, что получается вместе:


--- Код: ---Function ProcessPlayerBullet() 
    If spr_buf(32,ycoo) == 0 or spr_buf(32,ycoo) == 200
     spr_buf(32,ycoo) = 200
    If PlayerDiedThisVsync == 0
        spr_buf(32,xcoo) = player_x_pos +124 ; PinBulletToPlayer
                FireHoldoff()
        StartPlayerShootSound()
                GOTO tail05
    endif
      spr_buf(32,tile) = CHR_Blank
      Return
    Endif
#tail05
spr_buf(32,ycoo) = spr_buf(32,ycoo) - 4
if spr_buf(32,ycoo) > 0 Then Return
spr_buf(32,ycoo) = 200
If PlayerDiedThisVsync == 0
   spr_buf(32,xcoo) = player_x_pos +124   ; PinBulletToPlayer
           FireHoldoff()
   StartPlayerShootSound()
           GOTO tail05
endif
spr_buf(32,tile) = CHR_Blank
End Function
--- Конец кода ---

Наверняка можно записать еще проще, пока не знаю...
Код без меток получился больше по объему, чем Код с метками. И к тому-же Код с метками воспринимается лучше.
teremochek:
Вот. Код без меток получился такой.


--- Код: ---Function ProcessPlayerBullet() 
    If spr_buf(32,ycoo) == 0 or spr_buf(32,ycoo) == 200
     spr_buf(32,ycoo) = 200
    If PlayerDiedThisVsync == 0
spr_buf(32,xcoo) = player_x_pos +124
                FireHoldoff()
        StartPlayerShootSound() 
    else
        spr_buf(32,tile) = CHR_Blank
Return
            endif
    Endif

do While PlayerDiedThisVsync == 0
spr_buf(32,ycoo) = spr_buf(32,ycoo) - 4
if spr_buf(32,ycoo) > 0 Then Return
spr_buf(32,ycoo) = 200
If PlayerDiedThisVsync == 0
     spr_buf(32,xcoo) = player_x_pos +124   
             FireHoldoff()
     StartPlayerShootSound() 
endif
Loop

spr_buf(32,tile) = CHR_Blank
End Function
--- Конец кода ---

Вот такой возник вопрос.
...
CMP #5      
BCC skp019
...

Потом, в конце Функции встречается
...
DEC l_cur_bullet
BNE NextEnemyBullet

BCC работает без CMP т.е.
If a<0 Then Goto skp019

,а BNE ,будет работать с тем CMP, что написан вначале?


Еще, кажется я понял, как записать такое выражение.. Но это только в конкретном случае..
LDA spr_buf(33,xcoo),x
------------------------------
a = spr_buf(33 + x, xcoo)
Где   33 + x   это номер спрайта..
MetalliC:

--- Цитата: teremochek ---а BNE ,будет работать с тем CMP, что написан вначале?
--- Конец цитаты ---

BNE пляшет от флага Z, который изменяют почти все команды разве что кроме перехода/возврата, а не только CMP
DEC l_cur_bullet --- читаем в http://www.6502.org/tutorials/6502opcodes.html про комманду DEC - Affects Flags: S Z, т.е. после декремента в зависимости от результата взводятся/сбрасываются флаги Zero и Sign
BNE NextEnemyBullet
итого это можно записать как
l_cur_bullet = l_cur_bullet -1;
if (l_cur_bullet != 0) goto NextEnemyBullet;

ты почему-то думаешь что типа только CMP меняет флаги, но как я уже сказал это делают почти все комманды, даже например LDA - после записи числа в аккумулятор будут изменены флаги S и Z, грубо говоря CMP делается как бы автоматом после почти всех операций
teremochek:
Если я правильно понял, то CMP перед BCC, BCS стоят не просто так.
И по видимому записать такую последовательность:

CMP #5
BCC skp019
CMP #17
BCS DoneWithThisBullet

можно так:

if a - 5 < 0 Then Goto skp019
if a - 17 >= 0 Then Goto DoneWithThisBullet
GManiac:
Конечно, не просто так, CMP - это явное сравнение. Сравнение - это простановка флагов. После многих арифметических и не только команд происходит неявное сравнение результата с нулём, и это может использоваться в следующих командах - обычно, бранчах, но иногда и в других командах и даже в последовательности команд. Что может привести к не совсем очевидной логике для неопытного чтеца.
А если нужно сравнить произвольный операнд с произвольным числом, тут CMP и помогает.

Это самое неявное сравнение, точнее, простановка флагов (причём не всех всегда - некоторые команды проставляют не все флаги), делает асм более гибким, чем ЯВУ, но может усложнить понимание. В сях есть что-то похожее, когда можно и операцию произвести, и сравнение сразу, вроде
if (--a > 0) {...};

Две последние строки можно упростить:
if a < 5 then ...
if a >= 17 then ...
Добавлено позже:
Насчёт "не всех флагов": для M6502 могу ошибаться, но для M68k есть такое.
Навигация
Главная страница сообщений
Следующая страница
Предыдущая страница

Перейти к полной версии