Revenir au plan du site

Exporter notre jeu TRON pour être utilisé sur CPC ou GX4000

Le crossdev c'est sympa et pratique, on a le résultat directement dans l'émulateur mais il y a quelques petites différences avec la création d'un véritable exécutable (et encore plus dans le cas d'une cartouche!!!)


Export aux formats CDT et DSK

Dans le cas d'un simple exécutable comme c'est le cas pour nous, l'usage du RUN depuis le Basic présente quelques avantages : Les couleurs sont déjà prêtes, l'écran est déjà prêt et est effacé. Comme nous n'utilisons pas les interruptions, on va se 'couper' du système avec les classiques instructions qui ont fait le tour de la rubrique Rubidouille d'Amstrad 100%.
di ; couper les interruptions
ld hl,#C9FB ; valeur inversée des instructions EI:RET
ld (#38),hl

Il faut supprimer les lignes faisant référence au SNAPSHOT et les remplacer par BUILDTAPE (pour produire un fichier K7) ainsi qu'un SAVE en fin de source pour le fichier DSK.
; pour rappel, SAVE fichier,debut,taille,DSK,fichierDSK
SAVE 'tron.bin',#100,$-#100,DSK,'tron.dsk'

Et voici le source complet du TRON
BUILDTAPE
ORG #100 : RUN #100

;======================================
EffaceEcran
;======================================
ld hl,#C000
xor a ; A=0
.loop
ld (hl),a ; écrire #00 à l'adresse pointée par HL
inc l ; incrémenter seulement L positionne le flag Z une fois revenu à zéro
jr nz,.loop ; on boucle donc tant que L est différent de zéro
inc h ; même chose pour H, sauf que nous savons ici que L vaut zéro
jr nz,.loop ; tant que H ne vaut pas zéro on reboucle
; ici nous avons HL=#0000 nous sommes revenus au début de la mémoire, l'écran est effacer, on s'arrête

; initialiser les coordonnées de notre point
ld hl,160 : ld (positionx),hl
ld hl,100 : ld (positiony),hl ; position de départ au centre de l'écran
ld hl,0 : ld (increment_x),hl ; init!
ld hl,-1 : ld (increment_y),hl ; initialiser le premier mouvement vers le haut!

;======================================
AfficherPoint
;======================================
ld bc,(positionx)
ld hl,(positiony)
call CalculeAdressePixel
ld a,(hl) : and c ; on ne conserve que l'éventuel point qu'on veut mettre
cp c : jp z,FinDuJeu ; si le point était déjà là, on quitte
ld a,(hl) : or c : ld (hl),a ; fusionner le pixel à l'écran

call waitVBLstart ; 50Hz
call waitVBLstart ; 25Hz
call waitVBLstart ; 17Hz allez on speed un peu à 17 images par seconde :)

;======================================
LireLeClavier
;======================================
call lectureMatriceClavier

OCTET_CURSEUR_HAUT equ matriceClavier : BIT_CURSEUR_HAUT equ 1
OCTET_CURSEUR_DROITE equ matriceClavier : BIT_CURSEUR_DROITE equ 2
OCTET_CURSEUR_BAS equ matriceClavier : BIT_CURSEUR_BAS equ 4
OCTET_CURSEUR_GAUCHE equ matriceClavier+1 : BIT_CURSEUR_GAUCHE equ 1
OCTET_TOUCHE_ESPACE equ matriceClavier+5 : BIT_TOUCHE_ESPACE equ 128

; si on appuie sur Espace, on revient à l'effacement de l'écran
ld a,(OCTET_TOUCHE_ESPACE) : and BIT_TOUCHE_ESPACE : jp z,EffaceEcran

;
;
; touches du curseur
ld a,(OCTET_CURSEUR_HAUT) : and BIT_CURSEUR_HAUT : call z,gererHaut
ld a,(OCTET_CURSEUR_BAS) : and BIT_CURSEUR_BAS : call z,gererBas
ld a,(OCTET_CURSEUR_DROITE) : and BIT_CURSEUR_DROITE : call z,gererDroite
ld a,(OCTET_CURSEUR_GAUCHE) : and BIT_CURSEUR_GAUCHE : call z,gererGauche
; gérer le déplacement du point et le test des bords
ld hl,(positionx) : ld de,(increment_x) : add hl,de : ld (positionx),hl
ld a,h : cp #FF : jp z,FinDuJeu ; si HL est négatif, alors on quitte
xor a : ld de,320 : sbc hl,de : jp z,FinDuJeu ; si HL vaut 320 alors on est sorti de l'écran
ld hl,(positiony) : ld de,(increment_y) : add hl,de : ld (positiony),hl
ld a,h : cp #FF : jp z,FinDuJeu ; si HL est négatif, alors on quitte
ld a,l : cp 200 : jp z,FinDuJeu ; si HL vaut 200, on est sorti de l'écran et on quitte
;
jp AfficherPoint ; revenir tout le temps à l'affichage!
;======================================
; sous-routines de gestion des touches
;======================================
gererHaut ld hl,-1 : ld (increment_y),hl : ld hl,0 : ld (increment_x),hl : ret
gererBas ld hl,1 : ld (increment_y),hl : ld hl,0 : ld (increment_x),hl : ret
gererGauche ld hl,-1 : ld (increment_x),hl : ld hl,0 : ld (increment_y),hl : ret
gererDroite ld hl,1 : ld (increment_x),hl : ld hl,0 : ld (increment_y),hl : ret
;======================================
CalculeAdressePixel
;======================================
; BC=coordonnée X (0-319)
; HL=coordonnée Y (0-199)
ld de,tableau
add hl,hl ; adresses 16 bits, il faut indexer de 2 en 2
add hl,de
ld a,(hl) : inc hl
ld h,(hl) : ld l,a
; HL=adresse de la ligne
ld a,c ; on sauvegarde le X avant de diviser par 4
srl bc : srl bc ; diviser le X par 4 pour avoir l'octet en mode 1
add hl,bc
ld c,%10000000 ; encre 1 pour le pixel mode 1 le plus à gauche dans l'octet
and 3 ; avec le modulo 4 on va savoir quel est le pixel en partant de la gauche
jr z,.noShift
.Shift
srl c
dec a
jr nz,.Shift
.noShift
ret ; HL=adresse écran aux coordonnées X/Y données et C est le pixel d'encre 1
;-------------------
adresse_ecran=#C000
largeur_ecran=80
tableau
repeat 25
repeat 8
defw adresse_ecran
adresse_ecran+=#800
rend
adresse_ecran+=largeur_ecran
adresse_ecran-=#4000
rend
;==============
waitVBLstart
;==============
ld b,#F5
.loop in a,(c) : rra : jr c,.loop ; attendre l'absence de VBL
;==============
waitVBL
;==============
ld b,#F5
.loop in a,(c) : rra : jr nc,.loop ; attendre la VBL
ret
;======================================
FinDuJeu
;======================================
ld xl,100 ; 100 VBL = 2 secondes
.attente call waitVBLstart : dec xl : jr nz,.attente
jp EffaceEcran ; on recommence le jeu!
;======================================
lectureMatriceClavier
;======================================
ld hl,matriceClavier
ld bc,#f782
out (c),c
ld bc,#f40e
ld e,b
out (c),c
ld bc,#f6c0
ld d,b
out (c),c
out (c),0
ld bc,#f792
out (c),c
ld a,#40
ld c,d
.loop ld b,d
out (c),a ; sélectionner la ligne
ld b,e
ini ; lire et stocker dans notre tableau
inc a
inc c
jr nz,.loop
ret

matriceClavier defs 10,#FF
;=================================================
; en fin du programme, nos coordonnées sur 16 bits
;=================================================
positionx defw 0
positiony defw 0
increment_x defw 0
increment_y defw 0

SAVE 'tron.bin',#100,$-#100,DSK,'tron.dsk'


Export au format CPR

L'export au format CPR ne consiste pas qu'à indiquer à l'assembleur d'exporter directement une cartouche. En effet, lorsqu'un Amstrad Plus ou une GX-4000 démarre sur une cartouche, la machine n'est pas initialisée, il faut donc réaliser tout un tas d'opérations préliminaires!



Ces opérations se passent exclusivement au début du source, je vous mets directement le source avec les commentaires et un trait de rupture avec le source de base. Chose IMPORTANT à remarquer, le programme commence par s'exécuter DEPUIS la ROM, c'est à dire un espace dans lequel on ne peut pas écrire. Toute écriture sur l'adressage de la ROM aura en fait lieu "sous" la ROM, dans la mémoire.
Aussi, après nos initialisations, le programme de démarrage va copier le reste de la ROM en mémoire, se lancer et déconnecter la ROM

Enfin, léger 'détail' à ne pas oublier pour la GX4000, elle n'a pas de clavier il faut donc changer les contrôles du curseur par le Joystick!
BUILDCPR ; indiquer à RASM de produire une cartouche
BANK 0 ; ouvrir un espace ROM (16k max)
ORG 0 ; l'instruction BANK s'ouvre par défaut sur zéro
; je préfère l'ajouter car on peut se promener d'une ROM à l'autre
; et l'assembleur conserve le pointeur courant

di ; normalement elles sont déjà coupées, mais coupons-les quand même!
; réglages du CRTC pour un écran de type BASIC
ld bc,#BC00 : out (c),c : ld bc,#BD00+63 : out (c),c
ld bc,#BC01 : out (c),c : ld bc,#BD00+40 : out (c),c
ld bc,#BC02 : out (c),c : ld bc,#BD00+46 : out (c),c
ld bc,#BC03 : out (c),c : ld bc,#BD00+#8E : out (c),c
ld bc,#BC04 : out (c),c : ld bc,#BD00+38 : out (c),c
ld bc,#BC05 : out (c),c : ld bc,#BD00+0 : out (c),c
ld bc,#BC06 : out (c),c : ld bc,#BD00+25 : out (c),c
ld bc,#BC07 : out (c),c : ld bc,#BD00+30 : out (c),c
ld bc,#BC08 : out (c),c : ld bc,#BD00+0 : out (c),c
ld bc,#BC09 : out (c),c : ld bc,#BD00+7 : out (c),c
ld bc,#BC0C : out (c),c : ld bc,#BD00+#30 : out (c),c
ld bc,#BC0D : out (c),c : ld bc,#BD00+0 : out (c),c
; initialisation du PPI par défaut
ld bc,#F782 : out (c),c
ld b,#F4 : out (c),0 : ld b,#F6 : out (c),0
; initialisation de la mémoire par défaut
ld bc,#7FC0 : out (c),c ; configuration type 464, pas de mémoire étendue connectée
; on va se mettre quelques couleurs
ld bc,#7F10 : out (c),c : ld a,#55 : out (c),a ; border bleu vif
ld bc,#7F00 : out (c),c : ld a,#44 : out (c),a ; fond bleu marine
ld bc,#7F01 : out (c),c : ld a,#43 : out (c),a ; crayon jaune clair pastel
; effacer TOUTE la mémoire car comme on l'a vu, elle n'est pas propre au démarrage
ld hl,0 : ld a,l
InitialiseMemoire ld (hl),a : inc l : jr nz,InitialiseMemoire : inc h : jr nz,InitialiseMemoire
; Installer un retour d'interruption par défaut (même si on ne s'en sert pas dans ce programme...)
ld hl,#C9FB ; ld (#38),hl
; copier le programme de la ROM à la RAM
ld hl,#100 : ld de,#100 ; au même endroit!
ld bc,fin_du_source-#100
ldir ; COPIER!
; Installer un pointeur de pile juste avant le programme en #100
ld sp,#100
ld bc,#7F80+%1101 : out (c),c ; déconnecter toutes les ROM et MODE 1!!!
; le programme va continuer d'ici à #100 dans les zéros de la mémoire
; qu'on vient juste d'initialiser, ça tombe bien
; nop, nop, nop
;=========== source de BASE ===========================
ORG #100 ; le RUN devient obsolète, une cartouche démarre TOUJOURS en 0
EffaceEcran
ld hl,#C000
xor a ; A=0
.loop
ld (hl),a ; écrire #00 à l'adresse pointée par HL
inc l ; incrémenter seulement L positionne le flag Z une fois revenu à zéro
jr nz,.loop ; on boucle donc tant que L est différent de zéro
inc h ; même chose pour H, sauf que nous savons ici que L vaut zéro
jr nz,.loop ; tant que H ne vaut pas zéro on reboucle
; ici nous avons HL=#0000 nous sommes revenus au début de la mémoire, l'écran est effacer, on s'arrête

; initialiser les coordonnées de notre point
ld hl,160 : ld (positionx),hl
ld hl,100 : ld (positiony),hl ; position de départ au centre de l'écran
ld hl,0 : ld (increment_x),hl ; init!
ld hl,-1 : ld (increment_y),hl ; initialiser le premier mouvement vers le haut!

;======================================
AfficherPoint
;======================================
ld bc,(positionx)
ld hl,(positiony)
call CalculeAdressePixel
ld a,(hl) : and c ; on ne conserve que l'éventuel point qu'on veut mettre
cp c : jp z,FinDuJeu ; si le point était déjà là, on quitte
ld a,(hl) : or c : ld (hl),a ; fusionner le pixel à l'écran

call waitVBLstart ; 50Hz
call waitVBLstart ; 25Hz
call waitVBLstart ; 17Hz allez on speed un peu à 17 images par seconde :)

;======================================
LireLeClavier
;======================================
call lectureMatriceClavier

OCTET_JOYSTIK_HAUT equ matriceClavier+9 : BIT_JOYSTIK_HAUT equ 1
OCTET_JOYSTIK_BAS equ matriceClavier+9 : BIT_JOYSTIK_BAS equ 2
OCTET_JOYSTIK_GAUCHE equ matriceClavier+9 : BIT_JOYSTIK_GAUCHE equ 4
OCTET_JOYSTIK_DROITE equ matriceClavier+9 : BIT_JOYSTIK_DROITE equ 8
OCTET_TOUCHE_ESPACE equ matriceClavier+9 : BIT_TOUCHE_ESPACE equ 16

; si on appuie sur Espace, on revient à l'effacement de l'écran
ld a,(OCTET_TOUCHE_ESPACE) : and BIT_TOUCHE_ESPACE : jp z,EffaceEcran

;
;
; touches du curseur
ld a,(OCTET_JOYSTIK_HAUT) : and BIT_JOYSTIK_HAUT : call z,gererHaut
ld a,(OCTET_JOYSTIK_BAS) : and BIT_JOYSTIK_BAS : call z,gererBas
ld a,(OCTET_JOYSTIK_DROITE) : and BIT_JOYSTIK_DROITE : call z,gererDroite
ld a,(OCTET_JOYSTIK_GAUCHE) : and BIT_JOYSTIK_GAUCHE : call z,gererGauche
; gérer le déplacement du point et le test des bords
ld hl,(positionx) : ld de,(increment_x) : add hl,de : ld (positionx),hl
ld a,h : cp #FF : jp z,FinDuJeu ; si HL est négatif, alors on quitte
xor a : ld de,320 : sbc hl,de : jp z,FinDuJeu ; si HL vaut 320 alors on est sorti de l'écran
ld hl,(positiony) : ld de,(increment_y) : add hl,de : ld (positiony),hl
ld a,h : cp #FF : jp z,FinDuJeu ; si HL est négatif, alors on quitte
ld a,l : cp 200 : jp z,FinDuJeu ; si HL vaut 200, on est sorti de l'écran et on quitte
;
jp AfficherPoint ; revenir tout le temps à l'affichage!
;======================================
; sous-routines de gestion des touches
;======================================
gererHaut ld hl,-1 : ld (increment_y),hl : ld hl,0 : ld (increment_x),hl : ret
gererBas ld hl,1 : ld (increment_y),hl : ld hl,0 : ld (increment_x),hl : ret
gererGauche ld hl,-1 : ld (increment_x),hl : ld hl,0 : ld (increment_y),hl : ret
gererDroite ld hl,1 : ld (increment_x),hl : ld hl,0 : ld (increment_y),hl : ret
;======================================
CalculeAdressePixel
;======================================
; BC=coordonnée X (0-319)
; HL=coordonnée Y (0-199)
ld de,tableau
add hl,hl ; adresses 16 bits, il faut indexer de 2 en 2
add hl,de
ld a,(hl) : inc hl
ld h,(hl) : ld l,a
; HL=adresse de la ligne
ld a,c ; on sauvegarde le X avant de diviser par 4
srl bc : srl bc ; diviser le X par 4 pour avoir l'octet en mode 1
add hl,bc
ld c,%10000000 ; encre 1 pour le pixel mode 1 le plus à gauche dans l'octet
and 3 ; avec le modulo 4 on va savoir quel est le pixel en partant de la gauche
jr z,.noShift
.Shift
srl c
dec a
jr nz,.Shift
.noShift
ret ; HL=adresse écran aux coordonnées X/Y données et C est le pixel d'encre 1
;-------------------
adresse_ecran=#C000
largeur_ecran=80
tableau
repeat 25
repeat 8
defw adresse_ecran
adresse_ecran+=#800
rend
adresse_ecran+=largeur_ecran
adresse_ecran-=#4000
rend
;==============
waitVBLstart
;==============
ld b,#F5
.loop in a,(c) : rra : jr c,.loop ; attendre l'absence de VBL
;==============
waitVBL
;==============
ld b,#F5
.loop in a,(c) : rra : jr nc,.loop ; attendre la VBL
ret
;======================================
FinDuJeu
;======================================
ld xl,100 ; 100 VBL = 2 secondes
.attente call waitVBLstart : dec xl : jr nz,.attente
jp EffaceEcran ; on recommence le jeu!
;======================================
lectureMatriceClavier
;======================================
ld hl,matriceClavier
ld bc,#f782
out (c),c
ld bc,#f40e
ld e,b
out (c),c
ld bc,#f6c0
ld d,b
out (c),c
out (c),0
ld bc,#f792
out (c),c
ld a,#40
ld c,d
.loop ld b,d
out (c),a ; sélectionner la ligne
ld b,e
ini ; lire et stocker dans notre tableau
inc a
inc c
jr nz,.loop
ret

matriceClavier defs 10,#FF
;=================================================
; en fin du programme, nos coordonnées sur 16 bits
;=================================================
positionx defw 0
positiony defw 0
increment_x defw 0
increment_y defw 0
fin_du_source

Et voilà, nous avons notre TRON autonome avec un border différend du fond!