
Carattere OpenType che disassembla le istruzioni Z80
Qual è il tuo disassemblatore preferito? Il mio è un font:
https://github.com/user-attachments/assets/bb6ceb18-c2fd-40a9-be4f-202321a214d9
Questo font converte sequenze di caratteri esadecimali minuscoli in istruzioni Z80 disassemblate, facendo ampio uso della Glyph Substitution Table (GSUB) e Glyph Positioning Table (GPOS) di OpenType.
Se vuoi solo provarlo, una copia è disponibile in ./test/z80-sans.ttf.
Testato su Debian GNU/Linux 12. Nota che questa versione di Debian include ruby versione 3, mentre fontcustom è stato scritto per ruby versione 2 ed è incompatibile con versioni successive (ad esempio errori di sintassi). Un'installazione di ruby richiede anche una versione compatibile di OpenSSL. Pertanto, è possibile utilizzare RVM per gestire sia ruby che un'installazione locale di 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
Il file del font .ttf viene copiato in ~/.local/share/fonts/, che è utilizzato ad esempio da LibreOffice.
Rispetto ad altri font maledetti, Z80 Sans presenta queste sfide:
CALL), ma questo si collega anche al punto successivo...65536 * 7 = 458752 combinazioni possibili;0x80..0xff devono essere resi come numero negativo in complemento a due;Tutto ciò richiede una soluzione programmatica. Mentre fontcustom e ImageMagick si occupano di generare i glifi, sembra che un modo conveniente per scrivere le regole di lookup sia il formato .fea, ma non ho trovato un modo per integrarlo con il formato .ttx di fonttools (che è fondamentalmente xml). Ho adottato l'approccio del minimo comune denominatore modificando direttamente il .ttx di Noto Sans Mono (sebbene le forme dei glifi siano calcolate da Droid Sans Mono, dato che è quello con cui ho iniziato quando ho modificato FontForge).
Un parser a discesa ricorsiva viene utilizzato per generare tutti i glifi possibili, il che aiuta a valutare le espressioni nelle codifiche (ad esempio SET b,(IX+o) prende un bit e uno spostamento, codificato come espressione DD CB o C6+8*b). Queste codifiche sono state poi espanse a tutti i possibili valori che gli operandi possono assumere, prima di associare infine 1 o più byte esadecimali a ciascun glifo di disassemblaggio necessario per rendere un'istruzione espansa.
Ci sono alcuni buoni riferimenti per le funzionalità OpenType, ma sono scritti a livello alto, o in formato .fea(?):
Non è mai molto chiaro come tradurli in .ttx, quindi alla fine ho semplicemente convertito tutta la famiglia Noto Sans e ho usato il buon vecchio approccio brute force di 'imparare dall'esempio'. Questo è ancora più divertente di quanto sembri, grazie a molti fallimenti silenziosi durante la conversione da .ttx a .ttf, dove i lookup non corrispondono a causa di alcune ipotesi non validate da fonttools (ad esempio, le definizioni delle classi per le sostituzioni contestuali a catena devono avere almeno un glifo di copertura con class value='1').
Praticamente la maggior parte delle sfide sono state risolte con regole di concatenamento contestuale. Per gestire gli indirizzi, ogni nibble nell'intervallo 0..f è stato codificato con glifi distinti, con caratteri di spaziatura utilizzati per creare sostituzioni multiple, un carattere alla volta. Gli spostamenti hanno anche varianti con segno aggiuntive. Questo ci dà un totale di (4 + 2) * 16 glifi per i numeri. Questo è stato già sufficiente per mantenere il file del font sotto il limite di 65536 glifi.
La parte peggiore era ovviamente gli operandi fuori ordine. Tuttavia, a causa del numero limitato di variazioni che questi hanno nelle istruzioni, potrebbero essere coperti dalla stessa strategia delle istruzioni con prefissi codificati ambiguamente, ad esempio
["SET b,(IX+o)", "DD CB o C6+8*b"],
["SET b,(IY+o)", "FD CB o C6+8*b"],
È coperto dalle stesse regole di lookup di:
["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"],
Una proprietà interessante nell'ISA Z80 è che i bit e i registri hanno fino a 8 variazioni, e questi casi fuori ordine coinvolgono solo offset e uno di quegli operandi specifici. Pertanto, possiamo codificare bit o registri come letterali. Con sufficienti lookahead, possiamo abbinare fino all'ultimo byte esadecimale e creare lookup dedicati per ogni caso. Gli ultimi letterali possono essere ridotti generando una legatura che corrisponde al glifo suffisso. Il risultato finale sono state dozzine di lookup generati in più per questi casi (che possono probabilmente essere raggruppati per ridurre questo numero).
LD (IX+o),r viene reso come LD (IX+o r),;SET b,(IX+o) viene reso come SET b,(IX+o));FontForge supporta la modifica scriptabile delle funzionalità utilizzando i comandi GenerateFeatureFile() e MergeFeature() (brevemente trattati in The Terrible Secret of OpenType Glyph Substitution - Ansuz - mskala's home page). Ne sono venuto a conoscenza solo dopo aver realizzato l'implementazione basata su .ttx, ma avrebbe potuto potenzialmente evitare di dover armeggiare con i file .ttx.
Per set di istruzioni più complessi, un approccio alternativo che sembra avere meno vincoli è l'uso di font shapers. Alcuni esempi:
./resources/instructions.json è stato adattato da maziac/z80-instruction-set;./resources/instructions.json è sotto GNU Lesser General Public License versione 3;