Le mystère de la touche COPY!

Le mystère de la touche COPY!

Je suis tombé sur cette vidéo, dans laquelle on voit que la touche COPY semble se comporter de facon étonnante, car elle ne saurait pas faire la différence entre un bon caractère et un mauvais caractere à l'écran.

Pour rappel, la fonction Copy permettait de dédoubler le curseur à l'écran, de placer ce seconde curseur n'importe ou à l'écran et de recopier ce qu'il se trouve à l'écran à cet endroit,  l'emplacement du premier curseur, à partir du moment ou il s'agit d'un caractère reconnu par le firmware. Si on tentant de recopier un bout de dessin fait à l'aide de PLOT et de DRAW, on avait droit à un beep de mécontentement.

Mais alors comment expliquer ce qui est décrit comme un phénomène sidérant dans la vidéo? Rien de plus simple, il suffit d'aller jeter un œil dans la ROM! Nous allons nous baser sur une version disponible en ligne, il en existe plusieurs, celle ci est scindée en plusieurs fichiers ce qui est plus pratique:

GitHub - Bread80/CPC6128-Firmware-Source: Unassembled Amstrad CPC6128 Firmware
Unassembled Amstrad CPC6128 Firmware. Contribute to Bread80/CPC6128-Firmware-Source development by creating an account on GitHub.

Parmi la liste des fichiers, le premier qui attire notre regard est LineEditor.asm. Une recherche du mot 'COPY' nous mène directement à la ligne 637:

;; COPY key pressed
COPY_key_pressed:                 
        push    bc                
        push    hl                
        call    TXT_GET_CURSOR   
        ex      de,hl            
        call    get_copy_cursor_position
        jr      nz,_copy_key_pressed_12
        ld      a,b
        or      c 
        jr      nz,_copy_key_pressed_25
        call    TXT_GET_CURSOR 
        ld      (copy_cursor_rel_to_origin),hl
        jr      _copy_key_pressed_14

;;--------------------------------------------------------------------

_copy_key_pressed_12:             
        call    TXT_SET_CURSOR    
        call    TXT_PLACE_CURSOR  

_copy_key_pressed_14:             
        call    TXT_RD_CHAR       
        push    af                
        ex      de,hl             
        call    TXT_SET_CURSOR    
        ld      hl,(copy_cursor_rel_to_origin)
        inc     h
        call    TXT_VALIDATE
        jr      nc,_copy_key_pressed_23
        ld      (copy_cursor_rel_to_origin),hl 
_copy_key_pressed_23:             ;
        call    _shift_key__left_cursor_pressed_21
        pop     af                
_copy_key_pressed_25:        
        pop     hl               
        pop     bc               
        jp      c,edit_for_key_13 
        jp      edit_sound_bleeper

Pour réaliser une analyse rapide, on pourra consulter la table des indirections du pack text (préfixé par TXT), que l'on peut retrouver ici. En particulier:

ADDR CODE Description
&BB4E TXT_INITIALISE Initialise the Text VDU
&BB51 TXT_RESET Reset the Text VDU
&BB5A TXT_OUTPUT Output a character or control code to the Text VDU
&BB5D TXT_WR_CHAR Write a character onto the screen
&BB60 TXT_RD_CHAR Read a character from the screen
&BB6F TXT_SET_COLUMN Set cursor horizontal position
&BB72 TXT_SET_ROW Set cursor vertical position
&BB75 TXT_SET_CURSOR Set cursor position
&BB78 TXT_GET_CURSOR Ask current cursor position
&BB87 TXT_VALIDATE Check if a cursor position is within a window
&BB8A TXT_PLACE_CURSOR Put a cursor blob on the screen
&BB8D TXT_REMOVE_CURSOR Take a cursor blob off the screen
&BBA5 TXT_GET_MATRIX Get the address of a character matrix
&BBA8 TXT_SET_MATRIX Set a character matrix

Donc Après une analyse rapide, on comprend que l'appui de la touche copie provoque la récupération de la position des 2 curseurs ( TXT_GET_CURSOR ) suivi de l'appel a TXT_RD_CHAR, suivi d'une validation ( TXT_VALIDATE ), qui si elle échoue provoque l'émission d'un son (edit_sound_bleeper) . Cette validation est essentielle, car la fonction de recopie à l'écran est utilisée pour écrire du texte qui sera analysé par l'interpréteur basic. Il est hors de question de copier des caracteres inexistants!

Le vecteur &BB60

Ce TXT_RD_CHAR semble donc être la clé de notre énigme.  Une rapide recherche dans Clefs Pour Amstrad nous donne:

Bingo! C'est donc cette routine qui serait à l'origine du mystère présenté dans la vidéo. Allons voir le code de TXT_RD_CHAR, cette fois ci dans le fichier text.asm, à la ligne 720:

TXT_RD_CHAR:
        push    hl
        push    de
        push    bc
        call    scroll_window
        call    TXT_UNWRITE
        push    af
        call    TXT_DRAW_CURSOR
        pop     af
        pop     bc
        pop     de
        pop     hl
        ret

Rien de particulier, si ce n'est l'appel à TXT_UNWRITE, qui contient cette boucle:

        ld      c,$00
_ind_txt_unwrite_21:
        ld      a,c
        call    TXT_GET_MATRIX
        ld      de,RAM_b738
        ld      b,$08
_ind_txt_unwrite_25:
        ld      a,(de)
        cp      (hl)
        jr      nz,_ind_txt_unwrite_3
        inc     hl
        inc     de
        djnz    _ind_txt_unwrite_25
        ld      a,c
        cp      $8f
        scf
        ret
_ind_txt_unwrite_35:
        inc     c
        jr      nz,_ind_txt_unwrite_21;
        xor     a
        ret

Cette boucle compare un à un les caractères de la fonte avec le contenu de la RAM en &B738. TXT_GET_MATRIX permet de récupérer le caractère.  La zone en &B738 est utilisée par une autre fonction du firmware, cette fois dans le pack Screen, SCR_REPACK (&BC56). Elle est appelée au début de TXT_UNWRITE.

En effet, il y a la une subtilité gérée par le firmware, c'est qu'en fonction du mode écran, un même caractère n'occupera pas le même nombre d'octets en RAM. Il faut donc procéder a la transformation de la ce qu'il y a à l'écran sous forme standardisée. C'est exactement le rôle de SCR_REPACK:

&B738 : Compress a character matrix to the standard form

J'invite le lecteur à aller regarder le code de SCR_REPACK à la ligne 1146 du fichier Screen.asm en guise de récréation. Nous allons ici nous contenter de regarder le contenu de a RAM en &B738, après avoir entré le caractère A dans l'éditeur Basic, selon les modes:

Mode 2, 18 3C 66 66 7E 66 66
Mode 1, 11 00 33 CC 66 66 66 77 EE 66 66 66 66
Mode 0, 00 55 AA 00 00 FF FF 00 55 AA 55 AA ....

Il faut aussi se rendre compte que la matrice utilisée pour la comparaison tient aussi compte du fait qu'elle peut avoir été redéfinie par l'utilisateur (avec la fonction SYMBOL duBasic).

Conclusion

Il est remarquable de constater la encore la grande sophistication du firmware utilisé dans l'Amstrad. L'absence d'un véritable mode texte sur CPC a forcé les ingénieurs qui ont conçu cette machine, de mettre en œuvre toute une stratégie pour réaliser cette fonction de recopie à l'écran. Derrière cette fonction, il faut en effet récupérer les données placées sous le curseur, le convertir selon les modes utilisés, et aller comparer les caractères un a un avec la fonte système (voire la fonte redéfinie par l'utilisateur avec SYMBOL), et le cas échéant recopier le caractère.

Liens

GitHub - Bread80/CPC6128-Firmware-Source: Unassembled Amstrad CPC6128 Firmware
Unassembled Amstrad CPC6128 Firmware. Contribute to Bread80/CPC6128-Firmware-Source development by creating an account on GitHub.
Amstrad-CPC-ROM-Disassembly/CPC464 Firmware ROM Disassembly v1.00.dis at main · Richard-Lloyd/Amstrad-CPC-ROM-Disassembly
Reverse engineer of the firmware etc supplied with Amstrad CPC computers of the 1980's - Amstrad-CPC-ROM-Disassembly/CPC464 Firmware ROM Disassembly v1.00.dis at main · Richard-Lloyd/Amstrad-CP...