Notes et pensées qui ne trouvent pas leur place ailleurs.
Table des matières
|
Factorisation édition champ/ligne.
Il y a 3 routines pour rentrer du texte, chacune avec ces qualités.
- Éditeur : saisie lignes
- Utilise un système de table plus compacte qu'une série de CP/JR.
- Éditeur : saisie nom de fichier
- Permet de partir d'un nom pré-rempli.
- Monogams : saisie commandes
- Réaffichage complet du tampon à chaque action, puis on place le curseur nous-même.
Cette dernière manière de faire est bonne :
- code est court, simple, robuste.
- l'édition proprement dite (modification de la chaîne en mémoire) et l'affichage sont clairement séparé.
- en particulier plus besoin de distinguer les cas insertion & délétion pour savoir comment nettoyer la ligne.
- simplifie aussi la gestion curseur (évite de jongler avec le système, introduisant moult incohérences dès lors qu'on utilise nos propres routines d'affichage).
- ne ralentit en rien (afficher 80 caractères prend moins de 100 lignes raster. Donc on a largement le temps, même avec plein de rasters).
On distingue donc trois routines :
- field_editor (interprétation du code touche et modification en mémoire).
- field_refresh (simple affichage du champ)
- place_cursor
field_editor
-------------
In : HL = NT text buffer.
!!WARNING!! for empty string, set first byte to 0.
A = KeyCode
D bit 7 = Pre-selection
0 : No selection (i.e. default insertion mode)
1 : Text is pre-selected (i.e. any key other than cursor keys erase current text)
B = Max string length.
C = Initial cursor position in string (0 = First char, FF = Past last char).
Preconditions (to be checked by client) :
* len <= B (initial string cannot be longer than indicated max length)
* C <= len (cursor must be in string, or just after it)
where len is initial string length (not counting byte 0).
Out : All registers have the same meaning than at input. E.g. :
HL, D[0:6], B preserved
D[7] (possibly new) flag 'text selected'
C (possibly new) cursor position in string (-1 converted to valid pos)
Carry & NZ, key processed, field modified (so must be printed)
A destroyed, D[7], C updated.
Carry & Z, key processed, field not modified (so we may only refresh cursor)
A destroyed, D[7], C updated.
NC Key not processed, cause unhandled special key (ESC, CONTROL-?, DEL at pos 0...).
A = keycode.
Motivation :
- C à FF permet de se positionner après une chaine pré-remplie (cf CONTROL-S de l'éditeur).
- La routine renvoie la touche, afin que l'appelant décide ensuite comment interpréter ESC, RETURN, etc…
- Le flag "selection" permet de reproduire le comportement de saisie label/nom de fichier.
Factorisation des routines d'affichage : plan de route. (They have an agenda !)
Phase 0
Le code appelant ne doit plus manipuler lui-même curseur ou toute autre variable relative à l'affichage. Tout remplacer par des CALLs vers des routines de même préfixe.
Eg :
- disp_inline : affiche texte placée après le call
- disp_nl : retour à la ligne.
Cela permet de détourner facilement le pack de routines.
Phase 1
Pour les bouts de textes tenant sur une ligne, remplacer :
ld hl,txt_wrong_parameters:call disp_text_nl
Par A/ (si la chaîne n'est utilisée qu'une fois) :
call disp_inline_nl:BYTE "Wrong parameters.",0
Ou B/
call disp_message_nl:byte msg_wrong_parameters
Les deux solutions économisent la place.
La A/ économise en outre un label, et évite un déplacement pour aller voir le contenu de la chaîne.
La B/ permet de regrouper les messages dans une même ROM et autorise leur compression.
Phase 2 (Plus tard)
Utiliser échappement et "interpolation" pour générer la chaîne à afficher.
E.g. "Start: ",txt_esc,txt_hl_hexa," youpi", 0 affichera "Start: #BABE youpi" si hl contient #BABE.
Système sous tuteur [c'est fait, pièges compris. Voir détails paragraphe suivant]
Afin de rendre le moniteur le plus complet possible, la présence du système est souhaitable (accès aux RSX, chargement/sauvegarde, …) : point de vue "Hackeur".
Cela va à l'encontre d'un autre impératif : rendre le moniteur le moins intrusif possible, permettant d'étudier toute la mémoire telle que rendue par le programme : point de vue "Multiface".
Afin de concilier ces desiderata, deux solutions :
- installer le système en bank supplémentaire (C2, ou plutôt D2, E2, …).
- peu pratique, car les sorties écran atterriraient en bank (à moins de détourner BDD3-TXT WRITE CHAR et amis).
- installer le système en mémoire de base, après avoir sauvegardé la zone correspondante (en gros 00-3A et A000-BFFF). Lors d'un dump en B000 par exemple, le moniteur irait chercher dans ce back-up. Cette acrobatie est similaire à la lecture ROM / BANK, mais peut se révéler plus piégeur.
Mécanisme miroir firmware.
On distingue quatre pages #8000 (en fait, concerne seulement la zone #9800-#BFFF, comme approximation économe de la mémoire "système" à réinitialiser, en comptant les espaces de travail réservés par les ROMs, plus la zone 00-3F).
- Page avant exécution du programme (I comme Initial) qui sera restaurée.
- Page de travail pour Orgams (O comme Orgams)
- Page lors de l'exécution du programme (E comme Exec)
- La bank qui sert de copie (B)
La page O doit pour l'instant contenir le firmware, car l'éditeur l'utilise, ne serait-ce que pour les accès disc.
En l'état actuel, page I et O sont confondues. Avant exécution (Jump), elle est copiée en B. Au retour, elle est swappée avec B, de sorte que le moniteur puisse l'analyser comme si de rien n'était.
Premier problème (bug #63) : pour l'instant, l'assemblage n'est pas dérouté dans la page E.
Ce qu'il faut faire :
- Copier avant assemblage I vers B (si la zone n'est pas touchée, on la retrouvera telle quelle)
- Assembler en déroutant vers B
- Swapper (O et B)
- Exécuter
- Au retour, Swapper (O et B)
Pourquoi une copie ?
La zone 'système' est copiée en bank plutôt que restaurée via le firmware.
- Avantages
- Beaucoup plus rapide (cf latence de certaines ROMs qui font des checksums)
- Pas besoin de hacker pour empêcher l'affichage du boot et des roms
- Il s'agit de l'état tel que défini par l'utilisateur avant l'appel d'Orgams. Permet patches.
Inconvénients
* Plus gourmand en mémoire
* Moins robuste
* Ce point peut être allégé via un checksum (cf todo #29)
A venir
On compte se passer de plus en plus du firmware, et exploiter la page #8000 :
- Pour affichage supplémentaire
- Pour routines pivot (pour l'instant, réelles acrobaties dans le switch de bank, car il était prévu de pouvoir mettre le pile en BANK - cela reste nécessaire pour la rapidité de la trace).
Cela signifie de distinguer page I et O. [Edit: en fait non, voir point suivant]
Modèle revu pour version 'Debugging Deligths'
(notes perso pour modifications à venir)
On re-précise les notations :
- I: zone HIMEM-&BFFF avant exécution du programme (zone firmware)
- O: zone &8000-&9800 utilisée comme zone de travail par Orgams.
- E: zone &8000-&BFFF lors de l'exécution du programme.
- M: page &8000-&BFFF en mémoire centrale.
- B: bank qui sert de backup
Sous l'éditeur et Monogams, O+I sont "connectées" en M, tandis que lors de l'exécution, c'est E qui se voit connecté.
Flux:
- |O: on copie &8000-&9800 de M vers B, de sorte que la zone 'utilisateur' est préservée, et Orgams peut utiliser cette plage pour lui-même (O).
- Assemblage :
- On copie I de M vers B. On 'reset' donc l'état mémoire sur cette plage. C'est le but recherché : l'idée est qu'à chaque assemblage, la zone firmware soit valide (grosso-modo, telle que lors du premier assemblage).
- On assemble (les données assemblées en page &8000 sont déroutées vers B, ce qui permet d'assembler sur toute la page &8000-&BFFF, sans conflit avec le firmware ou Orgams).
- On coupe le système ('DI')
- On swape B et M, de sorte que le firmware et les données Orgams sont backupées, tandis que les données assemblées sont bien replacée.
- On saute au programme.
- Retour à Orgams :
- On swape B et M, de sorte que le firmware et les données Orgams sont restaurées, tandis que E reste observable de façon transparente (e.g. si le programme a créée une table en &BC00, on peut la lire par un simple m&BC00.)
Cas particulier de la trace.
ASIS:
Pour assurer la rapidité de la trace (mode rapide, N), E est connecté en M, et la pile n'est pas utilisable.
Pour la partie UI, on reconnecte la bank de travail et la pile se trouve en &8000.
TOBE:
Au retour de l'émulation Z80, on se libère une zone de travail de &8000 à &87ff (le swap prend 8*48 lignes rasters, soit ~1.23 frame, ce qui est considéré comme négligeable devant le temps pris pour le rafraîchissement écran).
En outre, pas de swap à faire pour la navigation (désassemblage).
Flux:
- Passage Monogams -> Trace (commmande d):
- on swap &8800-&bfff entre B et M ("connection" E en M, mis à part la zone travail spécial trace)
- pile en &8800
- Pour chaque commande de trace :
- swap &8000-&87ff entre B et M (connecte complétement E)
- pile en &8000
- appel émulation Z80
- swap &8000-&87ff entre B et M (rétabli zone travail)
- pile en &8800
- appel routines localisation et affichage source.
- Passage Trace -> Monogams/Editeur : swap &88800-&bfff entre B et M.
- BRK -> Trace :
- swap &8000-&87ff entre B et M
- pile en &8800
TODO:
- Recenser l'ensemble des zones utilisées par le parcours du source en mode 0 et 4 et par l'affichage du dit source et par le désassemblage.
- Vérifier le niveau de récursion (stack overflow ?).
Transcendance code éditeur miou-miou
Je propose ici une méthode qui :
- simplifie le code.
- facilite les améliorations ergonomiques et graphiques à venir (touche OvL!).
- corrige 7 bugs d'un coup.
Tout comme l'édition et la gestion du source (encodage/décodage) se vont vues isolées avec succès, il serait judicieux de séparer le côté "navigation+édition" (ie : aller en ligne 140, supprimer une ligne…) de l'affichage.
Il y a au moins 5 excellentes raisons d'opérer ainsi (cf machine à café).
Alors, descendre d'une ligne se résumerait à incrémenter POS_LINE (si on est pas déjà sur la dernière). Toute la partie affichage (déplacement curseur, éventuel scrolling) s'exécuterait après coup.
Toute la gestion se contenterait de 5 routines externes :
- valide_line (de = numéro ligne source). Echange avec codec qui refournit la ligne formatée. NB cette routine doit être appelée dès qu'on "quitte" la ligne. Non seulement en cas de déplacement Y, mais dès lors qu'une opération est lancée (assemblage, sauvegarde, …).
- goto_line (de = numéro ligne source). Se résume en fait à assigner POS_LINE.
- refresh_from (de = numéro ligne source). Notifie qu'il faut rafraichir à partir de telle ligne (cas insertion / suppression). Cette routine n'est pas obligé de réaliser le refresh elle-même. Dans l'idéal, elle met tout en place de sorte que 'update_screen' le fasse.
- refresh_all : après chargement ou toute opération ayant effacé l'écran.
- update_screen. Amène le curseur sur la ligne sélectionnée par goto_line, en scrollant éventuellement. Rafraichi ce qui doit l'être.
La partie navigation n'a pas à manipuler le curseur ou l'offset écran. La seule exception est pour l'édition de la ligne en cours, où l'on "prend la main". On notera :
- on ne touche pas au curseur Y (lecture seule)
- on pourrait aussi découpler ici, en ne modifiant que le buffer, et en laissant une routine externe rafraichir l'affichage. Ce serait pousser le concept un peu loin à première vue, mais cela apporte des possibilités intéressantes :
- facilite fenêtrage quand ligne éditée plus grande que ligne affichée (symétriquement à ce qui se passe en Y, où il y a plus de lignes qu'affichable).
- facilite réutilisation. Par exemple, dans l'éditeur, pourquoi ne profite-on pas du confort "flèches gauche et droite" dans l'édition des noms de fichiers ?
Cent discussions
Quelques décisions unilatérales soumises à commentaires.
Miniscule espace
Je compte afficher les mnémoniques en minuscules et sans padding. C'est plus compact, et je trouve ça plus lisible, particulièrement quand on enchaine les opcodes.
ASIS
LD A,(HL)
INC L
CALL raymond
LD A,(HL):INC L:LD (DE),A:INC E
LD A,(BC):INC C:LD (DE),A:INC E
TOBE
ld a,(hl)
inc l
call raymond
ld a,(hl):inc l:ld (de),a:inc e
ld a,(bc):inc c:ld (de),a:inc e
Fameux 3 ROMs.
Je m'étale sur 3 ROMs. Une fois tout fonctionnel, il serait possible d'alléger le code. Mais çela ne m'intéresse même pas ! Je prévois ensuite de nombreux outils supplémentaires (pour le moniteur et l'assembleur), qui redéborderaient de toute façon sur 3 ROMs.
Vrac
Gestion touches.
La saisie dans le moniteur et dans l'éditeur devrait être mutualisée ("factorisée" dans le jargon informatique), ne serait-ce que pour assurer l'homogénéité d'utilisation.
A une différence prêt : Hicks ne peut pas utiliser les vecteurs système.
Une gestion complète du clavier n'est pas triviale : gestion de l'auto-répétition, des touches mortes (CONTROL, SHIFT)…. D'ailleurs Zik s'était un peu cassé les dents dessus pour son SoundTracker+. Je pense qu'il ne faudrait pas partir de zéro, mais dans un premier temps copier les routines du firmware en déroutant les zones de travail aux endroits souhaités.
C'est un peu de boulot, mais un gros bénéfice collatéral serait la possibilité d'utiliser ce code dans le développement de démos, pour tester des paramètres en live de manière souple.
On gagnerait aussi en flexibilité. Avec les routines systèmes, il est très malaisé de détourner COPY et CAPS-LOCK de leur usage afin de les exploiter comme touches mortes (pour combinaisons COPY+Lettre par exemple).
Sur l'affichage du numéro de ligne
(TODO? Déplacer dans page VisuelEditeur?)
tl;dr : écarté pour la v1 ! Mais il faut limiter la largeur d'édition à 71 chr.
La motivation est décrite [[lien] là]. On se penche ici sur la réalisation.
Il est malin de ne réserver que le strict nécessaire (2 chr jusqu'à ligne 99, 3 jusqu'à 999).
7 ; Tu triches ? tu pré-calcules ?
8 sin_table = #4000
9 log_table = #4100
10 exp_table = #4200
Mais à trop faire le malin on se mord les doigts.
- En effet, dans la v1 on ne va pas gérer l'édition de lignes plus longues que la largeur d'écran. Or, avec cette approche, que faire si on copie la ligne 13 vers la ligne 2014 ? Les deux derniers caractères disparaitraient !
- Cela complique le scrolling : quand la ligne 100 apparait pour la 1ère fois, il faut tout décaler !
En seconde approche, on peut donc réserver 5 chiffres.
On aurait donc :
XXnnnnn TTTTTTTTTTTTTTTTTTTTTT_
Avec XX : goutière pour signalétique.
nnnnn : numéro de ligne
TTTTTTTT : le texte lui-même, largeur max = 71.
_ : espace de rab pour le curseur, s'il est gérè comme dans DAMS.
Reste à voir si tout cet espace réservé à gauche n'est pas trop pertubant visuellement !
Auquel cas, on pourra envisager de :
- Réaliser l'affichage compact du numéro de ligne. (Il faut tout de même limiter la largeur d'édition afin d'éviter l'écueil évoqué plus haut).
- Ne pas réserver de goutière (Affichage les signes à la place du numéro de ligne, ou encore à droite).
C'est aux détails qu'on reconnait l'ouvrage !
Mais tout cela est à mettre de côté (ahah on rigole on s'amuse) ; en bref, pour la v1 :
- pas de numéro de ligne (ce n'est pas une fonctionnalité indispensable). Méthode YAGNI : le strict nécessaire pour éditer le source de manière au moins aussi confortable qu'avec Dams, afin de pouvoir basculer.
- on limite l'édition à 71 caractères, afin de prévoir l'évolution.
Mince j'avais pensé à autre chose (de grandiose !), et j'ai oublié.
Zones de travail
La version Debuging delight offre &8000-&97ff comme zone de travail.
Une partie sera utilisée comme mémoire vidéo supplémentaire, et l'autre pour tout ce qui est temporaire.
Quelques exemples de la distinction à opérer:
- Buffers et variables temporaires
- Tampon DISC I/O
- Zones de travail lors assemblage
- Tout ce qu'on réinitialise de toute façon à chaque utilisation
- Buffers et variables persistents
- [monogams] Texte affiché (afin de le retrouver en revenant de l'aide, mais aussi du moniteur, et même après un reset)
- [monogams] Historique des commandes
- [editeur] Toutes les variables de positionnement dans le code, les chaines de recherche…
- bref tout ce qu'on doit retrouver après un reset.
Déplacer tout ce qui est temporaire en &8000-&97ff permet de libérer de la place en bank pour une utilisation plus judicieuse (historiques etc).
On pourrait par ailleurs fixer une convention pour distinguer les variables persistentes des temporaires (préfixe '_', préfixe 'tmp_', distinction par casse, …).
Suggestions connexes
Mode de distribution
Serait-il possible d'avoir une pensée pour les gens qui n'ont ni PC ni HxC, et de diffuser les ROM simplement sous forme de fichiers ?
Le HFE est une impasse sans HxC ni PC, et le DSK est une plaie (reformater une disquette complète à chaque fois juste pour transférer 3 fichiers c'est violent). Merci !