Shrinkler sur CPC!

Roudoudou a récemment réalisé le portage de la routine de décompression de « Shrinkler », un compresseur très performant, sur Z80. Il explique lui même sur Memoryfull les détails de ce portage, et propose une comparaison avec les autres crunchers.
Shrinkler fait partie de la famille des crunchers qui offrent un niveau de compression très élevé, mais au prix d’un temps de décompression important (environ 5 secondes par kilo-octet de données crunchées). Il faut donc l’utiliser à bon escient! De plus, il a été conçu pour optimiser le code assembleur des processeurs de la famille des 68000. Il n’est utilisable sur CPC qu’en mode ‘data’. Ceci étant dit, c’est donc un outil idéal pour vos futures productions 4KB!
Je vous propose ici un rapide tuto, avec rasm (c’est donc un article très roudoudou-compatible!), dans lequel on va tester la routine de décompession de shrinkler sur CPC! Nous allons compresser une image, ce qui n’est pas forcément le bon usage, mais comme cela ce sera bien visuel. On pourra en particulier constater que la décompression prend un peu de temps… On va donc partir d’un ficher de 16KO a compresser. avec comme adresse de départ #C000, l’adresse de la mémoire vidéo par défaut sur CPC.
Compression
La routine peut être trouvée ici. L’archive contient le fichier original de Roudoudou, mais aussi des version optimisées, dont celle de Madram.
Commençons tout d’abord par compresser le fichier. Ca se passe sur PC, par exemple sous linux ou windows, avec une commande de la sorte (merci Tronic pour m’avoir indiqué les options d’optimistion supplémentaires!) :
shrinkler -d -p -9 inputfile.bin crunched.bin
- -d pour le mode data
- -9 pour augmenter le niveau de compression au maximum
Pour l’exemple, j’ai utilisé le splash screen de Ghost’n’Goblins, qui passe de 16Kb à 8672 octets, une fois compressé.

Décompression
include "../lib/toolbox.asm"
DESTINATION: EQU #C000
ENTRY_POINT: EQU #BB06
; Si ROUDOUDOU = 1 la version originale de Roudoudou est utilisée
; Sinon C'est la version optimisée par Madram qui est utilisée
ROUDOUDOU: EQU 1
ORG #4000
; Saut optionnel si on veut que le point d'entrée soit #4000
; (Et qu'on a 3 octets à perdre)
JP START
CRUNCH_DATA:
INCBIN "crunched.bin"
START:
; Desactive ITs et Sauvegarde des registres Mirroir
DI
DISABLE_INT (inter+1)
PUSH_MIRROR_REGS
EI
; Décompression
LD IX,CRUNCH_DATA
if ROUDOUDOU==1
LD HL,DESTINATION
else
LD DE,DESTINATION
endif
call shrinkler_decrunch
; Restauration registres et ITs
POP_MIRROR_REGS
inter LD HL,0
LD (#38),HL
; Appel du programme décompressé (si existant)
CALL ENTRY_POINT
; On peut aussi restaurer les registres et ITs ici
; Suivant ce que fait le point d'entrée
RET
Les macros pour sauvegarder/restaurer les registres mirroirs sont dans le fichier ‘toolbox.asm’, dont voici le contenu:
; Desactive les interruptions
MACRO DISABLE_INT store
DI
LD HL,(#38)
LD ({store}),HL
LD HL,#c9fb ; EI + RET
LD (#38),HL
EI
MEND
; Sauvegarde les registres mirroir
MACRO PUSH_MIRROR_REGS
di
exx
push hl
push bc
push de
exx
ex AF,AF'
PUSH AF
ex AF,AF'
ei
MEND
; Restaure les registres mirroir
MACRO POP_MIRROR_REGS
di
ex AF,AF'
POP AF
ex AF,AF'
exx
pop de
pop bc
pop hl
exx
ei
MEND
Maintenant si l’on recommence l’opération, mais en visualisant la mémoire qui commence à l’adresse #4000
out &bc00,&0c
out &bd00,&10
Fantaisie en passant
Maintenant que la fine fleure des CPCistes a optimisé au maximum le code de décompression, il est temps d’y ajouter notre grain de sel, d’alourdir leur code, mais pour une bonne cause: en effet, attendre une quinzaine de secondes sans qu’il ne se passe rien à l’écran est passablement long et ennuyeux. Au prix de quelque octets et d’un léger ralentissement, il est possible d’agrémenter ce moment d’une animation minimaliste, qui détournera le spectateur de son impatience maladive.
Dans la routine de décompression, la boucle qui a pour label ‘literal’ est appelée quelques fois par trame vidéo. On peut y insérer son propre code a ce niveau, en prenant bien soin de sauvegarder les registres que l’on va utiliser. On pourrait écrire dans la mémoire vidéo (faire une barre de progression par exemple), mais en l’occurrence comme on décompresse une image, on va se contenter de changer les couleurs du bords:
literal
PUSH AF
PUSH HL
PUSH BC
LD A,C
LD BC,GA_PORT | GA_SELECT_BORDER
OUT (C),C
AND 31
OR GA_SET_COLOR+1
OUT (C),A
POP BC
POP HL
POP AF
Pas de commentaire particulier sur ce bout de code, si ce n’est que la valeur du registre C est utilisée pour décider de la couleur. Cela évite d’utiliser par exemple le registre R pour obtenir une couleur pseudo aléatoire. Par ailleurs, quelques symboles sont utilisés:
GA_PORT: EQU #7F00
GA_SELECT_BORDER: EQU #10
GA_SET_COLOR: EQU #40
Dans la version ‘madram’ on pourrait aussi utiler H ou IXL au lieu de C pour avoir une couleur qui change de facon moins aléatoire.