La Place des Développeurs Appeler des fonctions du Bios dans le hook H.TIMI
aoineko
Membre non connecté
Conseiller Municipal
Hello,
J'ai voulais tester le système de Hook du MSX mais je suis tombé sur un bug que je ne comprends vraiment pas.
Dans mon programme de test, j'ai juste une fonction qui incrémente un compter, change la couleur de fond et fait un beep via des routines bios.
Elle est branché sur le hook H.TIMI (vblank).
Ca fonctionne pendant un certains temps, puis ça fait reboot le MSX !
J'ai essayé de tracer le problème avec le debugger de OpenMSX, mais tout à l'air bon :
- L'adresse du Hook contient bien des RET au début, puis les instructions asm de JUMP vers ma fonction y sont bien copiés
- Je break sans soucis dans ma fonction de hook
- Après plusieurs centaines d'itérations tout semble normal : le code à l'adresse du HOOK est toujours présent et mon compteur s'incrémente tranquillement
Une idée de ce qu'il pourrait se produire !?
Voici le code du hook :
Et le code de la boucle principale :
J'ai voulais tester le système de Hook du MSX mais je suis tombé sur un bug que je ne comprends vraiment pas.
Dans mon programme de test, j'ai juste une fonction qui incrémente un compter, change la couleur de fond et fait un beep via des routines bios.
Elle est branché sur le hook H.TIMI (vblank).
Ca fonctionne pendant un certains temps, puis ça fait reboot le MSX !
J'ai essayé de tracer le problème avec le debugger de OpenMSX, mais tout à l'air bon :
- L'adresse du Hook contient bien des RET au début, puis les instructions asm de JUMP vers ma fonction y sont bien copiés
- Je break sans soucis dans ma fonction de hook
- Après plusieurs centaines d'itérations tout semble normal : le code à l'adresse du HOOK est toujours présent et mon compteur s'incrémente tranquillement
Une idée de ce qu'il pourrait se produire !?
Voici le code du hook :
Code C :
void VBlankHook() { DisableInterrupt(); timer++; g_BAKCLR = timer & 0x0F; CallBios(CHGCLR); CallBios(BEEP); EnableInterrupt(); }
Code ASM :
_VBlankHook:: di ld iy, #_timer inc 0 (iy) ld a, 0 (iy) and a, #0x0f ld (BAKCLR), a call CHGCLR call BEEP ret ei
Et le code de la boucle principale :
Code C :
void MainLoop() { SetHookFunc(H_TIMI, VBlankHook); while(1) {} }
Code ASM :
_MainLoop:: ld bc, #_VBlankHook ld hl, #0xfd9f ld (hl), #0xc3 ld (H_TIMI), bc 00102$: jr 00102$
On est toujours ignorant avant de savoir.
aoineko
Membre non connecté
Conseiller Municipal
Bon, comme souvent, c'est en essayant de bien expliquer un problème... qu'on en trouve la solution.
En fait la fonction bios "BEEP" empile des datas sans les dépiler !
En tout cas, la stack augmente de 52 octets à chaque appel de ma fonction callback... jusqu'à faire un écrasement de la mémoire.
Si je commente l'appel à la fonction BEEP, la stack ne bouge plus (la fonction CHGCLR (change color) n'a donc pas de problème de "fuite", elle).
Comment est-ce possible que la fonction BEEP puisse fuiter 52 octets à chaque appel !?
Je vais continuer mes tests pour essayer d'éclaircir ce mystère...
EDIT : J'ai check que si on fait des appels à la fonction bios BEEP en dehors de la fonction du hook (dans le programme principal), il n'y a pas de "fuite" mémoire. Y a t'il une liste de fonction bios à ne pas utiliser dans les Hook ? Toutes !? Ou y a-t-il une technique pour pouvoir les utiliser sans avoir les "fuites" ?
En fait la fonction bios "BEEP" empile des datas sans les dépiler !
En tout cas, la stack augmente de 52 octets à chaque appel de ma fonction callback... jusqu'à faire un écrasement de la mémoire.
Si je commente l'appel à la fonction BEEP, la stack ne bouge plus (la fonction CHGCLR (change color) n'a donc pas de problème de "fuite", elle).
Comment est-ce possible que la fonction BEEP puisse fuiter 52 octets à chaque appel !?
Je vais continuer mes tests pour essayer d'éclaircir ce mystère...
EDIT : J'ai check que si on fait des appels à la fonction bios BEEP en dehors de la fonction du hook (dans le programme principal), il n'y a pas de "fuite" mémoire. Y a t'il une liste de fonction bios à ne pas utiliser dans les Hook ? Toutes !? Ou y a-t-il une technique pour pouvoir les utiliser sans avoir les "fuites" ?
On est toujours ignorant avant de savoir.
ericb59
Membre non connecté
Conseiller Municipal
As tu regardé les fonctions d’interruption de Fusion-c 1.3
Il y en a une sur le vdp et une sur htmi
Apparemment c’est la seule méthode qui fonctionne bien depuis le dos.
Je n’en suis pas l’auteur et je n’y comprend pas grand chose en fait.
Le problème étant que le bios lui même fait des interruptions lui aussi, et qu’il faut trouver une astuce.
En tout cas c’est ce qu’il me semble ?
Il y en a une sur le vdp et une sur htmi
Apparemment c’est la seule méthode qui fonctionne bien depuis le dos.
Je n’en suis pas l’auteur et je n’y comprend pas grand chose en fait.
Le problème étant que le bios lui même fait des interruptions lui aussi, et qu’il faut trouver une astuce.
En tout cas c’est ce qu’il me semble ?
Sector28
Membre non connecté
Villageois
Le BEEP prend trop de temps à s’exécuter, pendant qu'il s’exécute, une autre interuption se produit, celle-ci a son tour exécute BEEP, pendant que ce dernier s’exécute, une interuption se produit, celle-ci a son tour execute BEEP, pendant que ce dernier s’exécute, une interuption se produit, celle-ci a son tour execute BEEP, pendant que ce dernier s’exécute, une interuption se produit, celle-ci a son tour execute BEEP, pendant que ce dernier s’exécute, une interuption se produit, celle-ci a son tour execute BEEP, pendant que ce dernier s’exécute, une interuption se produit, celle-ci a son tour execute BEEP, pendant que ce dernier s’exécute, une interuption se produit, celle-ci a son tour execute BEEP, pendant que ce dernier s’exécute, une interuption se produit, celle-ci a son tour execute BEEP, pendant que ce dernier s’exécute, une interuption se produit, celle-ci a son tour execute BEEP, pendant que ce dernier s’exécute, une interuption se produit, celle-ci a son tour execute BEEP, .. puis ça plante
DONALD TRUMP IS FAST APPROACHING
NEMESIS ! RETURN IMMEDIATELY !
aoineko
Membre non connecté
Conseiller Municipal
ericb59 :
As tu regardé les fonctions d’interruption de Fusion-c 1.3
J'ai jeté un œil, mais les fichiers complet en assembleur sont compliqués à lire pour moi. J'ai vu que (tous) les appels au Bios se font via des lectures/écritures interslot. Ce qui est pratique si la page 0 ne pointe pas vers la Main-ROM, mais je trouve que ça alourdit/ralenti l'exécution dans le cas de base (ou la Main-ROM est présente en page 0). Je pense pas que le problème vienne de là vu que je touche pas aux pages.
Sector28 :
Le BEEP prend trop de temps à s’exécuter, pendant qu'il s’exécute, une autre interuption se produit, celle-ci a son tour exécute BEEP, pendant que ce dernier s’exécute, une interuption se produit [..] puis ça plante
Pourtant, je désactive les interruptions avec DI/EI durant l'exécution du hook...
On est toujours ignorant avant de savoir.
Sector28
Membre non connecté
Villageois
J'ai un peu modifié ..
Code :
void main(void)
{
...
int sem=0;
...
}
void VBlankHook()
{
/* DisableInterrupt();*/
timer++;
g_BAKCLR = timer & 0x0F;
if (!sem) {
sem=1;
CallBios(CHGCLR);
CallBios(BEEP);
sem=0;
}
/* EnableInterrupt(); */
}
DONALD TRUMP IS FAST APPROACHING
NEMESIS ! RETURN IMMEDIATELY !
aoineko
Membre non connecté
Conseiller Municipal
Sector28 :
J'ai un peu modifié ..
Tu viens de réinventer les Mutex, qu'on utilise en programmation multi-thread.
Ca peut être utile, mais dans mon cas particulier, je pensais plutôt essayer de mettre en place un système de deferred hook.
L'idée c'est d'utiliser la boucle principale pour synchroniser les hooks.
En gros, au lieu de s'exécuter, les hooks vont s'enregistrer dans une liste, puis dans la boucle principale je vais dépiler la liste et exécuter les fonctions en série.
J'ai pas grand espoir, mais si j'arrivais à faire tenir le code d'enregistrement dans la liste en 4 bytes de code, je pourrais directement le stocker dans l'espace RAM alloué aux hooks.
Sinon, il faudra que je passe par une fonction intermédiaire.
On est toujours ignorant avant de savoir.
Sector28
Membre non connecté
Villageois
Pourquoi ne pas faire tourner ton programme entièrement sous interruptions vbl, et transformer ta boucle principale en séquence de sous-routines ?
Le programme principal (en dehors des interruptions) se résumerait à ceci:
Le programme principal (en dehors des interruptions) se résumerait à ceci:
Code :
; initialisation du vecteur irq
...
...
...
boucle: jr boucle
DONALD TRUMP IS FAST APPROACHING
NEMESIS ! RETURN IMMEDIATELY !
aoineko
Membre non connecté
Conseiller Municipal
C'est une question d'habitude. Même sur un moteur comme Unreal Engine 4, toute la partie multi-threadée est encapsulé de tel sorte que coté jeu, tu ne gères que des évènements "en séquence" et que tu n'as donc plus du tout besoin de t'inquiéter des problèmes de concurrence. C'est une philosophie qui me plait et qui peut n'avoir que très peu d'impact sur les perfs si c'est bien fait.
On est toujours ignorant avant de savoir.
aoineko
Membre non connecté
Conseiller Municipal
ericb59 :
Super intéressant !
Faudra que je regarder les spécifications de fonction __critical, __interrupt() et __preserves_regs().
Après, j'ai l'impression qu'il n'y a pas de solution miracle.
Si on utilise les interruptions, faut gérer d'une façon ou d'une autre les cas de concurrence.
On est toujours ignorant avant de savoir.
aoineko
Membre non connecté
Conseiller Municipal
Un article intéressant sur les interruptions : http://map.grauw.nl/articles/interrupts.php
On est toujours ignorant avant de savoir.
Sector28 :
Pourquoi ne pas faire tourner ton programme entièrement sous interruptions vbl, et transformer ta boucle principale en séquence de sous-routines ?
C'est la technique utilisée par Konami dans ses jeux sur MSX.
aoineko :
Ca peut être utile, mais dans mon cas particulier, je pensais plutôt essayer de mettre en place un système de deferred hook. L'idée c'est d'utiliser la boucle principale pour synchroniser les hooks. En gros, au lieu de s'exécuter, les hooks vont s'enregistrer dans une liste, puis dans la boucle principale je vais dépiler la liste et exécuter les fonctions en série.
Très intéressant cette technique ...
Tu peux développer ?
MSX1: Daewoo DPC-200 / Yamaha CX5M
MSX2: Sony HB-F9P
MSXVR
Vidéo: V9990 (GFX-9)
Audio: MSX-Music (FM-PAC) / MSX-Audio (Audiowave) / OPL4 (Monster Sound FM Blaster) / OPNB (Neotron)
Répondre
Vous n'êtes pas autorisé à écrire dans cette catégorie