IK+ Turbo

Roudoudou a récemment produit une version ‘Turbo’ du jeu International Karaté Plus, qui, comme son nom l’indique, est une version plus rapide du jeu, grâce à quelques optimisations qu’il a pu apporter dans le code. Retour sur les modifications apportées.

Pour commencer, le code du jeu, sommairement désassemblé, est disponible sur rasmlive. Les modifications peuvent être appliquées grâce aux symboles suivants:
OPTIM_CLEAR EQU 2 ; Optimisation de l'effacement de l'écran OPTIM_SHADOW EQU 1 ; Optimisation du dessin des ombres OPTIM_ROUD EQU (OPTIM_CLEAR |OPTIM_SHADOW)
Optimisation de l’effacement de l’écran
C’est la l’essentiel du gain de temps que l’on obtient. En effet, a chaque trame, l’intégralité de la zone de l’écran ou se situent les personnage est effacée, en remplissant la zone en question de gris, qui fait 8 bloc de 8 lignes, pour une largeur d’écran de 64 octets (CRTC.R1=32). il s’agit d’écrire 8*8*64 = 4096 octets. L’affichage est géré par un double buffer, donc l’adresse destination est calculée a chaque fois de la facon suivante:
ld hl,#0340
; Current Buffer #80 or #c0
ld a,(curScreenAdr)
or h
ld h,a
Ensuite, l’effacement se fait via une boucle répétée 8 fois, d’appels a une routine d’effacement, qui utilise LDI pour dupliquer une valeur (0 en l’occurence) sur toute la zone concernée:
ld a,8
loop:
push af
,hl
push hl
ld d,h
; DE = HL+1
ld e,l
inc e
ld (hl),0 ; Motif
repeat 7 ; Duplique 64*7+63 = 255 octets
call clearLine64
rend
call clearLine63
pop hl
ld a,h ; Passe a la ligne suivante
add a,8
ld h,a
pop af
dec a
jr nz,loop
La routine clearLine étant simplement une suite de 64 LDI (avec un point d’entrée permettant de faire 63 recopies)
clearLine64:
; Effectue 64 recopies
ldi
clearLine63:
; Effectue 63 recopies
repeat 63
ldi
rend
ret
Optimisation
Clairement, ici, il est possible d’utiliser la pile pour remplir la zone mémoire en question. Le seul souci avec cette technique est qu’il faut faire attention à ce qu’il n’y ait pas de conflit avec les interruptions.
ld hl,#0600
ld a,(curScreenAdr)
; Current Buffer #80 or #c0
or h
ld h,a
ld c,8
.loop1:
ld de,0
; Motif
push hl
di
; Bloque les interruptions
ld (.store_sp+1),sp
; Sauvegarde SP
ld sp,hl
; SP = HL = Destination
ld b,#15
ld a,2
.loop0:
repeat 16
; Ecrit 32 octets
push de
rend
djnz .loop0
ld b,a
dec a
djnz .loop0
.store_sp:
; Restitue SP
ld sp,0
ei
; Retablit les interruptions
pop hl
ld a,h
; Ligne suivante
add 8
ld h,a
dec c
jr nz, .loop1
Optimisation du dessin des ombres
Le dessin des ombres est aussi une routine assez gourmande qui consiste a recopier des octets en mémoire, sur une bande de 16 lignes. Ce code est répété 8 fois, pour chaque début de ligne en mémoire vidéo:
ld a,64
ld b,2
loop: ex af,af'
ld c,(hl)
ld a,(bc)
ld (de),a
inc hl
inc de
ex af,af'
dec a
jr nz, loop
Routine rapide
L’optimisation consiste essentiellement à dérouler la boucle. Ici au lieu de 64 sauts, on en fait plus que deux. De plus les incréments des registres hl et de sont remplacés par des incréments de l et e, car les adresses en questions sont toujours alignées de facon a ce qu’il n’y ait jamais de débordement.. On gagne ainsi 8*(60*3 + 64*2) nops, soit environ 40 raster lines.
ld a,2
ld b,2
.loop:
ex af,af'
repeat 32
; Dérouler la boucle
ld c,(hl)
ld a,(bc)
ld (de),a
inc l
; Incrément L au lieu de HL
inc e
; Incrément E au lieu de DE
rend
ex af,af'
dec a
jp nz, .loop
; Comme on a déroulé, on est trop loin pour faire un saut relatif
Il existe probablement d’autres endroits ou le code pourrait être optimisé. L’affichage du décor par exemple. Mais ces deux optimisations permettent déjà de gagner énormément de CPU, et d’augmenter très clairement le framerate du jeu.
Les versions optimisées présentées ici pourraient l’être encore plus, par exemple en déroulant les boucles complètement, et éviter des sauts inutiles et la sauvegarde de registres, mais elles ont été faites ainsi pour que le code optimisé ne prenne pas plus de place en mémoire, et donc puisse loger a la place du code original.
Liens
- Le code source : https://rasmlive.amstrad.info/edit/SRFqWTPHx2Z2dYhFS