Site logo

Triceraprog
La programmation depuis le Crétacé

  • Adressage 6502, un récapitulatif ()

    Si vous aussi vous jetez régulièrement un œil sur les adressages possibles et valides pour le 6502, voici un tableau récapitulatif qui pourra vous servir. En tout cas, il me servira.

    Tableau Récapitulatif

    Mode Syntaxe Exemple Taille Cycles Notes
    Implicite OP RTS 1 2-6 -
    Accumulateur OP A LSR A 1 2 -
    Immédiat OP #nn LDA #$42 2 2 Constante
    Page Zéro OP $nn LDA $80 2 3-5 Rapide
    Page Zéro,X OP $nn,X LDA $80,X 2 4-6 Wrapping en page 0
    Page Zéro,Y OP $nn,Y LDX $10,Y 2 4 LDX/STX uniquement
    Absolu OP $nnnn LDA $1234 3 4-6 -
    Absolu,X OP $nnnn,X LDA $2000,X 3 4-7 +1 si page crossed
    Absolu,Y OP $nnnn,Y LDA $3000,Y 3 4-7 +1 si page crossed
    Relatif OP label BEQ Loop 2 2-4 Branches uniquement
    Indirect JMP ($nnnn) JMP ($FFFC) 3 5 JMP uniquement
    Indexed Indirect OP ($nn,X) LDA ($40,X) 2 6 X ajouté d'abord
    Indirect Indexed OP ($nn),Y LDA ($40),Y 2 5-6 Y ajouté après

    ($nn,X) vs ($nn),Y

    Indexed Indirect vs. Indirect Indexed, ma source de confusion préférée.

    Écriture ($nn,X) ($nn),Y
    Évaluation X ajouté avant lecture Y ajouté après lecture
    Calcul addr = read16(($nn+X)&$FF) addr = read16($nn)+Y
    Wrapping X wrappe en page 0 Y sur 16-bit complet
    Usage typique Tables de pointeurs, dispatch Tableaux, structures

    Références

    Les deux pages sur lesquelles je vais régulièrement :


  • Forth sur 6502, épisode 11 ()

    ACCEPT, enfin !

    Cela aura pris un peu de temps, entre autres raisons car d'autres activités se sont invitées entre-temps, mais ACCEPT est enfin implémenté ! ACCEPT est un mot qui lit une ligne de texte entrée par l'utilisateur et la stocke dans une adresse mémoire fournie lors de l'appel. Généralement, cette adresse est le TIB (Terminal Input Buffer), qui sera ensuite fournie à l'interpréteur Forth.

    Comme indiqué lors du précédent article, se posait la question de savoir comment récupérer les données. J'ai tenté deux versions et je suis finalement partie sur celle que je pressentais : utiliser la mémoire PPU pour stocker les données en cours d'édition, puis les récupérer lorsque la touche RETURN est appuyée.

    Oui, mais... même si la mémoire représentant les caractères est linéaire, depuis le début de l'implémentation j'ai réservé des caractères sur les bords de l'écran pour ne rien y afficher, afin de respecter les marges nécessaires lors d'un affichage sur écran cathodique. Récupérer les données nécessite donc de prendre des morceaux de mémoire à différents endroits. Pas forcément très complexe, mais ça nécessite un peu plus de réflexion qu'un simple transfert en une fois.

    Et tant qu'à faire, puisque je dois définir une géométrie d'écran logique, pourquoi ne pas implémenter un mot WINDOW qui permettrait de définir les marges haut, bas, gauche et droite ? C'est ce que j'ai commencé par faire, en adaptant la gestion du curseur qui était codée en dur avec des marges fixes. Deux routines me permettent d'avancer et de reculer le curseur en respectant cette géométrie de fenêtre logique, et c'est bon. Les marges sont toujours là, initialisées au démarrage, mais à présent elles sont configurables.

    Retour sur ACCEPT. Au début, il s'agit uniquement de reprendre le code d'affichage de caractères que j'avais déjà. Le code me semble quand même voué à être un peu long, je lui dédie donc un fichier. Il me faut implémenter l'édition d'une ligne logique, avec gestion des touches curseur du clavier (au moins droite et gauche), ainsi que des touches INS (insertion), DEL (suppression) et RETURN (validation de la ligne). Ces touches ne sont pas encore branchées, je repasse donc sur la gestion du clavier.

    Gestion du clavier complet

    Tant qu'à faire, puisque je suis sur le clavier, j'en profite pour gérer les majuscules, minuscules et kanas. Pour cela, il me faut compléter la fonte. Celle que j'avais choisie n'a pas tous ces caractères. J'en cherche une et je tombe sur la fonte Misaki, qui, en plus des katakanas, contient des caractères graphiques. Parfait pour utiliser la touche GRPH du clavier.

    La fonte format bitmap 8x8 est fournie avec beaucoup de caractères. J'écris donc un petit script Python pour en prendre une sélection et générer ma fonte dans le format Famicom attendu. Puis je mappe tous les scancodes, un par un, vers leurs équivalents pour quatre tables différentes : majuscules, minuscules, kanas et graphiques. Je sélectionne quelques codes entre 0 et 32 pour les touches de contrôle, en respectant les codes classiques et en improvisant un peu pour les autres.

    Et me voilà avec un mapping complet. Je peux compléter ACCEPT pour gérer les touches SHIFT, KANA et GRAPH. Les touches de direction avant et arrière ne posent pas beaucoup de problèmes... à part le curseur.

    Tant que le curseur était en fin de ligne, c'était simple. Mais maintenant qu'il se déplace sur des caractères déjà présents, je veux le faire clignoter afin de laisser le caractère en dessous apparent. Et pour savoir quel est ce caractère, il faut aller le chercher... dans la mémoire de l'écran !

    Transferts PPU <-> RAM

    Je laisse donc le clavier de côté. Il est temps de s'occuper des transferts RAM/PPU. J'ai déjà, via le framework utilisé, un transfert de buffer de la RAM vers la mémoire PPU. Celui-ci fonctionne par recopie de données dans un buffer de commandes. C'est bien pour la sécurité des données : comme elles sont copiées, la source peut changer en RAM, elles sont au chaud dans la commande. Par contre, cela prend du temps de recopie et de la place en RAM.

    J'implémente donc une version qui permet de référencer une adresse mémoire et une longueur, qui servent directement lors du décodage des commandes pendant la synchronisation verticale. À la charge de l'appelant d'assurer que les données sont toujours là où elles doivent être. J'en profite pour afficher le message de bienvenue avec cette commande, afin de valider le fonctionnement.

    De là, il est assez facile d'en faire la commande symétrique : une commande qui référence une adresse mémoire et une longueur en RAM, et qui y copiera les résultats de requêtes PPU lors de la prochaine synchronisation verticale. En spécifiant comme adresse celle d'une variable contenant le caractère sous le curseur et comme longueur 1, je peux récupérer ce caractère courant.

    Pour être sûr des buffers, j'attends la synchronisation verticale après chaque déplacement de curseur. Pendant l'édition d'une ligne d'entrée logique, ce n'est pas très important.

    Retour à l'édition de ligne logique

    Bien. À présent, j'ai la possibilité de récupérer le caractère sous le curseur, mais aussi tous les caractères à l'écran. Avant d'implémenter RETURN, je fais un détour par INS et DEL, afin de pouvoir faire de l'édition et de ne pas partir du principe que l'utilisateur tape une ligne parfaite du premier coup. Mes multiples tests montrent que ce n'est pas le cas. J'ai besoin de ces touches !

    Un détour par le Family BASIC me montre que INS insère un espace à l'emplacement du curseur, ok, mais que DEL est en fait un BACKSPACE : il supprime le caractère avant le curseur et recule le curseur d'une position. Par cohérence, j'implémente le même comportement.

    Cependant, il n'est pas possible de déplacer des morceaux de mémoire PPU directement. Ou alors je n'ai pas trouvé (et les personnes à qui j'ai demandé ne savaient pas). Qu'à cela ne tienne, ACCEPT a à sa disposition un buffer de travail dans lequel il déposera les données finales. Je l'utilise comme buffer temporaire. Pour les deux opérations, je récupère les données de l'écran (en respectant la géométrie de la fenêtre logique), je fais la modification en RAM, puis je renvoie le tout à l'écran.

    Je ne m'embête pas à trouver la sous-partie qui va bouger, je récupère et je renvoie tout. J'attends même une synchronisation verticale pour chaque ligne physique transférée pour être certain de tenir dans le temps de synchronisation. Sur émulateur, ça ne bronche pas. On verra sur vrai matériel. Au pire, je pourrais grouper quelques transferts pour trouver le bon équilibre. Mais là encore, on est en pleine édition de ligne logique utilisateur, on peut se permettre des attentes sans affecter l'expérience utilisateur.

    ACCEPT !

    L'avantage avec l'implémentation de INS et DEL, c'est qu'il y a déjà tout ce qu'il faut pour implémenter RETURN. J'ai la récupération des données en RAM. Reste à pousser la longueur actuelle de la ligne logique sur la pile. Cette longueur est maintenue au fur et à mesure des opérations d'édition.

    Il reste des choses à faire pour une édition au clavier agréable et complète. Et une paire de petits bugs, dont la longueur de la ligne logique qui n'est pas mise à jour correctement dans certains cas. Je m'en occuperai plus tard. Il est grand temps d'avancer vers INTERPRET !

    Amélioration du framework de test

    Au passage, avec l'arrivée du clavier, j'ai ajouté comme prévu l'injection de caractères depuis le framework de tests. Cela m'a permis d'écrire un test pour INS et un autre pour DEL. Ces tests demandent la mise en place de contextes en plusieurs étapes, avec parfois des attentes de frames pour attendre les résultats. Les tests commençaient à être très verbeux et j'ai donc fait une passe pour factoriser tout ça dans des fonctions d'aide qui me permettent d'écrire le déroulement des opérations qui seront ensuite conduites de manière asynchrone.

    Interprète... interprète !

    Maintenant que j'ai une boucle d'entrée de texte, la prochaine étape est l'interprétation du résultat. Cela va nécessiter l'écriture de nombreux mots standards de Forth (au passage, j'ai implémenté DROP car actuellement je dois ignorer le retour de ACCEPT). Cependant, ces mots devraient être nettement plus simples que ACCEPT ou la gestion clavier.

    La « simple » implémentation d'ACCEPT a eu de nombreuses ramifications. J'en ai même profité pour faire quelques petites optimisations/simplifications simples au passage.

    J'aimerais pour la suite pouvoir écrire quelque chose comme HEX 7FF FF C!, qui écrit la valeur 0xFF à l'adresse 0x7FFF, ce que le framework de test peut détecter. Cela signifie le découpage par mots de la chaîne d'entrée, un traitement de base numérique, le parsing de nombres dans cette base et l'implémentation de C! (le plus facile de toute la liste).

    Partons là-dessus !

    Forth sur 6502 - écriture interactive à l'écran 2


  • Deux livres pour le développement sur Famicom ()

    En attendant le prochain article de la série sur le développement d'un Forth sur Famicom, qui prend un peu de temps, je voulais faire une petite aparté pour présenter les deux livres qui m'accompagnent, en plus de différentes ressources en ligne.

    En effet, un livre que l'on peut feuilleter, où on peut retrouver un passage rapidement, c'est pratique.

    Making Games for the NES

    Le premier livre est en anglais et aborde le développement de jeux pour la NES, et donc pour la Famicom. Très progressif, il amène chaque aspect de la machine avec clarté. Pas forcément dans les plus obscurs des détails et c'est aussi ce qui est intéressant : c'est une bonne première approche.

    Le livre date un peu. Ça n'est pas très important pour la console elle-même, qui n'a pas bougé, mais quelques outils évoqués ne sont plus forcément les meilleurs choix.

    L'auteur, Steven Hugg, a écrit d'autres livres pour d'autres machines et est aussi derrière le site 8bitworkshop, qui propose un environnement de développement en ligne pour différentes machines, dont la NES. Tous les exemples du livre y sont disponibles et peuvent être testés et modifiés directement dans le navigateur.

    Livre Making Games for the NES

    Langage machine 6502 et 6510

    Le second livre est en français et aborde le langage machine du 6502 ainsi que les particularités du 6510. C'est avant tout un livre écrit avec le Commodore 64 en tête, mais sa structure en fait une bonne référence à garder sous la main pour la partie 6502.

    N'ayant pas une grande expérience du 6502 (même si depuis le début du projet, j'ai progressé) je m'y réfère encore pas mal, surtout pour les modes d'adressages basés sur X et Y, pour lesquels j'ai encore tendance à tout mélanger.

    Je me sers aussi du site 6502.org, mais comme je le disais, avoir un livre sous la main, c'est pratique. Et en français, c'est reposant.

    Merci à l'auteur, Philippe Gianviti.

    Livre Langage machine 6502 et 6510


  • Forth sur 6502, épisode 10 ()

    Un curseur et des branches

    Le clavier est pour le moment implémenté avec deux mots. L'un qui sera gardé, KBDSCAN et l'autre qui est là en attendant de pouvoir écrire la même chose en Forth, KBDPROCESS. L'objectif premier est de transformer KBDPROCESS en son équivalent Forth que je placerai dans ma boucle principale (pas encore QUIT, qui n'est pas encore prêt).

    Mais avant toute chose, j'ai quelque chose à corriger avec le curseur. Pour le moment, j'affiche le curseur avec EMIT, ce qui fait avancer la prochaine position d'affichage de caractère. Ce que je veux, c'est afficher le caractère reçu du clavier avec EMIT puis afficher le caractère du curseur sans faire avancer la position d'affichage. Ainsi, le caractère du curseur sera toujours une position après le dernier caractère affiché.

    Pour cela, j'ai un peu remanié le code afin de séparer l'envoi du caractère à afficher et la mise à jour de la position d'affichage. Le code de EMIT appelle les deux fonctions et j'ai ajouté le mot PUTCHR qui affiche le caractère à la position courante sans avancer la position du curseur.

    Ainsi, l'affichage d'un caractère reçu depuis le clavier devient :

    KEY             \ Le caractère reçu du clavier est mis sur la pile
    EMIT            \ Affiche le caractère présent sur la pile et avance le curseur
    CURSOR_CODE @   \ Récupère le code du caractère du curseur
    PUTCHR          \ Affiche le caractère du curseur sans avancer le curseur
    

    Le fait de mettre le code du curseur dans une variable pointée par CURSOR_CODE permettra de changer la forme du curseur en fonction du mode (pour indiquer si on est en mode KANA ou non par exemple).

    Avec un mot KEY bloquant, cela serait suffisant. Ce bout de code serait mis dans la boucle infinie et puis voilà. Seulement, je veux pouvoir opérer d'autres traitements dans la boucle d'attente de caractère. Faire clignoter le curseur par exemple, sans gestion par interruption. Et puis, c'est une bonne occasion d'implémenter une branche conditionnelle.

    Le standard Forth actuel définit IF, ELSE et THEN de manière haut niveau, en indiquant le comportement attendu. Dans les Forth plus anciens, comme le FIG-Forth dont je m'inspire, le détail d'implémentation est d'utiliser les mots bas niveau 0BRANCH et BRANCH pour déplacer IP (le pointeur d'instruction).

    J'ai déjà BRANCH, c'est le mot qui me permet d'avoir ma boucle infinie, en reculant IP de façon inconditionnelle. QUIT, actuellement, ressemble à ceci :

        MAIN_LOOP       \ Appel du mot dans lequel je fais mes tests
        BRANCH          \ Déplacement inconditionnel de IP en ajoutant le mot qui suit en mémoire
        $fffc           \ le mot qui suit en mémoire : -4. Ce qui ramène IP au niveau de MAIN_LOOP
    

    0BRANCH fonctionne de la même manière, mais ne déplace IP que si la valeur au sommet de la pile est zéro. Dans le cas contraire, l'instruction ne fait rien d'autre que déplacer l'IP pour passer par-dessus le mot qui suit en mémoire, puis donne le contrôle à l'instruction suivante. Dans tous les cas, la valeur sur la pile est retirée de la pile.

    Le mot est assez simple à implémenter avec mes briques existantes. Voici le code assembleur :

        ; 0BRANCH, branches if TOS is zero, else continues
    DEFINE_CODE_WORD_WITH_SYMBOL ZERO_BRANCH, "0BRANCH", BRANCH, 0
        ; Pop TOS into Temp
        POP_PARAM_STACK_TO_REG Temp
    
        ; If Temp (16 bits) is zero, do the branch, call branch
        lda Temp
        ora Temp + 1
        beq BRANCH_word_pfa
    
        ; Not zero, advance IP by 2 (to skip the branch offset)
        inc_reg REG_IP
        inc_reg REG_IP
        END_TO_NEXT
    

    (rappel : TOS signifie "Top Of Stack", le sommet de la pile de données)

    Avec 0BRANCH, le traitement et affichage du clavier devient :

        KBDSCAN         \ Scan du clavier et mise à jour des tampons
        KEY?            \ Y a-t-il un caractère disponible ?
        0BRANCH         \ Si non, branchement
        $000c           \ vers l'instruction après PUTCHR (12 octets plus loin)
        KEY             \ Récupération du caractère
        EMIT            \ Affichage du caractère et avancée du curseur
        CURSOR_CODE @   \ Récupération du code du curseur
        PUTCHR          \ Affichage du curseur sans avancer le curseur
        \ Suite du code de la boucle principale
    

    Avec ce morceau de code, j'ai un curseur qui fonctionne et je peux l'initialiser dans COLD. J'en profite pour implémenter LIT qui permet de pousser la valeur qui suit le mot en mémoire sur la pile. Cela me permet d'écrire des valeurs immédiates dans le code Forth. Jusqu'à maintenant, j'utilisais des constantes qui contenaient les valeurs initiales.

    Il n'est pas impossible que je revienne sur l'initialisation (et le déplacement) du curseur dans le futur pour deux raisons liées. La première est qu'écrire sur les bords de l'écran sur ces machines n'est pas une bonne idée : les télés cathodiques avaient tendance à couper les bords de l'image. Pour le moment, je n'écris ni sur la première ni sur la dernière colonne. Mais en regardant Family BASIC, je vois que ce sont deux colonnes entières qui sont laissées comme marges. Ainsi que les deux premières lignes.

    Ce qui m'amène à la seconde raison liée : permettre de changer la géométrie de la fenêtre de texte par l'utilisateur. C'est une commande qui existe sur l'Hector HRX par exemple. Avec ce système, ce mot WINDOW permettrait de définir les marges haut, bas, gauche et droite. À noter pour plus tard... car cela compliquera la gestion du scroll lors de l'arrivée en bas de la zone.

    Entrer du texte

    Le texte s'affiche donc à l'écran, très bien. Mais il n'est pas encore prêt pour être traité par le système Forth. Pour cela, FIG-Forth utilise un buffer dont l'adresse est maintenue dans la variable adresse TIB (Text Input Buffer), ainsi que sa taille dans la variable adresse #TIB, comme indiqué dans le livre Starting Forth dans le chapitre Under the Hood.

    Je ne sais pas encore où placer ce buffer. Je vois plusieurs options possibles :

    • Je maintiens un buffer en RAM des caractères entrés et édités, en parallèle de ce qui est envoyé à l'écran. Cela limite l'édition à une ligne logique, pas possible de se balader sur un écran pleine page. Et maintenir en synchro deux systèmes parallèles n'est pas souvent une bonne idée.
    • J'utilise l'écran lui-même comme buffer et je limite l'édition à une ligne logique. Le fait que les données à l'écran ne soient pas continues à cause des réservations des premières et dernières colonnes complique un peu les choses. Il faut aussi ramener les informations présentes dans la mémoire vidéo vers la RAM pour traitement, ce qui est assez lent et nécessite d'être fait pendant la VSync.
    • J'utilise l'écran lui-même comme buffer et je permets l'édition pleine page. C'est globalement la même chose que l'option précédente, avec en plus un algorithme pour déterminer où commence la ligne logique lorsque l'on appuie sur RETURN.

    Dans l'idéal, j'aimerais la troisième option. Cependant si cette fonctionnalité est très pratique en BASIC où on peut revenir sur des lignes précédentes pour les éditer, l'intérêt est moins évident en Forth, où il faut « oublier » les mots avant de les redéfinir.

    La première option m'ennuie à cause du double traitement. Je pense que la seconde option sera mon choix de base. Si je vectorise le mot ACCEPT, il sera possible pour un utilisateur avancé de redéfinir le comportement pour faire de l'édition pleine page, en fonction de la RAM disponible sur la cartouche... je m'emballe.

    Et les tests ?

    L'affichage initial du curseur était testé par mes tests unitaires. Indirectement, le mot LIT que j'utilise à l'initialisation est testé. Par contre, j'ai à présent du texte en entrée et une boucle interactive avec du branchement conditionnel. Et pour ça, je n'ai qu'un test manuel : lancer le programme et vérifier que le clavier fonctionne. Et j'ai encore plus de mots standards que je devrais ajouter à un moment comme DUP, DROP, SWAP, OVER, etc. que je voudrais pouvoir tester depuis Forth.

    Il existe un framework de test Forth, extrêmement simple, du nom de ttester, de John Hayes, ainsi que des versions dérivées. J'ai hâte de pouvoir l'utiliser. J'ai deux options. La première est de réécrire le code avec mon système de macros. Jouable, mais un peu long et fastidieux. La seconde est d'attendre d'avoir un interpréteur Forth fonctionnel... mais cela va m'obliger à écrire de nombreux mots sans tests, ce qui me déplaît. Je vais réfléchir à la question.

    Pour la partie gestion du clavier, je pense ajouter à mon framework de test une injection de caractères. Il restera le test du clavier à tester physiquement, mais la boucle principale pourra être testée avec des entrées simulées.

    La suite !

    Mon étape suivante va être de compléter le traitement du clavier avec les majuscules, minuscules et kanas. Puis le traitement des touches de directions, insertions, suppressions, etc. Sur cette partie, je ne pense pas faire d'article, cela va être très mécanique. Cependant, je devrai me diriger vers l'implémentation du mot ACCEPT, qui gère l'entrée clavier et la fournit dans le buffer TIB. Cela m'obligera à choisir une des options évoquées plus haut, et ce sera donc probablement le sujet de l'article suivant.

    Ah, et il sera peut-être temps de vérifier si tout cela fonctionne sur une vraie Famicom avec un vrai clavier Family BASIC !

    En attendant, voici une capture sur émulateur :

    Forth sur 6502 - écriture interactive à l'écran 2


  • Forth sur 6502, épisode 9 ()

    Froid, abandon, quitte...

    C'est triste un démarrage de Forth... COLD, ABORT, QUIT. On aurait pu imaginer des mots comme WARMUP, READY, LOOP. Mais je n'ai pas prévu de renommer les mots standards de Forth. Et comme indiqué dans l'article précédent, il est temps de déplacer le code de démarrage vers les mots officiels.

    Et à vrai dire, comme prévu, il n'y avait pas grand chose à faire.

    Tout d'abord, le code de démarrage devient juste initialiser l'interpréteur avec le mot COLD :

    boot_forth:
        ; Set the Work Register to the first word to execute:
        lda #<COLD_word_cfa
        sta REG_W
        lda #>COLD_word_cfa
        sta REG_W + 1
    
        ; And use it
        jmp docol
    

    Et pour faire simple, ABORT et QUIT appellent juste des mots cachés qui contiennent le code assembleur que j'avais déjà. Cela donne :

        ; COLD
        DEFINE_FORTH_WORD COLD, 0, 0
        .word RESET_ENV_word_cfa
        .word ABORT_word_cfa
        ; ABORT never returns, no need for DO_SEMI
    
        ; ABORT
        DEFINE_FORTH_WORD ABORT, COLD, 0
        .word RESET_STACK_word_cfa
        .word QUIT_word_cfa
        ; QUIT never returns, no need for DO_SEMI
    

    Reste QUIT qui est la boucle infinie. J'avais déjà une boucle infinie sous forme de mot assembleur, je l'ai renommée et voilà, terminé !

    Certes, ce n'est pas encore un vrai mot QUIT, qui doit traiter les entrées utilisateur et exécuter les commandes. Mais en tout cas, la structure est en place.

    Lire le clavier

    Et donc puisque tout cela n'était pas bien compliqué, et que QUIT va avoir besoin de lire le clavier, passons à la lecture du clavier.

    Accès au clavier

    Le clavier Family BASIC est connecté à la Famicom via le port d'extension. Celui qui est devant dans une Famicom classique. Avant de l'interroger, il faut écrire à l'adresse $4016 puis lire depuis l'adresse $4017.

    L'écriture à l'adresse $4016 contrôle le balayage du clavier. En effet, le clavier est une matrice de touches sur 9 lignes et 2 colonnes. La matrice est visible sur NESDev.

    L'écriture à $4016 utilise les 3 premiers bits pour contrôler le scan.

    Bit Description
    0 0 = scan normal ; 1 = réinitialiser le scan ligne 0, colonne 0
    1 Sélection de colonne
    2 Activer le clavier (1 = activé)
    3-7 inutilisés

    La lecture depuis $4017 récupère les données du clavier.

    Bit Description
    0 Inutilisé
    1-4 Données de colonne
    5-7 Inutilisés

    Un délai doit être respecté entre l'écriture à $4016 et la lecture depuis $4017 pour permettre au clavier de préparer les lignes de données.

    Scan dans le Forth

    Pour le scan dans le Forth, j'utilise trois tampons en mémoire qui sont chacun de taille 9 octets (un par ligne). Pour chaque ligne les informations des deux colonnes sont regroupées dans un seul octet.

    • Bits 0-3 : données de la colonne 0
    • Bits 4-7 : données de la colonne 1

    Le premier tampon sert au scan en cours. Un second sert à stocker le résultat du scan précédent. Le troisième sert à détecter les changements entre les deux scans (en faisant un XOR entre les deux autres tampons).

    Une fois les tampons remplis, le tampon de changements est analysé pour détecter les changements d'état des touches. Si un bit a changé dans le tampon de changements, l'état pressé/relâché est déterminé à partir du tampon de matrice actuel. Ceci est fait pour l'octet entier, donc si plusieurs bits ont changé dans une ligne, ils sont tous traités.

    Puis pour chaque bit modifié qui est aussi détecté comme un appui, une recherche dans une table de caractères fait la correspondance. Si le caractère est affichable, il est placé dans une file de caractères qui pourra être interrogée plus tard par le mot Forth KEY, qu'il reste à implémenter.

    Pour chaque bit modifié, je calcule aussi un code de touche ((ligne << 4) | numéro_de_bit), mais je ne l'utilise pas. Cela vient d'une idée précédente où je voulais tout d'abord générer les évènements de touches puis les décoder dans un second temps. Au final, j'ai trouvé ça bien compliqué pour cette machine.

    Ce qu'il reste à faire

    Plein de choses. Même en se limitant au clavier. Mais je pense que le plus long a été fait : le principe général de scan et de décodage. Il me reste à traiter les touches spéciales (comme SHIFT, CTRL, etc) et à implémenter le mot Forth KEY.

    J'ai prévu d'implémenter aussi le mot KEY? qui permet de savoir si une touche est disponible. Ainsi que KEYPRESSED? et KEYDOWN? pour interroger l'état de touches spécifiques.

    On peut écrire !

    En branchant rapidement la détection des touches avec un appel codé en dur à EMIT dans la boucle principale, je peux écrire à l'écran ! Le curseur n'avance pas, car lui aussi était un appel codé en dur, cependant, c'est assez satisfaisant.

    D'ailleurs, je pense que la prochaine étape sera le traitement de l'avancée du curseur depuis la boucle principale en Forth. Cela va m'obliger à implémenter quelques nouveaux mots, comme 0BRANCH (afin de faire des tests conditionnels).

    Forth sur 6502 - écriture interactive à l'écran


Page 1 / 27 (suivant) »

Tous les tags

3d (15), 6502 (13), 6809 (1), 8bits (1), Affichage (24), AgonLight (2), Altaïr (1), Amstrad CPC (1), Apple (1), Aquarius (2), ASM (30), Assembleur (1), Atari (1), Atari 800 (1), Atari ST (2), Automatisation (4), BASIC (31), BASIC-80 (4), C (3), Calculs (1), CDC (1), Clion (1), cmake (1), Commodore (1), Commodore PET (1), Compression (4), CPU (1), Debug (5), Dithering (2), Divers (1), EF9345 (1), Émulation (7), Famicom (9), Forth (14), Game Jam (1), Hector (3), Histoire (1), Hooks (4), Huffman (1), i8008 (1), Image (17), Jeu (15), Jeu Vidéo (4), Livre (1), Logo (2), LZ (1), Machine virtuelle (2), Magazine (1), MAME (1), Matra Alice (3), MDLC (7), Micral (2), Motorola (1), MSX (1), Musée (2), Nintendo Switch (1), Nombres (3), Optimisation (1), Outils (3), Pascaline (1), Peertube (1), PHC-25 (2), Photo (2), Programmation (18), Python (1), RLE (1), ROM (15), RPUfOS (6), Salon (1), SC-3000 (1), Schéma (5), Synthèse (15), Tortue (1), Triceraprog (1), VG5000 (62), VIC-20 (1), Vidéo (1), Z80 (21), z88dk (1), ZX0 (1)

Les derniers articles

Adressage 6502, un récapitulatif
Forth sur 6502, épisode 11
Deux livres pour le développement sur Famicom
Forth sur 6502, épisode 10
Forth sur 6502, épisode 9
Forth sur 6502, épisode 8
Forth sur 6502, épisode 7
Forth sur 6502, épisode 6
Forth sur 6502, épisode 5
Forth sur 6502, épisode 4

Atom Feed

Réseaux