Ce soir j'ai voulu programmer un "petit" moteur de jeu de plate-forme tout bête pour Game Boy, un truc aussi con que genre:
u16 x, y, vx, vy;
extern u8 map[32][32];
void perso_init() {
x = y = vx = vy = 0;
}
void perso_handle() {
vy += 10; // gravité
x += vx, y += vy;
}
void perso_draw() {
OAM[0] = y >> 8;
OAM[1] = x >> 8;
}
void perso_check_col() {
if (map[y >> 11][x >> 11]) // Collision bidon
vy = 0;
}
Tout est clair dans ma tête donc, alors je me mets à l'écrire en ASM. Et là c'est le drâme, jugez par vous même (le moteur fait l'équivalent de ce code C):
; Constantes
.GRAVITY = 10
.OAM = 0xfe00
; Variables
.area _BSS
.x: ; position 24-bit fixed point (16.8)
.ds 3
.y:
.ds 3
.vx: ; vitesse 16-bit fixed point (8.8)
.ds 2
.vy:
.ds 2
.area _CODE
; Initialisation du personnage
_perso_init::
xor a ; a = 0
ld hl, #.x
ld (hl+), a ; x = 0
ld (hl+), a
ld (hl+), a
ld (hl+), a ; y = 0
ld (hl+), a
ld (hl+), a
ld (hl+), a ; vx = 0
ld (hl+), a
ld (hl+), a ; vy = 0
ld (hl+), a
ret
; Gestion de la physique du personnage
_perso_handle::
ld hl, #.vy ; vy += GRAVITY
ld b, #.GRAVITY
call add16_val8
call load16 ; push vy
push bc
ld hl, #.vx ; bc = vx
call load16
pop de ; de = vy
call _perso_move ; move(vx, vy)
ret
; Bouge le personnage
; bc = delta X, de = delta Y (1.7.8 signed fixed point)
_perso_move::
push de
ld d, #2 ; Pour x et y
ld hl, #.x
move_boucle:
bit 7, b ; bc.signe
jr nz, move_bc_negatif
ld a, (hl) ; partie basse
add c
ld (hl+), a
ld a, (hl) ; partie moyenne
adc b
ld (hl+), a
ld a, (hl) ; partie haute
adc #0
ld (hl+), a
jr move_suite
move_bc_negatif:
ld a, b ;
cpl ; }
ld b, a ; rend bc positif
ld a, c ; }
cpl ;
ld c, a ;
ld a, (hl) ; partie basse
sub c
ld (hl+), a
ld a, (hl) ; partie moyenne
sbc b
ld (hl+), a
ld a, (hl) ; partie haute
sbc #0
ld (hl+), a
move_suite:
dec d
ret z ; terminé? (y traité)
pop bc ; de a été poussé avant
jr move_boucle
; Retourne l'adresse de la map pour un point donné
; BC = point voulu (x, y) par rapport au perso
; shared_asm[0].w = adresse de la map
get_map_addr:
ld hl, #(.x + 1) ; de = (x, y)
ld d, (hl)
ld hl, #(.y + 1)
ld e, (hl)
; (x, y) += (b, c)
ld a, d
add b
ld d, a
push af
ld a, e
add c
ld e, a
push af
; (x, y) /= 8
srl d
srl d
srl d
srl e
srl e
srl e
; Retenue pour (x, y) += (b, c) avant
pop af
ld a, e
adc #0
ld e, a
pop af
ld a, d
adc #0
ld d, a
; On peut encore ajouter à (d, e) les 3 bits du haut de la position
ld hl, #(.x + 2) ; d += (x.hi & 7) << 5
ld a, (hl+)
and #7
swap a
sla a
add d
ld d, a
ld hl, #(.y + 2) ; pareil pour e
ld a, (hl)
and #7
swap a
sla a
add e
ld e, a
; FIXME
ld hl, #_bglvl01_map
ld a, d ; bc = d % 32
and #31
ld c, a
ld b, #0
add hl, bc ; colonne correspondante dans la map
ld b, e ; bc = e * 32 (largeur d'une map)
srl b ; 3 bits partent sur c
srl b
srl b
ld a, e ; 3 bits du bas -> en haut de c
and #7
swap a
sla a
ld c, a
add hl, bc ; ligne correspondante dans la map
ret
_perso_check_col::
ld b, #0
ld c, #0
call get_map_addr ; check en haut à gauche
ld a, (hl)
and a
ret z ; rien là?
; Collision -> stoppe net
ld hl, #.vy
ld a, #0
ld (hl+), a
ld (hl+), a
ret
; Dessin du personnage dans le sprite 0
_perso_draw::
ld hl, #.OAM
ld bc, #(.y + 1) ; y
ld a, (bc)
add #16
ld (hl+), a
ld bc, #(.x + 1) ; x
ld a, (bc)
add #8
ld (hl), a
ret
; Retourne la position du perso
; HL = tableau de 6 octets
_perso_get_pos::
; source = .x, dest = hl, count = 6
push hl
ld hl, #.x
pop de
ld bc, #6
call memcpy
ret
Je me dis que soit 1) les programmeurs de l'époque avaient sacrément du skill pour faire des jeux complets 2) je suis une buse totale mais je pense qu'il y a beaucoup des deux
