La Place des Développeurs ROM 48K et slots
aoineko
Membre non connecté
Conseiller Municipal
Ricco, je te réponds ici au cas ou ça intéresse quelqu'un d'autre.
Une cartouche 48K linéaire (sans mapper) est composé de 3 pages (1 page = 16K).
Par rapport aux adresses physique du Z80, elle peut être placée soit :
- de 0000h à BFFFh (page 0 à 2),
- de 4000h à FFFFh (page 1 à 3).
Au démarrage du MSX, le système va checker tous les slots (et sous-slots si un slot est "étendu") pour chercher l'entête "AB" à l'adresse 4000h (début de la page 1) et C000h (début de la page 2).
Si l'entête est trouvé, le système donne la main au code de la ROM.
Voici par exemple, le contenu des slots au démarrage d'un Philips NMS 8250 avec une cartouche 48K branchée dans le port cartouche 1 :
- Ici le slot 3 est "étendu" et donne accès à 4 "sous-slot".
- En gris, les zones d'adresses visibles par le Z80.
- Les cadres en gras représente le slot (ou sous-slot) sélectionné par le système pour chacune des pages au moment où le code de la cartouche démarre. A ce moment là, la page 0 pointe vers la Main-ROM (le BIOS), la page 1 point vers la cartouche (car c'est là où le système à trouvé l'entête) et les pages 2 et 3 points vers la RAM.
- Dans ma librairie, les crt0 s'occupent de changer ce vers quoi point chaque page. Par exemple, dans Final Smash, le crt0 va faire pointer la page 0 et 2 vers le même slot que celui de la page 1. Ainsi, les 48K de ma ROM son directement accessible depuis le Z80 sans avoir besoin de switcher les pages pendant le déroulement du programme.
Le problème qui complexifie la gestion des slots, c'est que le système du MSX s'attends à trouvé certaines infos à des adresses fixes.
Notamment :
- La partie haute de la RAM (vers FFFFh) qui contient une partie de la configuration des slots branchés sur chaque page et surtout, la pile d'appel. Il est donc assez compliqué de modifier le slot vers lequel point la page 3. On la laisse donc généralement avec sa valeur d'origine qui est toujours de la RAM (au moins 8K il me semble).
- Quand les interruptions (messages envoyés au Z80 par les différents périphériques) sont activés, le z80 saute automatique à une adresse basse (vers 0000h) donc si la page 0 ne pointe pas vers une ROM avec un code de gestion des interruptions (ISR), cela risque fort de faire crasher la machine. C'est pour ça qu'on demande de désactiver les interruptions quand on switch la page 0 vers un autre slot.
Mon astuce dans Final Smash c'est d'avoir placé un ISR directement dans ma cartouche en page 0 pour que les interruptions fonctionnent sans avoir à remettre la page 0 sur le slot de la Main-ROM / BIOS (slot 0).
Ce que te proposes Santiontanon, c'est de laisser la page 0 sur le slot de la Main-ROM et d'utiliser les accès inter-slot du BIOS pour accéder à tes données qui se trouve sur le slot de ta cartouche.
Dans le BIOS tu as 3 fonctions pour ça :
- RDSLT @ 0x000C : Reads the value of an address in another slot
- WRSLT @ 0x0014 : Writes a value to an address in another slot
- CALSLT @ 0x001C : Executes inter-slot call
C'est p'être effectivement le plus simple à court termes.
EDIT : J'ai des wrapper en C pour ces fonctions du BIOS : Bios_InterSlotRead(), Bios_InterSlotWrite() et Bios_InterSlotCall() dans https://github.com/aoineko-fr/CMSX/blob/master/cmsx/src/bios.c .
Une cartouche 48K linéaire (sans mapper) est composé de 3 pages (1 page = 16K).
Par rapport aux adresses physique du Z80, elle peut être placée soit :
- de 0000h à BFFFh (page 0 à 2),
- de 4000h à FFFFh (page 1 à 3).
Au démarrage du MSX, le système va checker tous les slots (et sous-slots si un slot est "étendu") pour chercher l'entête "AB" à l'adresse 4000h (début de la page 1) et C000h (début de la page 2).
Si l'entête est trouvé, le système donne la main au code de la ROM.
Voici par exemple, le contenu des slots au démarrage d'un Philips NMS 8250 avec une cartouche 48K branchée dans le port cartouche 1 :
- Ici le slot 3 est "étendu" et donne accès à 4 "sous-slot".
- En gris, les zones d'adresses visibles par le Z80.
- Les cadres en gras représente le slot (ou sous-slot) sélectionné par le système pour chacune des pages au moment où le code de la cartouche démarre. A ce moment là, la page 0 pointe vers la Main-ROM (le BIOS), la page 1 point vers la cartouche (car c'est là où le système à trouvé l'entête) et les pages 2 et 3 points vers la RAM.
- Dans ma librairie, les crt0 s'occupent de changer ce vers quoi point chaque page. Par exemple, dans Final Smash, le crt0 va faire pointer la page 0 et 2 vers le même slot que celui de la page 1. Ainsi, les 48K de ma ROM son directement accessible depuis le Z80 sans avoir besoin de switcher les pages pendant le déroulement du programme.
Le problème qui complexifie la gestion des slots, c'est que le système du MSX s'attends à trouvé certaines infos à des adresses fixes.
Notamment :
- La partie haute de la RAM (vers FFFFh) qui contient une partie de la configuration des slots branchés sur chaque page et surtout, la pile d'appel. Il est donc assez compliqué de modifier le slot vers lequel point la page 3. On la laisse donc généralement avec sa valeur d'origine qui est toujours de la RAM (au moins 8K il me semble).
- Quand les interruptions (messages envoyés au Z80 par les différents périphériques) sont activés, le z80 saute automatique à une adresse basse (vers 0000h) donc si la page 0 ne pointe pas vers une ROM avec un code de gestion des interruptions (ISR), cela risque fort de faire crasher la machine. C'est pour ça qu'on demande de désactiver les interruptions quand on switch la page 0 vers un autre slot.
Mon astuce dans Final Smash c'est d'avoir placé un ISR directement dans ma cartouche en page 0 pour que les interruptions fonctionnent sans avoir à remettre la page 0 sur le slot de la Main-ROM / BIOS (slot 0).
Ce que te proposes Santiontanon, c'est de laisser la page 0 sur le slot de la Main-ROM et d'utiliser les accès inter-slot du BIOS pour accéder à tes données qui se trouve sur le slot de ta cartouche.
Dans le BIOS tu as 3 fonctions pour ça :
- RDSLT @ 0x000C : Reads the value of an address in another slot
- WRSLT @ 0x0014 : Writes a value to an address in another slot
- CALSLT @ 0x001C : Executes inter-slot call
C'est p'être effectivement le plus simple à court termes.
EDIT : J'ai des wrapper en C pour ces fonctions du BIOS : Bios_InterSlotRead(), Bios_InterSlotWrite() et Bios_InterSlotCall() dans https://github.com/aoineko-fr/CMSX/blob/master/cmsx/src/bios.c .
On est toujours ignorant avant de savoir.
Merci pour ces infos Aoineko
Ca m'a l'air quand meme vachement tordu mais je vais essayer de m'y plonger pendant les vacances
Une réorganisation du jeu est à prévoir non ?
Bonne soirée et bonne semaine
Ca m'a l'air quand meme vachement tordu mais je vais essayer de m'y plonger pendant les vacances
Une réorganisation du jeu est à prévoir non ?
Bonne soirée et bonne semaine
aoineko
Membre non connecté
Conseiller Municipal
Ricco59 :
Une réorganisation du jeu est à prévoir non ?
Pas forcement.
Ta ROM 32K à probablement les caractéristiques suivantes :
- Ton header doit se trouver en 0x4000 (page 1). C'est le code .org 0x4000 dans le crt0 juste avant l'entête (le "AB" suivi de l'adresse de début de programme).
- Dans le linker (sdcc.exe) ton adresse de début de code doit être 0x4010 (ou une valeur proche ; page 1). C'est le paramètre --code-loc . Par défaut, le linker va accumuler le code et les données read-only à partir de cette adresse (d'abord dans la page 1, puis dans la page 2 quand le total dépassera 16K).
- Dans le linker (sdcc.exe) ton adresse de RAM doit être 0xC000 (page 3). C'est le paramètre --data-loc .
- Dans le créateur de ROM (hex2bin.exe) ton adresse de début de la ROM doit être 0x4000.
Si c'est bien le cas, tu peux garder tous ces paramètres pour une cartouche 48K et ne changer que l'adresse de début dans le linker (hex2bin.exe) et la mettre à 0x0000.
Et voilà, tu as une ROM de 48K qui va être visible en page 0, 1 et 2.
Si tu gardes le même crt0, la page 0 pointera vers le slot du Bios et les pages 1 et 2 pointeront vers le slot de la cartouche (et la page 3 vers la RAM).
Ta (probable) cartouche 32K et ta cartouche 48K.
Après, il faut "juste" que tu places des données en page 0.
Par défaut quand tu ajoutes des tableaux constants, ils sont ajouté à la fin de ton code, mais tu peux utiliser la directive __at pour les placer ou tu veux.
Voici un exemple :
Code C :
// Ce tableau va être placé à l'adresse 0x0000 (en page 0) __at(0x0000) const unsigned char DataPage0[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; // Ce tableau va être placé après ton code. En fonction de la taille de ton code, il pourrait se trouver en page 1 ou page 2 const unsigned char DataPage12[] = { 0, 1, 2, 3, 4, 5, 6, 7 };
Tu pourrais par ex. placer tes 112 niveaux dans la page 0. Ca te libérerai 8064 octets dans les pages 1 et 2 !
Tu n'aurais besoin de faire des accès inter-slot en lecture qu'à un seul endroit, là ou tu récupères les données du niveau en cours.
Regarde ma fonction Bios_InterSlotRead() .
Ca me semble assez simple à mettre en place... mais bon, c'est toujours plus simple quand on a déjà expérimenté la chose.
On est toujours ignorant avant de savoir.
ericb59
Membre non connecté
Conseiller Municipal
aoineko :
Sur le Wiki ?
Pourquoi pas ... ?
Tu as pondu un grand nombre de posts fort intéressant qui mériteraient un meilleur emplacement pour pas les perdre et les consulter facilement.
Par contre je ne sais plus où ils sont ces posts... Perso je à la ramasse en ce moment... J'ai lancé le mode Debug, mais il est long !
Cette idée me plait énormément Guillaume
il est Top et Fort notre Aoineko
Si en 0, je peux mettre tous mes datas :
- Gfx logo Debut
- Gfx Menu
- Datas niveaux (112*45 = 5040octets)
ca pourrait me libérer pas mal de place pour pouvoir insérer 1 ou 2 ziks et de bô jingles
Par contre, les gfx étant pletterizés, ca ne posera pas de pb pour les lire en ZERO et les décompacter en VRAM ??
Je me souviens aussi avoir lu qu'une histoire de tuto en C pour le MSX avait été abordée il y a.... quelques 10aines de posts de cela
Eh bien je suis partant à 2^10milliards
1000 mercis
il est Top et Fort notre Aoineko
Si en 0, je peux mettre tous mes datas :
- Gfx logo Debut
- Gfx Menu
- Datas niveaux (112*45 = 5040octets)
ca pourrait me libérer pas mal de place pour pouvoir insérer 1 ou 2 ziks et de bô jingles
Par contre, les gfx étant pletterizés, ca ne posera pas de pb pour les lire en ZERO et les décompacter en VRAM ??
Je me souviens aussi avoir lu qu'une histoire de tuto en C pour le MSX avait été abordée il y a.... quelques 10aines de posts de cela
Eh bien je suis partant à 2^10milliards
1000 mercis
aoineko
Membre non connecté
Conseiller Municipal
ericb59 :
Tu as pondu un grand nombre de posts fort intéressant qui mériteraient un meilleur emplacement pour pas les perdre et les consulter facilement.
Par contre je ne sais plus où ils sont ces posts...
Par contre je ne sais plus où ils sont ces posts...
Tu parles de quelles infos en particulier ?
Celles sur les slots ?
ericb59 :
Perso je à la ramasse en ce moment... J'ai lancé le mode Debug, mais il est long !
Bon courage !
Je fais aussi une grosse passe de nettoyage de ma lib et de debug / mise à jour de tous mes programmes d'exemple.
C'est pas super fun, mais c'est important.
Ensuite, je casse tout pour optimiser ma lib pour la nouvelle convention d'appelle de fonction.
Bon, en vrai, j'ai qu'un grosse vingtaine de fonctions en assembleur à adapter.
On est toujours ignorant avant de savoir.
Courage les zamis !
je debug aussi mes projets. Mais parfois il faut faire une pause et passer/penser à autre chose (même s'il faut se faire violence car au Village nous sommes tous des Passionnés)
Tchao
je debug aussi mes projets. Mais parfois il faut faire une pause et passer/penser à autre chose (même s'il faut se faire violence car au Village nous sommes tous des Passionnés)
Tchao
Bonne idée de rassembler les connaissances en articles structurés
1- Met en valeur votre travail
2- Permet à la communauté de l'utiliser encore mieux !
C'est toujours très intéressant de voir les passionnés à l'oeuvre
Rien ne se perd, tout doit se transformer.
Bon je me suis lancé pour le 48ko
- j'ai préparé les données qui doivent être transférées en ZERO (tous les graphs relatifs au menu + les 112 niveaux) : 0x23b9 soit 9145 octets !!!
- j'ai changé ce qui devait l'être dans mon compile.bat
- j'ai transféré les données vers ZERO
Le p'tit hic c'est qu'en lisant la procédure décrite par Aoineko, je n'ai mis qu'un seul __at(0x0000) pensant que les autres tableaux présents dans ce fichier seraient lus de façon sequentielle et que leur adresse se modifierait automatiquement...
Bin nan. Il faut mettre le __at(0xXXXX) devant chaque tableau de données
Pour le moment, lorsque je compile, mon fichier de sortie va de 0x0000 à 0x9bcd (je n'ai pas encore comblé le vide)
Si je vais voir en ZERO, mes données sont bien présentes
en 0x4000, j'ai bien AB et le point d'entrée du prg + quelques octets vierges
en 0x4010, j'ai bien mon crt0
A+ pour un prochain épisode : Bios_InterSlotRead()
Tchao les zamis
Edité par Ricco59 Le 09/12/2021 à 01h57
- j'ai préparé les données qui doivent être transférées en ZERO (tous les graphs relatifs au menu + les 112 niveaux) : 0x23b9 soit 9145 octets !!!
- j'ai changé ce qui devait l'être dans mon compile.bat
- j'ai transféré les données vers ZERO
Le p'tit hic c'est qu'en lisant la procédure décrite par Aoineko, je n'ai mis qu'un seul __at(0x0000) pensant que les autres tableaux présents dans ce fichier seraient lus de façon sequentielle et que leur adresse se modifierait automatiquement...
Bin nan. Il faut mettre le __at(0xXXXX) devant chaque tableau de données
Pour le moment, lorsque je compile, mon fichier de sortie va de 0x0000 à 0x9bcd (je n'ai pas encore comblé le vide)
Si je vais voir en ZERO, mes données sont bien présentes
en 0x4000, j'ai bien AB et le point d'entrée du prg + quelques octets vierges
en 0x4010, j'ai bien mon crt0
A+ pour un prochain épisode : Bios_InterSlotRead()
Tchao les zamis
Edité par Ricco59 Le 09/12/2021 à 01h57
TurboSEB
Membre non connecté
Conseiller Municipal
J'ai bien lu tout le poste et c'est génial d'apprendre des trucs de programmation comme cela même si c'est trop compliqué, enfin je n'ai pas le niveau quoi, mais aoineko est excellent pour synthétisé les expliquations
Donc si j'ai bien tout compris, cette technique permet de "rajouter" 16ko de données potentiels a une cartouche 32ko en remplaçant au boot 16ko des 64ko de données visible par le Z80.
Question : l'ordinateur a besoin d'une "mémoire de travail" RAM , et j'imagine que sur les 48ko de ROM se rajoute 16ko de RAM switchable , ou bien tout se joue en ROM dans ces jeux ? 16ko c'est suffisant ou c'est tout simplement la norme en faite ?
Je pense avoir trouvé la réponse : la RAM est mapper en slot entendu et l'ordinateur utilise 16ko a la fois.
Désolé de baissé le niveau mais bon
Edité par TurboSEB Le 09/12/2021 à 09h33
Donc si j'ai bien tout compris, cette technique permet de "rajouter" 16ko de données potentiels a une cartouche 32ko en remplaçant au boot 16ko des 64ko de données visible par le Z80.
Question : l'ordinateur a besoin d'une "mémoire de travail" RAM , et j'imagine que sur les 48ko de ROM se rajoute 16ko de RAM switchable , ou bien tout se joue en ROM dans ces jeux ? 16ko c'est suffisant ou c'est tout simplement la norme en faite ?
Je pense avoir trouvé la réponse : la RAM est mapper en slot entendu et l'ordinateur utilise 16ko a la fois.
Désolé de baissé le niveau mais bon
Edité par TurboSEB Le 09/12/2021 à 09h33
MSX 1&2 + Moniteurs+divers (environ 0.70Tonnes)
aoineko
Membre non connecté
Conseiller Municipal
Je vois 2 façons viables d'utiliser une cartouche 48K.
Dans tous les cas, les 16K de la page 3 (adresses C000h à FFFFh) pointent toujours vers la RAM. C'est compliqué de switch cette page car c'est là ou se trouve, entre autre, la pile d'appel (indispensable pour appeler une fonction en C ou en assembleur).
Dans tous les cas, les 32K des pages 1 et 2 (adresses 4000h à BFFFh) pointent toujours vers la cartouche. Comme pour une cartouche de 32K.
La différentiation se fait sur l'utilisation de la page 0 (adresses 0000h à 3FFFh), là ou se trouve la Main-ROM (les fonctions principales du BIOS et le gestionnaire d'interruption).
- Dans mon jeu Final Smash par ex., je fais toujours pointer la page 0 vers la cartouche. Ma cartouche est donc toujours visible sur les pages 0, 1 et 2 sans jamais avoir besoin de switcher les pages. C'est bon grand avantage en termes de performance et de simplicité. Par contre, cela ne fonction que parce que ma librairie peut se passer des fonctions du BIOS et parce que je place dans ma cartouche un gestionnaire d'interruption fait maison là ou le MSX s'attend à trouver celui du BIOS.
- L'autre façon de faire c'est de laisser la page 0 pointer vers la Main-ROM. Du coup, le programme n'a pas à fournir son propre code d'interruption et peut continuer à utiliser les fonctions du BIOS. La contrepartie, c'est qu'à chaque fois que le programme à besoin d'accéder aux données de la cartouche en page 0, il a besoin de switcher le slot vers lequel pointe cette page. Il y a des fonctions du BIOS qui font ça automatiquement (function RDSLT) mais très lente car elle switch les pages à chaque octet lu. Sur MRC, ils proposent une ruse sympa mais pas simple à mettre en place en C, c'est d'avoir une fonction de copy de donnée de la cartouche vers la RAM qu'on place dans la cartouche en page 0. En l'appelant via un appel interslot (function CALSLT) cela permet de copy autant d'octet qu'on veut tout en laissant le BIOS se charger du switch de slot.
Si j'étais Ricco, je mettrais juste les 112 niveaux non-compressé dans la page 0 de la cartouche et j'utiliserais la lecture inteslot pour récupérer les 72 octets d'un niveau. Ca sera certainement pas plus lent que de décompresser les données et c'est simple à mettre en place.
Pour l'adresse des tableaux, mes programmes qui génèrent mes .h de donnée ont une option pour calculer automatiquement les adresses des __at.
Dans tous les cas, les 16K de la page 3 (adresses C000h à FFFFh) pointent toujours vers la RAM. C'est compliqué de switch cette page car c'est là ou se trouve, entre autre, la pile d'appel (indispensable pour appeler une fonction en C ou en assembleur).
Dans tous les cas, les 32K des pages 1 et 2 (adresses 4000h à BFFFh) pointent toujours vers la cartouche. Comme pour une cartouche de 32K.
La différentiation se fait sur l'utilisation de la page 0 (adresses 0000h à 3FFFh), là ou se trouve la Main-ROM (les fonctions principales du BIOS et le gestionnaire d'interruption).
- Dans mon jeu Final Smash par ex., je fais toujours pointer la page 0 vers la cartouche. Ma cartouche est donc toujours visible sur les pages 0, 1 et 2 sans jamais avoir besoin de switcher les pages. C'est bon grand avantage en termes de performance et de simplicité. Par contre, cela ne fonction que parce que ma librairie peut se passer des fonctions du BIOS et parce que je place dans ma cartouche un gestionnaire d'interruption fait maison là ou le MSX s'attend à trouver celui du BIOS.
- L'autre façon de faire c'est de laisser la page 0 pointer vers la Main-ROM. Du coup, le programme n'a pas à fournir son propre code d'interruption et peut continuer à utiliser les fonctions du BIOS. La contrepartie, c'est qu'à chaque fois que le programme à besoin d'accéder aux données de la cartouche en page 0, il a besoin de switcher le slot vers lequel pointe cette page. Il y a des fonctions du BIOS qui font ça automatiquement (function RDSLT) mais très lente car elle switch les pages à chaque octet lu. Sur MRC, ils proposent une ruse sympa mais pas simple à mettre en place en C, c'est d'avoir une fonction de copy de donnée de la cartouche vers la RAM qu'on place dans la cartouche en page 0. En l'appelant via un appel interslot (function CALSLT) cela permet de copy autant d'octet qu'on veut tout en laissant le BIOS se charger du switch de slot.
Si j'étais Ricco, je mettrais juste les 112 niveaux non-compressé dans la page 0 de la cartouche et j'utiliserais la lecture inteslot pour récupérer les 72 octets d'un niveau. Ca sera certainement pas plus lent que de décompresser les données et c'est simple à mettre en place.
Pour l'adresse des tableaux, mes programmes qui génèrent mes .h de donnée ont une option pour calculer automatiquement les adresses des __at.
On est toujours ignorant avant de savoir.
Répondre
Vous n'êtes pas autorisé à écrire dans cette catégorie