j'ai l'impression que tu demandes bcp de prérequis et que tu passes très vite sur bcp bcp de choses pas évidentes, mais qu'à d'autres endroits tu détailles bcp certaines choses qui sont a priori assez évidentes pour qqun avec tous ces prérequis...
Ensuite je ne suis pas vraiment d'accord avec tes conseils, en pratique une fonction par instruction ça risque d'être bien trop verbeux... ce serait peut-être bien de rentrer dans les détails. Par exemple l'assembleur de GTC a en tout et pour tout 4 types d'instructions, les labels, les instructions "classiques", les sauts, et les instructions de déclarations de données brutes : si il fallait faire une fonction pour add, une autre pour addi, une autre pour addq, une autre pour adda, une autre pour sub, subi, etc... ce serait affreux

Plus précisément, pour les instructions "classiques" j'utilise un moteur de pattern-matching : grosso modo c'est une liste de contraintes "si l'instruction est addq .b/.w/.l, que l'opérande source est une valeur immédiate (A_IMM), utiliser le masque 0x5000, mettre l'opérande source dans le champ D_Q, la taille dans le champ D_SZ et l'opérande destination dans le champ D_EA"

C'est bien sûr un cas un peu particulier parce que l'assembleur 68000 est très régulier, place toujours ses opérandes à 2-3 endroits bien définis, etc..., mais ça permet d'avoir un code très compact et surtout de limiter les risques d'erreurs (j'ai dû avoir en tout et pour tout un bug sur une instruction rare où j'avais mal tapé le masque), et je pense que c'est une technique qui s'applique à plein d'autres plateformes...
Après tu passes *très* vite sur tout ce qui est résolution de labels, alors que je pense que c'est vraiment un point crucial de tout assembleur. Déjà, selon le but de l'assembleur, les contraintes vont varier : est-ce que je veux la performance à tout prix ? si oui, est-ce que mon assembleur doit optimiser "add.w #label2-label1,d0" en "addq.w #label2-label1,d0" quand c'est possible ? ou est-ce qu'au contraire je m'en tape complètement et je peux coder tous mes sauts sur 32/64 bits sans que ça soit gênant ? Si on est dans le dernier cas, pas de problème, il suffit de faire ça de façon bourrin. Sinon, il faut vraiment penser l'assembleur autour de l'optimisation des sauts, prévoir de faire plusieurs passes, etc...
Tu ne parles pas du tout non plus de ce que pourront contenir les opérandes, alors que c'est aussi un truc pas forcément évident. En pratique on va vouloir gérer des expressions littérales type C sur les entiers, pouvoir les évaluer au moment voulu, rajouter à tout ça les labels, peut-être même supporter les différences de labels (ou pas ? ça va dépendre du format objet choisi, c'est important), voire de supporter des calculs plus subtils sur les valeurs des labels (ou pas ? idem). Je ne parle pas non plus des equs et des macros...
Bref, j'espère que ton tuto servira à qqun, mais ça serait peut-être une bonne idée de le compléter, notamment en mettant en valeur les choix de conception qu'il faudra faire

(quel format source : qui gère les macros ? [préprocesseur externe, intégré au langage, non supporté] ; quel format de sortie : linker intégré [utile surtout pour des plateformes embarquées pour lesquelles il n'y aurait pas de linker vraiment utilisable], ou format objet [si oui lequel ?] ; quelles optimisations : optimisation des sauts internes au fichier ? optimisation des instructions [style add->addq] ? si oui, est-ce que ça peut avoir des effets nocifs ? [par exemple a68k permet de désactiver certaines optimisations pour faciliter l'écriture de code auto-modifiant] optimisations plus avancées ? [add->addq quand l'opérande est une différence de labels])