« XMAS GIFT » – VC³ 2024 coding tips
Les concours de Noël de Logiker sont difficiles. Il faut trouver la manière la plus compacte d’écrire un programme qui dessinera un motif imposé. Derrière la simplicité de l’énoncé se cache un défi redoutable. Pour remporter le concours il faut vraiment aller au bout des choses, creuser, creuser et encore creuser. Un délice pour les spécialites du code.
Overflow des Logon System y participe depuis deux ans déjà. Il va loin ! Pour cette année il a fini avec 42 octets. Après la clôture du concours, les différents programmes sont disponibles en téléchargement, ce qui fait que chacun peut les étudier à loisir. En combinant ses astuces de programmation et celles glanées ici et là, Overflow a pu aller plus loin : 38 octets !
Il nous livre ici le résultat de ses élucubrations en anglais : XMas gift, le concours de Logiker en 38 octets sur Amstrad CPC.
Pour les non anglophones, voici une traduction du texte original, avec l’accord de l’auteur bien entendu.
« Xmas GIFT » – VC³ 2024 coding tips
Il y a quelques mois, j’ai participé au Vintage Computing Christmas Challenge 2024.

J’ai aussi pris part à l’édition 2023. Vous pourrez trouver plus d’informations à ce sujet sur le Blog Logon System. Dernièrement on m’a demandé des explications concernant les astuces que j’ai employées, les voici donc.
En passant, les participants sur Amstrad ont eu des scores excellents cette année, comme le montre la page officielle des résultats.

Le meilleur dans tout ça ? C’est qu’en combinant mes idées avec celle des autres, nous avons réussi à optimiser encore plus le code, en le réduisant à seulement 38 octets ! Vous pouvez télécharger le dsk ou le source à partir du site.

GIF animé pour présenter l’algorithme

Source principal / mémoire

(A) Le call CALL <nnnn> à partir du Basic initialise DE
Lorsqu’on fait un CALL <nnnn> en Basic, le registre DE est défini à l’adresse <nnnn>.
Cela permet d’initialiser l’adresse de DE à la valeur #0901 au démarrage du programme. Une manière d’éviter un « LD DE,#0901 » dans le code. C’est autorisé par le règlement, alors pourquoi s’en priver ?
(B) La mémoire est remplie de zéros (équivalent pour le CPU à des NOPS)
Le call du Basic lance l’exécution en #0901. Si vous lancez le programme après un reset du CPC, la mémoire entre #0901 et #2100 (adresse où débute réellement le programme), le Z80 lira uniquement des zéros, qui correspondent à des NOPs, jusqu’à arriver à l’adress où se trouve réellement le code.
0901 00 NOP
... 00 NOP
2100 ; Le code démarre ici
; Et DE=#0901
(C) RST LO_SIDE_CALL : DEFW BASIC_PRINT_STRING
J’ai trouvé cette perle à partir du code de lightforce6128.
Jetez un oeil à pour en savoir plus (Désolé pour les non anglophones) : https://www.cpcwiki.eu/forum/programming/
Cet appel à la ROM Basic imprime des caractères ou des codes de contrôle à partir des octets pointés par HL, jusqu’à tomber sur un zéro.
Cela permet d’éviter de perdre des octets en bouclant sur l’habituel CALL #BB5A qui, lui, n’imprime qu’un seul caractère par appel.
2100 LD HL,data_ctrl
2103 RST #10 : DW #C38B
...
data_ctrl 211F ; liste des caractères / codes de contrôle
(D) La mémoire principale est remplie de 00/NOPs
La RST LO_SIDE_CALL : DEFW BASIC_PRINT_STRING quand elle tombe sur un octet à 0. il n’y a donc pas besoin de l’ajouter explicitement à la liste vu que la mémoire est déjà à 0.
2126 00
(E) Le compteur de ligne / y est à #2121
Cela permet de fixer HL facilement à cette adresse via un LD L,H, si bien entendu vous prenez garde à laisser H à #21
2106 LD L,H
(F) Le compteur de ligne / y est négatif
Le compteur court de -#14 à #00. Nous le verrons plus tard, cette valeur négative sera utile dans la suite de cet algorithme.
2107 INC (HL)
...
2121 EC ; = -#14
(G) Le RET vers le Basic est au milieu de la boucle
La première moitié de la boucle dessine le noeud « \o/ ».
La deuxième moitié écrit une ligne du paquet cadeau, soit « +——–+——–+ », soit « ! ! ! ».
A chaque boucle (mis à part la dernière), la seconde partie de la boucle efface le noeud.
Cette méthode est plutôt élégante en terme d’optimisation.
C’est aussi une manière stylée de faire monter le graphe à la manière d’une cathédrale sélevant du sol.
2108 RET Z
(H) HL est fixé à « ! «
C’est grâce à la table de caractères, cela permet d’économiser 1 ou 2 octets. Comme le caractère espace est égal à « ! » -1, (Dans la table ASCII #20 correspond à un espace et #21 est le point d’exclamation), DEC HL transforme « !! » en « ! « .
210F DEC HL
(I) DEC HL est inclus dans un autre OPCODE
Le dernier octet de LD HL,’+-‘ est #28, ce qui correspond à l’opcode du DEC HL. Cela permet d’économiser un octet grâce à un JR jr_y_std.
JR NZ,jr_y_std
...
210D 21 LD HL,"+-" ; Compte pour 3 octets 21 2D 2B
210E 2D
jr_y_std 210F 2B DEC HL
(J) JP loop_y fait partie de données
Cela permet d’économiser un ou deux octets vu que l’adresse du saut est aussi utilisée comme des données
211E C3 JP #091F ; Compte pour for 3 octets C3 1F 09
data_ctrl 211F 1F ; 1er caractère / control code
2120 09 ; 2nd caractère / control code
...
(K) La RAM est remplie de 00/NOP.
Les adresses #091F à #2100 sont remplies avec des zéros.
Ainsi chaque bouclage vers loop_y exécutera un paquet de NOPs inutiles, mais l’optimisation du code est à ce prix.
loop_y 091F 00 NOP
... 00 NOP
2100 ; Le vrai code démarre ici
...
211E C3 JP loop_y ; JP #091F
211F 1F
2120 09
(L) LOCATE 9,y (avec y<0) fait scroller l’écran vers le bas
Quand on utilise le code de contrôle pour positionner un caractère à une adresse particulière de l’écran, LOCATE <x,y>, si y<0 l’écran scroll d’un octet vers le bas et le caractère pointé à l’écran se trouve dans la ligne vide qui apparaît en haut du moniteur. Cela permet d’afficher le graphe comme une cathédrale, du bas vers le haut.
data_ctrl 211F 1F ; 1er caractère / control code
2120 09 ; 2nd caractère / control code
2121 EC ; 3ème caractère / control code
; = LOCATE #09,-#14
(M) Le compteur pour la ligne/y est aussi utilisé comme une donnée
Cette valeur pour le LOCATE est utilisée comme variable, allante de #EC (-#14) à #FF (-#01).
On économise un octet – Pas besoin d’avoir une valeur négative fixe ici.
data_ctrl 211F 1F ; 1ER caractère / control code
2120 09 ; 2nd caractère / control code
cpt_y 2121 <-y> ; 3ème caractère / control code
; = LOCATE #09, <de #EC à #FF>
Overflow / Logon System
Mercredi 15 Avril 2025