
Police OpenType qui désassemble les instructions Z80
Quel est votre désassembleur préféré ? Le mien est une police :
https://github.com/user-attachments/assets/bb6ceb18-c2fd-40a9-be4f-202321a214d9
Cette police convertit des séquences de caractères hexadécimaux minuscules en instructions Z80 désassemblées, en faisant un usage intensif de la Table de substitution de glyphes (GSUB) et de la Table de positionnement de glyphes (GPOS) d'OpenType.
Si vous souhaitez simplement l'essayer, une copie est disponible sous ./test/z80-sans.ttf.
Testé sur Debian GNU/Linux 12. Notez que cette version de Debian est livrée avec Ruby version 3, alors que fontcustom a été écrit pour Ruby version 2 et est incompatible avec les versions ultérieures (p. ex., erreurs de syntaxe). Une installation de Ruby nécessite également une version compatible d'OpenSSL. Par conséquent, RVM peut être utilisé pour gérer à la fois Ruby et une installation locale d'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
Le fichier de police .ttf est copié dans ~/.local/share/fonts/, qui est utilisé par exemple par LibreOffice.
Comparée à d'autres polices maudites, Z80 Sans présente les défis suivants :
CALL), mais cela nous amène au point suivant...65536 * 7 = 458752 combinaisons possibles ;0x80..0xff doivent être rendus comme un nombre négatif en complément à deux ;Tout cela invite à une solution programmatique. Alors que fontcustom et ImageMagick se chargent de générer les glyphes, il semble qu'une manière pratique d'écrire des règles de recherche soit le format .fea, mais je n'ai pas trouvé de moyen de l'intégrer au format .ttx de fonttools (qui est essentiellement du xml). J'ai adopté l'approche du plus petit dénominateur commun en éditant directement le .ttx de Noto Sans Mono (bien que les formes des glyphes soient calculées à partir de Droid Sans Mono, car c'est avec cela que j'ai commencé en patchant FontForge).
Un analyseur par descente récursive est utilisé pour générer tous les glyphes possibles, ce qui aide à évaluer les expressions dans les encodages (par exemple, SET b,(IX+o) prend un bit et un déplacement, encodé comme expression DD CB o C6+8*b). Ces encodages ont ensuite été étendus à toutes les valeurs possibles que les opérandes peuvent prendre, avant d'associer finalement 1 ou plusieurs octets hexadécimaux à chaque glyphe de désassemblage nécessaire pour rendre une instruction étendue.
Voici quelques bonnes références pour les fonctionnalités OpenType, mais elles sont rédigées à un niveau élevé ou au format .fea(?) :
Il n'est jamais très clair comment les traduire en .ttx, donc au final j'ai simplement converti toute la famille Noto Sans et utilisé la bonne vieille approche de force brute de « l'apprentissage par l'exemple ». C'est encore plus amusant que cela n'en a l'air, grâce à de nombreux échecs silencieux lors de la conversion de .ttx en .ttf, où les recherches ne correspondent pas en raison de certaines hypothèses non validées par fonttools (par exemple, les définitions de classes pour les substitutions contextuelles en chaîne doivent avoir au moins un glyphe de couverture avec une valeur de classe="1").
La plupart des défis ont été résolus avec des règles de chaînage contextuel. Pour gérer les adresses, chaque quartet dans la plage 0..f a été encodé avec des glyphes distincts, avec des caractères d'espacement utilisés pour créer plusieurs substitutions, un caractère à la fois. Les déplacements ont également des variantes signées supplémentaires. Cela nous donne un total de (4 + 2) * 16 glyphes pour les nombres. Cela était déjà suffisant pour maintenir le fichier de police sous la limite de 65536 glyphes.
La pire partie était bien sûr les opérandes dans le désordre. Cependant, en raison du nombre limité de variations qu'ils ont dans les instructions, ils pouvaient être couverts par la même stratégie que les instructions avec des préfixes encodés de manière ambiguë, par exemple :
["SET b,(IX+o)", "DD CB o C6+8*b"],
["SET b,(IY+o)", "FD CB o C6+8*b"],
Est couvert par les mêmes règles de recherche que :
["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"],
Une propriété intéressante du jeu d'instructions Z80 est que les bits et les registres ont jusqu'à 8 variations, et ces cas de désordre n'impliquent que des décalages et un de ces opérandes spécifiques. Par conséquent, nous pouvons encoder des bits ou des registres comme des littéraux. Avec suffisamment de lookaheads, nous pouvons faire correspondre jusqu'au dernier octet hexadécimal et créer des recherches dédiées pour chaque cas. Les derniers littéraux peuvent être réduits en générant une ligature qui correspond au glyphe suffixe. Le résultat final a été des dizaines de recherches supplémentaires générées pour ces cas (qui peuvent probablement être regroupées pour réduire ce nombre).
LD (IX+o),r est rendu comme LD (IX+o r), ;SET b,(IX+o) est rendu comme SET b,(IX+o)) ;FontForge prend en charge la modification scriptable des fonctionnalités à l'aide des commandes GenerateFeatureFile() et MergeFeature() (brièvement couvert dans The Terrible Secret of OpenType Glyph Substitution - Ansuz - mskala's home page). Je n'en ai eu connaissance qu'après avoir réalisé l'implémentation basée sur .ttx, mais cela aurait potentiellement évité de devoir toucher aux fichiers .ttx.
Pour des jeux d'instructions plus complexes, une approche alternative qui semble avoir moins de contraintes est d'utiliser des shapeurs de police. Quelques exemples :
./resources/instructions.json a été adapté depuis maziac/z80-instruction-set ;./resources/instructions.json est sous GNU Lesser General Public License version 3 ;