
OpenType шрифт, который дизассемблирует инструкции Z80
Какой у вас любимый дизассемблер? Мой — шрифт:
https://github.com/user-attachments/assets/bb6ceb18-c2fd-40a9-be4f-202321a214d9
Этот шрифт преобразует последовательности шестнадцатеричных символов нижнего регистра в дизассемблированные инструкции Z80, используя широкие возможности Glyph Substitution Table (GSUB) и Glyph Positioning Table (GPOS).
Если вы хотите просто попробовать, копия доступна по пути ./test/z80-sans.ttf.
Протестировано на Debian GNU/Linux 12. Обратите внимание, что эта версия Debian поставляется с Ruby версии 3, в то время как fontcustom был написан для Ruby версии 2 и несовместим с более поздними версиями (например, синтаксические ошибки). Установка Ruby также требует совместимой версии OpenSSL. Поэтому RVM можно использовать для управления как Ruby, так и локальной установкой OpenSSL.
apt install imagemagick potrace
pip install fonttools
git submodule update --init --recursive
# fontforge
(
cd ./modules/fontforge/
git checkout 4f4907d9541857b135bd0b361099e778325b4e28
git apply ../../resources/fontforge.diff
mkdir -p build
cd build
cmake -GNinja ..
ninja
ninja install
)
# woff2
(
cd ./modules/woff2/
make clean all
)
# fontcustom
rvm use 2.7
rvm pkg install openssl
rvm install 2.4 --with-openssl-dir=$HOME/.rvm/usr
gem update --system 3.3.22
(
export PATH=$PWD/modules/woff2/build:$PATH
cd ./modules/fontcustom/
git apply ../../resources/fontcustom.diff
gem build fontcustom.gemspec
gem install ./fontcustom-2.0.0.gem
)
cp ./resources/droid-sans-mono.ttf /tmp/base.ttf
./gen.py ./resources/instructions.json
Файл шрифта .ttf копируется в ~/.local/share/fonts/, который используется, например, LibreOffice.
По сравнению с другими проклятыми шрифтами, Z80 Sans имеет следующие сложности:
CALL), однако это также связано со следующим пунктом...65536 * 7 = 458752 возможных комбинаций;0x80..0xff должны отображаться как отрицательное число в дополнительном коде;Всё это требует программного решения. В то время как fontcustom и ImageMagick занимаются генерацией глифов, удобным способом написания правил подстановки, по-видимому, является формат .fea, но я не нашёл способа интегрировать его с форматом .ttx от fonttools (который по сути является xml). Я пошёл по пути наименьшего общего знаменателя, напрямую редактируя .ttx от Noto Sans Mono (хотя формы глифов вычислены из Droid Sans Mono, так как именно с него я начинал, патча FontForge).
Для генерации всех возможных глифов используется рекурсивный нисходящий парсер, что помогает вычислять выражения в кодировках (например, SET b,(IX+o) принимает бит и смещение, закодированные как выражение DD CB o C6+8*b). Затем эти кодировки расширялись до всех возможных значений, которые могут принимать операнды, после чего каждому глифу дизассемблирования, необходимому для отображения расширенной инструкции, сопоставлялся один или несколько шестнадцатеричных байтов.
Есть хорошие источники по функциям OpenType, но они написаны на высоком уровне или в формате .fea(?):
Никогда не было ясно, как перевести их в .ttx, поэтому в итоге я просто конвертировал всё семейство Noto Sans и использовал старый добрый метод перебора — «обучение на примерах». Это даже веселее, чем звучит, благодаря множеству молчаливых ошибок при конвертации из .ttx в .ttf, когда подстановки не срабатывают из-за некоторых предположений, не проверяемых fonttools (например, определения классов для замен в контекстных цепочках должны содержать хотя бы один охватывающий глиф с class value="1").
Практически все сложности были решены с помощью правил контекстной цепочки. Для обработки адресов каждый полубайт в диапазоне 0..f был закодирован отдельными глифами, с использованием пробельных символов для создания множественных подстановок по одному символу за раз. Смещения также имеют дополнительные знаковые варианты. Это даёт нам в общей сложности (4 + 2) * 16 глифов для чисел. Этого уже было достаточно, чтобы размер файла шрифта оставался в пределах лимита в 65536 глифов.
Худшей частью, конечно, были операнды не по порядку. Однако, благодаря ограниченному количеству вариаций в инструкциях, их можно покрыть той же стратегией, что и инструкции с неоднозначно закодированными префиксами, например:
["SET b,(IX+o)", "DD CB o C6+8*b"],
["SET b,(IY+o)", "FD CB o C6+8*b"],
Покрывается теми же правилами подстановки, что и:
["SRA (IX+o)", "DD CB o 2E"],
["SRA (IY+o)", "FD CB o 2E"],
["SRL (IX+o)", "DD CB o 3E"],
["SRL (IY+o)", "FD CB o 3E"],
Интересное свойство архитектуры Z80 в том, что биты и регистры имеют до 8 вариаций, и эти случаи нарушения порядка включают только смещения и один из этих конкретных операндов. Поэтому мы можем кодировать биты или регистры как литералы. С достаточным опережающим просмотром мы можем сопоставить до последнего шестнадцатеричного байта и создать специальные подстановки для каждого случая. Последние литералы можно сократить, создав лигатуру, совпадающую с суффиксным глифом. В итоге для этих случаев было создано несколько десятков дополнительных подстановок (которые, вероятно, можно сгруппировать, чтобы уменьшить их количество).
LD (IX+o),r отображается как LD (IX+o r),;SET b,(IX+o) отображается как SET b,(IX+o));FontForge поддерживает скриптовую модификацию функций с помощью команд GenerateFeatureFile() и MergeFeature() (кратко рассмотрено в The Terrible Secret of OpenType Glyph Substitution - Ansuz - mskala's home page). Я узнал об этом только после реализации на основе .ttx, но это могло бы избежать возни с файлами .ttx.
Для более сложных наборов инструкций альтернативным подходом, который, кажется, имеет меньше ограничений, является использование шейперов шрифтов. Некоторые примеры:
./resources/instructions.json был адаптирован из maziac/z80-instruction-set;./resources/instructions.json распространяется под GNU Lesser General Public License version 3;