samstyle / Xpeccy

Multiplatform emulator of retro computers
MIT License
73 stars 13 forks source link

Метки внутрь комманд, самомодификация кода #17

Closed Volutar closed 5 years ago

Volutar commented 5 years ago

Очень часто код - самомодифицируемый. То есть вида:

onetime1: ld a,1 and a jr z,label2 xor a ld (onetime+1),a label2: ....

Хотелось бы, чтобы отсылки внутрь инструкций также работали. Либо через смещение в адресации (если метка указывает ровно на саму инструкцию), либо через добавление строки типа:

ld a,1 onetime: equ $-1 ... ld (onetime),a

либо

onetime: equ $+1 ld a,1 ... ld (onetime),a

если метка указывает внутрь инструкции.

Volutar commented 5 years ago

Тут проблема разбивается на две части:

  1. когда метка указывает куда надо, т.е. на начало оператора, но в каких-то местах идёт отсылка на метка+смещение. Это делается относительно просто - метка имеет свою "длину", т.е. сколько байт оператора она охватывает (не только первый), и, соответствующе, может иметь смещение в этом отрезке (от +1 до +3).
  2. когда метка указывает внутрь оператора - возможно решение через equ $+смещение (как описано выше), либо через препроцессинг меток, и замена их на вариант (1).

Если это режим "трассировки", то метки в принципе не нужно рисовать, пока трассировка не остановится. Куча этих доп.сравнений и отражений меток нужна только для ручного хождения по коду.

Задача непростая, но если она будет сделана - получится безусловно лучший дизасм, на уровне, а то и выше IDA.

samstyle commented 5 years ago

Сложность в том, что дизасм знает только о том, что он показывает. Остальное для него - "куча каких-то байт". Между делом, метка может указывать не только на код. Это может быть ascii-строка, внутри которой что-то меняется. Это может быть "DW слово", у которого меняют старший байт...

Volutar commented 5 years ago

Эти ассемблерные структуры, к сожалению, недоступны. Внутрь структур или строк, с каким-то конкретным известным типом данных (как в том же IDA) - это задача как бы совсем другого уровня, и хранить как-то всю эту кухню, или каждый раз генерировать - гемор. Логичнее тут просто смотреть в радиусе 3х байт относительно ссылаемого адреса (условно) имеющиеся метки. Брать ближайшую в этом радиусе, и на неё ссылаться со сдвигом. Это как вариант решения (1).

А на вариант (2) - только во время отображения детектить что метка попадает внутрь, и не отображается, и рисовать её отдельной строкой, добавляя строчку с EQU. Проще - ниже, в виде equ $-N. Но наверное, можно и выше. Просто если этого не делать, то в дебаггере, в зависимости от кодестайла, будет немало меток, отсылка на которые есть, а увидеть их нельзя - они слева просто никогда не будут отображаться.

samstyle commented 5 years ago

Оба варианта будут работать "не всегда". Если метка изначально ссылается внутрь команды, то никакого сдвига относительно неё не будет. А с equ - обратный эффект. Если обращение было через (label + n), то никакой метки внутри команды не будет, мы будем иметь слово, которое может быть как адресом, так и просто каким-то числом. См пример: 0x8001 тут вообще не start+1

org 0x8000
start:
......
; и тут где-то встречается
ld hl,0x8001
and 7
ret z
lab:
rr h
rl l
dec a
jr nz,lab
ret
Volutar commented 5 years ago

А если там будет ld hl,0x8000, не связанный с адресом, он вместо него подставит start? Это же точно также будет неправильно, как и в случае 0x8001. Речь же не просто о вордах, а об операторах с прямой адресацией, иначе это надо анализатор кода какой-то делать - об этом речи нет.

samstyle commented 5 years ago

Что-то проклюнулось screenshot_20190114_212748

Volutar commented 5 years ago

Нифига себе :) Круто! Ну это кейс №2, когда метка существует, и она указывает внутрь команды. Но кейс №1 - когда метка указывает на саму команду, но отсылка идёт со смещением. Тут лично я не вижу никаких иных способов, кроме как включать в метку сдвиговый параметр, если адрес оператора входит в диапазон от -3 до +3 (макс. длина оператора 4 - и это уже по-идее должна быть другая метка). Ну и это только для тех операторов, которые не просто загрузка регистров, а прямая работа с памятью (ld r/rr,(N) и ld (N),r/rr). Возможно и ещё какие-то операторы, но по своему опыту это просто хранения переменных внутри самого кода в параметрах, и впихивание их по прямым адресам.

Подумалось, что этот параметр min/max можно конфигуряемым сделать . У меня допустим есть структуры где метка на начало а длина 12 байт, и было бы неплохо чтобы все отсылки внутрь неё шли по метка+смещение.

samstyle commented 5 years ago

Метка ищется на 4 байта назад screenshot_20190117_190722

Как вариант: для кода область действия - длина команды, для остальных - 8 байт

Volutar commented 5 years ago

Это же чисто визуальный хелпер, тут не будет вреда если сделать "радиус" настраиваемым (и в плюс и в минус).

В моём коде PSM вообще бывают Label-13 (тут как бы можно просто любой адрес интерпретировать как ближайшая_метка +- смещение). Это же касательно операторов типа LD reg,(addr) или LD (addr),reg, но не LD HL,value. Загрузка данных в регистр лично для меня вообще не должна отсылаться к меткам, даже если она +0. Интерпретация каждого значения #FFFF как метки, в случае наличия метки по этому адресу, чревато кучей мест, когда вывод этих значений как метки в 99% случаев будет ошибочным.

Это безусловно прискорбно, что просто 16разрядные значения EQU, и 16разрядные адреса меток - не имеют никакого признака для того чтобы их отличать.

Хотя, можно было бы принять какое-то соглашение о префиксах, чтобы эту таблицу меток интерпретировать более красиво. Например, если название метки начинается с символа _, то это не адрес. Можно было бы это даже сделать в опциях. Назвать поле например "const label prefixes", и если оно задано, интерпретировать все 16разрядные значения, совпадающие со значениями меток с таким префиксом, как метки, даже если это LD DE,#8004. Разрешить целый ряд этих префиксов, через запятую, мало ли как бывает.

Примерно такие мысли.

Но вообще выглядит уже зашибенски :) С такими фишками этот "на коленках писанный дебаггер" существенно удобнее всех имеющихся.