J'ai opté pour une lecture d'un tampon de 256 octets dans l'ISR pour tenir l'execution sous la période d'un /HSYNC. Il me faut donc dans la boucle du jeu, un algo qui va remplir ce tampon avec le code 'command' du PIT CTC0 que j'expliquerais. C'est cet algo qui m pose quelques difficultés et j'espère que l'un d'entre vous pourrait m'aider à y voir plus clair (spécialiste Z80).
Bien allons-y pour les détails :
Le corps d l'ISR :
Code : Tout sélectionner
; prologue de l'ISR
PUSH HL
PUSH AF
if DEBUG
; incrémentation du nombre d'appel à cet ISR
LD HL,isr_count
INC (HL)
endif
; réarmement du compteur CTC2 (mode 0, période : 1)
LD HL,$E006
LD (HL),1
; récupération de $20 (CTC0 mode 0 -> sortie audio haut)
; ou de $28 (CTC0 mode 4 -> sortie audio bas)
LD A,(sfx_buffer)
sfx_isr_buffer_index equ $-2
; reprogrammation du CTC0
LD ($E007),A
; incrémentation de l'adresse du tampon, maintenue dans la page
LD HL,sfx_isr_buffer_index
INC (HL)
; épilogue de l'ISR
POP AF
POP HL
EI
RET
Voici le détails des modes (traduction de l'anglais via DeepL) :
A l'initialisation je fais ceci :Modes de fonctionnement
Les bits D3, D2 et D1 du mot de contrôle définissent le mode de fonctionnement du timer. Il y a 6 modes au total ; pour les modes 2 et 3, le bit D3 est ignoré, donc les modes 6 et 7 manquants sont des alias pour les modes 2 et 3.
Tous les modes sont sensibles à l'entrée GATE, une GATE haute entraînant un fonctionnement normal, mais les effets d'une GATE basse dépendent du mode :
Modes 0 et 4 : Le comptage est suspendu lorsque GATE est basse, et repris lorsque GATE est haute.
Modes 2 et 3 : GATE bas force OUT haut immédiatement (sans attendre une impulsion d'horloge) et réinitialise le compteur (sur le front descendant de l'horloge suivante). Lorsque GATE repasse à l'état haut, le comptage reprend depuis le début.
Mode 0 (000) : Interruption sur le comptage des bornes
Le mode 0 est utilisé pour la génération d'une temporisation précise sous contrôle logiciel. Dans ce mode, le compteur commence à compter à partir de la valeur initiale COUNT chargée dans le compteur, jusqu'à 0. La vitesse de comptage est égale à la fréquence de l'horloge d'entrée.
La broche OUT est mise à un niveau bas après l'écriture du mot de contrôle, et le comptage commence un cycle d'horloge après la programmation du COUNT. La broche OUT reste basse jusqu'à ce que le compteur atteigne 0, auquel cas la broche OUT sera mise en haut jusqu'à ce que le compteur soit rechargé ou que le mot de contrôle soit écrit. Le compteur s'enroule jusqu'à 0xFFFF en interne et continue de compter, mais la broche OUT ne change plus jamais. Le signal Gate doit rester actif et élevé pour un comptage normal. Si Gate passe à l'état bas, le comptage est suspendu, et reprend lorsqu'il repasse à l'état haut.
Le premier octet du nouveau comptage, lorsqu'il est chargé dans le registre de comptage, arrête le comptage précédent.
Mode 2 (X10) : générateur de taux
Dans ce mode, le dispositif agit comme un compteur diviseur par n, qui est généralement utilisé pour générer une interruption d'horloge en temps réel.
Comme dans les autres modes, le processus de comptage commencera le prochain cycle d'horloge après l'envoi de COUNT. OUT restera alors élevé jusqu'à ce que le compteur atteigne 1, et deviendra faible pendant une impulsion d'horloge. Au cycle suivant, le compteur est rechargé, OUT passe à nouveau à l'état haut, et tout le processus se répète.
Le temps entre les impulsions hautes dépend du nombre prédéfini dans le registre du compteur, et est calculé à l'aide de la formule suivante :
Valeur à charger dans le compteur = {\displaystyle f_{\rm {input} \over f_{\rm {output}}{{{displaystyle f_{\rm {input}} \Nsur f_{\rm {output}}}
Notez que les valeurs du registre COUNT vont de {\displaystyle n}n à 1 ; le registre n'atteint jamais zéro.
Mode 3 (X11) : générateur d'ondes carrées
Ce mode est similaire au mode 2. Cependant, la durée des impulsions d'horloge haute et basse de la sortie sera différente du mode 2.
Supposons que {\displaystyle n}n soit le nombre chargé dans le compteur (le message COUNT), la sortie sera haute pour {\displaystyle \left\lceil {n \over 2}\right\rceil }{\displaystyle \left\lceil {n \over 2}\right\rceil } comptes, et faible pour {\displaystyle \left\lfloor {n \over 2}\right\rfloor }{\displaystyle \left\lfloor {n \over 2}\right\rfloor } comptes. Ainsi, la période sera de {\displaystyle n}n comptes, et si {\displaystyle n}n est impair, le demi-cycle supplémentaire est passé avec OUT haut.
Mode 4 (100) : Strobe déclenché par le logiciel
Après le chargement du mot de contrôle et de COUNT, la sortie restera haute jusqu'à ce que le compteur atteigne zéro. Le compteur génère alors une impulsion basse pendant 1 cycle d'horloge (un strobe) - après quoi la sortie redevient haute.
La GATE basse suspend le comptage, qui reprend lorsque la GATE redevient haute.
Code : Tout sélectionner
; CTC1 placé en mode 2 (rate generator, avec compteur LSB seulement)
LD HL,$E007 ; $E007 - counter control
LD (HL),$54 ; counter #1 - MODE 2 - set only LSB counter
; CTC2 placé en mode 0 (interrupt on terminal count, avec compteur LSB seulement)
LD (HL),$90 ; counter #2 - MODE 0 - set only LSB counter
; CTC1 a pour période 3 /HSYNC [ 2 (OUT1->CLK2 : H) -> 1 (OUT1->CLK2 : H) -> 0 (OUT1->CLK2 : L) ]
LD L,$05 ; $E005 - counter #1
LD (HL),2
; CTC2 doit enclencher l'interruption à chaque passage de OUT1 à L
LD L,$06 ; $E006 - counter #2
LD (HL),1
; Activation de l'interruption du CTC2 (il n'y a pas d'autre source)
EI
Bon entrons dans le vif du sujet : je veux faire l'algo qui remplit ce tampon de 256 octets dans la boucle du jeu. J'ai codé un truc mais je pense que je n'arrive plus à réfléchir à ce stade.
Voici le son code actuel :
Code : Tout sélectionner
sfx_stop:
; GATE0 à 0 -> coupure du son
LD L,$E008
LD (HL),0
RET
sfx_start:
; affectation initiale et courante de l'adresse du bloc de musique passé en HL
LD (sfx_play_src_caddr-2),HL
LD (sfx_play_src_iaddr-2),HL
; affectation initiale et courante de la taille du bloc de musique passé en BC
LD (sfx_play_src_csize-2),BC
LD (sfx_play_src_isize-2),BC
; remplissage du tampon
CALL sfx_play
; CTC0 en mode 4 -> OUT0 -> 1 (signal audio à 0)
LD HL,$E007
LD (HL),$28
; GATE0 à 1 -> reprise du son
LD L,$08
LD (HL),1
RET
sfx_play:
; address courante du tampon commence 128 octets plus loin
LD DE,sfx_buffer+128
sfx_play_dst_caddr:
; lire l'index du tampon en cours dans l'ISR
LD A,(sfx_isr_buffer_index)
; vérifier que l'on ne cherche pas à remplir sur la demi partie en cours de lecture par l'ISR
XOR E
AND $80
; si oui, trop tôt pour remplir le tampon - attendons le frame suivant
RET Z
; adresse courante du bloc de musique à lire
LD HL,0
sfx_play_src_caddr:
; taille restante du bloc de musique à lire
LD IX,0
sfx_play_src_csize:
; 128 octets à écrire dans le tampon et B contient le compteur d'ISR qui reste avant de basculer la sortie audio
LD BC,1:128
sfx_play_dst_csize:
; on se cale sur un /HSYNC
HALT
; et on entre directe pour traiter ce qui reste dans B
JP sfx_play_kernel
sfx_play_loop:
; lire le compteur d'ISR dans le bloc de musique
LD B,(HL)
; incrémenter l'adresse courante du bloc de musique
INC HL
; le fichier binaire compte à partir de 0, nous on veut à partir de 1 ici
INC B
sfx_play_kernel:
; décrémente la taille du bloc de musique
DEC IX
; si on arrive à la fin du bloc de musique, il faut "rewind-er"
LD A,IXL
OR IXH
CALL Z,sfx_play_rewind
; octet à mettre dans le tampon : $28 pour signal audio bas, $20 signal audio haut
LD A,$28
sfx_play_dst_cbyte:
; écrire l'octet dans le tampon
LD (DE),A
; incrémenter l'adresse de tampon tout en le maintenant dans un page
INC E
; on sort si le tampon est remplie des 128 octets
DEC C
JR Z,sfx_play_eob
; le compteur d'ISR lu dans le bloc de musique est décrémenté et on reboucle si non nul
DJNZ sfx_play_kernel
; le compteur d'ISR est à 0 alors on bascule le signal audio et on reboucle
XOR $20
LD (sfx_play_dst_cbyte-1),A
JP sfx_play_loop
sfx_play_eob:
; on sauvegarde le contexte pour le prochain appel à sfx_play
DEC B
LD (sfx_play_src_caddr-2),HL
LD (sfx_play_src_csize-2),IX
LD (sfx_play_dst_caddr-2),DE
LD A,B
LD (sfx_play_dst_csize-1),A
RET
sfx_play_rewind:
; on restaure l'adresse et la taille du bloc de musique à lire
LD HL,0
sfx_play_src_iaddr:
LD IX,0
sfx_play_src_isize:
LD (sfx_play_src_caddr-2),HL
LD (sfx_play_src_csize-2),IX
RET