KITAQ SYSTÈME DE DÉVELOPPEMENT
MANUEL D'UTILISATION 2026.09

KITAQ SERIES • ÉDITION DE RÉFÉRENCE

KITAQGB

Programmation C pour Game Boy

De votre première ligne à l'exécution, à l'observation et aux nouveaux tests.
Copie des sources : 14 septembre 2026 · Édition anglaise de référence : 14 septembre 2026

Compilateur natif Rust et outils auxiliaires

Rust 1.85 ou ultérieur permet de construire le compilateur et tous les outils auxiliaires pour Windows, Linux, macOS ARM et macOS Intel. Les exécutables natifs ne nécessitent pas .NET ; les outils de ressources fonctionnent aussi sans Python ni Pillow.

Les exécutables Windows se trouvent à la racine du dépôt. Ceux de Linux et macOS sont dans les dossiers bin/ indiqués ci-dessous. Conservez lib/ et les mentions de licence avec les outils. Sous Linux/macOS, attribuez les droits d'exécution avec chmod +x, puis ajoutez le dossier au PATH ou utilisez le chemin complet.

OSkitaqgb
Windows x64kitaqgb.exe
Linux x64 (musl)bin/linux-x86_64/kitaqgb
Linux x64 (GNU)bin/linux-gnu-x86_64/kitaqgb
macOS ARM64bin/macos-arm64/kitaqgb
macOS Intelbin/macos-x86_64/kitaqgb
macOS Universalbin/macos-universal/kitaqgb

kitaqgb · Compilateur natif Rust et outils auxiliaires · GitHub Actions

Les constructions natives et vérifications d'exécution ont réussi sous Windows, Linux, macOS ARM et macOS Intel. KITAQGB a passé 48 tests et 395 vérifications d'outils par environnement ; KITAQFC en a passé 55 et 401. Rust 1.85 a également été testé. PUBLIC_DISTRIBUTION.json consigne les empreintes des binaires installés et la provenance des validations. Les workflows GitHub Actions publics reconstruisent et testent ces sources indépendamment.

Les sorties de référence conservées vérifient les octets ROM, les diagnostics et les formats des outils. Les anciennes preuves d'émulation C# restent des résultats historiques liés à leurs empreintes sources. Elles ne prouvent pas automatiquement toutes les API Rust, le matériel réel ou le démarrage complet d'un jeu par le BIOS FDS. Le script PNG d'origine, indisponible, a été réimplémenté d'après sa spécification ; l'identité des octets avec ce script ne peut être affirmée.

--attach est accepté pour compatibilité, mais ne connecte pas de débogueur managé à l’exécutable natif. Utilisez un débogueur natif pour le programme hôte.

KITAQGB · SHA-256 · KITAQFC · SHA-256

Construction portable des exemples

Les scripts PowerShell utilisent l'exécutable Windows à la racine. Sous Linux/macOS, transmettez les mêmes sources C et options au compilateur natif, ou utilisez PowerShell 7. Les graphismes CHR et le source C sont des entrées distinctes ; font.chr conserve la police des exemples.

sh
kitaqgb game.c -I lib -o game.gb --cart=romonly --romsize=32k

1. Découvrir KITAQGB

KITAQGB est un compilateur de la famille C pour GB/CGB, développé à partir du NORCAL de Zachtronics.

NORCAL tire son nom du nord de la Californie (Northern California). S’inspirant de cette référence géographique, l’auteur a choisi le nom KITAQGB d’après Kitakyushu, la ville où il est né et a grandi. KITAQ + GB associe Game Boy à 北九 (キタキュー, Kitakyū), le surnom de Kitakyushu, dans la préfecture de Fukuoka, au Japon. KITAQ se prononce comme le japonais « キタキュー » ; la notation indicative en anglais est kee-tah-KYOO, soit /ˌkiːtɑːˈkjuː/ en alphabet phonétique international. Le Q final se prononce comme le nom anglais de la lettre Q. En anglais, KITAQGB se lit kee-tah-KYOO jee bee, en prononçant G et B séparément.

Le nom KITAQGB a deux sens. Kernel-Informed Toolchain for AI-Quality Game Boy Development exprime l’objectif d’une chaîne de développement qui comprend la machine cible et accompagne aussi bien les personnes qui programment que l’IA générative.

L’autre sens est Kids' Imagination Transformed into Actual Quests in Game Boy Forests : un outil qui transforme l’imagination des enfants en véritables aventures dans les forêts du Game Boy. Il traduit le souhait de faire de petites idées, de croquis et de prototypes créés avec l’aide de l’IA des aventures auxquelles on peut réellement jouer.

Il lit les fichiers C, génère les instructions du processeur et les rassemble dans une ROM. Son langage, ses bibliothèques et ses conventions d'appel diffèrent de ceux d'un compilateur C pour ordinateur de bureau.

La GB possède un processeur 8 bits et peu de mémoire. La plupart des graphismes utilisent des tuiles de 8 × 8 pixels. Les sprites sont de petites images que l'on positionne indépendamment. Le texte nécessite lui aussi des tuiles graphiques : il ne faut pas compter sur un affichage de texte universel intégré. Ces exemples utilisent les glyphes du fichier ascii.c de l'auteur, réordonnés selon les codes ASCII pour la GB sans modifier leurs pixels. L'édition FC convertit les mêmes dessins au format CHR de la NES.

2. Prérequis et compilation du compilateur

Rust 1.85 ou ultérieur permet de construire le compilateur et tous les outils auxiliaires pour Windows, Linux, macOS ARM et macOS Intel. Les exécutables natifs ne nécessitent pas .NET ; les outils de ressources fonctionnent aussi sans Python ni Pillow.

sh
cd kitaqgb
cargo test --locked --tests
cargo build --locked --release

Les exécutables Windows se trouvent à la racine du dépôt. Ceux de Linux et macOS sont dans les dossiers bin/ indiqués ci-dessous. Conservez lib/ et les mentions de licence avec les outils. Sous Linux/macOS, attribuez les droits d'exécution avec chmod +x, puis ajoutez le dossier au PATH ou utilisez le chemin complet.

OSkitaqgb
Windows x64kitaqgb.exe
Linux x64 (musl)bin/linux-x86_64/kitaqgb
Linux x64 (GNU)bin/linux-gnu-x86_64/kitaqgb
macOS ARM64bin/macos-arm64/kitaqgb
macOS Intelbin/macos-x86_64/kitaqgb
macOS Universalbin/macos-universal/kitaqgb
powershell
.\scripts\build.ps1
.\kitaqgb.exe --help
sh
sh scripts/build.sh

Les scripts PowerShell utilisent l'exécutable Windows à la racine. Sous Linux/macOS, transmettez les mêmes sources C et options au compilateur natif, ou utilisez PowerShell 7. Les graphismes CHR et le source C sont des entrées distinctes ; font.chr conserve la police des exemples.

3. Votre premier programme

Le fichier fourni samples/gb_hello.c utilise les fonctions d'affichage et de texte de gb_common.h. Conservez cet en-tête avec les fichiers de police. La directive #include lit les déclarations ou les définitions contenues dans un autre fichier.

c
#include "gb_common.h"
void main() {
    m_init();
    m_text(2, 5, "HELLO WORLD");
    m_number(42);
    while (1) { m_wait(); }
}
powershell
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\gb_hello.c -I .\kitaq-docs\samples -o .\out\hello.gb --profile=dev --rst-disable --stack-bank=fixed
.\kokura\kokura-cli.exe .\out\hello.gb --run-frames 120 --png .\out\hello.png --dump-report .\out\hello.json

L'exemple doit afficher HELLO WORLD et 042. Les noms commençant par m_ sont des fonctions pédagogiques définies dans l'en-tête commun des exemples, pas des commandes standard de KITAQGB. Les fonctions intrinsèques du compilateur comprennent notamment __wait_vblank et __vram_copy.

4. Syntaxe de base et types

Terminez les instructions par ; et regroupez-les entre { }. // ouvre un commentaire de ligne ; /* ... */ délimite un commentaire de bloc. Les noms distinguent les majuscules des minuscules. Utilisez void main() comme point d'entrée. Dans cette version GB, void main(void) provoque une erreur de syntaxe : ne recopiez donc pas telle quelle la déclaration utilisée sur FC.

TypeSignification et plage
u8Entier non signé sur 8 bits, de 0 à 255
s8Entier signé sur 8 bits, de -128 à 127
u16Entier non signé sur 16 bits, de 0 à 65535
s16Entier signé sur 16 bits, de -32768 à 32767
voidAucune valeur de retour
T*Pointeur vers des données de type T

Commencez par ces noms de types courts. Ne supposez pas que int, long, float, double ou les en-têtes standard se comportent comme sur un ordinateur de bureau. Cette version traite char comme une donnée non signée sur 8 bits ; utilisez explicitement s8 ou s16 pour les calculs signés.

c
u8 lives = 3;
u16 score;
score = (u16)200 + 100;

(u16) est une conversion de type explicite. Affecter à une grande variable une petite valeur qui a déjà débordé ne récupère pas les bits perdus : élargissez les opérandes avant le calcul. Pour les déplacements fractionnaires, utilisez la bibliothèque de calcul en virgule fixe.

5. Expressions et opérateurs

GroupeOpérateursExemple ou signification
Arithmétique+ - * / %n / 10 donne le quotient entier ; n % 10, le reste
Comparaison== != < <= > >=lives == 0 teste l'égalité
Logique! && / ||Négation, deux conditions vraies, au moins une condition vraie
Bits& / | / ^ ~ << >>Masques de boutons et autres ensembles de bits
Affectation= += -= et formes apparentéesx += 1 modifie une valeur
Incrémentation++ --i++ ajoute un
Sélectioncondition ? A : BChoisit une valeur selon une condition
Pointeurs&variable / *pObtient une adresse ou accède à la donnée pointée

Distinguez = de ==. Utilisez des parenthèses pour rendre les expressions complexes lisibles et évitez d'accumuler appels et effets de bord dans une seule instruction. Évitez les divisions par zéro et les accès hors des limites des tableaux. sizeof donne une taille en octets ; offsetof, le décalage d'un membre dans une structure.

Résultats des expressions et évaluation

Les opérateurs de comparaison ==, !=, <, <=, >, >= et les opérateurs logiques !, &&, || renvoient 0 pour faux et 1 pour vrai. Vous pouvez stocker le résultat dans un u16, le passer en argument, le renvoyer ou l’utiliser dans un calcul. Par exemple, score = 500 + (lives != 0); donne 501 s’il reste une vie, et 500 sinon.

&& n’évalue pas son opérande droit lorsque celui de gauche vaut zéro. || n’évalue pas son opérande droit lorsque celui de gauche est non nul. Dans pointer != 0 && pointer->active != 0, un pointeur nul empêche l’accès au membre. Les deux octets d’une valeur de 16 bits participent au test logique : 256 est donc vrai. Les opérateurs bit à bit & et | ne court-circuitent pas l’évaluation.

++value renvoie la valeur mise à jour ; value++ renvoie la valeur initiale. Ces opérateurs acceptent aussi les éléments de tableau, les pointeurs déréférencés et les membres de structure. buffer[index()]++ appelle index() une seule fois. Avec u16 *p, p++ avance de deux octets vers l’élément suivant, tandis que (*p)++ incrémente la valeur pointée.

sizeof(array) donne la taille en octets de tout le tableau ; sizeof(pointer) vaut 2. Pour u16 values[9];, sizeof(values) vaut 18. Cela s’applique aux tableaux en ROM, aux tableaux locaux et aux membres de type tableau. sizeof(function()) examine le type de retour sans appeler la fonction.

La déclaration et la définition d’une fonction doivent avoir les mêmes types de paramètres, dans le même ordre. Les noms peuvent différer ; le corps utilise ceux de la définition. Par exemple, u8 next(u8 input); peut être défini par u8 next(u8 value) { return (u8)(value + 1); }. Les paramètres et variables locales masquent les variables globales de même nom.

La sélection de tableaux ou de chaînes avec ?: produit un pointeur vers le type d’élément sélectionné. Vous pouvez le passer directement, par exemple dans show(ready ? "READY" : "WAIT");. Pour des tableaux u16 nommés a et b, (ready ? a : b) + 1 avance de deux octets vers le deuxième élément du tableau choisi. Le tableau n’est pas copié.

condition ? yes : no évalue la condition, puis uniquement la branche choisie. La condition et les branches peuvent contenir des décalages dont le nombre de bits est variable. Par exemple, on = (pattern & (0x80 >> bit)) != 0 ? 4 : 2; choisit quatre ou deux selon le bit désigné. Les appels de fonction présents dans un opérande droit ignoré par && ou || sont eux aussi ignorés. Un continue dans une boucle for exécute une fois l’expression de mise à jour avant de réévaluer la condition ; dans while et do ... while, il passe à la condition.

6. Branchements et boucles

c
if (lives == 0) { game_over = 1; }
else { score = score + 10; }
for (i = 0; i < 4; i++) { table[i] = i; }
while (running != 0) { update_game(); }
do { count++; } while (count < 3);

break quitte une boucle ou un switch, continue passe à l'itération suivante et return quitte une fonction. Employez l'instruction explicite fallthrough; pour poursuivre volontairement l'exécution d'un cas de switch dans le suivant ; une continuation implicite fait l'objet d'un diagnostic. Consultez l'exemple complet gb_control.c.

7. Fonctions, tableaux et structures

c
u8 add(u8 a, u8 b) { return (u8)(a + b); }
typedef struct { u8 x; u8 y; } Point;
Point player;
u8 tiles[4];

Les indices des tableaux commencent à zéro : un tableau de quatre éléments utilise les indices 0 à 3. player.x sélectionne un membre ; pointer->x y accède par un pointeur. Le compilateur analyse les structures, les unions et les énumérations, mais leur disposition dépend des types et des attributs __packed / __aligned. Vérifiez sizeof avant de partager des données avec le matériel ou un format binaire.

L’ABI Legacy par défaut place les arguments et le stockage local à des adresses fixes. Ne supposez pas une récursion ou une réentrance depuis les interruptions comparable à celle d’un ordinateur de bureau. __stackcall et --abi=stack sont des choix avancés de convention d’appel. Si vous combinez plusieurs conventions, examinez les rapports ABI et vérifiez l’exécution. Les rappels par pointeur de fonction ne sont pas pris en charge avec --abi=stack. Compilez les exemples complets de rappels système et de scène avec l’ABI Legacy par défaut, comme indiqué dans leurs commandes de compilation.

8. Plusieurs fichiers et préprocesseur

Placez les types, constantes et déclarations dans les en-têtes, et le corps des fonctions dans les fichiers .c. Utilisez #pragma once ou des gardes d'inclusion pour éviter les inclusions répétées. La compilation conditionnelle prend en charge #define, #undef, #if, #ifdef, #ifndef, #elif, #else et #endif.

powershell
.\kitaqgb\kitaqgb.exe main.c game.c .\kitaqgb\lib\input.c -I .\kitaqgb\lib -o .\out\game.gb --profile=dev

L'option -I ajoute un dossier de recherche d'en-têtes. Inclure l'en-tête d'une bibliothèque n'ajoute pas son implémentation : indiquez les fichiers .c nécessaires dans la commande de compilation. Choisissez les unités utiles ; ajouter sans distinction tous les sources peut dupliquer les définitions de registres ou les gestionnaires d'interruption.

9. ROM, mémoire et banques

La ROM contient le code et les constantes, la WRAM les variables, la VRAM les graphismes et l'OAM les descriptions des sprites. La commutation de banques change la mémoire physique visible à une adresse du processeur. Un pointeur sur 16 bits ne suffit pas à identifier des données situées dans une autre banque.

c
#pragma bank 1
__prg_rom u8 level_data[] = { 1, 2, 3, 4 };

__prg_rom place les données en ROM. Des attributs tels que __location(0xFF40) imposent une adresse, tandis que __wram / __hram sélectionnent une région mémoire. Avec #pragma bank ou #pragma fixed_bank, examinez le fichier de placement et assurez-vous que le code et les données utilisés par les interruptions restent accessibles.

powershell
.\kitaqgb\kitaqgb.exe main.c -o .\out\game.gbc --cart=mbc5 --romsize=256k --ramsize=32k --cgb=cgb --rom-title=MYGAME --stack-bank=fixed --emit-ai-metadata=.\out\build.json

--cgb=dmg déclare un logiciel destiné à la DMG, --cgb=cgb un logiciel compatible avec les deux modes et --cgb=cgb_only un logiciel réservé à la CGB. Un jeu à deux modes doit détecter le matériel avant d'utiliser des fonctions propres à la CGB. Un indicateur dans l'en-tête ne crée pas à lui seul ce comportement dans le jeu.

10. Mettre à jour les graphismes au bon moment

VBlank est l'intervalle entre deux images affichées. Écrire dans la VRAM ou l'OAM sans tenir compte du moment peut altérer l'affichage ou faire perdre des mises à jour. Chargez les ressources initiales lorsque l'écran est désactivé ; pour les mises à jour régulières, utilisez les fonctions intrinsèques adaptées ou la file VRAM. Les fonctions marquées _unsafe ou _fast exigent que l'appelant garantisse une période de transfert sûre.

Alignez le tampon de DMA OAM sur une limite de 256 octets. Sur GB, __oam_dma reçoit l'adresse source. La fonction intrinsèque FC du même nom ne reçoit aucun argument : ne confondez pas ces API.

11. Commandes de compilation et fichiers produits

-o choisit la sortie, -O0 / -O1 le niveau d'optimisation et --profile=dev|release|test un ensemble de réglages. --no-disasm désactive la sortie du désassemblage. --debug-out=... et --trace-out=... fixent les emplacements des données d'investigation. Adaptez la quantité de sorties à une compilation rapide ou à une recherche détaillée.

powershell
.\kitaqgb\kitaqgb.exe main.c -I .\kitaqgb\lib -o .\out\game.gb --profile=dev --diag-json=.\out\compile.json --emit-ai-metadata=.\out\build.json --debug-out=.\out\debug

Le fichier .map consigne les noms et leur placement ; .funcsizes.txt, la taille des fonctions ; .dbg2.json / .source_map.txt relient les positions d'exécution au source ; .build_report.json résume la compilation. Produire explicitement le JSON --emit-ai-metadata facilite le passage des données à SARAKURA.

12. Lire les erreurs en commençant par la première

Repérez le nom de fichier, la ligne et le numéro de diagnostic KQ de la première erreur. Les suivantes peuvent découler d'une seule faute de syntaxe. Pour un symbole non défini, vérifiez sa déclaration, son implémentation et sa présence dans la commande de compilation. En cas de débordement de ROM, examinez la taille des ressources et des fonctions, ainsi que leur répartition entre banques.

powershell
.\kitaqgb\kitaqgb.exe kqhelp KQ1002
.\kitaqgb\kitaqgb.exe symfind main --map=.\out\game.map
.\kitaqgb\kitaqgb.exe romdiff .\out\before.gb .\out\after.gb

13. Assembleur intégré

__asm { ... } accepte les noms d'instructions de KITAQGB. Cela ne signifie pas qu'un source quelconque écrit pour un autre assembleur GB sera accepté. Les noms internes comprennent par exemple LD_A_IMM. Avant d'utiliser l'assembleur intégré, maîtrisez les arguments, les valeurs de retour, les registres préservés et le fonctionnement de la pile. L'annexe répertorie les noms d'instructions et les formes d'opérandes.

c
void nop_example() {
    __asm { NOP }
}

14. Exemple pratique : développer un jeu avec l’IA

Renseignez les besoins, puis transmettez le prompt complet à votre assistant IA. Il couvre l’implémentation, les tests dans l’émulateur, l’analyse avec SARAKURA et la vérification des corrections.

· Télécharger le Markdown

Renseignez les besoins, puis transmettez ce document entier à l’assistant IA. Les commandes supposent que les dépôts kitaqgb, kitaqfc, kokura, kurosaki, sarakura et kitaq-docs, ainsi que le projet game-gb ou game-fc, se trouvent dans le même dossier parent. Exécutez-les depuis ce dossier et adaptez les chemins à votre environnement.

Besoins

  • Titre du jeu : <à renseigner>
  • Genre et mécanique principale : <à renseigner>
  • Commandes et conditions de réussite ou d’échec : <à renseigner>
  • Écrans, niveaux, ennemis et objets indispensables : <à renseigner>
  • Style graphique, musique et effets sonores : <à renseigner, avec les chemins des ressources fournies>
  • Sauvegarde, communication, périphériques et autres besoins : <à renseigner, ou aucun>
  • Dossier du projet : <à renseigner>
  • Conditions de redistribution : <par exemple, code et ressources originaux pouvant être publiés sous licence MIT>
  • Machine cible : <Game Boy d’origine / compatibilité GB et CGB / CGB uniquement>
  • Objectif de performances : <par exemple, 60 mises à jour de la logique par seconde en jeu normal ; préciser ce qui est acceptable dans les scènes exigeantes>

Travail demandé

Implémentez le jeu avec KITAQGB et ses bibliothèques. Utilisez KOKURA pour l’exécution et le débogage, et SARAKURA pour organiser les diagnostics et comparer les résultats avant et après correction.

Répétez ce cycle jusqu’à satisfaire les critères d’acceptation : préciser la spécification → implémenter une petite modification → compiler → appliquer des entrées et observer → rechercher la cause → corriger → refaire les tests dans les mêmes conditions. Un plan, du code fourni ou une compilation réussie ne suffisent pas à terminer le travail.

Vérifier l’environnement et les critères d’acceptation

  1. Lisez les consignes du dossier de travail, les README, les manuels HTML ainsi que les en-têtes et implémentations des bibliothèques utilisées. Relevez les chemins des exécutables et leurs versions ou empreintes SHA-256. Vérifiez les commandes dans la sortie réelle de --help et les API dans le code source.
  2. Définissez des critères mesurables pour les entrées, l’image, le son, la progression et la fréquence de mise à jour. Par exemple : appuyer puis relâcher START lance la partie ; une collision retire une vie ; la pause coupe les sons prévus et la reprise rétablit la lecture.
  3. Ne posez de questions que sur les ambiguïtés importantes. Prenez de façon autonome les décisions courantes et réversibles. Ne réduisez pas les exigences ni les critères d’acceptation.
  4. Commencez par faire passer un petit exemple fourni dans le compilateur, l’émulateur et SARAKURA. Cela vérifie leur articulation, pas l’achèvement du jeu demandé.

Réaliser une première version jouable

  • Utilisez le dialecte C de KITAQGB et void main(). Ne supposez pas que les API du C sur ordinateur ou de GBDK sont disponibles. Incluez les implémentations .c nécessaires, pas seulement leurs déclarations ; vérifiez initialisation, unités, signe, plages de valeurs, durée de vie des tampons et banques ROM.
  • Prévoyez les mises à jour VRAM/OAM, VBlank, interruptions, pile, banques ROM/WRAM et limites de tuiles et de sprites. La capacité totale et libre de la file de transferts est distincte de la capacité et de l’espace libre de la VRAM physique.
  • Un jeu DMG ne doit pas dépendre de fonctions réservées au CGB. Si les deux modes sont pris en charge, testez chacun d’eux.
  • Utilisez la police originale fournie dans ascii.c pour les lettres, chiffres et symboles, et vérifiez la correspondance entre caractères et tuiles.
  • Reliez d’abord démarrage, titre, personnage contrôlable, réussite ou échec, et nouvelle partie. Enrichissez ensuite le contenu.
  • Conservez les sources modifiables des graphismes, musiques et effets ainsi que les étapes de génération. Vérifiez que la compilation utilise réellement les données exportées.
  • Rédigez les commentaires du code en anglais et les comptes rendus d’avancement en français. Gardez les rapports standard de SARAKURA en anglais.

Relier chaque compilation à son exécution

Séparez les sorties par itération, par exemple dans out/iter-001. Consignez commandes, codes de sortie et empreintes du code, des ressources, outils, ROM et métadonnées. N’exécutez jamais une ancienne ROM après une compilation échouée. Les cartes mémoire, correspondances avec les sources et informations de débogage doivent provenir de la même compilation que la ROM.

Voici une vérification de base pour DMG. Préparez main.c et les implémentations de bibliothèque nécessaires ; adaptez les options et la séquence d’entrées au jeu.

powershell
$iteration = '.\game-gb\out\iter-001'
New-Item -ItemType Directory -Force $iteration | Out-Null

# Include all additional implementation units required by the game.
& '.\kitaqgb\kitaqgb.exe' '.\game-gb\src\main.c' `
  -I '.\kitaqgb\lib' -o "$iteration\game.gb" `
  --profile=dev --rst-disable --stack-bank=fixed --no-disasm `
  "--emit-ai-metadata=$iteration\build.json"
if ($LASTEXITCODE -ne 0) { throw 'Build failed; inspect the build log.' }

# This sequence presses START once, with released intervals on both sides.
& '.\kokura\kokura-cli.exe' "$iteration\game.gb" `
  --hardware dmg --run-frames 300 `
  --input-seq 'NONE:60;START:1;NONE:239' `
  --png "$iteration\frame.png" --record-wav "$iteration\audio.wav" `
  --dump-report "$iteration\run.json" `
  --emit-diagnostics "$iteration\events.jsonl"
if ($LASTEXITCODE -ne 0) { throw 'Emulator run failed; inspect the run log.' }

& '.\sarakura\sarakura.exe' gb analyze `
  --metadata "$iteration\build.json" --events "$iteration\events.jsonl" `
  --frames 300 --out "$iteration\analysis" --fail-on error
if ($LASTEXITCODE -ne 0) { throw 'Inspect the analysis report and fix the cause.' }

--hardware dmg sélectionne la Game Boy d’origine. Pour tester CGB ou les deux modes, faites correspondre l’en-tête ROM et le matériel choisi dans l’émulateur. La séquence appuie une fois sur START entre deux périodes où les boutons sont relâchés. Une exécution de 300 images ne teste pas l’ensemble du jeu.

Vérifier l’image, le son, l’état et les performances

  • Conservez les scénarios d’entrée en distinguant pression, maintien et relâchement. Parcourez toutes les voies prévues : démarrage, début de partie, déplacement, actions, collisions, défilement, changement de niveau, fin de partie, redémarrage, pause et, si nécessaire, sauvegarde ou communication.
  • Gardez les PNG des images pertinentes, les entrées, rapports d’exécution, diagnostics JSONL, WAV et observations nécessaires d’état ou de mémoire. Vérifiez le nombre d’images atteint et la raison de l’arrêt. Ouvrez réellement les images : une seule capture ne prouve ni mouvement ni réponse aux commandes. Comparez compteurs, positions et transitions aux valeurs attendues ; examinez bords d’écran, limites de tuiles et d’attributs, et scènes chargées en sprites.
  • Vérifiez musique, effets, lecture simultanée, coupures, pause et reprise. Produire un WAV ne prouve pas la justesse du son. Si l’écoute est impossible, distinguez les contrôles numériques ou de forme d’onde réalisés des qualités sonores non vérifiées.
  • Mesurez les scènes exigeantes, le travail du CPU cible, les mises à jour et les transferts ; sur FC, incluez NMI. Le débit de l’émulateur sur l’ordinateur hôte n’est ni la fréquence de mise à jour du jeu ni une preuve de vitesse sur matériel réel. Poursuivre avec --allow-unimplemented ne prouve pas la prise en charge de la fonction manquante.

Analyser, corriger et refaire les tests

  • Fournissez à SARAKURA les métadonnées de la ROM testée et le JSONL diagnostic de cette exécution. Une trace CPU ou un rapport d’exécution ordinaire ne les remplace pas. --frames précise les conditions d’analyse ; SARAKURA n’exécute pas la ROM et ne modifie pas automatiquement les sources.
  • Lisez report.html, ai_diagnostics.json, repair_prompt.md et retest_plan.json. Confrontez les diagnostics aux étapes de reproduction, images, sons et sources. Distinguez les emplacements ou causes supposés des faits vérifiés, et les boucles d’attente normales des blocages. Examinez chaque avertissement et notez les événements non pris en charge ou les limites d’analyse. Ne masquez pas les avertissements avec des filtres et ne raccourcissez pas les tests pour obtenir un succès.
  • Réduisez les défauts à des cas minimaux, corrigez leur cause et recompilez. Si le compilateur ou l’émulateur est en cause, isolez son défaut du code du jeu et ajoutez une vérification de non-régression à la correction de l’outil.
  • Refaites les tests avec les mêmes entrées, graine aléatoire, machine et standard vidéo, mapper, images observées et réglages diagnostics. Utilisez les métadonnées propres à chaque ROM ; ne réutilisez pas aveuglément les états sauvegardés après modification du code ou de la disposition RAM.
powershell
& '.\sarakura\sarakura.exe' baseline-delta `
  --baseline '.\game-gb\out\iter-001\analysis' `
  --current '.\game-gb\out\iter-002\analysis' `
  --out '.\game-gb\out\delta.json' --markdown '.\game-gb\out\delta.md' `
  --fail-on-new error --fail-on-regression error --enforce

Associez les différences de diagnostic aux critères d’acceptation des commandes, graphismes et sons. Si le même échec se répète, réexaminez les preuves et l’hypothèse au lieu d’enchaîner des modifications arbitraires.

Conditions de fin et livrables

Rejouez tous les scénarios obligatoires avec la ROM finale compilée depuis les sources et réglages livrés. L’invincibilité, des entrées automatiques de test ou un autre mapper ne suffisent pas à valider une partie normale dans la version finale. Fournissez un tableau reliant besoins et tests, expliquez les avertissements restants et indiquez les éléments non vérifiés ou non pris en charge. Précisez explicitement l’absence de tests sur matériel physique, le cas échéant.

Livrez les sources, l’identification des outils et bibliothèques, les ressources modifiables, les scripts reproductibles de compilation et de test, la ROM, les preuves finales et un README expliquant installation, commandes et limites connues. Incluez les replays et le programme de test si nécessaire. Publiez ou transmettez des fichiers à l’extérieur uniquement dans le périmètre expressément autorisé. Supprimez les compilations intermédiaires et traces temporaires inutiles après vérification, mais conservez sources, ressources, livrables et preuves de non-régression nécessaires.

Si l’environnement ou les permissions empêchent un contrôle obligatoire, indiquez les étapes exactes de reproduction et l’action nécessaire. Ne déclarez pas le travail terminé.

Emplacement des sources

Les sources du compilateur et le fichier projet se trouvent dans le sous-dossier portant le nom du dépôt. L’exécutable est à la racine et les bibliothèques dans lib/. Consultez la structure des dossiers pour les chemins et les prérequis de compilation.

Exemples de programmes complets

Chaque programme, les en-têtes communs, le fichier ascii.c fourni et ses ressources converties sont inclus. Les noms commençant par m_ sont des fonctions écrites pour ces manuels. Lisez chaque commande de compilation avec son résultat attendu et son compte rendu de vérification. Lancez les commandes depuis le dossier parent des dépôts voisins.

Script de compilation par lot / Résultats et captures d'écran

Afficher le jeu de caractères fourni gb_font.c

Objectif : Présente les lettres, chiffres et signes de ponctuation de la police originale fournie, ainsi que leur affichage.

Fonctionnement : m_init prépare l’affichage et la police. m_text dessine chaque ligne ; m_put dessine le guillemet double avec le code de caractère 34.

Commandes et résultat attendu : Repérez les 26 lettres majuscules, les 26 minuscules, les 10 chiffres et les 30 signes de ponctuation. Vérifiez que chaque glyphe est lisible. Aucune saisie n’est nécessaire.

Afficher le jeu de caractères fourni : Repérez les 26 lettres majuscules, les 26 minuscules, les 10 chiffres et les 30 signes de ponctuation. Vérifiez que chaque glyphe est lisible. Aucune saisie n’est nécessaire.
Exemple d’affichage : Repérez les 26 lettres majuscules, les 26 minuscules, les 10 chiffres et les 30 signes de ponctuation. Vérifiez que chaque glyphe est lisible. Aucune saisie n’est nécessaire.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe  kitaq-docs/samples/gb_font.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_font.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Display the letters, digits and punctuation from the supplied ascii.c font.
// Expected: Uppercase, lowercase, numbers and all 30 supplied punctuation glyphs
#include "gb_common.h"


// Lay out every supplied character category, yielding between rows. The final
// m_put uses character code 34 to display the double quote separately.
void main() {
    m_init();
    m_text(2,3,"FONT");
    m_text(2,5,"ABCDEFGHIJKLMNOP"); m_wait();
    m_text(2,6,"QRSTUVWXYZ"); m_wait();
    m_text(2,8,"0123456789"); m_wait();
    m_text(2,10,"abcdefghijklmnop"); m_wait();
    m_text(2,11,"qrstuvwxyz"); m_wait();
    m_text(2,13,"!#$%&'()*+,-./"); m_wait();
    m_text(2,14,":;<=>?@[]^_`{}~"); m_wait();
    m_put(16,13,34); m_wait();
    while (1) { m_wait();  }
}
Votre premier affichage de texte gb_hello.c

Objectif : Présente les étapes de base pour démarrer une ROM et dessiner du texte et un nombre sur le fond.

Fonctionnement : Après m_init, m_text dessine le message d’accueil et m_number formate 42. La boucle finale attend les images en conservant l’affichage.

Commandes et résultat attendu : L’écran affiche HELLO WORLD et 042. Le zéro initial provient du format décimal sur trois chiffres.

Votre premier affichage de texte : L’écran affiche HELLO WORLD et 042. Le zéro initial provient du format décimal sur trois chiffres.
Exemple d’affichage : L’écran affiche HELLO WORLD et 042. Le zéro initial provient du format décimal sur trois chiffres.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe  kitaq-docs/samples/gb_hello.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_hello.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Display a first message on screen.
// Expected: HELLO WORLD and 042
#include "gb_common.h"


// Initialize the display/font, write HELLO WORLD and a three-digit 042,
// then wait for frames so the result remains visible.
void main() {
    m_init();
    m_text(2,3,"HELLO");
    m_text(2,5,"HELLO WORLD");
    m_number(42);
    while (1) { m_wait();  }
}
Calculer avec un intermédiaire plus large gb_arithmetic.c

Objectif : Montre comment conserver un résultat intermédiaire qui dépasse huit bits.

Fonctionnement : (u16)200 + 100 donne 300. Le programme divise ce résultat par 10, puis le convertit en u8 pour l’afficher. Notez que la conversion vers le type plus large précède l’addition.

Commandes et résultat attendu : 030 représente le résultat calculé, 30.

Calculer avec un intermédiaire plus large : 030 représente le résultat calculé, 30.
Exemple d’affichage : 030 représente le résultat calculé, 30.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe  kitaq-docs/samples/gb_arithmetic.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_arithmetic.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Variables, arithmetic and type conversion.
// Expected: 030
#include "gb_common.h"


// Widen before adding 200 + 100, divide the 16-bit sum, and display 30.
// The cast demonstrates avoiding an 8-bit intermediate result.
void main() {
    m_init();
    m_text(2,3,"ARITHMETIC");
    u16 sum;
    sum = (u16)200 + 100;
    m_number((u8)(sum / 10));
    while (1) { m_wait();  }
}
Branchements et boucles gb_control.c

Objectif : Essaie for, continue, while, do/while et switch dans un petit calcul.

Fonctionnement : Additionner 0 à 7 en sautant 3 donne 25. La boucle while avance jusqu’à 30, do/while jusqu’à 32, puis switch choisit case 32 pour affecter 42.

Commandes et résultat attendu : 042 est le résultat final de ces boucles et branchements.

Branches et boucles : 042 est le résultat final de ces boucles et branches.
Exemple d’affichage : 042 est le résultat final de ces boucles et branchements.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe  kitaq-docs/samples/gb_control.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_control.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Conditional branches and loops.
// Expected: 042
#include "gb_common.h"


// Exercise loop control and a final branch, then display the selected value 42.
void main() {
    m_init();
    m_text(2,3,"CONTROL");
    u8 i; u8 sum; sum=0;
    for (i=0;i<8;i++) { if (i==3) continue; sum=(u8)(sum+i); }
    while (sum<30) { sum++; }
    do { sum++; } while (sum<32);
    switch (sum) { case 32: sum=42; break; default: sum=0; break; }
    m_number(sum);
    while (1) { m_wait();  }
}
Fonctions, tableaux, structures et pointeurs gb_aggregate.c

Objectif : Montre comment regrouper des valeurs et y accéder par des fonctions et des pointeurs.

Fonctionnement : Le programme copie un Point contenant (10,20), puis écrit de 1 à 4 dans un tableau via un pointeur. Il additionne le résultat de fonction 30, les valeurs du tableau 1, 2 et 4 et la constante 5.

Commandes et résultat attendu : La somme est affichée sous la forme 042. Suivez la copie de structure, les écritures dans ptr[i] et la valeur renvoyée par la fonction.

Fonctions, tableaux, structures et pointeurs : La somme est affichée sous la forme 042. Suivez la copie de structure, les écritures dans ptr[i] et la valeur renvoyée par la fonction.
Exemple d’affichage : La somme est affichée sous la forme 042. Suivez la copie de structure, les écritures dans ptr[i] et la valeur renvoyée par la fonction.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe  kitaq-docs/samples/gb_aggregate.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_aggregate.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Functions, arrays, structures and pointers.
// Expected: 042
#include "gb_common.h"

typedef struct { u8 x; u8 y; } Point;
// Add two byte values and explicitly narrow the returned result to u8.
u8 add(u8 a,u8 b) { return (u8)(a+b); }
// Copy a structure, fill an array through a pointer, and combine selected values
// through add() to produce 42. Keep the final screen visible in the idle loop.
void main() {
    m_init();
    m_text(2,3,"AGGREGATE");
    Point a; Point b; u8 i; u8 data[4]; u8* ptr;
    a.x=10; a.y=20; b=a; ptr=data;
    for(i=0;i<4;i++) ptr[i]=(u8)(i+1);
    m_number((u8)(add(b.x,b.y)+ptr[0]+ptr[1]+ptr[3]+5));
    while (1) { m_wait();  }
}
Drapeaux binaires et copie de tampon gb_bits_memory.c

Objectif : Regroupe des drapeaux dans un octet et copie un tampon dans une zone distincte.

Fonctionnement : __memset efface quatre octets. Le programme active le bit 3, inverse le bit 1, puis copie les quatre octets avec __memcpy.

Commandes et résultat attendu : le premier octet de destination est affiché sous la forme 010 ; le bit 3 apporte 8 et le bit 1 apporte 2.

Drapeaux binaires et copie de tampon : le premier octet de destination est affiché sous la forme 010 ; le bit 3 apporte 8 et le bit 1 apporte 2.
Exemple d’affichage : le premier octet de destination est affiché sous la forme 010 ; le bit 3 apporte 8 et le bit 1 apporte 2.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe  kitaq-docs/samples/gb_bits_memory.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_bits_memory.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Bit operations and memory copying.
// Expected: 010
#include "gb_common.h"
void __memset(void* dst,u8 value,u16 len);
void __memcpy(void* dst,const void* src,u16 len);
void __bit_set(u8* base,u16 bit);
void __bit_toggle(u8* base,u16 bit);

// Clear the source bytes, set bits 3 and 1, copy the buffer, and display 8 + 2 = 10.
void main() {
    m_init();
    m_text(2,3,"BITS MEMORY");
    u8 data[4]; u8 copy[4];
    __memset(data,0,4); __bit_set(data,3); __bit_toggle(data,1);
    __memcpy(copy,data,4); m_number(copy[0]);
    while (1) { m_wait();  }
}
Compter les nouveaux appuis sur un bouton gb_input.c

Objectif : Distingue un nouvel appui sur un bouton d’un bouton maintenu enfoncé.

Fonctionnement : Chaque image appelle input_update. Le compteur n’avance que si input_pressed(BTN_A) est vrai.

Commandes et résultat attendu : le compteur commence à 000. Appuyez sur A pour obtenir 001 ; maintenir le bouton n’ajoute rien. Relâchez-le puis appuyez de nouveau pour obtenir 002. Le compteur sur un octet revient de 255 à 000.

Compter les nouveaux appuis sur un bouton : le compteur commence à 000. Appuyez sur A pour obtenir 001 ; maintenir le bouton n’ajoute rien. Relâchez-le puis appuyez de nouveau pour obtenir 002. Le compteur sur un octet revient de 255 à 000.
Exemple d’affichage : le compteur commence à 000. Appuyez sur A pour obtenir 001 ; maintenir le bouton n’ajoute rien. Relâchez-le puis appuyez de nouveau pour obtenir 002. Le compteur sur un octet revient de 255 à 000.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe kitaqgb/lib/input.c kitaq-docs/samples/gb_input.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_input.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Distinguish a new button press from a held button.
// Expected: 000 initially; A increments once per press
#include "gb_common.h"
#include "input.h"
u8 manual_count;
// Sample buttons once per frame and increment only on a new A press.
// Holding A does not repeatedly increment; the byte-sized counter wraps after 255.
void main() {
    m_init();
    m_text(2,3,"INPUT");
    input_init();
    m_number(0);
    while (1) { m_wait(); input_update(); if(input_pressed(BTN_A)) { manual_count++; m_number(manual_count); } }
}
Multiplier des valeurs à virgule fixe Q8.8 gb_fixed.c

Objectif : Présente la conversion d’entier en Q8.8, la multiplication et le retour à un entier.

Fonctionnement : fix_from_int convertit 3 et 4 en Q8.8. fix_mul les multiplie, puis fix_to_int reconvertit le produit en entier.

Commandes et résultat attendu : 012 signifie 3 × 4 = 12. C’est l’entier converti, pas la valeur interne multipliée par 256.

Multiplier des valeurs à virgule fixe Q8.8 : 012 signifie 3 × 4 = 12. C’est l’entier converti, pas la valeur interne multipliée par 256.
Exemple d’affichage : 012 signifie 3 × 4 = 12. C’est l’entier converti, pas la valeur interne multipliée par 256.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe kitaqgb/lib/fixed.c kitaq-docs/samples/gb_fixed.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_fixed.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Calculate with signed Q8.8 fixed-point values.
// Expected: 012
#include "gb_common.h"
#include "fixed.h"

// Convert 3 and 4 to Q8.8, multiply them, convert the result back to an
// integer, and display 12.
void main() {
    m_init();
    m_text(2,3,"FIXED");
    fix8 a; fix8 b; a=fix_from_int(3); b=fix_from_int(4);
    m_number((u8)fix_to_int(fix_mul(a,b)));
    while (1) { m_wait();  }
}
Allouer dans un pool fixe d’objets gb_entity.c

Objectif : Crée un objet dans un ensemble fixe d’emplacements, sans allocation dynamique.

Fonctionnement : entity_init réinitialise le pool. entity_create crée un objet de type 1 en (20,30) ; le programme vérifie l’ID d’échec 0xFF avant de fixer vx à 2.

Commandes et résultat attendu : 001 est le nombre d’objets actifs, pas un ID ni une coordonnée. Cet exemple ne met pas à jour et ne dessine pas l’objet ; aucun personnage ne se déplace donc à l’écran.

Allouer dans un pool fixe d’objets : 001 est le nombre d’objets actifs, pas un ID ni une coordonnée. Cet exemple ne met pas à jour et ne dessine pas l’objet ; aucun personnage ne se déplace donc à l’écran.
Exemple d’affichage : 001 est le nombre d’objets actifs, pas un ID ni une coordonnée. Cet exemple ne met pas à jour et ne dessine pas l’objet ; aucun personnage ne se déplace donc à l’écran.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe kitaqgb/lib/entity.c kitaq-docs/samples/gb_entity.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_entity.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Allocate an entity from a fixed-capacity object pool.
// Expected: 001
#include "gb_common.h"
#include "entity.h"

// Create one entity and check the 0xFF allocation-failure sentinel before
// accessing its velocity. Display the pool occupancy, not the entity ID.
void main() {
    m_init();
    m_text(2,3,"ENTITY");
    u8 id; entity_init(); id=entity_create(1,20,30);
    if(id!=0xFF) entity_get(id)->vx=2; m_number(entity_count_active());
    while (1) { m_wait();  }
}
Enregistrer une valeur de diagnostic en RAM gb_debug.c

Objectif : Conserve une valeur de jeu nommée dans un tampon de diagnostic, indépendamment de l’affichage.

Fonctionnement : Le programme initialise le débogage, fixe l’étiquette d’image à 1 et enregistre la valeur 42 sous le nom HP.

Commandes et résultat attendu : 001 est le nombre d’entrées de trace. HP=42 est stocké dans le tampon de diagnostic ; l’écran seul ne montre ni ne vérifie ce contenu.

Enregistrer une valeur de diagnostic en RAM : 001 est le nombre d’entrées de trace. HP=42 est stocké dans le tampon de diagnostic ; l’écran seul ne montre ni ne vérifie ce contenu.
Exemple d’affichage : 001 est le nombre d’entrées de trace. HP=42 est stocké dans le tampon de diagnostic ; l’écran seul ne montre ni ne vérifie ce contenu.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe kitaqgb/lib/debug.c kitaq-docs/samples/gb_debug.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_debug.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Record diagnostic values in the debug trace.
// Expected: 001; trace log contains HP=42
#include "gb_common.h"
#include "debug.h"

// Record HP = 42 with frame tag 1 and display the number of trace entries.
// The trace buffer contains the value; the screen displays the count, not HP.
void main() {
    m_init();
    m_text(2,3,"DEBUG");
    debug_init(); debug_set_frame(1); debug_trace_u8("HP",42);
    m_number(debug_get_trace_count());
    while (1) { m_wait();  }
}
Stocker un historique de positions gb_chain.c

Objectif : Montre comment stocker un historique de positions dans un tampon circulaire.

Fonctionnement : chain_init associe un tableau pouvant contenir quatre points. chain_push_head enregistre ensuite (20,30).

Commandes et résultat attendu : 042 est un repère fixe de fin, pas une coordonnée ni un compteur. Examinez trail et points pour vérifier l’historique stocké. Ce programme ne dessine pas de corps suiveur.

Stocker un historique de positions : 042 est un repère fixe de fin, pas une coordonnée ni un compteur. Examinez trail et points pour vérifier l’historique stocké. Ce programme ne dessine pas de corps suiveur.
Exemple d’affichage : 042 est un repère fixe de fin, pas une coordonnée ni un compteur. Examinez trail et points pour vérifier l’historique stocké. Ce programme ne dessine pas de corps suiveur.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe kitaqgb/lib/chain.c kitaq-docs/samples/gb_chain.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_chain.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Store a position history in a ring buffer.
// Expected: 042; history contains (20,30)
#include "gb_common.h"
#include "chain.h"
Chain trail; ChainPoint points[4];
// Attach four history slots and record one point. The displayed 42 is a fixed
// completion marker; inspect the chain to verify the stored (20,30) point.
void main() {
    m_init();
    m_text(2,3,"CHAIN");
    chain_init(&trail,points,4); chain_push_head(&trail,20,30);
    m_number(42);
    while (1) { m_wait();  }
}
Lire le compteur d’images de la bibliothèque gb_system.c

Objectif : Montre l’effet des attentes comptées en images sur le compteur d’images de la bibliothèque.

Fonctionnement : Après system_init, le programme appelle deux fois system_wait_vblank et affiche system_get_frame8.

Commandes et résultat attendu : Le résultat est 002. La boucle d’attente utilise la fonction utilitaire m_wait du manuel ; la valeur affichée ne continue donc pas à augmenter.

Lire le compteur d’images de la bibliothèque : Le résultat est 002. La boucle d’attente utilise la fonction utilitaire m_wait du manuel ; la valeur affichée ne continue donc pas à augmenter.
Exemple d’affichage : Le résultat est 002. La boucle d’attente utilise la fonction utilitaire m_wait du manuel ; la valeur affichée ne continue donc pas à augmenter.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe kitaqgb/lib/system.c kitaq-docs/samples/gb_system.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_system.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Count completed frame waits.
// Expected: 002
#include "gb_common.h"
#include "system.h"

// Reset the library frame count, perform two counted waits, and display 2.
// Subsequent m_wait calls leave that library counter unchanged.
void main() {
    m_init();
    m_text(2,3,"SYSTEM");
    system_init(); system_wait_vblank(); system_wait_vblank();
    m_number(system_get_frame8());
    while (1) { m_wait();  }
}
Sélectionner l’ID de la scène courante gb_scene.c

Objectif : Montre comment sélectionner un ID pour distinguer des scènes, comme le titre et la partie en cours.

Fonctionnement : Le programme enregistre deux entrées SceneDef initialisées à zéro et sélectionne l’ID 1 avec scene_change.

Commandes et résultat attendu : 001 est l’ID courant renvoyé par scene_get_current. Cet exemple n’enregistre ni callbacks de scène ni dessin de transition.

Sélectionner l’ID de la scène courante : 001 est l’ID courant renvoyé par scene_get_current. Cet exemple n’enregistre ni callbacks de scène ni dessin de transition.
Exemple d’affichage : 001 est l’ID courant renvoyé par scene_get_current. Cet exemple n’enregistre ni callbacks de scène ni dessin de transition.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe kitaqgb/lib/scene.c kitaq-docs/samples/gb_scene.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_scene.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Switch the current scene ID.
// Expected: 001; no callbacks registered
#include "gb_common.h"
#include "scene.h"
SceneDef manual_scenes[2];
// Register two zero-initialized scene entries, select ID 1 and display it.
// No callbacks are registered in this example.
void main() {
    m_init();
    m_text(2,3,"SCENE");
    scene_init(manual_scenes,2); scene_change(1);
    m_number(scene_get_current());
    while (1) { m_wait();  }
}
Convertir les coordonnées du monde en coordonnées d’écran gb_camera.c

Objectif : Soustrait la position de la caméra pour obtenir la position à l’écran d’un point du monde.

Fonctionnement : Camera_Set reçoit la valeur Q8.8 2560, soit X=10 pixels. Camera_WorldToScreenX convertit ensuite la coordonnée du monde X=52.

Commandes et résultat attendu : 052 moins 010 donne le 042 affiché. C’est un calcul de coordonnées ; le fond ne défile pas.

Convertir les coordonnées du monde en coordonnées d’écran : 052 moins 010 donne la valeur affichée 042. Il s’agit d’un calcul de coordonnées ; le fond ne défile pas.
Exemple d’affichage : 052 moins 010 donne le 042 affiché. C’est un calcul de coordonnées ; le fond ne défile pas.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe kitaqgb/lib/scroll.c kitaqgb/lib/camera.c kitaq-docs/samples/gb_camera.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_camera.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Convert world coordinates to screen coordinates.
// Expected: 042: world x=52 minus camera x=10
#include "gb_common.h"
#include "camera.h"
Camera8_8 cam;
// Set the camera X coordinate to 10 pixels in Q8.8 (10 * 256), then subtract
// it from world X = 52 to display screen X = 42.
void main() {
    m_init();
    m_text(2,3,"CAMERA");
    Camera_Init(&cam); Camera_Set(&cam,2560,0);
    m_number((u8)Camera_WorldToScreenX(&cam,52));
    while (1) { m_wait();  }
}
Décompresser des données RLE en RAM gb_rle.c

Objectif : Essaie une représentation compacte des octets répétés sous forme de paires nombre/valeur.

Fonctionnement : Les octets compressés {3,42,0} représentent trois copies de 42, suivies d’un terminateur de compte nul. rle_decode écrit dans une destination de trois octets.

Commandes et résultat attendu : le dernier octet, out[2], est affiché sous la forme 042. Les trois octets décompressés doivent tous contenir 42.

Décompresser des données RLE en RAM : le dernier octet, out[2], est affiché sous la forme 042. Les trois octets décompressés doivent tous contenir 42.
Exemple d’affichage : le dernier octet, out[2], est affiché sous la forme 042. Les trois octets décompressés doivent tous contenir 42.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe kitaqgb/lib/rle.c kitaq-docs/samples/gb_rle.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_rle.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Decode run-length encoded data.
// Expected: 042; three bytes each equal to 42
#include "gb_common.h"
#include "rpg.h"
__prg_rom u8 packed[] = {3,42,0};
// Decode count/value pair (3,42), terminated by a zero count, into a
// three-byte buffer and display the final decoded byte.
void main() {
    m_init();
    m_text(2,3,"RLE");
    u8 out[3]; rle_decode(out,packed); m_number(out[2]);
    while (1) { m_wait();  }
}
Lire et écrire une case du plateau gb_board.c

Objectif : Montre comment les coordonnées d’une grille correspondent au stockage d’un jeu de plateau.

Fonctionnement : Un tableau de 16 octets stocke un plateau de 4×4. Le programme l’efface, écrit 42 dans la case (2,1), puis relit cette case.

Commandes et résultat attendu : 042 est la valeur relue. Le programme ne dessine pas le plateau de 4×4 lui-même.

Lire et écrire une case du plateau : 042 est la valeur relue. Le programme ne dessine pas le plateau de 4×4 lui-même.
Exemple d’affichage : 042 est la valeur relue. Le programme ne dessine pas le plateau de 4×4 lui-même.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe kitaqgb/lib/slg_board.c kitaq-docs/samples/gb_board.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_board.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Read and write cells in a game board.
// Expected: 042
#include "gb_common.h"
#include "slg.h"
SLGBoard board; u8 cells[16];
// Create a cleared 4-by-4 board in caller-owned storage, write 42 at (2,1),
// and display the value read back from the same cell.
void main() {
    m_init();
    m_text(2,3,"BOARD");
    slg_board_init(&board,4,4,cells); slg_board_clear(&board,0);
    slg_board_set(&board,2,1,42); m_number(slg_board_get(&board,2,1));
    while (1) { m_wait();  }
}
Faire avancer une boîte d’un pas de physique gb_physics.c

Objectif : Initialise un monde et un corps rectangulaire, puis met à jour la position à partir de la vitesse.

Fonctionnement : Le corps démarre en (40,40), avec une vitesse (2,0) et une gravité nulle. Le programme appelle kq2d_step une fois.

Commandes et résultat attendu : 042 est la coordonnée X obtenue, mesurée ici en pixels entiers. Aucune boîte ni animation continue n’est dessinée.

Faire avancer une boîte d’un pas de physique : 042 est la coordonnée X obtenue, mesurée ici en pixels entiers. Aucune boîte ni animation continue n’est dessinée.
Exemple d’affichage : 042 est la coordonnée X obtenue, mesurée ici en pixels entiers. Aucune boîte ni animation continue n’est dessinée.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe kitaqgb/lib/fixed.c kitaqgb/lib/physics2d.c kitaq-docs/samples/gb_physics.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_physics.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Advance a rectangular body through one physics step.
// Expected: 042: integer pixel coordinates
#include "gb_common.h"
#include "physics2d.h"
KQWorld2D world; KQBody2D boxes[1];
// Create one box at X = 40, set horizontal velocity 2 and zero gravity,
// then display its X coordinate after a single physics step.
void main() {
    m_init();
    m_text(2,3,"PHYSICS");
    kq2d_world_init(&world,boxes,1);
    kq2d_body_init(&boxes[0],40,40,4,4);
    kq2d_body_set_velocity(&boxes[0],2,0);
    world.gravity_x=0; world.gravity_y=0;
    kq2d_step(&world); m_number((u8)boxes[0].x);
    while (1) { m_wait();  }
}
Lire des données ROM avec un argument de banque gb_bank.c

Objectif : Présente les arguments nécessaires pour copier des données de la ROM vers la RAM.

Fonctionnement : far_data_read copie les deux octets 7 et 42 dans copied, avec l’argument de banque 0 et une longueur de 2.

Commandes et résultat attendu : Le deuxième octet de destination est affiché sous la forme 042. L’exemple lit la ROM fixe ; il n’illustre pas un transfert entre différentes banques commutables.

Lire des données ROM avec un argument de banque : Le deuxième octet de destination est affiché sous la forme 042. L’exemple lit la ROM fixe ; il n’illustre pas un transfert entre différentes banques commutables.
Exemple d’affichage : Le deuxième octet de destination est affiché sous la forme 042. L’exemple lit la ROM fixe ; il n’illustre pas un transfert entre différentes banques commutables.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe kitaqgb/lib/bank.c kitaq-docs/samples/gb_bank.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_bank.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Read data using an explicit ROM bank.
// Expected: 042; explicit bank argument 0
#include "gb_common.h"
#include "bank.h"
__prg_rom u8 values[] = {7,42}; u8 copied[2];
// Copy two ROM bytes using bank argument zero and display the second byte.
// This introduces the API; it does not test switching between distinct payload banks.
void main() {
    m_init();
    m_text(2,3,"BANK");
    far_data_read(0,values,copied,2); m_number(copied[1]);
    while (1) { m_wait();  }
}
Charger des données par ID de ressource gb_asset.c

Objectif : Enregistre l’emplacement et la longueur d’une ressource, puis récupère ses octets par ID.

Fonctionnement : La ressource 0 est de type RAW, dans la banque 0, avec un pointeur vers {7,42} et une longueur de 2. asset_load_raw la copie dans un tampon RAM de deux octets.

Commandes et résultat attendu : Le deuxième octet est affiché sous la forme 042. Cet exemple illustre la recherche et la copie d’une ressource, pas le rendu d’une image.

Charger des données par ID de ressource : Le deuxième octet est affiché sous la forme 042. Cet exemple illustre la recherche et la copie d’une ressource, pas le rendu d’une image.
Exemple d’affichage : Le deuxième octet est affiché sous la forme 042. Cet exemple illustre la recherche et la copie d’une ressource, pas le rendu d’une image.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe kitaqgb/lib/asset.c kitaq-docs/samples/gb_asset.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_asset.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Load asset bytes into RAM by asset ID.
// Expected: 042
#include "gb_common.h"
#include "asset.h"
AssetDesc table[1]; __prg_rom u8 values[] = {7,42}; u8 copied[2];
// Register a two-byte raw asset, copy it into RAM and display its second byte.
// This example uses bank zero and a destination sized for the complete payload.
void main() {
    m_init();
    m_text(2,3,"ASSET");
    table[0].type=ASSET_TYPE_RAW; table[0].bank=0;
    table[0].ptr=values; table[0].len=2;
    asset_set_table(0,table,1); asset_load_raw(0,copied,2);
    m_number(copied[1]);
    while (1) { m_wait();  }
}
Allouer un projectile gb_danmaku.c

Objectif : Initialise le pool de projectiles et examine son occupation après une création.

Fonctionnement : Après danmaku_reset, le programme appelle danmaku_spawn(40,40,16,0) et affiche dm_count.

Commandes et résultat attendu : 001 est le nombre de projectiles alloués. Il n’y a ici ni boucle de mise à jour des projectiles ni composition du fond ; aucun projectile mobile n’est dessiné.

Allouer un projectile : 001 est le nombre de projectiles alloués. Il n’y a ici ni boucle de mise à jour des projectiles ni composition du fond ; aucun projectile mobile n’est dessiné.
Exemple d’affichage : 001 est le nombre de projectiles alloués. Il n’y a ici ni boucle de mise à jour des projectiles ni composition du fond ; aucun projectile mobile n’est dessiné.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe kitaqgb/lib/danmaku.c kitaq-docs/samples/gb_danmaku.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_danmaku.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Allocate a bullet and inspect the pool count.
// Expected: 001; numeric pool example, no bullet BG compositor
#include "gb_common.h"
#include "danmaku.h"

// Reset the bullet pool, spawn one bullet and display the active count.
// This numeric example does not install the background bullet compositor.
void main() {
    m_init();
    m_text(2,3,"DANMAKU");
    danmaku_reset(); danmaku_spawn(40,40,16,0);
    m_number(dm_count);
    while (1) { m_wait();  }
}
Définir une palette de fond CGB gb_cgb_palette.c

Objectif : Choisit les couleurs du fond et du texte à partir des composantes RGB de la Game Boy Color.

Fonctionnement : Le programme désactive le LCD pendant le VBlank, règle la couleur 0 de la palette 0 sur le blanc (31,31,31) et la couleur 3 sur le violet (12,0,22), puis réactive l’affichage.

Commandes et résultat attendu : Exécutez en mode CGB pour voir le texte violet et 042 sur fond blanc. L’affichage monochrome DMG ne peut pas montrer ces couleurs.

Définir une palette de fond CGB : Exécutez en mode CGB pour voir le texte violet et 042 sur fond blanc. L’affichage monochrome DMG ne peut pas montrer ces couleurs.
Exemple d’affichage : Exécutez en mode CGB pour voir le texte violet et 042 sur fond blanc. L’affichage monochrome DMG ne peut pas montrer ces couleurs.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe kitaqgb/lib/cgb_palette.c kitaq-docs/samples/gb_cgb_palette.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_cgb_palette.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb
c
// Set Game Boy Color palette entries.
// Expected: 042 in purple on white; CGB mode
#include "gb_common.h"
#include "cgb_palette.h"

// Set palette-zero background colors to white and purple, then display 42.
// Run the ROM in CGB mode to observe the color result.
void main() {
    m_init();
    m_text(2,3,"CGB PALETTE");
    // Disable the LCD during VBlank so both palette bytes remain accessible.
    m_wait(); M_LCDC = 0;
    cgb_bg_rgb(0,0,31,31,31); cgb_bg_rgb(0,3,12,0,22);
    M_LCDC = 0x91;
    m_number(42);
    while (1) { m_wait();  }
}
Faire défiler le fond horizontalement gb_scroll.c

Objectif : Déplace le point de vue sans redessiner le contenu du fond.

Fonctionnement : Chaque image incrémente phase et le transmet à Scroll_SetBg. La position sur huit bits revient à zéro après 255.

Commandes et résultat attendu : Le mot SCROLL se déplace vers la gauche. Observez sa position au fil du temps ; une seule capture ne peut pas montrer le mouvement.

Faire défiler le fond horizontalement : Le mot SCROLL se déplace vers la gauche. Observez sa position au fil du temps ; une seule capture ne peut pas montrer le mouvement.
Exemple d’affichage : Le mot SCROLL se déplace vers la gauche. Observez sa position au fil du temps ; une seule capture ne peut pas montrer le mouvement.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe kitaqgb/lib/scroll.c kitaq-docs/samples/gb_scroll.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_scroll.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Scroll the background horizontally.
// Expected: title moves left as SCX increases
#include "gb_common.h"
#include "scroll.h"
u8 phase;
// Advance an 8-bit horizontal scroll position once per frame, wrapping naturally
// after 255. Background motion demonstrates the changing scroll register.
void main() {
    m_init();
    m_text(2,3,"SCROLL");
    Scroll_SetBg(0,0);
    while (1) { m_wait(); phase++; Scroll_SetBg(phase,0); }
}
Mettre une mise à jour de tuile en file gb_vram_queue.c

Objectif : Sépare une demande de dessin du transfert VRAM qui l’applique.

Fonctionnement : vram_queue_bg_tile met en file la tuile 52 à la coordonnée (3,8), puis vram_flush la transfère. Dans la police fournie, la tuile 52 correspond au chiffre 4.

Commandes et résultat attendu : Repérez un seul 4 à la coordonnée de tuile du fond (3,8). Il s’agit d’une mise à jour de tuile, pas d’un compteur numérique.

Mettre une mise à jour de tuile en file : Repérez un seul 4 à la coordonnée de tuile du fond (3,8). Il s’agit d’une mise à jour de tuile, pas d’un compteur numérique.
Exemple d’affichage : Repérez un seul 4 à la coordonnée de tuile du fond (3,8). Il s’agit d’une mise à jour de tuile, pas d’un compteur numérique.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe kitaqgb/lib/vram.c kitaq-docs/samples/gb_vram_queue.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_vram_queue.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Queue a VRAM update and flush it at VBlank.
// Expected: 4 at (3,8)
#include "gb_common.h"
#include "vram.h"

// Queue font tile 52 (the digit 4) at tile coordinate (3,8), then wait for
// VBlank and execute the queued transfer.
void main() {
    m_init();
    m_text(2,3,"VRAM QUEUE");
    vram_init();
    vram_queue_bg_tile(3,8,52); vram_flush();
    while (1) { m_wait();  }
}
Afficher un sprite par DMA OAM gb_sprite.c

Objectif : Alloue et affiche un sprite indépendamment du fond.

Fonctionnement : Le pool initialisé fournit l’emplacement 0. Le programme choisit la tuile 65, fixe la position (70,80), active l’affichage OBJ et transfère l’OAM miroir à chaque image.

Commandes et résultat attendu : Une lettre A immobile apparaît à la position de sprite indiquée. Dans un jeu, vérifiez l’échec éventuel de l’allocation avant d’utiliser l’ID renvoyé.

Afficher un sprite par DMA OAM : une lettre A immobile apparaît à la position indiquée pour le sprite. Dans un jeu, vérifiez que l’allocation a réussi avant d’utiliser l’ID renvoyé.
Exemple d’affichage : Une lettre A immobile apparaît à la position de sprite indiquée. Dans un jeu, vérifiez l’échec éventuel de l’allocation avant d’utiliser l’ID renvoyé.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe kitaqgb/lib/sprite.c kitaq-docs/samples/gb_sprite.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_sprite.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Display a sprite through the OAM DMA path.
// Expected: letter A at sprite position
#include "gb_common.h"
#include "sprite.h"

// Allocate the first sprite, choose font tile 65 (A), set its position and
// enable sprite display. Flush shadow OAM after each frame wait.
void main() {
    m_init();
    m_text(2,3,"SPRITE");
    // The freshly reset pool guarantees slot zero for this first allocation.
    // In a running game, check the allocation-failure sentinel before using the returned ID.
    sprite_init(); sprite_set_tile(sprite_alloc(),65);
    sprite_set_pos(0,70,80); M_LCDC=(u8)(M_LCDC|2);
    while (1) { m_wait(); sprite_flush_oam_now(); }
}
Générer une valeur aléatoire reproductible gb_rng.c

Objectif : Utilise une graine pour reproduire une séquence pseudo-aléatoire.

Fonctionnement : rng_seed(1234) initialise le générateur, puis rng_range(10) est appelé une fois. La borne supérieure 10 est exclue.

Commandes et résultat attendu : L’écran affiche une valeur entre 000 et 009. Relancer le même programme avec la même graine reproduit cette valeur ; elle ne change pas à chaque image.

Générer une valeur aléatoire reproductible : L’écran affiche une valeur entre 000 et 009. Relancer le même programme avec la même graine reproduit cette valeur ; elle ne change pas à chaque image.
Exemple d’affichage : L’écran affiche une valeur entre 000 et 009. Relancer le même programme avec la même graine reproduit cette valeur ; elle ne change pas à chaque image.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe kitaqgb/lib/rng.c kitaq-docs/samples/gb_rng.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_rng.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Generate a repeatable pseudorandom value from a seed.
// Expected: value 000 through 009; same seed repeats
#include "gb_common.h"
#include "rpg.h"

// Seed the generator with 1234 and display one value in [0,10).
// Restarting with the same seed reproduces the sequence.
void main() {
    m_init();
    m_text(2,3,"RNG");
    rng_seed(1234); m_number(rng_range(10));
    while (1) { m_wait();  }
}
Stocker un drapeau d’état du jeu gb_flags.c

Objectif : Représente un état booléen, comme l’achèvement d’un événement, par un numéro de drapeau.

Fonctionnement : Le programme efface le drapeau 12, le définit, puis le relit avec flag_get.

Commandes et résultat attendu : 001 signifie que le drapeau 12 est vrai. Cet exemple utilise des drapeaux en RAM et ne les conserve pas après extinction.

Stocker un drapeau d’état du jeu : 001 signifie que le drapeau 12 est vrai. Cet exemple utilise des drapeaux en RAM et ne les conserve pas après extinction.
Exemple d’affichage : 001 signifie que le drapeau 12 est vrai. Cet exemple utilise des drapeaux en RAM et ne les conserve pas après extinction.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe kitaqgb/lib/flags.c kitaq-docs/samples/gb_flags.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_flags.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Read and write RAM-backed game-state flags.
// Expected: 001
#include "gb_common.h"
#include "rpg.h"

// Clear and set flag 12, then display its Boolean value. This does not write
// a save file or demonstrate persistence across power cycles.
void main() {
    m_init();
    m_text(2,3,"FLAGS");
    flag_clear(12); flag_set(12); m_number(flag_get(12));
    while (1) { m_wait();  }
}
Faire avancer un cercle d’un pas de physique gb_circle.c

Objectif : Configure un corps circulaire et met à jour sa position à partir de sa vitesse.

Fonctionnement : Le cercle actif a un rayon de 4, une position (40,40) et une vitesse (2,0). La gravité est nulle et linear_damping_q8 vaut 255 ; kq2dc_step s’exécute une fois.

Commandes et résultat attendu : 042 est la coordonnée X entière obtenue. Cet exemple ne dessine ni contour de cercle ni animation de collision.

Faire avancer un cercle d’un pas de physique : 042 est la coordonnée X entière obtenue. Cet exemple ne dessine ni contour de cercle ni animation de collision.
Exemple d’affichage : 042 est la coordonnée X entière obtenue. Cet exemple ne dessine ni contour de cercle ni animation de collision.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe kitaqgb/lib/physics2d_circle.c kitaq-docs/samples/gb_circle.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_circle.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Advance a circular body through one physics step.
// Expected: 042 after one step; this example uses integer pixels
#include "gb_common.h"
#include "physics2d_circle.h"
KQCircleBody2D balls[1]; KQCircleWorld2D world;
// Activate one circle at X = 40 with horizontal velocity 2 and zero gravity.
// Advance one step and display its resulting integer-pixel X coordinate.
void main() {
    m_init();
    m_text(2,3,"CIRCLE");
    kq2dc_world_init(&world,balls,1);
    balls[0].active=1; balls[0].x=40; balls[0].y=40;
    balls[0].radius=4; balls[0].inv_mass_q8=256;
    balls[0].vx=2; balls[0].vy=0;
    world.gravity_x=0; world.gravity_y=0; world.linear_damping_q8=255;
    kq2dc_step(&world); m_number((u8)balls[0].x);
    while (1) { m_wait();  }
}
Traiter un effet sonore à chaque image gb_sound.c

Objectif : Démarre un flux d’effet sonore et le fait progresser par des mises à jour régulières à chaque image.

Fonctionnement : Après Audio_Init, Audio_PlaySFX lance tone sur le canal d’ID 1. La boucle principale appelle Audio_Update une fois par image.

Commandes et résultat attendu : Un bref effet sonore pulsé joue pendant que 042 reste affiché. Ce nombre est un repère fixe, pas le volume ni la position de lecture. Évaluez le son à l’écoute ou à partir d’un enregistrement.

Traiter un effet sonore à chaque image : Un bref effet sonore pulsé joue pendant que 042 reste affiché. Ce nombre est un repère fixe, pas le volume ni la position de lecture. Évaluez le son à l’écoute ou à partir d’un enregistrement.
Exemple d’affichage : Un bref effet sonore pulsé joue pendant que 042 reste affiché. Ce nombre est un repère fixe, pas le volume ni la position de lecture. Évaluez le son à l’écoute ou à partir d’un enregistrement.

Télécharger le source / État de la vérification

powershell
.\kitaqgb\kitaqgb.exe kitaqgb/lib/audio_hwregs_gb.c kitaqgb/lib/audio.c kitaq-docs/samples/gb_sound.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_sound.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Play a Game Boy pulse-channel sound effect.
// Expected: 042 and a short pulse tone
#include "gb_common.h"
#include "audio.h"
__prg_rom u8 tone[] = { 24,0xF0,24,0xE0,24,0xC0,24,0xA0,24,0x80,24,0x60,0 };
// Start a pulse-channel effect and service Audio_Update once per frame
// so the effect stream advances while 42 remains on screen.
void main() {
    m_init();
    m_text(2,3,"SOUND");
    Audio_Init(); Audio_PlaySFX(tone,1); m_number(42);
    while (1) { m_wait(); Audio_Update(); }
}

Dictionnaire des fonctions intrinsèques

149 entrées. Ouvrez une entrée pour consulter sa syntaxe, ses arguments, les notes source et son exemple. Les fragments d'appel nécessitent une initialisation autour du code ; ce ne sont pas des ROM autonomes. Les exemples montrent comment transmettre des valeurs préparées à l'API, sans constituer une validation sur le périphérique.

asset.h — Identifiants de ressources et descripteurs de données

La bibliothèque de ressources retrouve les données en ROM à partir d’un identifiant de 8 bits. Incluez asset.c et, sur FC, également bank.c. Chaque AssetDesc compact occupe 6 octets : type, banque des données, adresse sur 16 bits et longueur en octets. La table et les données peuvent se trouver dans des banques différentes.

Enregistrez la table avant toute recherche. Cet enregistrement est un état global partagé : ne le modifiez pas pendant une recherche. La bibliothèque vérifie les identifiants et le pointeur de sortie de asset_get, mais pas la table enregistrée, les adresses des données ni le contenu de la cartouche.

Recherche une ressource RAW ou TILES et transfère ses desc.len octets avec __vram_copy. Sélectionnez d’abord la banque ROM des données : cette fonction GB utilise directement desc.ptr et ne sélectionne pas desc.bank pour le transfert.

__farmemcpy

Sélectionne temporairement une banque ROM, copie len octets de src vers dst par adresses croissantes, puis rétablit la banque mémorisée par le compilateur. Sur cette plateforme, __farmemcpy et __far_memcpy utilisent la même implémentation.

Syntaxe

c
void __farmemcpy(void* dst, u8 bank, const void* src, u16 len);

Paramètres

dst

Destination en mémoire accessible en écriture, assez grande pour tous les octets copiés. Hors transfert de zéro octet, les adresses nulles ou invalides ne sont pas vérifiées.

bank

Sélecteur de banque sur 8 bits pour la ROM source. Utilisez une banque compatible avec la cartouche et l’organisation du compilateur ; ce paramètre est distinct de l’adresse source.

src

Pointeur proche vers des octets lisibles dans la banque ROM choisie. Il ne contient pas de numéro de banque.

len

Nombre d’octets, de 0 à 65535. Avec zéro, aucune mémoire n’est copiée, mais la sélection et le rétablissement de la banque ont bien lieu.

Valeur renvoyée

Aucune (void).

Conditions et limites

Toute la source doit être lisible dans le mappage ROM prévu, et toute la destination accessible en écriture. La copie ne passe pas automatiquement à la banque suivante, ne gère pas les chevauchements comme memmove et ne synchronise pas les écritures VRAM. Utilisez les API graphiques pour la VRAM.

Les exemples placent le code appelant dans la banque fixe 0 afin qu’un changement de banque source ne le rende pas inaccessible. Les transferts sont synchrones. N’interrompez pas un transfert par une autre copie changeant de banque ; cette fonction ne garantit pas à elle seule la sûreté des interruptions de toute l’application.

La routine GB rétablit la banque mémorisée par le compilateur. Maintenez cet état avec __bankswitch : une écriture directe dans le mapper ne le met pas à jour. L’emplacement HRAM de sauvegarde est partagé ; des transferts imbriqués qui l’écrasent ne sont pas sûrs.

Exemple d’utilisation

c
__farmemcpy(buffer,payload_bank,payload_pointer,32);
expect(*asset_bank_marker==0xA1,27); // Source bank 2 was selected only for the copy.
for(i=0;i<32;i++) expect(buffer[(__safe_index u8)i]==expected_shape_byte(i),28);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Copie deux motifs, soit 32 octets, depuis la banque 2 alors que la banque 1 est sélectionnée au départ. Le programme compare tous les octets, vérifie le retour à la banque 1 et la conservation du témoin 0x55 lors d’une copie de longueur nulle.

Image attendue : une rangée rouge part de (16,32) et occupe 72×8 pixels. Elle répète trois fois un triangle de 8×8, un carré de 8×8 au contour d’un pixel et une tuile dont les quatre colonnes de gauche sont pleines. Le triangle s’élargit d’un pixel en haut à gauche à huit sur sa ligne inférieure.

Sur GB, le triangle et le contour bleus en (16,64) montrent le résultat de __farmemcpy ; la paire verte en (16,96), celui de __far_memcpy. Chaque paire occupe 16×8 pixels. Le CGB utilise ces couleurs ; le DMG affiche les mêmes formes en noir sur blanc.

L’état attendu est FAILED CHECKS 000 et FIRST FAILURE 00. La vérification exécute 300 images puis compare texte, formes et couleurs aux résultats attendus. Il s’agit d’une preuve sur émulateur, pas d’un essai sur matériel physique.

__farmemcpy: DMG
DMG
__farmemcpy: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\asset_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\asset_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/asset.h:17

__vram_copy

__vram_copy est un alias du compilateur pour __vram_memcpy. Le compte reste en octets, avec le même transfert immédiat par CPU et les mêmes règles d’accès au LCD.

Copie immédiatement len octets de src vers dst par adresses croissantes. Revient à la fin du transfert, sans mise en file. Les deux adresses avancent d’un octet, sans pas de ligne de carte ni conversion de format graphique.

Syntaxe

c
void __vram_copy(u16 dst, const void* src, u16 len);

Paramètres

dst

Adresse du premier octet de destination, normalement dans la VRAM 0x8000–0x9FFF. Toute la plage doit être valide. Le CGB utilise la banque courante ; dans la carte, la banque 0 contient les indices et la banque 1 les attributs. Ces routines ne choisissent pas la carte selon LCDC, ne sautent pas de ligne et n’allouent pas de VRAM.

src

Pointeur sur les octets source, valide et correctement mappé pendant tout l’appel. Les données sont lues immédiatement, sans conservation pour un flush ultérieur. La banque source n’est pas changée automatiquement. Utilisez des plages disjointes : cette copie vers l’avant n’offre pas la sémantique de memmove.

len

Nombre d’octets consécutifs, de 0 à 65535, pas de tuiles. Gardez toute la plage dans une source valide et une destination modifiable ; l’API ne vérifie pas les limites et n’empêche pas le bouclage d’adresse sur 16 bits.

Valeur renvoyée

Aucune (void).

Conditions et limites

LCD actif, chaque écriture attend le mode STAT 0 ou 1 sous DI, puis exécute EI. LCD éteint, un simple parcours CPU est utilisé. L’appel peut durer plusieurs images, ne préserve pas les interruptions désactivées à l’entrée et ne configure aucun registre DMA. Une longueur nulle ne transfère aucune donnée.

Exemple d’utilisation

c
__vram_copy(0x9862,strip,3); // The copy alias uses the same CPU transfer.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple utilise __vram_copy pour écrire trois octets de carte à la tuile (2,3), soit au pixel (16,24). On attend [1,2,3] de gauche à droite dans une zone de 24 × 8, avec les cases voisines vides. Le programme complet place chaque copie ou remplissage testé sur sa propre ligne.

Les graphismes originaux fournis utilisent la tuile 1 pour un carré plein de 8 × 8, la 2 pour ses quatre colonnes gauches et la 3 pour un triangle aligné à gauche, élargi d’un pixel par ligne de 1 à 8. Le DMG utilise le noir sur blanc ; le CGB distingue les opérations par des couleurs en gardant le texte noir.

Le carré au pixel (16,88) doit subsister après les appels sûrs et sans attente de longueur nulle. LCD actif, la copie et le remplissage sûrs ajoutent des carrés en (16,96) et (32,96). FAILED CHECKS doit afficher 000. Les tests d’émulateur comparent les pixels et le compteur. Le CGB utilise ici la banque 0 ; les autres banques et le matériel physique ne sont pas testés.

Sur CGB, les lignes de MEMCPY à FILL UNSAFE répètent la séquence rouge, bleu, vert. Le carré de ZERO COUNT est rouge. Dans LCD ON, celui de gauche est bleu et celui de droite vert. Le texte reste noir. Les tests vérifient ces couleurs sur chaque pixel dessiné.

__vram_copy: DMG
DMG
__vram_copy: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\vram_memory_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_memory_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/asset.h:18

bank.h — Accès au code et aux données tenant compte des banques

Une banque ROM est un bloc de stockage de la cartouche mappé dans l’espace d’adressage du CPU. Plusieurs banques peuvent apparaître à la même adresse : un pointeur de 16 bits ne suffit donc pas à identifier les données. BankPtr est un descripteur compact de trois octets : un numéro de banque sur un octet et un pointeur sur deux octets. Créez-le avec farptr_make, puis accédez aux données avec farptr_read8, farptr_read16 ou farptr_read.

L’exemple GB utilise une cartouche MBC5 : le code de la banque fixe 0 reste accessible en $0000–$3FFF, tandis que les banques 1, 2 et 3 sont sélectionnées en $4000–$7FFF. Placez bank_switch et le code exécuté après son retour en ROM fixe. Initialisez l’état de la bibliothèque avec bank_switch avant de le consulter. Sur GB, far_call accepte un pointeur de rappel ; __farcall exige un nom de fonction.

__farcall_ptr

Invoque un rappel sans argument à une adresse déterminée à l’exécution dans la banque fournie. La routine en ROM fixe sauvegarde la banque de chaque appelant sur la pile du CPU et la restaure au retour. L’intrinsèque ne renvoie aucune valeur.

Syntaxe

c
void __farcall_ptr(u8 bank, const void* func);

Paramètres

bank

Numéro de banque ROM non signé sur 8 bits. Pour un appel, il doit correspondre au placement de la fonction ; pour une lecture, il sélectionne la source. Respectez la plage du type de cartouche.

func

Adresse exécutable valide d’un rappel sans paramètre, fournie par un nom de fonction ou un pointeur d’exécution. Les pointeurs nuls et les associations banque/adresse incorrectes ne sont pas vérifiés à l’exécution.

Valeur renvoyée

Aucune (void).

Conditions et limites

Ces appels ne transmettent pas d’arguments utilisateur et ne rendent pas le rappel réentrant face aux interruptions. Gérez correctement ses données, sa pile et ses interruptions. La restauration des banques est testée avec des appels imbriqués et répétés ; l’exemple ne couvre ni le matériel physique ni tous les entrelacements d’interruptions.

Ces interfaces GB transportent huit bits de banque et ne règlent pas le neuvième bit MBC5. Les règles de la cartouche restent applicables, notamment pour la banque zéro. L’exemple utilise explicitement les banques MBC5 1–3 ; ne mélangez pas cet état avec des écritures directes non suivies dans le mapper.

Exemple d’utilisation

c
requested_bank=3;requested_callback=callback;
__farcall_ptr(requested_bank,requested_callback); // Runtime bank and address.
bank_expect(callback_count==3 && callback_byte==0xD3,17);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

À ce stade du programme complet, __farcall_ptr porte callback_count à 3. Le rappel lit D3 dans la banque 3. Une lecture proche ultérieure de B2 dans home2 confirme le retour avec la banque 2 sélectionnée.

Le programme complet montre comment identifier des données par une banque et une adresse proche, appeler des rappels entre banques et distinguer l’état de la bibliothèque du mappage réel. Tous les nombres sont hexadécimaux. BANK IN et BANK OUT doivent valoir 02 ; BYTE HEX, D3 ; WORD HEX, 5C7A ; FAILED CHECKS et FIRST FAILURE, 00. Le texte est bleu sur CGB et FC, noir sur DMG.

CALL COUNT vaut 03 : far_call, __farcall et __farcall_ptr invoquent chacun le rappel une fois.

__farcall_ptr: DMG
DMG
__farcall_ptr: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/bank.h:15

kitaqgb/kitaqgb/CodeGenerator.cs:8335

csharp
if (funcName == "__farcall_ptr")
            {
                if (args.Length != 2) Error(expr, "__farcall_ptr(bank, func) expects 2 arguments");
                CType callbackType = TypeOf(args[1]);
                if (callbackType.IsPointer) callbackType = callbackType.Subtype;
                if (callbackType != null && callbackType.IsFunction &&
                    (callbackType.ParamTypes ?? Array.Empty<CType>()).Length != 0)
                    Error(expr, "__farcall_ptr requires a callback with no parameters");
                CompileIntoA(args[0]);
                EmitAsm("PUSH_AF");
                CompileIntoHL(args[1]);
                EmitAsm("POP_AF");
                UseFarCallPointerBank0Helper = true;
                EmitAsm("CALL", new AsmOperand("__kq_farcall_pointer_bank0", AddressMode.Absolute));
                RecordCallEdge(expr, "<indirect-far>", -1, "farcall_pointer", viaThunk: true,
                    viaFarcall: true, actualArgSizes: new int[0], expectedArgSizes: new int[0]);
                return;
            }
__farpeek16

Lit une valeur de 16 bits little-endian en addr et addr+1 après une sélection temporaire de banque, puis rétablit la banque précédente. C’est l’intrinsèque utilisée par far_data_read16.

Syntaxe

c
u16 __farpeek16(u8 bank, const void* addr);

Paramètres

bank

Numéro de banque ROM non signé sur 8 bits. Pour un appel, il doit correspondre au placement de la fonction ; pour une lecture, il sélectionne la source. Respectez la plage du type de cartouche.

addr

Pointeur proche vers des octets lisibles dans la banque ROM choisie. Il ne contient pas de numéro de banque.

Valeur renvoyée

Le mot little-endian lu, sous forme de u16 (0–65535).

Conditions et limites

L’octet ou le mot entier doit être lisible dans le mappage CPU sélectionné. Ces lectures ne valident pas les pointeurs, ne bornent pas les adresses et ne passent pas automatiquement à une autre banque à la limite d’une fenêtre. Un mot exige deux octets consécutifs valides.

Les lectures et copies distantes sont synchrones et utilisent un espace de travail partagé du compilateur. Une interruption ne doit pas y exécuter un autre transfert pendant son utilisation. Sur GB, la copie utilise aussi un emplacement HRAM partagé pour la banque sauvegardée. L’exemple exécute ces opérations depuis du code fixe, sans gestionnaire de transfert concurrent.

Ces interfaces GB transportent huit bits de banque et ne règlent pas le neuvième bit MBC5. Les règles de la cartouche restent applicables, notamment pour la banque zéro. L’exemple utilise explicitement les banques MBC5 1–3 ; ne mélangez pas cet état avec des écritures directes non suivies dans le mapper.

Exemple d’utilisation

c
bank_expect(__farpeek16(3,payload+1)==0x5C7A,14);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet extrait utilise __farpeek16 pour lire payload+1 dans la banque 3 et vérifie 5C7A. Le programme complet lit aussi home2 avec un pointeur proche pour confirmer la restauration de la banque 2.

Le programme complet montre comment identifier des données par une banque et une adresse proche, appeler des rappels entre banques et distinguer l’état de la bibliothèque du mappage réel. Tous les nombres sont hexadécimaux. BANK IN et BANK OUT doivent valoir 02 ; BYTE HEX, D3 ; WORD HEX, 5C7A ; FAILED CHECKS et FIRST FAILURE, 00. Le texte est bleu sur CGB et FC, noir sur DMG.

CALL COUNT vaut 03 : far_call, __farcall et __farcall_ptr invoquent chacun le rappel une fois.

__farpeek16: DMG
DMG
__farpeek16: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/bank.h:13

kitaqgb/kitaqgb/CodeGenerator.cs:8295

csharp
if (funcName == "__farpeek16")
            {
                if (args.Length != 2) Error(expr, "__farpeek16(bank, addr) expects 2 arguments");

                CompileIntoA(args[0]); EmitPushAAsWord(); // bank
                CompileIntoHL(args[1]); EmitAsm("PUSH_HL"); // src
                EmitAsm("ADD_SP_IMM", new AsmOperand(-2, AddressMode.Relative)); // scratch
                // At SP: scratch, saved address, saved bank; each occupies two bytes.

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("PUSH_HL"); // dst = &scratch

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_E_A");
                EmitAsm("INC_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("LD_D_A");

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(6, AddressMode.Relative));
                EmitAsm("LD_A_HL"); // bank

                EmitAsm("POP_HL"); // dst
                EmitAsm("LD_BC_IMM", new AsmOperand(2, AddressMode.Immediate16));
                EmitCallFarMemcpyHelper();

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_E_A");
                EmitAsm("INC_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("LD_D_A");
                EmitAsm("LD_H_D");
                EmitAsm("LD_L_E");
                EmitAsm("ADD_SP_IMM", new AsmOperand(6, AddressMode.Relative));
                LastCallReturnsHL = true;
                return;
            }
__farpeek8

Sélectionne temporairement la banque indiquée, lit un octet à l’adresse CPU et rétablit la banque précédente. C’est l’intrinsèque utilisée par far_data_read8.

Syntaxe

c
u8 __farpeek8(u8 bank, const void* addr);

Paramètres

bank

Numéro de banque ROM non signé sur 8 bits. Pour un appel, il doit correspondre au placement de la fonction ; pour une lecture, il sélectionne la source. Respectez la plage du type de cartouche.

addr

Pointeur proche vers des octets lisibles dans la banque ROM choisie. Il ne contient pas de numéro de banque.

Valeur renvoyée

L’octet lu, sous forme de u8 (0–255).

Conditions et limites

L’octet ou le mot entier doit être lisible dans le mappage CPU sélectionné. Ces lectures ne valident pas les pointeurs, ne bornent pas les adresses et ne passent pas automatiquement à une autre banque à la limite d’une fenêtre. Un mot exige deux octets consécutifs valides.

Les lectures et copies distantes sont synchrones et utilisent un espace de travail partagé du compilateur. Une interruption ne doit pas y exécuter un autre transfert pendant son utilisation. Sur GB, la copie utilise aussi un emplacement HRAM partagé pour la banque sauvegardée. L’exemple exécute ces opérations depuis du code fixe, sans gestionnaire de transfert concurrent.

Ces interfaces GB transportent huit bits de banque et ne règlent pas le neuvième bit MBC5. Les règles de la cartouche restent applicables, notamment pour la banque zéro. L’exemple utilise explicitement les banques MBC5 1–3 ; ne mélangez pas cet état avec des écritures directes non suivies dans le mapper.

Exemple d’utilisation

c
bank_expect(__farpeek8(3,payload)==0xD3,13);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet extrait utilise __farpeek8 pour lire payload dans la banque 3 et vérifie D3. Le programme complet lit aussi home2 avec un pointeur proche pour confirmer la restauration de la banque 2.

Le programme complet montre comment identifier des données par une banque et une adresse proche, appeler des rappels entre banques et distinguer l’état de la bibliothèque du mappage réel. Tous les nombres sont hexadécimaux. BANK IN et BANK OUT doivent valoir 02 ; BYTE HEX, D3 ; WORD HEX, 5C7A ; FAILED CHECKS et FIRST FAILURE, 00. Le texte est bleu sur CGB et FC, noir sur DMG.

CALL COUNT vaut 03 : far_call, __farcall et __farcall_ptr invoquent chacun le rappel une fois.

__farpeek8: DMG
DMG
__farpeek8: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/bank.h:12

kitaqgb/kitaqgb/CodeGenerator.cs:8262

csharp
if (funcName == "__farpeek8")
            {
                if (args.Length != 2) Error(expr, "__farpeek8(bank, addr) expects 2 arguments");

                CompileIntoA(args[0]); EmitPushAAsWord(); // bank
                CompileIntoHL(args[1]); EmitAsm("PUSH_HL"); // src
                EmitAsm("ADD_SP_IMM", new AsmOperand(-2, AddressMode.Relative)); // scratch
                // At SP: scratch, saved address, saved bank; each occupies two bytes.

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("PUSH_HL"); // dst = &scratch

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_E_A");
                EmitAsm("INC_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("LD_D_A");

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(6, AddressMode.Relative));
                EmitAsm("LD_A_HL"); // bank

                EmitAsm("POP_HL"); // dst
                EmitAsm("LD_BC_IMM", new AsmOperand(1, AddressMode.Immediate16));
                EmitCallFarMemcpyHelper();

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("ADD_SP_IMM", new AsmOperand(6, AddressMode.Relative));
                return;
            }

cgb_palette.h — Palettes BG et OBJ de la CGB

cgb_palette.h et cgb_palette.c proposent le compactage RGB15 et la mise à jour immédiate des palettes CGB. Utilisez BG pour fond/fenêtre et OBJ pour sprites. Les fonctions de couleur ou de composantes modifient une couleur ; les plages écrivent des mots consécutifs ; une palette entière écrit quatre mots. Les plages raw utilisent des emplacements de couleur absolus, les intrinsèques de registre des positions d'octet. Attribuez la palette par la carte ou l'OAM. Incluez l'implémentation une seule fois, comme dans l'exemple complet.

Les écritures de palette et de registres protégés n'écrivent rien en mode DMG. Safe désigne ici le contrôle du mode CGB, pas une attente d'accès PPU ni une section critique. Configurez avec le LCD éteint ou effectuez une mise à jour assez courte pendant une phase accessible. Aucune attente VBlank ni protection contre les interruptions n'est ajoutée.

Les palettes BG/fenêtre et OBJ sont stockées séparément. Les écritures de la bibliothèque activent l'incrément et laissent le registre d'indice modifié. La couleur OBJ zéro reste transparente au rendu des sprites, même si ses octets peuvent être écrits et lus.

__cgb_is_cgb

Renvoie le booléen de mode CGB du compilateur. Un ciblage explicite --cgb=dmg devient la constante 0 et --cgb=cgb_only la constante 1 ; --cgb=cgb utilise une détection à l'exécution. Il s'agit du mode d'exécution choisi, pas d'un inventaire des capacités de la machine physique.

Syntaxe

c
u8 __cgb_is_cgb();

Valeur renvoyée

u8 : 0 en mode DMG, 1 en mode CGB.

Exemple d’utilisation

c
color_mode=__cgb_is_cgb(); // 0 in DMG mode, 1 in CGB mode for this dual-mode ROM.
if (color_mode>1) failures++;

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Ce programme montre comment les palettes BG et OBJ colorent indépendamment un même motif. Chaque triangle occupe 8 × 8 pixels, d'un seul pixel à gauche en haut jusqu'à une ligne complète en bas. Aux abscisses 16, 32, 48, 64, 80, 96, 112, la ligne BG à y = 24 est rouge, bleue, verte, rouge, bleue, verte, rouge ; la ligne OBJ à y = 64 est bleue, verte, rouge, bleue, verte, rouge, bleue. Les colonnes utilisent respectivement une couleur, les composantes, une plage indexée, une palette entière, une plage absolue, un registre canonique et son alias.

À y = 88, sept cases de 8 × 8 montrent : triangle rouge (x = 16), contour carré bleu d'un pixel provenant de la banque 1 (32), triangle vert réfléchi horizontalement (48), rouge réfléchi verticalement (64), bleu réfléchi sur les deux axes (80), triangle vert devant un sprite rouge visible dans les pixels de couleur zéro (96), puis sprite rouge plein devant le triangle BG, témoin sans priorité (112). Les coordonnées sont en pixels depuis le coin supérieur gauche. Le fond reste blanc et le texte noir.

FAILED CHECKS 000 confirme les masques RGB, les booléens (dont 2), la normalisation des indices, le franchissement de palettes, l'arrêt à l'emplacement 31, l'absence d'écriture pour pointeur nul ou longueur nulle et la relecture des couleurs LCD éteint. Quatre combinaisons sont testées : ROM mixte sur DMG/CGB, ROM DMG seule sur DMG, ROM CGB seule sur CGB. Sur DMG, CGB MODE vaut 0, les triangles sont noirs, banque et réflexions n'affectent pas l'image et les deux dernières cases du bas sont des carrés noirs. Sur CGB, CGB MODE vaut 1. Le matériel réel et les conflits d'accès LCD allumé ne sont pas testés.

__cgb_is_cgb: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_is_cgb: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_is_cgb: --cgb=dmg / DMG
--cgb=dmg / DMG
__cgb_is_cgb: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/cgb_palette.h:8

Notes de déclaration (texte source)
Safe here means CGB hardware gating. Palette data writes still require an accessible
PPU phase (for example LCD off or a sufficiently short VBlank update).
__cgb_safe_set_bcpd

Écrit un octet dans le registre de données BCPD/BGPD ($FF69) des palettes de fond/fenêtre sur CGB. __cgb_safe_set_bgpd désigne le même registre. Le registre d'indice choisit la position ; une couleur RGB15 complète exige un octet faible et un octet fort.

Syntaxe

c
void __cgb_safe_set_bcpd(u8 value);

Paramètres

value

Un octet, 0–255. Écrivez l'octet faible puis l'octet fort de RGB15 ; activez d'abord l'incrément automatique ou sélectionnez chaque position. L'écriture de données n'active pas elle-même l'incrément.

Valeur renvoyée

Aucune (void).

Conditions et limites

Les écritures de palette et de registres protégés n'écrivent rien en mode DMG. Safe désigne ici le contrôle du mode CGB, pas une attente d'accès PPU ni une section critique. Configurez avec le LCD éteint ou effectuez une mise à jour assez courte pendant une phase accessible. Aucune attente VBlank ni protection contre les interruptions n'est ajoutée.

Exemple d’utilisation

c
__cgb_safe_set_bcpd(0xE0); __cgb_safe_set_bcpd(0x03); // Green word 0x03E0, low then high.
expect_color(0,27,992);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Ce programme montre comment les palettes BG et OBJ colorent indépendamment un même motif. Chaque triangle occupe 8 × 8 pixels, d'un seul pixel à gauche en haut jusqu'à une ligne complète en bas. Aux abscisses 16, 32, 48, 64, 80, 96, 112, la ligne BG à y = 24 est rouge, bleue, verte, rouge, bleue, verte, rouge ; la ligne OBJ à y = 64 est bleue, verte, rouge, bleue, verte, rouge, bleue. Les colonnes utilisent respectivement une couleur, les composantes, une plage indexée, une palette entière, une plage absolue, un registre canonique et son alias.

À y = 88, sept cases de 8 × 8 montrent : triangle rouge (x = 16), contour carré bleu d'un pixel provenant de la banque 1 (32), triangle vert réfléchi horizontalement (48), rouge réfléchi verticalement (64), bleu réfléchi sur les deux axes (80), triangle vert devant un sprite rouge visible dans les pixels de couleur zéro (96), puis sprite rouge plein devant le triangle BG, témoin sans priorité (112). Les coordonnées sont en pixels depuis le coin supérieur gauche. Le fond reste blanc et le texte noir.

FAILED CHECKS 000 confirme les masques RGB, les booléens (dont 2), la normalisation des indices, le franchissement de palettes, l'arrêt à l'emplacement 31, l'absence d'écriture pour pointeur nul ou longueur nulle et la relecture des couleurs LCD éteint. Quatre combinaisons sont testées : ROM mixte sur DMG/CGB, ROM DMG seule sur DMG, ROM CGB seule sur CGB. Sur DMG, CGB MODE vaut 0, les triangles sont noirs, banque et réflexions n'affectent pas l'image et les deux dernières cases du bas sont des carrés noirs. Sur CGB, CGB MODE vaut 1. Le matériel réel et les conflits d'accès LCD allumé ne sont pas testés.

__cgb_safe_set_bcpd: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_bcpd: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_bcpd: --cgb=dmg / DMG
--cgb=dmg / DMG
__cgb_safe_set_bcpd: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/cgb_palette.h:10

__cgb_safe_set_bcps

Écrit sur CGB le registre d'indice d'octet BCPS/BGPI ($FF68) des palettes de fond/fenêtre. __cgb_safe_set_bgpi accède au même registre. Sélectionne la position du prochain accès au registre de données, sans écrire de couleur à lui seul.

Syntaxe

c
void __cgb_safe_set_bcps(u8 value);

Paramètres

value

Indice d'octet aux bits 0–5 (0–63) ; le bit 7 active l'incrément après écriture des données. Le bit 6 est inutilisé. La couleur n commence à l'octet 2n. Après 63, l'indice revient à zéro : gérez les limites lors des écritures directes.

Valeur renvoyée

Aucune (void).

Conditions et limites

Les écritures de palette et de registres protégés n'écrivent rien en mode DMG. Safe désigne ici le contrôle du mode CGB, pas une attente d'accès PPU ni une section critique. Configurez avec le LCD éteint ou effectuez une mise à jour assez courte pendant une phase accessible. Aucune attente VBlank ni protection contre les interruptions n'est ajoutée.

Exemple d’utilisation

c
__cgb_safe_set_bcps(0xB6); // BG byte 54, with auto-increment enabled (slot 27 low byte).

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Ce programme montre comment les palettes BG et OBJ colorent indépendamment un même motif. Chaque triangle occupe 8 × 8 pixels, d'un seul pixel à gauche en haut jusqu'à une ligne complète en bas. Aux abscisses 16, 32, 48, 64, 80, 96, 112, la ligne BG à y = 24 est rouge, bleue, verte, rouge, bleue, verte, rouge ; la ligne OBJ à y = 64 est bleue, verte, rouge, bleue, verte, rouge, bleue. Les colonnes utilisent respectivement une couleur, les composantes, une plage indexée, une palette entière, une plage absolue, un registre canonique et son alias.

À y = 88, sept cases de 8 × 8 montrent : triangle rouge (x = 16), contour carré bleu d'un pixel provenant de la banque 1 (32), triangle vert réfléchi horizontalement (48), rouge réfléchi verticalement (64), bleu réfléchi sur les deux axes (80), triangle vert devant un sprite rouge visible dans les pixels de couleur zéro (96), puis sprite rouge plein devant le triangle BG, témoin sans priorité (112). Les coordonnées sont en pixels depuis le coin supérieur gauche. Le fond reste blanc et le texte noir.

FAILED CHECKS 000 confirme les masques RGB, les booléens (dont 2), la normalisation des indices, le franchissement de palettes, l'arrêt à l'emplacement 31, l'absence d'écriture pour pointeur nul ou longueur nulle et la relecture des couleurs LCD éteint. Quatre combinaisons sont testées : ROM mixte sur DMG/CGB, ROM DMG seule sur DMG, ROM CGB seule sur CGB. Sur DMG, CGB MODE vaut 0, les triangles sont noirs, banque et réflexions n'affectent pas l'image et les deux dernières cases du bas sont des carrés noirs. Sur CGB, CGB MODE vaut 1. Le matériel réel et les conflits d'accès LCD allumé ne sont pas testés.

__cgb_safe_set_bcps: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_bcps: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_bcps: --cgb=dmg / DMG
--cgb=dmg / DMG
__cgb_safe_set_bcps: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/cgb_palette.h:9

__cgb_safe_set_ocpd

Écrit un octet dans le registre de données OCPD/OBPD ($FF6B) des palettes de sprites (OBJ) sur CGB. __cgb_safe_set_obpd désigne le même registre. Le registre d'indice choisit la position ; une couleur RGB15 complète exige un octet faible et un octet fort.

Syntaxe

c
void __cgb_safe_set_ocpd(u8 value);

Paramètres

value

Un octet, 0–255. Écrivez l'octet faible puis l'octet fort de RGB15 ; activez d'abord l'incrément automatique ou sélectionnez chaque position. L'écriture de données n'active pas elle-même l'incrément.

Valeur renvoyée

Aucune (void).

Conditions et limites

Les écritures de palette et de registres protégés n'écrivent rien en mode DMG. Safe désigne ici le contrôle du mode CGB, pas une attente d'accès PPU ni une section critique. Configurez avec le LCD éteint ou effectuez une mise à jour assez courte pendant une phase accessible. Aucune attente VBlank ni protection contre les interruptions n'est ajoutée.

Exemple d’utilisation

c
__cgb_safe_set_ocpd(31); __cgb_safe_set_ocpd(0); // Red sprite color.
expect_color(1,27,31);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Ce programme montre comment les palettes BG et OBJ colorent indépendamment un même motif. Chaque triangle occupe 8 × 8 pixels, d'un seul pixel à gauche en haut jusqu'à une ligne complète en bas. Aux abscisses 16, 32, 48, 64, 80, 96, 112, la ligne BG à y = 24 est rouge, bleue, verte, rouge, bleue, verte, rouge ; la ligne OBJ à y = 64 est bleue, verte, rouge, bleue, verte, rouge, bleue. Les colonnes utilisent respectivement une couleur, les composantes, une plage indexée, une palette entière, une plage absolue, un registre canonique et son alias.

À y = 88, sept cases de 8 × 8 montrent : triangle rouge (x = 16), contour carré bleu d'un pixel provenant de la banque 1 (32), triangle vert réfléchi horizontalement (48), rouge réfléchi verticalement (64), bleu réfléchi sur les deux axes (80), triangle vert devant un sprite rouge visible dans les pixels de couleur zéro (96), puis sprite rouge plein devant le triangle BG, témoin sans priorité (112). Les coordonnées sont en pixels depuis le coin supérieur gauche. Le fond reste blanc et le texte noir.

FAILED CHECKS 000 confirme les masques RGB, les booléens (dont 2), la normalisation des indices, le franchissement de palettes, l'arrêt à l'emplacement 31, l'absence d'écriture pour pointeur nul ou longueur nulle et la relecture des couleurs LCD éteint. Quatre combinaisons sont testées : ROM mixte sur DMG/CGB, ROM DMG seule sur DMG, ROM CGB seule sur CGB. Sur DMG, CGB MODE vaut 0, les triangles sont noirs, banque et réflexions n'affectent pas l'image et les deux dernières cases du bas sont des carrés noirs. Sur CGB, CGB MODE vaut 1. Le matériel réel et les conflits d'accès LCD allumé ne sont pas testés.

__cgb_safe_set_ocpd: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_ocpd: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_ocpd: --cgb=dmg / DMG
--cgb=dmg / DMG
__cgb_safe_set_ocpd: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/cgb_palette.h:12

__cgb_safe_set_ocps

Écrit sur CGB le registre d'indice d'octet OCPS/OBPI ($FF6A) des palettes de sprites (OBJ). __cgb_safe_set_obpi accède au même registre. Sélectionne la position du prochain accès au registre de données, sans écrire de couleur à lui seul.

Syntaxe

c
void __cgb_safe_set_ocps(u8 value);

Paramètres

value

Indice d'octet aux bits 0–5 (0–63) ; le bit 7 active l'incrément après écriture des données. Le bit 6 est inutilisé. La couleur n commence à l'octet 2n. Après 63, l'indice revient à zéro : gérez les limites lors des écritures directes.

Valeur renvoyée

Aucune (void).

Conditions et limites

Les écritures de palette et de registres protégés n'écrivent rien en mode DMG. Safe désigne ici le contrôle du mode CGB, pas une attente d'accès PPU ni une section critique. Configurez avec le LCD éteint ou effectuez une mise à jour assez courte pendant une phase accessible. Aucune attente VBlank ni protection contre les interruptions n'est ajoutée.

Exemple d’utilisation

c
__cgb_safe_set_ocps(0xB6); // OBJ byte 54; this does not select a BG color.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Ce programme montre comment les palettes BG et OBJ colorent indépendamment un même motif. Chaque triangle occupe 8 × 8 pixels, d'un seul pixel à gauche en haut jusqu'à une ligne complète en bas. Aux abscisses 16, 32, 48, 64, 80, 96, 112, la ligne BG à y = 24 est rouge, bleue, verte, rouge, bleue, verte, rouge ; la ligne OBJ à y = 64 est bleue, verte, rouge, bleue, verte, rouge, bleue. Les colonnes utilisent respectivement une couleur, les composantes, une plage indexée, une palette entière, une plage absolue, un registre canonique et son alias.

À y = 88, sept cases de 8 × 8 montrent : triangle rouge (x = 16), contour carré bleu d'un pixel provenant de la banque 1 (32), triangle vert réfléchi horizontalement (48), rouge réfléchi verticalement (64), bleu réfléchi sur les deux axes (80), triangle vert devant un sprite rouge visible dans les pixels de couleur zéro (96), puis sprite rouge plein devant le triangle BG, témoin sans priorité (112). Les coordonnées sont en pixels depuis le coin supérieur gauche. Le fond reste blanc et le texte noir.

FAILED CHECKS 000 confirme les masques RGB, les booléens (dont 2), la normalisation des indices, le franchissement de palettes, l'arrêt à l'emplacement 31, l'absence d'écriture pour pointeur nul ou longueur nulle et la relecture des couleurs LCD éteint. Quatre combinaisons sont testées : ROM mixte sur DMG/CGB, ROM DMG seule sur DMG, ROM CGB seule sur CGB. Sur DMG, CGB MODE vaut 0, les triangles sont noirs, banque et réflexions n'affectent pas l'image et les deux dernières cases du bas sont des carrés noirs. Sur CGB, CGB MODE vaut 1. Le matériel réel et les conflits d'accès LCD allumé ne sont pas testés.

__cgb_safe_set_ocps: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_ocps: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_ocps: --cgb=dmg / DMG
--cgb=dmg / DMG
__cgb_safe_set_ocps: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/cgb_palette.h:11

cgb_tile.h — Numéros et attributs de tuiles CGB

cgb_tile.h regroupe les déclarations des fonctions intrinsèques de carte de tuiles et les macros de construction des attributs CGB. Le numéro de tuile choisit un motif ; son attribut choisit palette, banque VRAM du motif, retournements et priorité du fond. Modifier les attributs ne change pas les pixels du motif.

La famille __settile vise la carte 0x9800 ; __settileat utilise une base explicite ; __settilebg, la carte de fond choisie par LCDC ; __settilewin, sa carte de fenêtre. Les formes attr n’écrivent que les attributs, les formes cgb écrivent numéros et attributs. Les formes bulk transfèrent des octets consécutifs à l’adresse fournie. Choisissez selon la destination et les données à écrire.

Utilisez des macros comme KQ_CGB_ATTR_PAL pour les bits individuels, ou KQ_CGB_ATTR pour combiner les champs. Le champ bank choisit la banque VRAM du motif, pas une banque ROM. Les écritures ordinaires de numéros utilisent la banque VRAM courante : sélectionnez la banque zéro avant les opérations combinées tuile/attribut. Sur DMG, les opérations d’attribut seul sont sans effet ; les opérations combinées écrivent toujours les numéros.

Le compilateur génère l’implémentation des fonctions intrinsèques __ ; il n’est pas nécessaire de lier cgb_tile.c. Les chemins d’écriture ordinaires attendent l’accès VRAM, mais certains changent aussi l’activation des interruptions. Les formes _unsafe et _fast omettent ces attentes : utilisez-les seulement si l’appelant garantit une VRAM accessible en écriture, par exemple LCD éteint. Vérifiez les conditions d’interruption, de banque et de longueur de chaque entrée.

__cgb_safe_set_vbk

Sélectionne la banque VRAM de la CPU par VBK ($FF4F) sur CGB. Seul value & 1 est écrit : valeur paire pour la banque 0, impaire pour la banque 1. Affecte les accès CPU dans $8000–$9FFF, pas la banque de motifs désignée par les attributs de carte. La banque précédente n'est pas restaurée.

Syntaxe

c
void __cgb_safe_set_vbk(u8 value);

Paramètres

value

Sélectionne la banque VRAM de la CPU par VBK ($FF4F) sur CGB. Seul value & 1 est écrit : valeur paire pour la banque 0, impaire pour la banque 1. Affecte les accès CPU dans $8000–$9FFF, pas la banque de motifs désignée par les attributs de carte. La banque précédente n'est pas restaurée.

Valeur renvoyée

Aucune (void).

Conditions et limites

Les écritures de palette et de registres protégés n'écrivent rien en mode DMG. Safe désigne ici le contrôle du mode CGB, pas une attente d'accès PPU ni une section critique. Configurez avec le LCD éteint ou effectuez une mise à jour assez courte pendant une phase accessible. Aucune attente VBlank ni protection contre les interruptions n'est ajoutée.

Exemple d’utilisation

c
if (color_mode) {
    __cgb_safe_set_vbk(3); // Only bit zero is kept: CPU accesses now use bank 1.
    if ((P_VBK&1)!=1) failures++;
    __vram_copy(0x8010,shape1,16);
    __cgb_safe_set_vbk(2); // Bit zero is clear, so return to bank 0.
    if ((P_VBK&1)!=0) failures++;
}

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Ce programme montre comment les palettes BG et OBJ colorent indépendamment un même motif. Chaque triangle occupe 8 × 8 pixels, d'un seul pixel à gauche en haut jusqu'à une ligne complète en bas. Aux abscisses 16, 32, 48, 64, 80, 96, 112, la ligne BG à y = 24 est rouge, bleue, verte, rouge, bleue, verte, rouge ; la ligne OBJ à y = 64 est bleue, verte, rouge, bleue, verte, rouge, bleue. Les colonnes utilisent respectivement une couleur, les composantes, une plage indexée, une palette entière, une plage absolue, un registre canonique et son alias.

À y = 88, sept cases de 8 × 8 montrent : triangle rouge (x = 16), contour carré bleu d'un pixel provenant de la banque 1 (32), triangle vert réfléchi horizontalement (48), rouge réfléchi verticalement (64), bleu réfléchi sur les deux axes (80), triangle vert devant un sprite rouge visible dans les pixels de couleur zéro (96), puis sprite rouge plein devant le triangle BG, témoin sans priorité (112). Les coordonnées sont en pixels depuis le coin supérieur gauche. Le fond reste blanc et le texte noir.

FAILED CHECKS 000 confirme les masques RGB, les booléens (dont 2), la normalisation des indices, le franchissement de palettes, l'arrêt à l'emplacement 31, l'absence d'écriture pour pointeur nul ou longueur nulle et la relecture des couleurs LCD éteint. Quatre combinaisons sont testées : ROM mixte sur DMG/CGB, ROM DMG seule sur DMG, ROM CGB seule sur CGB. Sur DMG, CGB MODE vaut 0, les triangles sont noirs, banque et réflexions n'affectent pas l'image et les deux dernières cases du bas sont des carrés noirs. Sur CGB, CGB MODE vaut 1. Le matériel réel et les conflits d'accès LCD allumé ne sont pas testés.

__cgb_safe_set_vbk: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_vbk: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_vbk: --cgb=dmg / DMG
--cgb=dmg / DMG
__cgb_safe_set_vbk: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:28

Notes de déclaration (texte source)
Write the VRAM bank selection only on CGB. Safe here means hardware-mode guarded, not PPU-timed.
__settile

Écrit le numéro de tuile d’une case de la carte ; sélectionne une image existante sans modifier ses pixels.

La destination est toujours 0x9800 + 32*y + x, indépendamment des cartes de fond et de fenêtre sélectionnées par LCDC.

Attend si nécessaire que la VRAM soit accessible en écriture. LCD actif, le chemin masque les interruptions et peut les réactiver ensuite ; il ne garantit ni l’état d’interruption initial ni une fin dans un seul VBlank.

Syntaxe

c
void __settile(u8 x, u8 y, u8 tile);

Paramètres

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

tile

Numéro de tuile, de 0 à 255. Le bit 4 de LCDC détermine l’adressage de son image. Ce n’est pas une adresse d’octet en VRAM.

Valeur renvoyée

Aucune (void).

Conditions et limites

Les octets sont écrits dans la banque VRAM sélectionnée. Sur CGB, choisissez la banque 0 pour les numéros de tuile ; cet appel ne change pas de banque.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settile
// Write one tile-number byte to the tile map; this selects an existing tile image, not
// new pixel data. The destination is always `0x9800 + 32*y + x`, regardless of which
// maps LCDC selects for the background and window.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"

void main() {
    u8 failures;
    failures = 0;
    tile_color_example_begin();
    __settile(2, 3, 'A');
    if (tile_example_read(0x9862, 0) != 'A') { failures++; }
    if (tile_example_read(0x9C62, 0) != 0) { failures++; }
    tile_example_finish(failures, 0);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, les caractères de l’exemple et le texte des résultats s’affichent en bleu sur fond blanc. La préparation attribue le bleu à la couleur de tracé de la palette 0, sans modifier les attributs de la carte de tuiles. Sur DMG, les mêmes caractères s’affichent en noir sur fond blanc.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
A
__settile: DMG
DMG
__settile: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settile.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settile.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:31

Notes de déclaration (texte source)
Write a tile number to map 9800, waiting for VRAM access when necessary.
__settile_bulk

Copie count octets consécutifs de numéros de tuile de src vers l’adresse VRAM dest. Le compte est en octets, pas en images de tuile ; aucun saut de ligne n’est ajusté.

Attend si nécessaire que la VRAM soit accessible en écriture. LCD actif, le chemin masque les interruptions et peut les réactiver ensuite ; il ne garantit ni l’état d’interruption initial ni une fin dans un seul VBlank.

Syntaxe

c
void __settile_bulk(u16 dest, const u8* src, u8 count);

Paramètres

dest

Adresse du premier octet en VRAM. Pour une carte, utilisez 0x9800–0x9FFF et gardez tout le transfert dans la carte visée.

src

Octets contigus en RAM ou dans une banque ROM visible. Le flux de tuiles contient des numéros ; celui d’attributs contient les attributs CGB. La copie est immédiate et le pointeur n’est pas conservé.

count

Nombre d’octets, de 0 à 255. Zéro ne copie rien. La source doit contenir au moins autant d’octets lisibles.

Valeur renvoyée

Aucune (void).

Conditions et limites

Les octets sont écrits dans la banque VRAM sélectionnée. Sur CGB, choisissez la banque 0 pour les numéros de tuile ; cet appel ne change pas de banque.

Gardez les tampons source lisibles et leur banque ROM accessible pendant toute la copie. Aucun retour.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settile_bulk
// Copy `count` consecutive tile-number bytes from `src` to the VRAM byte address `dest`.
// The count is in bytes, not tile-image units; no row-stride adjustment is made. Waits
// for writable VRAM when necessary. The LCD-on path uses interrupt masking and can
// enable interrupts afterward; it is not a promise to preserve the caller's interrupt
// state or to finish within one VBlank.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
const u8 example_tiles[] = {'A','B','C'};
const u8 example_attrs[] = {1,2,3};

void main() {
    u8 failures;
    failures = 0;
    tile_color_example_begin();
    __settile_bulk(0x9862, example_tiles, 3);
    if (tile_example_read(0x9862, 0) != 'A') { failures++; }
    if (tile_example_read(0x9863, 0) != 'B') { failures++; }
    if (tile_example_read(0x9864, 0) != 'C') { failures++; }
    if (tile_example_read(0x9865, 0) != 0) { failures++; }
    tile_example_finish(failures, 0);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, les caractères de l’exemple et le texte des résultats s’affichent en bleu sur fond blanc. La préparation attribue le bleu à la couleur de tracé de la palette 0, sans modifier les attributs de la carte de tuiles. Sur DMG, les mêmes caractères s’affichent en noir sur fond blanc.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
ABC
__settile_bulk: DMG
DMG
__settile_bulk: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settile_bulk.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settile_bulk.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:48

Notes de déclaration (texte source)
Copy count sequential bytes to dest using the current VRAM bank and access-checked transfer.
__settile_bulk_fast

Copie count octets consécutifs de numéros de tuile de src vers l’adresse VRAM dest. Le compte est en octets, pas en images de tuile ; aucun saut de ligne n’est ajusté.

N’attend pas l’accès à la VRAM. À utiliser LCD éteint ou lorsque l’appelant garantit un créneau d’écriture assez long pour tout le transfert.

Syntaxe

c
void __settile_bulk_fast(u16 dest, const u8* src, u8 count);

Paramètres

dest

Adresse du premier octet en VRAM. Pour une carte, utilisez 0x9800–0x9FFF et gardez tout le transfert dans la carte visée.

src

Octets contigus en RAM ou dans une banque ROM visible. Le flux de tuiles contient des numéros ; celui d’attributs contient les attributs CGB. La copie est immédiate et le pointeur n’est pas conservé.

count

Nombre d’octets, de 0 à 255. Zéro ne copie rien. La source doit contenir au moins autant d’octets lisibles.

Valeur renvoyée

Aucune (void).

Conditions et limites

Les octets sont écrits dans la banque VRAM sélectionnée. Sur CGB, choisissez la banque 0 pour les numéros de tuile ; cet appel ne change pas de banque.

Gardez les tampons source lisibles et leur banque ROM accessible pendant toute la copie. Aucun retour.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settile_bulk_fast
// Copy `count` consecutive tile-number bytes from `src` to the VRAM byte address `dest`.
// The count is in bytes, not tile-image units; no row-stride adjustment is made. Does
// not wait for VRAM access. Call only while the LCD is off or within a caller-managed
// writable interval with enough time for the whole transfer.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
const u8 example_tiles[] = {'A','B','C'};
const u8 example_attrs[] = {1,2,3};

void main() {
    u8 failures;
    failures = 0;
    tile_color_example_begin();
    __settile_bulk_fast(0x9862, example_tiles, 3);
    if (tile_example_read(0x9862, 0) != 'A') { failures++; }
    if (tile_example_read(0x9863, 0) != 'B') { failures++; }
    if (tile_example_read(0x9864, 0) != 'C') { failures++; }
    if (tile_example_read(0x9865, 0) != 0) { failures++; }
    tile_example_finish(failures, 0);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, les caractères de l’exemple et le texte des résultats s’affichent en bleu sur fond blanc. La préparation attribue le bleu à la couleur de tracé de la palette 0, sans modifier les attributs de la carte de tuiles. Sur DMG, les mêmes caractères s’affichent en noir sur fond blanc.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
ABC
__settile_bulk_fast: DMG
DMG
__settile_bulk_fast: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settile_bulk_fast.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settile_bulk_fast.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:50

Notes de déclaration (texte source)
Copy count sequential bytes to dest without VRAM access waits; retain source-bank visibility.
__settile_unsafe

Écrit le numéro de tuile d’une case de la carte ; sélectionne une image existante sans modifier ses pixels.

La destination est toujours 0x9800 + 32*y + x, indépendamment des cartes de fond et de fenêtre sélectionnées par LCDC.

N’attend pas l’accès à la VRAM. À utiliser LCD éteint ou lorsque l’appelant garantit un créneau d’écriture assez long pour tout le transfert.

Syntaxe

c
void __settile_unsafe(u8 x, u8 y, u8 tile);

Paramètres

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

tile

Numéro de tuile, de 0 à 255. Le bit 4 de LCDC détermine l’adressage de son image. Ce n’est pas une adresse d’octet en VRAM.

Valeur renvoyée

Aucune (void).

Conditions et limites

Les octets sont écrits dans la banque VRAM sélectionnée. Sur CGB, choisissez la banque 0 pour les numéros de tuile ; cet appel ne change pas de banque.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settile_unsafe
// Write one tile-number byte to the tile map; this selects an existing tile image, not
// new pixel data. The destination is always `0x9800 + 32*y + x`, regardless of which
// maps LCDC selects for the background and window.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"

void main() {
    u8 failures;
    failures = 0;
    tile_color_example_begin();
    __settile_unsafe(2, 3, 'A');
    if (tile_example_read(0x9862, 0) != 'A') { failures++; }
    if (tile_example_read(0x9C62, 0) != 0) { failures++; }
    tile_example_finish(failures, 0);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, les caractères de l’exemple et le texte des résultats s’affichent en bleu sur fond blanc. La préparation attribue le bleu à la couleur de tracé de la palette 0, sans modifier les attributs de la carte de tuiles. Sur DMG, les mêmes caractères s’affichent en noir sur fond blanc.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
A
__settile_unsafe: DMG
DMG
__settile_unsafe: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settile_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settile_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:33

Notes de déclaration (texte source)
Write a tile number to map 9800 without waiting for writable VRAM.
__settileat

Écrit le numéro de tuile d’une case de la carte ; sélectionne une image existante sans modifier ses pixels.

base choisit la carte (0x9800 ou 0x9C00) ; l’adresse de l’octet est base + 32*y + x.

Attend si nécessaire que la VRAM soit accessible en écriture. LCD actif, le chemin masque les interruptions et peut les réactiver ensuite ; il ne garantit ni l’état d’interruption initial ni une fin dans un seul VBlank.

Syntaxe

c
void __settileat(u16 base, u8 x, u8 y, u8 tile);

Paramètres

base

base choisit la carte (0x9800 ou 0x9C00) ; l’adresse de l’octet est base + 32*y + x.

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

tile

Numéro de tuile, de 0 à 255. Le bit 4 de LCDC détermine l’adressage de son image. Ce n’est pas une adresse d’octet en VRAM.

Valeur renvoyée

Aucune (void).

Conditions et limites

Les octets sont écrits dans la banque VRAM sélectionnée. Sur CGB, choisissez la banque 0 pour les numéros de tuile ; cet appel ne change pas de banque.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settileat
// Write one tile-number byte to the tile map; this selects an existing tile image, not
// new pixel data. `base` explicitly selects the map (`0x9800` or `0x9C00`); the byte
// address is `base + 32*y + x`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"

void main() {
    u8 failures;
    failures = 0;
    tile_color_example_begin();
    __settileat(0x9C00, 2, 3, 'A');
    if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
    if (tile_example_read(0x9862, 0) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, les caractères de l’exemple et le texte des résultats s’affichent en bleu sur fond blanc. La préparation attribue le bleu à la couleur de tracé de la palette 0, sans modifier les attributs de la carte de tuiles. Sur DMG, les mêmes caractères s’affichent en noir sur fond blanc.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
A
__settileat: DMG
DMG
__settileat: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settileat.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settileat.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:35

Notes de déclaration (texte source)
Write at base+32*y+x with VRAM access checks; supply a valid tile-map base.
__settileat_unsafe

Écrit le numéro de tuile d’une case de la carte ; sélectionne une image existante sans modifier ses pixels.

base choisit la carte (0x9800 ou 0x9C00) ; l’adresse de l’octet est base + 32*y + x.

N’attend pas l’accès à la VRAM. À utiliser LCD éteint ou lorsque l’appelant garantit un créneau d’écriture assez long pour tout le transfert.

Syntaxe

c
void __settileat_unsafe(u16 base, u8 x, u8 y, u8 tile);

Paramètres

base

base choisit la carte (0x9800 ou 0x9C00) ; l’adresse de l’octet est base + 32*y + x.

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

tile

Numéro de tuile, de 0 à 255. Le bit 4 de LCDC détermine l’adressage de son image. Ce n’est pas une adresse d’octet en VRAM.

Valeur renvoyée

Aucune (void).

Conditions et limites

Les octets sont écrits dans la banque VRAM sélectionnée. Sur CGB, choisissez la banque 0 pour les numéros de tuile ; cet appel ne change pas de banque.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settileat_unsafe
// Write one tile-number byte to the tile map; this selects an existing tile image, not
// new pixel data. `base` explicitly selects the map (`0x9800` or `0x9C00`); the byte
// address is `base + 32*y + x`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"

void main() {
    u8 failures;
    failures = 0;
    tile_color_example_begin();
    __settileat_unsafe(0x9C00, 2, 3, 'A');
    if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
    if (tile_example_read(0x9862, 0) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, les caractères de l’exemple et le texte des résultats s’affichent en bleu sur fond blanc. La préparation attribue le bleu à la couleur de tracé de la palette 0, sans modifier les attributs de la carte de tuiles. Sur DMG, les mêmes caractères s’affichent en noir sur fond blanc.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
A
__settileat_unsafe: DMG
DMG
__settileat_unsafe: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settileat_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settileat_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:37

Notes de déclaration (texte source)
Use an explicit map base without waiting for VRAM access.
__settileatattr

Modifie uniquement l’octet d’attributs CGB, sans changer le numéro de tuile. Ses bits choisissent palette, banque d’image, retournements et priorité.

base choisit la carte (0x9800 ou 0x9C00) ; l’adresse de l’octet est base + 32*y + x.

Attend si nécessaire que la VRAM soit accessible en écriture. LCD actif, le chemin masque les interruptions et peut les réactiver ensuite ; il ne garantit ni l’état d’interruption initial ni une fin dans un seul VBlank.

Syntaxe

c
void __settileatattr(u16 base, u8 x, u8 y, u8 attr);

Paramètres

base

base choisit la carte (0x9800 ou 0x9C00) ; l’adresse de l’octet est base + 32*y + x.

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

attr

Octet d’attributs CGB : bits 0–2, palette 0–7 ; bit 3, banque d’image ; bits 5 et 6, retournement horizontal et vertical ; bit 7, priorité du fond ou de la fenêtre. Laissez à zéro le bit 4 inutilisé.

Valeur renvoyée

Aucune (void).

Conditions et limites

Sur CGB, écrit dans la banque VRAM 1 puis laisse VBK=0. Sur DMG, aucun attribut n’est écrit.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settileatattr
// Write only the CGB attribute byte; leave the tile number unchanged. Attribute bits
// select palette, tile-data bank, flips and priority. `base` explicitly selects the map
// (`0x9800` or `0x9C00`); the byte address is `base + 32*y + x`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"

void main() {
    u8 failures;
    failures = 0;
    tile_example_begin();
    __settileat(0x9C00, 2, 3, 'A');
    __settileatattr(0x9C00, 2, 3, KQ_CGB_ATTR(2, 0, 0, 0, 0));
    if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
    if (tile_example_read(0x9862, 0) != 0) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 2) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, le A est bleu : l’attribut 2 sélectionne la palette bleue préparée par l’exemple. Sur DMG, le même caractère est noir, car les attributs CGB ne s’appliquent pas.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
A
__settileatattr: DMG
DMG
__settileatattr: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settileatattr.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settileatattr.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:62

Notes de déclaration (texte source)
Write attributes in bank one at the explicit map base on CGB, then leave VBK zero; skip on DMG. Use the access-checked write path.
__settileatattr_unsafe

Modifie uniquement l’octet d’attributs CGB, sans changer le numéro de tuile. Ses bits choisissent palette, banque d’image, retournements et priorité.

base choisit la carte (0x9800 ou 0x9C00) ; l’adresse de l’octet est base + 32*y + x.

N’attend pas l’accès à la VRAM. À utiliser LCD éteint ou lorsque l’appelant garantit un créneau d’écriture assez long pour tout le transfert.

Syntaxe

c
void __settileatattr_unsafe(u16 base, u8 x, u8 y, u8 attr);

Paramètres

base

base choisit la carte (0x9800 ou 0x9C00) ; l’adresse de l’octet est base + 32*y + x.

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

attr

Octet d’attributs CGB : bits 0–2, palette 0–7 ; bit 3, banque d’image ; bits 5 et 6, retournement horizontal et vertical ; bit 7, priorité du fond ou de la fenêtre. Laissez à zéro le bit 4 inutilisé.

Valeur renvoyée

Aucune (void).

Conditions et limites

Sur CGB, écrit dans la banque VRAM 1 puis laisse VBK=0. Sur DMG, aucun attribut n’est écrit.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settileatattr_unsafe
// Write only the CGB attribute byte; leave the tile number unchanged. Attribute bits
// select palette, tile-data bank, flips and priority. `base` explicitly selects the map
// (`0x9800` or `0x9C00`); the byte address is `base + 32*y + x`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"

void main() {
    u8 failures;
    failures = 0;
    tile_example_begin();
    __settileat(0x9C00, 2, 3, 'A');
    __settileatattr_unsafe(0x9C00, 2, 3, KQ_CGB_ATTR(2, 0, 0, 0, 0));
    if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
    if (tile_example_read(0x9862, 0) != 0) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 2) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, le A est bleu : l’attribut 2 sélectionne la palette bleue préparée par l’exemple. Sur DMG, le même caractère est noir, car les attributs CGB ne s’appliquent pas.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
A
__settileatattr_unsafe: DMG
DMG
__settileatattr_unsafe: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settileatattr_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settileatattr_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:64

Notes de déclaration (texte source)
Write attributes in bank one at the explicit map base on CGB, then leave VBK zero; skip on DMG. No VRAM access waits are inserted.
__settileatcgb

Écrit le numéro de tuile et l’attribut CGB. Sur DMG, seul le numéro est écrit. Sur CGB, sélectionnez d’abord la banque 0 ; les attributs utilisent la banque 1 et l’appel se termine avec VBK=0.

base choisit la carte (0x9800 ou 0x9C00) ; l’adresse de l’octet est base + 32*y + x.

Attend si nécessaire que la VRAM soit accessible en écriture. LCD actif, le chemin masque les interruptions et peut les réactiver ensuite ; il ne garantit ni l’état d’interruption initial ni une fin dans un seul VBlank.

Syntaxe

c
void __settileatcgb(u16 base, u8 x, u8 y, u8 tile, u8 attr);

Paramètres

base

base choisit la carte (0x9800 ou 0x9C00) ; l’adresse de l’octet est base + 32*y + x.

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

tile

Numéro de tuile, de 0 à 255. Le bit 4 de LCDC détermine l’adressage de son image. Ce n’est pas une adresse d’octet en VRAM.

attr

Octet d’attributs CGB : bits 0–2, palette 0–7 ; bit 3, banque d’image ; bits 5 et 6, retournement horizontal et vertical ; bit 7, priorité du fond ou de la fenêtre. Laissez à zéro le bit 4 inutilisé.

Valeur renvoyée

Aucune (void).

Conditions et limites

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settileatcgb
// Write a tile number and its CGB attribute. On DMG, only the tile-number write is
// performed. On CGB, enter with VRAM bank 0 selected; attributes use bank 1 and the call
// finishes with `VBK=0`. `base` explicitly selects the map (`0x9800` or `0x9C00`); the
// byte address is `base + 32*y + x`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"

void main() {
    u8 failures;
    failures = 0;
    tile_example_begin();
    __settileatcgb(0x9C00, 2, 3, 'A', KQ_CGB_ATTR(2, 0, 0, 0, 0));
    if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
    if (tile_example_read(0x9862, 0) != 0) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 2) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, le A est bleu : l’attribut 2 sélectionne la palette bleue préparée par l’exemple. Sur DMG, le même caractère est noir, car les attributs CGB ne s’appliquent pas.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
A
__settileatcgb: DMG
DMG
__settileatcgb: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settileatcgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settileatcgb.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:66

Notes de déclaration (texte source)
Write tile and CGB attributes at the explicit map base; DMG receives only the tile write. Use the access-checked write path.
__settileatcgb_unsafe

Écrit le numéro de tuile et l’attribut CGB. Sur DMG, seul le numéro est écrit. Sur CGB, sélectionnez d’abord la banque 0 ; les attributs utilisent la banque 1 et l’appel se termine avec VBK=0.

base choisit la carte (0x9800 ou 0x9C00) ; l’adresse de l’octet est base + 32*y + x.

N’attend pas l’accès à la VRAM. À utiliser LCD éteint ou lorsque l’appelant garantit un créneau d’écriture assez long pour tout le transfert.

Syntaxe

c
void __settileatcgb_unsafe(u16 base, u8 x, u8 y, u8 tile, u8 attr);

Paramètres

base

base choisit la carte (0x9800 ou 0x9C00) ; l’adresse de l’octet est base + 32*y + x.

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

tile

Numéro de tuile, de 0 à 255. Le bit 4 de LCDC détermine l’adressage de son image. Ce n’est pas une adresse d’octet en VRAM.

attr

Octet d’attributs CGB : bits 0–2, palette 0–7 ; bit 3, banque d’image ; bits 5 et 6, retournement horizontal et vertical ; bit 7, priorité du fond ou de la fenêtre. Laissez à zéro le bit 4 inutilisé.

Valeur renvoyée

Aucune (void).

Conditions et limites

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settileatcgb_unsafe
// Write a tile number and its CGB attribute. On DMG, only the tile-number write is
// performed. On CGB, enter with VRAM bank 0 selected; attributes use bank 1 and the call
// finishes with `VBK=0`. `base` explicitly selects the map (`0x9800` or `0x9C00`); the
// byte address is `base + 32*y + x`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"

void main() {
    u8 failures;
    failures = 0;
    tile_example_begin();
    __settileatcgb_unsafe(0x9C00, 2, 3, 'A', KQ_CGB_ATTR(2, 0, 0, 0, 0));
    if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
    if (tile_example_read(0x9862, 0) != 0) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 2) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, le A est bleu : l’attribut 2 sélectionne la palette bleue préparée par l’exemple. Sur DMG, le même caractère est noir, car les attributs CGB ne s’appliquent pas.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
A
__settileatcgb_unsafe: DMG
DMG
__settileatcgb_unsafe: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settileatcgb_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settileatcgb_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:68

Notes de déclaration (texte source)
Write tile and CGB attributes at the explicit map base; DMG receives only the tile write. No VRAM access waits are inserted.
__settileattr

Modifie uniquement l’octet d’attributs CGB, sans changer le numéro de tuile. Ses bits choisissent palette, banque d’image, retournements et priorité.

La destination est toujours 0x9800 + 32*y + x, indépendamment des cartes de fond et de fenêtre sélectionnées par LCDC.

Attend si nécessaire que la VRAM soit accessible en écriture. LCD actif, le chemin masque les interruptions et peut les réactiver ensuite ; il ne garantit ni l’état d’interruption initial ni une fin dans un seul VBlank.

Syntaxe

c
void __settileattr(u8 x, u8 y, u8 attr);

Paramètres

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

attr

Octet d’attributs CGB : bits 0–2, palette 0–7 ; bit 3, banque d’image ; bits 5 et 6, retournement horizontal et vertical ; bit 7, priorité du fond ou de la fenêtre. Laissez à zéro le bit 4 inutilisé.

Valeur renvoyée

Aucune (void).

Conditions et limites

Sur CGB, écrit dans la banque VRAM 1 puis laisse VBK=0. Sur DMG, aucun attribut n’est écrit.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settileattr
// Write only the CGB attribute byte; leave the tile number unchanged. Attribute bits
// select palette, tile-data bank, flips and priority. The destination is always `0x9800
// + 32*y + x`, regardless of which maps LCDC selects for the background and window.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"

void main() {
    u8 failures;
    failures = 0;
    tile_example_begin();
    __settileat(0x9800, 2, 3, 'A');
    __settileattr(2, 3, KQ_CGB_ATTR(2, 0, 0, 0, 0));
    if (tile_example_read(0x9862, 0) != 'A') { failures++; }
    if (tile_example_read(0x9C62, 0) != 0) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 2) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 0) { failures++; }
    tile_example_finish(failures, 0);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, le A est bleu : l’attribut 2 sélectionne la palette bleue préparée par l’exemple. Sur DMG, le même caractère est noir, car les attributs CGB ne s’appliquent pas.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
A
__settileattr: DMG
DMG
__settileattr: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settileattr.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settileattr.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:53

Notes de déclaration (texte source)
Write attributes in bank one at map 9800 on CGB, then leave VBK zero; skip on DMG. Use the access-checked write path.
__settileattr_bulk

Copie count attributs CGB consécutifs de src vers dest dans la banque VRAM 1. Les numéros de tuile restent inchangés ; dest est une adresse, pas un couple (x,y).

Attend si nécessaire que la VRAM soit accessible en écriture. LCD actif, le chemin masque les interruptions et peut les réactiver ensuite ; il ne garantit ni l’état d’interruption initial ni une fin dans un seul VBlank.

Syntaxe

c
void __settileattr_bulk(u16 dest, const u8* src, u8 count);

Paramètres

dest

Adresse du premier octet en VRAM. Pour une carte, utilisez 0x9800–0x9FFF et gardez tout le transfert dans la carte visée.

src

Octets contigus en RAM ou dans une banque ROM visible. Le flux de tuiles contient des numéros ; celui d’attributs contient les attributs CGB. La copie est immédiate et le pointeur n’est pas conservé.

count

Nombre d’octets, de 0 à 255. Zéro ne copie rien. La source doit contenir au moins autant d’octets lisibles.

Valeur renvoyée

Aucune (void).

Conditions et limites

Sur CGB, écrit dans la banque VRAM 1 puis laisse VBK=0. Sur DMG, aucun attribut n’est écrit.

Gardez les tampons source lisibles et leur banque ROM accessible pendant toute la copie. Aucun retour.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settileattr_bulk
// Copy `count` consecutive CGB attribute bytes from `src` to `dest` in VRAM bank 1. Tile
// numbers are untouched; the destination is a byte address, not an `(x,y)` pair. Waits
// for writable VRAM when necessary. The LCD-on path uses interrupt masking and can
// enable interrupts afterward; it is not a promise to preserve the caller's interrupt
// state or to finish within one VBlank.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
const u8 example_tiles[] = {'A','B','C'};
const u8 example_attrs[] = {1,2,3};

void main() {
    u8 failures;
    failures = 0;
    tile_example_begin();
    __settile_bulk(0x9862, example_tiles, 3);
    __settileattr_bulk(0x9862, example_attrs, 3);
    if (tile_example_read(0x9862, 0) != 'A') { failures++; }
    if (tile_example_read(0x9863, 0) != 'B') { failures++; }
    if (tile_example_read(0x9864, 0) != 'C') { failures++; }
    if (tile_example_read(0x9865, 0) != 0) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 1) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9864, 1) != 3) { failures++; }
    tile_example_finish(failures, 0);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, A, B et C sont respectivement rouge, bleu et vert : les attributs 1,2,3 sélectionnent trois palettes distinctes. Sur DMG, les trois lettres sont noires.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
ABC
__settileattr_bulk: DMG
DMG
__settileattr_bulk: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settileattr_bulk.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settileattr_bulk.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:89

Notes de déclaration (texte source)
Copy count attributes to dest in bank one on CGB, then leave VBK zero; skip on DMG. Transfers use access checks.
__settileattr_bulk_fast

Copie count attributs CGB consécutifs de src vers dest dans la banque VRAM 1. Les numéros de tuile restent inchangés ; dest est une adresse, pas un couple (x,y).

N’attend pas l’accès à la VRAM. À utiliser LCD éteint ou lorsque l’appelant garantit un créneau d’écriture assez long pour tout le transfert.

Syntaxe

c
void __settileattr_bulk_fast(u16 dest, const u8* src, u8 count);

Paramètres

dest

Adresse du premier octet en VRAM. Pour une carte, utilisez 0x9800–0x9FFF et gardez tout le transfert dans la carte visée.

src

Octets contigus en RAM ou dans une banque ROM visible. Le flux de tuiles contient des numéros ; celui d’attributs contient les attributs CGB. La copie est immédiate et le pointeur n’est pas conservé.

count

Nombre d’octets, de 0 à 255. Zéro ne copie rien. La source doit contenir au moins autant d’octets lisibles.

Valeur renvoyée

Aucune (void).

Conditions et limites

Sur CGB, écrit dans la banque VRAM 1 puis laisse VBK=0. Sur DMG, aucun attribut n’est écrit.

Gardez les tampons source lisibles et leur banque ROM accessible pendant toute la copie. Aucun retour.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settileattr_bulk_fast
// Copy `count` consecutive CGB attribute bytes from `src` to `dest` in VRAM bank 1. Tile
// numbers are untouched; the destination is a byte address, not an `(x,y)` pair. Does
// not wait for VRAM access. Call only while the LCD is off or within a caller-managed
// writable interval with enough time for the whole transfer.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
const u8 example_tiles[] = {'A','B','C'};
const u8 example_attrs[] = {1,2,3};

void main() {
    u8 failures;
    failures = 0;
    tile_example_begin();
    __settile_bulk(0x9862, example_tiles, 3);
    __settileattr_bulk_fast(0x9862, example_attrs, 3);
    if (tile_example_read(0x9862, 0) != 'A') { failures++; }
    if (tile_example_read(0x9863, 0) != 'B') { failures++; }
    if (tile_example_read(0x9864, 0) != 'C') { failures++; }
    if (tile_example_read(0x9865, 0) != 0) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 1) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9864, 1) != 3) { failures++; }
    tile_example_finish(failures, 0);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, A, B et C sont respectivement rouge, bleu et vert : les attributs 1,2,3 sélectionnent trois palettes distinctes. Sur DMG, les trois lettres sont noires.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
ABC
__settileattr_bulk_fast: DMG
DMG
__settileattr_bulk_fast: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settileattr_bulk_fast.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settileattr_bulk_fast.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:91

Notes de déclaration (texte source)
Copy count attributes to dest in bank one on CGB, then leave VBK zero; skip on DMG. The caller supplies writable VRAM timing.
__settileattr_unsafe

Modifie uniquement l’octet d’attributs CGB, sans changer le numéro de tuile. Ses bits choisissent palette, banque d’image, retournements et priorité.

La destination est toujours 0x9800 + 32*y + x, indépendamment des cartes de fond et de fenêtre sélectionnées par LCDC.

N’attend pas l’accès à la VRAM. À utiliser LCD éteint ou lorsque l’appelant garantit un créneau d’écriture assez long pour tout le transfert.

Syntaxe

c
void __settileattr_unsafe(u8 x, u8 y, u8 attr);

Paramètres

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

attr

Octet d’attributs CGB : bits 0–2, palette 0–7 ; bit 3, banque d’image ; bits 5 et 6, retournement horizontal et vertical ; bit 7, priorité du fond ou de la fenêtre. Laissez à zéro le bit 4 inutilisé.

Valeur renvoyée

Aucune (void).

Conditions et limites

Sur CGB, écrit dans la banque VRAM 1 puis laisse VBK=0. Sur DMG, aucun attribut n’est écrit.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settileattr_unsafe
// Write only the CGB attribute byte; leave the tile number unchanged. Attribute bits
// select palette, tile-data bank, flips and priority. The destination is always `0x9800
// + 32*y + x`, regardless of which maps LCDC selects for the background and window.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"

void main() {
    u8 failures;
    failures = 0;
    tile_example_begin();
    __settileat(0x9800, 2, 3, 'A');
    __settileattr_unsafe(2, 3, KQ_CGB_ATTR(2, 0, 0, 0, 0));
    if (tile_example_read(0x9862, 0) != 'A') { failures++; }
    if (tile_example_read(0x9C62, 0) != 0) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 2) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 0) { failures++; }
    tile_example_finish(failures, 0);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, le A est bleu : l’attribut 2 sélectionne la palette bleue préparée par l’exemple. Sur DMG, le même caractère est noir, car les attributs CGB ne s’appliquent pas.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
A
__settileattr_unsafe: DMG
DMG
__settileattr_unsafe: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settileattr_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settileattr_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:55

Notes de déclaration (texte source)
Write attributes in bank one at map 9800 on CGB, then leave VBK zero; skip on DMG. No VRAM access waits are inserted.
__settilebg

Écrit le numéro de tuile d’une case de la carte ; sélectionne une image existante sans modifier ses pixels.

Le bit 3 de LCDC choisit la carte de fond : zéro donne 0x9800, un donne 0x9C00. On ajoute 32*y + x à cette base.

Attend si nécessaire que la VRAM soit accessible en écriture. LCD actif, le chemin masque les interruptions et peut les réactiver ensuite ; il ne garantit ni l’état d’interruption initial ni une fin dans un seul VBlank.

Syntaxe

c
void __settilebg(u8 x, u8 y, u8 tile);

Paramètres

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

tile

Numéro de tuile, de 0 à 255. Le bit 4 de LCDC détermine l’adressage de son image. Ce n’est pas une adresse d’octet en VRAM.

Valeur renvoyée

Aucune (void).

Conditions et limites

Les octets sont écrits dans la banque VRAM sélectionnée. Sur CGB, choisissez la banque 0 pour les numéros de tuile ; cet appel ne change pas de banque.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settilebg
// Write one tile-number byte to the tile map; this selects an existing tile image, not
// new pixel data. LCDC bit 3 selects the background map: zero uses `0x9800`, one uses
// `0x9C00`. The destination is that base plus `32*y + x`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"

void main() {
    u8 failures;
    failures = 0;
    tile_color_example_begin();
    __settilebg(2, 3, 'A');
    if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
    if (tile_example_read(0x9862, 0) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, les caractères de l’exemple et le texte des résultats s’affichent en bleu sur fond blanc. La préparation attribue le bleu à la couleur de tracé de la palette 0, sans modifier les attributs de la carte de tuiles. Sur DMG, les mêmes caractères s’affichent en noir sur fond blanc.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
A
__settilebg: DMG
DMG
__settilebg: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilebg.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilebg.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:43

Notes de déclaration (texte source)
Select the background map from LCDC bit 3, then write with access checks.
__settilebg_unsafe

Écrit le numéro de tuile d’une case de la carte ; sélectionne une image existante sans modifier ses pixels.

Le bit 3 de LCDC choisit la carte de fond : zéro donne 0x9800, un donne 0x9C00. On ajoute 32*y + x à cette base.

N’attend pas l’accès à la VRAM. À utiliser LCD éteint ou lorsque l’appelant garantit un créneau d’écriture assez long pour tout le transfert.

Syntaxe

c
void __settilebg_unsafe(u8 x, u8 y, u8 tile);

Paramètres

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

tile

Numéro de tuile, de 0 à 255. Le bit 4 de LCDC détermine l’adressage de son image. Ce n’est pas une adresse d’octet en VRAM.

Valeur renvoyée

Aucune (void).

Conditions et limites

Les octets sont écrits dans la banque VRAM sélectionnée. Sur CGB, choisissez la banque 0 pour les numéros de tuile ; cet appel ne change pas de banque.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settilebg_unsafe
// Write one tile-number byte to the tile map; this selects an existing tile image, not
// new pixel data. LCDC bit 3 selects the background map: zero uses `0x9800`, one uses
// `0x9C00`. The destination is that base plus `32*y + x`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"

void main() {
    u8 failures;
    failures = 0;
    tile_color_example_begin();
    __settilebg_unsafe(2, 3, 'A');
    if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
    if (tile_example_read(0x9862, 0) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, les caractères de l’exemple et le texte des résultats s’affichent en bleu sur fond blanc. La préparation attribue le bleu à la couleur de tracé de la palette 0, sans modifier les attributs de la carte de tuiles. Sur DMG, les mêmes caractères s’affichent en noir sur fond blanc.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
A
__settilebg_unsafe: DMG
DMG
__settilebg_unsafe: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilebg_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilebg_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:45

Notes de déclaration (texte source)
Select the LCDC background map and write without access waits.
__settilebgattr

Modifie uniquement l’octet d’attributs CGB, sans changer le numéro de tuile. Ses bits choisissent palette, banque d’image, retournements et priorité.

Le bit 3 de LCDC choisit la carte de fond : zéro donne 0x9800, un donne 0x9C00. On ajoute 32*y + x à cette base.

Attend si nécessaire que la VRAM soit accessible en écriture. LCD actif, le chemin masque les interruptions et peut les réactiver ensuite ; il ne garantit ni l’état d’interruption initial ni une fin dans un seul VBlank.

Syntaxe

c
void __settilebgattr(u8 x, u8 y, u8 attr);

Paramètres

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

attr

Octet d’attributs CGB : bits 0–2, palette 0–7 ; bit 3, banque d’image ; bits 5 et 6, retournement horizontal et vertical ; bit 7, priorité du fond ou de la fenêtre. Laissez à zéro le bit 4 inutilisé.

Valeur renvoyée

Aucune (void).

Conditions et limites

Sur CGB, écrit dans la banque VRAM 1 puis laisse VBK=0. Sur DMG, aucun attribut n’est écrit.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settilebgattr
// Write only the CGB attribute byte; leave the tile number unchanged. Attribute bits
// select palette, tile-data bank, flips and priority. LCDC bit 3 selects the background
// map: zero uses `0x9800`, one uses `0x9C00`. The destination is that base plus `32*y +
// x`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"

void main() {
    u8 failures;
    failures = 0;
    tile_example_begin();
    __settileat(0x9C00, 2, 3, 'A');
    __settilebgattr(2, 3, KQ_CGB_ATTR(2, 0, 0, 0, 0));
    if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
    if (tile_example_read(0x9862, 0) != 0) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 2) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, le A est bleu : l’attribut 2 sélectionne la palette bleue préparée par l’exemple. Sur DMG, le même caractère est noir, car les attributs CGB ne s’appliquent pas.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
A
__settilebgattr: DMG
DMG
__settilebgattr: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilebgattr.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilebgattr.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:80

Notes de déclaration (texte source)
Write attributes in bank one at the LCDC background map on CGB, then leave VBK zero; skip on DMG. Use the access-checked write path.
__settilebgattr_unsafe

Modifie uniquement l’octet d’attributs CGB, sans changer le numéro de tuile. Ses bits choisissent palette, banque d’image, retournements et priorité.

Le bit 3 de LCDC choisit la carte de fond : zéro donne 0x9800, un donne 0x9C00. On ajoute 32*y + x à cette base.

N’attend pas l’accès à la VRAM. À utiliser LCD éteint ou lorsque l’appelant garantit un créneau d’écriture assez long pour tout le transfert.

Syntaxe

c
void __settilebgattr_unsafe(u8 x, u8 y, u8 attr);

Paramètres

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

attr

Octet d’attributs CGB : bits 0–2, palette 0–7 ; bit 3, banque d’image ; bits 5 et 6, retournement horizontal et vertical ; bit 7, priorité du fond ou de la fenêtre. Laissez à zéro le bit 4 inutilisé.

Valeur renvoyée

Aucune (void).

Conditions et limites

Sur CGB, écrit dans la banque VRAM 1 puis laisse VBK=0. Sur DMG, aucun attribut n’est écrit.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settilebgattr_unsafe
// Write only the CGB attribute byte; leave the tile number unchanged. Attribute bits
// select palette, tile-data bank, flips and priority. LCDC bit 3 selects the background
// map: zero uses `0x9800`, one uses `0x9C00`. The destination is that base plus `32*y +
// x`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"

void main() {
    u8 failures;
    failures = 0;
    tile_example_begin();
    __settileat(0x9C00, 2, 3, 'A');
    __settilebgattr_unsafe(2, 3, KQ_CGB_ATTR(2, 0, 0, 0, 0));
    if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
    if (tile_example_read(0x9862, 0) != 0) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 2) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, le A est bleu : l’attribut 2 sélectionne la palette bleue préparée par l’exemple. Sur DMG, le même caractère est noir, car les attributs CGB ne s’appliquent pas.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
A
__settilebgattr_unsafe: DMG
DMG
__settilebgattr_unsafe: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilebgattr_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilebgattr_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:82

Notes de déclaration (texte source)
Write attributes in bank one at the LCDC background map on CGB, then leave VBK zero; skip on DMG. No VRAM access waits are inserted.
__settilebgcgb

Écrit le numéro de tuile et l’attribut CGB. Sur DMG, seul le numéro est écrit. Sur CGB, sélectionnez d’abord la banque 0 ; les attributs utilisent la banque 1 et l’appel se termine avec VBK=0.

Le bit 3 de LCDC choisit la carte de fond : zéro donne 0x9800, un donne 0x9C00. On ajoute 32*y + x à cette base.

Attend si nécessaire que la VRAM soit accessible en écriture. LCD actif, le chemin masque les interruptions et peut les réactiver ensuite ; il ne garantit ni l’état d’interruption initial ni une fin dans un seul VBlank.

Syntaxe

c
void __settilebgcgb(u8 x, u8 y, u8 tile, u8 attr);

Paramètres

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

tile

Numéro de tuile, de 0 à 255. Le bit 4 de LCDC détermine l’adressage de son image. Ce n’est pas une adresse d’octet en VRAM.

attr

Octet d’attributs CGB : bits 0–2, palette 0–7 ; bit 3, banque d’image ; bits 5 et 6, retournement horizontal et vertical ; bit 7, priorité du fond ou de la fenêtre. Laissez à zéro le bit 4 inutilisé.

Valeur renvoyée

Aucune (void).

Conditions et limites

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settilebgcgb
// Write a tile number and its CGB attribute. On DMG, only the tile-number write is
// performed. On CGB, enter with VRAM bank 0 selected; attributes use bank 1 and the call
// finishes with `VBK=0`. LCDC bit 3 selects the background map: zero uses `0x9800`, one
// uses `0x9C00`. The destination is that base plus `32*y + x`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"

void main() {
    u8 failures;
    failures = 0;
    tile_example_begin();
    __settilebgcgb(2, 3, 'A', KQ_CGB_ATTR(2, 0, 0, 0, 0));
    if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
    if (tile_example_read(0x9862, 0) != 0) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 2) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, le A est bleu : l’attribut 2 sélectionne la palette bleue préparée par l’exemple. Sur DMG, le même caractère est noir, car les attributs CGB ne s’appliquent pas.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
A
__settilebgcgb: DMG
DMG
__settilebgcgb: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilebgcgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilebgcgb.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:84

Notes de déclaration (texte source)
Write tile and CGB attributes at the LCDC background map; DMG receives only the tile write. Use the access-checked write path.
__settilebgcgb_unsafe

Écrit le numéro de tuile et l’attribut CGB. Sur DMG, seul le numéro est écrit. Sur CGB, sélectionnez d’abord la banque 0 ; les attributs utilisent la banque 1 et l’appel se termine avec VBK=0.

Le bit 3 de LCDC choisit la carte de fond : zéro donne 0x9800, un donne 0x9C00. On ajoute 32*y + x à cette base.

N’attend pas l’accès à la VRAM. À utiliser LCD éteint ou lorsque l’appelant garantit un créneau d’écriture assez long pour tout le transfert.

Syntaxe

c
void __settilebgcgb_unsafe(u8 x, u8 y, u8 tile, u8 attr);

Paramètres

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

tile

Numéro de tuile, de 0 à 255. Le bit 4 de LCDC détermine l’adressage de son image. Ce n’est pas une adresse d’octet en VRAM.

attr

Octet d’attributs CGB : bits 0–2, palette 0–7 ; bit 3, banque d’image ; bits 5 et 6, retournement horizontal et vertical ; bit 7, priorité du fond ou de la fenêtre. Laissez à zéro le bit 4 inutilisé.

Valeur renvoyée

Aucune (void).

Conditions et limites

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settilebgcgb_unsafe
// Write a tile number and its CGB attribute. On DMG, only the tile-number write is
// performed. On CGB, enter with VRAM bank 0 selected; attributes use bank 1 and the call
// finishes with `VBK=0`. LCDC bit 3 selects the background map: zero uses `0x9800`, one
// uses `0x9C00`. The destination is that base plus `32*y + x`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"

void main() {
    u8 failures;
    failures = 0;
    tile_example_begin();
    __settilebgcgb_unsafe(2, 3, 'A', KQ_CGB_ATTR(2, 0, 0, 0, 0));
    if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
    if (tile_example_read(0x9862, 0) != 0) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 2) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, le A est bleu : l’attribut 2 sélectionne la palette bleue préparée par l’exemple. Sur DMG, le même caractère est noir, car les attributs CGB ne s’appliquent pas.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
A
__settilebgcgb_unsafe: DMG
DMG
__settilebgcgb_unsafe: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilebgcgb_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilebgcgb_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:86

Notes de déclaration (texte source)
Write tile and CGB attributes at the LCDC background map; DMG receives only the tile write. No VRAM access waits are inserted.
__settilecgb

Écrit le numéro de tuile et l’attribut CGB. Sur DMG, seul le numéro est écrit. Sur CGB, sélectionnez d’abord la banque 0 ; les attributs utilisent la banque 1 et l’appel se termine avec VBK=0.

La destination est toujours 0x9800 + 32*y + x, indépendamment des cartes de fond et de fenêtre sélectionnées par LCDC.

Attend si nécessaire que la VRAM soit accessible en écriture. LCD actif, le chemin masque les interruptions et peut les réactiver ensuite ; il ne garantit ni l’état d’interruption initial ni une fin dans un seul VBlank.

Syntaxe

c
void __settilecgb(u8 x, u8 y, u8 tile, u8 attr);

Paramètres

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

tile

Numéro de tuile, de 0 à 255. Le bit 4 de LCDC détermine l’adressage de son image. Ce n’est pas une adresse d’octet en VRAM.

attr

Octet d’attributs CGB : bits 0–2, palette 0–7 ; bit 3, banque d’image ; bits 5 et 6, retournement horizontal et vertical ; bit 7, priorité du fond ou de la fenêtre. Laissez à zéro le bit 4 inutilisé.

Valeur renvoyée

Aucune (void).

Conditions et limites

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settilecgb
// Write a tile number and its CGB attribute. On DMG, only the tile-number write is
// performed. On CGB, enter with VRAM bank 0 selected; attributes use bank 1 and the call
// finishes with `VBK=0`. The destination is always `0x9800 + 32*y + x`, regardless of
// which maps LCDC selects for the background and window.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"

void main() {
    u8 failures;
    failures = 0;
    tile_example_begin();
    __settilecgb(2, 3, 'A', KQ_CGB_ATTR(2, 0, 0, 0, 0));
    if (tile_example_read(0x9862, 0) != 'A') { failures++; }
    if (tile_example_read(0x9C62, 0) != 0) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 2) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 0) { failures++; }
    tile_example_finish(failures, 0);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, le A est bleu : l’attribut 2 sélectionne la palette bleue préparée par l’exemple. Sur DMG, le même caractère est noir, car les attributs CGB ne s’appliquent pas.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
A
__settilecgb: DMG
DMG
__settilecgb: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilecgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilecgb.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:57

Notes de déclaration (texte source)
Write tile and CGB attributes at map 9800; DMG receives only the tile write. Use the access-checked write path.
__settilecgb_bulk

Copie count numéros depuis tile_src, puis count attributs depuis attr_src, aux mêmes adresses à partir de dest. Les tableaux sont séparés, non entrelacés.

Attend si nécessaire que la VRAM soit accessible en écriture. LCD actif, le chemin masque les interruptions et peut les réactiver ensuite ; il ne garantit ni l’état d’interruption initial ni une fin dans un seul VBlank.

Syntaxe

c
void __settilecgb_bulk(u16 dest, const u8* tile_src, const u8* attr_src, u8 count);

Paramètres

dest

Adresse du premier octet en VRAM. Pour une carte, utilisez 0x9800–0x9FFF et gardez tout le transfert dans la carte visée.

tile_src, attr_src

Octets contigus en RAM ou dans une banque ROM visible. Le flux de tuiles contient des numéros ; celui d’attributs contient les attributs CGB. La copie est immédiate et le pointeur n’est pas conservé.

count

Nombre d’octets, de 0 à 255. Zéro ne copie rien. La source doit contenir au moins autant d’octets lisibles.

Valeur renvoyée

Aucune (void).

Conditions et limites

Écrit le numéro de tuile et l’attribut CGB. Sur DMG, seul le numéro est écrit. Sur CGB, sélectionnez d’abord la banque 0 ; les attributs utilisent la banque 1 et l’appel se termine avec VBK=0.

Gardez les tampons source lisibles et leur banque ROM accessible pendant toute la copie. Aucun retour.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settilecgb_bulk
// Copy `count` tile numbers from `tile_src`, then `count` attributes from `attr_src`, to
// the same map addresses starting at `dest`. These are separate byte arrays, not
// interleaved pairs. Waits for writable VRAM when necessary. The LCD-on path uses
// interrupt masking and can enable interrupts afterward; it is not a promise to preserve
// the caller's interrupt state or to finish within one VBlank.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
const u8 example_tiles[] = {'A','B','C'};
const u8 example_attrs[] = {1,2,3};

void main() {
    u8 failures;
    failures = 0;
    tile_example_begin();
    __settilecgb_bulk(0x9862, example_tiles, example_attrs, 3);
    if (tile_example_read(0x9862, 0) != 'A') { failures++; }
    if (tile_example_read(0x9863, 0) != 'B') { failures++; }
    if (tile_example_read(0x9864, 0) != 'C') { failures++; }
    if (tile_example_read(0x9865, 0) != 0) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 1) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9864, 1) != 3) { failures++; }
    tile_example_finish(failures, 0);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, A, B et C sont respectivement rouge, bleu et vert : les attributs 1,2,3 sélectionnent trois palettes distinctes. Sur DMG, les trois lettres sont noires.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
ABC
__settilecgb_bulk: DMG
DMG
__settilecgb_bulk: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilecgb_bulk.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilecgb_bulk.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:93

Notes de déclaration (texte source)
Copy tile and attribute streams to dest; DMG receives only tiles. Keep both sources readable. Transfers use access checks.
__settilecgb_bulk_fast

Copie count numéros depuis tile_src, puis count attributs depuis attr_src, aux mêmes adresses à partir de dest. Les tableaux sont séparés, non entrelacés.

N’attend pas l’accès à la VRAM. À utiliser LCD éteint ou lorsque l’appelant garantit un créneau d’écriture assez long pour tout le transfert.

Syntaxe

c
void __settilecgb_bulk_fast(u16 dest, const u8* tile_src, const u8* attr_src, u8 count);

Paramètres

dest

Adresse du premier octet en VRAM. Pour une carte, utilisez 0x9800–0x9FFF et gardez tout le transfert dans la carte visée.

tile_src, attr_src

Octets contigus en RAM ou dans une banque ROM visible. Le flux de tuiles contient des numéros ; celui d’attributs contient les attributs CGB. La copie est immédiate et le pointeur n’est pas conservé.

count

Nombre d’octets, de 0 à 255. Zéro ne copie rien. La source doit contenir au moins autant d’octets lisibles.

Valeur renvoyée

Aucune (void).

Conditions et limites

Écrit le numéro de tuile et l’attribut CGB. Sur DMG, seul le numéro est écrit. Sur CGB, sélectionnez d’abord la banque 0 ; les attributs utilisent la banque 1 et l’appel se termine avec VBK=0.

Gardez les tampons source lisibles et leur banque ROM accessible pendant toute la copie. Aucun retour.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settilecgb_bulk_fast
// Copy `count` tile numbers from `tile_src`, then `count` attributes from `attr_src`, to
// the same map addresses starting at `dest`. These are separate byte arrays, not
// interleaved pairs. Does not wait for VRAM access. Call only while the LCD is off or
// within a caller-managed writable interval with enough time for the whole transfer.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
const u8 example_tiles[] = {'A','B','C'};
const u8 example_attrs[] = {1,2,3};

void main() {
    u8 failures;
    failures = 0;
    tile_example_begin();
    __settilecgb_bulk_fast(0x9862, example_tiles, example_attrs, 3);
    if (tile_example_read(0x9862, 0) != 'A') { failures++; }
    if (tile_example_read(0x9863, 0) != 'B') { failures++; }
    if (tile_example_read(0x9864, 0) != 'C') { failures++; }
    if (tile_example_read(0x9865, 0) != 0) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 1) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9864, 1) != 3) { failures++; }
    tile_example_finish(failures, 0);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, A, B et C sont respectivement rouge, bleu et vert : les attributs 1,2,3 sélectionnent trois palettes distinctes. Sur DMG, les trois lettres sont noires.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
ABC
__settilecgb_bulk_fast: DMG
DMG
__settilecgb_bulk_fast: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilecgb_bulk_fast.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilecgb_bulk_fast.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:95

Notes de déclaration (texte source)
Copy tile and attribute streams to dest; DMG receives only tiles. Keep both sources readable. The caller supplies writable VRAM timing.
__settilecgb_unsafe

Écrit le numéro de tuile et l’attribut CGB. Sur DMG, seul le numéro est écrit. Sur CGB, sélectionnez d’abord la banque 0 ; les attributs utilisent la banque 1 et l’appel se termine avec VBK=0.

La destination est toujours 0x9800 + 32*y + x, indépendamment des cartes de fond et de fenêtre sélectionnées par LCDC.

N’attend pas l’accès à la VRAM. À utiliser LCD éteint ou lorsque l’appelant garantit un créneau d’écriture assez long pour tout le transfert.

Syntaxe

c
void __settilecgb_unsafe(u8 x, u8 y, u8 tile, u8 attr);

Paramètres

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

tile

Numéro de tuile, de 0 à 255. Le bit 4 de LCDC détermine l’adressage de son image. Ce n’est pas une adresse d’octet en VRAM.

attr

Octet d’attributs CGB : bits 0–2, palette 0–7 ; bit 3, banque d’image ; bits 5 et 6, retournement horizontal et vertical ; bit 7, priorité du fond ou de la fenêtre. Laissez à zéro le bit 4 inutilisé.

Valeur renvoyée

Aucune (void).

Conditions et limites

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settilecgb_unsafe
// Write a tile number and its CGB attribute. On DMG, only the tile-number write is
// performed. On CGB, enter with VRAM bank 0 selected; attributes use bank 1 and the call
// finishes with `VBK=0`. The destination is always `0x9800 + 32*y + x`, regardless of
// which maps LCDC selects for the background and window.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"

void main() {
    u8 failures;
    failures = 0;
    tile_example_begin();
    __settilecgb_unsafe(2, 3, 'A', KQ_CGB_ATTR(2, 0, 0, 0, 0));
    if (tile_example_read(0x9862, 0) != 'A') { failures++; }
    if (tile_example_read(0x9C62, 0) != 0) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 2) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 0) { failures++; }
    tile_example_finish(failures, 0);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, le A est bleu : l’attribut 2 sélectionne la palette bleue préparée par l’exemple. Sur DMG, le même caractère est noir, car les attributs CGB ne s’appliquent pas.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
A
__settilecgb_unsafe: DMG
DMG
__settilecgb_unsafe: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilecgb_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilecgb_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:59

Notes de déclaration (texte source)
Write tile and CGB attributes at map 9800; DMG receives only the tile write. No VRAM access waits are inserted.
__settilewin

Écrit le numéro de tuile d’une case de la carte ; sélectionne une image existante sans modifier ses pixels.

Le bit 6 de LCDC choisit la carte de fenêtre : zéro donne 0x9800, un donne 0x9C00. Il s’agit de coordonnées dans la carte, pas de la position de la fenêtre à l’écran.

Attend si nécessaire que la VRAM soit accessible en écriture. LCD actif, le chemin masque les interruptions et peut les réactiver ensuite ; il ne garantit ni l’état d’interruption initial ni une fin dans un seul VBlank.

Syntaxe

c
void __settilewin(u8 x, u8 y, u8 tile);

Paramètres

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

tile

Numéro de tuile, de 0 à 255. Le bit 4 de LCDC détermine l’adressage de son image. Ce n’est pas une adresse d’octet en VRAM.

Valeur renvoyée

Aucune (void).

Conditions et limites

Les octets sont écrits dans la banque VRAM sélectionnée. Sur CGB, choisissez la banque 0 pour les numéros de tuile ; cet appel ne change pas de banque.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settilewin
// Write one tile-number byte to the tile map; this selects an existing tile image, not
// new pixel data. LCDC bit 6 selects the window map: zero uses `0x9800`, one uses
// `0x9C00`. These are map coordinates, not the window's screen position.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"

void main() {
    u8 failures;
    failures = 0;
    tile_color_example_begin();
    __settilewin(2, 3, 'A');
    if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
    if (tile_example_read(0x9862, 0) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, les caractères de l’exemple et le texte des résultats s’affichent en bleu sur fond blanc. La préparation attribue le bleu à la couleur de tracé de la palette 0, sans modifier les attributs de la carte de tuiles. Sur DMG, les mêmes caractères s’affichent en noir sur fond blanc.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
A
__settilewin: DMG
DMG
__settilewin: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilewin.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilewin.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:39

Notes de déclaration (texte source)
Select the window map from LCDC bit 6, then write a tile with access checks.
__settilewin_unsafe

Écrit le numéro de tuile d’une case de la carte ; sélectionne une image existante sans modifier ses pixels.

Le bit 6 de LCDC choisit la carte de fenêtre : zéro donne 0x9800, un donne 0x9C00. Il s’agit de coordonnées dans la carte, pas de la position de la fenêtre à l’écran.

N’attend pas l’accès à la VRAM. À utiliser LCD éteint ou lorsque l’appelant garantit un créneau d’écriture assez long pour tout le transfert.

Syntaxe

c
void __settilewin_unsafe(u8 x, u8 y, u8 tile);

Paramètres

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

tile

Numéro de tuile, de 0 à 255. Le bit 4 de LCDC détermine l’adressage de son image. Ce n’est pas une adresse d’octet en VRAM.

Valeur renvoyée

Aucune (void).

Conditions et limites

Les octets sont écrits dans la banque VRAM sélectionnée. Sur CGB, choisissez la banque 0 pour les numéros de tuile ; cet appel ne change pas de banque.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settilewin_unsafe
// Write one tile-number byte to the tile map; this selects an existing tile image, not
// new pixel data. LCDC bit 6 selects the window map: zero uses `0x9800`, one uses
// `0x9C00`. These are map coordinates, not the window's screen position.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"

void main() {
    u8 failures;
    failures = 0;
    tile_color_example_begin();
    __settilewin_unsafe(2, 3, 'A');
    if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
    if (tile_example_read(0x9862, 0) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, les caractères de l’exemple et le texte des résultats s’affichent en bleu sur fond blanc. La préparation attribue le bleu à la couleur de tracé de la palette 0, sans modifier les attributs de la carte de tuiles. Sur DMG, les mêmes caractères s’affichent en noir sur fond blanc.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
A
__settilewin_unsafe: DMG
DMG
__settilewin_unsafe: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilewin_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilewin_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:41

Notes de déclaration (texte source)
Select the LCDC window map and write without access waits.
__settilewinattr

Modifie uniquement l’octet d’attributs CGB, sans changer le numéro de tuile. Ses bits choisissent palette, banque d’image, retournements et priorité.

Le bit 6 de LCDC choisit la carte de fenêtre : zéro donne 0x9800, un donne 0x9C00. Il s’agit de coordonnées dans la carte, pas de la position de la fenêtre à l’écran.

Attend si nécessaire que la VRAM soit accessible en écriture. LCD actif, le chemin masque les interruptions et peut les réactiver ensuite ; il ne garantit ni l’état d’interruption initial ni une fin dans un seul VBlank.

Syntaxe

c
void __settilewinattr(u8 x, u8 y, u8 attr);

Paramètres

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

attr

Octet d’attributs CGB : bits 0–2, palette 0–7 ; bit 3, banque d’image ; bits 5 et 6, retournement horizontal et vertical ; bit 7, priorité du fond ou de la fenêtre. Laissez à zéro le bit 4 inutilisé.

Valeur renvoyée

Aucune (void).

Conditions et limites

Sur CGB, écrit dans la banque VRAM 1 puis laisse VBK=0. Sur DMG, aucun attribut n’est écrit.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settilewinattr
// Write only the CGB attribute byte; leave the tile number unchanged. Attribute bits
// select palette, tile-data bank, flips and priority. LCDC bit 6 selects the window map:
// zero uses `0x9800`, one uses `0x9C00`. These are map coordinates, not the window's
// screen position.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"

void main() {
    u8 failures;
    failures = 0;
    tile_example_begin();
    __settileat(0x9C00, 2, 3, 'A');
    __settilewinattr(2, 3, KQ_CGB_ATTR(2, 0, 0, 0, 0));
    if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
    if (tile_example_read(0x9862, 0) != 0) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 2) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, le A est bleu : l’attribut 2 sélectionne la palette bleue préparée par l’exemple. Sur DMG, le même caractère est noir, car les attributs CGB ne s’appliquent pas.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
A
__settilewinattr: DMG
DMG
__settilewinattr: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilewinattr.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilewinattr.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:71

Notes de déclaration (texte source)
Write attributes in bank one at the LCDC window map on CGB, then leave VBK zero; skip on DMG. Use the access-checked write path.
__settilewinattr_unsafe

Modifie uniquement l’octet d’attributs CGB, sans changer le numéro de tuile. Ses bits choisissent palette, banque d’image, retournements et priorité.

Le bit 6 de LCDC choisit la carte de fenêtre : zéro donne 0x9800, un donne 0x9C00. Il s’agit de coordonnées dans la carte, pas de la position de la fenêtre à l’écran.

N’attend pas l’accès à la VRAM. À utiliser LCD éteint ou lorsque l’appelant garantit un créneau d’écriture assez long pour tout le transfert.

Syntaxe

c
void __settilewinattr_unsafe(u8 x, u8 y, u8 attr);

Paramètres

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

attr

Octet d’attributs CGB : bits 0–2, palette 0–7 ; bit 3, banque d’image ; bits 5 et 6, retournement horizontal et vertical ; bit 7, priorité du fond ou de la fenêtre. Laissez à zéro le bit 4 inutilisé.

Valeur renvoyée

Aucune (void).

Conditions et limites

Sur CGB, écrit dans la banque VRAM 1 puis laisse VBK=0. Sur DMG, aucun attribut n’est écrit.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settilewinattr_unsafe
// Write only the CGB attribute byte; leave the tile number unchanged. Attribute bits
// select palette, tile-data bank, flips and priority. LCDC bit 6 selects the window map:
// zero uses `0x9800`, one uses `0x9C00`. These are map coordinates, not the window's
// screen position.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"

void main() {
    u8 failures;
    failures = 0;
    tile_example_begin();
    __settileat(0x9C00, 2, 3, 'A');
    __settilewinattr_unsafe(2, 3, KQ_CGB_ATTR(2, 0, 0, 0, 0));
    if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
    if (tile_example_read(0x9862, 0) != 0) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 2) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, le A est bleu : l’attribut 2 sélectionne la palette bleue préparée par l’exemple. Sur DMG, le même caractère est noir, car les attributs CGB ne s’appliquent pas.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
A
__settilewinattr_unsafe: DMG
DMG
__settilewinattr_unsafe: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilewinattr_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilewinattr_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:73

Notes de déclaration (texte source)
Write attributes in bank one at the LCDC window map on CGB, then leave VBK zero; skip on DMG. No VRAM access waits are inserted.
__settilewincgb

Écrit le numéro de tuile et l’attribut CGB. Sur DMG, seul le numéro est écrit. Sur CGB, sélectionnez d’abord la banque 0 ; les attributs utilisent la banque 1 et l’appel se termine avec VBK=0.

Le bit 6 de LCDC choisit la carte de fenêtre : zéro donne 0x9800, un donne 0x9C00. Il s’agit de coordonnées dans la carte, pas de la position de la fenêtre à l’écran.

Attend si nécessaire que la VRAM soit accessible en écriture. LCD actif, le chemin masque les interruptions et peut les réactiver ensuite ; il ne garantit ni l’état d’interruption initial ni une fin dans un seul VBlank.

Syntaxe

c
void __settilewincgb(u8 x, u8 y, u8 tile, u8 attr);

Paramètres

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

tile

Numéro de tuile, de 0 à 255. Le bit 4 de LCDC détermine l’adressage de son image. Ce n’est pas une adresse d’octet en VRAM.

attr

Octet d’attributs CGB : bits 0–2, palette 0–7 ; bit 3, banque d’image ; bits 5 et 6, retournement horizontal et vertical ; bit 7, priorité du fond ou de la fenêtre. Laissez à zéro le bit 4 inutilisé.

Valeur renvoyée

Aucune (void).

Conditions et limites

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settilewincgb
// Write a tile number and its CGB attribute. On DMG, only the tile-number write is
// performed. On CGB, enter with VRAM bank 0 selected; attributes use bank 1 and the call
// finishes with `VBK=0`. LCDC bit 6 selects the window map: zero uses `0x9800`, one uses
// `0x9C00`. These are map coordinates, not the window's screen position.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"

void main() {
    u8 failures;
    failures = 0;
    tile_example_begin();
    __settilewincgb(2, 3, 'A', KQ_CGB_ATTR(2, 0, 0, 0, 0));
    if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
    if (tile_example_read(0x9862, 0) != 0) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 2) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, le A est bleu : l’attribut 2 sélectionne la palette bleue préparée par l’exemple. Sur DMG, le même caractère est noir, car les attributs CGB ne s’appliquent pas.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
A
__settilewincgb: DMG
DMG
__settilewincgb: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilewincgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilewincgb.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:75

Notes de déclaration (texte source)
Write tile and CGB attributes at the LCDC window map; DMG receives only the tile write. Use the access-checked write path.
__settilewincgb_unsafe

Écrit le numéro de tuile et l’attribut CGB. Sur DMG, seul le numéro est écrit. Sur CGB, sélectionnez d’abord la banque 0 ; les attributs utilisent la banque 1 et l’appel se termine avec VBK=0.

Le bit 6 de LCDC choisit la carte de fenêtre : zéro donne 0x9800, un donne 0x9C00. Il s’agit de coordonnées dans la carte, pas de la position de la fenêtre à l’écran.

N’attend pas l’accès à la VRAM. À utiliser LCD éteint ou lorsque l’appelant garantit un créneau d’écriture assez long pour tout le transfert.

Syntaxe

c
void __settilewincgb_unsafe(u8 x, u8 y, u8 tile, u8 attr);

Paramètres

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

tile

Numéro de tuile, de 0 à 255. Le bit 4 de LCDC détermine l’adressage de son image. Ce n’est pas une adresse d’octet en VRAM.

attr

Octet d’attributs CGB : bits 0–2, palette 0–7 ; bit 3, banque d’image ; bits 5 et 6, retournement horizontal et vertical ; bit 7, priorité du fond ou de la fenêtre. Laissez à zéro le bit 4 inutilisé.

Valeur renvoyée

Aucune (void).

Conditions et limites

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settilewincgb_unsafe
// Write a tile number and its CGB attribute. On DMG, only the tile-number write is
// performed. On CGB, enter with VRAM bank 0 selected; attributes use bank 1 and the call
// finishes with `VBK=0`. LCDC bit 6 selects the window map: zero uses `0x9800`, one uses
// `0x9C00`. These are map coordinates, not the window's screen position.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"

void main() {
    u8 failures;
    failures = 0;
    tile_example_begin();
    __settilewincgb_unsafe(2, 3, 'A', KQ_CGB_ATTR(2, 0, 0, 0, 0));
    if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
    if (tile_example_read(0x9862, 0) != 0) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9C62, 1) != 2) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9862, 1) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, le A est bleu : l’attribut 2 sélectionne la palette bleue préparée par l’exemple. Sur DMG, le même caractère est noir, car les attributs CGB ne s’appliquent pas.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
A
__settilewincgb_unsafe: DMG
DMG
__settilewincgb_unsafe: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilewincgb_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilewincgb_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/cgb_tile.h:77

Notes de déclaration (texte source)
Write tile and CGB attributes at the LCDC window map; DMG receives only the tile write. No VRAM access waits are inserted.

input.h — État des boutons, appuis, relâchements et répétition

Utilisez input_down pour un mouvement continu, input_pressed pour une action unique, input_released pour agir au relâchement et input_repeat pour répéter une sélection de menu. Initialisez une fois, mettez à jour une fois par cycle d’entrée, puis consultez autant que nécessaire. btn_down, btn_pressed, btn_released et btn_repeat sont les alias de ces quatre consultations.

Masques communs : BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80. Un bit à 1 indique un bouton maintenu. Ce format s’applique à input_* sur GB et FC ; les lectures brutes FC utilisent un autre ordre.

Les délais se comptent en mises à jour, pas en millisecondes. Un nouvel appui produit un événement immédiat. Avec INPUT_REPEAT_DELAY=18 et INPUT_REPEAT_RATE=5, le suivant arrive 19 mises à jour plus tard, puis tous les 6 appels. Chaque bouton a son compteur. Appliquez les mêmes macros lors de la compilation de input.c ; modifier seulement l’en-tête du code appelant ne reconfigure pas la bibliothèque.

__readpadex

Lit tous les boutons et les compare au masque précédent fourni. Place l’état courant dans l’octet bas et les nouveaux appuis, current & ~previous, dans l’octet haut. Aucun état précédent n’est mémorisé en interne ; aucun masque de relâchement ou de répétition n’est renvoyé.

Syntaxe

c
u16 __readpadex(u8 prev_keys);

Paramètres

prev_keys

Le masque complet du précédent __readpad() ou l’octet bas du dernier __readpadex(). Passez 0 à la première lecture, puis mémorisez le nouvel octet bas pour l’appel suivant.

Valeur renvoyée

Un u16 : (u8)result est le masque courant ; (u8)(result >> 8) celui des nouveaux appuis. Utilisez une variable de 16 bits pour ne pas perdre les bits d’appui.

Conditions et limites

Masques communs : BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80. Un bit à 1 indique un bouton maintenu. Ce format s’applique à input_* sur GB et FC ; les lectures brutes FC utilisent un autre ordre.

Exemple d’utilisation

c
packed_first=__readpadex(0); // First sample: current 0x11, new presses 0x11.
current_keys=(u8)packed_first;
press_keys=(u8)(packed_first>>8);
packed_held=__readpadex(current_keys); // Held without a change: new presses are 0.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Ce programme compare les trois formats de masque et le résultat regroupant l’état actuel et les nouveaux appuis. Maintenez A et droite ensemble. ALL KEYS vaut 17 ; DIRECTIONS et BUTTONS LOW valent chacun 1. Avec un masque précédent de 0, EX CURRENT et EX NEW PRESS valent 17. En transmettant le masque actuel à la lecture suivante, HELD PRESS vaut 0. FAILED CHECKS doit valoir 0. Les valeurs restent affichées après le relâchement des boutons.

__readpadex: DMG
DMG
__readpadex: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\input_raw.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_raw.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache
Implémentation et source

kitaqgb/lib/input.h:28

intrinsics — Fonctions intrinsèques du compilateur

Cette section regroupe les intrinsèques traités directement par le générateur de code KITAQGB. intrinsics est une catégorie documentaire, pas une instruction d’inclure un en-tête GB commun nommé intrinsics.h. Utilisez les en-têtes et l’initialisation de chaque exemple complet. Vous n’avez pas à définir l’implémentation de l’intrinsèque dans votre jeu.

Certains intrinsèques utilisent des informations de compilation ; d’autres accèdent au matériel ou à la mémoire pendant le jeu. Par exemple, __bankof(symbol) résout la banque affectée à un objet ou une fonction nommée, tandis que __bankswitch(bank) modifie le mappage ROM visible à l’exécution. Distinguez numéros de banque, adresses CPU et pointeurs dans vos arguments.

Un préfixe __ ne garantit ni contrôle des limites, ni attente d’accès à la VRAM, ni utilisation sûre depuis une interruption. Par exemple, l’appelant de __vram_memcpy_unsafe doit garantir une période où la VRAM est inscriptible. Vérifiez les unités, plages valides, mappages de banques et contraintes d’interruption de chaque entrée avant de choisir une opération.

__bankof

Obtient le numéro de banque ROM logique attribué à un objet de données ou à une fonction nommée. Résout une information de placement, sans consulter la banque active ni déréférencer un pointeur.

Syntaxe

c
u8 bank = __bankof(symbol);

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Paramètres

symbol

Nom d’un objet ROM ou d’une fonction dont le placement est connu du compilateur ou de l’éditeur de liens. Une variable pointeur ne désigne pas ici l’objet pointé ; une expression d’exécution n’est pas un nom de symbole.

Valeur renvoyée

Un numéro de banque u8. Zéro peut désigner la ROM commune, sans être un sélecteur valide sur toutes les cibles.

Exemple d’utilisation

c
bank_expect(__bankof(payload)==3,3);
bank_expect(__bankof(callback)==3,4);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

L’exemple vérifie que payload et callback sont tous deux placés en banque 3. Cela valide les métadonnées de placement ; les lectures et contrôles du rappel vérifient séparément le comportement à l’exécution.

Le programme complet montre comment identifier des données par une banque et une adresse proche, appeler des rappels entre banques et distinguer l’état de la bibliothèque du mappage réel. Tous les nombres sont hexadécimaux. BANK IN et BANK OUT doivent valoir 02 ; BYTE HEX, D3 ; WORD HEX, 5C7A ; FAILED CHECKS et FIRST FAILURE, 00. Le texte est bleu sur CGB et FC, noir sur DMG.

CALL COUNT vaut 03 : far_call, __farcall et __farcall_ptr invoquent chacun le rappel une fois.

__bankof: DMG
DMG
__bankof: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:432

kitaqgb/kitaqgb/CodeGenerator.cs:7360

csharp
if (funcName == "__bankof")
            {
                if (args.Length != 1) Program.Error("__bankof(symbol) expects 1 argument");
                if (!args[0].Match(Tag.Name, out string symName)) Program.Error("__bankof(symbol) requires a symbol name argument");
                if (TryGetKnownRomBankForSymbol(symName, out int knownBank))
                {
                    EmitAsm("LD_A_IMM", new AsmOperand(knownBank & 0xFF, AddressMode.Immediate));
                    return;
                }
                EmitAsm("LD_A_IMM", new AsmOperand(symName, ImmediateModifier.Bank));
                return;
            }
__bankswitch

Écrit l’octet bas du numéro de banque à l’adresse $2000 de la cartouche et met à jour l’état du compilateur. La sélection persiste après l’appel ; le registre logiciel de bank_get_current n’est pas mis à jour.

Syntaxe

c
void __bankswitch(u8 bank);

Paramètres

bank

Numéro de banque ROM non signé sur 8 bits. Pour un appel, il doit correspondre au placement de la fonction ; pour une lecture, il sélectionne la source. Respectez la plage du type de cartouche.

Valeur renvoyée

Aucune (void).

Conditions et limites

Un changement persistant de banque ne doit pas rendre inaccessibles le code exécuté ou son adresse de retour. Placez cet appel, bank_switch elle-même si elle est utilisée, et le code qui suit dans la banque commune ou fixe 0, comme dans l’exemple.

L’état de la bibliothèque commence à zéro et ne détecte pas le mappage initial de la cartouche. Initialisez-le avec bank_switch. Les intrinsèques et écritures directes dans le mapper n’actualisent pas cet état distinct ; bank_get_current ne vérifie donc pas le mappage matériel réel.

Ces interfaces GB transportent huit bits de banque et ne règlent pas le neuvième bit MBC5. Les règles de la cartouche restent applicables, notamment pour la banque zéro. L’exemple utilise explicitement les banques MBC5 1–3 ; ne mélangez pas cet état avec des écritures directes non suivies dans le mapper.

Exemple d’utilisation

c
__bankswitch(1);
bank_expect(home1[0]==0xA1,19);
bank_expect(bank_get_current()==2,20); // Intrinsics bypass the library record.
__bankswitch(2);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

bank_switch(2) enregistre et sélectionne d’abord la banque 2. Une commutation directe sélectionne ensuite la banque 1 et lit A1 dans home1, tandis que bank_get_current indique toujours 2 ; après restauration, home2 donne à nouveau B2.

Le programme complet montre comment identifier des données par une banque et une adresse proche, appeler des rappels entre banques et distinguer l’état de la bibliothèque du mappage réel. Tous les nombres sont hexadécimaux. BANK IN et BANK OUT doivent valoir 02 ; BYTE HEX, D3 ; WORD HEX, 5C7A ; FAILED CHECKS et FIRST FAILURE, 00. Le texte est bleu sur CGB et FC, noir sur DMG.

CALL COUNT vaut 03 : far_call, __farcall et __farcall_ptr invoquent chacun le rappel une fois.

__bankswitch: DMG
DMG
__bankswitch: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/bank.h:11

kitaqgb/kitaqgb/CodeGenerator.cs:7268

csharp
if (funcName == "__bankswitch")
            {
                if (args.Length != 1) Program.Error("__bankswitch(bank) expects 1 argument");
                CompileIntoA(args[0]);
                if (CurrentFunctionBank == 0)
                {
                    EmitAsm("LD_MEM_A", new AsmOperand(0x2000, AddressMode.Absolute));
                    EmitStoreA(RomBankVar);
                }
                else
                {
                    UseBankSwitchBank0Helper = true;
                    EmitAsm("CALL", new AsmOperand("__kq_bankswitch_bank0", AddressMode.Absolute));
                }
                return;
            }
__cgb_safe_set_bgpd

Écrit un octet dans le registre de données BCPD/BGPD ($FF69) des palettes de fond/fenêtre sur CGB. __cgb_safe_set_bcpd désigne le même registre. Le registre d'indice choisit la position ; une couleur RGB15 complète exige un octet faible et un octet fort.

Syntaxe

c
__cgb_safe_set_bgpd(/* 書式は実装と呼び出し例を参照 */);

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Paramètres

value

Un octet, 0–255. Écrivez l'octet faible puis l'octet fort de RGB15 ; activez d'abord l'incrément automatique ou sélectionnez chaque position. L'écriture de données n'active pas elle-même l'incrément.

Valeur renvoyée

Aucune (void).

Conditions et limites

Les écritures de palette et de registres protégés n'écrivent rien en mode DMG. Safe désigne ici le contrôle du mode CGB, pas une attente d'accès PPU ni une section critique. Configurez avec le LCD éteint ou effectuez une mise à jour assez courte pendant une phase accessible. Aucune attente VBlank ni protection contre les interruptions n'est ajoutée.

Exemple d’utilisation

c
__cgb_safe_set_bgpd(31); __cgb_safe_set_bgpd(0); // Alias of BCPD: red at slot 31.
expect_color(0,31,31);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Ce programme montre comment les palettes BG et OBJ colorent indépendamment un même motif. Chaque triangle occupe 8 × 8 pixels, d'un seul pixel à gauche en haut jusqu'à une ligne complète en bas. Aux abscisses 16, 32, 48, 64, 80, 96, 112, la ligne BG à y = 24 est rouge, bleue, verte, rouge, bleue, verte, rouge ; la ligne OBJ à y = 64 est bleue, verte, rouge, bleue, verte, rouge, bleue. Les colonnes utilisent respectivement une couleur, les composantes, une plage indexée, une palette entière, une plage absolue, un registre canonique et son alias.

À y = 88, sept cases de 8 × 8 montrent : triangle rouge (x = 16), contour carré bleu d'un pixel provenant de la banque 1 (32), triangle vert réfléchi horizontalement (48), rouge réfléchi verticalement (64), bleu réfléchi sur les deux axes (80), triangle vert devant un sprite rouge visible dans les pixels de couleur zéro (96), puis sprite rouge plein devant le triangle BG, témoin sans priorité (112). Les coordonnées sont en pixels depuis le coin supérieur gauche. Le fond reste blanc et le texte noir.

FAILED CHECKS 000 confirme les masques RGB, les booléens (dont 2), la normalisation des indices, le franchissement de palettes, l'arrêt à l'emplacement 31, l'absence d'écriture pour pointeur nul ou longueur nulle et la relecture des couleurs LCD éteint. Quatre combinaisons sont testées : ROM mixte sur DMG/CGB, ROM DMG seule sur DMG, ROM CGB seule sur CGB. Sur DMG, CGB MODE vaut 0, les triangles sont noirs, banque et réflexions n'affectent pas l'image et les deux dernières cases du bas sont des carrés noirs. Sur CGB, CGB MODE vaut 1. Le matériel réel et les conflits d'accès LCD allumé ne sont pas testés.

__cgb_safe_set_bgpd: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_bgpd: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_bgpd: --cgb=dmg / DMG
--cgb=dmg / DMG
__cgb_safe_set_bgpd: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:1865

__cgb_safe_set_bgpi

Écrit sur CGB le registre d'indice d'octet BCPS/BGPI ($FF68) des palettes de fond/fenêtre. __cgb_safe_set_bcps accède au même registre. Sélectionne la position du prochain accès au registre de données, sans écrire de couleur à lui seul.

Syntaxe

c
__cgb_safe_set_bgpi(/* 書式は実装と呼び出し例を参照 */);

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Paramètres

value

Indice d'octet aux bits 0–5 (0–63) ; le bit 7 active l'incrément après écriture des données. Le bit 6 est inutilisé. La couleur n commence à l'octet 2n. Après 63, l'indice revient à zéro : gérez les limites lors des écritures directes.

Valeur renvoyée

Aucune (void).

Conditions et limites

Les écritures de palette et de registres protégés n'écrivent rien en mode DMG. Safe désigne ici le contrôle du mode CGB, pas une attente d'accès PPU ni une section critique. Configurez avec le LCD éteint ou effectuez une mise à jour assez courte pendant une phase accessible. Aucune attente VBlank ni protection contre les interruptions n'est ajoutée.

Exemple d’utilisation

c
__cgb_safe_set_bgpi(0xBE); // Alias of BCPS: BG byte 62, auto-increment enabled.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Ce programme montre comment les palettes BG et OBJ colorent indépendamment un même motif. Chaque triangle occupe 8 × 8 pixels, d'un seul pixel à gauche en haut jusqu'à une ligne complète en bas. Aux abscisses 16, 32, 48, 64, 80, 96, 112, la ligne BG à y = 24 est rouge, bleue, verte, rouge, bleue, verte, rouge ; la ligne OBJ à y = 64 est bleue, verte, rouge, bleue, verte, rouge, bleue. Les colonnes utilisent respectivement une couleur, les composantes, une plage indexée, une palette entière, une plage absolue, un registre canonique et son alias.

À y = 88, sept cases de 8 × 8 montrent : triangle rouge (x = 16), contour carré bleu d'un pixel provenant de la banque 1 (32), triangle vert réfléchi horizontalement (48), rouge réfléchi verticalement (64), bleu réfléchi sur les deux axes (80), triangle vert devant un sprite rouge visible dans les pixels de couleur zéro (96), puis sprite rouge plein devant le triangle BG, témoin sans priorité (112). Les coordonnées sont en pixels depuis le coin supérieur gauche. Le fond reste blanc et le texte noir.

FAILED CHECKS 000 confirme les masques RGB, les booléens (dont 2), la normalisation des indices, le franchissement de palettes, l'arrêt à l'emplacement 31, l'absence d'écriture pour pointeur nul ou longueur nulle et la relecture des couleurs LCD éteint. Quatre combinaisons sont testées : ROM mixte sur DMG/CGB, ROM DMG seule sur DMG, ROM CGB seule sur CGB. Sur DMG, CGB MODE vaut 0, les triangles sont noirs, banque et réflexions n'affectent pas l'image et les deux dernières cases du bas sont des carrés noirs. Sur CGB, CGB MODE vaut 1. Le matériel réel et les conflits d'accès LCD allumé ne sont pas testés.

__cgb_safe_set_bgpi: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_bgpi: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_bgpi: --cgb=dmg / DMG
--cgb=dmg / DMG
__cgb_safe_set_bgpi: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:1863

__cgb_safe_set_hdma1

Écrit HDMA1 ($FF51), l'octet fort de l'adresse source du DMA CGB. Configure uniquement la source, sans copier ni démarrer le transfert. HDMA2 définit l'octet faible.

Syntaxe

c
void __cgb_safe_set_hdma1(u8 value);

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Paramètres

value

Valeur du registre en u8 (0–255). Les bits propres à ce registre sont décrits ci-dessus ; l'argument est un octet, pas un pointeur ni un nombre d'octets.

Valeur renvoyée

Aucune (void).

Conditions et limites

Le compilateur conditionne l'écriture au mode CGB ; rien n'est écrit en DMG. Safe désigne seulement ce contrôle. La valeur précédente du registre n'est ni sauvegardée ni restaurée.

Un bloc vaut 16 octets. Un nouveau transfert accepte 1–128 blocs (16–2048 octets) : la valeur 0 signifie 16 octets, pas un transfert vide. GDMA suspend la CPU jusqu'à la fin. HBlank DMA copie un bloc par HBlank visible et peut traverser plusieurs images ; vérifiez la fin avant de réutiliser les données.

Utilisez GDMA LCD éteint ou pendant une période d'accès assez longue. Lancez HBlank DMA LCD actif, hors du mode STAT 0 ; l'exemple le lance en VBlank. Ne changez ni banques source, ni VBK destination, ni données pendant le transfert. HALT suspend aussi HBlank DMA. Ces fonctions n'ajoutent ni attente, ni protection contre les interruptions, ni contrôle des plages.

Lisez $FF55 séparément : bit 7 nul signifie actif, $FF signifie terminé. Après annulation, le bit 7 vaut un et les bits 0–6 conservent les blocs restants moins un. Les octets copiés ne sont pas restaurés. N'annulez qu'un transfert actif ; sinon, le bit 7 nul démarre GDMA. Gardez tout le transfert dans la mémoire valide et la banque VRAM destination, sans dépendre d'un débordement d'adresse.

Ces fonctions HDMA contrôlent le DMA matériel CGB. __vram_copy_dma est un alias distinct du compilateur pour une boucle de copie CPU ; son nom ne signifie pas qu'il utilise ce contrôleur DMA.

Exemple d’utilisation

c
__cgb_safe_set_hdma1(0xC6); // Source high byte: fixed-WRAM buffer at $C600.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Le programme compare trois chemins avec un tampon aligné sur 16 octets à $C600. GDMA copie 32 octets vers $8010 : en (16,24), un triangle rouge de 8 × 8 s'élargit d'un pixel à gauche jusqu'à une ligne complète en bas ; en (24,24), un contour carré rouge d'un pixel. HBlank DMA copie 32 octets vers $8030 : en (16,48), une moitié gauche bleue de 4 × 8 ; en (24,48), un carré bleu de 8 × 8. Ce sont les motifs réellement transférés, pas des indicateurs numériques.

CANCELED montre une moitié verte de 4 × 8 en (16,72) et un carré vert de 8 × 8 en (24,72), écrits par la CPU. Un HBlank DMA de deux blocs est armé puis annulé dans le même VBlank, avant le premier bloc. $FF55 doit valoir $81 et les motifs rester intacts après une image. Sur DMG, les deux premières lignes restent vides et seuls ces témoins sont noirs. Texte noir sur blanc. FAILED CHECKS 000 vérifie aussi octets et état final. Testé dans KOKURA en ROM mixte DMG/CGB et CGB seule ; annulation partielle, double vitesse et matériel réel non testés.

__cgb_safe_set_hdma1: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_hdma1: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_hdma1: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_dma_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_dma_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:1871

__cgb_safe_set_hdma2

Écrit HDMA2 ($FF52), l'octet faible de l'adresse source. Le matériel ignore les bits 0–3 et aligne vers le bas sur 16 octets. HDMA1=$C6, HDMA2=$0F désigne $C600. Ne démarre pas le DMA.

Syntaxe

c
void __cgb_safe_set_hdma2(u8 value);

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Paramètres

value

Valeur du registre en u8 (0–255). Les bits propres à ce registre sont décrits ci-dessus ; l'argument est un octet, pas un pointeur ni un nombre d'octets.

Valeur renvoyée

Aucune (void).

Conditions et limites

Le compilateur conditionne l'écriture au mode CGB ; rien n'est écrit en DMG. Safe désigne seulement ce contrôle. La valeur précédente du registre n'est ni sauvegardée ni restaurée.

Un bloc vaut 16 octets. Un nouveau transfert accepte 1–128 blocs (16–2048 octets) : la valeur 0 signifie 16 octets, pas un transfert vide. GDMA suspend la CPU jusqu'à la fin. HBlank DMA copie un bloc par HBlank visible et peut traverser plusieurs images ; vérifiez la fin avant de réutiliser les données.

Utilisez GDMA LCD éteint ou pendant une période d'accès assez longue. Lancez HBlank DMA LCD actif, hors du mode STAT 0 ; l'exemple le lance en VBlank. Ne changez ni banques source, ni VBK destination, ni données pendant le transfert. HALT suspend aussi HBlank DMA. Ces fonctions n'ajoutent ni attente, ni protection contre les interruptions, ni contrôle des plages.

Lisez $FF55 séparément : bit 7 nul signifie actif, $FF signifie terminé. Après annulation, le bit 7 vaut un et les bits 0–6 conservent les blocs restants moins un. Les octets copiés ne sont pas restaurés. N'annulez qu'un transfert actif ; sinon, le bit 7 nul démarre GDMA. Gardez tout le transfert dans la mémoire valide et la banque VRAM destination, sans dépendre d'un débordement d'adresse.

Ces fonctions HDMA contrôlent le DMA matériel CGB. __vram_copy_dma est un alias distinct du compilateur pour une boucle de copie CPU ; son nom ne signifie pas qu'il utilise ce contrôleur DMA.

Exemple d’utilisation

c
__cgb_safe_set_hdma2(0x0F); // Low four bits are ignored by hardware: source remains $C600.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Le programme compare trois chemins avec un tampon aligné sur 16 octets à $C600. GDMA copie 32 octets vers $8010 : en (16,24), un triangle rouge de 8 × 8 s'élargit d'un pixel à gauche jusqu'à une ligne complète en bas ; en (24,24), un contour carré rouge d'un pixel. HBlank DMA copie 32 octets vers $8030 : en (16,48), une moitié gauche bleue de 4 × 8 ; en (24,48), un carré bleu de 8 × 8. Ce sont les motifs réellement transférés, pas des indicateurs numériques.

CANCELED montre une moitié verte de 4 × 8 en (16,72) et un carré vert de 8 × 8 en (24,72), écrits par la CPU. Un HBlank DMA de deux blocs est armé puis annulé dans le même VBlank, avant le premier bloc. $FF55 doit valoir $81 et les motifs rester intacts après une image. Sur DMG, les deux premières lignes restent vides et seuls ces témoins sont noirs. Texte noir sur blanc. FAILED CHECKS 000 vérifie aussi octets et état final. Testé dans KOKURA en ROM mixte DMG/CGB et CGB seule ; annulation partielle, double vitesse et matériel réel non testés.

__cgb_safe_set_hdma2: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_hdma2: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_hdma2: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_dma_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_dma_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:1872

__cgb_safe_set_hdma3

Écrit HDMA3 ($FF53), la partie haute de la destination DMA en VRAM. Seuls les bits 0–4 sont utilisés ; la destination reste dans $8000–$9FFF. À associer à HDMA4. Ne choisit pas la banque VRAM et ne démarre pas le DMA.

Syntaxe

c
void __cgb_safe_set_hdma3(u8 value);

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Paramètres

value

Valeur du registre en u8 (0–255). Les bits propres à ce registre sont décrits ci-dessus ; l'argument est un octet, pas un pointeur ni un nombre d'octets.

Valeur renvoyée

Aucune (void).

Conditions et limites

Le compilateur conditionne l'écriture au mode CGB ; rien n'est écrit en DMG. Safe désigne seulement ce contrôle. La valeur précédente du registre n'est ni sauvegardée ni restaurée.

Un bloc vaut 16 octets. Un nouveau transfert accepte 1–128 blocs (16–2048 octets) : la valeur 0 signifie 16 octets, pas un transfert vide. GDMA suspend la CPU jusqu'à la fin. HBlank DMA copie un bloc par HBlank visible et peut traverser plusieurs images ; vérifiez la fin avant de réutiliser les données.

Utilisez GDMA LCD éteint ou pendant une période d'accès assez longue. Lancez HBlank DMA LCD actif, hors du mode STAT 0 ; l'exemple le lance en VBlank. Ne changez ni banques source, ni VBK destination, ni données pendant le transfert. HALT suspend aussi HBlank DMA. Ces fonctions n'ajoutent ni attente, ni protection contre les interruptions, ni contrôle des plages.

Lisez $FF55 séparément : bit 7 nul signifie actif, $FF signifie terminé. Après annulation, le bit 7 vaut un et les bits 0–6 conservent les blocs restants moins un. Les octets copiés ne sont pas restaurés. N'annulez qu'un transfert actif ; sinon, le bit 7 nul démarre GDMA. Gardez tout le transfert dans la mémoire valide et la banque VRAM destination, sans dépendre d'un débordement d'adresse.

Ces fonctions HDMA contrôlent le DMA matériel CGB. __vram_copy_dma est un alias distinct du compilateur pour une boucle de copie CPU ; son nom ne signifie pas qu'il utilise ce contrôleur DMA.

Exemple d’utilisation

c
__cgb_safe_set_hdma3(0xE0); // Only low five bits select the high VRAM offset.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Le programme compare trois chemins avec un tampon aligné sur 16 octets à $C600. GDMA copie 32 octets vers $8010 : en (16,24), un triangle rouge de 8 × 8 s'élargit d'un pixel à gauche jusqu'à une ligne complète en bas ; en (24,24), un contour carré rouge d'un pixel. HBlank DMA copie 32 octets vers $8030 : en (16,48), une moitié gauche bleue de 4 × 8 ; en (24,48), un carré bleu de 8 × 8. Ce sont les motifs réellement transférés, pas des indicateurs numériques.

CANCELED montre une moitié verte de 4 × 8 en (16,72) et un carré vert de 8 × 8 en (24,72), écrits par la CPU. Un HBlank DMA de deux blocs est armé puis annulé dans le même VBlank, avant le premier bloc. $FF55 doit valoir $81 et les motifs rester intacts après une image. Sur DMG, les deux premières lignes restent vides et seuls ces témoins sont noirs. Texte noir sur blanc. FAILED CHECKS 000 vérifie aussi octets et état final. Testé dans KOKURA en ROM mixte DMG/CGB et CGB seule ; annulation partielle, double vitesse et matériel réel non testés.

__cgb_safe_set_hdma3: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_hdma3: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_hdma3: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_dma_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_dma_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:1873

__cgb_safe_set_hdma4

Écrit HDMA4 ($FF54), l'octet faible de la destination VRAM. Les bits 0–3 sont ignorés, avec alignement vers le bas sur 16 octets. HDMA3=$E0, HDMA4=$1F désigne $8010. Configure une adresse, pas une longueur.

Syntaxe

c
void __cgb_safe_set_hdma4(u8 value);

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Paramètres

value

Valeur du registre en u8 (0–255). Les bits propres à ce registre sont décrits ci-dessus ; l'argument est un octet, pas un pointeur ni un nombre d'octets.

Valeur renvoyée

Aucune (void).

Conditions et limites

Le compilateur conditionne l'écriture au mode CGB ; rien n'est écrit en DMG. Safe désigne seulement ce contrôle. La valeur précédente du registre n'est ni sauvegardée ni restaurée.

Un bloc vaut 16 octets. Un nouveau transfert accepte 1–128 blocs (16–2048 octets) : la valeur 0 signifie 16 octets, pas un transfert vide. GDMA suspend la CPU jusqu'à la fin. HBlank DMA copie un bloc par HBlank visible et peut traverser plusieurs images ; vérifiez la fin avant de réutiliser les données.

Utilisez GDMA LCD éteint ou pendant une période d'accès assez longue. Lancez HBlank DMA LCD actif, hors du mode STAT 0 ; l'exemple le lance en VBlank. Ne changez ni banques source, ni VBK destination, ni données pendant le transfert. HALT suspend aussi HBlank DMA. Ces fonctions n'ajoutent ni attente, ni protection contre les interruptions, ni contrôle des plages.

Lisez $FF55 séparément : bit 7 nul signifie actif, $FF signifie terminé. Après annulation, le bit 7 vaut un et les bits 0–6 conservent les blocs restants moins un. Les octets copiés ne sont pas restaurés. N'annulez qu'un transfert actif ; sinon, le bit 7 nul démarre GDMA. Gardez tout le transfert dans la mémoire valide et la banque VRAM destination, sans dépendre d'un débordement d'adresse.

Ces fonctions HDMA contrôlent le DMA matériel CGB. __vram_copy_dma est un alias distinct du compilateur pour une boucle de copie CPU ; son nom ne signifie pas qu'il utilise ce contrôleur DMA.

Exemple d’utilisation

c
__cgb_safe_set_hdma4(0x1F); // Destination aligns to $8010, not $801F.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Le programme compare trois chemins avec un tampon aligné sur 16 octets à $C600. GDMA copie 32 octets vers $8010 : en (16,24), un triangle rouge de 8 × 8 s'élargit d'un pixel à gauche jusqu'à une ligne complète en bas ; en (24,24), un contour carré rouge d'un pixel. HBlank DMA copie 32 octets vers $8030 : en (16,48), une moitié gauche bleue de 4 × 8 ; en (24,48), un carré bleu de 8 × 8. Ce sont les motifs réellement transférés, pas des indicateurs numériques.

CANCELED montre une moitié verte de 4 × 8 en (16,72) et un carré vert de 8 × 8 en (24,72), écrits par la CPU. Un HBlank DMA de deux blocs est armé puis annulé dans le même VBlank, avant le premier bloc. $FF55 doit valoir $81 et les motifs rester intacts après une image. Sur DMG, les deux premières lignes restent vides et seuls ces témoins sont noirs. Texte noir sur blanc. FAILED CHECKS 000 vérifie aussi octets et état final. Testé dans KOKURA en ROM mixte DMG/CGB et CGB seule ; annulation partielle, double vitesse et matériel réel non testés.

__cgb_safe_set_hdma4: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_hdma4: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_hdma4: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_dma_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_dma_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:1874

__cgb_safe_set_hdma5

Écrit HDMA5 ($FF55) pour lancer le DMA ou annuler un transfert HBlank actif. Pour un nouveau transfert, les bits 0–6 codent le nombre de blocs moins un, le bit 7 choisit GDMA (0) ou HBlank DMA (1). Pendant un HBlank DMA actif, écrire le bit 7 à zéro annule le reste sans lancer de GDMA.

Syntaxe

c
void __cgb_safe_set_hdma5(u8 value);

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Paramètres

value

Valeur du registre en u8 (0–255). Les bits propres à ce registre sont décrits ci-dessus ; l'argument est un octet, pas un pointeur ni un nombre d'octets.

Valeur renvoyée

Aucune (void).

Conditions et limites

Le compilateur conditionne l'écriture au mode CGB ; rien n'est écrit en DMG. Safe désigne seulement ce contrôle. La valeur précédente du registre n'est ni sauvegardée ni restaurée.

Un bloc vaut 16 octets. Un nouveau transfert accepte 1–128 blocs (16–2048 octets) : la valeur 0 signifie 16 octets, pas un transfert vide. GDMA suspend la CPU jusqu'à la fin. HBlank DMA copie un bloc par HBlank visible et peut traverser plusieurs images ; vérifiez la fin avant de réutiliser les données.

Utilisez GDMA LCD éteint ou pendant une période d'accès assez longue. Lancez HBlank DMA LCD actif, hors du mode STAT 0 ; l'exemple le lance en VBlank. Ne changez ni banques source, ni VBK destination, ni données pendant le transfert. HALT suspend aussi HBlank DMA. Ces fonctions n'ajoutent ni attente, ni protection contre les interruptions, ni contrôle des plages.

Lisez $FF55 séparément : bit 7 nul signifie actif, $FF signifie terminé. Après annulation, le bit 7 vaut un et les bits 0–6 conservent les blocs restants moins un. Les octets copiés ne sont pas restaurés. N'annulez qu'un transfert actif ; sinon, le bit 7 nul démarre GDMA. Gardez tout le transfert dans la mémoire valide et la banque VRAM destination, sans dépendre d'un débordement d'adresse.

Ces fonctions HDMA contrôlent le DMA matériel CGB. __vram_copy_dma est un alias distinct du compilateur pour une boucle de copie CPU ; son nom ne signifie pas qu'il utilise ce contrôleur DMA.

Exemple d’utilisation

c
__cgb_safe_set_hdma5(1); // LCD is off: GDMA, (1+1)*16 = 32 bytes.
if (color_mode && D_CONTROL!=255) failures++;

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Le programme compare trois chemins avec un tampon aligné sur 16 octets à $C600. GDMA copie 32 octets vers $8010 : en (16,24), un triangle rouge de 8 × 8 s'élargit d'un pixel à gauche jusqu'à une ligne complète en bas ; en (24,24), un contour carré rouge d'un pixel. HBlank DMA copie 32 octets vers $8030 : en (16,48), une moitié gauche bleue de 4 × 8 ; en (24,48), un carré bleu de 8 × 8. Ce sont les motifs réellement transférés, pas des indicateurs numériques.

CANCELED montre une moitié verte de 4 × 8 en (16,72) et un carré vert de 8 × 8 en (24,72), écrits par la CPU. Un HBlank DMA de deux blocs est armé puis annulé dans le même VBlank, avant le premier bloc. $FF55 doit valoir $81 et les motifs rester intacts après une image. Sur DMG, les deux premières lignes restent vides et seuls ces témoins sont noirs. Texte noir sur blanc. FAILED CHECKS 000 vérifie aussi octets et état final. Testé dans KOKURA en ROM mixte DMG/CGB et CGB seule ; annulation partielle, double vitesse et matériel réel non testés.

__cgb_safe_set_hdma5: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_hdma5: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_hdma5: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_dma_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_dma_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:1875

__cgb_safe_set_obpd

Écrit un octet dans le registre de données OCPD/OBPD ($FF6B) des palettes de sprites (OBJ) sur CGB. __cgb_safe_set_ocpd désigne le même registre. Le registre d'indice choisit la position ; une couleur RGB15 complète exige un octet faible et un octet fort.

Syntaxe

c
__cgb_safe_set_obpd(/* 書式は実装と呼び出し例を参照 */);

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Paramètres

value

Un octet, 0–255. Écrivez l'octet faible puis l'octet fort de RGB15 ; activez d'abord l'incrément automatique ou sélectionnez chaque position. L'écriture de données n'active pas elle-même l'incrément.

Valeur renvoyée

Aucune (void).

Conditions et limites

Les écritures de palette et de registres protégés n'écrivent rien en mode DMG. Safe désigne ici le contrôle du mode CGB, pas une attente d'accès PPU ni une section critique. Configurez avec le LCD éteint ou effectuez une mise à jour assez courte pendant une phase accessible. Aucune attente VBlank ni protection contre les interruptions n'est ajoutée.

Exemple d’utilisation

c
__cgb_safe_set_obpd(0); __cgb_safe_set_obpd(0x7C); // Alias of OCPD: blue at slot 31.
expect_color(1,31,31744);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Ce programme montre comment les palettes BG et OBJ colorent indépendamment un même motif. Chaque triangle occupe 8 × 8 pixels, d'un seul pixel à gauche en haut jusqu'à une ligne complète en bas. Aux abscisses 16, 32, 48, 64, 80, 96, 112, la ligne BG à y = 24 est rouge, bleue, verte, rouge, bleue, verte, rouge ; la ligne OBJ à y = 64 est bleue, verte, rouge, bleue, verte, rouge, bleue. Les colonnes utilisent respectivement une couleur, les composantes, une plage indexée, une palette entière, une plage absolue, un registre canonique et son alias.

À y = 88, sept cases de 8 × 8 montrent : triangle rouge (x = 16), contour carré bleu d'un pixel provenant de la banque 1 (32), triangle vert réfléchi horizontalement (48), rouge réfléchi verticalement (64), bleu réfléchi sur les deux axes (80), triangle vert devant un sprite rouge visible dans les pixels de couleur zéro (96), puis sprite rouge plein devant le triangle BG, témoin sans priorité (112). Les coordonnées sont en pixels depuis le coin supérieur gauche. Le fond reste blanc et le texte noir.

FAILED CHECKS 000 confirme les masques RGB, les booléens (dont 2), la normalisation des indices, le franchissement de palettes, l'arrêt à l'emplacement 31, l'absence d'écriture pour pointeur nul ou longueur nulle et la relecture des couleurs LCD éteint. Quatre combinaisons sont testées : ROM mixte sur DMG/CGB, ROM DMG seule sur DMG, ROM CGB seule sur CGB. Sur DMG, CGB MODE vaut 0, les triangles sont noirs, banque et réflexions n'affectent pas l'image et les deux dernières cases du bas sont des carrés noirs. Sur CGB, CGB MODE vaut 1. Le matériel réel et les conflits d'accès LCD allumé ne sont pas testés.

__cgb_safe_set_obpd: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_obpd: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_obpd: --cgb=dmg / DMG
--cgb=dmg / DMG
__cgb_safe_set_obpd: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:1869

__cgb_safe_set_obpi

Écrit sur CGB le registre d'indice d'octet OCPS/OBPI ($FF6A) des palettes de sprites (OBJ). __cgb_safe_set_ocps accède au même registre. Sélectionne la position du prochain accès au registre de données, sans écrire de couleur à lui seul.

Syntaxe

c
__cgb_safe_set_obpi(/* 書式は実装と呼び出し例を参照 */);

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Paramètres

value

Indice d'octet aux bits 0–5 (0–63) ; le bit 7 active l'incrément après écriture des données. Le bit 6 est inutilisé. La couleur n commence à l'octet 2n. Après 63, l'indice revient à zéro : gérez les limites lors des écritures directes.

Valeur renvoyée

Aucune (void).

Conditions et limites

Les écritures de palette et de registres protégés n'écrivent rien en mode DMG. Safe désigne ici le contrôle du mode CGB, pas une attente d'accès PPU ni une section critique. Configurez avec le LCD éteint ou effectuez une mise à jour assez courte pendant une phase accessible. Aucune attente VBlank ni protection contre les interruptions n'est ajoutée.

Exemple d’utilisation

c
__cgb_safe_set_obpi(0xBE); // Alias of OCPS: select the OBJ store's last word.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Ce programme montre comment les palettes BG et OBJ colorent indépendamment un même motif. Chaque triangle occupe 8 × 8 pixels, d'un seul pixel à gauche en haut jusqu'à une ligne complète en bas. Aux abscisses 16, 32, 48, 64, 80, 96, 112, la ligne BG à y = 24 est rouge, bleue, verte, rouge, bleue, verte, rouge ; la ligne OBJ à y = 64 est bleue, verte, rouge, bleue, verte, rouge, bleue. Les colonnes utilisent respectivement une couleur, les composantes, une plage indexée, une palette entière, une plage absolue, un registre canonique et son alias.

À y = 88, sept cases de 8 × 8 montrent : triangle rouge (x = 16), contour carré bleu d'un pixel provenant de la banque 1 (32), triangle vert réfléchi horizontalement (48), rouge réfléchi verticalement (64), bleu réfléchi sur les deux axes (80), triangle vert devant un sprite rouge visible dans les pixels de couleur zéro (96), puis sprite rouge plein devant le triangle BG, témoin sans priorité (112). Les coordonnées sont en pixels depuis le coin supérieur gauche. Le fond reste blanc et le texte noir.

FAILED CHECKS 000 confirme les masques RGB, les booléens (dont 2), la normalisation des indices, le franchissement de palettes, l'arrêt à l'emplacement 31, l'absence d'écriture pour pointeur nul ou longueur nulle et la relecture des couleurs LCD éteint. Quatre combinaisons sont testées : ROM mixte sur DMG/CGB, ROM DMG seule sur DMG, ROM CGB seule sur CGB. Sur DMG, CGB MODE vaut 0, les triangles sont noirs, banque et réflexions n'affectent pas l'image et les deux dernières cases du bas sont des carrés noirs. Sur CGB, CGB MODE vaut 1. Le matériel réel et les conflits d'accès LCD allumé ne sont pas testés.

__cgb_safe_set_obpi: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_obpi: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_obpi: --cgb=dmg / DMG
--cgb=dmg / DMG
__cgb_safe_set_obpi: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:1867

__cgb_safe_set_svbk

Écrit le sélecteur WRAM SVBK ($FF70) sur CGB. Le matériel utilise les bits 0–2 ; 0 et 1 mappent tous deux la banque 1 dans $D000–$DFFF. Le 8 mappe aussi la banque 1. Contrairement à __svbk_set, accepte une cible mixte et ne renvoie pas la banque précédente.

Syntaxe

c
void __cgb_safe_set_svbk(u8 value);

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Paramètres

value

Valeur du registre en u8 (0–255). Les bits propres à ce registre sont décrits ci-dessus ; l'argument est un octet, pas un pointeur ni un nombre d'octets.

Valeur renvoyée

Aucune (void).

Conditions et limites

Le compilateur conditionne l'écriture au mode CGB ; rien n'est écrit en DMG. Safe désigne seulement ce contrôle. La valeur précédente du registre n'est ni sauvegardée ni restaurée.

Changer de banque affecte tout accès dans $D000–$DFFF ; la WRAM fixe de $C000–$CFFF reste accessible. Placez pile et état nécessaire hors de cette fenêtre. L'exemple utilise --stack-bank=fixed et des variables explicitement placées en WRAM fixe. Coordonnez interruptions et DMA bancarisé ; ces API n'ajoutent aucune section critique.

Exemple d’utilisation

c
__cgb_safe_set_svbk(0); // Raw selector 0 maps effective bank 1; this API returns void.
if ((W_SELECTOR&7)!=0) failures++;

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Le programme CGB seul écrit 1 à $D100 dans la banque WRAM 2 et 2 à la même adresse dans la banque 3, relit les deux puis restaure la banque 1. Ces valeurs choisissent un carré rouge de 8 × 8 en (16,24) et un contour bleu de 8 × 8, épais d'un pixel, en (16,48). FAILED CHECKS 000 vérifie normalisation, séparation et retour de l'ancienne banque avec constantes, variables, expressions et résultats de fonction. Six compilations invalides vérifient KQ2103 en DMG/mixte et KQ2102 pour les banques constantes 0 et 8. Exécution dans KOKURA ; matériel réel et changements concurrents par interruption non testés.

__cgb_safe_set_svbk: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_wram_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_wram_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb_only --no-cache --no-disasm
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:1862

__critical_enter

Incrémente la profondeur logicielle d’imbrication et exécute DI.

Syntaxe

c
u8 __critical_enter();

Valeur renvoyée

Renvoie la profondeur précédente sous forme de jeton u8 : zéro pour la portée extérieure, un pour sa première portée imbriquée.

Conditions et limites

Passez les jetons à leave dans l’ordre inverse et évitez le rebouclage de profondeur à 256. L’état IME matériel précédent n’est pas sauvegardé ; n’utilisez pas cette paire si un état DI préexistant doit être préservé.

Exemple d’utilisation

c
*((u8*)0xFFFF)=0;outer=__critical_enter();inner=__critical_enter();result[7]=outer;result[8]=inner;

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Découvrez sélection de carte, défilement, imbrication et attentes de ligne. Les bases valent 38912/39936 ; les jetons imbriqués, 0/1 ; les LY cibles observés, 80/144. Vérifiez aussi que les attentes reviennent avec le LCD éteint.

__critical_enter: DMG
DMG
__critical_enter: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_display.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_display.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:7970

kitaqgb/kitaqgb/CodeGenerator.cs:7970

csharp
if (funcName == "__critical_enter")
            {
                if (args.Length != 0) Error(expr, "__critical_enter() expects 0 arguments");

                EmitLoadA(CriticalDepthVar);
                EmitAsm("LD_B_A"); // token = old depth
                EmitAsm("INC_A");
                EmitStoreA(CriticalDepthVar);
                EmitAsm("DI");
                EmitAsm("LD_A_B");
                return;
            }
__critical_leave

Réduit la profondeur d’imbrication et exécute EI pour le jeton extérieur zéro.

Syntaxe

c
void __critical_leave(u8 token);

Paramètres

token

Jeton renvoyé par l’appel correspondant à __critical_enter.

Valeur renvoyée

Aucune valeur de retour.

Conditions et limites

Les jetons non nuls n’exécutent pas EI. Passer arbitrairement zéro peut activer les interruptions malgré d’autres portées actives ; appariez correctement les appels.

Exemple d’utilisation

c
__critical_leave(inner);__critical_leave(outer);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Découvrez sélection de carte, défilement, imbrication et attentes de ligne. Les bases valent 38912/39936 ; les jetons imbriqués, 0/1 ; les LY cibles observés, 80/144. Vérifiez aussi que les attentes reviennent avec le LCD éteint.

__critical_leave: DMG
DMG
__critical_leave: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_display.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_display.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:7984

kitaqgb/kitaqgb/CodeGenerator.cs:7984

csharp
if (funcName == "__critical_leave")
            {
                if (args.Length != 1) Error(expr, "__critical_leave(token) expects 1 argument");

                CompileIntoA(args[0]);
                EmitAsm("LD_B_A"); // token

                AsmOperand clAfterDec = MakeUniqueLabel("critleave_afterdec");
                AsmOperand clDone = MakeUniqueLabel("critleave_done");

                EmitLoadA(CriticalDepthVar);
                EmitAsm("OR_A");
                EmitAsm("JR_Z", clAfterDec);
                EmitAsm("DEC_A");
                EmitStoreA(CriticalDepthVar);
                EmitLabel(clAfterDec);

                // Re-enable only for outermost token (token==0).
                EmitAsm("LD_A_B");
                EmitAsm("OR_A");
                EmitAsm("JR_NZ", clDone);
                EmitAsm("EI");
                EmitLabel(clDone);
                return;
            }
__dot2_q8_8

Calcule un produit scalaire non signé à deux termes.

Syntaxe

c
u16 __dot2_q8_8(u16 a0, u16 a1, u8 b0, u8 b1);

Paramètres

a0

Mot non signé de 0 à 65535.

a1

Mot non signé de 0 à 65535.

b0

Coefficient non signé de 0 à 255.

b1

Coefficient non signé de 0 à 255.

Valeur renvoyée

Chaque terme vaut floor(a*b/256). Additionne les termes et renvoie les 16 bits de poids faible.

Conditions et limites

Sur GB, les produits sont divisés par 256 ; les résultats signés sont tronqués vers zéro. Les variantes Q1.7 doublent ce résultat tronqué et peuvent différer d’une unité d’une division exacte par 128. Chaque terme est arrondi séparément ; l’addition finale reboucle sur 16 bits.

Exemple d’utilisation

c
// Compare literal coefficients with values read from RAM.
result[36]=__dot2_q8_8(513,769,64,127);
v0=513;v1=769;v2=64;v3=127;
result[37]=__dot2_q8_8(v0,v1,v2,v3);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Comparez les entrées littérales aux valeurs en RAM. Les résultats représentatifs sont affichés sur 16 bits non signés ; les négatifs utilisent le complément à deux. Le GB remet à l’échelle les produits à virgule fixe, tandis que le FC renvoie des produits entiers : des entrées identiques ne donnent donc pas nécessairement les mêmes résultats.

L’extrait stocke les valeurs suivantes sur 16 bits : result[36]=509, result[37]=509. Chaque calcul avec une entrée littérale correspond à son équivalent avec une entrée en RAM. L’écran affiche des valeurs représentatives ; le tableau result conserve tous les résultats.

__dot2_q8_8: DMG
DMG
__dot2_q8_8: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:11575

kitaqgb/kitaqgb/CodeGenerator.cs:11575

csharp
if (funcName == "__dot2_q8_8")
{
    if (args.Length != 4) Error(expr, "__dot2_q8_8 requires 4 arguments");
    LastCallReturnsHL = true;

    // Fast paths when coefficients are compile-time constants.
    // Special-case: 0 / power-of-two coefficients (u8) => shift instead of mul.
    if (TryGetU8Const(args[2], out int ax0) && (ax0 == 0) && TryGetU8Const(args[3], out int ay0) && (ay0 == 0))
    {
        CompileDiscard(args[0]);
        CompileDiscard(args[1]);
        EmitAsm("XOR_A");
        EmitAsm("LD_H_A");
        EmitAsm("LD_L_A");
        return;
    }
    if (TryGetU8Const(args[2], out int axOnly) && (axOnly == 0))
    {
        CompileDiscard(args[0]);
        // only y*ay
        if (!TryEmitDot2Pow2Term(args[1], args[3], signed: false, q1_7: false, out _))
        {
            CompileIntoHL(args[1]);
            EmitAsm("PUSH_HL");
            CompileIntoA(args[3]);
            EmitAsm("LD_C_A");
            EmitAsm("POP_HL");
            EmitMul16x8Shr8_HL("__dot2_y");
        }
        return;
    }
    if (TryGetU8Const(args[3], out int ayOnly) && (ayOnly == 0))
    {
        // only x*ax
        if (!TryEmitDot2Pow2Term(args[0], args[2], signed: false, q1_7: false, out _))
        {
            CompileIntoHL(args[0]);
            EmitAsm("PUSH_HL");
            CompileIntoA(args[2]);
            EmitAsm("LD_C_A");
            EmitAsm("POP_HL");
            EmitMul16x8Shr8_HL("__dot2_x");
        }
        // Preserve the scaled result while evaluating the zero-product input.
        EmitAsm("PUSH_HL");
        CompileDiscard(args[1]);
        EmitAsm("POP_HL");
        return;
    }

    // x*ax
    if (!TryEmitDot2Pow2Term(args[0], args[2], signed: false, q1_7: false, out _))
    {
        CompileIntoHL(args[0]);
        EmitAsm("PUSH_HL");
        CompileIntoA(args[2]);
        EmitAsm("LD_C_A");
        EmitAsm("POP_HL");
        EmitMul16x8Shr8_HL("__dot2_x");
    }
    EmitAsm("PUSH_HL");

    // y*ay
    if (!TryEmitDot2Pow2Term(args[1], args[3], signed: false, q1_7: false, out _))
    {
        CompileIntoHL(args[1]);
        EmitAsm("PUSH_HL");
        CompileIntoA(args[3]);
        EmitAsm("LD_C_A");
        EmitAsm("POP_HL");
        EmitMul16x8Shr8_HL("__dot2_y");
    }
    EmitAsm("POP_DE");
    EmitAsm("ADD_HL_DE");
    return;
}
__dot3_q8_8

Calcule un produit scalaire non signé à trois termes.

Syntaxe

c
u16 __dot3_q8_8(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);

Paramètres

a0

Mot non signé de 0 à 65535.

a1

Mot non signé de 0 à 65535.

a2

Mot non signé de 0 à 65535.

b0

Coefficient non signé de 0 à 255.

b1

Coefficient non signé de 0 à 255.

b2

Coefficient non signé de 0 à 255.

Valeur renvoyée

Chaque terme vaut floor(a*b/256). Additionne les termes et renvoie les 16 bits de poids faible.

Conditions et limites

Sur GB, les produits sont divisés par 256 ; les résultats signés sont tronqués vers zéro. Les variantes Q1.7 doublent ce résultat tronqué et peuvent différer d’une unité d’une division exacte par 128. Chaque terme est arrondi séparément ; l’addition finale reboucle sur 16 bits.

Exemple d’utilisation

c
// Compare literal coefficients with values read from RAM.
result[6]=__dot3_q8_8(513,769,1025,64,127,3);
v0=513;v1=769;v2=1025;v3=64;v4=127;v5=3;
result[7]=__dot3_q8_8(v0,v1,v2,v3,v4,v5);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Comparez les entrées littérales aux valeurs en RAM. Les résultats représentatifs sont affichés sur 16 bits non signés ; les négatifs utilisent le complément à deux. Le GB remet à l’échelle les produits à virgule fixe, tandis que le FC renvoie des produits entiers : des entrées identiques ne donnent donc pas nécessairement les mêmes résultats.

L’extrait stocke les valeurs suivantes sur 16 bits : result[6]=521, result[7]=521. Chaque calcul avec une entrée littérale correspond à son équivalent avec une entrée en RAM. L’écran affiche des valeurs représentatives ; le tableau result conserve tous les résultats.

__dot3_q8_8: DMG
DMG
__dot3_q8_8: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:11550

kitaqgb/kitaqgb/CodeGenerator.cs:11550

csharp
if (funcName == "__dot3_q8_8")
            {
                if (args.Length != 6) Error(expr, "__dot3_q8_8 requires 6 arguments");
                LastCallReturnsHL = true;

                // x*ax
                EmitDotTermToHL(args[0], args[3], signed: false, q1_7: false, labelPrefix: "__dot3_x");
                EmitAsm("PUSH_HL");

                // y*ay
                EmitDotTermToHL(args[1], args[4], signed: false, q1_7: false, labelPrefix: "__dot3_y");
                EmitAsm("POP_DE");
                EmitAsm("ADD_HL_DE");
                EmitAsm("PUSH_HL");

                // z*az
                EmitDotTermToHL(args[2], args[5], signed: false, q1_7: false, labelPrefix: "__dot3_z");
                EmitAsm("POP_DE");
                EmitAsm("ADD_HL_DE");
                return;
            }
__farcall

Appelle une fonction nommée sans argument avec le numéro de banque fourni. Si une commutation est nécessaire, une routine en ROM fixe restaure la banque de l’appelant au retour.

Syntaxe

c
__farcall(bank, function_name);

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Paramètres

bank

Numéro de banque ROM non signé sur 8 bits. Pour un appel, il doit correspondre au placement de la fonction ; pour une lecture, il sélectionne la source. Respectez la plage du type de cartouche.

func

Nom déclaré d’une fonction sans paramètre. Cet appel intrinsèque par nom n’accepte pas une variable contenant une adresse de fonction. L’argument bank n’est pas transmis au rappel.

Valeur renvoyée

Le résultat suit le type de la fonction appelée. Un rappel void ne renvoie rien ; les rappels u8 et u16 renvoient leur valeur scalaire. L’exemple affiché utilise des rappels void.

Conditions et limites

Ces appels ne transmettent pas d’arguments utilisateur et ne rendent pas le rappel réentrant face aux interruptions. Gérez correctement ses données, sa pile et ses interruptions. La restauration des banques est testée avec des appels imbriqués et répétés ; l’exemple ne couvre ni le matériel physique ni tous les entrelacements d’interruptions.

Ces interfaces GB transportent huit bits de banque et ne règlent pas le neuvième bit MBC5. Les règles de la cartouche restent applicables, notamment pour la banque zéro. L’exemple utilise explicitement les banques MBC5 1–3 ; ne mélangez pas cet état avec des écritures directes non suivies dans le mapper.

Exemple d’utilisation

c
__farcall(3,callback);
bank_expect(callback_count==2 && callback_byte==0xD3,16);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

À ce stade du programme complet, __farcall porte callback_count à 2. Le rappel lit D3 dans la banque 3. Une lecture proche ultérieure de B2 dans home2 confirme le retour avec la banque 2 sélectionnée.

Le programme complet montre comment identifier des données par une banque et une adresse proche, appeler des rappels entre banques et distinguer l’état de la bibliothèque du mappage réel. Tous les nombres sont hexadécimaux. BANK IN et BANK OUT doivent valoir 02 ; BYTE HEX, D3 ; WORD HEX, 5C7A ; FAILED CHECKS et FIRST FAILURE, 00. Le texte est bleu sur CGB et FC, noir sur DMG.

CALL COUNT vaut 03 : far_call, __farcall et __farcall_ptr invoquent chacun le rappel une fois.

__farcall: DMG
DMG
__farcall: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\banked_data_calls.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\banked_data_calls.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:7288

kitaqgb/kitaqgb/CodeGenerator.cs:7288

csharp
if (funcName == "__farcall")
            {
                if (args.Length != 2) Program.Error("__farcall(bank, func) expects 2 arguments");
                if (!args[1].Match(Tag.Name, out string targetName)) Program.Error("__farcall(bank, func) requires 2nd argument to be a function name");

                // A named far call supplies no arguments. Reject data symbols
                // and callbacks that would otherwise read stale argument storage.
                CFunctionInfo targetInfo;
                if (!Functions.TryGetValue(targetName, out targetInfo))
                {
                    Error(expr, "__farcall requires a declared function name");
                    return;
                }
                if ((targetInfo.Parameters ?? Array.Empty<FieldInfo>()).Length != 0)
                {
                    Error(expr, "__farcall requires a callback with no parameters");
                    return;
                }
                Expr bankExpr = FoldConstants(args[0]);
                int targetBank = -1;
                if (targetInfo != null)
                {
                    targetBank = targetInfo.RomBank;
                    PrepareStructReturnDestination(expr, targetInfo.ReturnType, targetInfo, targetName);
                    if (targetInfo.ReturnType != null)
                        LastCallReturnsHL = ReturnsInHL(expr, targetInfo.ReturnType);
                }

                // A known bank permits direct dispatch when safe, otherwise request a fixed-bank thunk.
                if (TryResolveBankExprToConstU8(args[0], out int bankLiteral))
                {
                    int optimizedBank = bankLiteral & 0xFF;
                    bool farcallDirectOK = IsDirectCallBankSafe(optimizedBank, CurrentFunctionBank);
                    if (!farcallDirectOK)
                    {
                        string farcallThunkName = EnsureBank0Thunk(
                            targetName,
                            optimizedBank,
                            isStackCall: targetInfo != null && targetInfo.IsStackCall,
                            stackArgBytes: 0);
                        EmitAsm("CALL", new AsmOperand(farcallThunkName, AddressMode.Absolute));
                        RecordCallEdge(expr, targetName, optimizedBank, "farcall_bank_thunk", viaThunk: true, viaFarcall: true, actualArgSizes: new int[0], expectedArgSizes: new int[0]);
                    }
                    else
                    {
                        if (Program.CheckBankCalls && !InUnsafe && optimizedBank != 0)
                        {
                            AsmOperand ok = MakeUniqueLabel("kq_bank_ok");
                            EmitLoadA(RomBankVar);
                            EmitAsm("CP_IMM", new AsmOperand(optimizedBank & 0xFF, AddressMode.Immediate));
                            EmitAsm("JP_Z", ok);
                            EmitAsm("JP", EnsureCheckTrap());
                            EmitLabel(ok);
                        }

                        EmitAsm("CALL", new AsmOperand(targetName, AddressMode.Absolute));
                        RecordCallEdge(expr, targetName, optimizedBank, "farcall_direct", viaThunk: false, viaFarcall: true, actualArgSizes: new int[0], expectedArgSizes: new int[0]);
                    }
                    return;
                }

                // A = requested bank argument (runtime value)
                CompileIntoA(bankExpr);

                // Always dispatch via bank0 helper thunk.
                string thunkName = EnsureBank0RuntimeFarcallThunk(targetName);
                EmitAsm("CALL", new AsmOperand(thunkName, AddressMode.Absolute));
                RecordCallEdge(expr, targetName, targetBank, "farcall_intrinsic", viaThunk: true, viaFarcall: true, actualArgSizes: new int[0], expectedArgSizes: new int[0]);
                return;
            }
__getwinmapbase

Lit le bit 6 de LCDC pour choisir l’adresse de base de la carte de tuiles de la fenêtre.

Syntaxe

c
u16 __getwinmapbase();

Valeur renvoyée

Renvoie $9800 ou $9C00, la base de la carte de tuiles et non l’adresse des graphismes.

Exemple d’utilisation

c
M_LCDC=0;result[2]=__getwinmapbase();M_LCDC=0x40;result[3]=__getwinmapbase();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Découvrez sélection de carte, défilement, imbrication et attentes de ligne. Les bases valent 38912/39936 ; les jetons imbriqués, 0/1 ; les LY cibles observés, 80/144. Vérifiez aussi que les attentes reviennent avec le LCD éteint.

__getwinmapbase: DMG
DMG
__getwinmapbase: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_display.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_display.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:10642

kitaqgb/kitaqgb/CodeGenerator.cs:10642

csharp
if (funcName == "__getwinmapbase")
{
    if (args.Length != 0) Error(expr, "__getwinmapbase requires 0 arguments");

    AsmOperand win_base0 = MakeUniqueLabel("getwinbase_base0");
    AsmOperand win_done  = MakeUniqueLabel("getwinbase_done");

    EmitAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem)); // LCDC
    EmitAsm("AND_IMM", new AsmOperand(0x40, AddressMode.Immediate)); // bit6 (Window tile map display select)
    EmitAsm("JR_Z", win_base0);
    EmitAsm("LD_HL_IMM", new AsmOperand(0x9C00, AddressMode.Immediate16));
    EmitAsm("JR", win_done);
    EmitLabel(win_base0);
    EmitAsm("LD_HL_IMM", new AsmOperand(0x9800, AddressMode.Immediate16));
    EmitLabel(win_done);
    LastCallReturnsHL = true;
    return;
}
__mac16

Calcule une multiplication-accumulation non signée.

Syntaxe

c
u16 __mac16(u16 acc, u16 a, u8 b);

Paramètres

acc

Accumulateur sur 16 bits à ajouter.

a

Mot non signé de 0 à 65535.

b

Coefficient non signé de 0 à 255.

Valeur renvoyée

Chaque terme vaut floor(a*b/256) et est ajouté à acc ; renvoie les 16 bits de poids faible.

Conditions et limites

Sur GB, les produits sont divisés par 256 ; les résultats signés sont tronqués vers zéro. Les variantes Q1.7 doublent ce résultat tronqué et peuvent différer d’une unité d’une division exacte par 128. Chaque terme est arrondi séparément ; l’addition finale reboucle sur 16 bits.

Exemple d’utilisation

c
// Compare literal coefficients with values read from RAM.
result[34]=__mac16(65500,513,127);
v0=65500;v1=513;v2=127;
result[35]=__mac16(v0,v1,v2);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Comparez les entrées littérales aux valeurs en RAM. Les résultats représentatifs sont affichés sur 16 bits non signés ; les négatifs utilisent le complément à deux. Le GB remet à l’échelle les produits à virgule fixe, tandis que le FC renvoie des produits entiers : des entrées identiques ne donnent donc pas nécessairement les mêmes résultats.

L’extrait stocke les valeurs suivantes sur 16 bits : result[34]=218, result[35]=218. Chaque calcul avec une entrée littérale correspond à son équivalent avec une entrée en RAM. L’écran affiche des valeurs représentatives ; le tableau result conserve tous les résultats.

__mac16: DMG
DMG
__mac16: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:11499

kitaqgb/kitaqgb/CodeGenerator.cs:11499

csharp
if (funcName == "__mac16")
            {
                if (args.Length != 3) Error(expr, "__mac16 requires 3 arguments");

                // mul = (a*b)>>8
                EmitDotTermToHL(args[1], args[2], signed: false, q1_7: false, labelPrefix: "__mac16_mul");

                // acc + mul
                EmitAsm("PUSH_HL");
                CompileIntoHL(args[0]);
                EmitAsm("POP_DE");
                EmitAsm("ADD_HL_DE");
                LastCallReturnsHL = true;
                return;
            }
__mul16x8

Calcule une multiplication non signée.

Syntaxe

c
u16 __mul16x8(u16 a, u8 b);

Paramètres

a

Mot non signé de 0 à 65535.

b

Coefficient non signé de 0 à 255.

Valeur renvoyée

Chaque terme vaut floor(a*b/256) ; renvoie les 16 bits de poids faible.

Conditions et limites

Sur GB, les produits sont divisés par 256 ; les résultats signés sont tronqués vers zéro. Les variantes Q1.7 doublent ce résultat tronqué et peuvent différer d’une unité d’une division exacte par 128. Chaque terme est arrondi séparément ; l’addition finale reboucle sur 16 bits.

Exemple d’utilisation

c
// Compare literal coefficients with values read from RAM.
result[24]=__mul16x8(513,127);
v0=513;v1=127;
result[25]=__mul16x8(v0,v1);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Comparez les entrées littérales aux valeurs en RAM. Les résultats représentatifs sont affichés sur 16 bits non signés ; les négatifs utilisent le complément à deux. Le GB remet à l’échelle les produits à virgule fixe, tandis que le FC renvoie des produits entiers : des entrées identiques ne donnent donc pas nécessairement les mêmes résultats.

L’extrait stocke les valeurs suivantes sur 16 bits : result[24]=254, result[25]=254. Chaque calcul avec une entrée littérale correspond à son équivalent avec une entrée en RAM. L’écran affiche des valeurs représentatives ; le tableau result conserve tous les résultats.

__mul16x8: DMG
DMG
__mul16x8: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:11467

kitaqgb/kitaqgb/CodeGenerator.cs:11467

csharp
if (funcName == "__mul16x8")
            {
                if (args.Length != 2) Error(expr, "__mul16x8 requires 2 arguments");

                EmitDotTermToHL(args[0], args[1], signed: false, q1_7: false, labelPrefix: "__mul16x8");
                LastCallReturnsHL = true;
                return;
            }
__mul8x8_hi

Multiplie deux octets non signés et renvoie l’octet de poids fort.

Syntaxe

c
u8 __mul8x8_hi(u8 a, u8 b);

Paramètres

a

Octet non signé de 0 à 255.

b

Octet non signé de 0 à 255.

Valeur renvoyée

floor(a*b/256), dans l’intervalle 0..254.

Exemple d’utilisation

c
// Compare literal coefficients with values read from RAM.
result[4]=__mul8x8_hi(255,255);
v0=255;v1=255;
result[5]=__mul8x8_hi(v0,v1);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Comparez les entrées littérales aux valeurs en RAM. Les résultats représentatifs sont affichés sur 16 bits non signés ; les négatifs utilisent le complément à deux. Le GB remet à l’échelle les produits à virgule fixe, tandis que le FC renvoie des produits entiers : des entrées identiques ne donnent donc pas nécessairement les mêmes résultats.

L’extrait stocke les valeurs suivantes sur 16 bits : result[4]=254, result[5]=254. Chaque calcul avec une entrée littérale correspond à son équivalent avec une entrée en RAM. L’écran affiche des valeurs représentatives ; le tableau result conserve tous les résultats.

__mul8x8_hi: DMG
DMG
__mul8x8_hi: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:11452

kitaqgb/kitaqgb/CodeGenerator.cs:11452

csharp
if (funcName == "__mul8x8_hi")
            {
                if (args.Length != 2) Error(expr, "__mul8x8_hi requires 2 arguments");

                CompileIntoA(args[0]);
                EmitAsm("PUSH_AF");
                CompileIntoA(args[1]);
                EmitAsm("LD_C_A");
                EmitAsm("POP_AF");
                EmitAsm("LD_E_A");
                EmitMul8x8ToHL_EC("__mul8x8_hi");
                EmitAsm("LD_A_H");
                return;
            }
__padrep

Programme la répétition d’une seule action parmi celles autorisées par mask. Parmi les nouveaux appuis admissibles, choisit le bit actif de poids le plus faible et le renvoie immédiatement. Sinon, conserve l’action active tant qu’elle reste autorisée et maintenue. À sa fin, adopte le bit maintenu de poids le plus faible sans impulsion immédiate. En l’absence de bouton admissible maintenu, efface l’état d’exécution.

Syntaxe

c
u8 __padrep(u8* state, u8 keys, u8 trigger, u8 mask);

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Paramètres

state

Pointeur vers cinq octets consécutifs de RAM modifiable, par exemple u8 state[5]. Disposition : [0] bit du bouton actif, [1] délai initial, [2] intervalle, [3] compteur du délai écoulé, [4] compteur de répétition écoulée. Prévoyez un état distinct pour chaque répétition indépendante et initialisez-le avant utilisation. Un pointeur nul, une zone de moins de cinq octets ou le partage d’un état entre variantes directionnelles et générale ne sont pas pris en charge.

keys

Masque complet des boutons maintenus au format GB BTN_*. Fournissez le résultat de __readpad, l’octet faible de __readpadex ou celui de input_current ; la fonction de répétition ne lit pas le matériel.

trigger

Bits nouvellement pressés pendant cette mise à jour, au même format que keys : en général keys & ~previous_keys ou l’octet fort de __readpadex. N’incluez que des bits également présents dans keys. Passer systématiquement le masque de maintien relance à tort le nouvel appui à chaque appel.

mask

Bits de boutons GB autorisés, combinés avec |. Zéro désactive toutes les actions et efface l’état d’exécution. Plusieurs bits offrent plusieurs candidats, mais la fonction ne renvoie qu’un seul bit sélectionné par appel, jamais un masque combinant plusieurs impulsions.

Valeur renvoyée

Zéro si aucune impulsion n’est due ; sinon, une des valeurs suivantes : 1, 2, 4, 8, 16, 32, 64, 128. Le résultat non nul est un bit de bouton, pas un booléen normalisé ni un nombre de répétitions.

Conditions et limites

Masques communs : BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80. Un bit à 1 indique un bouton maintenu. Ce format s’applique à input_* sur GB et FC ; les lectures brutes FC utilisent un autre ordre.

Délai initial de 0 à 255 appels à la fonction de répétition. Pour une valeur positive N, la première répétition survient exactement N appels après l’impulsion du nouvel appui. Avec 0, le cas d’égalité du délai initial est sauté et l’intervalle détermine la première répétition. Il ne s’agit pas de millisecondes.

Intervalle des répétitions suivantes, de 0 à 255 appels. Un N positif produit une impulsion toutes les N mises à jour admissibles de maintien après la première répétition. Zéro en produit une à chaque mise à jour admissible après le délai initial ; à ce stade, 0 et 1 donnent donc tous deux une impulsion par mise à jour. Si les deux délais valent 0, chaque mise à jour de maintien produit une impulsion.

Appelez la fonction une fois par cycle d’entrée prévu. Chaque appel modifie l’état sans attendre le VBlank. L’adoption d’un bouton maintenu sans nouvel appui renvoie 0 pour cet appel et démarre un nouveau délai. Ces fonctions choisissent une action active par état ; input_repeat entretient des temporisateurs indépendants pour les huit boutons.

Exemple d’utilisation

c
result=__padrep(actions,keys,trigger,48); // A|B: select A, the lower bit.
    if (result!=0) action_pulses++;

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Ce programme fournit des états de boutons prédéfinis à quatre états indépendants pour montrer quand une répétition se produit. Avec un délai de 3 et un intervalle de 2, huit appels numérotés de 0 à 7 produisent des impulsions aux appels 0, 3, 5 et 7. RIGHT PULSES, DOWN PULSES, ACTION PULSES et ALIAS PULSES affichent donc 004. Les variantes générales reçoivent A et B ensemble et sélectionnent A (16). Le programme vérifie aussi l’initialisation, la remise à zéro, la priorité des directions, le relâchement, l’adoption d’une direction déjà maintenue, les délais nuls et un argument contenant une expression. CHECKS affiche 057 et FAILED CHECKS doit afficher 000. Cet exemple déterministe traite des états prédéfinis ; il n’attend pas d’appui de votre part.

__padrep: DMG
DMG
__padrep: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\pad_repeat_state.c -I .\kitaq-docs\samples -I .\kitaqgb\lib -o .\out\pad_repeat_state.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:11249

csharp
{
    if (args.Length != 4) Error(expr, "__padrep requires 4 arguments (state*, keys, trigger, mask)");

    AsmOperand pm_done        = MakeUniqueLabel("padrepM_done");
    AsmOperand pm_ret0        = MakeUniqueLabel("padrepM_ret0");
    AsmOperand pm_no_trig     = MakeUniqueLabel("padrepM_notrig");
    AsmOperand pm_reset_all   = MakeUniqueLabel("padrepM_reset");
    AsmOperand pm_dir0        = MakeUniqueLabel("padrepM_dir0");
    AsmOperand pm_have_dir    = MakeUniqueLabel("padrepM_havedir");
    AsmOperand pm_first_rep   = MakeUniqueLabel("padrepM_firstrep");
    AsmOperand pm_ret_dir     = MakeUniqueLabel("padrepM_retdir");
    AsmOperand pm_skip_dasinc = MakeUniqueLabel("padrepM_skipdasinc");
    AsmOperand pm_skip_arrinc = MakeUniqueLabel("padrepM_skiparrinc");

    // Evaluate args safely:
    // HL = state*, B = keys, C = trigger, E = mask
    CompileIntoHL(args[0]);
    EmitAsm("PUSH_HL");
    CompileIntoA(args[1]); EmitAsm("LD_B_A"); // keys
    CompileIntoA(args[2]); EmitAsm("LD_C_A"); // trigger
    CompileIntoA(args[3]); EmitAsm("LD_E_A"); // mask
    EmitAsm("POP_HL");

    // trigMasked = trigger & mask -> D
    EmitAsm("LD_A_C");
    EmitAsm("AND_E");
    EmitAsm("LD_D_A");
    EmitAsm("OR_A");
    EmitAsm("JP_Z", pm_no_trig);

    // select = lowbit(trigMasked) = x & -x
    EmitAsm("LD_A_D");
    EmitAsm("CPL");
    EmitAsm("INC_A");
    EmitAsm("AND_D");
    EmitAsm("LD_C_A"); // save selected in C

    // dirMask=selected, dasCnt=0, arrCnt=0
    EmitAsm("LD_A_C");
    EmitAsm("LD_HL_A"); // [0]=selected
    EmitAsm("PUSH_HL");
    EmitAsm("INC_HL"); Emi
__padrep_down

Avance d’un pas la répétition réservée à la direction bas. Un nouvel appui vers le bas renvoie immédiatement 8 et redémarre les compteurs. Le maintien produit ensuite des impulsions selon les délais de l’état ; le relâchement efface l’état d’exécution. Les autres boutons ne déclenchent pas cette répétition.

Syntaxe

c
u8 __padrep_down(u8* state, u8 keys, u8 trigger);

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Paramètres

state

Pointeur vers cinq octets consécutifs de RAM modifiable, par exemple u8 state[5]. Disposition : [0] bit du bouton actif, [1] délai initial, [2] intervalle, [3] compteur du délai écoulé, [4] compteur de répétition écoulée. Prévoyez un état distinct pour chaque répétition indépendante et initialisez-le avant utilisation. Un pointeur nul, une zone de moins de cinq octets ou le partage d’un état entre variantes directionnelles et générale ne sont pas pris en charge.

keys

Masque complet des boutons maintenus au format GB BTN_*. Fournissez le résultat de __readpad, l’octet faible de __readpadex ou celui de input_current ; la fonction de répétition ne lit pas le matériel.

trigger

Bits nouvellement pressés pendant cette mise à jour, au même format que keys : en général keys & ~previous_keys ou l’octet fort de __readpadex. N’incluez que des bits également présents dans keys. Passer systématiquement le masque de maintien relance à tort le nouvel appui à chaque appel.

Valeur renvoyée

Zéro si aucune impulsion n’est due ; sinon, une des valeurs suivantes : 8. Le résultat non nul est un bit de bouton, pas un booléen normalisé ni un nombre de répétitions.

Conditions et limites

Masques communs : BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80. Un bit à 1 indique un bouton maintenu. Ce format s’applique à input_* sur GB et FC ; les lectures brutes FC utilisent un autre ordre.

Délai initial de 0 à 255 appels à la fonction de répétition. Pour une valeur positive N, la première répétition survient exactement N appels après l’impulsion du nouvel appui. Avec 0, le cas d’égalité du délai initial est sauté et l’intervalle détermine la première répétition. Il ne s’agit pas de millisecondes.

Intervalle des répétitions suivantes, de 0 à 255 appels. Un N positif produit une impulsion toutes les N mises à jour admissibles de maintien après la première répétition. Zéro en produit une à chaque mise à jour admissible après le délai initial ; à ce stade, 0 et 1 donnent donc tous deux une impulsion par mise à jour. Si les deux délais valent 0, chaque mise à jour de maintien produit une impulsion.

Appelez la fonction une fois par cycle d’entrée prévu. Chaque appel modifie l’état sans attendre le VBlank. L’adoption d’un bouton maintenu sans nouvel appui renvoie 0 pour cet appel et démarre un nouveau délai. Ces fonctions choisissent une action active par état ; input_repeat entretient des temporisateurs indépendants pour les huit boutons.

Exemple d’utilisation

c
result=__padrep_down(down,keys,trigger);
    if (result!=0) down_pulses++;

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Ce programme fournit des états de boutons prédéfinis à quatre états indépendants pour montrer quand une répétition se produit. Avec un délai de 3 et un intervalle de 2, huit appels numérotés de 0 à 7 produisent des impulsions aux appels 0, 3, 5 et 7. RIGHT PULSES, DOWN PULSES, ACTION PULSES et ALIAS PULSES affichent donc 004. Les variantes générales reçoivent A et B ensemble et sélectionnent A (16). Le programme vérifie aussi l’initialisation, la remise à zéro, la priorité des directions, le relâchement, l’adoption d’une direction déjà maintenue, les délais nuls et un argument contenant une expression. CHECKS affiche 057 et FAILED CHECKS doit afficher 000. Cet exemple déterministe traite des états prédéfinis ; il n’attend pas d’appui de votre part.

__padrep_down: DMG
DMG
__padrep_down: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\pad_repeat_state.c -I .\kitaq-docs\samples -I .\kitaqgb\lib -o .\out\pad_repeat_state.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:11069

csharp
{
    if (args.Length != 3) Error(expr, "__padrep_down requires 3 arguments (state*, keys, trigger)");

    AsmOperand pd_done        = MakeUniqueLabel("padrepD_done");
    AsmOperand pd_ret0        = MakeUniqueLabel("padrepD_ret0");
    AsmOperand pd_no_trig     = MakeUniqueLabel("padrepD_notrig");
    AsmOperand pd_dir0        = MakeUniqueLabel("padrepD_dir0");
    AsmOperand pd_have_dir    = MakeUniqueLabel("padrepD_havedir");
    AsmOperand pd_first_rep   = MakeUniqueLabel("padrepD_firstrep");
    AsmOperand pd_ret_dir     = MakeUniqueLabel("padrepD_retdir");
    AsmOperand pd_skip_dasinc = MakeUniqueLabel("padrepD_skipdasinc");
    AsmOperand pd_skip_arrinc = MakeUniqueLabel("padrepD_skiparrinc");
    AsmOperand pd_reset_all   = MakeUniqueLabel("padrepD_reset");

    // Evaluate args safely:
    // HL = state*, B = keys, C = trigger
    CompileIntoHL(args[0]);
    EmitAsm("PUSH_HL");
    CompileIntoA(args[1]); EmitAsm("LD_B_A"); // keys
    CompileIntoA(args[2]); EmitAsm("LD_C_A"); // trigger
    EmitAsm("POP_HL");

    // --- Trigger Down? (bit3 = 0x08) ---
    EmitAsm("LD_A_C");
    EmitAsm("AND_IMM", new AsmOperand(0x08, AddressMode.Immediate));
    EmitAsm("JP_Z", pd_no_trig);

    // dirMask=0x08, dasCnt=0, arrCnt=0
    EmitAsm("LD_A_IMM", new AsmOperand(0x08, AddressMode.Immediate));
    EmitAsm("LD_HL_A"); // [0]=0x08
    EmitAsm("PUSH_HL");
    EmitAsm("INC_HL"); EmitAsm("INC_HL"); EmitAsm("INC_HL"); // -> [3]
    EmitAsm("XOR_A");
    EmitAsm("LD_HL_A"); // dasCnt=0
    EmitAsm("INC_HL");
    EmitAsm("LD_HL_A"); // arrCnt=0
    EmitAsm("POP_HL");

    EmitAsm
__padrep_init

Initialise les cinq octets d’état fournis par le programme : efface l’action active et les deux compteurs, puis enregistre le délai initial et l’intervalle de répétition. Ne lit aucun bouton et ne produit aucune impulsion.

Syntaxe

c
void __padrep_init(u8* state, u8 das, u8 arr);

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Paramètres

state

Pointeur vers cinq octets consécutifs de RAM modifiable, par exemple u8 state[5]. Disposition : [0] bit du bouton actif, [1] délai initial, [2] intervalle, [3] compteur du délai écoulé, [4] compteur de répétition écoulée. Prévoyez un état distinct pour chaque répétition indépendante et initialisez-le avant utilisation. Un pointeur nul, une zone de moins de cinq octets ou le partage d’un état entre variantes directionnelles et générale ne sont pas pris en charge.

das

Délai initial de 0 à 255 appels à la fonction de répétition. Pour une valeur positive N, la première répétition survient exactement N appels après l’impulsion du nouvel appui. Avec 0, le cas d’égalité du délai initial est sauté et l’intervalle détermine la première répétition. Il ne s’agit pas de millisecondes.

arr

Intervalle des répétitions suivantes, de 0 à 255 appels. Un N positif produit une impulsion toutes les N mises à jour admissibles de maintien après la première répétition. Zéro en produit une à chaque mise à jour admissible après le délai initial ; à ce stade, 0 et 1 donnent donc tous deux une impulsion par mise à jour. Si les deux délais valent 0, chaque mise à jour de maintien produit une impulsion.

Valeur renvoyée

Aucune (void).

Exemple d’utilisation

c
__padrep_init(lr,3,2);
// Active action and counters are zero; delay and interval are retained.
expect(lr[0],0); expect(lr[1],3); expect(lr[2],2);
expect(lr[3],0); expect(lr[4],0);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Ce programme fournit des états de boutons prédéfinis à quatre états indépendants pour montrer quand une répétition se produit. Avec un délai de 3 et un intervalle de 2, huit appels numérotés de 0 à 7 produisent des impulsions aux appels 0, 3, 5 et 7. RIGHT PULSES, DOWN PULSES, ACTION PULSES et ALIAS PULSES affichent donc 004. Les variantes générales reçoivent A et B ensemble et sélectionnent A (16). Le programme vérifie aussi l’initialisation, la remise à zéro, la priorité des directions, le relâchement, l’adoption d’une direction déjà maintenue, les délais nuls et un argument contenant une expression. CHECKS affiche 057 et FAILED CHECKS doit afficher 000. Cet exemple déterministe traite des états prédéfinis ; il n’attend pas d’appui de votre part.

__padrep_init: DMG
DMG
__padrep_init: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\pad_repeat_state.c -I .\kitaq-docs\samples -I .\kitaqgb\lib -o .\out\pad_repeat_state.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:10808

csharp
{
    if (args.Length != 3) Error(expr, "__padrep_init requires 3 arguments (state*, das, arr)");

    // Evaluate args with register safety
    CompileIntoHL(args[0]);
    EmitAsm("PUSH_HL");
    CompileIntoA(args[1]); EmitAsm("LD_B_A"); // das
    CompileIntoA(args[2]); EmitAsm("LD_C_A"); // arr
    EmitAsm("POP_HL");

    // dirMask = 0; dasCnt=0; arrCnt=0; store params
    EmitAsm("XOR_A");
    EmitAsm("LD_HL_A"); // [0]=0
    EmitAsm("INC_HL");
    EmitAsm("LD_A_B");
    EmitAsm("LD_HL_A"); // [1]=das
    EmitAsm("INC_HL");
    EmitAsm("LD_A_C");
    EmitAsm("LD_HL_A"); // [2]=arr
    EmitAsm("INC_HL");
    EmitAsm("XOR_A");
    EmitAsm("LD_HL_A"); // [3]=dasCnt
    EmitAsm("INC_HL");
    EmitAsm("LD_HL_A"); // [4]=arrCnt
    return;
}

// 2.21 __padrep_reset(state_ptr) -> void
// Resets runtime counters for PadRepeatState while keeping das/arr params:
// dirMask=0, dasCnt=0, arrCnt=0
// Treated as a compiler intrinsic (no C definition required).
if (funcName == "__padrep_reset")
{
    if (args.Length != 1) Error(expr, "__padrep_reset requires 1 argument (state*)");

    CompileIntoHL(args[0]);

    EmitAsm("XOR_A");
    EmitAsm("LD_HL_A"); // [0]=0 (dirMask)

    // [3]=0, [4]=0
    EmitAsm("PUSH_HL");
    EmitAsm("INC_HL"); EmitAsm("INC_HL"); EmitAsm("INC_HL"); // -> [3]
    EmitAsm("LD_HL_A");
    EmitAsm("INC_HL"); // -> [4]
    EmitAsm("LD_HL_A");
    EmitAsm("POP_HL");
    return;
}

// __padrep_lr(state*, keys, trigger) returns Right (1), Left (2), or zero.
// New triggers take priority, with Right selected first when both trigger bits are set.
// Without a new
__padrep_lr

Avance d’un pas la répétition d’une seule direction, gauche ou droite. Un nouvel appui à droite renvoie immédiatement 1 ; à défaut, un nouvel appui à gauche renvoie 2. Si les deux sont nouveaux, droite est prioritaire. Sans nouvel appui, maintenir les deux directions renvoie 0 et fige les compteurs ; n’en maintenir aucune efface l’état d’exécution.

Syntaxe

c
u8 __padrep_lr(u8* state, u8 keys, u8 trigger);

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Paramètres

state

Pointeur vers cinq octets consécutifs de RAM modifiable, par exemple u8 state[5]. Disposition : [0] bit du bouton actif, [1] délai initial, [2] intervalle, [3] compteur du délai écoulé, [4] compteur de répétition écoulée. Prévoyez un état distinct pour chaque répétition indépendante et initialisez-le avant utilisation. Un pointeur nul, une zone de moins de cinq octets ou le partage d’un état entre variantes directionnelles et générale ne sont pas pris en charge.

keys

Masque complet des boutons maintenus au format GB BTN_*. Fournissez le résultat de __readpad, l’octet faible de __readpadex ou celui de input_current ; la fonction de répétition ne lit pas le matériel.

trigger

Bits nouvellement pressés pendant cette mise à jour, au même format que keys : en général keys & ~previous_keys ou l’octet fort de __readpadex. N’incluez que des bits également présents dans keys. Passer systématiquement le masque de maintien relance à tort le nouvel appui à chaque appel.

Valeur renvoyée

Zéro si aucune impulsion n’est due ; sinon, une des valeurs suivantes : 1, 2. Le résultat non nul est un bit de bouton, pas un booléen normalisé ni un nombre de répétitions.

Conditions et limites

Masques communs : BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80. Un bit à 1 indique un bouton maintenu. Ce format s’applique à input_* sur GB et FC ; les lectures brutes FC utilisent un autre ordre.

Délai initial de 0 à 255 appels à la fonction de répétition. Pour une valeur positive N, la première répétition survient exactement N appels après l’impulsion du nouvel appui. Avec 0, le cas d’égalité du délai initial est sauté et l’intervalle détermine la première répétition. Il ne s’agit pas de millisecondes.

Intervalle des répétitions suivantes, de 0 à 255 appels. Un N positif produit une impulsion toutes les N mises à jour admissibles de maintien après la première répétition. Zéro en produit une à chaque mise à jour admissible après le délai initial ; à ce stade, 0 et 1 donnent donc tous deux une impulsion par mise à jour. Si les deux délais valent 0, chaque mise à jour de maintien produit une impulsion.

Appelez la fonction une fois par cycle d’entrée prévu. Chaque appel modifie l’état sans attendre le VBlank. L’adoption d’un bouton maintenu sans nouvel appui renvoie 0 pour cet appel et démarre un nouveau délai. Ces fonctions choisissent une action active par état ; input_repeat entretient des temporisateurs indépendants pour les huit boutons.

Exemple d’utilisation

c
result=__padrep_lr(lr,keys,trigger);
    if (result!=0) pulses++;

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Ce programme fournit des états de boutons prédéfinis à quatre états indépendants pour montrer quand une répétition se produit. Avec un délai de 3 et un intervalle de 2, huit appels numérotés de 0 à 7 produisent des impulsions aux appels 0, 3, 5 et 7. RIGHT PULSES, DOWN PULSES, ACTION PULSES et ALIAS PULSES affichent donc 004. Les variantes générales reçoivent A et B ensemble et sélectionnent A (16). Le programme vérifie aussi l’initialisation, la remise à zéro, la priorité des directions, le relâchement, l’adoption d’une direction déjà maintenue, les délais nuls et un argument contenant une expression. CHECKS affiche 057 et FAILED CHECKS doit afficher 000. Cet exemple déterministe traite des états prédéfinis ; il n’attend pas d’appui de votre part.

__padrep_lr: DMG
DMG
__padrep_lr: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\pad_repeat_state.c -I .\kitaq-docs\samples -I .\kitaqgb\lib -o .\out\pad_repeat_state.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:10864

csharp
{
    if (args.Length != 3) Error(expr, "__padrep_lr requires 3 arguments (state*, keys, trigger)");

    AsmOperand pr_done        = MakeUniqueLabel("padrep_done");
    AsmOperand pr_ret0        = MakeUniqueLabel("padrep_ret0");
    AsmOperand pr_check_left  = MakeUniqueLabel("padrep_chkL");
    AsmOperand pr_no_trig     = MakeUniqueLabel("padrep_notrig");
    AsmOperand pr_reset_all   = MakeUniqueLabel("padrep_reset");
    AsmOperand pr_dir0        = MakeUniqueLabel("padrep_dir0");
    AsmOperand pr_have_dir    = MakeUniqueLabel("padrep_havedir");
    AsmOperand pr_first_rep   = MakeUniqueLabel("padrep_firstrep");
    AsmOperand pr_ret_dir     = MakeUniqueLabel("padrep_retdir");
    AsmOperand pr_skip_dasinc = MakeUniqueLabel("padrep_skipdasinc");
    AsmOperand pr_skip_arrinc = MakeUniqueLabel("padrep_skiparrinc");

    // Evaluate args safely:
    // HL = state*, B = keys, C = trigger
    CompileIntoHL(args[0]);
    EmitAsm("PUSH_HL");
    CompileIntoA(args[1]); EmitAsm("LD_B_A"); // keys
    CompileIntoA(args[2]); EmitAsm("LD_C_A"); // trigger
    EmitAsm("POP_HL");

    // --- Trigger Right? (bit0) ---
    EmitAsm("LD_A_C");
    EmitAsm("AND_IMM", new AsmOperand(0x01, AddressMode.Immediate));
    EmitAsm("JP_Z", pr_check_left);

    // dirMask=0x01, dasCnt=0, arrCnt=0
    EmitAsm("LD_A_IMM", new AsmOperand(0x01, AddressMode.Immediate));
    EmitAsm("LD_HL_A"); // [0]=0x01
    EmitAsm("PUSH_HL");
    EmitAsm("INC_HL"); EmitAsm("INC_HL"); EmitAsm("INC_HL"); // -> [3]
    EmitAsm("XOR_A");
    EmitAsm("LD_HL_A"); // dasCnt=0
    EmitAsm("INC_HL");
    EmitAsm("LD_HL_A")
__padrep_mask

__padrep_mask est un autre nom de __padrep ; les deux appels utilisent la même implémentation du compilateur. La sélection d’une action, les délais, la disposition de l’état et les valeurs renvoyées sont identiques. Le nom explicite rend l’emploi d’un masque plus visible à l’endroit de l’appel.

Programme la répétition d’une seule action parmi celles autorisées par mask. Parmi les nouveaux appuis admissibles, choisit le bit actif de poids le plus faible et le renvoie immédiatement. Sinon, conserve l’action active tant qu’elle reste autorisée et maintenue. À sa fin, adopte le bit maintenu de poids le plus faible sans impulsion immédiate. En l’absence de bouton admissible maintenu, efface l’état d’exécution.

Syntaxe

c
u8 __padrep_mask(u8* state, u8 keys, u8 trigger, u8 mask);

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Paramètres

state

Pointeur vers cinq octets consécutifs de RAM modifiable, par exemple u8 state[5]. Disposition : [0] bit du bouton actif, [1] délai initial, [2] intervalle, [3] compteur du délai écoulé, [4] compteur de répétition écoulée. Prévoyez un état distinct pour chaque répétition indépendante et initialisez-le avant utilisation. Un pointeur nul, une zone de moins de cinq octets ou le partage d’un état entre variantes directionnelles et générale ne sont pas pris en charge.

keys

Masque complet des boutons maintenus au format GB BTN_*. Fournissez le résultat de __readpad, l’octet faible de __readpadex ou celui de input_current ; la fonction de répétition ne lit pas le matériel.

trigger

Bits nouvellement pressés pendant cette mise à jour, au même format que keys : en général keys & ~previous_keys ou l’octet fort de __readpadex. N’incluez que des bits également présents dans keys. Passer systématiquement le masque de maintien relance à tort le nouvel appui à chaque appel.

mask

Bits de boutons GB autorisés, combinés avec |. Zéro désactive toutes les actions et efface l’état d’exécution. Plusieurs bits offrent plusieurs candidats, mais la fonction ne renvoie qu’un seul bit sélectionné par appel, jamais un masque combinant plusieurs impulsions.

Valeur renvoyée

Zéro si aucune impulsion n’est due ; sinon, une des valeurs suivantes : 1, 2, 4, 8, 16, 32, 64, 128. Le résultat non nul est un bit de bouton, pas un booléen normalisé ni un nombre de répétitions.

Conditions et limites

Masques communs : BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80. Un bit à 1 indique un bouton maintenu. Ce format s’applique à input_* sur GB et FC ; les lectures brutes FC utilisent un autre ordre.

Délai initial de 0 à 255 appels à la fonction de répétition. Pour une valeur positive N, la première répétition survient exactement N appels après l’impulsion du nouvel appui. Avec 0, le cas d’égalité du délai initial est sauté et l’intervalle détermine la première répétition. Il ne s’agit pas de millisecondes.

Intervalle des répétitions suivantes, de 0 à 255 appels. Un N positif produit une impulsion toutes les N mises à jour admissibles de maintien après la première répétition. Zéro en produit une à chaque mise à jour admissible après le délai initial ; à ce stade, 0 et 1 donnent donc tous deux une impulsion par mise à jour. Si les deux délais valent 0, chaque mise à jour de maintien produit une impulsion.

Appelez la fonction une fois par cycle d’entrée prévu. Chaque appel modifie l’état sans attendre le VBlank. L’adoption d’un bouton maintenu sans nouvel appui renvoie 0 pour cet appel et démarre un nouveau délai. Ces fonctions choisissent une action active par état ; input_repeat entretient des temporisateurs indépendants pour les huit boutons.

Exemple d’utilisation

c
result=__padrep_mask(alias_state,keys,trigger,48);
    if (result!=0) alias_pulses++;

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Ce programme fournit des états de boutons prédéfinis à quatre états indépendants pour montrer quand une répétition se produit. Avec un délai de 3 et un intervalle de 2, huit appels numérotés de 0 à 7 produisent des impulsions aux appels 0, 3, 5 et 7. RIGHT PULSES, DOWN PULSES, ACTION PULSES et ALIAS PULSES affichent donc 004. Les variantes générales reçoivent A et B ensemble et sélectionnent A (16). Le programme vérifie aussi l’initialisation, la remise à zéro, la priorité des directions, le relâchement, l’adoption d’une direction déjà maintenue, les délais nuls et un argument contenant une expression. CHECKS affiche 057 et FAILED CHECKS doit afficher 000. Cet exemple déterministe traite des états prédéfinis ; il n’attend pas d’appui de votre part.

__padrep_mask: DMG
DMG
__padrep_mask: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\pad_repeat_state.c -I .\kitaq-docs\samples -I .\kitaqgb\lib -o .\out\pad_repeat_state.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:11249

csharp
{
    if (args.Length != 4) Error(expr, "__padrep requires 4 arguments (state*, keys, trigger, mask)");

    AsmOperand pm_done        = MakeUniqueLabel("padrepM_done");
    AsmOperand pm_ret0        = MakeUniqueLabel("padrepM_ret0");
    AsmOperand pm_no_trig     = MakeUniqueLabel("padrepM_notrig");
    AsmOperand pm_reset_all   = MakeUniqueLabel("padrepM_reset");
    AsmOperand pm_dir0        = MakeUniqueLabel("padrepM_dir0");
    AsmOperand pm_have_dir    = MakeUniqueLabel("padrepM_havedir");
    AsmOperand pm_first_rep   = MakeUniqueLabel("padrepM_firstrep");
    AsmOperand pm_ret_dir     = MakeUniqueLabel("padrepM_retdir");
    AsmOperand pm_skip_dasinc = MakeUniqueLabel("padrepM_skipdasinc");
    AsmOperand pm_skip_arrinc = MakeUniqueLabel("padrepM_skiparrinc");

    // Evaluate args safely:
    // HL = state*, B = keys, C = trigger, E = mask
    CompileIntoHL(args[0]);
    EmitAsm("PUSH_HL");
    CompileIntoA(args[1]); EmitAsm("LD_B_A"); // keys
    CompileIntoA(args[2]); EmitAsm("LD_C_A"); // trigger
    CompileIntoA(args[3]); EmitAsm("LD_E_A"); // mask
    EmitAsm("POP_HL");

    // trigMasked = trigger & mask -> D
    EmitAsm("LD_A_C");
    EmitAsm("AND_E");
    EmitAsm("LD_D_A");
    EmitAsm("OR_A");
    EmitAsm("JP_Z", pm_no_trig);

    // select = lowbit(trigMasked) = x & -x
    EmitAsm("LD_A_D");
    EmitAsm("CPL");
    EmitAsm("INC_A");
    EmitAsm("AND_D");
    EmitAsm("LD_C_A"); // save selected in C

    // dirMask=selected, dasCnt=0, arrCnt=0
    EmitAsm("LD_A_C");
    EmitAsm("LD_HL_A"); // [0]=selected
    EmitAsm("PUSH_HL");
    EmitAsm("INC_HL"); Emi
__padrep_reset

Annule l’action active et remet les deux compteurs à zéro, tout en conservant les délais configurés. Utilisez cette fonction à la fermeture d’un menu ou au changement de destinataire des commandes pour ne pas reprendre une répétition déjà engagée.

Syntaxe

c
void __padrep_reset(u8* state);

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Paramètres

state

Pointeur vers cinq octets consécutifs de RAM modifiable, par exemple u8 state[5]. Disposition : [0] bit du bouton actif, [1] délai initial, [2] intervalle, [3] compteur du délai écoulé, [4] compteur de répétition écoulée. Prévoyez un état distinct pour chaque répétition indépendante et initialisez-le avant utilisation. Un pointeur nul, une zone de moins de cinq octets ou le partage d’un état entre variantes directionnelles et générale ne sont pas pris en charge.

Valeur renvoyée

Aucune (void).

Exemple d’utilisation

c
__padrep_reset(lr);
expect(lr[0],0); expect(lr[1],3); expect(lr[2],2);
expect(lr[3],0); expect(lr[4],0);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Ce programme fournit des états de boutons prédéfinis à quatre états indépendants pour montrer quand une répétition se produit. Avec un délai de 3 et un intervalle de 2, huit appels numérotés de 0 à 7 produisent des impulsions aux appels 0, 3, 5 et 7. RIGHT PULSES, DOWN PULSES, ACTION PULSES et ALIAS PULSES affichent donc 004. Les variantes générales reçoivent A et B ensemble et sélectionnent A (16). Le programme vérifie aussi l’initialisation, la remise à zéro, la priorité des directions, le relâchement, l’adoption d’une direction déjà maintenue, les délais nuls et un argument contenant une expression. CHECKS affiche 057 et FAILED CHECKS doit afficher 000. Cet exemple déterministe traite des états prédéfinis ; il n’attend pas d’appui de votre part.

__padrep_reset: DMG
DMG
__padrep_reset: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\pad_repeat_state.c -I .\kitaq-docs\samples -I .\kitaqgb\lib -o .\out\pad_repeat_state.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:10840

csharp
{
    if (args.Length != 1) Error(expr, "__padrep_reset requires 1 argument (state*)");

    CompileIntoHL(args[0]);

    EmitAsm("XOR_A");
    EmitAsm("LD_HL_A"); // [0]=0 (dirMask)

    // [3]=0, [4]=0
    EmitAsm("PUSH_HL");
    EmitAsm("INC_HL"); EmitAsm("INC_HL"); EmitAsm("INC_HL"); // -> [3]
    EmitAsm("LD_HL_A");
    EmitAsm("INC_HL"); // -> [4]
    EmitAsm("LD_HL_A");
    EmitAsm("POP_HL");
    return;
}

// __padrep_lr(state*, keys, trigger) returns Right (1), Left (2), or zero.
// New triggers take priority, with Right selected first when both trigger bits are set.
// Without a new trigger, simultaneous Left/Right holds suppress movement without resetting counters.
// A single held direction is adopted without immediate movement when no direction is active.
// Subsequent updates use the five-byte state's initial delay and repeat interval; ARR=0 repeats each update.
if (funcName == "__padrep_lr")
{
    if (args.Length != 3) Error(expr, "__padrep_lr requires 3 arguments (state*, keys, trigger)");

    AsmOperand pr_done        = MakeUniqueLabel("padrep_done");
    AsmOperand pr_ret0        = MakeUniqueLabel("padrep_ret0");
    AsmOperand pr_check_left  = MakeUniqueLabel("padrep_chkL");
    AsmOperand pr_no_trig     = MakeUniqueLabel("padrep_notrig");
    AsmOperand pr_reset_all   = MakeUniqueLabel("padrep_reset");
    AsmOperand pr_dir0        = MakeUniqueLabel("padrep_dir0");
    AsmOperand pr_have_dir    = MakeUniqueLabel("padrep_havedir");
    AsmOperand pr_first_rep   = MakeUniqueLabel("padrep_firstrep");
    AsmOperand pr_ret_dir     = MakeUniqueLabel("padrep
__readpad

Lit la croix directionnelle et les boutons d’action dans JOYP, puis les combine dans le masque commun de 8 bits. Sélectionne chaque groupe, effectue deux lectures pour la stabilisation et remet les bits de sélection à 0x30.

Syntaxe

c
u8 __readpad();

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Valeur renvoyée

Un masque de 8 bits au format commun BTN_*. Plusieurs boutons peuvent être présents simultanément ; 0 signifie aucun.

Conditions et limites

Masques communs : BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80. Un bit à 1 indique un bouton maintenu. Ce format s’applique à input_* sur GB et FC ; les lectures brutes FC utilisent un autre ordre.

Cet appel lit un nouvel état matériel. Il ne met pas à jour les états mémorisés input_* ou nes_pad1_* et ne produit ni événements d’appui ou de relâchement, ni répétitions. Mémorisez le résultat et comparez-le à votre masque précédent pour détecter les transitions.

Exemple d’utilisation

c
all_keys=__readpad(); // A + Right: 0x11, decimal 17.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Ce programme compare les trois formats de masque et le résultat regroupant l’état actuel et les nouveaux appuis. Maintenez A et droite ensemble. ALL KEYS vaut 17 ; DIRECTIONS et BUTTONS LOW valent chacun 1. Avec un masque précédent de 0, EX CURRENT et EX NEW PRESS valent 17. En transmettant le masque actuel à la lecture suivante, HELD PRESS vaut 0. FAILED CHECKS doit valoir 0. Les valeurs restent affichées après le relâchement des boutons.

__readpad: DMG
DMG
__readpad: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\input_raw.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_raw.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:10669

csharp
{
    if (args.Length != 0) Error(expr, "__readpad requires 0 arguments");

    AsmOperand p1 = new AsmOperand(0x00, AddressMode.HighMem); // P1 / JOYP

    // Directions (P1=0x20)
    EmitAsm("LD_A_IMM", new AsmOperand(0x20, AddressMode.Immediate));
    EmitAsm("LDH_MEM_A", p1);
    EmitAsm("LDH_A_MEM", p1); // dummy reads for settle
    EmitAsm("LDH_A_MEM", p1);
    EmitAsm("CPL");
    EmitAsm("AND_IMM", new AsmOperand(0x0F, AddressMode.Immediate));
    EmitAsm("LD_C_A"); // dpad in C

    // Buttons (P1=0x10)
    EmitAsm("LD_A_IMM", new AsmOperand(0x10, AddressMode.Immediate));
    EmitAsm("LDH_MEM_A", p1);
    EmitAsm("LDH_A_MEM", p1);
    EmitAsm("LDH_A_MEM", p1);
    EmitAsm("CPL");
    EmitAsm("AND_IMM", new AsmOperand(0x0F, AddressMode.Immediate));
    // shift into upper nibble (<<4)
    EmitAsm("RLCA"); EmitAsm("RLCA"); EmitAsm("RLCA"); EmitAsm("RLCA");
    EmitAsm("OR_C");

    // Restore P1 selection (0x30) without losing return value
    EmitAsm("LD_C_A");
    EmitAsm("LD_A_IMM", new AsmOperand(0x30, AddressMode.Immediate));
    EmitAsm("LDH_MEM_A", p1);
    EmitAsm("LD_A_C");
    return;
}

// 2.17 __readpaddir() -> u8 in A (bits0..3 only)
// Returns dpad nibble (Right,Left,Up,Down) with 1 when pressed.
// Treated as a compiler intrinsic (no C definition required).
if (funcName == "__readpaddir")
{
    if (args.Length != 0) Error(expr, "__readpaddir requires 0 arguments");

    AsmOperand p1 = new AsmOperand(0x00, AddressMode.HighMem); // P1 / JOYP

    EmitAsm("LD_A_IMM", new AsmOperand(0x20, AddressMode.Immediate));
    EmitAsm("LDH_MEM_A", p1);
    EmitAsm
__readpadbtn

Lit uniquement les boutons d’action de JOYP dans les quatre bits bas : A=1, B=2, Select=4, Start=8. Ces valeurs diffèrent de BTN_A à BTN_START, placés dans les bits hauts. La sélection JOYP est remise à 0x30.

Syntaxe

c
u8 __readpadbtn();

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Valeur renvoyée

Masque de 0 à 15 dans l’ordre indiqué. Plusieurs bits peuvent être actifs ; testez un bouton avec result & bit plutôt qu’en comparant tout le résultat à 1.

Conditions et limites

Cet appel lit un nouvel état matériel. Il ne met pas à jour les états mémorisés input_* ou nes_pad1_* et ne produit ni événements d’appui ou de relâchement, ni répétitions. Mémorisez le résultat et comparez-le à votre masque précédent pour détecter les transitions.

Exemple d’utilisation

c
buttons=__readpadbtn(); // A: 1, not BTN_A (16).

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Ce programme compare les trois formats de masque et le résultat regroupant l’état actuel et les nouveaux appuis. Maintenez A et droite ensemble. ALL KEYS vaut 17 ; DIRECTIONS et BUTTONS LOW valent chacun 1. Avec un masque précédent de 0, EX CURRENT et EX NEW PRESS valent 17. En transmettant le masque actuel à la lecture suivante, HELD PRESS vaut 0. FAILED CHECKS doit valoir 0. Les valeurs restent affichées après le relâchement des boutons.

__readpadbtn: DMG
DMG
__readpadbtn: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\input_raw.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_raw.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:10729

csharp
{
    if (args.Length != 0) Error(expr, "__readpadbtn requires 0 arguments");

    AsmOperand p1 = new AsmOperand(0x00, AddressMode.HighMem); // P1 / JOYP

    EmitAsm("LD_A_IMM", new AsmOperand(0x10, AddressMode.Immediate));
    EmitAsm("LDH_MEM_A", p1);
    EmitAsm("LDH_A_MEM", p1);
    EmitAsm("LDH_A_MEM", p1);
    EmitAsm("CPL");
    EmitAsm("AND_IMM", new AsmOperand(0x0F, AddressMode.Immediate));

    EmitAsm("LD_C_A");
    EmitAsm("LD_A_IMM", new AsmOperand(0x30, AddressMode.Immediate));
    EmitAsm("LDH_MEM_A", p1);
    EmitAsm("LD_A_C");
    return;
}

// 2.19 __readpadex(prev_u8) -> u16 in HL
// Returns: L = keys (same encoding as __readpad), H = trigger (newly pressed edge: keys & ~prev).
// This mirrors the common pattern:
// prev = keys; keys = __readpad(); trigger = keys & ~prev;
// Treated as a compiler intrinsic (no C definition required).
if (funcName == "__readpadex")
{
    if (args.Length != 1) Error(expr, "__readpadex requires 1 argument (prev_keys)");

    // prev -> B
    CompileIntoA(args[0]);
    EmitAsm("LD_B_A");

    AsmOperand p1 = new AsmOperand(0x00, AddressMode.HighMem); // P1 / JOYP

    // Directions (P1=0x20)
    EmitAsm("LD_A_IMM", new AsmOperand(0x20, AddressMode.Immediate));
    EmitAsm("LDH_MEM_A", p1);
    EmitAsm("LDH_A_MEM", p1);
    EmitAsm("LDH_A_MEM", p1);
    EmitAsm("CPL");
    EmitAsm("AND_IMM", new AsmOperand(0x0F, AddressMode.Immediate));
    EmitAsm("LD_C_A"); // dpad in C

    // Buttons (P1=0x10)
    EmitAsm("LD_A_IMM", new AsmOperand(0x10, AddressMode.Immediate));
    EmitAsm("LDH_MEM_A", p1);
    EmitAsm("LDH_A_MEM", p1)
__readpaddir

Lit uniquement le groupe directionnel de JOYP. Les bits 0–3 valent droite=1, gauche=2, haut=4, bas=8 ; les bits 4–7 sont nuls. Les boutons d’action sont ignorés et la sélection JOYP est remise à 0x30.

Syntaxe

c
u8 __readpaddir();

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Valeur renvoyée

Masque de 0 à 15 dans l’ordre indiqué. Plusieurs bits peuvent être actifs ; testez un bouton avec result & bit plutôt qu’en comparant tout le résultat à 1.

Conditions et limites

Cet appel lit un nouvel état matériel. Il ne met pas à jour les états mémorisés input_* ou nes_pad1_* et ne produit ni événements d’appui ou de relâchement, ni répétitions. Mémorisez le résultat et comparez-le à votre masque précédent pour détecter les transitions.

Exemple d’utilisation

c
directions=__readpaddir(); // Right: 1. The held A button is not included.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Ce programme compare les trois formats de masque et le résultat regroupant l’état actuel et les nouveaux appuis. Maintenez A et droite ensemble. ALL KEYS vaut 17 ; DIRECTIONS et BUTTONS LOW valent chacun 1. Avec un masque précédent de 0, EX CURRENT et EX NEW PRESS valent 17. En transmettant le masque actuel à la lecture suivante, HELD PRESS vaut 0. FAILED CHECKS doit valoir 0. Les valeurs restent affichées après le relâchement des boutons.

__readpaddir: DMG
DMG
__readpaddir: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\input_raw.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_raw.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:10706

csharp
{
    if (args.Length != 0) Error(expr, "__readpaddir requires 0 arguments");

    AsmOperand p1 = new AsmOperand(0x00, AddressMode.HighMem); // P1 / JOYP

    EmitAsm("LD_A_IMM", new AsmOperand(0x20, AddressMode.Immediate));
    EmitAsm("LDH_MEM_A", p1);
    EmitAsm("LDH_A_MEM", p1);
    EmitAsm("LDH_A_MEM", p1);
    EmitAsm("CPL");
    EmitAsm("AND_IMM", new AsmOperand(0x0F, AddressMode.Immediate));

    EmitAsm("LD_C_A");
    EmitAsm("LD_A_IMM", new AsmOperand(0x30, AddressMode.Immediate));
    EmitAsm("LDH_MEM_A", p1);
    EmitAsm("LD_A_C");
    return;
}

// 2.18 __readpadbtn() -> u8 in A (bits0..3 only)
// Returns buttons nibble (A,B,Select,Start) with 1 when pressed.
// Treated as a compiler intrinsic (no C definition required).
if (funcName == "__readpadbtn")
{
    if (args.Length != 0) Error(expr, "__readpadbtn requires 0 arguments");

    AsmOperand p1 = new AsmOperand(0x00, AddressMode.HighMem); // P1 / JOYP

    EmitAsm("LD_A_IMM", new AsmOperand(0x10, AddressMode.Immediate));
    EmitAsm("LDH_MEM_A", p1);
    EmitAsm("LDH_A_MEM", p1);
    EmitAsm("LDH_A_MEM", p1);
    EmitAsm("CPL");
    EmitAsm("AND_IMM", new AsmOperand(0x0F, AddressMode.Immediate));

    EmitAsm("LD_C_A");
    EmitAsm("LD_A_IMM", new AsmOperand(0x30, AddressMode.Immediate));
    EmitAsm("LDH_MEM_A", p1);
    EmitAsm("LD_A_C");
    return;
}

// 2.19 __readpadex(prev_u8) -> u16 in HL
// Returns: L = keys (same encoding as __readpad), H = trigger (newly pressed edge: keys & ~prev).
// This mirrors the common pattern:
// prev = keys; keys = __readpad(); trigger = keys & ~prev;
// Treated as a c
__sdot2_q1_7

Calcule un produit scalaire signé à deux termes.

Syntaxe

c
u16 __sdot2_q1_7(u16 a0, u16 a1, u8 b0, u8 b1);

Paramètres

a0

Motif de bits signé sur 16 bits représentant −32768 à 32767.

a1

Motif de bits signé sur 16 bits représentant −32768 à 32767.

b0

Motif de bits d’un octet signé, coefficient de −128 à 127.

b1

Motif de bits d’un octet signé, coefficient de −128 à 127.

Valeur renvoyée

Chaque terme vaut 2*trunc(a*b/256). Additionne les termes et renvoie les 16 bits de poids faible.

Conditions et limites

Sur GB, les produits sont divisés par 256 ; les résultats signés sont tronqués vers zéro. Les variantes Q1.7 doublent ce résultat tronqué et peuvent différer d’une unité d’une division exacte par 128. Chaque terme est arrondi séparément ; l’addition finale reboucle sur 16 bits.

Exemple d’utilisation

c
// Compare literal coefficients with values read from RAM.
result[20]=__sdot2_q1_7(-513,769,-64,127);
v0=-513;v1=769;v2=-64;v3=127;
result[21]=__sdot2_q1_7(v0,v1,v2,v3);
// Reverse coefficient signs and verify negative products.
result[22]=__sdot2_q1_7(-513,769,64,-127);
v0=-513;v1=769;v2=64;v3=-127;
result[23]=__sdot2_q1_7(v0,v1,v2,v3);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Comparez les entrées littérales aux valeurs en RAM. Les résultats représentatifs sont affichés sur 16 bits non signés ; les négatifs utilisent le complément à deux. Le GB remet à l’échelle les produits à virgule fixe, tandis que le FC renvoie des produits entiers : des entrées identiques ne donnent donc pas nécessairement les mêmes résultats.

L’extrait stocke les valeurs suivantes sur 16 bits : result[20]=1018, result[21]=1018, result[22]=64518, result[23]=64518. Chaque calcul avec une entrée littérale correspond à son équivalent avec une entrée en RAM. L’écran affiche des valeurs représentatives ; le tableau result conserve tous les résultats.

__sdot2_q1_7: DMG
DMG
__sdot2_q1_7: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:11732

kitaqgb/kitaqgb/CodeGenerator.cs:11732

csharp
if (funcName == "__sdot2_q1_7")
{
    if (args.Length != 4) Error(expr, "__sdot2_q1_7 requires 4 arguments");
    LastCallReturnsHL = true;

    // x*ax
    if (!TryEmitDot2Pow2Term(args[0], args[2], signed: true, q1_7: true, out _))
    {
        CompileIntoHL(args[0]);
        EmitAsm("PUSH_HL");
        CompileIntoA(args[2]);
        EmitAsm("LD_C_A");
        EmitAsm("POP_HL");
        EmitSMul16x8Shr8_HL("__sdot2_q1_7_x");
        EmitAsm("ADD_HL_HL");
    }
    EmitAsm("PUSH_HL");

    // y*ay
    if (!TryEmitDot2Pow2Term(args[1], args[3], signed: true, q1_7: true, out _))
    {
        CompileIntoHL(args[1]);
        EmitAsm("PUSH_HL");
        CompileIntoA(args[3]);
        EmitAsm("LD_C_A");
        EmitAsm("POP_HL");
        EmitSMul16x8Shr8_HL("__sdot2_q1_7_y");
        EmitAsm("ADD_HL_HL");
    }
    EmitAsm("POP_DE");
    EmitAsm("ADD_HL_DE");
    return;
}
__sdot2_q8_8

Calcule un produit scalaire signé à deux termes.

Syntaxe

c
u16 __sdot2_q8_8(u16 a0, u16 a1, u8 b0, u8 b1);

Paramètres

a0

Motif de bits signé sur 16 bits représentant −32768 à 32767.

a1

Motif de bits signé sur 16 bits représentant −32768 à 32767.

b0

Motif de bits d’un octet signé, coefficient de −128 à 127.

b1

Motif de bits d’un octet signé, coefficient de −128 à 127.

Valeur renvoyée

Chaque terme vaut trunc(a*b/256). Additionne les termes et renvoie les 16 bits de poids faible.

Conditions et limites

Sur GB, les produits sont divisés par 256 ; les résultats signés sont tronqués vers zéro. Les variantes Q1.7 doublent ce résultat tronqué et peuvent différer d’une unité d’une division exacte par 128. Chaque terme est arrondi séparément ; l’addition finale reboucle sur 16 bits.

Exemple d’utilisation

c
// Compare literal coefficients with values read from RAM.
result[30]=__sdot2_q8_8(-513,769,-64,127);
v0=-513;v1=769;v2=-64;v3=127;
result[31]=__sdot2_q8_8(v0,v1,v2,v3);
// Reverse coefficient signs and verify negative products.
result[32]=__sdot2_q8_8(-513,769,64,-127);
v0=-513;v1=769;v2=64;v3=-127;
result[33]=__sdot2_q8_8(v0,v1,v2,v3);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Comparez les entrées littérales aux valeurs en RAM. Les résultats représentatifs sont affichés sur 16 bits non signés ; les négatifs utilisent le complément à deux. Le GB remet à l’échelle les produits à virgule fixe, tandis que le FC renvoie des produits entiers : des entrées identiques ne donnent donc pas nécessairement les mêmes résultats.

L’extrait stocke les valeurs suivantes sur 16 bits : result[30]=509, result[31]=509, result[32]=65027, result[33]=65027. Chaque calcul avec une entrée littérale correspond à son équivalent avec une entrée en RAM. L’écran affiche des valeurs représentatives ; le tableau result conserve tous les résultats.

__sdot2_q8_8: DMG
DMG
__sdot2_q8_8: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:11654

kitaqgb/kitaqgb/CodeGenerator.cs:11654

csharp
if (funcName == "__sdot2_q8_8")
{
    if (args.Length != 4) Error(expr, "__sdot2_q8_8 requires 4 arguments");
    LastCallReturnsHL = true;

    // Fast paths when coefficients are compile-time constants.
    // Special-case: 0 / power-of-two coefficients (s8 two's complement) => arithmetic shift instead of mul.
    if (TryGetS8Const(args[2], out int sax0) && (sax0 == 0) && TryGetS8Const(args[3], out int say0) && (say0 == 0))
    {
        CompileDiscard(args[0]);
        CompileDiscard(args[1]);
        EmitAsm("XOR_A");
        EmitAsm("LD_H_A");
        EmitAsm("LD_L_A");
        return;
    }
    if (TryGetS8Const(args[2], out int saxOnly) && (saxOnly == 0))
    {
        CompileDiscard(args[0]);
        // only y*ay
        if (!TryEmitDot2Pow2Term(args[1], args[3], signed: true, q1_7: false, out _))
        {
            CompileIntoHL(args[1]);
            EmitAsm("PUSH_HL");
            CompileIntoA(args[3]);
            EmitAsm("LD_C_A");
            EmitAsm("POP_HL");
            EmitSMul16x8Shr8_HL("__sdot2_y");
        }
        return;
    }
    if (TryGetS8Const(args[3], out int sayOnly) && (sayOnly == 0))
    {
        // only x*ax
        if (!TryEmitDot2Pow2Term(args[0], args[2], signed: true, q1_7: false, out _))
        {
            CompileIntoHL(args[0]);
            EmitAsm("PUSH_HL");
            CompileIntoA(args[2]);
            EmitAsm("LD_C_A");
            EmitAsm("POP_HL");
            EmitSMul16x8Shr8_HL("__sdot2_x");
        }
        // Preserve the scaled result while evaluating the zero-product input.
        EmitAsm("PUSH_HL");
        CompileDiscard(args[1]);
        EmitAsm("POP_HL");
        return;
    }

    // x*ax
    if (!TryEmitDot2Pow2Term(args[0], args[2], signed: true, q1_7: false, out _))
    {
        CompileIntoHL(args[0]);
        EmitAsm("PUSH_HL");
        CompileIntoA(args[2]);
        EmitAsm("LD_C_A");
        EmitAsm("POP_HL");
        EmitSMul16x8Shr8_HL("__sdot2_x");
    }
    EmitAsm("PUSH_HL");

    // y*ay
    if (!TryEmitDot2Pow2Term(args[1], args[3], signed: true, q1_7: false, out _))
    {
        CompileIntoHL(args[1]);
        EmitAsm("PUSH_HL");
        CompileIntoA(args[3]);
        EmitAsm("LD_C_A");
        EmitAsm("POP_HL");
        EmitSMul16x8Shr8_HL("__sdot2_y");
    }
    EmitAsm("POP_DE");
    EmitAsm("ADD_HL_DE");
    return;
}
__sdot3_q1_7

Calcule un produit scalaire signé à trois termes.

Syntaxe

c
u16 __sdot3_q1_7(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);

Paramètres

a0

Motif de bits signé sur 16 bits représentant −32768 à 32767.

a1

Motif de bits signé sur 16 bits représentant −32768 à 32767.

a2

Motif de bits signé sur 16 bits représentant −32768 à 32767.

b0

Motif de bits d’un octet signé, coefficient de −128 à 127.

b1

Motif de bits d’un octet signé, coefficient de −128 à 127.

b2

Motif de bits d’un octet signé, coefficient de −128 à 127.

Valeur renvoyée

Chaque terme vaut 2*trunc(a*b/256). Additionne les termes et renvoie les 16 bits de poids faible.

Conditions et limites

Sur GB, les produits sont divisés par 256 ; les résultats signés sont tronqués vers zéro. Les variantes Q1.7 doublent ce résultat tronqué et peuvent différer d’une unité d’une division exacte par 128. Chaque terme est arrondi séparément ; l’addition finale reboucle sur 16 bits.

Exemple d’utilisation

c
// Compare literal coefficients with values read from RAM.
result[16]=__sdot3_q1_7(-513,769,-1025,-64,127,-3);
v0=-513;v1=769;v2=-1025;v3=-64;v4=127;v5=-3;
result[17]=__sdot3_q1_7(v0,v1,v2,v3,v4,v5);
// Reverse coefficient signs and verify negative products.
result[18]=__sdot3_q1_7(-513,769,-1025,64,-127,3);
v0=-513;v1=769;v2=-1025;v3=64;v4=-127;v5=3;
result[19]=__sdot3_q1_7(v0,v1,v2,v3,v4,v5);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Comparez les entrées littérales aux valeurs en RAM. Les résultats représentatifs sont affichés sur 16 bits non signés ; les négatifs utilisent le complément à deux. Le GB remet à l’échelle les produits à virgule fixe, tandis que le FC renvoie des produits entiers : des entrées identiques ne donnent donc pas nécessairement les mêmes résultats.

L’extrait stocke les valeurs suivantes sur 16 bits : result[16]=1042, result[17]=1042, result[18]=64494, result[19]=64494. Chaque calcul avec une entrée littérale correspond à son équivalent avec une entrée en RAM. L’écran affiche des valeurs représentatives ; le tableau result conserve tous les résultats.

__sdot3_q1_7: DMG
DMG
__sdot3_q1_7: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:11790

kitaqgb/kitaqgb/CodeGenerator.cs:11790

csharp
if (funcName == "__sdot3_q1_7")
            {
                if (args.Length != 6) Error(expr, "__sdot3_q1_7 requires 6 arguments");
                LastCallReturnsHL = true;

                // x*ax
                EmitDotTermToHL(args[0], args[3], signed: true, q1_7: true, labelPrefix: "__sdot3_q1_7_x");
                EmitAsm("PUSH_HL");

                // y*ay
                EmitDotTermToHL(args[1], args[4], signed: true, q1_7: true, labelPrefix: "__sdot3_q1_7_y");
                EmitAsm("POP_DE");
                EmitAsm("ADD_HL_DE");
                EmitAsm("PUSH_HL");

                // z*az
                EmitDotTermToHL(args[2], args[5], signed: true, q1_7: true, labelPrefix: "__sdot3_q1_7_z");
                EmitAsm("POP_DE");
                EmitAsm("ADD_HL_DE");
                return;
            }
__sdot3_q8_8

Calcule un produit scalaire signé à trois termes.

Syntaxe

c
u16 __sdot3_q8_8(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);

Paramètres

a0

Motif de bits signé sur 16 bits représentant −32768 à 32767.

a1

Motif de bits signé sur 16 bits représentant −32768 à 32767.

a2

Motif de bits signé sur 16 bits représentant −32768 à 32767.

b0

Motif de bits d’un octet signé, coefficient de −128 à 127.

b1

Motif de bits d’un octet signé, coefficient de −128 à 127.

b2

Motif de bits d’un octet signé, coefficient de −128 à 127.

Valeur renvoyée

Chaque terme vaut trunc(a*b/256). Additionne les termes et renvoie les 16 bits de poids faible.

Conditions et limites

Sur GB, les produits sont divisés par 256 ; les résultats signés sont tronqués vers zéro. Les variantes Q1.7 doublent ce résultat tronqué et peuvent différer d’une unité d’une division exacte par 128. Chaque terme est arrondi séparément ; l’addition finale reboucle sur 16 bits.

Exemple d’utilisation

c
// Compare literal coefficients with values read from RAM.
result[8]=__sdot3_q8_8(-513,769,-1025,-64,127,-3);
v0=-513;v1=769;v2=-1025;v3=-64;v4=127;v5=-3;
result[9]=__sdot3_q8_8(v0,v1,v2,v3,v4,v5);
// Reverse coefficient signs and verify negative products.
result[10]=__sdot3_q8_8(-513,769,-1025,64,-127,3);
v0=-513;v1=769;v2=-1025;v3=64;v4=-127;v5=3;
result[11]=__sdot3_q8_8(v0,v1,v2,v3,v4,v5);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Comparez les entrées littérales aux valeurs en RAM. Les résultats représentatifs sont affichés sur 16 bits non signés ; les négatifs utilisent le complément à deux. Le GB remet à l’échelle les produits à virgule fixe, tandis que le FC renvoie des produits entiers : des entrées identiques ne donnent donc pas nécessairement les mêmes résultats.

L’extrait stocke les valeurs suivantes sur 16 bits : result[8]=521, result[9]=521, result[10]=65015, result[11]=65015. Chaque calcul avec une entrée littérale correspond à son équivalent avec une entrée en RAM. L’écran affiche des valeurs représentatives ; le tableau result conserve tous les résultats.

__sdot3_q8_8: DMG
DMG
__sdot3_q8_8: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:11767

kitaqgb/kitaqgb/CodeGenerator.cs:11767

csharp
if (funcName == "__sdot3_q8_8")
            {
                if (args.Length != 6) Error(expr, "__sdot3_q8_8 requires 6 arguments");
                LastCallReturnsHL = true;

                // x*ax
                EmitDotTermToHL(args[0], args[3], signed: true, q1_7: false, labelPrefix: "__sdot3_x");
                EmitAsm("PUSH_HL");

                // y*ay
                EmitDotTermToHL(args[1], args[4], signed: true, q1_7: false, labelPrefix: "__sdot3_y");
                EmitAsm("POP_DE");
                EmitAsm("ADD_HL_DE");
                EmitAsm("PUSH_HL");

                // z*az
                EmitDotTermToHL(args[2], args[5], signed: true, q1_7: false, labelPrefix: "__sdot3_z");
                EmitAsm("POP_DE");
                EmitAsm("ADD_HL_DE");
                return;
            }
__settile_fast

Écrit le numéro de tuile d’une case de la carte ; sélectionne une image existante sans modifier ses pixels.

La destination est toujours 0x9800 + 32*y + x, indépendamment des cartes de fond et de fenêtre sélectionnées par LCDC.

N’attend pas l’accès à la VRAM. À utiliser LCD éteint ou lorsque l’appelant garantit un créneau d’écriture assez long pour tout le transfert.

Syntaxe

c
__settile_fast(x, y, tile);

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Paramètres

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

tile

Numéro de tuile, de 0 à 255. Le bit 4 de LCDC détermine l’adressage de son image. Ce n’est pas une adresse d’octet en VRAM.

Valeur renvoyée

Aucune (void).

Conditions et limites

Les octets sont écrits dans la banque VRAM sélectionnée. Sur CGB, choisissez la banque 0 pour les numéros de tuile ; cet appel ne change pas de banque.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settile_fast
// Write one tile-number byte to the tile map; this selects an existing tile image, not
// new pixel data. The destination is always `0x9800 + 32*y + x`, regardless of which
// maps LCDC selects for the background and window.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
void __settile_fast(u8 x, u8 y, u8 tile);

void main() {
    u8 failures;
    failures = 0;
    tile_color_example_begin();
    __settile_fast(2, 3, 'A');
    if (tile_example_read(0x9862, 0) != 'A') { failures++; }
    if (tile_example_read(0x9C62, 0) != 0) { failures++; }
    tile_example_finish(failures, 0);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, les caractères de l’exemple et le texte des résultats s’affichent en bleu sur fond blanc. La préparation attribue le bleu à la couleur de tracé de la palette 0, sans modifier les attributs de la carte de tuiles. Sur DMG, les mêmes caractères s’affichent en noir sur fond blanc.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
A
__settile_fast: DMG
DMG
__settile_fast: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settile_fast.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settile_fast.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:9321

csharp
{
                if (args.Length != 3) Error(expr, "__settile_unsafe requires 3 arguments");

                // Evaluate args left-to-right (matches normal call semantics)
                // Preserve x,y while evaluating tile.
                CompileIntoA(args[0]);
                EmitAsm("LD_L_A");
                EmitAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate));
                EmitAsm("PUSH_HL"); // x

                CompileIntoA(args[1]);
                EmitAsm("LD_L_A");
                EmitAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate));
                EmitAsm("PUSH_HL"); // y

                CompileIntoA(args[2]);
                EmitAsm("LD_C_A"); // tile

                // Restore y -> A, x -> B
                EmitAsm("POP_HL");
                EmitAsm("LD_A_L");
                EmitAsm("LD_D_A"); // y temp

                EmitAsm("POP_HL");
                EmitAsm("LD_A_L");
                EmitAsm("LD_B_A"); // x

                EmitAsm("LD_A_D"); // y

                UsedSetTileFastCore = true;
                EmitAsm("CALL", new AsmOperand("__settile_fast_core", AddressMode.Absolute));
                return;
            }

            // 2.6b __settile_bulk(dest_u16, src_u16, count_u8)
            // v9: bulk tile-map copy (LCD ON safe with wait+DI/EI inside core).
            if (funcName == "__settile_bulk")
            {
                if (args.Length != 3) Error(expr, "__settile_bulk requires 3 arguments");

                Expr bulkCountExpr = FoldConstants(args[2]);

                // dest
__settilebg16_buf

Écrit une cellule de 16×16 pixels dans un tampon de carte en RAM : tile,tile+1 en haut, tile+2,tile+3 en bas. Les valeurs rebouclent à 256. Aucun transfert vers la VRAM n’est effectué.

Syntaxe

c
__settilebg16_buf(buf, x, y, tile);

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Paramètres

buf

Tampon RAM avec 32 octets par ligne de tuiles. Une carte complète de 32×32 demande 1024 octets. Le coin supérieur gauche est à 64*y + 2*x ; les quatre décalages relatifs sont 0,1,32,33.

x, y

Coordonnées en cellules de 16×16 pixels, de 0 à 15 pour un tampon complet de 32×32 tuiles. Leur unité diffère des coordonnées en tuiles de 8×8 des écritures unitaires.

tile

Numéro de tuile, de 0 à 255. Le bit 4 de LCDC détermine l’adressage de son image. Ce n’est pas une adresse d’octet en VRAM.

Valeur renvoyée

Aucune (void).

Conditions et limites

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settilebg16_buf
// Write a 16×16-pixel cell into a RAM tile-map buffer: the four 8×8 tile numbers are
// `tile,tile+1` on top and `tile+2,tile+3` below. Values wrap at 256. This does not
// transfer anything to VRAM.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
u8 example_tiles[1024];
u8 example_attrs[1024];
void __memset(void* dest, u8 value, u16 len);
void __settilebg16_buf(u8* buf, u8 x, u8 y, u8 tile);
void __settilebg16cgb_buf(u8* tilebuf, u8* attrbuf, u8 x, u8 y, u8 tile, u8 attr);
void __settilebg16_flush(const u8* buf, u8 y, u8 x, u8 count);
void __settilebg16cgb_flush(const u8* tilebuf, const u8* attrbuf, u8 y, u8 x, u8 count);

void main() {
    u8 failures;
    failures = 0;
    tile_color_example_begin();
    __memset(example_tiles, 0, 1024);
    __memset(example_attrs, 0, 1024);
    __settilebg16_buf(example_tiles, 1, 1, 'A');
    // The buffer operation must not write to VRAM yet.
    if (tile_example_read(0x9C42, 0) != 0) { failures++; }
    // Transfer the prepared cell so its four characters can be seen.
    __settilebg16_flush(example_tiles, 1, 2, 2);
    if (tile_example_read(0x9C42, 0) != 'A') { failures++; }
    if (tile_example_read(0x9C43, 0) != 'B') { failures++; }
    if (tile_example_read(0x9C62, 0) != 'C') { failures++; }
    if (tile_example_read(0x9C63, 0) != 'D') { failures++; }
    if (example_tiles[66] != 'A') { failures++; }
    if (example_tiles[99] != 'D') { failures++; }
    if (tile_example_read(0x9C44, 0) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

La cellule affiche AB en haut et CD en bas, à partir de la tuile (2,2) : une cellule de 16×16 pixels en (1,1). Les exemples d’écriture en tampon vérifient que la préparation RAM ne modifie pas la VRAM avant transfert. Les exemples de transfert préparent explicitement les octets RAM au préalable.

Sur CGB, les caractères de l’exemple et le texte des résultats s’affichent en bleu sur fond blanc. La préparation attribue le bleu à la couleur de tracé de la palette 0, sans modifier les attributs de la carte de tuiles. Sur DMG, les mêmes caractères s’affichent en noir sur fond blanc.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=2), soit au pixel (16,16). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
AB
CD
__settilebg16_buf: DMG
DMG
__settilebg16_buf: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilebg16_buf.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilebg16_buf.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:10564

csharp
{
                if (args.Length != 4) Error(expr, "__settilebg16_buf requires 4 arguments");
                EmitTile16BufferedWriteIntrinsic(args[0], args[1], args[2], args[3], incrementQuad: true, labelPrefix: "settilebg16buf");
                return;
            }

            // Update tile and attribute RAM buffers separately, repeating the attribute across the cell.
            if (funcName == "__settilebg16cgb_buf")
            {
                if (args.Length != 6) Error(expr, "__settilebg16cgb_buf requires 6 arguments");
                EmitTile16BufferedWriteIntrinsic(args[0], args[2], args[3], args[4], incrementQuad: true, labelPrefix: "settilebg16cgbbuf_tile");
                EmitTile16BufferedWriteIntrinsic(args[1], args[2], args[3], args[5], incrementQuad: false, labelPrefix: "settilebg16cgbbuf_attr");
                return;
            }

            // Flush both tile rows belonging to the requested buffered cell row.
            if (funcName == "__settilebg16_flush")
            {
                if (args.Length != 4) Error(expr, "__settilebg16_flush requires 4 arguments");
                EmitTile16FlushRowsIntrinsic(args[0], args[1], args[2], args[3], cgbAttr: false, labelPrefix: "settilebg16flush");
                return;
            }

            // Flush the tile buffer first and copy attributes only when CGB hardware is available.
            if (funcName == "__settilebg16cgb_flush")
            {
                if (args.Length != 5) Error(expr, "__settilebg16cgb_flush requires 5 arguments");
                EmitTile16FlushRowsIn
__settilebg16_flush

Transfère deux lignes de tuiles adjacentes du tampon RAM vers la carte BG sélectionnée par LCDC. Le décalage source est 64*y + x ; count octets sont copiés depuis cette ligne et celle située 32 octets plus loin.

Attend si nécessaire que la VRAM soit accessible en écriture. LCD actif, le chemin masque les interruptions et peut les réactiver ensuite ; il ne garantit ni l’état d’interruption initial ni une fin dans un seul VBlank.

Syntaxe

c
__settilebg16_flush(buf, y, x, count);

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Paramètres

buf

Tampon de carte lisible avec un pas de 32 octets par ligne. Une carte complète occupe 1024 octets. La forme à deux tampons reçoit les numéros et les attributs dans des tableaux séparés de même disposition, sans les entrelacer.

y, x, count

L’ordre est y,x,count. y est une ligne de cellules de 16 pixels (0–15) ; x et count sont en tuiles de 8 pixels. Respectez x+count <= 32. Pour une cellule en (2,3), utilisez y=3,x=4,count=2.

Valeur renvoyée

Aucune (void).

Conditions et limites

Les octets sont écrits dans la banque VRAM sélectionnée. Sur CGB, choisissez la banque 0 pour les numéros de tuile ; cet appel ne change pas de banque.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settilebg16_flush
// Transfer two adjacent tile rows from the RAM buffer to the LCDC-selected BG map. The
// source offset is `64*y + x`; `count` bytes are copied from that row and from the row
// 32 bytes below it. Waits for writable VRAM when necessary. The LCD-on path uses
// interrupt masking and can enable interrupts afterward; it is not a promise to preserve
// the caller's interrupt state or to finish within one VBlank.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
u8 example_tiles[1024];
u8 example_attrs[1024];
void __memset(void* dest, u8 value, u16 len);
void __settilebg16_buf(u8* buf, u8 x, u8 y, u8 tile);
void __settilebg16cgb_buf(u8* tilebuf, u8* attrbuf, u8 x, u8 y, u8 tile, u8 attr);
void __settilebg16_flush(const u8* buf, u8 y, u8 x, u8 count);
void __settilebg16cgb_flush(const u8* tilebuf, const u8* attrbuf, u8 y, u8 x, u8 count);

void main() {
    u8 failures;
    failures = 0;
    tile_color_example_begin();
    __memset(example_tiles, 0, 1024);
    __memset(example_attrs, 0, 1024);
    example_tiles[66]='A'; example_tiles[67]='B';
    example_tiles[98]='C'; example_tiles[99]='D';
    __settilebg16_flush(example_tiles, 1, 2, 2);
    if (tile_example_read(0x9C42, 0) != 'A') { failures++; }
    if (tile_example_read(0x9C43, 0) != 'B') { failures++; }
    if (tile_example_read(0x9C62, 0) != 'C') { failures++; }
    if (tile_example_read(0x9C63, 0) != 'D') { failures++; }
    if (example_tiles[66] != 'A') { failures++; }
    if (example_tiles[99] != 'D') { failures++; }
    if (tile_example_read(0x9C44, 0) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

La cellule affiche AB en haut et CD en bas, à partir de la tuile (2,2) : une cellule de 16×16 pixels en (1,1). Les exemples d’écriture en tampon vérifient que la préparation RAM ne modifie pas la VRAM avant transfert. Les exemples de transfert préparent explicitement les octets RAM au préalable.

Sur CGB, les caractères de l’exemple et le texte des résultats s’affichent en bleu sur fond blanc. La préparation attribue le bleu à la couleur de tracé de la palette 0, sans modifier les attributs de la carte de tuiles. Sur DMG, les mêmes caractères s’affichent en noir sur fond blanc.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=2), soit au pixel (16,16). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
AB
CD
__settilebg16_flush: DMG
DMG
__settilebg16_flush: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilebg16_flush.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilebg16_flush.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:10581

csharp
{
                if (args.Length != 4) Error(expr, "__settilebg16_flush requires 4 arguments");
                EmitTile16FlushRowsIntrinsic(args[0], args[1], args[2], args[3], cgbAttr: false, labelPrefix: "settilebg16flush");
                return;
            }

            // Flush the tile buffer first and copy attributes only when CGB hardware is available.
            if (funcName == "__settilebg16cgb_flush")
            {
                if (args.Length != 5) Error(expr, "__settilebg16cgb_flush requires 5 arguments");
                EmitTile16FlushRowsIntrinsic(args[0], args[2], args[3], args[4], cgbAttr: false, labelPrefix: "settilebg16cgbflush_tile");
                if (IsKnownCgbRuntimeTrue())
                {
                    EmitSetVbkUnchecked(1);
                    EmitTile16FlushRowsIntrinsic(args[1], args[2], args[3], args[4], cgbAttr: true, labelPrefix: "settilebg16cgbflush_attr");
                }
                else
                {
                    AsmOperand skip = MakeUniqueLabel("settilebg16cgbflush_skip");
                    AsmOperand done = MakeUniqueLabel("settilebg16cgbflush_done");
                    EmitRuntimeCgbCheckCall();
                    EmitAsm("OR_A");
                    // Two unrolled row copies can exceed the relative-branch range.
                    EmitAsm("JP_Z", skip);
                    EmitSetVbkUnchecked(1);
                    EmitTile16FlushRowsIntrinsic(args[1], args[2], args[3], args[4], cgbAttr: true, labelPrefix: "settilebg16cgbflush_attr");
                    EmitAsm("JR", done);
__settilebg16cgb_buf

Écrit une cellule de 16×16 pixels dans un tampon de carte en RAM : tile,tile+1 en haut, tile+2,tile+3 en bas. Les valeurs rebouclent à 256. Aucun transfert vers la VRAM n’est effectué.

Écrit aussi le même octet attr dans les quatre cases correspondantes du tampon d’attributs séparé. Les deux tampons étant en RAM ordinaire, cette préparation s’exécute aussi sur DMG.

Syntaxe

c
__settilebg16cgb_buf(tilebuf, attrbuf, x, y, tile, attr);

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Paramètres

tilebuf, attrbuf

Tampon RAM avec 32 octets par ligne de tuiles. Une carte complète de 32×32 demande 1024 octets. Le coin supérieur gauche est à 64*y + 2*x ; les quatre décalages relatifs sont 0,1,32,33.

x, y

Coordonnées en cellules de 16×16 pixels, de 0 à 15 pour un tampon complet de 32×32 tuiles. Leur unité diffère des coordonnées en tuiles de 8×8 des écritures unitaires.

tile

Numéro de tuile, de 0 à 255. Le bit 4 de LCDC détermine l’adressage de son image. Ce n’est pas une adresse d’octet en VRAM.

attr

Octet d’attributs CGB : bits 0–2, palette 0–7 ; bit 3, banque d’image ; bits 5 et 6, retournement horizontal et vertical ; bit 7, priorité du fond ou de la fenêtre. Laissez à zéro le bit 4 inutilisé.

Valeur renvoyée

Aucune (void).

Conditions et limites

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settilebg16cgb_buf
// Write a 16×16-pixel cell into a RAM tile-map buffer: the four 8×8 tile numbers are
// `tile,tile+1` on top and `tile+2,tile+3` below. Values wrap at 256. This does not
// transfer anything to VRAM. Also write the same `attr` byte to all four corresponding
// cells in the separate attribute buffer. Both buffers are ordinary RAM, so this
// preparation also runs on DMG.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
u8 example_tiles[1024];
u8 example_attrs[1024];
void __memset(void* dest, u8 value, u16 len);
void __settilebg16_buf(u8* buf, u8 x, u8 y, u8 tile);
void __settilebg16cgb_buf(u8* tilebuf, u8* attrbuf, u8 x, u8 y, u8 tile, u8 attr);
void __settilebg16_flush(const u8* buf, u8 y, u8 x, u8 count);
void __settilebg16cgb_flush(const u8* tilebuf, const u8* attrbuf, u8 y, u8 x, u8 count);

void main() {
    u8 failures;
    failures = 0;
    tile_example_begin();
    __memset(example_tiles, 0, 1024);
    __memset(example_attrs, 0, 1024);
    __settilebg16cgb_buf(example_tiles, example_attrs, 1, 1, 'A', 2);
    // The buffer operation must not write to VRAM yet.
    if (tile_example_read(0x9C42, 0) != 0) { failures++; }
    // Transfer the prepared cell so its four characters can be seen.
    __settilebg16cgb_flush(example_tiles, example_attrs, 1, 2, 2);
    if (tile_example_read(0x9C42, 0) != 'A') { failures++; }
    if (tile_example_read(0x9C43, 0) != 'B') { failures++; }
    if (tile_example_read(0x9C62, 0) != 'C') { failures++; }
    if (tile_example_read(0x9C63, 0) != 'D') { failures++; }
    if (example_tiles[66] != 'A') { failures++; }
    if (example_tiles[99] != 'D') { failures++; }
    if (tile_example_read(0x9C44, 0) != 0) { failures++; }
    if (example_attrs[66] != 2) { failures++; }
    if (example_attrs[99] != 2) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9C42, 1) != 2) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9C63, 1) != 2) { failures++; }
    tile_example_finish(failures, 1);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

La cellule affiche AB en haut et CD en bas, à partir de la tuile (2,2) : une cellule de 16×16 pixels en (1,1). Les exemples d’écriture en tampon vérifient que la préparation RAM ne modifie pas la VRAM avant transfert. Les exemples de transfert préparent explicitement les octets RAM au préalable.

Les quatre caractères sont bleus sur CGB, car leurs attributs valent 2. Ils sont noirs sur DMG.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=2), soit au pixel (16,16). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
AB
CD
__settilebg16cgb_buf: DMG
DMG
__settilebg16cgb_buf: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilebg16cgb_buf.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilebg16cgb_buf.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:10572

csharp
{
                if (args.Length != 6) Error(expr, "__settilebg16cgb_buf requires 6 arguments");
                EmitTile16BufferedWriteIntrinsic(args[0], args[2], args[3], args[4], incrementQuad: true, labelPrefix: "settilebg16cgbbuf_tile");
                EmitTile16BufferedWriteIntrinsic(args[1], args[2], args[3], args[5], incrementQuad: false, labelPrefix: "settilebg16cgbbuf_attr");
                return;
            }

            // Flush both tile rows belonging to the requested buffered cell row.
            if (funcName == "__settilebg16_flush")
            {
                if (args.Length != 4) Error(expr, "__settilebg16_flush requires 4 arguments");
                EmitTile16FlushRowsIntrinsic(args[0], args[1], args[2], args[3], cgbAttr: false, labelPrefix: "settilebg16flush");
                return;
            }

            // Flush the tile buffer first and copy attributes only when CGB hardware is available.
            if (funcName == "__settilebg16cgb_flush")
            {
                if (args.Length != 5) Error(expr, "__settilebg16cgb_flush requires 5 arguments");
                EmitTile16FlushRowsIntrinsic(args[0], args[2], args[3], args[4], cgbAttr: false, labelPrefix: "settilebg16cgbflush_tile");
                if (IsKnownCgbRuntimeTrue())
                {
                    EmitSetVbkUnchecked(1);
                    EmitTile16FlushRowsIntrinsic(args[1], args[2], args[3], args[4], cgbAttr: true, labelPrefix: "settilebg16cgbflush_attr");
                }
                else
                {
                    AsmOperand ski
__settilebg16cgb_flush

Transfère deux lignes de tuiles adjacentes du tampon RAM vers la carte BG sélectionnée par LCDC. Le décalage source est 64*y + x ; count octets sont copiés depuis cette ligne et celle située 32 octets plus loin.

Écrit le numéro de tuile et l’attribut CGB. Sur DMG, seul le numéro est écrit. Sur CGB, sélectionnez d’abord la banque 0 ; les attributs utilisent la banque 1 et l’appel se termine avec VBK=0.

Attend si nécessaire que la VRAM soit accessible en écriture. LCD actif, le chemin masque les interruptions et peut les réactiver ensuite ; il ne garantit ni l’état d’interruption initial ni une fin dans un seul VBlank.

Syntaxe

c
__settilebg16cgb_flush(tilebuf, attrbuf, y, x, count);

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Paramètres

tilebuf, attrbuf

Tampon de carte lisible avec un pas de 32 octets par ligne. Une carte complète occupe 1024 octets. La forme à deux tampons reçoit les numéros et les attributs dans des tableaux séparés de même disposition, sans les entrelacer.

y, x, count

L’ordre est y,x,count. y est une ligne de cellules de 16 pixels (0–15) ; x et count sont en tuiles de 8 pixels. Respectez x+count <= 32. Pour une cellule en (2,3), utilisez y=3,x=4,count=2.

Valeur renvoyée

Aucune (void).

Conditions et limites

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settilebg16cgb_flush
// Transfer two adjacent tile rows from the RAM buffer to the LCDC-selected BG map. The
// source offset is `64*y + x`; `count` bytes are copied from that row and from the row
// 32 bytes below it. Write a tile number and its CGB attribute. On DMG, only the tile-
// number write is performed. On CGB, enter with VRAM bank 0 selected; attributes use
// bank 1 and the call finishes with `VBK=0`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
u8 example_tiles[1024];
u8 example_attrs[1024];
void __memset(void* dest, u8 value, u16 len);
void __settilebg16_buf(u8* buf, u8 x, u8 y, u8 tile);
void __settilebg16cgb_buf(u8* tilebuf, u8* attrbuf, u8 x, u8 y, u8 tile, u8 attr);
void __settilebg16_flush(const u8* buf, u8 y, u8 x, u8 count);
void __settilebg16cgb_flush(const u8* tilebuf, const u8* attrbuf, u8 y, u8 x, u8 count);

void main() {
    u8 failures;
    failures = 0;
    tile_example_begin();
    __memset(example_tiles, 0, 1024);
    __memset(example_attrs, 0, 1024);
    example_tiles[66]='A'; example_tiles[67]='B';
    example_tiles[98]='C'; example_tiles[99]='D';
    example_attrs[66]=2; example_attrs[67]=2;
    example_attrs[98]=2; example_attrs[99]=2;
    __settilebg16cgb_flush(example_tiles, example_attrs, 1, 2, 2);
    if (tile_example_read(0x9C42, 0) != 'A') { failures++; }
    if (tile_example_read(0x9C43, 0) != 'B') { failures++; }
    if (tile_example_read(0x9C62, 0) != 'C') { failures++; }
    if (tile_example_read(0x9C63, 0) != 'D') { failures++; }
    if (example_tiles[66] != 'A') { failures++; }
    if (example_tiles[99] != 'D') { failures++; }
    if (tile_example_read(0x9C44, 0) != 0) { failures++; }
    if (example_attrs[66] != 2) { failures++; }
    if (example_attrs[99] != 2) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9C42, 1) != 2) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9C63, 1) != 2) { failures++; }
    tile_example_finish(failures, 1);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

La cellule affiche AB en haut et CD en bas, à partir de la tuile (2,2) : une cellule de 16×16 pixels en (1,1). Les exemples d’écriture en tampon vérifient que la préparation RAM ne modifie pas la VRAM avant transfert. Les exemples de transfert préparent explicitement les octets RAM au préalable.

Les quatre caractères sont bleus sur CGB, car leurs attributs valent 2. Ils sont noirs sur DMG.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=2), soit au pixel (16,16). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
AB
CD
__settilebg16cgb_flush: DMG
DMG
__settilebg16cgb_flush: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilebg16cgb_flush.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilebg16cgb_flush.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:10589

csharp
{
                if (args.Length != 5) Error(expr, "__settilebg16cgb_flush requires 5 arguments");
                EmitTile16FlushRowsIntrinsic(args[0], args[2], args[3], args[4], cgbAttr: false, labelPrefix: "settilebg16cgbflush_tile");
                if (IsKnownCgbRuntimeTrue())
                {
                    EmitSetVbkUnchecked(1);
                    EmitTile16FlushRowsIntrinsic(args[1], args[2], args[3], args[4], cgbAttr: true, labelPrefix: "settilebg16cgbflush_attr");
                }
                else
                {
                    AsmOperand skip = MakeUniqueLabel("settilebg16cgbflush_skip");
                    AsmOperand done = MakeUniqueLabel("settilebg16cgbflush_done");
                    EmitRuntimeCgbCheckCall();
                    EmitAsm("OR_A");
                    // Two unrolled row copies can exceed the relative-branch range.
                    EmitAsm("JP_Z", skip);
                    EmitSetVbkUnchecked(1);
                    EmitTile16FlushRowsIntrinsic(args[1], args[2], args[3], args[4], cgbAttr: true, labelPrefix: "settilebg16cgbflush_attr");
                    EmitAsm("JR", done);
                    EmitLabel(skip);
                    EmitLabel(done);
                }
                return;
            }

            // 2.14 __getbgmapbase() -> u16 in HL
// Returns the BG tilemap base address selected by LCDC bit3:
// bit3=0 => 0x9800, bit3=1 => 0x9C00
// Treated as a compiler intrinsic (no C definition required).
if (funcName == "__getbgmapbase")
{
    if (args.Length != 0) Error(expr, "__getb
__smac16

Calcule une multiplication-accumulation signée.

Syntaxe

c
u16 __smac16(u16 acc, u16 a, u8 b);

Paramètres

acc

Accumulateur sur 16 bits à ajouter.

a

Motif de bits signé sur 16 bits représentant −32768 à 32767.

b

Motif de bits d’un octet signé, coefficient de −128 à 127.

Valeur renvoyée

Chaque terme vaut trunc(a*b/256) et est ajouté à acc ; renvoie les 16 bits de poids faible.

Conditions et limites

Sur GB, les produits sont divisés par 256 ; les résultats signés sont tronqués vers zéro. Les variantes Q1.7 doublent ce résultat tronqué et peuvent différer d’une unité d’une division exacte par 128. Chaque terme est arrondi séparément ; l’addition finale reboucle sur 16 bits.

Exemple d’utilisation

c
// Compare literal coefficients with values read from RAM.
result[26]=__smac16(65500,-513,-64);
v0=65500;v1=-513;v2=-64;
result[27]=__smac16(v0,v1,v2);
// Reverse coefficient signs and verify negative products.
result[28]=__smac16(65500,-513,64);
v0=65500;v1=-513;v2=64;
result[29]=__smac16(v0,v1,v2);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Comparez les entrées littérales aux valeurs en RAM. Les résultats représentatifs sont affichés sur 16 bits non signés ; les négatifs utilisent le complément à deux. Le GB remet à l’échelle les produits à virgule fixe, tandis que le FC renvoie des produits entiers : des entrées identiques ne donnent donc pas nécessairement les mêmes résultats.

L’extrait stocke les valeurs suivantes sur 16 bits : result[26]=92, result[27]=92, result[28]=65372, result[29]=65372. Chaque calcul avec une entrée littérale correspond à son équivalent avec une entrée en RAM. L’écran affiche des valeurs représentatives ; le tableau result conserve tous les résultats.

__smac16: DMG
DMG
__smac16: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:11516

kitaqgb/kitaqgb/CodeGenerator.cs:11516

csharp
if (funcName == "__smac16")
            {
                if (args.Length != 3) Error(expr, "__smac16 requires 3 arguments");

                // mul = (a*b)>>8 (signed)
                EmitDotTermToHL(args[1], args[2], signed: true, q1_7: false, labelPrefix: "__smac16_mul");

                // acc + mul
                EmitAsm("PUSH_HL");
                CompileIntoHL(args[0]);
                EmitAsm("POP_DE");
                EmitAsm("ADD_HL_DE");
                LastCallReturnsHL = true;
                return;
            }
__smac16_q1_7

Calcule une multiplication-accumulation signée.

Syntaxe

c
u16 __smac16_q1_7(u16 acc, u16 a, u8 b);

Paramètres

acc

Accumulateur sur 16 bits à ajouter.

a

Motif de bits signé sur 16 bits représentant −32768 à 32767.

b

Motif de bits d’un octet signé, coefficient de −128 à 127.

Valeur renvoyée

Chaque terme vaut 2*trunc(a*b/256) et est ajouté à acc ; renvoie les 16 bits de poids faible.

Conditions et limites

Sur GB, les produits sont divisés par 256 ; les résultats signés sont tronqués vers zéro. Les variantes Q1.7 doublent ce résultat tronqué et peuvent différer d’une unité d’une division exacte par 128. Chaque terme est arrondi séparément ; l’addition finale reboucle sur 16 bits.

Exemple d’utilisation

c
// Compare literal coefficients with values read from RAM.
result[0]=__smac16_q1_7(65500,-513,-64);
v0=65500;v1=-513;v2=-64;
result[1]=__smac16_q1_7(v0,v1,v2);
// Reverse coefficient signs and verify negative products.
result[2]=__smac16_q1_7(65500,-513,64);
v0=65500;v1=-513;v2=64;
result[3]=__smac16_q1_7(v0,v1,v2);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Comparez les entrées littérales aux valeurs en RAM. Les résultats représentatifs sont affichés sur 16 bits non signés ; les négatifs utilisent le complément à deux. Le GB remet à l’échelle les produits à virgule fixe, tandis que le FC renvoie des produits entiers : des entrées identiques ne donnent donc pas nécessairement les mêmes résultats.

L’extrait stocke les valeurs suivantes sur 16 bits : result[0]=220, result[1]=220, result[2]=65244, result[3]=65244. Chaque calcul avec une entrée littérale correspond à son équivalent avec une entrée en RAM. L’écran affiche des valeurs représentatives ; le tableau result conserve tous les résultats.

__smac16_q1_7: DMG
DMG
__smac16_q1_7: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:11533

kitaqgb/kitaqgb/CodeGenerator.cs:11533

csharp
if (funcName == "__smac16_q1_7")
            {
                if (args.Length != 3) Error(expr, "__smac16_q1_7 requires 3 arguments");

                // mul = (a*b)>>7 (approx via >>8 then *2)
                EmitDotTermToHL(args[1], args[2], signed: true, q1_7: true, labelPrefix: "__smac16_q1_7_mul");

                // acc + mul
                EmitAsm("PUSH_HL");
                CompileIntoHL(args[0]);
                EmitAsm("POP_DE");
                EmitAsm("ADD_HL_DE");
                LastCallReturnsHL = true;
                return;
            }
__smul16x8_q1_7

Calcule une multiplication signée.

Syntaxe

c
u16 __smul16x8_q1_7(u16 a, u8 b);

Paramètres

a

Motif de bits signé sur 16 bits représentant −32768 à 32767.

b

Motif de bits d’un octet signé, coefficient de −128 à 127.

Valeur renvoyée

Chaque terme vaut 2*trunc(a*b/256) ; renvoie les 16 bits de poids faible.

Conditions et limites

Sur GB, les produits sont divisés par 256 ; les résultats signés sont tronqués vers zéro. Les variantes Q1.7 doublent ce résultat tronqué et peuvent différer d’une unité d’une division exacte par 128. Chaque terme est arrondi séparément ; l’addition finale reboucle sur 16 bits.

Exemple d’utilisation

c
// Compare literal coefficients with values read from RAM.
result[12]=__smul16x8_q1_7(-513,-64);
v0=-513;v1=-64;
result[13]=__smul16x8_q1_7(v0,v1);
// Reverse coefficient signs and verify negative products.
result[14]=__smul16x8_q1_7(-513,64);
v0=-513;v1=64;
result[15]=__smul16x8_q1_7(v0,v1);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Comparez les entrées littérales aux valeurs en RAM. Les résultats représentatifs sont affichés sur 16 bits non signés ; les négatifs utilisent le complément à deux. Le GB remet à l’échelle les produits à virgule fixe, tandis que le FC renvoie des produits entiers : des entrées identiques ne donnent donc pas nécessairement les mêmes résultats.

L’extrait stocke les valeurs suivantes sur 16 bits : result[12]=256, result[13]=256, result[14]=65280, result[15]=65280. Chaque calcul avec une entrée littérale correspond à son équivalent avec une entrée en RAM. L’écran affiche des valeurs représentatives ; le tableau result conserve tous les résultats.

__smul16x8_q1_7: DMG
DMG
__smul16x8_q1_7: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:11490

kitaqgb/kitaqgb/CodeGenerator.cs:11490

csharp
if (funcName == "__smul16x8_q1_7")
            {
                if (args.Length != 2) Error(expr, "__smul16x8_q1_7 requires 2 arguments");

                EmitDotTermToHL(args[0], args[1], signed: true, q1_7: true, labelPrefix: "__smul16x8_q1_7");
                LastCallReturnsHL = true;
                return;
            }
__sram_read8

Lit un octet de SRAM à une adresse CPU.

Syntaxe

c
u8 __sram_read8(u16 address);

Paramètres

address

Adresse CPU dans $A000..$BFFF.

Valeur renvoyée

Renvoie l’octet sans vérifier la présence de RAM ni la validité des données.

Conditions et limites

Écrit 0x0A à $0000 pour activer la RAM avant l’accès, puis zéro pour la désactiver. Ne choisit pas la banque et ne valide pas l’adresse ; l’appelant configure la banque RAM. Aucun en-tête de sauvegarde ni somme de contrôle n’est géré.

Exemple d’utilisation

c
*((u8*)0x4000)=0;result[12]=__sram_read8(0xA006);result[19]=*((u8*)0xA006);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Sauvegardez trois octets, 7/42/255, puis testez une longueur différente, un emplacement invalide et une somme de contrôle endommagée. Un chargement réussi donne 42 au milieu ; une lecture échouée préserve la destination à 77 ; la validation après effacement renvoie 0.

__sram_read8: DMG
DMG
__sram_read8: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_save.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_save.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=8k
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:8825

kitaqgb/kitaqgb/CodeGenerator.cs:8825

csharp
if (funcName == "__sram_read8")
            {
                if (args.Length != 1) Error(expr, "__sram_read8(addr) expects 1 argument");

                CompileIntoHL(args[0]);
                EmitAsm("PUSH_HL");

                EmitAsm("LD_A_IMM", new AsmOperand(0x0A, AddressMode.Immediate));
                EmitAsm("LD_MEM_A", new AsmOperand(0x0000, AddressMode.Absolute));

                EmitAsm("POP_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("LD_B_A");

                EmitAsm("LD_A_IMM", new AsmOperand(0x00, AddressMode.Immediate));
                EmitAsm("LD_MEM_A", new AsmOperand(0x0000, AddressMode.Absolute));
                EmitAsm("LD_A_B");
                return;
            }
__sram_write8

Écrit un octet de SRAM à une adresse CPU.

Syntaxe

c
void __sram_write8(u16 address, u8 value);

Paramètres

address

Adresse CPU dans $A000..$BFFF.

value

Valeur d’octet de 0 à 255.

Valeur renvoyée

Aucune valeur de retour.

Conditions et limites

Écrit 0x0A à $0000 pour activer la RAM avant l’accès, puis zéro pour la désactiver. Ne choisit pas la banque et ne valide pas l’adresse ; l’appelant configure la banque RAM. Aucun en-tête de sauvegarde ni somme de contrôle n’est géré.

Exemple d’utilisation

c
__sram_write8(0xA006,8);result[20]=*((u8*)0xA006);result[13]=save_exists(0);readback[0]=77;result[14]=save_read(0,readback,3);result[15]=readback[0];

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Sauvegardez trois octets, 7/42/255, puis testez une longueur différente, un emplacement invalide et une somme de contrôle endommagée. Un chargement réussi donne 42 au milieu ; une lecture échouée préserve la destination à 77 ; la validation après effacement renvoie 0.

__sram_write8: DMG
DMG
__sram_write8: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_save.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_save.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=8k
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:8846

kitaqgb/kitaqgb/CodeGenerator.cs:8846

csharp
if (funcName == "__sram_write8")
            {
                if (args.Length != 2) Error(expr, "__sram_write8(addr, value) expects 2 arguments");

                CompileIntoHL(args[0]);
                EmitAsm("PUSH_HL");
                CompileIntoA(args[1]);
                EmitAsm("LD_C_A");

                EmitAsm("LD_A_IMM", new AsmOperand(0x0A, AddressMode.Immediate));
                EmitAsm("LD_MEM_A", new AsmOperand(0x0000, AddressMode.Absolute));

                EmitAsm("POP_HL");
                EmitAsm("LD_A_C");
                EmitAsm("LD_HL_A");

                EmitAsm("LD_A_IMM", new AsmOperand(0x00, AddressMode.Immediate));
                EmitAsm("LD_MEM_A", new AsmOperand(0x0000, AddressMode.Absolute));
                return;
            }
__svbk_get

Lit la banque WRAM effective mappée dans $D000–$DFFF. Lit les bits 0–2 de SVBK et normalise le sélecteur 0 en banque 1. Renvoie un numéro de banque, pas une capacité RAM, une adresse ou un pointeur.

Syntaxe

c
u8 __svbk_get();

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Valeur renvoyée

Banque effective actuelle en u8, 1–7 ; le sélecteur 0 est renvoyé comme 1.

Conditions et limites

__svbk_get et __svbk_set exigent --cgb=cgb_only ou #pragma rom_cgb cgb_only. Les cibles DMG et mixtes les refusent avec KQ2103, même si une condition d'exécution évite l'appel. Utilisez l'écriture directe protégée uniquement si son contrat différent convient au programme.

Changer de banque affecte tout accès dans $D000–$DFFF ; la WRAM fixe de $C000–$CFFF reste accessible. Placez pile et état nécessaire hors de cette fenêtre. L'exemple utilise --stack-bank=fixed et des variables explicitement placées en WRAM fixe. Coordonnez interruptions et DMA bancarisé ; ces API n'ajoutent aucune section critique.

Exemple d’utilisation

c
if (__svbk_get()!=1) failures++; // Normalize raw selector zero to effective bank 1.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Le programme CGB seul écrit 1 à $D100 dans la banque WRAM 2 et 2 à la même adresse dans la banque 3, relit les deux puis restaure la banque 1. Ces valeurs choisissent un carré rouge de 8 × 8 en (16,24) et un contour bleu de 8 × 8, épais d'un pixel, en (16,48). FAILED CHECKS 000 vérifie normalisation, séparation et retour de l'ancienne banque avec constantes, variables, expressions et résultats de fonction. Six compilations invalides vérifient KQ2103 en DMG/mixte et KQ2102 pour les banques constantes 0 et 8. Exécution dans KOKURA ; matériel réel et changements concurrents par interruption non testés.

__svbk_get: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_wram_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_wram_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb_only --no-cache --no-disasm
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:7387

csharp
{
                if (args.Length != 0) Error(expr, "__svbk_get() expects 0 arguments");
                if (!Program.IsCgbOnlyTargetRequested())
                    Error(expr, ErrorCode.BankedWramRequiresCgbOnly, "__svbk_get() requires #pragma rom_cgb cgb_only or --cgb=cgb_only");
                EmitReadNormalizedSvbkIntoA();
                return;
            }

            // Select a WRAM bank and return the prior normalized bank; diagnose out-of-range constant requests.
            if (funcName == "__svbk_set")
            {
                if (args.Length != 1) Error(expr, "__svbk_set(bank_u8) expects 1 argument");
                if (!Program.IsCgbOnlyTargetRequested())
                    Error(expr, ErrorCode.BankedWramRequiresCgbOnly, "__svbk_set() requires #pragma rom_cgb cgb_only or --cgb=cgb_only");

                Expr bankExpr = FoldConstants(args[0]);
                if (bankExpr.Match(Tag.Integer, out int bankLiteral) && (bankLiteral < 1 || bankLiteral > 7))
                    Error(expr, ErrorCode.InvalidWramXBank, "__svbk_set() bank must be in range 1..7 (got {0})", bankLiteral);

                EmitReadNormalizedSvbkIntoA();
                EmitAsm("LD_B_A");
                CompileIntoA(bankExpr);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem));
                EmitAsm("LD_A_B");
                return;
            }

            // __cgb_safe_set_* (safe CGB-only register write wrappers)
            if (TryGetCgbSafeRegister(funcName, out int cgbOffset, out string cgbReg))
            {
__svbk_set

Change la banque WRAM mappée dans $D000–$DFFF et renvoie la banque effective précédente. Conservez ce résultat pour restaurer la banque de l'appelant. Ne copie ni n'efface la mémoire et ne restaure pas automatiquement la banque.

Syntaxe

c
u8 __svbk_set(u8 bank);

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Paramètres

bank

Banque demandée en u8, 1–7. Une constante hors plage produit KQ2102. Les valeurs dynamiques ne sont pas contrôlées ; validez-les avant l'appel, sans compter sur le masque du registre.

Valeur renvoyée

Banque effective avant le changement en u8, 1–7. C'est l'ancienne banque, pas celle qui vient d'être sélectionnée.

Conditions et limites

__svbk_get et __svbk_set exigent --cgb=cgb_only ou #pragma rom_cgb cgb_only. Les cibles DMG et mixtes les refusent avec KQ2103, même si une condition d'exécution évite l'appel. Utilisez l'écriture directe protégée uniquement si son contrat différent convient au programme.

Changer de banque affecte tout accès dans $D000–$DFFF ; la WRAM fixe de $C000–$CFFF reste accessible. Placez pile et état nécessaire hors de cette fenêtre. L'exemple utilise --stack-bank=fixed et des variables explicitement placées en WRAM fixe. Coordonnez interruptions et DMA bancarisé ; ces API n'ajoutent aucune section critique.

Exemple d’utilisation

c
previous_bank=__svbk_set(requested_bank);
if(previous_bank!=1 || __svbk_get()!=2) failures++;
*((u8*)0xD100)=1; // This byte belongs to bank 2.
previous_bank=__svbk_set(next_bank()); // Dynamic argument evaluates to 3.
if(previous_bank!=2 || __svbk_get()!=3) failures++;
*((u8*)0xD100)=2; // Same CPU address, different physical RAM.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Le programme CGB seul écrit 1 à $D100 dans la banque WRAM 2 et 2 à la même adresse dans la banque 3, relit les deux puis restaure la banque 1. Ces valeurs choisissent un carré rouge de 8 × 8 en (16,24) et un contour bleu de 8 × 8, épais d'un pixel, en (16,48). FAILED CHECKS 000 vérifie normalisation, séparation et retour de l'ancienne banque avec constantes, variables, expressions et résultats de fonction. Six compilations invalides vérifient KQ2103 en DMG/mixte et KQ2102 pour les banques constantes 0 et 8. Exécution dans KOKURA ; matériel réel et changements concurrents par interruption non testés.

__svbk_set: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_wram_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_wram_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb_only --no-cache --no-disasm
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:7397

csharp
{
                if (args.Length != 1) Error(expr, "__svbk_set(bank_u8) expects 1 argument");
                if (!Program.IsCgbOnlyTargetRequested())
                    Error(expr, ErrorCode.BankedWramRequiresCgbOnly, "__svbk_set() requires #pragma rom_cgb cgb_only or --cgb=cgb_only");

                Expr bankExpr = FoldConstants(args[0]);
                if (bankExpr.Match(Tag.Integer, out int bankLiteral) && (bankLiteral < 1 || bankLiteral > 7))
                    Error(expr, ErrorCode.InvalidWramXBank, "__svbk_set() bank must be in range 1..7 (got {0})", bankLiteral);

                EmitReadNormalizedSvbkIntoA();
                EmitAsm("LD_B_A");
                CompileIntoA(bankExpr);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem));
                EmitAsm("LD_A_B");
                return;
            }

            // __cgb_safe_set_* (safe CGB-only register write wrappers)
            if (TryGetCgbSafeRegister(funcName, out int cgbOffset, out string cgbReg))
            {
                if (args.Length != 1) Program.Error(funcName + "(value_u8) expects 1 argument");

                if (IsKnownCgbRuntimeTrue())
                {
                    CompileIntoA(args[0]);
                    if (cgbOffset == 0x4F)
                        EmitAsm("AND_IMM", new AsmOperand(0x01, AddressMode.Immediate));
                    EmitAsm("LDH_MEM_A", new AsmOperand(cgbOffset, AddressMode.HighMem));
                    CgbGuardedWriteCount++;
                    CgbGuardedRegisters.Add(cgbReg);
                    return;
__vram_memcpy_unsafe

Copie immédiatement len octets de src vers dst par adresses croissantes. Revient à la fin du transfert, sans mise en file. Les deux adresses avancent d’un octet, sans pas de ligne de carte ni conversion de format graphique.

Syntaxe

c
void __vram_memcpy_unsafe(u16 dst, const void* src, u16 len);

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Paramètres

dst

Adresse du premier octet de destination, normalement dans la VRAM 0x8000–0x9FFF. Toute la plage doit être valide. Le CGB utilise la banque courante ; dans la carte, la banque 0 contient les indices et la banque 1 les attributs. Ces routines ne choisissent pas la carte selon LCDC, ne sautent pas de ligne et n’allouent pas de VRAM.

src

Pointeur sur les octets source, valide et correctement mappé pendant tout l’appel. Les données sont lues immédiatement, sans conservation pour un flush ultérieur. La banque source n’est pas changée automatiquement. Utilisez des plages disjointes : cette copie vers l’avant n’offre pas la sémantique de memmove.

len

Nombre d’octets consécutifs, de 0 à 65535, pas de tuiles. Gardez toute la plage dans une source valide et une destination modifiable ; l’API ne vérifie pas les limites et n’empêche pas le bouclage d’adresse sur 16 bits.

Valeur renvoyée

Aucune (void).

Conditions et limites

Ignore toute attente LCD/STAT et écrit directement par le CPU. Éteignez le LCD ou assurez-vous que tout le transfert tient dans un intervalle d’écriture maîtrisé. Entrer en VBlank ne rend pas sûr un transfert de taille quelconque. Cette intrinsèque n’émet ni DI ni EI pour le transfert et ne borne ni ne vérifie les plages.

Exemple d’utilisation

c
// The LCD is off throughout these unchecked writes.
__vram_memcpy_unsafe(0x98C2,strip,computed_length());

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple utilise __vram_memcpy_unsafe pour écrire trois octets de carte à la tuile (2,6), soit au pixel (16,48). On attend [1,2,3] de gauche à droite dans une zone de 24 × 8, avec les cases voisines vides. Le programme complet place chaque copie ou remplissage testé sur sa propre ligne.

Les graphismes originaux fournis utilisent la tuile 1 pour un carré plein de 8 × 8, la 2 pour ses quatre colonnes gauches et la 3 pour un triangle aligné à gauche, élargi d’un pixel par ligne de 1 à 8. Le DMG utilise le noir sur blanc ; le CGB distingue les opérations par des couleurs en gardant le texte noir.

Le carré au pixel (16,88) doit subsister après les appels sûrs et sans attente de longueur nulle. LCD actif, la copie et le remplissage sûrs ajoutent des carrés en (16,96) et (32,96). FAILED CHECKS doit afficher 000. Les tests d’émulateur comparent les pixels et le compteur. Le CGB utilise ici la banque 0 ; les autres banques et le matériel physique ne sont pas testés.

Sur CGB, les lignes de MEMCPY à FILL UNSAFE répètent la séquence rouge, bleu, vert. Le carré de ZERO COUNT est rouge. Dans LCD ON, celui de gauche est bleu et celui de droite vert. Le texte reste noir. Les tests vérifient ces couleurs sur chaque pixel dessiné.

__vram_memcpy_unsafe: DMG
DMG
__vram_memcpy_unsafe: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\vram_memory_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_memory_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:8104

csharp
{
                if (args.Length != 3) Error(expr, "__vram_memcpy_unsafe(dst, src, len) expects 3 arguments");

                CompileIntoHL(args[0]); EmitAsm("PUSH_HL"); // dst
                CompileIntoHL(args[1]); EmitAsm("PUSH_HL"); // src
                // Use the same count specialization without emitting LCD-mode waits.
                Expr vramCopyUnsafeLen = FoldConstants(args[2]);
                EmitLengthExprIntoBC(vramCopyUnsafeLen);
                EmitAsm("POP_DE"); // src
                EmitAsm("POP_HL"); // dst

                if (vramCopyUnsafeLen.Match(Tag.Integer, out int vramCopyUnsafeLenConst) &&
                    TryEmitVramMemcpyConstCount(false, vramCopyUnsafeLenConst, "vramcpyu_small"))
                    return;

                AsmOperand vcuLoop = MakeUniqueLabel("vramcpyu_loop");
                AsmOperand vcuDone = MakeUniqueLabel("vramcpyu_done");
                EmitLabel(vcuLoop);
                EmitAsm("LD_A_B");
                EmitAsm("OR_C");
                EmitAsm("JR_Z", vcuDone);
                EmitAsm("LD_A_DE");
                EmitAsm("LDI_HL_A");
                EmitAsm("INC_DE");
                EmitAsm("DEC_BC");
                EmitAsm("JR", vcuLoop);
                EmitLabel(vcuDone);
                return;
            }

            // __vram_memset(dst_ptr, value_u8, len_u16)
            // Safe VRAM fill: if LCD is on, each byte write is gated by STAT mode 0/1.
            if (funcName == "__vram_memset")
            {
                if (args.Length != 3) Error(expr, "__vram_memset(dst, value, l
__vram_memset_unsafe

Remplit immédiatement len octets consécutifs à partir de dst avec l’octet value. Répète un octet, pas un motif graphique complet. Dans une carte, cela répète un indice de tuile ; dans les motifs, cela modifie les plans de bits des pixels.

Syntaxe

c
void __vram_memset_unsafe(u16 dst, u8 value, u16 len);

Les noms des paramètres servent à expliquer l’intrinsèque du compilateur ; il ne s’agit pas d’un prototype C à ajouter.

Paramètres

dst

Adresse du premier octet de destination, normalement dans la VRAM 0x8000–0x9FFF. Toute la plage doit être valide. Le CGB utilise la banque courante ; dans la carte, la banque 0 contient les indices et la banque 1 les attributs. Ces routines ne choisissent pas la carte selon LCDC, ne sautent pas de ligne et n’allouent pas de VRAM.

value

Un octet de 0 à 255 répété. Son effet visible dépend du contenu de destination : indices de tuiles, plans de pixels ou autre structure d’octets.

len

Nombre d’octets consécutifs, de 0 à 65535, pas de tuiles. Gardez toute la plage dans une source valide et une destination modifiable ; l’API ne vérifie pas les limites et n’empêche pas le bouclage d’adresse sur 16 bits.

Valeur renvoyée

Aucune (void).

Conditions et limites

Ignore toute attente LCD/STAT et écrit directement par le CPU. Éteignez le LCD ou assurez-vous que tout le transfert tient dans un intervalle d’écriture maîtrisé. Entrer en VBlank ne rend pas sûr un transfert de taille quelconque. Cette intrinsèque n’émet ni DI ni EI pour le transfert et ne borne ni ne vérifie les plages.

Exemple d’utilisation

c
__vram_memset_unsafe(0x9942,1,computed_length()); // Safe here because the LCD is off.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple utilise __vram_memset_unsafe pour remplir trois octets de carte à partir de la tuile (2,10), pixel (16,80), avec l’indice 1. On attend une barre pleine de 24 × 8, entourée de cases vides. Le compte provient de variables ou du résultat d’une fonction ; les trois octets doivent bien recevoir la valeur 1.

Les graphismes originaux fournis utilisent la tuile 1 pour un carré plein de 8 × 8, la 2 pour ses quatre colonnes gauches et la 3 pour un triangle aligné à gauche, élargi d’un pixel par ligne de 1 à 8. Le DMG utilise le noir sur blanc ; le CGB distingue les opérations par des couleurs en gardant le texte noir.

Le carré au pixel (16,88) doit subsister après les appels sûrs et sans attente de longueur nulle. LCD actif, la copie et le remplissage sûrs ajoutent des carrés en (16,96) et (32,96). FAILED CHECKS doit afficher 000. Les tests d’émulateur comparent les pixels et le compteur. Le CGB utilise ici la banque 0 ; les autres banques et le matériel physique ne sont pas testés.

Sur CGB, les lignes de MEMCPY à FILL UNSAFE répètent la séquence rouge, bleu, vert. Le carré de ZERO COUNT est rouge. Dans LCD ON, celui de gauche est bleu et celui de droite vert. Le texte reste noir. Les tests vérifient ces couleurs sur chaque pixel dessiné.

__vram_memset_unsafe: DMG
DMG
__vram_memset_unsafe: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\vram_memory_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_memory_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:8196

csharp
{
                if (args.Length != 3) Error(expr, "__vram_memset_unsafe(dst, value, len) expects 3 arguments");

                CompileIntoHL(args[0]); EmitAsm("PUSH_HL"); // dst
                CompileIntoA(args[1]); EmitAsm("LD_D_A"); // value
                // Specialize a constant unsafe fill or fall back to its unchecked byte loop.
                Expr vramSetUnsafeLen = FoldConstants(args[2]);
                EmitLengthExprIntoBC(vramSetUnsafeLen);
                EmitAsm("POP_HL"); // dst

                if (vramSetUnsafeLen.Match(Tag.Integer, out int vramSetUnsafeLenConst) &&
                    TryEmitVramMemsetConstCount(false, vramSetUnsafeLenConst, "vramsetu_small"))
                    return;

                AsmOperand vsuLoop = MakeUniqueLabel("vramsetu_loop");
                AsmOperand vsuDone = MakeUniqueLabel("vramsetu_done");
                EmitLabel(vsuLoop);
                EmitAsm("LD_A_B");
                EmitAsm("OR_C");
                EmitAsm("JR_Z", vsuDone);
                EmitAsm("LD_A_D");
                EmitAsm("LDI_HL_A");
                EmitAsm("DEC_BC");
                EmitAsm("JR", vsuLoop);
                EmitLabel(vsuDone);
                return;
            }

            // __far_memcpy(dst_ptr, bank_u8, src_ptr, len_u16)
            // Copies from switchable ROM bank via a bank0 helper and restores original bank.
            if (funcName == "__far_memcpy")
            {
                if (args.Length != 4) Error(expr, "__far_memcpy(dst, bank, src, len) expects 4 arguments");

                CompileIntoHL(
__wait_ly

LCD actif, attend que LY soit égal à la ligne cible.

Syntaxe

c
void __wait_ly(u8 ly);

Paramètres

ly

Ligne de balayage de 0 à 153 ; une valeur supérieure à 153 ne termine pas l’attente tant que le LCD fonctionne.

Valeur renvoyée

Aucune valeur de retour.

Conditions et limites

Renvoie immédiatement sur la ligne cible ; si elle est passée, attend la trame suivante. Renvoie aussi immédiatement si le LCD est éteint.

Exemple d’utilisation

c
M_LCDC=0x91;__wait_ly(80);result[9]=*((u8*)0xFF44);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Découvrez sélection de carte, défilement, imbrication et attentes de ligne. Les bases valent 38912/39936 ; les jetons imbriqués, 0/1 ; les LY cibles observés, 80/144. Vérifiez aussi que les attentes reviennent avec le LCD éteint.

__wait_ly: DMG
DMG
__wait_ly: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_display.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_display.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implémentation et source

kitaqgb/kitaqgb/CodeGenerator.cs:7483

kitaqgb/kitaqgb/CodeGenerator.cs:7483

csharp
if (funcName == "__wait_ly")
            {
                if (args.Length != 1) Error(expr, "__wait_ly(target) expects 1 argument");

                CompileIntoA(args[0]);
                EmitAsm("LD_B_A");

                AsmOperand wlyLoop = MakeUniqueLabel("waitly_loop");
                AsmOperand wlyDone = MakeUniqueLabel("waitly_done");

                // If LCD is off, LY is not meaningful; return.
                EmitAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem)); // LCDC
                EmitAsm("AND_IMM", new AsmOperand(0x80, AddressMode.Immediate));
                EmitAsm("JR_Z", wlyDone);

                EmitLabel(wlyLoop);
                EmitAsm("LDH_A_MEM", new AsmOperand(0x44, AddressMode.HighMem)); // LY
                EmitAsm("CP_B");
                EmitAsm("JR_NZ", wlyLoop);
                EmitLabel(wlyDone);
                return;
            }

physics2d.h — Mouvement et collisions en 2D

Fournit l’intégration des boîtes, les contacts AABB et un solveur optionnel de réponse aux surfaces. Les corps utilisent des centres et des demi-dimensions ; active=0 exclut un emplacement, et inv_mass_q8<=0 rend le corps statique. KQRect utilise au contraire un coin supérieur gauche et une largeur/hauteur. Les contacts entre boîtes n’utilisent pas le champ restitution du corps ; les réponses aux surfaces utilisent les coefficients de KQSurface2D.

Utilisez des unités entières cohérentes pour les positions, rayons, demi-dimensions, vitesses et la gravité. Un pas correspond à un tick de simulation. Les exemples utilisent une unité par pixel. Ces routines ne convertissent pas automatiquement les positions en Q8.8 et n’effectuent aucun rendu.

Le monde emprunte son tableau : l’appelant alloue et possède la mémoire des corps et doit la garder valide. Initialiser le monde n’initialise pas les éléments du tableau.

__smul16x8

Multiplie une valeur signée de 16 bits par un coefficient signé de 8 bits, divise par 256 et tronque vers zéro.

Syntaxe

c
s16 __smul16x8(s16 a, s8 b);

Paramètres

a

Valeur signée de 16 bits à mettre à l’échelle.

b

Coefficient signé sur un octet, de -128 à 127.

Valeur renvoyée

Renvoie le produit mis à l’échelle sous forme d’entier signé de 16 bits.

Conditions et limites

Le coefficient va de -128 à 127, avec une échelle où 256 représente 1,0. Les produits sont tronqués vers zéro quel que soit leur signe : -257*64/256 renvoie -64.

Exemple d’utilisation

c
result[13]=__smul16x8(-257,64);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

À gauche, l’état initial ; à droite, après un tick. Une boîte rouge de 16x16, centrée en (24,24) avec une vitesse de (4,16), atteint (28,36) et sa vitesse Y s’annule sur le sol bleu. Les origines des panneaux sont aux coordonnées écran (8,48) et (88,48). Sur DMG, tous les repères sont noirs. Les contrôles RAM couvrent aussi l’initialisation, les fonctions de réglage, l’intégration, les tests de rectangles et de points et la mise à l’échelle signée.

__smul16x8: DMG — À gauche, l’état initial ; à droite, après un tick. Une boîte rouge de 16x16, centrée en (24,24) avec une vitesse de (4,16), atteint (28,36) et sa vitesse Y s’annule sur le sol bleu. Les origines des panneaux sont aux coordonnées écran (8,48) et (88,48). Sur DMG, tous les repères sont noirs. Les contrôles RAM couvrent aussi l’initialisation, les fonctions de réglage, l’intégration, les tests de rectangles et de points et la mise à l’échelle signée.
DMG — À gauche, l’état initial ; à droite, après un tick. Une boîte rouge de 16x16, centrée en (24,24) avec une vitesse de (4,16), atteint (28,36) et sa vitesse Y s’annule sur le sol bleu. Les origines des panneaux sont aux coordonnées écran (8,48) et (88,48). Sur DMG, tous les repères sont noirs. Les contrôles RAM couvrent aussi l’initialisation, les fonctions de réglage, l’intégration, les tests de rectangles et de points et la mise à l’échelle signée.
__smul16x8: CGB — À gauche, l’état initial ; à droite, après un tick. Une boîte rouge de 16x16, centrée en (24,24) avec une vitesse de (4,16), atteint (28,36) et sa vitesse Y s’annule sur le sol bleu. Les origines des panneaux sont aux coordonnées écran (8,48) et (88,48). Sur DMG, tous les repères sont noirs. Les contrôles RAM couvrent aussi l’initialisation, les fonctions de réglage, l’intégration, les tests de rectangles et de points et la mise à l’échelle signée.
CGB — À gauche, l’état initial ; à droite, après un tick. Une boîte rouge de 16x16, centrée en (24,24) avec une vitesse de (4,16), atteint (28,36) et sa vitesse Y s’annule sur le sol bleu. Les origines des panneaux sont aux coordonnées écran (8,48) et (88,48). Sur DMG, tous les repères sont noirs. Les contrôles RAM couvrent aussi l’initialisation, les fonctions de réglage, l’intégration, les tests de rectangles et de points et la mise à l’échelle signée.

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body2d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body2d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/physics2d.h:9

Notes de déclaration (texte source)
Multiply a signed word by a signed byte; divide by 256, truncating toward zero.

kitaqgb/kitaqgb/CodeGenerator.cs:11478

csharp
if (funcName == "__smul16x8")
            {
                if (args.Length != 2) Error(expr, "__smul16x8 requires 2 arguments");

                EmitDotTermToHL(args[0], args[1], signed: true, q1_7: false, labelPrefix: "__smul16x8");
                LastCallReturnsHL = true;
                return;
            }

rpg.h — Nombres aléatoires, texte, cartes, sauvegardes et services RPG/ADV/SLG

Les fonctions de texte à position absolue dessinent directement sur la carte du fond. text_window ouvre une zone encadrée, text_clear_rect remplit une région et text_print_xy écrit les octets de tuiles sans décoder les contrôles. text_print_u8, text_print_u16 et text_print_s16 convertissent les nombres en décimal en n’utilisant que les cases nécessaires. Réservez et effacez un champ complet pour remplacer des valeurs de longueurs différentes.

Les fonctions RNG déclarées dans rpg.h sont implémentées dans lib/rng.c. Elles partagent le générateur GB du compilateur : rng_seed le réinitialise, rng8/rng_next8 consomment un octet, rng16/rng_next16 en consomment deux, et rand_range/rng_range, rng_chance et weighted_choice adaptent les tirages aux choix du jeu. Compilez rng.c avec le programme si vous appelez ces fonctions. Pour des tests reproductibles, fixez la graine et l’ordre des appels ; les choix par modulo ne garantissent pas des probabilités exactes.

Le décodage RLE utilise des paires d’octets count/value et un compte nul terminal. rle_decode lit un pointeur ordinaire vers la RAM ; rle_decode_far lit un pointeur associé à une banque ROM vers la RAM ; __rle_decode_vram décompresse directement en VRAM avec des écritures adaptées au LCD. Les fonctions RAM sont dans lib/rle.c ; l’opération VRAM directe est générée par le compilateur. Toutes trois renvoient le nombre d’octets produits et exigent un flux valide ainsi qu’un espace de destination suffisant.

Le stockage d’état de jeu déclaré dans rpg.h est implémenté par lib/flags.c. Les trois fonctions d’indicateurs agissent sur 2048 bits booléens indépendants ; quest_state et quest_set_state agissent sur 64 octets indépendants pour les étapes définies par le jeu. Compilez flags.c, respectez les limites d’ID et utilisez un système de sauvegarde distinct si la progression doit survivre à l’extinction.

Le dialogue fenêtré partage un rectangle actif, un curseur et un réglage de vitesse. text_open initialise la zone, text_close l’efface, text_set_speed règle les attentes VBlank par caractère. text_print et text_print_far interprètent les flux ; text_choice exécute un sélecteur vertical bloquant. Chargez les tuiles, dimensionnez la zone et prévoyez la gestion de ces appels bloquants dans le jeu.

Fournit le dessin de cartes et métatuiles, les scripts, les sauvegardes SRAM et la recherche de chemins. Les cases ordinaires mesurent 8×8 pixels, les métatuiles 16×16. Chargement/dessin, masques de déplacement et d’attaque sont distincts ; le jeu coordonne le chargement graphique et l’exécution des événements.

La recherche utilise environ 4 Kio d’espace de travail partagé non réentrant. Limitez les cartes à 1024 cases et les distances explorées à 254. La distance 0xFF signifie « non atteint » : un trajet de 255 pas n’est donc pas représentable. Les sorties de coût et de masque nécessitent width*height octets accessibles en écriture.

Par défaut, quatre emplacements de 512 octets contiennent chacun un en-tête de six octets et jusqu’à 506 octets de données. L’exemple utilise MBC5 avec 8 Kio de SRAM. Les appels se terminent avec la RAM désactivée, sans restaurer sa banque ni son état d’activation précédent.

Les menus proposent une sélection bloquante ou des appels séparés de traitement des entrées et de dessin, pilotés par la boucle de jeu. Le menu d’inventaire affiche les noms des objets et leurs quantités.

__bit_clear

Efface le drapeau choisi à 0 sans effacer le reste de l’octet. Utilisez-le pour retirer une condition enregistrée, par exemple un état temporaire. Effacer de nouveau le même bit le laisse à 0.

Syntaxe

c
void __bit_clear(u8* p, u16 bit_index);

Paramètres

p

Pointeur vers le premier octet du tableau de drapeaux en mémoire accessible au CPU. Cet appel n’alloue ni n’initialise le tableau. Utilisez de la RAM accessible en écriture pour mettre, effacer ou inverser les bits ; le test ne fait que lire.

bit_index

Indice de bit u16 à partir de zéro (0–65535), et non décalage d’octet ou masque. L’indice 0 choisit le masque 0x01 dans l’octet 0 ; 7 choisit 0x80 dans l’octet 0 ; 8 choisit 0x01 dans l’octet 1. En général, le décalage est index / 8 et le masque 1 << (index % 8). Pour un tableau de N octets, exigez index < 8*N et une plage d’adresses CPU valide.

Valeur renvoyée

Aucune (void).

Conditions et limites

L’appelant doit fournir assez de stockage. Il n’y a ni argument de longueur, ni découpage aux limites, ni changement de banque, ni transfert graphique automatique. Un grand numéro de bit peut viser un octet bien au-delà du tableau. Gardez le mappage mémoire stable ; n’utilisez pas ces fonctions de drapeaux RAM comme raccourci pour des registres matériels dont les accès ont des effets de bord.

Mise à 1, effacement et inversion lisent un octet, modifient le bit choisi puis réécrivent l’octet. Cette séquence n’est pas atomique. Coordonnez les écritures de la boucle principale et des gestionnaires d’interruption pour ne pas perdre une modification intermédiaire d’un autre bit du même octet.

Exemple d’utilisation

c
__bit_clear(bit_flags,2);
bit_clear_value=bit_flags[0]; // A6 & FB = A2, decimal 162.
__bit_clear(bit_flags,2);     // Clearing it twice leaves it clear.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Le programme complet arrive ici avec l’octet 0 à 0xA6. Effacer le bit 2 donne 0xA2 (binaire 10100010, décimal 162) ; CLEAR BIT2 doit afficher 162. Un nouvel effacement du bit 2 ne change rien. Les autres drapeaux sont préservés dans les deux appels.

Valeurs GB attendues en décimal : TEST OFF 000, TEST ON 001, SET BIT1 166, CLEAR BIT2 162, TOGGLE BIT7 034, RESTORE 162, NEXT BYTE 130, FAILED 000. NEXT BYTE vient de l’activation de l’indice 9 dans le second octet, de 0x80 à 0x82, sans modifier l’octet 0. Le DMG affiche du noir sur blanc ; le CGB, du bleu sur blanc.

Le programme vérifie chaque transition affichée et indique le nombre d’échecs. Mettre, effacer et inverser ne renvoient rien ; les lignes affichent l’octet RAM obtenu. Des tests distincts sur émulateur comparent les 520 octets de tampons protégés dans 136 cas par plateforme : indices constants et calculés franchissant les limites d’octet et de 256 octets, et les huit masques non nuls possibles. Ce sont des vérifications d’état RAM, pas une démonstration de dessin. Le matériel physique et les écritures concurrentes par interruption n’ont pas été testés.

__bit_clear: DMG
DMG
__bit_clear: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\bit_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\bit_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/rpg.h:187

kitaqgb/kitaqgb/CodeGenerator.cs:8582

csharp
if (funcName == "__bit_test" || funcName == "__bit_set" || funcName == "__bit_clear" || funcName == "__bit_toggle")
            {
                if (args.Length != 2) Error(expr, funcName + "(ptr, bit_index) expects 2 arguments");

                CompileIntoHL(args[0]); EmitAsm("PUSH_HL"); // ptr
                CompileIntoHL(args[1]); EmitAsm("PUSH_HL"); // bit_index

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("AND_IMM", new AsmOperand(7, AddressMode.Immediate));
                EmitAsm("LD_B_A"); // bit number 0..7

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_E_A");
                EmitAsm("INC_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("LD_D_A");
                EmitAsm("LD_H_D");
                EmitAsm("LD_L_E");
                EmitShiftRightLogicalHL(3);
                EmitAsm("PUSH_HL"); // byte offset

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_E_A");
                EmitAsm("INC_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("LD_D_A");

                EmitAsm("POP_HL");
                EmitAsm("ADD_HL_DE"); // HL = ptr + (bit_index >> 3)

                AsmOperand maskReady = MakeUniqueLabel("bit_mask_ready");
                AsmOperand maskLoop = MakeUniqueLabel("bit_mask_loop");
                EmitAsm("LD_A_B");
                EmitAsm("OR_A");
                EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
                EmitAsm("JR_Z", maskReady);
                EmitLabel(maskLoop);
                EmitAsm("ADD_A");
                EmitAsm("DEC_B");
                EmitAsm("JR_NZ", maskLoop);
                EmitLabel(maskReady);
                EmitAsm("LD_B_A"); // mask

                if (funcName == "__bit_test")
                {
                    AsmOperand retZero = MakeUniqueLabel("bit_test_zero");
                    AsmOperand done = MakeUniqueLabel("bit_test_done");
                    EmitAsm("LD_A_HL");
                    EmitAsm("AND_B");
                    EmitAsm("JR_Z", retZero);
                    EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
                    EmitAsm("JR", done);
                    EmitLabel(retZero);
                    EmitAsm("XOR_A");
                    EmitLabel(done);
                    EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                    return;
                }

                if (funcName == "__bit_set")
                {
                    EmitAsm("LD_A_HL");
                    EmitAsm("OR_B");
                    EmitAsm("LD_HL_A");
                    EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                    return;
                }

                if (funcName == "__bit_clear")
                {
                    EmitAsm("LD_A_B");
                    EmitAsm("CPL");
                    EmitAsm("LD_B_A");
                    EmitAsm("LD_A_HL");
                    EmitAsm("AND_B");
                    EmitAsm("LD_HL_A");
                    EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                    return;
                }

                EmitAsm("LD_A_HL");
                EmitAsm("XOR_B");
                EmitAsm("LD_HL_A");
                EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                return;
            }
__bit_set

Met le drapeau choisi à 1 en préservant les sept autres bits du même octet. Vous pouvez ainsi marquer une quête terminée ou un objet ramassé. Remettre à 1 un bit déjà à 1 ne le change pas ; ce n’est pas une inversion.

Syntaxe

c
void __bit_set(u8* p, u16 bit_index);

Paramètres

p

Pointeur vers le premier octet du tableau de drapeaux en mémoire accessible au CPU. Cet appel n’alloue ni n’initialise le tableau. Utilisez de la RAM accessible en écriture pour mettre, effacer ou inverser les bits ; le test ne fait que lire.

bit_index

Indice de bit u16 à partir de zéro (0–65535), et non décalage d’octet ou masque. L’indice 0 choisit le masque 0x01 dans l’octet 0 ; 7 choisit 0x80 dans l’octet 0 ; 8 choisit 0x01 dans l’octet 1. En général, le décalage est index / 8 et le masque 1 << (index % 8). Pour un tableau de N octets, exigez index < 8*N et une plage d’adresses CPU valide.

Valeur renvoyée

Aucune (void).

Conditions et limites

L’appelant doit fournir assez de stockage. Il n’y a ni argument de longueur, ni découpage aux limites, ni changement de banque, ni transfert graphique automatique. Un grand numéro de bit peut viser un octet bien au-delà du tableau. Gardez le mappage mémoire stable ; n’utilisez pas ces fonctions de drapeaux RAM comme raccourci pour des registres matériels dont les accès ont des effets de bord.

Mise à 1, effacement et inversion lisent un octet, modifient le bit choisi puis réécrivent l’octet. Cette séquence n’est pas atomique. Coordonnez les écritures de la boucle principale et des gestionnaires d’interruption pour ne pas perdre une modification intermédiaire d’un autre bit du même octet.

Exemple d’utilisation

c
__bit_set(bit_flags,1);
bit_set_value=bit_flags[0]; // A4 | 02 = A6, decimal 166.
__bit_set(bit_flags,1);     // Setting it twice leaves it set.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Mettez à 1 le bit 1 de 0xA4 : 10100100 | 00000010 devient 10100110, soit 0xA6 (166 en décimal). La ligne SET BIT1 doit afficher 166. L’exemple répète l’opération et vérifie que l’octet reste à 0xA6, montrant que des mises à 1 répétées n’inversent pas le drapeau.

Valeurs GB attendues en décimal : TEST OFF 000, TEST ON 001, SET BIT1 166, CLEAR BIT2 162, TOGGLE BIT7 034, RESTORE 162, NEXT BYTE 130, FAILED 000. NEXT BYTE vient de l’activation de l’indice 9 dans le second octet, de 0x80 à 0x82, sans modifier l’octet 0. Le DMG affiche du noir sur blanc ; le CGB, du bleu sur blanc.

Le programme vérifie chaque transition affichée et indique le nombre d’échecs. Mettre, effacer et inverser ne renvoient rien ; les lignes affichent l’octet RAM obtenu. Des tests distincts sur émulateur comparent les 520 octets de tampons protégés dans 136 cas par plateforme : indices constants et calculés franchissant les limites d’octet et de 256 octets, et les huit masques non nuls possibles. Ce sont des vérifications d’état RAM, pas une démonstration de dessin. Le matériel physique et les écritures concurrentes par interruption n’ont pas été testés.

__bit_set: DMG
DMG
__bit_set: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\bit_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\bit_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/rpg.h:186

kitaqgb/kitaqgb/CodeGenerator.cs:8582

csharp
if (funcName == "__bit_test" || funcName == "__bit_set" || funcName == "__bit_clear" || funcName == "__bit_toggle")
            {
                if (args.Length != 2) Error(expr, funcName + "(ptr, bit_index) expects 2 arguments");

                CompileIntoHL(args[0]); EmitAsm("PUSH_HL"); // ptr
                CompileIntoHL(args[1]); EmitAsm("PUSH_HL"); // bit_index

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("AND_IMM", new AsmOperand(7, AddressMode.Immediate));
                EmitAsm("LD_B_A"); // bit number 0..7

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_E_A");
                EmitAsm("INC_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("LD_D_A");
                EmitAsm("LD_H_D");
                EmitAsm("LD_L_E");
                EmitShiftRightLogicalHL(3);
                EmitAsm("PUSH_HL"); // byte offset

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_E_A");
                EmitAsm("INC_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("LD_D_A");

                EmitAsm("POP_HL");
                EmitAsm("ADD_HL_DE"); // HL = ptr + (bit_index >> 3)

                AsmOperand maskReady = MakeUniqueLabel("bit_mask_ready");
                AsmOperand maskLoop = MakeUniqueLabel("bit_mask_loop");
                EmitAsm("LD_A_B");
                EmitAsm("OR_A");
                EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
                EmitAsm("JR_Z", maskReady);
                EmitLabel(maskLoop);
                EmitAsm("ADD_A");
                EmitAsm("DEC_B");
                EmitAsm("JR_NZ", maskLoop);
                EmitLabel(maskReady);
                EmitAsm("LD_B_A"); // mask

                if (funcName == "__bit_test")
                {
                    AsmOperand retZero = MakeUniqueLabel("bit_test_zero");
                    AsmOperand done = MakeUniqueLabel("bit_test_done");
                    EmitAsm("LD_A_HL");
                    EmitAsm("AND_B");
                    EmitAsm("JR_Z", retZero);
                    EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
                    EmitAsm("JR", done);
                    EmitLabel(retZero);
                    EmitAsm("XOR_A");
                    EmitLabel(done);
                    EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                    return;
                }

                if (funcName == "__bit_set")
                {
                    EmitAsm("LD_A_HL");
                    EmitAsm("OR_B");
                    EmitAsm("LD_HL_A");
                    EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                    return;
                }

                if (funcName == "__bit_clear")
                {
                    EmitAsm("LD_A_B");
                    EmitAsm("CPL");
                    EmitAsm("LD_B_A");
                    EmitAsm("LD_A_HL");
                    EmitAsm("AND_B");
                    EmitAsm("LD_HL_A");
                    EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                    return;
                }

                EmitAsm("LD_A_HL");
                EmitAsm("XOR_B");
                EmitAsm("LD_HL_A");
                EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                return;
            }
__bit_test

Lit un drapeau dans un tableau de bits compactés sans modifier le tableau. Huit drapeaux indépendants peuvent ainsi partager chaque octet de RAM. Le résultat indique si le bit choisi est à zéro ou à un ; sa représentation numérique dépend de la cible.

Syntaxe

c
u8 __bit_test(const u8* p, u16 bit_index);

Paramètres

p

Pointeur vers le premier octet du tableau de drapeaux en mémoire accessible au CPU. Cet appel n’alloue ni n’initialise le tableau. Utilisez de la RAM accessible en écriture pour mettre, effacer ou inverser les bits ; le test ne fait que lire.

bit_index

Indice de bit u16 à partir de zéro (0–65535), et non décalage d’octet ou masque. L’indice 0 choisit le masque 0x01 dans l’octet 0 ; 7 choisit 0x80 dans l’octet 0 ; 8 choisit 0x01 dans l’octet 1. En général, le décalage est index / 8 et le masque 1 << (index % 8). Pour un tableau de N octets, exigez index < 8*N et une plage d’adresses CPU valide.

Valeur renvoyée

u8 : exactement 0 si le bit est effacé, 1 s’il est actif. L’implémentation GB normalise l’octet masqué vers ces deux valeurs.

Conditions et limites

L’appelant doit fournir assez de stockage. Il n’y a ni argument de longueur, ni découpage aux limites, ni changement de banque, ni transfert graphique automatique. Un grand numéro de bit peut viser un octet bien au-delà du tableau. Gardez le mappage mémoire stable ; n’utilisez pas ces fonctions de drapeaux RAM comme raccourci pour des registres matériels dont les accès ont des effets de bord.

Exemple d’utilisation

c
bit_off=__bit_test(bit_flags,1); // Bit 1 of A4 is clear: zero.
bit_on=__bit_test(bit_flags,7);  // Bit 7 is set: GB returns 1; FC returns 128.
bit_is_on=0;
if(bit_on!=0)bit_is_on=1; // The same nonzero test works on both targets.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Commencez avec l’octet 0 à 0xA4 (binaire 10100100). Tester le bit 1 renvoie 0 ; tester le bit 7 renvoie 1 sur GB ou 128 sur FC. L’exemple convertit aussi ce résultat non nul en drapeau commun et vérifie que l’octet initial est inchangé. Regardez TEST OFF et TEST ON dans l’écran ci-dessous.

Valeurs GB attendues en décimal : TEST OFF 000, TEST ON 001, SET BIT1 166, CLEAR BIT2 162, TOGGLE BIT7 034, RESTORE 162, NEXT BYTE 130, FAILED 000. NEXT BYTE vient de l’activation de l’indice 9 dans le second octet, de 0x80 à 0x82, sans modifier l’octet 0. Le DMG affiche du noir sur blanc ; le CGB, du bleu sur blanc.

Le programme vérifie chaque transition affichée et indique le nombre d’échecs. Mettre, effacer et inverser ne renvoient rien ; les lignes affichent l’octet RAM obtenu. Des tests distincts sur émulateur comparent les 520 octets de tampons protégés dans 136 cas par plateforme : indices constants et calculés franchissant les limites d’octet et de 256 octets, et les huit masques non nuls possibles. Ce sont des vérifications d’état RAM, pas une démonstration de dessin. Le matériel physique et les écritures concurrentes par interruption n’ont pas été testés.

__bit_test: DMG
DMG
__bit_test: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\bit_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\bit_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/rpg.h:185

kitaqgb/kitaqgb/CodeGenerator.cs:8582

csharp
if (funcName == "__bit_test" || funcName == "__bit_set" || funcName == "__bit_clear" || funcName == "__bit_toggle")
            {
                if (args.Length != 2) Error(expr, funcName + "(ptr, bit_index) expects 2 arguments");

                CompileIntoHL(args[0]); EmitAsm("PUSH_HL"); // ptr
                CompileIntoHL(args[1]); EmitAsm("PUSH_HL"); // bit_index

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("AND_IMM", new AsmOperand(7, AddressMode.Immediate));
                EmitAsm("LD_B_A"); // bit number 0..7

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_E_A");
                EmitAsm("INC_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("LD_D_A");
                EmitAsm("LD_H_D");
                EmitAsm("LD_L_E");
                EmitShiftRightLogicalHL(3);
                EmitAsm("PUSH_HL"); // byte offset

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_E_A");
                EmitAsm("INC_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("LD_D_A");

                EmitAsm("POP_HL");
                EmitAsm("ADD_HL_DE"); // HL = ptr + (bit_index >> 3)

                AsmOperand maskReady = MakeUniqueLabel("bit_mask_ready");
                AsmOperand maskLoop = MakeUniqueLabel("bit_mask_loop");
                EmitAsm("LD_A_B");
                EmitAsm("OR_A");
                EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
                EmitAsm("JR_Z", maskReady);
                EmitLabel(maskLoop);
                EmitAsm("ADD_A");
                EmitAsm("DEC_B");
                EmitAsm("JR_NZ", maskLoop);
                EmitLabel(maskReady);
                EmitAsm("LD_B_A"); // mask

                if (funcName == "__bit_test")
                {
                    AsmOperand retZero = MakeUniqueLabel("bit_test_zero");
                    AsmOperand done = MakeUniqueLabel("bit_test_done");
                    EmitAsm("LD_A_HL");
                    EmitAsm("AND_B");
                    EmitAsm("JR_Z", retZero);
                    EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
                    EmitAsm("JR", done);
                    EmitLabel(retZero);
                    EmitAsm("XOR_A");
                    EmitLabel(done);
                    EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                    return;
                }

                if (funcName == "__bit_set")
                {
                    EmitAsm("LD_A_HL");
                    EmitAsm("OR_B");
                    EmitAsm("LD_HL_A");
                    EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                    return;
                }

                if (funcName == "__bit_clear")
                {
                    EmitAsm("LD_A_B");
                    EmitAsm("CPL");
                    EmitAsm("LD_B_A");
                    EmitAsm("LD_A_HL");
                    EmitAsm("AND_B");
                    EmitAsm("LD_HL_A");
                    EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                    return;
                }

                EmitAsm("LD_A_HL");
                EmitAsm("XOR_B");
                EmitAsm("LD_HL_A");
                EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                return;
            }
__bit_toggle

Inverse le drapeau choisi : 0 devient 1 et 1 devient 0. Les autres bits sont préservés. Deux appels sur le même bit rétablissent son état initial, à condition qu’aucune autre écriture de ce bit ne survienne entre les appels.

Syntaxe

c
void __bit_toggle(u8* p, u16 bit_index);

Paramètres

p

Pointeur vers le premier octet du tableau de drapeaux en mémoire accessible au CPU. Cet appel n’alloue ni n’initialise le tableau. Utilisez de la RAM accessible en écriture pour mettre, effacer ou inverser les bits ; le test ne fait que lire.

bit_index

Indice de bit u16 à partir de zéro (0–65535), et non décalage d’octet ou masque. L’indice 0 choisit le masque 0x01 dans l’octet 0 ; 7 choisit 0x80 dans l’octet 0 ; 8 choisit 0x01 dans l’octet 1. En général, le décalage est index / 8 et le masque 1 << (index % 8). Pour un tableau de N octets, exigez index < 8*N et une plage d’adresses CPU valide.

Valeur renvoyée

Aucune (void).

Conditions et limites

L’appelant doit fournir assez de stockage. Il n’y a ni argument de longueur, ni découpage aux limites, ni changement de banque, ni transfert graphique automatique. Un grand numéro de bit peut viser un octet bien au-delà du tableau. Gardez le mappage mémoire stable ; n’utilisez pas ces fonctions de drapeaux RAM comme raccourci pour des registres matériels dont les accès ont des effets de bord.

Mise à 1, effacement et inversion lisent un octet, modifient le bit choisi puis réécrivent l’octet. Cette séquence n’est pas atomique. Coordonnez les écritures de la boucle principale et des gestionnaires d’interruption pour ne pas perdre une modification intermédiaire d’un autre bit du même octet.

Exemple d’utilisation

c
__bit_toggle(bit_flags,7);
bit_toggle_value=bit_flags[0]; // A2 ^ 80 = 22, decimal 34.
__bit_toggle(bit_flags,7);
bit_restore_value=bit_flags[0]; // Two toggles restore A2.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Le programme complet commence cette étape à 0xA2. Inverser le bit 7 donne 0x22 (34 en décimal), donc TOGGLE BIT7 affiche 034. L’inverser encore restaure 0xA2 et RESTORE affiche 162. Ces observations montrent les deux sens de l’inversion tout en préservant les autres bits.

Valeurs GB attendues en décimal : TEST OFF 000, TEST ON 001, SET BIT1 166, CLEAR BIT2 162, TOGGLE BIT7 034, RESTORE 162, NEXT BYTE 130, FAILED 000. NEXT BYTE vient de l’activation de l’indice 9 dans le second octet, de 0x80 à 0x82, sans modifier l’octet 0. Le DMG affiche du noir sur blanc ; le CGB, du bleu sur blanc.

Le programme vérifie chaque transition affichée et indique le nombre d’échecs. Mettre, effacer et inverser ne renvoient rien ; les lignes affichent l’octet RAM obtenu. Des tests distincts sur émulateur comparent les 520 octets de tampons protégés dans 136 cas par plateforme : indices constants et calculés franchissant les limites d’octet et de 256 octets, et les huit masques non nuls possibles. Ce sont des vérifications d’état RAM, pas une démonstration de dessin. Le matériel physique et les écritures concurrentes par interruption n’ont pas été testés.

__bit_toggle: DMG
DMG
__bit_toggle: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\bit_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\bit_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/rpg.h:188

kitaqgb/kitaqgb/CodeGenerator.cs:8582

csharp
if (funcName == "__bit_test" || funcName == "__bit_set" || funcName == "__bit_clear" || funcName == "__bit_toggle")
            {
                if (args.Length != 2) Error(expr, funcName + "(ptr, bit_index) expects 2 arguments");

                CompileIntoHL(args[0]); EmitAsm("PUSH_HL"); // ptr
                CompileIntoHL(args[1]); EmitAsm("PUSH_HL"); // bit_index

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("AND_IMM", new AsmOperand(7, AddressMode.Immediate));
                EmitAsm("LD_B_A"); // bit number 0..7

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_E_A");
                EmitAsm("INC_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("LD_D_A");
                EmitAsm("LD_H_D");
                EmitAsm("LD_L_E");
                EmitShiftRightLogicalHL(3);
                EmitAsm("PUSH_HL"); // byte offset

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_E_A");
                EmitAsm("INC_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("LD_D_A");

                EmitAsm("POP_HL");
                EmitAsm("ADD_HL_DE"); // HL = ptr + (bit_index >> 3)

                AsmOperand maskReady = MakeUniqueLabel("bit_mask_ready");
                AsmOperand maskLoop = MakeUniqueLabel("bit_mask_loop");
                EmitAsm("LD_A_B");
                EmitAsm("OR_A");
                EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
                EmitAsm("JR_Z", maskReady);
                EmitLabel(maskLoop);
                EmitAsm("ADD_A");
                EmitAsm("DEC_B");
                EmitAsm("JR_NZ", maskLoop);
                EmitLabel(maskReady);
                EmitAsm("LD_B_A"); // mask

                if (funcName == "__bit_test")
                {
                    AsmOperand retZero = MakeUniqueLabel("bit_test_zero");
                    AsmOperand done = MakeUniqueLabel("bit_test_done");
                    EmitAsm("LD_A_HL");
                    EmitAsm("AND_B");
                    EmitAsm("JR_Z", retZero);
                    EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
                    EmitAsm("JR", done);
                    EmitLabel(retZero);
                    EmitAsm("XOR_A");
                    EmitLabel(done);
                    EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                    return;
                }

                if (funcName == "__bit_set")
                {
                    EmitAsm("LD_A_HL");
                    EmitAsm("OR_B");
                    EmitAsm("LD_HL_A");
                    EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                    return;
                }

                if (funcName == "__bit_clear")
                {
                    EmitAsm("LD_A_B");
                    EmitAsm("CPL");
                    EmitAsm("LD_B_A");
                    EmitAsm("LD_A_HL");
                    EmitAsm("AND_B");
                    EmitAsm("LD_HL_A");
                    EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                    return;
                }

                EmitAsm("LD_A_HL");
                EmitAsm("XOR_B");
                EmitAsm("LD_HL_A");
                EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                return;
            }
__copy16

Copie exactement 16 octets de src vers dst, sans argument de longueur. On peut ainsi copier un enregistrement de 16 octets ou une tuile GB/NES placée en mémoire accessible au processeur. Le nombre 16 désigne des octets, pas des bits ; cet appel ne charge pas les graphismes dans le matériel vidéo.

Syntaxe

c
void __copy16(void* dst, const void* src);

Paramètres

dst

Pointeur vers le premier octet accessible en écriture de la destination. Prévoyez une zone contiguë assez grande pour toute l’opération ; aucun tampon n’est alloué automatiquement.

src

Pointeur vers le premier octet lisible de la source. Toute la zone doit rester accessible et inchangée pendant la copie.

Valeur renvoyée

Aucune (void).

Conditions et limites

Utilisez des zones source et destination sans chevauchement. Ces routines copient vers les adresses croissantes et n’offrent pas la sécurité de memmove en cas de recouvrement ; écraser une partie de la source encore non lue peut altérer la suite de la copie.

L’opération se termine avant de rendre la main. Elle ne change pas de banque ROM/RAM et ne limite pas automatiquement la zone aux bornes d’un tampon. Maintenez le mappage stable, utilisez des zones valides et coordonnez l’accès avec les interruptions utilisant les mêmes tampons.

Sur GB, il s’agit d’accès mémoire ordinaires du processeur. Ils n’attendent pas un créneau sûr pour la VRAM/OAM, ne lancent pas de DMA et ne sélectionnent pas les banques VRAM/WRAM du CGB. Utilisez les API de transfert graphique dédiées à ces tâches.

Exemple d’utilisation

c
memory_reset();
__copy16(memory_destination+1,memory_source+1);
memory_copy_16=memory_check(16,0); // Exactly 16 bytes, not 16 bits.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple utilise __copy16 pour copier 16 octets suivant un motif connu à partir du décalage 1. memory_check compare les tableaux source et destination en entier ; la ligne COPY16 doit afficher 016. Le premier octet sert de garde et les octets suivant le bloc doivent également rester inchangés.

Le programme complet initialise deux tampons de 264 octets avant chaque opération, puis vérifie tous les octets de la source, de la destination et des zones de garde. L’écran attendu affiche COPY WORD 257, FILL WORD 257, COPY BYTE 255, FILL BYTE 255, COPY16 016, COPY32 032, ZERO CALLS 002 et FAILED 000. WORD/BYTE désignent la largeur du compteur, pas la taille des éléments copiés. Ces nombres proviennent des vérifications de l’exemple ; ces fonctions void ne renvoient aucune valeur.

Les captures montrent du texte noir sur blanc sur DMG, bleu sur blanc sur CGB et bleu sur noir sur FC. L’exemple vérifie des opérations en RAM ; le texte en présente les résultats, sans illustrer une primitive de dessin. Des contrôles distincts sur émulateur couvrent 88 cas par plateforme : longueurs constantes ou déterminées à l’exécution de 0, 1, 16, 17, 32, 33, 64, 65 et 255 octets, plus 256 et 257 pour les compteurs sur 16 bits. Aucun test sur matériel réel n’a été effectué.

__copy16: DMG
DMG
__copy16: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\memory_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\memory_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/rpg.h:177

kitaqgb/kitaqgb/CodeGenerator.cs:8247

csharp
if (funcName == "__copy16" || funcName == "__copy32")
            {
                if (args.Length != 2) Error(expr, funcName + "(dst, src) expects 2 arguments");

                int copyLen = (funcName == "__copy16") ? 16 : 32;
                CompileIntoHL(args[0]); EmitAsm("PUSH_HL"); // dst
                CompileIntoHL(args[1]); EmitAsm("PUSH_HL"); // src
                EmitAsm("LD_BC_IMM", new AsmOperand(copyLen, AddressMode.Immediate16));
                EmitAsm("POP_DE"); // src
                EmitAsm("POP_HL"); // dst
                EmitRamMemcpyLoop(funcName.TrimStart('_'));
                return;
            }

void EmitRamMemcpyLoop(string labelPrefix)
    {
        AsmOperand loop = MakeUniqueLabel(labelPrefix + "_loop");
        AsmOperand done = MakeUniqueLabel(labelPrefix + "_done");

        EmitLabel(loop);
        EmitAsm("LD_A_B");
        EmitAsm("OR_C");
        EmitAsm("JR_Z", done);
        EmitAsm("LD_A_DE");
        EmitAsm("LDI_HL_A");
        EmitAsm("INC_DE");
        EmitAsm("DEC_BC");
        EmitAsm("JR", loop);
        EmitLabel(done);
    }

// kitaqgb/kitaqgb/Lowerer.cs:2145
if (expr.MatchAny(Tag.Call, out callFunc, out callArgs))
        {
            string knownCallName;
            if (callFunc != null && callFunc.Match(Tag.Name, out knownCallName) &&
                knownCallName == "__cgb_is_cgb" &&
                callArgs != null && callArgs.Length == 0 &&
                // A resolved target configuration can replace the zero-argument CGB query with a literal.
                Program.TryGetKnownCgbRuntimeValue(out int knownCgbValue))
            {
                return Expr.Make(Tag.Integer, knownCgbValue).WithSource(expr.Source);
            }

            Expr newFunc = LowerExpr(ctx, callFunc, false, prefix);

            // Optimization (post-A3): when we must spill a non-atomic argument into a temp,
            // prefer a u8 temp if the *parameter type* is u8-like.
            // This preserves semantics (implicit truncation to u8) while avoiding needless
            // u16 temporaries created by integer promotions.
            CType[] paramTypes = null;
            string __fname = null;
            if (newFunc.Match(Tag.Name, out __fname))
            {
                paramTypes = ctx.FindFuncParams(__fname);
                if (paramTypes == null)
                {
                    CType fnTypeFromName;
                    if (TryGetCallableFunctionType(ctx.FindTypeOfName(__fname), out fnTypeFromName))
                        paramTypes = fnTypeFromName.ParamTypes;
                }
            }
            else
            {
                CType fnTypeFromExpr;
                if (TryGetCallableFunctionType(InferType(ctx, newFunc), out fnTypeFromExpr))
                    paramTypes = fnTypeFromExpr.ParamTypes;
            }

            Expr[] newArgs = new Expr[callArgs.Length];
            for (int i = 0; i < callArgs.Length; i++)
            {
                Expr a = LowerExpr(ctx, callArgs[i], allowExtract, prefix);
                bool preserveConstantArgIntrinsic = false;
                if (a.MatchAny(Tag.Call, out Expr argFunc, out Expr[] _argCallArgs) &&
                    argFunc.Match(Tag.Name, out string argFuncName))
                {
                    preserveConstantArgIntrinsic = (argFuncName == "__bankof" || argFuncName == "__cgb_is_cgb");
                }
                // Keep evaluable constants and special intrinsic/bank operands visible to code generation instead of spilling them.
                bool preserveCompileTimeConstantArg = ctx.Global.TryEvalConstInt(a, out _);
                bool preserveFarcallBankArg = (__fname == "__farcall" && i == 0);

                if (allowExtract && !preserveFarcallBankArg && !preserveConstantArgIntrinsic && !preserveCompileTimeConstantArg && !IsAtom(a))
                {
                    // If we know the callee's parameter type, use it to choose a smaller temp.
                    // Example: (u8)(row*32) passed to a u8 param should spill as u8, not u16.
                    CType spillAs = null;
                    if (paramTypes != null && i < paramTypes.Length)
                    {
                        CType pt = paramTypes[i];
                        if (IsU8Like(pt)) spillAs = CType.UInt8;
                    }
                    if (spillAs == null) spillAs = InferType(ctx, a);
                    if (!IsAggregateType(spillAs) && SizeOfType(spillAs) <= 2)
                        a = SpillToTemp(ctx, a, spillAs, prefix);
                }
                newArgs[i] = a;
            }

            // Lint: const discard for argument passing
            // Warn when passing a const-qualified pointer (or const array decay) to a non-const pointer parameter.
            // Suppress when the argument has an explicit cast, or inside __unsafe.
            if (!ctx.InUnsafe && paramTypes != null)
            {
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    CType pt = paramTypes[i];
                    if (pt == null) continue;
                    if (!IsPointerToNonConst(pt)) continue;
                    if (HasTopLevelCast(newArgs[i])) continue;

                    CType at = InferType(ctx, newArgs[i]);
                    if (at != null && (IsPointerToConst(at) || IsArrayOfConst(at)))
                    {
                        string fn = __fname ?? "<call>";
                        WarnConstDiscard(expr, $"argument {i} to '{fn}'");
                        break; // keep it quiet: one warning per call is enough
                    }
                }
            }

            if (paramTypes != null)
            {
                string fn = __fname ?? "<call>";
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    FilePosition argPos = newArgs[i].Source;
                    if (string.IsNullOrEmpty(argPos.Filename) || argPos.Filename == "<unknown>")
                        argPos = expr.Source;
                    WarnImplicitNarrowingIfNeeded(ctx, newArgs[i], InferType(ctx, newArgs[i]), paramTypes[i], argPos,
                        "argument " + i + " to '" + fn + "'");
                }
            }

            // Lint: __enum_strict argument passing.
            if (paramTypes != null)
            {
                string fn = __fname ?? "<call>";
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    CType pt = paramTypes[i];
                    if (pt == null) continue;

                    CType at = InferType(ctx, newArgs[i]);
                    if (at == null) continue;

                    if (HasTopLevelCast(newArgs[i])) continue;

                    if (IsStrictEnum(pt))
                    {
                        if (!IsSameEnum(pt, at))
                        {
                            Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
                                "argument {0} to '{1}' expects __enum_strict {2}, got {3}; cast explicitly to silence",
                                i, fn, pt.Show(), at.Show());
                        }
                    }
                    else if (IsPlainInteger(pt) && IsStrictEnum(at))
                    {
                        Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
                            "argument {0} to '{1}' passes __enum_strict {2} to integer parameter {3}; cast explicitly to silence",
                            i, fn, at.Show(), pt.Show());
                    }
                }
            }

            if (!ctx.InUnsafe)
                RestrictAliasLint(ctx, newFunc, newArgs, expr.Source);

            // __slice(ptr,len) helper: represent a "slice" (ptr+len) in the IR.
            // Semantics: evaluate both arguments, result type is the pointer/array (same as arg0).
            // Bounds checks are injected only with -Zcheck (see Program.CheckSliceBounds).
            // Note: keep the intrinsic name conservative to avoid collisions ("slice" is allowed only
            // when there is no declared function named "slice").
            if (newFunc.Match(Tag.Name, out string __sfname))
            {
                bool isSliceIntrinsic = (__sfname == "__slice") || (__sfname == "slice" && ctx.FindFuncParams("slice") == null);
                if (isSliceIntrinsic)
                {
                    if (newArgs.Length != 2)
                    {
                        Program.Error(expr.Source, ErrorCode.ParseError, "{0}: expected 2 arguments: __slice(ptr,len)", __sfname);
                    }
                    else
                    {
                        CType pty = InferType(ctx, newArgs[0]);
                        if (pty == null || !(pty.IsPointer || pty.IsArray))
                            Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: first argument must be a pointer or array, got: {1}", __sfname, (pty == null ? "<unknown>" : pty.Show()));

                        CType lty = InferType(ctx, newArgs[1]);
                        if (lty == null || !lty.IsInteger)
                            Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: second argument must be an integer length, got: {1}", __sfname, (lty == null ? "<unknown>" : lty.Show()));
                    }

                    return Expr.Make(Tag.Slice, newArgs[0], newArgs[1]).WithSource(expr.Source);
                }
            }

            return MakeCall(newFunc, newArgs).WithSource(expr.Source);
        }
__copy32

Copie exactement 32 octets de src vers dst, sans argument de longueur. Utilisez cet appel pour un enregistrement fixe de 32 octets ou deux motifs de tuile contigus de 16 octets en mémoire accessible au processeur. La copie avance octet par octet ; ce n’est ni une lecture unique sur 32 bits ni un transfert avec changement de banque.

Syntaxe

c
void __copy32(void* dst, const void* src);

Paramètres

dst

Pointeur vers le premier octet accessible en écriture de la destination. Prévoyez une zone contiguë assez grande pour toute l’opération ; aucun tampon n’est alloué automatiquement.

src

Pointeur vers le premier octet lisible de la source. Toute la zone doit rester accessible et inchangée pendant la copie.

Valeur renvoyée

Aucune (void).

Conditions et limites

Utilisez des zones source et destination sans chevauchement. Ces routines copient vers les adresses croissantes et n’offrent pas la sécurité de memmove en cas de recouvrement ; écraser une partie de la source encore non lue peut altérer la suite de la copie.

L’opération se termine avant de rendre la main. Elle ne change pas de banque ROM/RAM et ne limite pas automatiquement la zone aux bornes d’un tampon. Maintenez le mappage stable, utilisez des zones valides et coordonnez l’accès avec les interruptions utilisant les mêmes tampons.

Sur GB, il s’agit d’accès mémoire ordinaires du processeur. Ils n’attendent pas un créneau sûr pour la VRAM/OAM, ne lancent pas de DMA et ne sélectionnent pas les banques VRAM/WRAM du CGB. Utilisez les API de transfert graphique dédiées à ces tâches.

Exemple d’utilisation

c
memory_reset();
__copy32(memory_destination+1,memory_source+1);
memory_copy_32=memory_check(32,0);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple utilise __copy32 pour copier 32 octets suivant un motif connu à partir du décalage 1. memory_check compare les tableaux source et destination en entier ; la ligne COPY32 doit afficher 032. Le premier octet sert de garde et les octets suivant le bloc doivent également rester inchangés.

Le programme complet initialise deux tampons de 264 octets avant chaque opération, puis vérifie tous les octets de la source, de la destination et des zones de garde. L’écran attendu affiche COPY WORD 257, FILL WORD 257, COPY BYTE 255, FILL BYTE 255, COPY16 016, COPY32 032, ZERO CALLS 002 et FAILED 000. WORD/BYTE désignent la largeur du compteur, pas la taille des éléments copiés. Ces nombres proviennent des vérifications de l’exemple ; ces fonctions void ne renvoient aucune valeur.

Les captures montrent du texte noir sur blanc sur DMG, bleu sur blanc sur CGB et bleu sur noir sur FC. L’exemple vérifie des opérations en RAM ; le texte en présente les résultats, sans illustrer une primitive de dessin. Des contrôles distincts sur émulateur couvrent 88 cas par plateforme : longueurs constantes ou déterminées à l’exécution de 0, 1, 16, 17, 32, 33, 64, 65 et 255 octets, plus 256 et 257 pour les compteurs sur 16 bits. Aucun test sur matériel réel n’a été effectué.

__copy32: DMG
DMG
__copy32: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\memory_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\memory_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/rpg.h:178

kitaqgb/kitaqgb/CodeGenerator.cs:8247

csharp
if (funcName == "__copy16" || funcName == "__copy32")
            {
                if (args.Length != 2) Error(expr, funcName + "(dst, src) expects 2 arguments");

                int copyLen = (funcName == "__copy16") ? 16 : 32;
                CompileIntoHL(args[0]); EmitAsm("PUSH_HL"); // dst
                CompileIntoHL(args[1]); EmitAsm("PUSH_HL"); // src
                EmitAsm("LD_BC_IMM", new AsmOperand(copyLen, AddressMode.Immediate16));
                EmitAsm("POP_DE"); // src
                EmitAsm("POP_HL"); // dst
                EmitRamMemcpyLoop(funcName.TrimStart('_'));
                return;
            }

void EmitRamMemcpyLoop(string labelPrefix)
    {
        AsmOperand loop = MakeUniqueLabel(labelPrefix + "_loop");
        AsmOperand done = MakeUniqueLabel(labelPrefix + "_done");

        EmitLabel(loop);
        EmitAsm("LD_A_B");
        EmitAsm("OR_C");
        EmitAsm("JR_Z", done);
        EmitAsm("LD_A_DE");
        EmitAsm("LDI_HL_A");
        EmitAsm("INC_DE");
        EmitAsm("DEC_BC");
        EmitAsm("JR", loop);
        EmitLabel(done);
    }

// kitaqgb/kitaqgb/Lowerer.cs:2145
if (expr.MatchAny(Tag.Call, out callFunc, out callArgs))
        {
            string knownCallName;
            if (callFunc != null && callFunc.Match(Tag.Name, out knownCallName) &&
                knownCallName == "__cgb_is_cgb" &&
                callArgs != null && callArgs.Length == 0 &&
                // A resolved target configuration can replace the zero-argument CGB query with a literal.
                Program.TryGetKnownCgbRuntimeValue(out int knownCgbValue))
            {
                return Expr.Make(Tag.Integer, knownCgbValue).WithSource(expr.Source);
            }

            Expr newFunc = LowerExpr(ctx, callFunc, false, prefix);

            // Optimization (post-A3): when we must spill a non-atomic argument into a temp,
            // prefer a u8 temp if the *parameter type* is u8-like.
            // This preserves semantics (implicit truncation to u8) while avoiding needless
            // u16 temporaries created by integer promotions.
            CType[] paramTypes = null;
            string __fname = null;
            if (newFunc.Match(Tag.Name, out __fname))
            {
                paramTypes = ctx.FindFuncParams(__fname);
                if (paramTypes == null)
                {
                    CType fnTypeFromName;
                    if (TryGetCallableFunctionType(ctx.FindTypeOfName(__fname), out fnTypeFromName))
                        paramTypes = fnTypeFromName.ParamTypes;
                }
            }
            else
            {
                CType fnTypeFromExpr;
                if (TryGetCallableFunctionType(InferType(ctx, newFunc), out fnTypeFromExpr))
                    paramTypes = fnTypeFromExpr.ParamTypes;
            }

            Expr[] newArgs = new Expr[callArgs.Length];
            for (int i = 0; i < callArgs.Length; i++)
            {
                Expr a = LowerExpr(ctx, callArgs[i], allowExtract, prefix);
                bool preserveConstantArgIntrinsic = false;
                if (a.MatchAny(Tag.Call, out Expr argFunc, out Expr[] _argCallArgs) &&
                    argFunc.Match(Tag.Name, out string argFuncName))
                {
                    preserveConstantArgIntrinsic = (argFuncName == "__bankof" || argFuncName == "__cgb_is_cgb");
                }
                // Keep evaluable constants and special intrinsic/bank operands visible to code generation instead of spilling them.
                bool preserveCompileTimeConstantArg = ctx.Global.TryEvalConstInt(a, out _);
                bool preserveFarcallBankArg = (__fname == "__farcall" && i == 0);

                if (allowExtract && !preserveFarcallBankArg && !preserveConstantArgIntrinsic && !preserveCompileTimeConstantArg && !IsAtom(a))
                {
                    // If we know the callee's parameter type, use it to choose a smaller temp.
                    // Example: (u8)(row*32) passed to a u8 param should spill as u8, not u16.
                    CType spillAs = null;
                    if (paramTypes != null && i < paramTypes.Length)
                    {
                        CType pt = paramTypes[i];
                        if (IsU8Like(pt)) spillAs = CType.UInt8;
                    }
                    if (spillAs == null) spillAs = InferType(ctx, a);
                    if (!IsAggregateType(spillAs) && SizeOfType(spillAs) <= 2)
                        a = SpillToTemp(ctx, a, spillAs, prefix);
                }
                newArgs[i] = a;
            }

            // Lint: const discard for argument passing
            // Warn when passing a const-qualified pointer (or const array decay) to a non-const pointer parameter.
            // Suppress when the argument has an explicit cast, or inside __unsafe.
            if (!ctx.InUnsafe && paramTypes != null)
            {
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    CType pt = paramTypes[i];
                    if (pt == null) continue;
                    if (!IsPointerToNonConst(pt)) continue;
                    if (HasTopLevelCast(newArgs[i])) continue;

                    CType at = InferType(ctx, newArgs[i]);
                    if (at != null && (IsPointerToConst(at) || IsArrayOfConst(at)))
                    {
                        string fn = __fname ?? "<call>";
                        WarnConstDiscard(expr, $"argument {i} to '{fn}'");
                        break; // keep it quiet: one warning per call is enough
                    }
                }
            }

            if (paramTypes != null)
            {
                string fn = __fname ?? "<call>";
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    FilePosition argPos = newArgs[i].Source;
                    if (string.IsNullOrEmpty(argPos.Filename) || argPos.Filename == "<unknown>")
                        argPos = expr.Source;
                    WarnImplicitNarrowingIfNeeded(ctx, newArgs[i], InferType(ctx, newArgs[i]), paramTypes[i], argPos,
                        "argument " + i + " to '" + fn + "'");
                }
            }

            // Lint: __enum_strict argument passing.
            if (paramTypes != null)
            {
                string fn = __fname ?? "<call>";
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    CType pt = paramTypes[i];
                    if (pt == null) continue;

                    CType at = InferType(ctx, newArgs[i]);
                    if (at == null) continue;

                    if (HasTopLevelCast(newArgs[i])) continue;

                    if (IsStrictEnum(pt))
                    {
                        if (!IsSameEnum(pt, at))
                        {
                            Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
                                "argument {0} to '{1}' expects __enum_strict {2}, got {3}; cast explicitly to silence",
                                i, fn, pt.Show(), at.Show());
                        }
                    }
                    else if (IsPlainInteger(pt) && IsStrictEnum(at))
                    {
                        Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
                            "argument {0} to '{1}' passes __enum_strict {2} to integer parameter {3}; cast explicitly to silence",
                            i, fn, at.Show(), pt.Show());
                    }
                }
            }

            if (!ctx.InUnsafe)
                RestrictAliasLint(ctx, newFunc, newArgs, expr.Source);

            // __slice(ptr,len) helper: represent a "slice" (ptr+len) in the IR.
            // Semantics: evaluate both arguments, result type is the pointer/array (same as arg0).
            // Bounds checks are injected only with -Zcheck (see Program.CheckSliceBounds).
            // Note: keep the intrinsic name conservative to avoid collisions ("slice" is allowed only
            // when there is no declared function named "slice").
            if (newFunc.Match(Tag.Name, out string __sfname))
            {
                bool isSliceIntrinsic = (__sfname == "__slice") || (__sfname == "slice" && ctx.FindFuncParams("slice") == null);
                if (isSliceIntrinsic)
                {
                    if (newArgs.Length != 2)
                    {
                        Program.Error(expr.Source, ErrorCode.ParseError, "{0}: expected 2 arguments: __slice(ptr,len)", __sfname);
                    }
                    else
                    {
                        CType pty = InferType(ctx, newArgs[0]);
                        if (pty == null || !(pty.IsPointer || pty.IsArray))
                            Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: first argument must be a pointer or array, got: {1}", __sfname, (pty == null ? "<unknown>" : pty.Show()));

                        CType lty = InferType(ctx, newArgs[1]);
                        if (lty == null || !lty.IsInteger)
                            Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: second argument must be an integer length, got: {1}", __sfname, (lty == null ? "<unknown>" : lty.Show()));
                    }

                    return Expr.Make(Tag.Slice, newArgs[0], newArgs[1]).WithSource(expr.Source);
                }
            }

            return MakeCall(newFunc, newArgs).WithSource(expr.Source);
        }
__far_memcpy

Sélectionne temporairement une banque ROM, copie len octets de src vers dst par adresses croissantes, puis rétablit la banque mémorisée par le compilateur. Sur cette plateforme, __farmemcpy et __far_memcpy utilisent la même implémentation.

Syntaxe

c
void __far_memcpy(void* dst, u8 bank, const void* src, u16 len);

Paramètres

dst

Destination en mémoire accessible en écriture, assez grande pour tous les octets copiés. Hors transfert de zéro octet, les adresses nulles ou invalides ne sont pas vérifiées.

bank

Sélecteur de banque sur 8 bits pour la ROM source. Utilisez une banque compatible avec la cartouche et l’organisation du compilateur ; ce paramètre est distinct de l’adresse source.

src

Pointeur proche vers des octets lisibles dans la banque ROM choisie. Il ne contient pas de numéro de banque.

len

Nombre d’octets, de 0 à 65535. Avec zéro, aucune mémoire n’est copiée, mais la sélection et le rétablissement de la banque ont bien lieu.

Valeur renvoyée

Aucune (void).

Conditions et limites

Toute la source doit être lisible dans le mappage ROM prévu, et toute la destination accessible en écriture. La copie ne passe pas automatiquement à la banque suivante, ne gère pas les chevauchements comme memmove et ne synchronise pas les écritures VRAM. Utilisez les API graphiques pour la VRAM.

Les exemples placent le code appelant dans la banque fixe 0 afin qu’un changement de banque source ne le rende pas inaccessible. Les transferts sont synchrones. N’interrompez pas un transfert par une autre copie changeant de banque ; cette fonction ne garantit pas à elle seule la sûreté des interruptions de toute l’application.

La routine GB rétablit la banque mémorisée par le compilateur. Maintenez cet état avec __bankswitch : une écriture directe dans le mapper ne le met pas à jour. L’emplacement HRAM de sauvegarde est partagé ; des transferts imbriqués qui l’écrasent ne sont pas sûrs.

Exemple d’utilisation

c
__far_memcpy(buffer,2,asset_shapes,32); // Same backend as __farmemcpy.
expect(*asset_bank_marker==0xA1,29);
for(i=0;i<32;i++) expect(buffer[(__safe_index u8)i]==expected_shape_byte(i),32);
buffer[32]=0x55;
__far_memcpy(buffer+32,2,asset_shapes,0); // Zero count writes no bytes and restores the bank.
expect(buffer[32]==0x55 && *asset_bank_marker==0xA1,30);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Copie deux motifs, soit 32 octets, depuis la banque 2 alors que la banque 1 est sélectionnée au départ. Le programme compare tous les octets, vérifie le retour à la banque 1 et la conservation du témoin 0x55 lors d’une copie de longueur nulle.

Image attendue : une rangée rouge part de (16,32) et occupe 72×8 pixels. Elle répète trois fois un triangle de 8×8, un carré de 8×8 au contour d’un pixel et une tuile dont les quatre colonnes de gauche sont pleines. Le triangle s’élargit d’un pixel en haut à gauche à huit sur sa ligne inférieure.

Sur GB, le triangle et le contour bleus en (16,64) montrent le résultat de __farmemcpy ; la paire verte en (16,96), celui de __far_memcpy. Chaque paire occupe 16×8 pixels. Le CGB utilise ces couleurs ; le DMG affiche les mêmes formes en noir sur blanc.

L’état attendu est FAILED CHECKS 000 et FIRST FAILURE 00. La vérification exécute 300 images puis compare texte, formes et couleurs aux résultats attendus. Il s’agit d’une preuve sur émulateur, pas d’un essai sur matériel physique.

__far_memcpy: DMG
DMG
__far_memcpy: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\asset_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\asset_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/rpg.h:148

__fill_tilemap

__fill_tilemap est un alias du compilateur pour __vram_memset. Le compte reste en octets, avec le même transfert immédiat par CPU et les mêmes règles d’accès au LCD.

Remplit immédiatement len octets consécutifs à partir de dst avec l’octet value. Répète un octet, pas un motif graphique complet. Dans une carte, cela répète un indice de tuile ; dans les motifs, cela modifie les plans de bits des pixels.

Syntaxe

c
void __fill_tilemap(u16 dst, u8 value, u16 len);

Paramètres

dst

Adresse du premier octet de destination, normalement dans la VRAM 0x8000–0x9FFF. Toute la plage doit être valide. Le CGB utilise la banque courante ; dans la carte, la banque 0 contient les indices et la banque 1 les attributs. Ces routines ne choisissent pas la carte selon LCDC, ne sautent pas de ligne et n’allouent pas de VRAM.

value

Un octet de 0 à 255 répété. Son effet visible dépend du contenu de destination : indices de tuiles, plans de pixels ou autre structure d’octets.

len

Nombre d’octets consécutifs, de 0 à 65535, pas de tuiles. Gardez toute la plage dans une source valide et une destination modifiable ; l’API ne vérifie pas les limites et n’empêche pas le bouclage d’adresse sur 16 bits.

Valeur renvoyée

Aucune (void).

Conditions et limites

Indiquez l’adresse initiale et le nombre d’octets. Cet alias ne choisit pas la carte de fond et ne la remplit pas automatiquement en entier. Pour une carte complète de 32 × 32 indices, sélectionnez la banque VRAM 0, puis fournissez la base et 1024 octets.

LCD actif, chaque écriture attend le mode STAT 0 ou 1 sous DI, puis exécute EI. LCD éteint, un simple parcours CPU est utilisé. L’appel peut durer plusieurs images, ne préserve pas les interruptions désactivées à l’entrée et ne configure aucun registre DMA. Une longueur nulle ne transfère aucune donnée.

Exemple d’utilisation

c
__fill_tilemap(0x9922,1,length_base+length_extra); // Explicit address and byte count, not a whole-map clear.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple utilise __fill_tilemap pour remplir trois octets de carte à partir de la tuile (2,9), pixel (16,72), avec l’indice 1. On attend une barre pleine de 24 × 8, entourée de cases vides. Le compte provient de variables ou du résultat d’une fonction ; les trois octets doivent bien recevoir la valeur 1.

Les graphismes originaux fournis utilisent la tuile 1 pour un carré plein de 8 × 8, la 2 pour ses quatre colonnes gauches et la 3 pour un triangle aligné à gauche, élargi d’un pixel par ligne de 1 à 8. Le DMG utilise le noir sur blanc ; le CGB distingue les opérations par des couleurs en gardant le texte noir.

Le carré au pixel (16,88) doit subsister après les appels sûrs et sans attente de longueur nulle. LCD actif, la copie et le remplissage sûrs ajoutent des carrés en (16,96) et (32,96). FAILED CHECKS doit afficher 000. Les tests d’émulateur comparent les pixels et le compteur. Le CGB utilise ici la banque 0 ; les autres banques et le matériel physique ne sont pas testés.

Sur CGB, les lignes de MEMCPY à FILL UNSAFE répètent la séquence rouge, bleu, vert. Le carré de ZERO COUNT est rouge. Dans LCD ON, celui de gauche est bleu et celui de droite vert. Le texte reste noir. Les tests vérifient ces couleurs sur chaque pixel dessiné.

__fill_tilemap: DMG
DMG
__fill_tilemap: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\vram_memory_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_memory_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/rpg.h:157

__getbgmapbase

Lit le bit 3 de LCDC pour choisir l’adresse de base de la carte de tuiles du fond.

Syntaxe

c
u16 __getbgmapbase();

Valeur renvoyée

Renvoie $9800 ou $9C00, la base de la carte de tuiles et non l’adresse des graphismes.

Exemple d’utilisation

c
M_LCDC=0;result[0]=__getbgmapbase();M_LCDC=8;result[1]=__getbgmapbase();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Découvrez sélection de carte, défilement, imbrication et attentes de ligne. Les bases valent 38912/39936 ; les jetons imbriqués, 0/1 ; les LY cibles observés, 80/144. Vérifiez aussi que les attentes reviennent avec le LCD éteint.

__getbgmapbase: DMG
DMG
__getbgmapbase: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_display.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_display.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implémentation et source

kitaqgb/lib/rpg.h:140

kitaqgb/kitaqgb/CodeGenerator.cs:10619

csharp
if (funcName == "__getbgmapbase")
{
    if (args.Length != 0) Error(expr, "__getbgmapbase requires 0 arguments");

    AsmOperand gb_base0 = MakeUniqueLabel("getbgbase_base0");
    AsmOperand gb_done  = MakeUniqueLabel("getbgbase_done");

    EmitAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem)); // LCDC
    EmitAsm("AND_IMM", new AsmOperand(0x08, AddressMode.Immediate)); // bit3 (BG tile map display select)
    EmitAsm("JR_Z", gb_base0);
    EmitAsm("LD_HL_IMM", new AsmOperand(0x9C00, AddressMode.Immediate16));
    EmitAsm("JR", gb_done);
    EmitLabel(gb_base0);
    EmitAsm("LD_HL_IMM", new AsmOperand(0x9800, AddressMode.Immediate16));
    EmitLabel(gb_done);
    LastCallReturnsHL = true;
    return;
}
__manhattan

Additionne les différences absolues en X et en Y entre deux points.

Syntaxe

c
u8 __manhattan(u8 x1, u8 y1, u8 x2, u8 y2);

Paramètres

x1

Coordonnée X du premier point, de 0 à 255.

y1

Coordonnée Y du premier point, de 0 à 255.

x2

Coordonnée X du second point, de 0 à 255.

y2

Coordonnée Y du second point, de 0 à 255.

Valeur renvoyée

Renvoie l’octet de poids faible de |x1−x2|+|y1−y2| ; les distances supérieures à 255 rebouclent.

Exemple d’utilisation

c
result[5]=__manhattan(2,3,9,11);result[6]=__manhattan(0,0,255,255);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Testez intérieur, limites, point extérieur et largeur nulle : INSIDE=1, RIGHT EDGE=0, LEFT EDGE=0, HIGH X=1. Vérifiez la distance 15, la distance rebouclée 254 et les indices de tableau 64/65280.

__manhattan: DMG
DMG
__manhattan: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_coordinates.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_coordinates.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implémentation et source

kitaqgb/lib/rpg.h:183

kitaqgb/kitaqgb/CodeGenerator.cs:8516

csharp
if (funcName == "__manhattan")
            {
                if (args.Length != 4) Error(expr, "__manhattan(x1, y1, x2, y2) expects 4 arguments");

                Expr[] foldedManhattanArgs = args.Select(FoldConstants).ToArray();
                if (foldedManhattanArgs[0].Match(Tag.Integer, out int constX1) &&
                    foldedManhattanArgs[1].Match(Tag.Integer, out int constY1) &&
                    foldedManhattanArgs[2].Match(Tag.Integer, out int constX2) &&
                    foldedManhattanArgs[3].Match(Tag.Integer, out int constY2))
                {
                    int dx = Math.Abs((constX1 & 0xFF) - (constX2 & 0xFF));
                    int dy = Math.Abs((constY1 & 0xFF) - (constY2 & 0xFF));
                    EmitAsm("LD_A_IMM", new AsmOperand((dx + dy) & 0xFF, AddressMode.Immediate));
                    return;
                }

                EmitPushExprAsWord(args[0]); // x1
                EmitPushExprAsWord(args[1]); // y1
                EmitPushExprAsWord(args[2]); // x2
                EmitPushExprAsWord(args[3]); // y2

                AsmOperand dxGe = MakeUniqueLabel("manhattan_dx_ge");
                AsmOperand dxDone = MakeUniqueLabel("manhattan_dx_done");
                AsmOperand dyGe = MakeUniqueLabel("manhattan_dy_ge");
                AsmOperand dyDone = MakeUniqueLabel("manhattan_dy_done");

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(6, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_B_A"); // x1
                EmitAsm("LD_HL_SP_IMM", new AsmOperand(2, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_C_A"); // x2
                EmitAsm("LD_A_B");
                EmitAsm("CP_C");
                EmitAsm("JR_NC", dxGe);
                EmitAsm("LD_A_C");
                EmitAsm("SUB_B");
                EmitAsm("JR", dxDone);
                EmitLabel(dxGe);
                EmitAsm("LD_A_B");
                EmitAsm("SUB_C");
                EmitLabel(dxDone);
                EmitAsm("LD_B_A"); // dx

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_C_A"); // y1
                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_D_A"); // y2
                EmitAsm("LD_A_C");
                EmitAsm("CP_D");
                EmitAsm("JR_NC", dyGe);
                EmitAsm("LD_A_D");
                EmitAsm("SUB_C");
                EmitAsm("JR", dyDone);
                EmitLabel(dyGe);
                EmitAsm("LD_A_C");
                EmitAsm("SUB_D");
                EmitLabel(dyDone);
                EmitAsm("ADD_B");
                EmitAsm("ADD_SP_IMM", new AsmOperand(8, AddressMode.Relative));
                return;
            }
__map_index

Convertit les coordonnées d’une case en indice de tableau rangé par lignes.

Syntaxe

c
u16 __map_index(u8 x, u8 y, u8 width);

Paramètres

x

Colonne de 0 à 255, normalement inférieure à width.

y

Ligne de 0 à 255.

width

Nombre de cases par ligne, de 0 à 255.

Valeur renvoyée

Renvoie y*width+x sous forme de u16 ; ne valide pas les coordonnées.

Exemple d’utilisation

c
result[7]=__map_index(4,3,20);result[8]=__map_index(255,255,255);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Testez intérieur, limites, point extérieur et largeur nulle : INSIDE=1, RIGHT EDGE=0, LEFT EDGE=0, HIGH X=1. Vérifiez la distance 15, la distance rebouclée 254 et les indices de tableau 64/65280.

__map_index: DMG
DMG
__map_index: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_coordinates.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_coordinates.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implémentation et source

kitaqgb/lib/rpg.h:181

kitaqgb/kitaqgb/CodeGenerator.cs:8365

csharp
if (funcName == "__map_index")
            {
                if (args.Length != 3) Error(expr, "__map_index(x, y, width) expects 3 arguments");

                Expr xExpr = FoldConstants(args[0]);
                Expr yExpr = FoldConstants(args[1]);
                Expr widthExpr = FoldConstants(args[2]);
                // Apply the width parameter's byte conversion before choosing a shift specialization.
                if (widthExpr.Match(Tag.Integer, out int byteWidth))
                    widthExpr = Expr.Make(Tag.Integer, byteWidth & 0xFF).WithSource(widthExpr.Source);
                if (xExpr.Match(Tag.Integer, out int fullXConst) &&
                    yExpr.Match(Tag.Integer, out int fullYConst) &&
                    widthExpr.Match(Tag.Integer, out int fullWidthConst))
                {
                    // Apply the byte argument widths before calculating the word-sized constant index.
                    int fullIndexConst = (((fullYConst & 0xFF) * (fullWidthConst & 0xFF)) + (fullXConst & 0xFF)) & 0xFFFF;
                    EmitAsm("LD_HL_IMM", new AsmOperand(fullIndexConst, AddressMode.Immediate16));
                    LastCallReturnsHL = true;
                    return;
                }

                if (widthExpr.Match(Tag.Integer, out int widthConst) && widthConst > 0 && IsPow2(widthConst))
                {
                    CompileIntoA(yExpr);
                    EmitAsm("LD_L_A");
                    EmitAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate));
                    int sh = Log2Pow2(widthConst);
                    for (int i = 0; i < sh; i++) EmitAsm("ADD_HL_HL");

                    if (xExpr.Match(Tag.Integer, out int xConst) && xConst == 0)
                    {
                        LastCallReturnsHL = true;
                        return;
                    }

                    CompileIntoA(xExpr);
                    EmitAsm("LD_E_A");
                    EmitAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate));
                    EmitAsm("ADD_HL_DE");
                    LastCallReturnsHL = true;
                    return;
                }

                CompileIntoA(args[0]); EmitPushAAsWord(); // x
                CompileIntoA(args[1]); EmitPushAAsWord(); // y
                CompileIntoA(args[2]); EmitPushAAsWord(); // width

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_C_A"); // width

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(2, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_E_A"); // y

                EmitMul8x8ToHL_EC("__map_index");

                // Loading the saved X argument uses HL, so preserve the multiplication result first.
                EmitAsm("PUSH_HL");
                EmitAsm("LD_HL_SP_IMM", new AsmOperand(6, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_E_A");
                EmitAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate));
                EmitAsm("POP_HL");
                EmitAsm("ADD_HL_DE");
                EmitAsm("ADD_SP_IMM", new AsmOperand(6, AddressMode.Relative));
                LastCallReturnsHL = true;
                return;
            }
__memcpy

Copie de src vers dst le nombre d’octets indiqué. Le compteur sur 16 bits permet de dépasser 255 octets, par exemple pour copier une table entière en RAM. La lecture et l’écriture suivent l’ordre croissant des adresses.

Syntaxe

c
void __memcpy(void* dst, const void* src, u16 count);

Paramètres

dst

Pointeur vers le premier octet accessible en écriture de la destination. Prévoyez une zone contiguë assez grande pour toute l’opération ; aucun tampon n’est alloué automatiquement.

src

Pointeur vers le premier octet lisible de la source. Toute la zone doit rester accessible et inchangée pendant la copie.

count

Nombre d’octets en u16 (0–65535), et non nombre d’éléments. Le maximum du type ne garantit pas que la machine dispose d’un tampon valide de cette taille. Toute la zone doit tenir dans le mappage mémoire sélectionné.

Valeur renvoyée

Aucune (void).

Conditions et limites

Un compteur nul ne transfère aucun octet. Les effets de bord des expressions passées en argument ont néanmoins lieu : l’exemple fournit deux fonctions renvoyant des pointeurs et constate leurs deux appels malgré une longueur nulle.

Utilisez des zones source et destination sans chevauchement. Ces routines copient vers les adresses croissantes et n’offrent pas la sécurité de memmove en cas de recouvrement ; écraser une partie de la source encore non lue peut altérer la suite de la copie.

L’opération se termine avant de rendre la main. Elle ne change pas de banque ROM/RAM et ne limite pas automatiquement la zone aux bornes d’un tampon. Maintenez le mappage stable, utilisez des zones valides et coordonnez l’accès avec les interruptions utilisant les mêmes tampons.

Sur GB, il s’agit d’accès mémoire ordinaires du processeur. Ils n’attendent pas un créneau sûr pour la VRAM/OAM, ne lancent pas de DMA et ne sélectionnent pas les banques VRAM/WRAM du CGB. Utilisez les API de transfert graphique dédiées à ces tâches.

Exemple d’utilisation

c
memory_reset();
__memcpy(memory_destination+1,memory_source+1,257);
memory_copy_word=memory_check(257,0); // 257 copied bytes; guards unchanged.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple utilise __memcpy pour copier 257 octets suivant un motif connu à partir du décalage 1. memory_check compare les tableaux source et destination en entier ; la ligne COPY WORD doit afficher 257. Le premier octet sert de garde et les octets suivant le bloc doivent également rester inchangés.

Le programme complet initialise deux tampons de 264 octets avant chaque opération, puis vérifie tous les octets de la source, de la destination et des zones de garde. L’écran attendu affiche COPY WORD 257, FILL WORD 257, COPY BYTE 255, FILL BYTE 255, COPY16 016, COPY32 032, ZERO CALLS 002 et FAILED 000. WORD/BYTE désignent la largeur du compteur, pas la taille des éléments copiés. Ces nombres proviennent des vérifications de l’exemple ; ces fonctions void ne renvoient aucune valeur.

Les captures montrent du texte noir sur blanc sur DMG, bleu sur blanc sur CGB et bleu sur noir sur FC. L’exemple vérifie des opérations en RAM ; le texte en présente les résultats, sans illustrer une primitive de dessin. Des contrôles distincts sur émulateur couvrent 88 cas par plateforme : longueurs constantes ou déterminées à l’exécution de 0, 1, 16, 17, 32, 33, 64, 65 et 255 octets, plus 256 et 257 pour les compteurs sur 16 bits. Aucun test sur matériel réel n’a été effectué.

__memcpy: DMG
DMG
__memcpy: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\memory_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\memory_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/rpg.h:142

kitaqgb/kitaqgb/CodeGenerator.cs:9084

csharp
if (funcName == "__memcpy" || funcName == "__memcpy_small")
            {
                // Select byte or word count semantics before optional fixed-array bounds diagnostics.
                bool useU8Count = (funcName == "__memcpy_small");
                if (args.Length != 3) { Error(expr, "__memcpy requires 3 arguments"); return; }

                bool doCheck = Program.CheckMemCopy && !InUnsafe;
                bool hasDst = false, hasSrc = false;
                int dstBytes = 0, srcBytes = 0;
                string dstName = null, srcName = null;
                if (doCheck)
                {
                    hasDst = TryGetFixedArrayByteLength(args[0], out dstBytes, out dstName);
                    hasSrc = TryGetFixedArrayByteLength(args[1], out srcBytes, out srcName);
                }

                bool isConstCountAny = args[2].Match(Tag.Integer, out int constCountAny);
                int normalizedConstCount = constCountAny;
                if (isConstCountAny && useU8Count)
                    normalizedConstCount &= 0xFF;

                if (doCheck && isConstCountAny)
                {
                    if (hasDst && normalizedConstCount >= 0 && normalizedConstCount > dstBytes)
                    {
                        Program.Warning(args[2].Source, "__memcpy count {0} exceeds fixed dst array '{1}' size {2} bytes (-Zcheck)", normalizedConstCount, dstName, dstBytes);
                        EmitAsm("JP", EnsureCheckTrap());
                        return;
                    }
                    if (hasSrc && normalizedConstCount >= 0 && normalizedConstCount > srcBytes)
                    {
                        Program.Warning(args[2].Source, "__memcpy count {0} exceeds fixed src array '{1}' size {2} bytes (-Zcheck)", normalizedConstCount, srcName, srcBytes);
                        EmitAsm("JP", EnsureCheckTrap());
                        return;
                    }
                }

                if (isConstCountAny && normalizedConstCount >= 0 && normalizedConstCount <= 16)
                {
                    if (normalizedConstCount == 0) return;

                    CompileIntoHL(args[0]);
                    EmitAsm("PUSH_HL");
                    CompileIntoHL(args[1]);
                    EmitAsm("PUSH_HL");
                    EmitAsm("POP_DE");
                    EmitAsm("POP_HL");

                    for (int k = 0; k < normalizedConstCount; k++)
                    {
                        EmitAsm("LD_A_DE");
                        EmitAsm("LDI_HL_A");
                        EmitAsm("INC_DE");
                    }
                    return;
                }

                if (isConstCountAny && normalizedConstCount > 16 && normalizedConstCount <= 255)
                {
                    CompileIntoHL(args[0]);
                    EmitAsm("PUSH_HL");
                    CompileIntoHL(args[1]);
                    EmitAsm("PUSH_HL");
                    EmitAsm("POP_DE");
                    EmitAsm("POP_HL");

                    if (normalizedConstCount <= 64)
                    {
                        int blocks8 = normalizedConstCount / 8;
                        int rem8 = normalizedConstCount % 8;
                        if (blocks8 > 0)
                        {
                            EmitAsm("LD_B_IMM", new AsmOperand(blocks8, AddressMode.Immediate));
                            // Advance source and destination through eight-byte groups, then emit the remaining constant bytes.
                            AsmOperand loop8 = MakeUniqueLabel("memcpy8_loop");
                            EmitLabel(loop8);
                            for (int k = 0; k < 8; k++)
                            {
                                EmitAsm("LD_A_DE");
                                EmitAsm("LDI_HL_A");
                                EmitAsm("INC_DE");
                            }
                            EmitAsm("DEC_B");
                            EmitAsm("JP_NZ", loop8);
                        }
                        for (int k = 0; k < rem8; k++)
                        {
                            EmitAsm("LD_A_DE");
                            EmitAsm("LDI_HL_A");
                            EmitAsm("INC_DE");
                        }
                        return;
                    }

                    int blocks16 = normalizedConstCount / 16;
                    int rem16 = normalizedConstCount % 16;
                    if (blocks16 > 0)
                    {
                        EmitAsm("LD_B_IMM", new AsmOperand(blocks16, AddressMode.Immediate));
                        // Use sixteen-byte groups while keeping the residual copy length known at compile time.
                        AsmOperand loop16 = MakeUniqueLabel("memcpy16_loop");
                        EmitLabel(loop16);
                        for (int k = 0; k < 16; k++)
                        {
                            EmitAsm("LD_A_DE");
                            EmitAsm("LDI_HL_A");
                            EmitAsm("INC_DE");
                        }
                        EmitAsm("DEC_B");
                        EmitAsm("JP_NZ", loop16);
                    }
                    for (int k = 0; k < rem16; k++)
                    {
                        EmitAsm("LD_A_DE");
                        EmitAsm("LDI_HL_A");
                        EmitAsm("INC_DE");
                    }
                    return;
                }

                if (useU8Count)
                {
                    CompileIntoHL(args[0]);
                    EmitAsm("PUSH_HL");

                    CompileIntoHL(args[1]);
                    EmitAsm("PUSH_HL");

                    CompileIntoA(args[2]);
                    EmitAsm("LD_B_A");
                    if (doCheck)
                    {
                        if (hasDst) EmitU8LeqConstOrTrapInA(dstBytes);
                        if (hasSrc) EmitU8LeqConstOrTrapInA(srcBytes);
                    }

                    AsmOperand doneLabel = MakeUniqueLabel("memcpy_small_done");
                    AsmOperand smallLoopLabel = MakeUniqueLabel("memcpy_small_loop");
                    EmitAsm("OR_A");
                    EmitAsm("POP_DE");
                    EmitAsm("POP_HL");
                    EmitAsm("JP_Z", doneLabel);

                    EmitLabel(smallLoopLabel);
                    EmitAsm("LD_A_DE");
                    EmitAsm("LDI_HL_A");
                    EmitAsm("INC_DE");
                    EmitAsm("DEC_B");
                    EmitAsm("JP_NZ", smallLoopLabel);
                    EmitLabel(doneLabel);
                    return;
                }

                CompileIntoHL(args[0]);
                EmitAsm("PUSH_HL");

                CompileIntoHL(args[1]);
                EmitAsm("PUSH_HL");

                CompileIntoHL(args[2]);
                if (doCheck && !isConstCountAny)
                {
                    if (hasDst) EmitU16LeqConstOrTrap(dstBytes);
                    if (hasSrc) EmitU16LeqConstOrTrap(srcBytes);
                }
                EmitAsm("LD_B_H");
                EmitAsm("LD_C_L");

                EmitAsm("POP_DE");
                EmitAsm("POP_HL");

                AsmOperand loopLabel = MakeUniqueLabel("memcpy_loop");
                AsmOperand wordDoneLabel = MakeUniqueLabel("memcpy_done");
                // A zero word count must skip the first write as well as the loop.
                EmitAsm("LD_A_B");
                EmitAsm("OR_C");
                EmitAsm("JP_Z", wordDoneLabel);
                EmitLabel(loopLabel);

                EmitAsm("LD_A_DE");
                EmitAsm("LDI_HL_A");
                EmitAsm("INC_DE");

                EmitAsm("DEC_BC");
                EmitAsm("LD_A_B");
                EmitAsm("OR_C");
                EmitAsm("JP_NZ", loopLabel);
                EmitLabel(wordDoneLabel);

                return;
            }

// kitaqgb/kitaqgb/Lowerer.cs:2145
if (expr.MatchAny(Tag.Call, out callFunc, out callArgs))
        {
            string knownCallName;
            if (callFunc != null && callFunc.Match(Tag.Name, out knownCallName) &&
                knownCallName == "__cgb_is_cgb" &&
                callArgs != null && callArgs.Length == 0 &&
                // A resolved target configuration can replace the zero-argument CGB query with a literal.
                Program.TryGetKnownCgbRuntimeValue(out int knownCgbValue))
            {
                return Expr.Make(Tag.Integer, knownCgbValue).WithSource(expr.Source);
            }

            Expr newFunc = LowerExpr(ctx, callFunc, false, prefix);

            // Optimization (post-A3): when we must spill a non-atomic argument into a temp,
            // prefer a u8 temp if the *parameter type* is u8-like.
            // This preserves semantics (implicit truncation to u8) while avoiding needless
            // u16 temporaries created by integer promotions.
            CType[] paramTypes = null;
            string __fname = null;
            if (newFunc.Match(Tag.Name, out __fname))
            {
                paramTypes = ctx.FindFuncParams(__fname);
                if (paramTypes == null)
                {
                    CType fnTypeFromName;
                    if (TryGetCallableFunctionType(ctx.FindTypeOfName(__fname), out fnTypeFromName))
                        paramTypes = fnTypeFromName.ParamTypes;
                }
            }
            else
            {
                CType fnTypeFromExpr;
                if (TryGetCallableFunctionType(InferType(ctx, newFunc), out fnTypeFromExpr))
                    paramTypes = fnTypeFromExpr.ParamTypes;
            }

            Expr[] newArgs = new Expr[callArgs.Length];
            for (int i = 0; i < callArgs.Length; i++)
            {
                Expr a = LowerExpr(ctx, callArgs[i], allowExtract, prefix);
                bool preserveConstantArgIntrinsic = false;
                if (a.MatchAny(Tag.Call, out Expr argFunc, out Expr[] _argCallArgs) &&
                    argFunc.Match(Tag.Name, out string argFuncName))
                {
                    preserveConstantArgIntrinsic = (argFuncName == "__bankof" || argFuncName == "__cgb_is_cgb");
                }
                // Keep evaluable constants and special intrinsic/bank operands visible to code generation instead of spilling them.
                bool preserveCompileTimeConstantArg = ctx.Global.TryEvalConstInt(a, out _);
                bool preserveFarcallBankArg = (__fname == "__farcall" && i == 0);

                if (allowExtract && !preserveFarcallBankArg && !preserveConstantArgIntrinsic && !preserveCompileTimeConstantArg && !IsAtom(a))
                {
                    // If we know the callee's parameter type, use it to choose a smaller temp.
                    // Example: (u8)(row*32) passed to a u8 param should spill as u8, not u16.
                    CType spillAs = null;
                    if (paramTypes != null && i < paramTypes.Length)
                    {
                        CType pt = paramTypes[i];
                        if (IsU8Like(pt)) spillAs = CType.UInt8;
                    }
                    if (spillAs == null) spillAs = InferType(ctx, a);
                    if (!IsAggregateType(spillAs) && SizeOfType(spillAs) <= 2)
                        a = SpillToTemp(ctx, a, spillAs, prefix);
                }
                newArgs[i] = a;
            }

            // Lint: const discard for argument passing
            // Warn when passing a const-qualified pointer (or const array decay) to a non-const pointer parameter.
            // Suppress when the argument has an explicit cast, or inside __unsafe.
            if (!ctx.InUnsafe && paramTypes != null)
            {
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    CType pt = paramTypes[i];
                    if (pt == null) continue;
                    if (!IsPointerToNonConst(pt)) continue;
                    if (HasTopLevelCast(newArgs[i])) continue;

                    CType at = InferType(ctx, newArgs[i]);
                    if (at != null && (IsPointerToConst(at) || IsArrayOfConst(at)))
                    {
                        string fn = __fname ?? "<call>";
                        WarnConstDiscard(expr, $"argument {i} to '{fn}'");
                        break; // keep it quiet: one warning per call is enough
                    }
                }
            }

            if (paramTypes != null)
            {
                string fn = __fname ?? "<call>";
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    FilePosition argPos = newArgs[i].Source;
                    if (string.IsNullOrEmpty(argPos.Filename) || argPos.Filename == "<unknown>")
                        argPos = expr.Source;
                    WarnImplicitNarrowingIfNeeded(ctx, newArgs[i], InferType(ctx, newArgs[i]), paramTypes[i], argPos,
                        "argument " + i + " to '" + fn + "'");
                }
            }

            // Lint: __enum_strict argument passing.
            if (paramTypes != null)
            {
                string fn = __fname ?? "<call>";
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    CType pt = paramTypes[i];
                    if (pt == null) continue;

                    CType at = InferType(ctx, newArgs[i]);
                    if (at == null) continue;

                    if (HasTopLevelCast(newArgs[i])) continue;

                    if (IsStrictEnum(pt))
                    {
                        if (!IsSameEnum(pt, at))
                        {
                            Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
                                "argument {0} to '{1}' expects __enum_strict {2}, got {3}; cast explicitly to silence",
                                i, fn, pt.Show(), at.Show());
                        }
                    }
                    else if (IsPlainInteger(pt) && IsStrictEnum(at))
                    {
                        Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
                            "argument {0} to '{1}' passes __enum_strict {2} to integer parameter {3}; cast explicitly to silence",
                            i, fn, at.Show(), pt.Show());
                    }
                }
            }

            if (!ctx.InUnsafe)
                RestrictAliasLint(ctx, newFunc, newArgs, expr.Source);

            // __slice(ptr,len) helper: represent a "slice" (ptr+len) in the IR.
            // Semantics: evaluate both arguments, result type is the pointer/array (same as arg0).
            // Bounds checks are injected only with -Zcheck (see Program.CheckSliceBounds).
            // Note: keep the intrinsic name conservative to avoid collisions ("slice" is allowed only
            // when there is no declared function named "slice").
            if (newFunc.Match(Tag.Name, out string __sfname))
            {
                bool isSliceIntrinsic = (__sfname == "__slice") || (__sfname == "slice" && ctx.FindFuncParams("slice") == null);
                if (isSliceIntrinsic)
                {
                    if (newArgs.Length != 2)
                    {
                        Program.Error(expr.Source, ErrorCode.ParseError, "{0}: expected 2 arguments: __slice(ptr,len)", __sfname);
                    }
                    else
                    {
                        CType pty = InferType(ctx, newArgs[0]);
                        if (pty == null || !(pty.IsPointer || pty.IsArray))
                            Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: first argument must be a pointer or array, got: {1}", __sfname, (pty == null ? "<unknown>" : pty.Show()));

                        CType lty = InferType(ctx, newArgs[1]);
                        if (lty == null || !lty.IsInteger)
                            Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: second argument must be an integer length, got: {1}", __sfname, (lty == null ? "<unknown>" : lty.Show()));
                    }

                    return Expr.Make(Tag.Slice, newArgs[0], newArgs[1]).WithSource(expr.Source);
                }
            }

            return MakeCall(newFunc, newArgs).WithSource(expr.Source);
        }
__memcpy_small

Copie un bloc d’au plus 255 octets avec un compteur sur 8 bits. Choisissez cette forme lorsque la longueur tient dans un octet. _small désigne la largeur du compteur ; ce nom ne garantit ni une durée d’exécution ni une taille de code précises.

Syntaxe

c
void __memcpy_small(void* dst, const void* src, u8 len);

Paramètres

dst

Pointeur vers le premier octet accessible en écriture de la destination. Prévoyez une zone contiguë assez grande pour toute l’opération ; aucun tampon n’est alloué automatiquement.

src

Pointeur vers le premier octet lisible de la source. Toute la zone doit rester accessible et inchangée pendant la copie.

len

Nombre d’octets en u8 : 0–255. Ne passez pas 256 pour désigner un bloc entier de 256 octets : cette longueur ne tient pas dans le paramètre. À partir de 256 octets, utilisez la fonction correspondante sans _small.

Valeur renvoyée

Aucune (void).

Conditions et limites

Un compteur nul ne transfère aucun octet. Les effets de bord des expressions passées en argument ont néanmoins lieu : l’exemple fournit deux fonctions renvoyant des pointeurs et constate leurs deux appels malgré une longueur nulle.

Utilisez des zones source et destination sans chevauchement. Ces routines copient vers les adresses croissantes et n’offrent pas la sécurité de memmove en cas de recouvrement ; écraser une partie de la source encore non lue peut altérer la suite de la copie.

L’opération se termine avant de rendre la main. Elle ne change pas de banque ROM/RAM et ne limite pas automatiquement la zone aux bornes d’un tampon. Maintenez le mappage stable, utilisez des zones valides et coordonnez l’accès avec les interruptions utilisant les mêmes tampons.

Sur GB, il s’agit d’accès mémoire ordinaires du processeur. Ils n’attendent pas un créneau sûr pour la VRAM/OAM, ne lancent pas de DMA et ne sélectionnent pas les banques VRAM/WRAM du CGB. Utilisez les API de transfert graphique dédiées à ces tâches.

Exemple d’utilisation

c
memory_reset();
__memcpy_small(memory_destination+1,memory_source+1,255);
memory_copy_byte=memory_check(255,0); // Maximum representable byte count.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple utilise __memcpy_small pour copier 255 octets suivant un motif connu à partir du décalage 1. memory_check compare les tableaux source et destination en entier ; la ligne COPY BYTE doit afficher 255. Le premier octet sert de garde et les octets suivant le bloc doivent également rester inchangés.

Le programme complet initialise deux tampons de 264 octets avant chaque opération, puis vérifie tous les octets de la source, de la destination et des zones de garde. L’écran attendu affiche COPY WORD 257, FILL WORD 257, COPY BYTE 255, FILL BYTE 255, COPY16 016, COPY32 032, ZERO CALLS 002 et FAILED 000. WORD/BYTE désignent la largeur du compteur, pas la taille des éléments copiés. Ces nombres proviennent des vérifications de l’exemple ; ces fonctions void ne renvoient aucune valeur.

Les captures montrent du texte noir sur blanc sur DMG, bleu sur blanc sur CGB et bleu sur noir sur FC. L’exemple vérifie des opérations en RAM ; le texte en présente les résultats, sans illustrer une primitive de dessin. Des contrôles distincts sur émulateur couvrent 88 cas par plateforme : longueurs constantes ou déterminées à l’exécution de 0, 1, 16, 17, 32, 33, 64, 65 et 255 octets, plus 256 et 257 pour les compteurs sur 16 bits. Aucun test sur matériel réel n’a été effectué.

__memcpy_small: DMG
DMG
__memcpy_small: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\memory_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\memory_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/rpg.h:175

kitaqgb/kitaqgb/CodeGenerator.cs:9084

csharp
if (funcName == "__memcpy" || funcName == "__memcpy_small")
            {
                // Select byte or word count semantics before optional fixed-array bounds diagnostics.
                bool useU8Count = (funcName == "__memcpy_small");
                if (args.Length != 3) { Error(expr, "__memcpy requires 3 arguments"); return; }

                bool doCheck = Program.CheckMemCopy && !InUnsafe;
                bool hasDst = false, hasSrc = false;
                int dstBytes = 0, srcBytes = 0;
                string dstName = null, srcName = null;
                if (doCheck)
                {
                    hasDst = TryGetFixedArrayByteLength(args[0], out dstBytes, out dstName);
                    hasSrc = TryGetFixedArrayByteLength(args[1], out srcBytes, out srcName);
                }

                bool isConstCountAny = args[2].Match(Tag.Integer, out int constCountAny);
                int normalizedConstCount = constCountAny;
                if (isConstCountAny && useU8Count)
                    normalizedConstCount &= 0xFF;

                if (doCheck && isConstCountAny)
                {
                    if (hasDst && normalizedConstCount >= 0 && normalizedConstCount > dstBytes)
                    {
                        Program.Warning(args[2].Source, "__memcpy count {0} exceeds fixed dst array '{1}' size {2} bytes (-Zcheck)", normalizedConstCount, dstName, dstBytes);
                        EmitAsm("JP", EnsureCheckTrap());
                        return;
                    }
                    if (hasSrc && normalizedConstCount >= 0 && normalizedConstCount > srcBytes)
                    {
                        Program.Warning(args[2].Source, "__memcpy count {0} exceeds fixed src array '{1}' size {2} bytes (-Zcheck)", normalizedConstCount, srcName, srcBytes);
                        EmitAsm("JP", EnsureCheckTrap());
                        return;
                    }
                }

                if (isConstCountAny && normalizedConstCount >= 0 && normalizedConstCount <= 16)
                {
                    if (normalizedConstCount == 0) return;

                    CompileIntoHL(args[0]);
                    EmitAsm("PUSH_HL");
                    CompileIntoHL(args[1]);
                    EmitAsm("PUSH_HL");
                    EmitAsm("POP_DE");
                    EmitAsm("POP_HL");

                    for (int k = 0; k < normalizedConstCount; k++)
                    {
                        EmitAsm("LD_A_DE");
                        EmitAsm("LDI_HL_A");
                        EmitAsm("INC_DE");
                    }
                    return;
                }

                if (isConstCountAny && normalizedConstCount > 16 && normalizedConstCount <= 255)
                {
                    CompileIntoHL(args[0]);
                    EmitAsm("PUSH_HL");
                    CompileIntoHL(args[1]);
                    EmitAsm("PUSH_HL");
                    EmitAsm("POP_DE");
                    EmitAsm("POP_HL");

                    if (normalizedConstCount <= 64)
                    {
                        int blocks8 = normalizedConstCount / 8;
                        int rem8 = normalizedConstCount % 8;
                        if (blocks8 > 0)
                        {
                            EmitAsm("LD_B_IMM", new AsmOperand(blocks8, AddressMode.Immediate));
                            // Advance source and destination through eight-byte groups, then emit the remaining constant bytes.
                            AsmOperand loop8 = MakeUniqueLabel("memcpy8_loop");
                            EmitLabel(loop8);
                            for (int k = 0; k < 8; k++)
                            {
                                EmitAsm("LD_A_DE");
                                EmitAsm("LDI_HL_A");
                                EmitAsm("INC_DE");
                            }
                            EmitAsm("DEC_B");
                            EmitAsm("JP_NZ", loop8);
                        }
                        for (int k = 0; k < rem8; k++)
                        {
                            EmitAsm("LD_A_DE");
                            EmitAsm("LDI_HL_A");
                            EmitAsm("INC_DE");
                        }
                        return;
                    }

                    int blocks16 = normalizedConstCount / 16;
                    int rem16 = normalizedConstCount % 16;
                    if (blocks16 > 0)
                    {
                        EmitAsm("LD_B_IMM", new AsmOperand(blocks16, AddressMode.Immediate));
                        // Use sixteen-byte groups while keeping the residual copy length known at compile time.
                        AsmOperand loop16 = MakeUniqueLabel("memcpy16_loop");
                        EmitLabel(loop16);
                        for (int k = 0; k < 16; k++)
                        {
                            EmitAsm("LD_A_DE");
                            EmitAsm("LDI_HL_A");
                            EmitAsm("INC_DE");
                        }
                        EmitAsm("DEC_B");
                        EmitAsm("JP_NZ", loop16);
                    }
                    for (int k = 0; k < rem16; k++)
                    {
                        EmitAsm("LD_A_DE");
                        EmitAsm("LDI_HL_A");
                        EmitAsm("INC_DE");
                    }
                    return;
                }

                if (useU8Count)
                {
                    CompileIntoHL(args[0]);
                    EmitAsm("PUSH_HL");

                    CompileIntoHL(args[1]);
                    EmitAsm("PUSH_HL");

                    CompileIntoA(args[2]);
                    EmitAsm("LD_B_A");
                    if (doCheck)
                    {
                        if (hasDst) EmitU8LeqConstOrTrapInA(dstBytes);
                        if (hasSrc) EmitU8LeqConstOrTrapInA(srcBytes);
                    }

                    AsmOperand doneLabel = MakeUniqueLabel("memcpy_small_done");
                    AsmOperand smallLoopLabel = MakeUniqueLabel("memcpy_small_loop");
                    EmitAsm("OR_A");
                    EmitAsm("POP_DE");
                    EmitAsm("POP_HL");
                    EmitAsm("JP_Z", doneLabel);

                    EmitLabel(smallLoopLabel);
                    EmitAsm("LD_A_DE");
                    EmitAsm("LDI_HL_A");
                    EmitAsm("INC_DE");
                    EmitAsm("DEC_B");
                    EmitAsm("JP_NZ", smallLoopLabel);
                    EmitLabel(doneLabel);
                    return;
                }

                CompileIntoHL(args[0]);
                EmitAsm("PUSH_HL");

                CompileIntoHL(args[1]);
                EmitAsm("PUSH_HL");

                CompileIntoHL(args[2]);
                if (doCheck && !isConstCountAny)
                {
                    if (hasDst) EmitU16LeqConstOrTrap(dstBytes);
                    if (hasSrc) EmitU16LeqConstOrTrap(srcBytes);
                }
                EmitAsm("LD_B_H");
                EmitAsm("LD_C_L");

                EmitAsm("POP_DE");
                EmitAsm("POP_HL");

                AsmOperand loopLabel = MakeUniqueLabel("memcpy_loop");
                AsmOperand wordDoneLabel = MakeUniqueLabel("memcpy_done");
                // A zero word count must skip the first write as well as the loop.
                EmitAsm("LD_A_B");
                EmitAsm("OR_C");
                EmitAsm("JP_Z", wordDoneLabel);
                EmitLabel(loopLabel);

                EmitAsm("LD_A_DE");
                EmitAsm("LDI_HL_A");
                EmitAsm("INC_DE");

                EmitAsm("DEC_BC");
                EmitAsm("LD_A_B");
                EmitAsm("OR_C");
                EmitAsm("JP_NZ", loopLabel);
                EmitLabel(wordDoneLabel);

                return;
            }

// kitaqgb/kitaqgb/Lowerer.cs:2145
if (expr.MatchAny(Tag.Call, out callFunc, out callArgs))
        {
            string knownCallName;
            if (callFunc != null && callFunc.Match(Tag.Name, out knownCallName) &&
                knownCallName == "__cgb_is_cgb" &&
                callArgs != null && callArgs.Length == 0 &&
                // A resolved target configuration can replace the zero-argument CGB query with a literal.
                Program.TryGetKnownCgbRuntimeValue(out int knownCgbValue))
            {
                return Expr.Make(Tag.Integer, knownCgbValue).WithSource(expr.Source);
            }

            Expr newFunc = LowerExpr(ctx, callFunc, false, prefix);

            // Optimization (post-A3): when we must spill a non-atomic argument into a temp,
            // prefer a u8 temp if the *parameter type* is u8-like.
            // This preserves semantics (implicit truncation to u8) while avoiding needless
            // u16 temporaries created by integer promotions.
            CType[] paramTypes = null;
            string __fname = null;
            if (newFunc.Match(Tag.Name, out __fname))
            {
                paramTypes = ctx.FindFuncParams(__fname);
                if (paramTypes == null)
                {
                    CType fnTypeFromName;
                    if (TryGetCallableFunctionType(ctx.FindTypeOfName(__fname), out fnTypeFromName))
                        paramTypes = fnTypeFromName.ParamTypes;
                }
            }
            else
            {
                CType fnTypeFromExpr;
                if (TryGetCallableFunctionType(InferType(ctx, newFunc), out fnTypeFromExpr))
                    paramTypes = fnTypeFromExpr.ParamTypes;
            }

            Expr[] newArgs = new Expr[callArgs.Length];
            for (int i = 0; i < callArgs.Length; i++)
            {
                Expr a = LowerExpr(ctx, callArgs[i], allowExtract, prefix);
                bool preserveConstantArgIntrinsic = false;
                if (a.MatchAny(Tag.Call, out Expr argFunc, out Expr[] _argCallArgs) &&
                    argFunc.Match(Tag.Name, out string argFuncName))
                {
                    preserveConstantArgIntrinsic = (argFuncName == "__bankof" || argFuncName == "__cgb_is_cgb");
                }
                // Keep evaluable constants and special intrinsic/bank operands visible to code generation instead of spilling them.
                bool preserveCompileTimeConstantArg = ctx.Global.TryEvalConstInt(a, out _);
                bool preserveFarcallBankArg = (__fname == "__farcall" && i == 0);

                if (allowExtract && !preserveFarcallBankArg && !preserveConstantArgIntrinsic && !preserveCompileTimeConstantArg && !IsAtom(a))
                {
                    // If we know the callee's parameter type, use it to choose a smaller temp.
                    // Example: (u8)(row*32) passed to a u8 param should spill as u8, not u16.
                    CType spillAs = null;
                    if (paramTypes != null && i < paramTypes.Length)
                    {
                        CType pt = paramTypes[i];
                        if (IsU8Like(pt)) spillAs = CType.UInt8;
                    }
                    if (spillAs == null) spillAs = InferType(ctx, a);
                    if (!IsAggregateType(spillAs) && SizeOfType(spillAs) <= 2)
                        a = SpillToTemp(ctx, a, spillAs, prefix);
                }
                newArgs[i] = a;
            }

            // Lint: const discard for argument passing
            // Warn when passing a const-qualified pointer (or const array decay) to a non-const pointer parameter.
            // Suppress when the argument has an explicit cast, or inside __unsafe.
            if (!ctx.InUnsafe && paramTypes != null)
            {
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    CType pt = paramTypes[i];
                    if (pt == null) continue;
                    if (!IsPointerToNonConst(pt)) continue;
                    if (HasTopLevelCast(newArgs[i])) continue;

                    CType at = InferType(ctx, newArgs[i]);
                    if (at != null && (IsPointerToConst(at) || IsArrayOfConst(at)))
                    {
                        string fn = __fname ?? "<call>";
                        WarnConstDiscard(expr, $"argument {i} to '{fn}'");
                        break; // keep it quiet: one warning per call is enough
                    }
                }
            }

            if (paramTypes != null)
            {
                string fn = __fname ?? "<call>";
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    FilePosition argPos = newArgs[i].Source;
                    if (string.IsNullOrEmpty(argPos.Filename) || argPos.Filename == "<unknown>")
                        argPos = expr.Source;
                    WarnImplicitNarrowingIfNeeded(ctx, newArgs[i], InferType(ctx, newArgs[i]), paramTypes[i], argPos,
                        "argument " + i + " to '" + fn + "'");
                }
            }

            // Lint: __enum_strict argument passing.
            if (paramTypes != null)
            {
                string fn = __fname ?? "<call>";
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    CType pt = paramTypes[i];
                    if (pt == null) continue;

                    CType at = InferType(ctx, newArgs[i]);
                    if (at == null) continue;

                    if (HasTopLevelCast(newArgs[i])) continue;

                    if (IsStrictEnum(pt))
                    {
                        if (!IsSameEnum(pt, at))
                        {
                            Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
                                "argument {0} to '{1}' expects __enum_strict {2}, got {3}; cast explicitly to silence",
                                i, fn, pt.Show(), at.Show());
                        }
                    }
                    else if (IsPlainInteger(pt) && IsStrictEnum(at))
                    {
                        Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
                            "argument {0} to '{1}' passes __enum_strict {2} to integer parameter {3}; cast explicitly to silence",
                            i, fn, at.Show(), pt.Show());
                    }
                }
            }

            if (!ctx.InUnsafe)
                RestrictAliasLint(ctx, newFunc, newArgs, expr.Source);

            // __slice(ptr,len) helper: represent a "slice" (ptr+len) in the IR.
            // Semantics: evaluate both arguments, result type is the pointer/array (same as arg0).
            // Bounds checks are injected only with -Zcheck (see Program.CheckSliceBounds).
            // Note: keep the intrinsic name conservative to avoid collisions ("slice" is allowed only
            // when there is no declared function named "slice").
            if (newFunc.Match(Tag.Name, out string __sfname))
            {
                bool isSliceIntrinsic = (__sfname == "__slice") || (__sfname == "slice" && ctx.FindFuncParams("slice") == null);
                if (isSliceIntrinsic)
                {
                    if (newArgs.Length != 2)
                    {
                        Program.Error(expr.Source, ErrorCode.ParseError, "{0}: expected 2 arguments: __slice(ptr,len)", __sfname);
                    }
                    else
                    {
                        CType pty = InferType(ctx, newArgs[0]);
                        if (pty == null || !(pty.IsPointer || pty.IsArray))
                            Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: first argument must be a pointer or array, got: {1}", __sfname, (pty == null ? "<unknown>" : pty.Show()));

                        CType lty = InferType(ctx, newArgs[1]);
                        if (lty == null || !lty.IsInteger)
                            Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: second argument must be an integer length, got: {1}", __sfname, (lty == null ? "<unknown>" : lty.Show()));
                    }

                    return Expr.Make(Tag.Slice, newArgs[0], newArgs[1]).WithSource(expr.Source);
                }
            }

            return MakeCall(newFunc, newArgs).WithSource(expr.Source);
        }
__memset

Remplit la zone de destination avec une même valeur d’un octet. Le compteur sur 16 bits convient à l’effacement d’un grand tampon ou à l’initialisation d’une table. Par exemple, 0x5A écrit 5A 5A 5A…, et non un entier de plusieurs octets répété.

Syntaxe

c
void __memset(void* dst, u8 value, u16 count);

Paramètres

dst

Pointeur vers le premier octet accessible en écriture de la destination. Prévoyez une zone contiguë assez grande pour toute l’opération ; aucun tampon n’est alloué automatiquement.

value

Octet répété à chaque adresse de destination : 0–255. La valeur 0 remet la zone à zéro. Pour initialiser un tableau de u16 avec un nombre non nul, affectez ce nombre à chaque élément.

count

Nombre d’octets en u16 (0–65535), et non nombre d’éléments. Le maximum du type ne garantit pas que la machine dispose d’un tampon valide de cette taille. Toute la zone doit tenir dans le mappage mémoire sélectionné.

Valeur renvoyée

Aucune (void).

Conditions et limites

Un compteur nul ne transfère aucun octet. Les effets de bord des expressions passées en argument ont néanmoins lieu : l’exemple fournit deux fonctions renvoyant des pointeurs et constate leurs deux appels malgré une longueur nulle.

L’opération se termine avant de rendre la main. Elle ne change pas de banque ROM/RAM et ne limite pas automatiquement la zone aux bornes d’un tampon. Maintenez le mappage stable, utilisez des zones valides et coordonnez l’accès avec les interruptions utilisant les mêmes tampons.

Sur GB, il s’agit d’accès mémoire ordinaires du processeur. Ils n’attendent pas un créneau sûr pour la VRAM/OAM, ne lancent pas de DMA et ne sélectionnent pas les banques VRAM/WRAM du CGB. Utilisez les API de transfert graphique dédiées à ces tâches.

Exemple d’utilisation

c
memory_reset();
__memset(memory_destination+1,0x5A,257);
memory_fill_word=memory_check(257,1); // Repeat one byte, not a 16-bit word.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple utilise __memset pour remplir 257 octets avec 0x5A à partir du décalage 1. memory_check exige que tous ces octets valent 0x5A, que les octets de garde restent à 0xCC et que le tampon source distinct ne change pas. La ligne FILL WORD doit afficher 257.

Le programme complet initialise deux tampons de 264 octets avant chaque opération, puis vérifie tous les octets de la source, de la destination et des zones de garde. L’écran attendu affiche COPY WORD 257, FILL WORD 257, COPY BYTE 255, FILL BYTE 255, COPY16 016, COPY32 032, ZERO CALLS 002 et FAILED 000. WORD/BYTE désignent la largeur du compteur, pas la taille des éléments copiés. Ces nombres proviennent des vérifications de l’exemple ; ces fonctions void ne renvoient aucune valeur.

Les captures montrent du texte noir sur blanc sur DMG, bleu sur blanc sur CGB et bleu sur noir sur FC. L’exemple vérifie des opérations en RAM ; le texte en présente les résultats, sans illustrer une primitive de dessin. Des contrôles distincts sur émulateur couvrent 88 cas par plateforme : longueurs constantes ou déterminées à l’exécution de 0, 1, 16, 17, 32, 33, 64, 65 et 255 octets, plus 256 et 257 pour les compteurs sur 16 bits. Aucun test sur matériel réel n’a été effectué.

__memset: DMG
DMG
__memset: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\memory_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\memory_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/rpg.h:141

kitaqgb/kitaqgb/CodeGenerator.cs:8926

csharp
if (funcName == "__memset" || funcName == "__memset_small")
            {
                // The small API truncates its count to a byte; the general API uses a word count.
                bool useU8Count = (funcName == "__memset_small");
                if (args.Length != 3) { Error(expr, "__memset requires 3 arguments"); return; }

                // -Zcheck: fixed-array range check for memset length
                bool doCheck = Program.CheckMemCopy && !InUnsafe;
                bool hasDst = false;
                int dstBytes = 0;
                string dstName = null;
                if (doCheck)
                    hasDst = TryGetFixedArrayByteLength(args[0], out dstBytes, out dstName);

                bool isConstCountAny = args[2].Match(Tag.Integer, out int constCountAny);
                int normalizedConstCount = constCountAny;
                if (isConstCountAny && useU8Count)
                    normalizedConstCount &= 0xFF;

                if (doCheck && hasDst && isConstCountAny && normalizedConstCount >= 0 && normalizedConstCount > dstBytes)
                {
                    Program.Warning(args[2].Source, "__memset count {0} exceeds fixed array '{1}' size {2} bytes (-Zcheck)", normalizedConstCount, dstName, dstBytes);
                    EmitAsm("JP", EnsureCheckTrap());
                    return;
                }

                // Size-specialized memset for small constant counts (<=16): unrolled stores.
                if (isConstCountAny && normalizedConstCount >= 0 && normalizedConstCount <= 16)
                {
                    if (normalizedConstCount == 0) return;

                    CompileIntoHL(args[0]);
                    EmitAsm("PUSH_HL");
                    CompileIntoA(args[1]);
                    EmitAsm("POP_HL");

                    for (int k = 0; k < normalizedConstCount; k++)
                        EmitAsm("LDI_HL_A");
                    return;
                }

                // Size-specialized memset for constant counts beyond 16
                if (isConstCountAny && normalizedConstCount >= 0)
                {
                    if (normalizedConstCount == 0) return;

                    if (normalizedConstCount > 16 && normalizedConstCount <= 64)
                    {
                        int blocks8 = normalizedConstCount / 8;
                        int rem = normalizedConstCount % 8;

                        CompileIntoHL(args[0]);
                        EmitAsm("PUSH_HL");
                        CompileIntoA(args[1]);
                        EmitAsm("POP_HL");

                        if (blocks8 > 0)
                        {
                            EmitAsm("LD_B_IMM", new AsmOperand(blocks8, AddressMode.Immediate));
                            // Emit groups of eight stores followed by the constant remainder for medium fills.
                            AsmOperand loop8 = MakeUniqueLabel("memset8_loop");
                            EmitLabel(loop8);
                            for (int k = 0; k < 8; k++) EmitAsm("LDI_HL_A");
                            EmitAsm("DEC_B");
                            EmitAsm("JP_NZ", loop8);
                        }
                        for (int k = 0; k < rem; k++) EmitAsm("LDI_HL_A");
                        return;
                    }

                    if (normalizedConstCount <= 255)
                    {
                        int blocks16 = normalizedConstCount / 16;
                        int rem = normalizedConstCount % 16;

                        CompileIntoHL(args[0]);
                        EmitAsm("PUSH_HL");
                        CompileIntoA(args[1]);
                        EmitAsm("POP_HL");

                        if (blocks16 > 0)
                        {
                            EmitAsm("LD_B_IMM", new AsmOperand(blocks16, AddressMode.Immediate));
                            // Use sixteen-store groups for larger byte-range constant fills.
                            AsmOperand loop16 = MakeUniqueLabel("memset16_loop");
                            EmitLabel(loop16);
                            for (int k = 0; k < 16; k++) EmitAsm("LDI_HL_A");
                            EmitAsm("DEC_B");
                            EmitAsm("JP_NZ", loop16);
                        }
                        for (int k = 0; k < rem; k++) EmitAsm("LDI_HL_A");
                        return;
                    }
                }

                if (useU8Count)
                {
                    CompileIntoHL(args[0]);
                    EmitAsm("PUSH_HL");

                    CompileIntoA(args[1]);
                    EmitAsm("LD_D_A");

                    CompileIntoA(args[2]);
                    EmitAsm("LD_B_A");
                    if (doCheck && hasDst)
                        EmitU8LeqConstOrTrapInA(dstBytes);

                    AsmOperand doneLabel = MakeUniqueLabel("memset_small_done");
                    AsmOperand smallLoopLabel = MakeUniqueLabel("memset_small_loop");
                    EmitAsm("OR_A");
                    EmitAsm("POP_HL");
                    EmitAsm("JP_Z", doneLabel);
                    EmitAsm("LD_A_D");
                    EmitLabel(smallLoopLabel);
                    EmitAsm("LDI_HL_A");
                    EmitAsm("DEC_B");
                    EmitAsm("JP_NZ", smallLoopLabel);
                    EmitLabel(doneLabel);
                    return;
                }

                CompileIntoHL(args[0]);
                EmitAsm("PUSH_HL");

                CompileIntoHL(args[2]);
                if (doCheck && hasDst && !isConstCountAny)
                    EmitU16LeqConstOrTrap(dstBytes);
                EmitAsm("LD_B_H");
                EmitAsm("LD_C_L");

                CompileIntoA(args[1]);
                EmitAsm("LD_D_A");

                EmitAsm("POP_HL");

                AsmOperand loopLabel = MakeUniqueLabel("memset_loop");
                AsmOperand wordDoneLabel = MakeUniqueLabel("memset_done");
                // A zero word count must skip the first write as well as the loop.
                EmitAsm("LD_A_B");
                EmitAsm("OR_C");
                EmitAsm("JP_Z", wordDoneLabel);
                EmitLabel(loopLabel);

                EmitAsm("LD_A_D");
                EmitAsm("LDI_HL_A");

                EmitAsm("DEC_BC");
                EmitAsm("LD_A_B");
                EmitAsm("OR_C");
                EmitAsm("JP_NZ", loopLabel);
                EmitLabel(wordDoneLabel);

                return;
            }

// kitaqgb/kitaqgb/Lowerer.cs:2145
if (expr.MatchAny(Tag.Call, out callFunc, out callArgs))
        {
            string knownCallName;
            if (callFunc != null && callFunc.Match(Tag.Name, out knownCallName) &&
                knownCallName == "__cgb_is_cgb" &&
                callArgs != null && callArgs.Length == 0 &&
                // A resolved target configuration can replace the zero-argument CGB query with a literal.
                Program.TryGetKnownCgbRuntimeValue(out int knownCgbValue))
            {
                return Expr.Make(Tag.Integer, knownCgbValue).WithSource(expr.Source);
            }

            Expr newFunc = LowerExpr(ctx, callFunc, false, prefix);

            // Optimization (post-A3): when we must spill a non-atomic argument into a temp,
            // prefer a u8 temp if the *parameter type* is u8-like.
            // This preserves semantics (implicit truncation to u8) while avoiding needless
            // u16 temporaries created by integer promotions.
            CType[] paramTypes = null;
            string __fname = null;
            if (newFunc.Match(Tag.Name, out __fname))
            {
                paramTypes = ctx.FindFuncParams(__fname);
                if (paramTypes == null)
                {
                    CType fnTypeFromName;
                    if (TryGetCallableFunctionType(ctx.FindTypeOfName(__fname), out fnTypeFromName))
                        paramTypes = fnTypeFromName.ParamTypes;
                }
            }
            else
            {
                CType fnTypeFromExpr;
                if (TryGetCallableFunctionType(InferType(ctx, newFunc), out fnTypeFromExpr))
                    paramTypes = fnTypeFromExpr.ParamTypes;
            }

            Expr[] newArgs = new Expr[callArgs.Length];
            for (int i = 0; i < callArgs.Length; i++)
            {
                Expr a = LowerExpr(ctx, callArgs[i], allowExtract, prefix);
                bool preserveConstantArgIntrinsic = false;
                if (a.MatchAny(Tag.Call, out Expr argFunc, out Expr[] _argCallArgs) &&
                    argFunc.Match(Tag.Name, out string argFuncName))
                {
                    preserveConstantArgIntrinsic = (argFuncName == "__bankof" || argFuncName == "__cgb_is_cgb");
                }
                // Keep evaluable constants and special intrinsic/bank operands visible to code generation instead of spilling them.
                bool preserveCompileTimeConstantArg = ctx.Global.TryEvalConstInt(a, out _);
                bool preserveFarcallBankArg = (__fname == "__farcall" && i == 0);

                if (allowExtract && !preserveFarcallBankArg && !preserveConstantArgIntrinsic && !preserveCompileTimeConstantArg && !IsAtom(a))
                {
                    // If we know the callee's parameter type, use it to choose a smaller temp.
                    // Example: (u8)(row*32) passed to a u8 param should spill as u8, not u16.
                    CType spillAs = null;
                    if (paramTypes != null && i < paramTypes.Length)
                    {
                        CType pt = paramTypes[i];
                        if (IsU8Like(pt)) spillAs = CType.UInt8;
                    }
                    if (spillAs == null) spillAs = InferType(ctx, a);
                    if (!IsAggregateType(spillAs) && SizeOfType(spillAs) <= 2)
                        a = SpillToTemp(ctx, a, spillAs, prefix);
                }
                newArgs[i] = a;
            }

            // Lint: const discard for argument passing
            // Warn when passing a const-qualified pointer (or const array decay) to a non-const pointer parameter.
            // Suppress when the argument has an explicit cast, or inside __unsafe.
            if (!ctx.InUnsafe && paramTypes != null)
            {
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    CType pt = paramTypes[i];
                    if (pt == null) continue;
                    if (!IsPointerToNonConst(pt)) continue;
                    if (HasTopLevelCast(newArgs[i])) continue;

                    CType at = InferType(ctx, newArgs[i]);
                    if (at != null && (IsPointerToConst(at) || IsArrayOfConst(at)))
                    {
                        string fn = __fname ?? "<call>";
                        WarnConstDiscard(expr, $"argument {i} to '{fn}'");
                        break; // keep it quiet: one warning per call is enough
                    }
                }
            }

            if (paramTypes != null)
            {
                string fn = __fname ?? "<call>";
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    FilePosition argPos = newArgs[i].Source;
                    if (string.IsNullOrEmpty(argPos.Filename) || argPos.Filename == "<unknown>")
                        argPos = expr.Source;
                    WarnImplicitNarrowingIfNeeded(ctx, newArgs[i], InferType(ctx, newArgs[i]), paramTypes[i], argPos,
                        "argument " + i + " to '" + fn + "'");
                }
            }

            // Lint: __enum_strict argument passing.
            if (paramTypes != null)
            {
                string fn = __fname ?? "<call>";
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    CType pt = paramTypes[i];
                    if (pt == null) continue;

                    CType at = InferType(ctx, newArgs[i]);
                    if (at == null) continue;

                    if (HasTopLevelCast(newArgs[i])) continue;

                    if (IsStrictEnum(pt))
                    {
                        if (!IsSameEnum(pt, at))
                        {
                            Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
                                "argument {0} to '{1}' expects __enum_strict {2}, got {3}; cast explicitly to silence",
                                i, fn, pt.Show(), at.Show());
                        }
                    }
                    else if (IsPlainInteger(pt) && IsStrictEnum(at))
                    {
                        Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
                            "argument {0} to '{1}' passes __enum_strict {2} to integer parameter {3}; cast explicitly to silence",
                            i, fn, at.Show(), pt.Show());
                    }
                }
            }

            if (!ctx.InUnsafe)
                RestrictAliasLint(ctx, newFunc, newArgs, expr.Source);

            // __slice(ptr,len) helper: represent a "slice" (ptr+len) in the IR.
            // Semantics: evaluate both arguments, result type is the pointer/array (same as arg0).
            // Bounds checks are injected only with -Zcheck (see Program.CheckSliceBounds).
            // Note: keep the intrinsic name conservative to avoid collisions ("slice" is allowed only
            // when there is no declared function named "slice").
            if (newFunc.Match(Tag.Name, out string __sfname))
            {
                bool isSliceIntrinsic = (__sfname == "__slice") || (__sfname == "slice" && ctx.FindFuncParams("slice") == null);
                if (isSliceIntrinsic)
                {
                    if (newArgs.Length != 2)
                    {
                        Program.Error(expr.Source, ErrorCode.ParseError, "{0}: expected 2 arguments: __slice(ptr,len)", __sfname);
                    }
                    else
                    {
                        CType pty = InferType(ctx, newArgs[0]);
                        if (pty == null || !(pty.IsPointer || pty.IsArray))
                            Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: first argument must be a pointer or array, got: {1}", __sfname, (pty == null ? "<unknown>" : pty.Show()));

                        CType lty = InferType(ctx, newArgs[1]);
                        if (lty == null || !lty.IsInteger)
                            Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: second argument must be an integer length, got: {1}", __sfname, (lty == null ? "<unknown>" : lty.Show()));
                    }

                    return Expr.Make(Tag.Slice, newArgs[0], newArgs[1]).WithSource(expr.Source);
                }
            }

            return MakeCall(newFunc, newArgs).WithSource(expr.Source);
        }
__memset_small

Remplit jusqu’à 255 octets consécutifs avec une même valeur, à l’aide d’un compteur sur 8 bits. Cette forme convient à l’initialisation d’un petit tampon de travail. Au-delà de 255 octets, utilisez __memset.

Syntaxe

c
void __memset_small(void* dst, u8 value, u8 len);

Paramètres

dst

Pointeur vers le premier octet accessible en écriture de la destination. Prévoyez une zone contiguë assez grande pour toute l’opération ; aucun tampon n’est alloué automatiquement.

value

Octet répété à chaque adresse de destination : 0–255. La valeur 0 remet la zone à zéro. Pour initialiser un tableau de u16 avec un nombre non nul, affectez ce nombre à chaque élément.

len

Nombre d’octets en u8 : 0–255. Ne passez pas 256 pour désigner un bloc entier de 256 octets : cette longueur ne tient pas dans le paramètre. À partir de 256 octets, utilisez la fonction correspondante sans _small.

Valeur renvoyée

Aucune (void).

Conditions et limites

Un compteur nul ne transfère aucun octet. Les effets de bord des expressions passées en argument ont néanmoins lieu : l’exemple fournit deux fonctions renvoyant des pointeurs et constate leurs deux appels malgré une longueur nulle.

L’opération se termine avant de rendre la main. Elle ne change pas de banque ROM/RAM et ne limite pas automatiquement la zone aux bornes d’un tampon. Maintenez le mappage stable, utilisez des zones valides et coordonnez l’accès avec les interruptions utilisant les mêmes tampons.

Sur GB, il s’agit d’accès mémoire ordinaires du processeur. Ils n’attendent pas un créneau sûr pour la VRAM/OAM, ne lancent pas de DMA et ne sélectionnent pas les banques VRAM/WRAM du CGB. Utilisez les API de transfert graphique dédiées à ces tâches.

Exemple d’utilisation

c
memory_reset();
__memset_small(memory_destination+1,0x5A,255);
memory_fill_byte=memory_check(255,1);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple utilise __memset_small pour remplir 255 octets avec 0x5A à partir du décalage 1. memory_check exige que tous ces octets valent 0x5A, que les octets de garde restent à 0xCC et que le tampon source distinct ne change pas. La ligne FILL BYTE doit afficher 255.

Le programme complet initialise deux tampons de 264 octets avant chaque opération, puis vérifie tous les octets de la source, de la destination et des zones de garde. L’écran attendu affiche COPY WORD 257, FILL WORD 257, COPY BYTE 255, FILL BYTE 255, COPY16 016, COPY32 032, ZERO CALLS 002 et FAILED 000. WORD/BYTE désignent la largeur du compteur, pas la taille des éléments copiés. Ces nombres proviennent des vérifications de l’exemple ; ces fonctions void ne renvoient aucune valeur.

Les captures montrent du texte noir sur blanc sur DMG, bleu sur blanc sur CGB et bleu sur noir sur FC. L’exemple vérifie des opérations en RAM ; le texte en présente les résultats, sans illustrer une primitive de dessin. Des contrôles distincts sur émulateur couvrent 88 cas par plateforme : longueurs constantes ou déterminées à l’exécution de 0, 1, 16, 17, 32, 33, 64, 65 et 255 octets, plus 256 et 257 pour les compteurs sur 16 bits. Aucun test sur matériel réel n’a été effectué.

__memset_small: DMG
DMG
__memset_small: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\memory_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\memory_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/rpg.h:176

kitaqgb/kitaqgb/CodeGenerator.cs:8926

csharp
if (funcName == "__memset" || funcName == "__memset_small")
            {
                // The small API truncates its count to a byte; the general API uses a word count.
                bool useU8Count = (funcName == "__memset_small");
                if (args.Length != 3) { Error(expr, "__memset requires 3 arguments"); return; }

                // -Zcheck: fixed-array range check for memset length
                bool doCheck = Program.CheckMemCopy && !InUnsafe;
                bool hasDst = false;
                int dstBytes = 0;
                string dstName = null;
                if (doCheck)
                    hasDst = TryGetFixedArrayByteLength(args[0], out dstBytes, out dstName);

                bool isConstCountAny = args[2].Match(Tag.Integer, out int constCountAny);
                int normalizedConstCount = constCountAny;
                if (isConstCountAny && useU8Count)
                    normalizedConstCount &= 0xFF;

                if (doCheck && hasDst && isConstCountAny && normalizedConstCount >= 0 && normalizedConstCount > dstBytes)
                {
                    Program.Warning(args[2].Source, "__memset count {0} exceeds fixed array '{1}' size {2} bytes (-Zcheck)", normalizedConstCount, dstName, dstBytes);
                    EmitAsm("JP", EnsureCheckTrap());
                    return;
                }

                // Size-specialized memset for small constant counts (<=16): unrolled stores.
                if (isConstCountAny && normalizedConstCount >= 0 && normalizedConstCount <= 16)
                {
                    if (normalizedConstCount == 0) return;

                    CompileIntoHL(args[0]);
                    EmitAsm("PUSH_HL");
                    CompileIntoA(args[1]);
                    EmitAsm("POP_HL");

                    for (int k = 0; k < normalizedConstCount; k++)
                        EmitAsm("LDI_HL_A");
                    return;
                }

                // Size-specialized memset for constant counts beyond 16
                if (isConstCountAny && normalizedConstCount >= 0)
                {
                    if (normalizedConstCount == 0) return;

                    if (normalizedConstCount > 16 && normalizedConstCount <= 64)
                    {
                        int blocks8 = normalizedConstCount / 8;
                        int rem = normalizedConstCount % 8;

                        CompileIntoHL(args[0]);
                        EmitAsm("PUSH_HL");
                        CompileIntoA(args[1]);
                        EmitAsm("POP_HL");

                        if (blocks8 > 0)
                        {
                            EmitAsm("LD_B_IMM", new AsmOperand(blocks8, AddressMode.Immediate));
                            // Emit groups of eight stores followed by the constant remainder for medium fills.
                            AsmOperand loop8 = MakeUniqueLabel("memset8_loop");
                            EmitLabel(loop8);
                            for (int k = 0; k < 8; k++) EmitAsm("LDI_HL_A");
                            EmitAsm("DEC_B");
                            EmitAsm("JP_NZ", loop8);
                        }
                        for (int k = 0; k < rem; k++) EmitAsm("LDI_HL_A");
                        return;
                    }

                    if (normalizedConstCount <= 255)
                    {
                        int blocks16 = normalizedConstCount / 16;
                        int rem = normalizedConstCount % 16;

                        CompileIntoHL(args[0]);
                        EmitAsm("PUSH_HL");
                        CompileIntoA(args[1]);
                        EmitAsm("POP_HL");

                        if (blocks16 > 0)
                        {
                            EmitAsm("LD_B_IMM", new AsmOperand(blocks16, AddressMode.Immediate));
                            // Use sixteen-store groups for larger byte-range constant fills.
                            AsmOperand loop16 = MakeUniqueLabel("memset16_loop");
                            EmitLabel(loop16);
                            for (int k = 0; k < 16; k++) EmitAsm("LDI_HL_A");
                            EmitAsm("DEC_B");
                            EmitAsm("JP_NZ", loop16);
                        }
                        for (int k = 0; k < rem; k++) EmitAsm("LDI_HL_A");
                        return;
                    }
                }

                if (useU8Count)
                {
                    CompileIntoHL(args[0]);
                    EmitAsm("PUSH_HL");

                    CompileIntoA(args[1]);
                    EmitAsm("LD_D_A");

                    CompileIntoA(args[2]);
                    EmitAsm("LD_B_A");
                    if (doCheck && hasDst)
                        EmitU8LeqConstOrTrapInA(dstBytes);

                    AsmOperand doneLabel = MakeUniqueLabel("memset_small_done");
                    AsmOperand smallLoopLabel = MakeUniqueLabel("memset_small_loop");
                    EmitAsm("OR_A");
                    EmitAsm("POP_HL");
                    EmitAsm("JP_Z", doneLabel);
                    EmitAsm("LD_A_D");
                    EmitLabel(smallLoopLabel);
                    EmitAsm("LDI_HL_A");
                    EmitAsm("DEC_B");
                    EmitAsm("JP_NZ", smallLoopLabel);
                    EmitLabel(doneLabel);
                    return;
                }

                CompileIntoHL(args[0]);
                EmitAsm("PUSH_HL");

                CompileIntoHL(args[2]);
                if (doCheck && hasDst && !isConstCountAny)
                    EmitU16LeqConstOrTrap(dstBytes);
                EmitAsm("LD_B_H");
                EmitAsm("LD_C_L");

                CompileIntoA(args[1]);
                EmitAsm("LD_D_A");

                EmitAsm("POP_HL");

                AsmOperand loopLabel = MakeUniqueLabel("memset_loop");
                AsmOperand wordDoneLabel = MakeUniqueLabel("memset_done");
                // A zero word count must skip the first write as well as the loop.
                EmitAsm("LD_A_B");
                EmitAsm("OR_C");
                EmitAsm("JP_Z", wordDoneLabel);
                EmitLabel(loopLabel);

                EmitAsm("LD_A_D");
                EmitAsm("LDI_HL_A");

                EmitAsm("DEC_BC");
                EmitAsm("LD_A_B");
                EmitAsm("OR_C");
                EmitAsm("JP_NZ", loopLabel);
                EmitLabel(wordDoneLabel);

                return;
            }

// kitaqgb/kitaqgb/Lowerer.cs:2145
if (expr.MatchAny(Tag.Call, out callFunc, out callArgs))
        {
            string knownCallName;
            if (callFunc != null && callFunc.Match(Tag.Name, out knownCallName) &&
                knownCallName == "__cgb_is_cgb" &&
                callArgs != null && callArgs.Length == 0 &&
                // A resolved target configuration can replace the zero-argument CGB query with a literal.
                Program.TryGetKnownCgbRuntimeValue(out int knownCgbValue))
            {
                return Expr.Make(Tag.Integer, knownCgbValue).WithSource(expr.Source);
            }

            Expr newFunc = LowerExpr(ctx, callFunc, false, prefix);

            // Optimization (post-A3): when we must spill a non-atomic argument into a temp,
            // prefer a u8 temp if the *parameter type* is u8-like.
            // This preserves semantics (implicit truncation to u8) while avoiding needless
            // u16 temporaries created by integer promotions.
            CType[] paramTypes = null;
            string __fname = null;
            if (newFunc.Match(Tag.Name, out __fname))
            {
                paramTypes = ctx.FindFuncParams(__fname);
                if (paramTypes == null)
                {
                    CType fnTypeFromName;
                    if (TryGetCallableFunctionType(ctx.FindTypeOfName(__fname), out fnTypeFromName))
                        paramTypes = fnTypeFromName.ParamTypes;
                }
            }
            else
            {
                CType fnTypeFromExpr;
                if (TryGetCallableFunctionType(InferType(ctx, newFunc), out fnTypeFromExpr))
                    paramTypes = fnTypeFromExpr.ParamTypes;
            }

            Expr[] newArgs = new Expr[callArgs.Length];
            for (int i = 0; i < callArgs.Length; i++)
            {
                Expr a = LowerExpr(ctx, callArgs[i], allowExtract, prefix);
                bool preserveConstantArgIntrinsic = false;
                if (a.MatchAny(Tag.Call, out Expr argFunc, out Expr[] _argCallArgs) &&
                    argFunc.Match(Tag.Name, out string argFuncName))
                {
                    preserveConstantArgIntrinsic = (argFuncName == "__bankof" || argFuncName == "__cgb_is_cgb");
                }
                // Keep evaluable constants and special intrinsic/bank operands visible to code generation instead of spilling them.
                bool preserveCompileTimeConstantArg = ctx.Global.TryEvalConstInt(a, out _);
                bool preserveFarcallBankArg = (__fname == "__farcall" && i == 0);

                if (allowExtract && !preserveFarcallBankArg && !preserveConstantArgIntrinsic && !preserveCompileTimeConstantArg && !IsAtom(a))
                {
                    // If we know the callee's parameter type, use it to choose a smaller temp.
                    // Example: (u8)(row*32) passed to a u8 param should spill as u8, not u16.
                    CType spillAs = null;
                    if (paramTypes != null && i < paramTypes.Length)
                    {
                        CType pt = paramTypes[i];
                        if (IsU8Like(pt)) spillAs = CType.UInt8;
                    }
                    if (spillAs == null) spillAs = InferType(ctx, a);
                    if (!IsAggregateType(spillAs) && SizeOfType(spillAs) <= 2)
                        a = SpillToTemp(ctx, a, spillAs, prefix);
                }
                newArgs[i] = a;
            }

            // Lint: const discard for argument passing
            // Warn when passing a const-qualified pointer (or const array decay) to a non-const pointer parameter.
            // Suppress when the argument has an explicit cast, or inside __unsafe.
            if (!ctx.InUnsafe && paramTypes != null)
            {
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    CType pt = paramTypes[i];
                    if (pt == null) continue;
                    if (!IsPointerToNonConst(pt)) continue;
                    if (HasTopLevelCast(newArgs[i])) continue;

                    CType at = InferType(ctx, newArgs[i]);
                    if (at != null && (IsPointerToConst(at) || IsArrayOfConst(at)))
                    {
                        string fn = __fname ?? "<call>";
                        WarnConstDiscard(expr, $"argument {i} to '{fn}'");
                        break; // keep it quiet: one warning per call is enough
                    }
                }
            }

            if (paramTypes != null)
            {
                string fn = __fname ?? "<call>";
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    FilePosition argPos = newArgs[i].Source;
                    if (string.IsNullOrEmpty(argPos.Filename) || argPos.Filename == "<unknown>")
                        argPos = expr.Source;
                    WarnImplicitNarrowingIfNeeded(ctx, newArgs[i], InferType(ctx, newArgs[i]), paramTypes[i], argPos,
                        "argument " + i + " to '" + fn + "'");
                }
            }

            // Lint: __enum_strict argument passing.
            if (paramTypes != null)
            {
                string fn = __fname ?? "<call>";
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    CType pt = paramTypes[i];
                    if (pt == null) continue;

                    CType at = InferType(ctx, newArgs[i]);
                    if (at == null) continue;

                    if (HasTopLevelCast(newArgs[i])) continue;

                    if (IsStrictEnum(pt))
                    {
                        if (!IsSameEnum(pt, at))
                        {
                            Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
                                "argument {0} to '{1}' expects __enum_strict {2}, got {3}; cast explicitly to silence",
                                i, fn, pt.Show(), at.Show());
                        }
                    }
                    else if (IsPlainInteger(pt) && IsStrictEnum(at))
                    {
                        Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
                            "argument {0} to '{1}' passes __enum_strict {2} to integer parameter {3}; cast explicitly to silence",
                            i, fn, at.Show(), pt.Show());
                    }
                }
            }

            if (!ctx.InUnsafe)
                RestrictAliasLint(ctx, newFunc, newArgs, expr.Source);

            // __slice(ptr,len) helper: represent a "slice" (ptr+len) in the IR.
            // Semantics: evaluate both arguments, result type is the pointer/array (same as arg0).
            // Bounds checks are injected only with -Zcheck (see Program.CheckSliceBounds).
            // Note: keep the intrinsic name conservative to avoid collisions ("slice" is allowed only
            // when there is no declared function named "slice").
            if (newFunc.Match(Tag.Name, out string __sfname))
            {
                bool isSliceIntrinsic = (__sfname == "__slice") || (__sfname == "slice" && ctx.FindFuncParams("slice") == null);
                if (isSliceIntrinsic)
                {
                    if (newArgs.Length != 2)
                    {
                        Program.Error(expr.Source, ErrorCode.ParseError, "{0}: expected 2 arguments: __slice(ptr,len)", __sfname);
                    }
                    else
                    {
                        CType pty = InferType(ctx, newArgs[0]);
                        if (pty == null || !(pty.IsPointer || pty.IsArray))
                            Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: first argument must be a pointer or array, got: {1}", __sfname, (pty == null ? "<unknown>" : pty.Show()));

                        CType lty = InferType(ctx, newArgs[1]);
                        if (lty == null || !lty.IsInteger)
                            Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: second argument must be an integer length, got: {1}", __sfname, (lty == null ? "<unknown>" : lty.Show()));
                    }

                    return Expr.Make(Tag.Slice, newArgs[0], newArgs[1]).WithSource(expr.Source);
                }
            }

            return MakeCall(newFunc, newArgs).WithSource(expr.Source);
        }
__rle_decode_vram

Décompresse un flux count/value associé à une banque directement en VRAM, sans tampon de sortie intermédiaire en RAM. Le compilateur émet une boucle qui lit chaque paire, remplit sa répétition avec une écriture VRAM tenant compte du LCD et renvoie le nombre d’octets produits. Utile pour charger des tuiles compressées ou une carte de tuiles organisée en octets.

Syntaxe

c
u16 __rle_decode_vram(u16 dst, u8 bank, const void* src);

Paramètres

dst

Adresse CPU de 16 bits du premier octet de sortie, entre 0x8000 et 0x9FFF. Réservez assez d’octets consécutifs sans dépasser 0x9FFF. Par exemple, 0x8860 correspond à la tuile 134 dans la disposition de tuiles à indices non signés. La fonction n’alloue pas de tuiles et ne vérifie pas la plage de destination.

bank

Numéro de banque ROM source en u8. Banque et adresse CPU doivent correspondre au mapper de la cartouche et à l’emplacement de la ressource. La sélection de banque est restaurée après chaque lecture d’octet encodé. Ce n’est pas un sélecteur de banque VRAM de destination, et il ne peut pas représenter un numéro MBC5 sur neuf bits.

src

Pointeur CPU vers le flux encodé dans la banque fournie. L’adresse avance, mais le numéro de banque reste fixe. Gardez tout le flux, zéro terminal compris, dans un mappage lisible ; franchir 0x7FFF ne poursuit pas automatiquement dans la banque ROM suivante.

Valeur renvoyée

Nombre d’octets écrits en u16. Un compte nul immédiat ne produit aucun octet et renvoie 0. Le terminateur n’est pas copié. La longueur décompressée doit tenir dans u16 ; le retour n’a pas de code d’erreur distinct et ne représente pas un nombre de tuiles.

Conditions et limites

Le codage porte sur des octets, pas sur du texte. Par exemple, {3, 0x12, 2, 0xA5, 0} donne cinq octets : 12 12 12 A5 A5. Cette liste de sortie est en hexadécimal. Une valeur nulle dans une paire à compte non nul est une donnée ordinaire, pas un marqueur de fin.

Aucun argument ne décrit la taille encodée, la capacité de sortie ou les données mal formées. Validez les ressources à leur création ou chargement, fournissez le zéro terminal et calculez la taille décompressée avant le décodage. Un flux invalide ou non terminé peut lire au-delà de la source et écrire au-delà de la destination.

LCD éteint, les répétitions sont écrites directement. LCD allumé, la routine attend un mode autorisant l’écriture pour chaque octet, avec DI/EI autour de l’attente et de l’écriture. Un long flux peut donc couvrir plusieurs images ; l’appel est synchrone, pas mis en file. Cette voie réactive les interruptions après chaque octet au lieu de préserver un état initialement désactivé.

Sur CGB, les écritures utilisent la banque VBK sélectionnée sans la modifier. Choisissez VBK 0 pour les tuiles de l’exemple. L’argument de banque sélectionne toujours la ROM source, même si VBK 1 est choisi en sortie. Le décodeur ne gère ni les palettes CGB ni les attributs de carte.

Pendant une lecture avec banque, ne laissez pas une interruption changer le mappage ROM ni réutiliser la routine de copie distante : ces fonctions partagent leur suivi de banque. Placez le décodeur à pointeur ordinaire et sa source dans des mappages compatibles ; le passage à la banque de la fonction ne doit pas masquer le pointeur source ordinaire.

rle_decode et rle_decode_far sont déclarées dans rpg.h et implémentées dans lib/rle.c. Compilez cette implémentation une seule fois avec le jeu. L’exemple complet inclut rle.c après #pragma bank 0 pour garder appelant et décodeurs dans la banque commune ; ne passez pas aussi rle.c comme entrée séparée pour cet exemple. L’intrinsèque __rle_decode_vram ne nécessite aucun fichier d’implémentation de bibliothèque.

Exemple d’utilisation

c
vram_count=__rle_decode_vram(0x8860,2,banked_rle); // Expand directly to 48 bytes of tile data.
if(*rle_bank_marker!=0xA1)failures++;

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple décompresse le flux de la banque 2 directement en VRAM à 0x8860, sans tampon RAM de sortie. DIRECT VRAM affiche 048. La rangée inférieure est bleue sur CGB et doit présenter les trois mêmes motifs que les rangées passées par la RAM. Ce programme pédagogique effectue le transfert avec le LCD éteint.

Chaque rangée contient de gauche à droite un carré plein de 8×8, un contour de 8×8 épais d’un pixel et un rectangle plein de 4×8 occupant la moitié gauche d’une tuile de 8×8. Ils sont aux colonnes de tuiles 2, 5 et 8 (x = 16, 40 et 64 pixels). Les rangées commencent à y = 32, 72 et 112 pixels. Les formes sont noires sur DMG ; les rangées CGB sont rouges, vertes et bleues. FAILED doit afficher 000. Les nombres comptent des octets : trois tuiles GB de 16 octets nécessitent 48 octets décompressés.

La matrice de validation couvre 42 cas : flux vide, répétition d’un octet, données nulles, répétition de 255 octets, sortie de 510 octets franchissant une page et données encodées franchissant une page source. Elle compare les 528 octets observés de destination et de garde, la longueur produite et le mappage ROM restauré. Les cas VRAM directe incluent LCD allumé/éteint sur DMG et CGB, ainsi que VBK 1 sur CGB. Les deux écrans pédagogiques sont contrôlés après 240 images émulées. Les ressources invalides, l’utilisation concurrente par interruption et le matériel réel ne sont pas couverts.

__rle_decode_vram: DMG
DMG
__rle_decode_vram: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rle_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rle_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/rpg.h:191

Notes de déclaration (texte source)
Compiler intrinsic, separate from the RAM rle_decode helpers below; it is not a bounded-stream API.

kitaqgb/kitaqgb/CodeGenerator.cs:8673

csharp
if (funcName == "__rle_decode_vram")
            {
                if (args.Length != 3) Error(expr, "__rle_decode_vram(dst, bank, src) expects 3 arguments");

                CompileIntoHL(args[0]); EmitAsm("PUSH_HL"); // dst
                CompileIntoA(args[1]); EmitPushAAsWord(); // bank
                CompileIntoHL(args[2]); EmitAsm("PUSH_HL"); // src
                EmitAsm("ADD_SP_IMM", new AsmOperand(-4, AddressMode.Relative)); // total(2) + scratch(2)

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("XOR_A");
                EmitAsm("LD_HL_A");
                EmitAsm("INC_HL");
                EmitAsm("LD_HL_A");

                AsmOperand loop = MakeUniqueLabel("rlevram_loop");
                // Keep total, scratch, source, bank and destination in stack slots across repeated far-memory calls.
                AsmOperand countDone = MakeUniqueLabel("rlevram_count_inc_done");
                AsmOperand valueDone = MakeUniqueLabel("rlevram_value_inc_done");
                AsmOperand finished = MakeUniqueLabel("rlevram_finished");

                EmitLabel(loop);

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(2, AddressMode.Relative));
                EmitAsm("PUSH_HL"); // &scratch

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(6, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_E_A");
                EmitAsm("INC_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("LD_D_A"); // src

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(8, AddressMode.Relative));
                EmitAsm("LD_A_HL"); // bank

                EmitAsm("POP_HL"); // &scratch
                EmitAsm("LD_BC_IMM", new AsmOperand(1, AddressMode.Immediate16));
                EmitCallFarMemcpyHelper();

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(2, AddressMode.Relative));
                EmitAsm("LD_A_HL"); // count
                EmitAsm("OR_A");
                EmitAsm("JP_Z", finished);
                EmitAsm("LD_HL_SP_IMM", new AsmOperand(3, AddressMode.Relative));
                EmitAsm("LD_HL_A"); // save count in scratch+1

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("ADD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
                EmitAsm("LD_HL_A");
                EmitAsm("JR_NC", countDone);
                EmitAsm("INC_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("ADC_IMM", new AsmOperand(0, AddressMode.Immediate));
                EmitAsm("LD_HL_A");
                EmitLabel(countDone);

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(2, AddressMode.Relative));
                EmitAsm("PUSH_HL"); // &scratch

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(6, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_E_A");
                EmitAsm("INC_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("LD_D_A"); // src

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(8, AddressMode.Relative));
                EmitAsm("LD_A_HL"); // bank

                EmitAsm("POP_HL"); // &scratch
                EmitAsm("LD_BC_IMM", new AsmOperand(1, AddressMode.Immediate16));
                EmitCallFarMemcpyHelper();

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(2, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_D_A"); // value

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(3, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_C_A");
                EmitAsm("LD_B_IMM", new AsmOperand(0, AddressMode.Immediate)); // count

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(8, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_E_A");
                EmitAsm("INC_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("LD_D_A");
                EmitAsm("LD_H_D");
                EmitAsm("LD_L_E"); // dst
                // Loading dst used D for its high byte. Recover the run value
                // from stack scratch while preserving the completed HL pointer.
                EmitAsm("PUSH_HL");
                EmitAsm("LD_HL_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_D_A");
                EmitAsm("POP_HL");

                // Use the LCD-aware fill loop for each decoded run, then store its advanced destination pointer.
                EmitVramMemsetLoop(true, "rlevram_fill");

                EmitAsm("PUSH_HL");
                EmitAsm("LD_HL_SP_IMM", new AsmOperand(10, AddressMode.Relative));
                EmitAsm("POP_DE");
                EmitAsm("LD_A_E");
                EmitAsm("LD_HL_A");
                EmitAsm("INC_HL");
                EmitAsm("LD_A_D");
                EmitAsm("LD_HL_A"); // save dst

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(3, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_B_A"); // count
                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("ADD_B");
                EmitAsm("LD_HL_A");
                EmitAsm("INC_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("ADC_IMM", new AsmOperand(0, AddressMode.Immediate));
                EmitAsm("LD_HL_A"); // total += count

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("ADD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
                EmitAsm("LD_HL_A");
                EmitAsm("JR_NC", valueDone);
                EmitAsm("INC_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("ADC_IMM", new AsmOperand(0, AddressMode.Immediate));
                EmitAsm("LD_HL_A");
                EmitLabel(valueDone);

                EmitAsm("JP", loop);

                EmitLabel(finished);
                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_E_A");
                EmitAsm("INC_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("LD_D_A");
                EmitAsm("LD_H_D");
                EmitAsm("LD_L_E");
                EmitAsm("ADD_SP_IMM", new AsmOperand(10, AddressMode.Relative));
                LastCallReturnsHL = true;
                return;
            }
__rng8

Fait avancer une fois le générateur pseudo-aléatoire GB partagé et renvoie son nouvel octet haut. Utilisez cette intrinsèque pour obtenir un octet sans lier les fonctions d’enrobage de rng.c.

Syntaxe

c
u8 __rng8();

Valeur renvoyée

Un résultat u8 (0–255), après un avancement du générateur partagé. Cela décrit le type et les bornes, pas une équiprobabilité de chaque octet.

Conditions et limites

L’étape GB ajoute 0x5D17 à l’état de 16 bits avec débordement circulaire, applique XOR entre l’octet bas mis à jour et l’octet haut, puis ajoute cet octet bas à l’octet haut tourné d’un bit vers la gauche. Elle stocke les deux octets modifiés et renvoie l’octet haut.

L’état zéro est accepté. Au prochain tirage d’octet, le générateur GB le remplace par 0x5AA5 avant d’avancer ; __rng_seed(0) suivi de __rng8() renvoie donc 122. Réinitialiser sans cesse la graine redémarre la suite au lieu d’ajouter du hasard.

Ces appels partagent un état persistant. La reproduction exige la même cible, la même graine et le même ordre d’appels, y compris ceux des autres fonctions auxiliaires. Gardez réglage de graine et tirages dans un même contexte d’exécution ; ces vérifications ne prouvent pas la sûreté d’un accès concurrent depuis une interruption.

GB et FC utilisent des algorithmes différents et ne garantissent pas la même suite avec la même graine. Ce sont des générateurs déterministes pour le jeu, pas des sources d’aléa cryptographique. Les tests vérifient le comportement de l’implémentation, pas sa qualité statistique ni sa période.

Incluez rpg.h et compilez lib/rng.c avec le jeu pour utiliser les fonctions de bibliothèque. Les intrinsèques __rng_seed et __rng8 sont émises par le compilateur ; un programme limité à ces deux fonctions n’a pas besoin d’objet rng.c.

Exemple d’utilisation

c
__rng_seed(0x1234);
first=__rng8();  // 2: the first draw from this seed on GB.
second=__rng8(); // 34: a second draw advances the same shared state.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple montre que chaque tirage fait avancer la suite. Après la graine 0x1234, les deux premiers appels __rng8 affichent FIRST 00002 et SECOND 00034. Répéter la graine fait repartir à 00002.

Le programme complet affiche les résultats décimaux sur fond blanc : texte noir sur DMG, bleu sur CGB. FAILED doit indiquer 00000. Les images montrent la sortie réelle de cet exemple dans l’émulateur.

La validation a comparé, pour chaque cas, 32 sorties et huit tirages d’octet suivants à un modèle hôte indépendant : 93 cas GB et 18 cas FC, avec graines constantes ou calculées à l’exécution et limites GB de plage, chance et poids. Les écrans pédagogiques ont été contrôlés après 240 images émulées. Le matériel réel et la qualité statistique n’ont pas été testés.

__rng8: DMG
DMG
__rng8: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rng_values.c .\kitaqgb\lib\rng.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rng_values.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/rpg.h:149

kitaqgb/kitaqgb/CodeGenerator.cs:8881

csharp
if (funcName == "__rng8")
            {
                if (args.Length != 0) Error(expr, "__rng8() expects 0 arguments");

                EmitLoadA(RngStateLoVar);
                EmitAsm("LD_B_A");
                EmitLoadA(RngStateHiVar);
                EmitAsm("LD_C_A");

                // Zero state fallback for deterministic startup.
                AsmOperand rngSeeded = MakeUniqueLabel("rng_seeded");
                EmitAsm("LD_A_B");
                EmitAsm("OR_C");
                EmitAsm("JR_NZ", rngSeeded);
                EmitAsm("LD_A_IMM", new AsmOperand(0xA5, AddressMode.Immediate));
                EmitAsm("LD_B_A");
                EmitAsm("LD_A_IMM", new AsmOperand(0x5A, AddressMode.Immediate));
                EmitAsm("LD_C_A");
                EmitLabel(rngSeeded);

                // 16-bit mixed add/xor/rotate step.
                EmitAsm("LD_A_B");
                EmitAsm("ADD_A_IMM", new AsmOperand(0x17, AddressMode.Immediate));
                EmitAsm("LD_B_A");
                EmitAsm("LD_A_C");
                EmitAsm("ADC_IMM", new AsmOperand(0x5D, AddressMode.Immediate));
                EmitAsm("LD_C_A");
                EmitAsm("LD_A_B");
                EmitAsm("XOR_C");
                EmitAsm("LD_B_A");
                EmitAsm("LD_A_C");
                EmitAsm("RLCA");
                EmitAsm("ADD_B");
                EmitAsm("LD_C_A");

                EmitAsm("LD_A_B");
                EmitStoreA(RngStateLoVar);
                EmitAsm("LD_A_C");
                EmitStoreA(RngStateHiVar);
                EmitAsm("LD_A_C");
                return;
            }
__rng_seed

Remplace l’état de 16 bits du générateur GB partagé. Utilisez une graine fixe pour rejouer la même suite pendant le débogage. Le réglage de la graine ne tire aucune valeur.

Syntaxe

c
void __rng_seed(u16 seed);

Paramètres

seed

Graine non signée de 16 bits, de 0 à 65535. Les deux octets sont stockés tels quels. Choisissez et notez la graine avant la suite de tirages de jeu à reproduire.

Valeur renvoyée

Aucune (void).

Conditions et limites

L’état zéro est accepté. Au prochain tirage d’octet, le générateur GB le remplace par 0x5AA5 avant d’avancer ; __rng_seed(0) suivi de __rng8() renvoie donc 122. Réinitialiser sans cesse la graine redémarre la suite au lieu d’ajouter du hasard.

Ces appels partagent un état persistant. La reproduction exige la même cible, la même graine et le même ordre d’appels, y compris ceux des autres fonctions auxiliaires. Gardez réglage de graine et tirages dans un même contexte d’exécution ; ces vérifications ne prouvent pas la sûreté d’un accès concurrent depuis une interruption.

GB et FC utilisent des algorithmes différents et ne garantissent pas la même suite avec la même graine. Ce sont des générateurs déterministes pour le jeu, pas des sources d’aléa cryptographique. Les tests vérifient le comportement de l’implémentation, pas sa qualité statistique ni sa période.

Incluez rpg.h et compilez lib/rng.c avec le jeu pour utiliser les fonctions de bibliothèque. Les intrinsèques __rng_seed et __rng8 sont émises par le compilateur ; un programme limité à ces deux fonctions n’a pas besoin d’objet rng.c.

Exemple d’utilisation

c
__rng_seed(0x1234);
repeated=__rng8(); // Reseeding reproduces the first byte, 2.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple montre la réinitialisation d’une suite de test. Avec la graine 0x1234, FIRST et RESEEDED affichent 00002, SECOND affiche 00034. La graine de bibliothèque réinitialise le même état ; RNG8 affiche donc aussi 00002.

Le programme complet affiche les résultats décimaux sur fond blanc : texte noir sur DMG, bleu sur CGB. FAILED doit indiquer 00000. Les images montrent la sortie réelle de cet exemple dans l’émulateur.

La validation a comparé, pour chaque cas, 32 sorties et huit tirages d’octet suivants à un modèle hôte indépendant : 93 cas GB et 18 cas FC, avec graines constantes ou calculées à l’exécution et limites GB de plage, chance et poids. Les écrans pédagogiques ont été contrôlés après 240 images émulées. Le matériel réel et la qualité statistique n’ont pas été testés.

__rng_seed: DMG
DMG
__rng_seed: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rng_values.c .\kitaqgb\lib\rng.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rng_values.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/rpg.h:150

kitaqgb/kitaqgb/CodeGenerator.cs:8868

csharp
if (funcName == "__rng_seed")
            {
                if (args.Length != 1) Error(expr, "__rng_seed(seed) expects 1 argument");

                CompileIntoHL(args[0]);
                EmitAsm("LD_A_L");
                EmitStoreA(RngStateLoVar);
                EmitAsm("LD_A_H");
                EmitStoreA(RngStateHiVar);
                return;
            }
__scroll_bg_set

Écrit immédiatement SCX/SCY du fond et synchronise les valeurs logicielles courantes et en attente.

Syntaxe

c
void __scroll_bg_set(u8 scx, u8 scy);

Paramètres

scx

Défilement horizontal de 0 à 255 pixels.

scy

Défilement vertical de 0 à 255 pixels.

Valeur renvoyée

Aucune valeur de retour.

Conditions et limites

Efface le drapeau de mise à jour du fond en attente. Une écriture en cours de trame affecte le balayage suivant ; appelez normalement pendant VBlank.

Exemple d’utilisation

c
__scroll_bg_set(19,27);result[5]=*((u8*)0xFF43);result[6]=*((u8*)0xFF42);__scroll_bg_set(0,0);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Découvrez sélection de carte, défilement, imbrication et attentes de ligne. Les bases valent 38912/39936 ; les jetons imbriqués, 0/1 ; les LY cibles observés, 80/144. Vérifiez aussi que les attentes reviennent avec le LCD éteint.

__scroll_bg_set: DMG
DMG
__scroll_bg_set: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_display.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_display.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implémentation et source

kitaqgb/lib/rpg.h:151

kitaqgb/kitaqgb/CodeGenerator.cs:7507

csharp
if (funcName == "__scroll_bg_set")
            {
                if (args.Length != 2) Error(expr, "__scroll_bg_set(scx, scy) expects 2 arguments");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitPushAAsWord();
                CompileIntoA(args[1]);
                EmitAsm("LD_B_A");
                EmitAsm("POP_HL");

                EmitAsm("LD_A_L");
                EmitStoreA(ScrollBgXCurVar);
                EmitStoreA(ScrollBgXNextVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem)); // SCX

                EmitAsm("LD_A_B");
                EmitStoreA(ScrollBgYCurVar);
                EmitStoreA(ScrollBgYNextVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem)); // SCY

                EmitLoadA(ScrollDirtyVar);
                EmitAsm("AND_IMM", new AsmOperand(0xFF ^ ScrollDirtyBgMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__settile_col

Copie verticalement len numéros de tuile consécutifs de src, à partir de (x,y) dans la carte BG sélectionnée par LCDC. La source avance d’un octet et la destination de 32 par tuile.

Attend si nécessaire que la VRAM soit accessible en écriture. LCD actif, le chemin masque les interruptions et peut les réactiver ensuite ; il ne garantit ni l’état d’interruption initial ni une fin dans un seul VBlank.

Syntaxe

c
void __settile_col(u8 x, u8 y, const u8* src, u8 len);

Paramètres

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

src

Octets contigus en RAM ou dans une banque ROM visible. Le flux de tuiles contient des numéros ; celui d’attributs contient les attributs CGB. La copie est immédiate et le pointeur n’est pas conservé.

len

Nombre d’octets, de 0 à 255. Zéro ne copie rien. La source doit contenir au moins autant d’octets lisibles.

Valeur renvoyée

Aucune (void).

Conditions et limites

Les octets sont écrits dans la banque VRAM sélectionnée. Sur CGB, choisissez la banque 0 pour les numéros de tuile ; cet appel ne change pas de banque.

Pour une copie horizontale, respectez x+len <= 32 ; pour une copie verticale, y+len <= 32. Les coordonnées ne rebouclent pas au bord de la carte.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settile_col
// Copy `len` successive tile numbers from `src` vertically, starting at `(x,y)` in the
// LCDC-selected BG map. The source advances one byte and the destination 32 bytes per
// tile. Waits for writable VRAM when necessary. The LCD-on path uses interrupt masking
// and can enable interrupts afterward; it is not a promise to preserve the caller's
// interrupt state or to finish within one VBlank.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
const u8 example_tiles[] = {'A','B','C'};

void main() {
    u8 failures;
    failures = 0;
    tile_color_example_begin();
    __settile_col(2, 3, example_tiles, 3);
    if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
    if (tile_example_read(0x9CA2, 0) != 'C') { failures++; }
    if (tile_example_read(0x9C63, 0) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, les caractères de l’exemple et le texte des résultats s’affichent en bleu sur fond blanc. La préparation attribue le bleu à la couleur de tracé de la palette 0, sans modifier les attributs de la carte de tuiles. Sur DMG, les mêmes caractères s’affichent en noir sur fond blanc.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
A
B
C
__settile_col: DMG
DMG
__settile_col: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settile_col.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settile_col.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/rpg.h:156

__settile_rect

Remplit un rectangle de w sur h dans la carte BG sélectionnée par LCDC avec un seul numéro de tuile. Le pas des lignes est de 32 octets ; la routine répète une tuile et ne copie pas un tableau d’images.

Attend si nécessaire que la VRAM soit accessible en écriture. LCD actif, le chemin masque les interruptions et peut les réactiver ensuite ; il ne garantit ni l’état d’interruption initial ni une fin dans un seul VBlank.

Syntaxe

c
void __settile_rect(u8 x, u8 y, u8 w, u8 h, u8 tile);

Paramètres

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

w, h

Largeur et hauteur en tuiles de 8×8. Respectez x+w <= 32 et y+h <= 32. Une dimension nulle n’effectue aucune écriture.

tile

Numéro de tuile, de 0 à 255. Le bit 4 de LCDC détermine l’adressage de son image. Ce n’est pas une adresse d’octet en VRAM.

Valeur renvoyée

Aucune (void).

Conditions et limites

Les octets sont écrits dans la banque VRAM sélectionnée. Sur CGB, choisissez la banque 0 pour les numéros de tuile ; cet appel ne change pas de banque.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settile_rect
// Fill a `w` by `h` rectangle in the LCDC-selected BG map with one tile number. Rows are
// 32 bytes apart; this repeats a tile, rather than copying an image array. Waits for
// writable VRAM when necessary. The LCD-on path uses interrupt masking and can enable
// interrupts afterward; it is not a promise to preserve the caller's interrupt state or
// to finish within one VBlank.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"

void main() {
    u8 failures;
    failures = 0;
    tile_color_example_begin();
    __settile_rect(2, 3, 3, 2, 'A');
    if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
    if (tile_example_read(0x9C84, 0) != 'A') { failures++; }
    if (tile_example_read(0x9C65, 0) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, les caractères de l’exemple et le texte des résultats s’affichent en bleu sur fond blanc. La préparation attribue le bleu à la couleur de tracé de la palette 0, sans modifier les attributs de la carte de tuiles. Sur DMG, les mêmes caractères s’affichent en noir sur fond blanc.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
AAA
AAA
__settile_rect: DMG
DMG
__settile_rect: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settile_rect.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settile_rect.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/rpg.h:154

__settile_row

Copie horizontalement len numéros de tuile consécutifs de src, à partir de (x,y) dans la carte BG sélectionnée par LCDC. Source et destination avancent d’un octet par tuile.

Attend si nécessaire que la VRAM soit accessible en écriture. LCD actif, le chemin masque les interruptions et peut les réactiver ensuite ; il ne garantit ni l’état d’interruption initial ni une fin dans un seul VBlank.

Syntaxe

c
void __settile_row(u8 x, u8 y, const u8* src, u8 len);

Paramètres

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

src

Octets contigus en RAM ou dans une banque ROM visible. Le flux de tuiles contient des numéros ; celui d’attributs contient les attributs CGB. La copie est immédiate et le pointeur n’est pas conservé.

len

Nombre d’octets, de 0 à 255. Zéro ne copie rien. La source doit contenir au moins autant d’octets lisibles.

Valeur renvoyée

Aucune (void).

Conditions et limites

Les octets sont écrits dans la banque VRAM sélectionnée. Sur CGB, choisissez la banque 0 pour les numéros de tuile ; cet appel ne change pas de banque.

Pour une copie horizontale, respectez x+len <= 32 ; pour une copie verticale, y+len <= 32. Les coordonnées ne rebouclent pas au bord de la carte.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settile_row
// Copy `len` successive tile numbers from `src` horizontally, starting at `(x,y)` in the
// LCDC-selected BG map. Source and destination advance by one byte per tile. Waits for
// writable VRAM when necessary. The LCD-on path uses interrupt masking and can enable
// interrupts afterward; it is not a promise to preserve the caller's interrupt state or
// to finish within one VBlank.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
const u8 example_tiles[] = {'A','B','C'};

void main() {
    u8 failures;
    failures = 0;
    tile_color_example_begin();
    __settile_row(2, 3, example_tiles, 3);
    if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
    if (tile_example_read(0x9C64, 0) != 'C') { failures++; }
    if (tile_example_read(0x9C82, 0) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, les caractères de l’exemple et le texte des résultats s’affichent en bleu sur fond blanc. La préparation attribue le bleu à la couleur de tracé de la palette 0, sans modifier les attributs de la carte de tuiles. Sur DMG, les mêmes caractères s’affichent en noir sur fond blanc.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
ABC
__settile_row: DMG
DMG
__settile_row: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settile_row.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settile_row.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/rpg.h:155

__settile_xy

Écrit le numéro de tuile d’une case de la carte ; sélectionne une image existante sans modifier ses pixels.

Le bit 3 de LCDC choisit la carte de fond : zéro donne 0x9800, un donne 0x9C00. On ajoute 32*y + x à cette base.

Attend si nécessaire que la VRAM soit accessible en écriture. LCD actif, le chemin masque les interruptions et peut les réactiver ensuite ; il ne garantit ni l’état d’interruption initial ni une fin dans un seul VBlank.

Syntaxe

c
void __settile_xy(u8 x, u8 y, u8 tile);

Paramètres

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

tile

Numéro de tuile, de 0 à 255. Le bit 4 de LCDC détermine l’adressage de son image. Ce n’est pas une adresse d’octet en VRAM.

Valeur renvoyée

Aucune (void).

Conditions et limites

Les octets sont écrits dans la banque VRAM sélectionnée. Sur CGB, choisissez la banque 0 pour les numéros de tuile ; cet appel ne change pas de banque.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settile_xy
// Write one tile-number byte to the tile map; this selects an existing tile image, not
// new pixel data. LCDC bit 3 selects the background map: zero uses `0x9800`, one uses
// `0x9C00`. The destination is that base plus `32*y + x`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"

void main() {
    u8 failures;
    failures = 0;
    tile_color_example_begin();
    __settile_xy(2, 3, 'A');
    if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
    if (tile_example_read(0x9862, 0) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, les caractères de l’exemple et le texte des résultats s’affichent en bleu sur fond blanc. La préparation attribue le bleu à la couleur de tracé de la palette 0, sans modifier les attributs de la carte de tuiles. Sur DMG, les mêmes caractères s’affichent en noir sur fond blanc.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
A
__settile_xy: DMG
DMG
__settile_xy: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settile_xy.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settile_xy.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/rpg.h:153

__settilemap_rect

Copie un tableau compact de w*h numéros de tuile dans un rectangle à (x,y) de la carte choisie par base. Les lignes sources ont w octets ; le pas des lignes de destination est de 32 octets.

Attend si nécessaire que la VRAM soit accessible en écriture. LCD actif, le chemin masque les interruptions et peut les réactiver ensuite ; il ne garantit ni l’état d’interruption initial ni une fin dans un seul VBlank.

Syntaxe

c
void __settilemap_rect(u16 base, u8 x, u8 y, u8 w, u8 h, const u8* src);

Paramètres

base

base choisit la carte (0x9800 ou 0x9C00) ; l’adresse de l’octet est base + 32*y + x.

x, y

Coordonnées en tuiles de 8×8, de 0 à 31 chacune. Toute l’écriture doit rester dans la carte de 32×32 ; les coordonnées ne sont pas bornées automatiquement.

w, h

Largeur et hauteur en tuiles de 8×8. Respectez x+w <= 32 et y+h <= 32. Une dimension nulle n’effectue aucune écriture.

src

Octets contigus en RAM ou dans une banque ROM visible. Le flux de tuiles contient des numéros ; celui d’attributs contient les attributs CGB. La copie est immédiate et le pointeur n’est pas conservé.

Valeur renvoyée

Aucune (void).

Conditions et limites

Les octets sont écrits dans la banque VRAM sélectionnée. Sur CGB, choisissez la banque 0 pour les numéros de tuile ; cet appel ne change pas de banque.

L’exemple utilise gb_common.h : m_init() charge la police ASCII fournie en utilisant les codes de caractères comme numéros de tuile. m_wait(); M_LCDC=0; arrête le LCD pendant VBlank avant les transferts sans attente. La valeur finale de LCDC choisit la carte visible.

Exemple d’utilisation

c
// __settilemap_rect
// Copy a tightly packed `w*h` array of tile numbers to a rectangle at `(x,y)` in the map
// selected by `base`. Source rows have `w` bytes; destination rows have a 32-byte
// stride. Waits for writable VRAM when necessary. The LCD-on path uses interrupt masking
// and can enable interrupts afterward; it is not a promise to preserve the caller's
// interrupt state or to finish within one VBlank.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
const u8 example_tiles[] = {'A','B','C','D','E','F'};

void main() {
    u8 failures;
    failures = 0;
    tile_color_example_begin();
    __settilemap_rect(0x9C00, 2, 3, 3, 2, example_tiles);
    if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
    if (tile_example_read(0x9C84, 0) != 'F') { failures++; }
    if (tile_example_read(0x9C65, 0) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

Résultat attendu

La carte choisie affiche les caractères écrits ou la zone remplie. Le programme relit les octets de tuiles et, sur CGB, les attributs ; FAILED CHECKS 000 indique que les comparaisons concordent. Les deux cartes sont initialement nulles et BG/fenêtre sélectionnent 0x9C00 avant l’appel, ce qui distingue la carte fixe de celle sélectionnée. Les écritures se font LCD arrêté ; cet exemple ne mesure pas les temporisations LCD allumé.

Sur CGB, les caractères de l’exemple et le texte des résultats s’affichent en bleu sur fond blanc. La préparation attribue le bleu à la couleur de tracé de la palette 0, sans modifier les attributs de la carte de tuiles. Sur DMG, les mêmes caractères s’affichent en noir sur fond blanc.

Image attendue : le motif ci-dessous commence aux coordonnées de tuile (x=2, y=3), soit au pixel (16,24). Chaque caractère occupe une tuile de 8×8. Lisez de haut en bas et de gauche à droite ; les tuiles voisines restent vides.

text
ABC
DEF
__settilemap_rect: DMG
DMG
__settilemap_rect: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilemap_rect.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilemap_rect.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implémentation et source

kitaqgb/lib/rpg.h:158

__tile_addr

Calcule l’adresse CPU d’une case dans une carte de 32 tuiles de large.

Syntaxe

c
u16 __tile_addr(u16 base, u8 x, u8 y);

Paramètres

base

Base de la carte de tuiles, généralement $9800/$9C00.

x

Colonne, généralement de 0 à 31.

y

Ligne, généralement de 0 à 31.

Valeur renvoyée

Renvoie (base + y*32 + x) modulo 65536 sans accéder à la mémoire.

Exemple d’utilisation

c
result[4]=__tile_addr(0x9800,3,2);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Découvrez sélection de carte, défilement, imbrication et attentes de ligne. Les bases valent 38912/39936 ; les jetons imbriqués, 0/1 ; les LY cibles observés, 80/144. Vérifiez aussi que les attentes reviennent avec le LCD éteint.

__tile_addr: DMG
DMG
__tile_addr: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_display.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_display.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implémentation et source

kitaqgb/lib/rpg.h:180

kitaqgb/kitaqgb/CodeGenerator.cs:8355

csharp
if (funcName == "__tile_addr")
            {
                if (args.Length != 3) Error(expr, "__tile_addr(base, x, y) expects 3 arguments");

                EmitTileMapAddressIntoHL(args[0], args[1], args[2]);
                LastCallReturnsHL = true;
                return;
            }
__vram_copy_dma

__vram_copy_dma est un alias du compilateur pour __vram_memcpy. Le compte reste en octets, avec le même transfert immédiat par CPU et les mêmes règles d’accès au LCD.

Copie immédiatement len octets de src vers dst par adresses croissantes. Revient à la fin du transfert, sans mise en file. Les deux adresses avancent d’un octet, sans pas de ligne de carte ni conversion de format graphique.

Syntaxe

c
void __vram_copy_dma(u16 dst, const void* src, u16 len);

Paramètres

dst

Adresse du premier octet de destination, normalement dans la VRAM 0x8000–0x9FFF. Toute la plage doit être valide. Le CGB utilise la banque courante ; dans la carte, la banque 0 contient les indices et la banque 1 les attributs. Ces routines ne choisissent pas la carte selon LCDC, ne sautent pas de ligne et n’allouent pas de VRAM.

src

Pointeur sur les octets source, valide et correctement mappé pendant tout l’appel. Les données sont lues immédiatement, sans conservation pour un flush ultérieur. La banque source n’est pas changée automatiquement. Utilisez des plages disjointes : cette copie vers l’avant n’offre pas la sémantique de memmove.

len

Nombre d’octets consécutifs, de 0 à 65535, pas de tuiles. Gardez toute la plage dans une source valide et une destination modifiable ; l’API ne vérifie pas les limites et n’empêche pas le bouclage d’adresse sur 16 bits.

Valeur renvoyée

Aucune (void).

Conditions et limites

Le nom ne sélectionne pas de moteur DMA et ne programme rien pour un HBlank ultérieur. L’appel attend la fin. LCD actif, le même polling CPU accepte le mode 0 (HBlank) ou 1 (VBlank) ; le HDMA du CGB n’est pas configuré.

LCD actif, chaque écriture attend le mode STAT 0 ou 1 sous DI, puis exécute EI. LCD éteint, un simple parcours CPU est utilisé. L’appel peut durer plusieurs images, ne préserve pas les interruptions désactivées à l’entrée et ne configure aucun registre DMA. Une longueur nulle ne transfère aucune donnée.

Exemple d’utilisation

c
__vram_copy_dma(0x98A2,strip,3); // This alias also uses CPU writes, not a DMA engine.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple utilise __vram_copy_dma pour écrire trois octets de carte à la tuile (2,5), soit au pixel (16,40). On attend [1,2,3] de gauche à droite dans une zone de 24 × 8, avec les cases voisines vides. Le programme complet place chaque copie ou remplissage testé sur sa propre ligne.

Les graphismes originaux fournis utilisent la tuile 1 pour un carré plein de 8 × 8, la 2 pour ses quatre colonnes gauches et la 3 pour un triangle aligné à gauche, élargi d’un pixel par ligne de 1 à 8. Le DMG utilise le noir sur blanc ; le CGB distingue les opérations par des couleurs en gardant le texte noir.

Le carré au pixel (16,88) doit subsister après les appels sûrs et sans attente de longueur nulle. LCD actif, la copie et le remplissage sûrs ajoutent des carrés en (16,96) et (32,96). FAILED CHECKS doit afficher 000. Les tests d’émulateur comparent les pixels et le compteur. Le CGB utilise ici la banque 0 ; les autres banques et le matériel physique ne sont pas testés.

Sur CGB, les lignes de MEMCPY à FILL UNSAFE répètent la séquence rouge, bleu, vert. Le carré de ZERO COUNT est rouge. Dans LCD ON, celui de gauche est bleu et celui de droite vert. Le texte reste noir. Les tests vérifient ces couleurs sur chaque pixel dessiné.

__vram_copy_dma: DMG
DMG
__vram_copy_dma: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\vram_memory_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_memory_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/rpg.h:163

__vram_copy_hblank

__vram_copy_hblank est un alias du compilateur pour __vram_memcpy. Le compte reste en octets, avec le même transfert immédiat par CPU et les mêmes règles d’accès au LCD.

Copie immédiatement len octets de src vers dst par adresses croissantes. Revient à la fin du transfert, sans mise en file. Les deux adresses avancent d’un octet, sans pas de ligne de carte ni conversion de format graphique.

Syntaxe

c
void __vram_copy_hblank(u16 dst, const void* src, u16 len);

Paramètres

dst

Adresse du premier octet de destination, normalement dans la VRAM 0x8000–0x9FFF. Toute la plage doit être valide. Le CGB utilise la banque courante ; dans la carte, la banque 0 contient les indices et la banque 1 les attributs. Ces routines ne choisissent pas la carte selon LCDC, ne sautent pas de ligne et n’allouent pas de VRAM.

src

Pointeur sur les octets source, valide et correctement mappé pendant tout l’appel. Les données sont lues immédiatement, sans conservation pour un flush ultérieur. La banque source n’est pas changée automatiquement. Utilisez des plages disjointes : cette copie vers l’avant n’offre pas la sémantique de memmove.

len

Nombre d’octets consécutifs, de 0 à 65535, pas de tuiles. Gardez toute la plage dans une source valide et une destination modifiable ; l’API ne vérifie pas les limites et n’empêche pas le bouclage d’adresse sur 16 bits.

Valeur renvoyée

Aucune (void).

Conditions et limites

Le nom ne sélectionne pas de moteur DMA et ne programme rien pour un HBlank ultérieur. L’appel attend la fin. LCD actif, le même polling CPU accepte le mode 0 (HBlank) ou 1 (VBlank) ; le HDMA du CGB n’est pas configuré.

LCD actif, chaque écriture attend le mode STAT 0 ou 1 sous DI, puis exécute EI. LCD éteint, un simple parcours CPU est utilisé. L’appel peut durer plusieurs images, ne préserve pas les interruptions désactivées à l’entrée et ne configure aucun registre DMA. Une longueur nulle ne transfère aucune donnée.

Exemple d’utilisation

c
__vram_copy_hblank(0x9882,strip,3); // Immediate safe copy; no scheduled HBlank DMA.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple utilise __vram_copy_hblank pour écrire trois octets de carte à la tuile (2,4), soit au pixel (16,32). On attend [1,2,3] de gauche à droite dans une zone de 24 × 8, avec les cases voisines vides. Le programme complet place chaque copie ou remplissage testé sur sa propre ligne.

Les graphismes originaux fournis utilisent la tuile 1 pour un carré plein de 8 × 8, la 2 pour ses quatre colonnes gauches et la 3 pour un triangle aligné à gauche, élargi d’un pixel par ligne de 1 à 8. Le DMG utilise le noir sur blanc ; le CGB distingue les opérations par des couleurs en gardant le texte noir.

Le carré au pixel (16,88) doit subsister après les appels sûrs et sans attente de longueur nulle. LCD actif, la copie et le remplissage sûrs ajoutent des carrés en (16,96) et (32,96). FAILED CHECKS doit afficher 000. Les tests d’émulateur comparent les pixels et le compteur. Le CGB utilise ici la banque 0 ; les autres banques et le matériel physique ne sont pas testés.

Sur CGB, les lignes de MEMCPY à FILL UNSAFE répètent la séquence rouge, bleu, vert. Le carré de ZERO COUNT est rouge. Dans LCD ON, celui de gauche est bleu et celui de droite vert. Le texte reste noir. Les tests vérifient ces couleurs sur chaque pixel dessiné.

__vram_copy_hblank: DMG
DMG
__vram_copy_hblank: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\vram_memory_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_memory_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/rpg.h:162

__vram_fill

__vram_fill est un alias du compilateur pour __vram_memset. Le compte reste en octets, avec le même transfert immédiat par CPU et les mêmes règles d’accès au LCD.

Remplit immédiatement len octets consécutifs à partir de dst avec l’octet value. Répète un octet, pas un motif graphique complet. Dans une carte, cela répète un indice de tuile ; dans les motifs, cela modifie les plans de bits des pixels.

Syntaxe

c
void __vram_fill(u16 dst, u8 value, u16 len);

Paramètres

dst

Adresse du premier octet de destination, normalement dans la VRAM 0x8000–0x9FFF. Toute la plage doit être valide. Le CGB utilise la banque courante ; dans la carte, la banque 0 contient les indices et la banque 1 les attributs. Ces routines ne choisissent pas la carte selon LCDC, ne sautent pas de ligne et n’allouent pas de VRAM.

value

Un octet de 0 à 255 répété. Son effet visible dépend du contenu de destination : indices de tuiles, plans de pixels ou autre structure d’octets.

len

Nombre d’octets consécutifs, de 0 à 65535, pas de tuiles. Gardez toute la plage dans une source valide et une destination modifiable ; l’API ne vérifie pas les limites et n’empêche pas le bouclage d’adresse sur 16 bits.

Valeur renvoyée

Aucune (void).

Conditions et limites

LCD actif, chaque écriture attend le mode STAT 0 ou 1 sous DI, puis exécute EI. LCD éteint, un simple parcours CPU est utilisé. L’appel peut durer plusieurs images, ne préserve pas les interruptions désactivées à l’entrée et ne configure aucun registre DMA. Une longueur nulle ne transfère aucune donnée.

Exemple d’utilisation

c
__vram_fill(0x9902,1,computed_length());

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple utilise __vram_fill pour remplir trois octets de carte à partir de la tuile (2,8), pixel (16,64), avec l’indice 1. On attend une barre pleine de 24 × 8, entourée de cases vides. Le compte provient de variables ou du résultat d’une fonction ; les trois octets doivent bien recevoir la valeur 1.

Les graphismes originaux fournis utilisent la tuile 1 pour un carré plein de 8 × 8, la 2 pour ses quatre colonnes gauches et la 3 pour un triangle aligné à gauche, élargi d’un pixel par ligne de 1 à 8. Le DMG utilise le noir sur blanc ; le CGB distingue les opérations par des couleurs en gardant le texte noir.

Le carré au pixel (16,88) doit subsister après les appels sûrs et sans attente de longueur nulle. LCD actif, la copie et le remplissage sûrs ajoutent des carrés en (16,96) et (32,96). FAILED CHECKS doit afficher 000. Les tests d’émulateur comparent les pixels et le compteur. Le CGB utilise ici la banque 0 ; les autres banques et le matériel physique ne sont pas testés.

Sur CGB, les lignes de MEMCPY à FILL UNSAFE répètent la séquence rouge, bleu, vert. Le carré de ZERO COUNT est rouge. Dans LCD ON, celui de gauche est bleu et celui de droite vert. Le texte reste noir. Les tests vérifient ces couleurs sur chaque pixel dessiné.

__vram_fill: DMG
DMG
__vram_fill: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\vram_memory_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_memory_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/rpg.h:161

__vram_memcpy

Copie immédiatement len octets de src vers dst par adresses croissantes. Revient à la fin du transfert, sans mise en file. Les deux adresses avancent d’un octet, sans pas de ligne de carte ni conversion de format graphique.

Syntaxe

c
void __vram_memcpy(u16 dst, const void* src, u16 len);

Paramètres

dst

Adresse du premier octet de destination, normalement dans la VRAM 0x8000–0x9FFF. Toute la plage doit être valide. Le CGB utilise la banque courante ; dans la carte, la banque 0 contient les indices et la banque 1 les attributs. Ces routines ne choisissent pas la carte selon LCDC, ne sautent pas de ligne et n’allouent pas de VRAM.

src

Pointeur sur les octets source, valide et correctement mappé pendant tout l’appel. Les données sont lues immédiatement, sans conservation pour un flush ultérieur. La banque source n’est pas changée automatiquement. Utilisez des plages disjointes : cette copie vers l’avant n’offre pas la sémantique de memmove.

len

Nombre d’octets consécutifs, de 0 à 65535, pas de tuiles. Gardez toute la plage dans une source valide et une destination modifiable ; l’API ne vérifie pas les limites et n’empêche pas le bouclage d’adresse sur 16 bits.

Valeur renvoyée

Aucune (void).

Conditions et limites

LCD actif, chaque écriture attend le mode STAT 0 ou 1 sous DI, puis exécute EI. LCD éteint, un simple parcours CPU est utilisé. L’appel peut durer plusieurs images, ne préserve pas les interruptions désactivées à l’entrée et ne configure aucun registre DMA. Une longueur nulle ne transfère aucune donnée.

Exemple d’utilisation

c
__vram_memcpy(0x9842,strip,length_base+length_extra); // Three map bytes at tile (2,2).

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple utilise __vram_memcpy pour écrire trois octets de carte à la tuile (2,2), soit au pixel (16,16). On attend [1,2,3] de gauche à droite dans une zone de 24 × 8, avec les cases voisines vides. Le programme complet place chaque copie ou remplissage testé sur sa propre ligne.

Les graphismes originaux fournis utilisent la tuile 1 pour un carré plein de 8 × 8, la 2 pour ses quatre colonnes gauches et la 3 pour un triangle aligné à gauche, élargi d’un pixel par ligne de 1 à 8. Le DMG utilise le noir sur blanc ; le CGB distingue les opérations par des couleurs en gardant le texte noir.

Le carré au pixel (16,88) doit subsister après les appels sûrs et sans attente de longueur nulle. LCD actif, la copie et le remplissage sûrs ajoutent des carrés en (16,96) et (32,96). FAILED CHECKS doit afficher 000. Les tests d’émulateur comparent les pixels et le compteur. Le CGB utilise ici la banque 0 ; les autres banques et le matériel physique ne sont pas testés.

Sur CGB, les lignes de MEMCPY à FILL UNSAFE répètent la séquence rouge, bleu, vert. Le carré de ZERO COUNT est rouge. Dans LCD ON, celui de gauche est bleu et celui de droite vert. Le texte reste noir. Les tests vérifient ces couleurs sur chaque pixel dessiné.

__vram_memcpy: DMG
DMG
__vram_memcpy: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\vram_memory_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_memory_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/rpg.h:146

__vram_memset

Remplit immédiatement len octets consécutifs à partir de dst avec l’octet value. Répète un octet, pas un motif graphique complet. Dans une carte, cela répète un indice de tuile ; dans les motifs, cela modifie les plans de bits des pixels.

Syntaxe

c
void __vram_memset(u16 dst, u8 value, u16 len);

Paramètres

dst

Adresse du premier octet de destination, normalement dans la VRAM 0x8000–0x9FFF. Toute la plage doit être valide. Le CGB utilise la banque courante ; dans la carte, la banque 0 contient les indices et la banque 1 les attributs. Ces routines ne choisissent pas la carte selon LCDC, ne sautent pas de ligne et n’allouent pas de VRAM.

value

Un octet de 0 à 255 répété. Son effet visible dépend du contenu de destination : indices de tuiles, plans de pixels ou autre structure d’octets.

len

Nombre d’octets consécutifs, de 0 à 65535, pas de tuiles. Gardez toute la plage dans une source valide et une destination modifiable ; l’API ne vérifie pas les limites et n’empêche pas le bouclage d’adresse sur 16 bits.

Valeur renvoyée

Aucune (void).

Conditions et limites

LCD actif, chaque écriture attend le mode STAT 0 ou 1 sous DI, puis exécute EI. LCD éteint, un simple parcours CPU est utilisé. L’appel peut durer plusieurs images, ne préserve pas les interruptions désactivées à l’entrée et ne configure aucun registre DMA. Une longueur nulle ne transfère aucune donnée.

Exemple d’utilisation

c
__vram_memset(0x98E2,1,length_base+length_extra); // Three solid tiles, not three tile patterns.

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple utilise __vram_memset pour remplir trois octets de carte à partir de la tuile (2,7), pixel (16,56), avec l’indice 1. On attend une barre pleine de 24 × 8, entourée de cases vides. Le compte provient de variables ou du résultat d’une fonction ; les trois octets doivent bien recevoir la valeur 1.

Les graphismes originaux fournis utilisent la tuile 1 pour un carré plein de 8 × 8, la 2 pour ses quatre colonnes gauches et la 3 pour un triangle aligné à gauche, élargi d’un pixel par ligne de 1 à 8. Le DMG utilise le noir sur blanc ; le CGB distingue les opérations par des couleurs en gardant le texte noir.

Le carré au pixel (16,88) doit subsister après les appels sûrs et sans attente de longueur nulle. LCD actif, la copie et le remplissage sûrs ajoutent des carrés en (16,96) et (32,96). FAILED CHECKS doit afficher 000. Les tests d’émulateur comparent les pixels et le compteur. Le CGB utilise ici la banque 0 ; les autres banques et le matériel physique ne sont pas testés.

Sur CGB, les lignes de MEMCPY à FILL UNSAFE répètent la séquence rouge, bleu, vert. Le carré de ZERO COUNT est rouge. Dans LCD ON, celui de gauche est bleu et celui de droite vert. Le texte reste noir. Les tests vérifient ces couleurs sur chaque pixel dessiné.

__vram_memset: DMG
DMG
__vram_memset: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\vram_memory_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_memory_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/rpg.h:147

__wait_vblank

LCD actif, attend la fin de tout VBlank courant puis le prochain LY≥144.

Syntaxe

c
void __wait_vblank();

Valeur renvoyée

Aucune valeur de retour.

Conditions et limites

Renvoie immédiatement si le LCD est éteint. Interroge LY sans activer les interruptions ni avancer le compteur de trames d’une bibliothèque.

Exemple d’utilisation

c
__wait_vblank();result[10]=*((u8*)0xFF44);M_LCDC=0;__wait_vblank();__wait_ly(200);result[11]=1;

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Découvrez sélection de carte, défilement, imbrication et attentes de ligne. Les bases valent 38912/39936 ; les jetons imbriqués, 0/1 ; les LY cibles observés, 80/144. Vérifiez aussi que les attentes reviennent avec le LCD éteint.

__wait_vblank: DMG
DMG
__wait_vblank: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_display.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_display.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implémentation et source

kitaqgb/lib/rpg.h:138

kitaqgb/kitaqgb/CodeGenerator.cs:7451

csharp
if (funcName == "__wait_vblank")
            {
                if (args.Length != 0) Error(expr, "__wait_vblank() expects 0 arguments");

                AsmOperand wbRet = MakeUniqueLabel("waitvb_ret");
                AsmOperand wbExitVblank = MakeUniqueLabel("waitvb_exit");
                AsmOperand wbEnterVblank = MakeUniqueLabel("waitvb_enter");

                // If LCD is off, return immediately.
                EmitAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem)); // LCDC
                EmitAsm("AND_IMM", new AsmOperand(0x80, AddressMode.Immediate));
                EmitAsm("JR_Z", wbRet);

                // while (LY >= 144) {}
                EmitLabel(wbExitVblank);
                EmitAsm("LDH_A_MEM", new AsmOperand(0x44, AddressMode.HighMem)); // LY
                EmitAsm("CP_IMM", new AsmOperand(144, AddressMode.Immediate));
                EmitAsm("JR_C", wbEnterVblank);
                EmitAsm("JR", wbExitVblank);

                // while (LY < 144) {}
                EmitLabel(wbEnterVblank);
                EmitAsm("LDH_A_MEM", new AsmOperand(0x44, AddressMode.HighMem)); // LY
                EmitAsm("CP_IMM", new AsmOperand(144, AddressMode.Immediate));
                EmitAsm("JR_C", wbEnterVblank);

                EmitLabel(wbRet);
                return;
            }
__xy_in_rect

Teste l’appartenance à un rectangle, bords supérieur/gauche inclus et inférieur/droit exclus.

Syntaxe

c
u8 __xy_in_rect(u8 x, u8 y, u8 rx, u8 ry, u8 rw, u8 rh);

Paramètres

x

X du point, de 0 à 255.

y

Y du point, de 0 à 255.

rx

Bord gauche du rectangle, de 0 à 255.

ry

Bord supérieur du rectangle, de 0 à 255.

rw

Largeur de 0 à 255.

rh

Hauteur de 0 à 255.

Valeur renvoyée

Renvoie 1 à l’intérieur, sinon 0. Une largeur ou une hauteur nulle donne un rectangle vide.

Conditions et limites

Utilise les différences de coordonnées : un bord droit supérieur à 255 ne reboucle pas vers la gauche.

Exemple d’utilisation

c
result[0]=__xy_in_rect(10,20,10,20,3,2);result[1]=__xy_in_rect(13,20,10,20,3,2);result[2]=__xy_in_rect(9,20,10,20,3,2);result[3]=__xy_in_rect(255,1,250,0,10,2);result[4]=__xy_in_rect(10,20,10,20,0,2);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Testez intérieur, limites, point extérieur et largeur nulle : INSIDE=1, RIGHT EDGE=0, LEFT EDGE=0, HIGH X=1. Vérifiez la distance 15, la distance rebouclée 254 et les indices de tableau 64/65280.

__xy_in_rect: DMG
DMG
__xy_in_rect: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_coordinates.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_coordinates.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implémentation et source

kitaqgb/lib/rpg.h:182

kitaqgb/kitaqgb/CodeGenerator.cs:8436

csharp
if (funcName == "__xy_in_rect")
            {
                if (args.Length != 6) Error(expr, "__xy_in_rect(x, y, rx, ry, rw, rh) expects 6 arguments");

                Expr[] foldedRectArgs = args.Select(FoldConstants).ToArray();
                if (foldedRectArgs[0].Match(Tag.Integer, out int constX) &&
                    foldedRectArgs[1].Match(Tag.Integer, out int constY) &&
                    foldedRectArgs[2].Match(Tag.Integer, out int constRx) &&
                    foldedRectArgs[3].Match(Tag.Integer, out int constRy) &&
                    foldedRectArgs[4].Match(Tag.Integer, out int constRw) &&
                    foldedRectArgs[5].Match(Tag.Integer, out int constRh))
                {
                    int x = constX & 0xFF;
                    int y = constY & 0xFF;
                    int rx = constRx & 0xFF;
                    int ry = constRy & 0xFF;
                    int rw = constRw & 0xFF;
                    int rh = constRh & 0xFF;
                    bool inside = x >= rx && y >= ry && (x - rx) < rw && (y - ry) < rh;
                    if (inside) EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
                    else EmitAsm("XOR_A");
                    return;
                }

                EmitPushExprAsWord(args[0]); // x
                EmitPushExprAsWord(args[1]); // y
                EmitPushExprAsWord(args[2]); // rx
                EmitPushExprAsWord(args[3]); // ry
                EmitPushExprAsWord(args[4]); // rw
                EmitPushExprAsWord(args[5]); // rh

                AsmOperand ret0 = MakeUniqueLabel("xyrect_ret0");
                AsmOperand done = MakeUniqueLabel("xyrect_done");

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(10, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_B_A"); // x
                EmitAsm("LD_HL_SP_IMM", new AsmOperand(6, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_C_A"); // rx
                EmitAsm("LD_A_B");
                EmitAsm("CP_C");
                EmitAsm("JR_C", ret0);
                EmitAsm("SUB_C");
                EmitAsm("LD_B_A");
                EmitAsm("LD_HL_SP_IMM", new AsmOperand(2, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_C_A"); // rw
                EmitAsm("LD_A_B");
                EmitAsm("CP_C");
                EmitAsm("JR_NC", ret0);

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(8, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_B_A"); // y
                EmitAsm("LD_HL_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_C_A"); // ry
                EmitAsm("LD_A_B");
                EmitAsm("CP_C");
                EmitAsm("JR_C", ret0);
                EmitAsm("SUB_C");
                EmitAsm("LD_B_A");
                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_C_A"); // rh
                EmitAsm("LD_A_B");
                EmitAsm("CP_C");
                EmitAsm("JR_NC", ret0);

                EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
                EmitAsm("JR", done);
                EmitLabel(ret0);
                EmitAsm("XOR_A");
                EmitLabel(done);
                EmitAsm("ADD_SP_IMM", new AsmOperand(12, AddressMode.Relative));
                return;
            }

scroll.h — Arrière-plan, fenêtre et défilement fractionné

Contrôle le défilement immédiat ou en tampon du fond et de la fenêtre, ainsi que jusqu’à huit coupures raster. Distinguez les valeurs courantes suivies par les accesseurs, les registres matériels réels et les valeurs en attente.

__scroll_bg_add

Ajoute des déplacements signés aux coordonnées suivies du fond et les applique immédiatement.

Syntaxe

c
void __scroll_bg_add(s8 dx, s8 dy);

Paramètres

dx

Déplacement horizontal signé en pixels, de −128 à 127.

dy

Déplacement vertical signé en pixels, de −128 à 127.

Valeur renvoyée

Aucune valeur de retour. L’état modifié et le moment de sa prise d’effet sont décrits dans cette entrée.

Conditions et limites

Aucune attente n’est effectuée. Les réglages et additions immédiats synchronisent la paire de coordonnées en attente concernée avec les valeurs courantes et effacent son drapeau de modification. Même le réglage immédiat d’un seul axe annule la valeur en attente de l’autre axe de la même paire. Les résultats rebouclent modulo 256. Les additions partent des valeurs courantes suivies, pas des valeurs en attente ni des valeurs temporaires des registres raster.

Exemple d’utilisation

c
__scroll_bg_add(-20,3);result[22]=M_SCX;result[23]=M_SCY;

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple distingue valeurs immédiates et en attente et illustre le rebouclage des additions signées. BG X vaut 250 avant application et 16 après ; WX passe de 23 à 39. Ajouter −20 à BG X donne 252 ; ajouter −40 à WY donne 248. L’exemple visuel montre aussi un rectangle de fond et un cadre de fenêtre indépendant.

__scroll_bg_add: DMG
DMG
__scroll_bg_add: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k

Exemple d’utilisation

c
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Avec la caméra en (8,16), le rectangle rouge de 32×24 apparaît à l’écran en (24,16). La fenêtre montre un cadre bleu de 64×64 en (96,80) et un carré vert de 16×16 en (112,96). Toutes les formes sont noires sur DMG.

__scroll_bg_add: DMG
DMG
__scroll_bg_add: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implémentation et source

kitaqgb/lib/scroll.h:37

kitaqgb/kitaqgb/CodeGenerator.cs:7835

csharp
if (funcName == "__scroll_bg_add")
            {
                if (args.Length != 2) Error(expr, "__scroll_bg_add(dx, dy) expects 2 arguments");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitPushAAsWord();
                CompileIntoA(args[1]);
                EmitAsm("LD_C_A");
                EmitAsm("POP_HL");
                EmitAsm("LD_B_L");

                EmitLoadA(ScrollBgXCurVar);
                EmitAsm("ADD_B");
                EmitStoreA(ScrollBgXCurVar);
                EmitStoreA(ScrollBgXNextVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem)); // SCX

                EmitLoadA(ScrollBgYCurVar);
                EmitAsm("ADD_C");
                EmitStoreA(ScrollBgYCurVar);
                EmitStoreA(ScrollBgYNextVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem)); // SCY

                EmitLoadA(ScrollDirtyVar);
                EmitAsm("AND_IMM", new AsmOperand(0xFF ^ ScrollDirtyBgMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__scroll_bg_set_buffered

Prépare les deux coordonnées de défilement du fond pour une application ultérieure.

Syntaxe

c
void __scroll_bg_set_buffered(u8 scx, u8 scy);

Paramètres

scx

Défilement horizontal du fond, de 0 à 255 pixels ; l’augmenter déplace le fond vers la gauche.

scy

Défilement vertical du fond, de 0 à 255 pixels ; l’augmenter déplace le fond vers le haut.

Valeur renvoyée

Aucune valeur de retour. L’état modifié et le moment de sa prise d’effet sont décrits dans cette entrée.

Conditions et limites

Ne modifie ni les valeurs courantes suivies ni les registres. Appliquez-les avec __scroll_flush ou Scroll_Flush ; la mise en attente n’attend pas VBlank.

Exemple d’utilisation

c
__scroll_bg_set_buffered(16,24);result[14]=M_SCX;result[15]=__scroll_bg_x_get();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple distingue valeurs immédiates et en attente et illustre le rebouclage des additions signées. BG X vaut 250 avant application et 16 après ; WX passe de 23 à 39. Ajouter −20 à BG X donne 252 ; ajouter −40 à WY donne 248. L’exemple visuel montre aussi un rectangle de fond et un cadre de fenêtre indépendant.

__scroll_bg_set_buffered: DMG
DMG
__scroll_bg_set_buffered: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k

Exemple d’utilisation

c
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Avec la caméra en (8,16), le rectangle rouge de 32×24 apparaît à l’écran en (24,16). La fenêtre montre un cadre bleu de 64×64 en (96,80) et un carré vert de 16×16 en (112,96). Toutes les formes sont noires sur DMG.

__scroll_bg_set_buffered: DMG
DMG
__scroll_bg_set_buffered: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implémentation et source

kitaqgb/lib/scroll.h:22

kitaqgb/kitaqgb/CodeGenerator.cs:7671

csharp
if (funcName == "__scroll_bg_set_buffered")
            {
                if (args.Length != 2) Error(expr, "__scroll_bg_set_buffered(scx, scy) expects 2 arguments");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitPushAAsWord();
                CompileIntoA(args[1]);
                EmitAsm("LD_B_A");
                EmitAsm("POP_HL");

                EmitAsm("LD_A_L");
                EmitStoreA(ScrollBgXNextVar);
                EmitAsm("LD_A_B");
                EmitStoreA(ScrollBgYNextVar);
                EmitLoadA(ScrollDirtyVar);
                EmitAsm("OR_IMM", new AsmOperand(ScrollDirtyBgMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__scroll_bg_x_get

Lit la valeur courante suivie du défilement horizontal du fond.

Syntaxe

c
u8 __scroll_bg_x_get();

Valeur renvoyée

Renvoie l’octet courant suivi, de 0 à 255. WX comprend son décalage matériel de sept pixels.

Conditions et limites

Ce n’est pas une lecture de registre matériel. Les valeurs en tampon encore en attente, les écritures directes dans les registres et les valeurs temporaires des coupures raster en sont exclues.

Exemple d’utilisation

c
result[6]=__scroll_bg_x_get();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple distingue valeurs immédiates et en attente et illustre le rebouclage des additions signées. BG X vaut 250 avant application et 16 après ; WX passe de 23 à 39. Ajouter −20 à BG X donne 252 ; ajouter −40 à WY donne 248. L’exemple visuel montre aussi un rectangle de fond et un cadre de fenêtre indépendant.

__scroll_bg_x_get: DMG
DMG
__scroll_bg_x_get: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k

Exemple d’utilisation

c
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Avec la caméra en (8,16), le rectangle rouge de 32×24 apparaît à l’écran en (24,16). La fenêtre montre un cadre bleu de 64×64 en (96,80) et un carré vert de 16×16 en (112,96). Toutes les formes sont noires sur DMG.

__scroll_bg_x_get: DMG
DMG
__scroll_bg_x_get: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implémentation et source

kitaqgb/lib/scroll.h:13

kitaqgb/kitaqgb/CodeGenerator.cs:7571

csharp
if (funcName == "__scroll_bg_x_get")
            {
                if (args.Length != 0) Error(expr, "__scroll_bg_x_get() expects 0 arguments");
                EnsureScrollState();
                EmitLoadA(ScrollBgXCurVar);
                return;
            }
__scroll_bg_x_set

Règle immédiatement le défilement horizontal du fond.

Syntaxe

c
void __scroll_bg_x_set(u8 scx);

Paramètres

scx

Défilement horizontal du fond, de 0 à 255 pixels ; l’augmenter déplace le fond vers la gauche.

Valeur renvoyée

Aucune valeur de retour. L’état modifié et le moment de sa prise d’effet sont décrits dans cette entrée.

Conditions et limites

Aucune attente n’est effectuée. Les réglages et additions immédiats synchronisent la paire de coordonnées en attente concernée avec les valeurs courantes et effacent son drapeau de modification. Même le réglage immédiat d’un seul axe annule la valeur en attente de l’autre axe de la même paire.

Exemple d’utilisation

c
__scroll_bg_x_set(250);result[4]=M_SCX;

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple distingue valeurs immédiates et en attente et illustre le rebouclage des additions signées. BG X vaut 250 avant application et 16 après ; WX passe de 23 à 39. Ajouter −20 à BG X donne 252 ; ajouter −40 à WY donne 248. L’exemple visuel montre aussi un rectangle de fond et un cadre de fenêtre indépendant.

__scroll_bg_x_set: DMG
DMG
__scroll_bg_x_set: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k

Exemple d’utilisation

c
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Avec la caméra en (8,16), le rectangle rouge de 32×24 apparaît à l’écran en (24,16). La fenêtre montre un cadre bleu de 64×64 en (96,80) et un carré vert de 16×16 en (112,96). Toutes les formes sont noires sur DMG.

__scroll_bg_x_set: DMG
DMG
__scroll_bg_x_set: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implémentation et source

kitaqgb/lib/scroll.h:11

kitaqgb/kitaqgb/CodeGenerator.cs:7535

csharp
if (funcName == "__scroll_bg_x_set")
            {
                if (args.Length != 1) Error(expr, "__scroll_bg_x_set(scx) expects 1 argument");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitStoreA(ScrollBgXCurVar);
                EmitStoreA(ScrollBgXNextVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem)); // SCX
                EmitLoadA(ScrollBgYCurVar);
                EmitStoreA(ScrollBgYNextVar);
                EmitLoadA(ScrollDirtyVar);
                EmitAsm("AND_IMM", new AsmOperand(0xFF ^ ScrollDirtyBgMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__scroll_bg_x_set_buffered

Prépare uniquement le X du fond.

Syntaxe

c
void __scroll_bg_x_set_buffered(u8 scx);

Paramètres

scx

Défilement horizontal du fond, de 0 à 255 pixels ; l’augmenter déplace le fond vers la gauche.

Valeur renvoyée

Aucune valeur de retour. L’état modifié et le moment de sa prise d’effet sont décrits dans cette entrée.

Conditions et limites

Conserve l’autre axe en attente. Les valeurs courantes et les registres restent inchangés jusqu’à l’application ; initialisez d’abord la paire de coordonnées.

Exemple d’utilisation

c
__scroll_bg_x_set_buffered(32);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple distingue valeurs immédiates et en attente et illustre le rebouclage des additions signées. BG X vaut 250 avant application et 16 après ; WX passe de 23 à 39. Ajouter −20 à BG X donne 252 ; ajouter −40 à WY donne 248. L’exemple visuel montre aussi un rectangle de fond et un cadre de fenêtre indépendant.

__scroll_bg_x_set_buffered: DMG
DMG
__scroll_bg_x_set_buffered: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k

Exemple d’utilisation

c
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Avec la caméra en (8,16), le rectangle rouge de 32×24 apparaît à l’écran en (24,16). La fenêtre montre un cadre bleu de 64×64 en (96,80) et un carré vert de 16×16 en (112,96). Toutes les formes sont noires sur DMG.

__scroll_bg_x_set_buffered: DMG
DMG
__scroll_bg_x_set_buffered: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implémentation et source

kitaqgb/lib/scroll.h:23

kitaqgb/kitaqgb/CodeGenerator.cs:7693

csharp
if (funcName == "__scroll_bg_x_set_buffered")
            {
                if (args.Length != 1) Error(expr, "__scroll_bg_x_set_buffered(scx) expects 1 argument");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitStoreA(ScrollBgXNextVar);
                EmitLoadA(ScrollDirtyVar);
                EmitAsm("OR_IMM", new AsmOperand(ScrollDirtyBgMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__scroll_bg_y_get

Lit la valeur courante suivie du défilement vertical du fond.

Syntaxe

c
u8 __scroll_bg_y_get();

Valeur renvoyée

Renvoie l’octet courant suivi, de 0 à 255. WX comprend son décalage matériel de sept pixels.

Conditions et limites

Ce n’est pas une lecture de registre matériel. Les valeurs en tampon encore en attente, les écritures directes dans les registres et les valeurs temporaires des coupures raster en sont exclues.

Exemple d’utilisation

c
result[7]=__scroll_bg_y_get();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple distingue valeurs immédiates et en attente et illustre le rebouclage des additions signées. BG X vaut 250 avant application et 16 après ; WX passe de 23 à 39. Ajouter −20 à BG X donne 252 ; ajouter −40 à WY donne 248. L’exemple visuel montre aussi un rectangle de fond et un cadre de fenêtre indépendant.

__scroll_bg_y_get: DMG
DMG
__scroll_bg_y_get: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k

Exemple d’utilisation

c
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Avec la caméra en (8,16), le rectangle rouge de 32×24 apparaît à l’écran en (24,16). La fenêtre montre un cadre bleu de 64×64 en (96,80) et un carré vert de 16×16 en (112,96). Toutes les formes sont noires sur DMG.

__scroll_bg_y_get: DMG
DMG
__scroll_bg_y_get: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implémentation et source

kitaqgb/lib/scroll.h:14

kitaqgb/kitaqgb/CodeGenerator.cs:7580

csharp
if (funcName == "__scroll_bg_y_get")
            {
                if (args.Length != 0) Error(expr, "__scroll_bg_y_get() expects 0 arguments");
                EnsureScrollState();
                EmitLoadA(ScrollBgYCurVar);
                return;
            }
__scroll_bg_y_set

Règle immédiatement le défilement vertical du fond.

Syntaxe

c
void __scroll_bg_y_set(u8 scy);

Paramètres

scy

Défilement vertical du fond, de 0 à 255 pixels ; l’augmenter déplace le fond vers le haut.

Valeur renvoyée

Aucune valeur de retour. L’état modifié et le moment de sa prise d’effet sont décrits dans cette entrée.

Conditions et limites

Aucune attente n’est effectuée. Les réglages et additions immédiats synchronisent la paire de coordonnées en attente concernée avec les valeurs courantes et effacent son drapeau de modification. Même le réglage immédiat d’un seul axe annule la valeur en attente de l’autre axe de la même paire.

Exemple d’utilisation

c
__scroll_bg_y_set(2);result[5]=M_SCY;

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple distingue valeurs immédiates et en attente et illustre le rebouclage des additions signées. BG X vaut 250 avant application et 16 après ; WX passe de 23 à 39. Ajouter −20 à BG X donne 252 ; ajouter −40 à WY donne 248. L’exemple visuel montre aussi un rectangle de fond et un cadre de fenêtre indépendant.

__scroll_bg_y_set: DMG
DMG
__scroll_bg_y_set: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k

Exemple d’utilisation

c
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Avec la caméra en (8,16), le rectangle rouge de 32×24 apparaît à l’écran en (24,16). La fenêtre montre un cadre bleu de 64×64 en (96,80) et un carré vert de 16×16 en (112,96). Toutes les formes sont noires sur DMG.

__scroll_bg_y_set: DMG
DMG
__scroll_bg_y_set: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implémentation et source

kitaqgb/lib/scroll.h:12

kitaqgb/kitaqgb/CodeGenerator.cs:7553

csharp
if (funcName == "__scroll_bg_y_set")
            {
                if (args.Length != 1) Error(expr, "__scroll_bg_y_set(scy) expects 1 argument");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitStoreA(ScrollBgYCurVar);
                EmitStoreA(ScrollBgYNextVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem)); // SCY
                EmitLoadA(ScrollBgXCurVar);
                EmitStoreA(ScrollBgXNextVar);
                EmitLoadA(ScrollDirtyVar);
                EmitAsm("AND_IMM", new AsmOperand(0xFF ^ ScrollDirtyBgMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__scroll_bg_y_set_buffered

Prépare uniquement le Y du fond.

Syntaxe

c
void __scroll_bg_y_set_buffered(u8 scy);

Paramètres

scy

Défilement vertical du fond, de 0 à 255 pixels ; l’augmenter déplace le fond vers le haut.

Valeur renvoyée

Aucune valeur de retour. L’état modifié et le moment de sa prise d’effet sont décrits dans cette entrée.

Conditions et limites

Conserve l’autre axe en attente. Les valeurs courantes et les registres restent inchangés jusqu’à l’application ; initialisez d’abord la paire de coordonnées.

Exemple d’utilisation

c
__scroll_bg_y_set_buffered(40);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple distingue valeurs immédiates et en attente et illustre le rebouclage des additions signées. BG X vaut 250 avant application et 16 après ; WX passe de 23 à 39. Ajouter −20 à BG X donne 252 ; ajouter −40 à WY donne 248. L’exemple visuel montre aussi un rectangle de fond et un cadre de fenêtre indépendant.

__scroll_bg_y_set_buffered: DMG
DMG
__scroll_bg_y_set_buffered: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k

Exemple d’utilisation

c
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Avec la caméra en (8,16), le rectangle rouge de 32×24 apparaît à l’écran en (24,16). La fenêtre montre un cadre bleu de 64×64 en (96,80) et un carré vert de 16×16 en (112,96). Toutes les formes sont noires sur DMG.

__scroll_bg_y_set_buffered: DMG
DMG
__scroll_bg_y_set_buffered: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implémentation et source

kitaqgb/lib/scroll.h:24

kitaqgb/kitaqgb/CodeGenerator.cs:7707

csharp
if (funcName == "__scroll_bg_y_set_buffered")
            {
                if (args.Length != 1) Error(expr, "__scroll_bg_y_set_buffered(scy) expects 1 argument");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitStoreA(ScrollBgYNextVar);
                EmitLoadA(ScrollDirtyVar);
                EmitAsm("OR_IMM", new AsmOperand(ScrollDirtyBgMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__scroll_flush

Applique les coordonnées modifiées du fond et de la fenêtre à l’état suivi et au matériel, puis efface leurs drapeaux d’attente.

Syntaxe

c
void __scroll_flush();

Valeur renvoyée

Aucune valeur de retour. L’état modifié et le moment de sa prise d’effet sont décrits dans cette entrée.

Conditions et limites

Une paire de coordonnées inchangée est laissée intacte. Aucune interruption n’est masquée et aucune attente VBlank n’est effectuée ; l’appelant doit prévoir le moment approprié.

Exemple d’utilisation

c
__scroll_flush();result[18]=M_SCX;result[19]=M_SCY;result[20]=M_WX;result[21]=M_WY;

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple distingue valeurs immédiates et en attente et illustre le rebouclage des additions signées. BG X vaut 250 avant application et 16 après ; WX passe de 23 à 39. Ajouter −20 à BG X donne 252 ; ajouter −40 à WY donne 248. L’exemple visuel montre aussi un rectangle de fond et un cadre de fenêtre indépendant.

__scroll_flush: DMG
DMG
__scroll_flush: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k

Exemple d’utilisation

c
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Avec la caméra en (8,16), le rectangle rouge de 32×24 apparaît à l’écran en (24,16). La fenêtre montre un cadre bleu de 64×64 en (96,80) et un carré vert de 16×16 en (112,96). Toutes les formes sont noires sur DMG.

__scroll_flush: DMG
DMG
__scroll_flush: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implémentation et source

kitaqgb/lib/scroll.h:28

kitaqgb/kitaqgb/CodeGenerator.cs:7771

csharp
if (funcName == "__scroll_flush")
            {
                if (args.Length != 0) Error(expr, "__scroll_flush() expects 0 arguments");
                EnsureScrollState();

                AsmOperand flushSkipBg = MakeUniqueLabel("scroll_flush_skip_bg");
                AsmOperand flushSkipWin = MakeUniqueLabel("scroll_flush_skip_win");

                EmitLoadA(ScrollDirtyVar);
                EmitAsm("AND_IMM", new AsmOperand(ScrollDirtyBgMask, AddressMode.Immediate));
                EmitAsm("JR_Z", flushSkipBg);
                EmitLoadA(ScrollBgXNextVar);
                EmitStoreA(ScrollBgXCurVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem)); // SCX
                EmitLoadA(ScrollBgYNextVar);
                EmitStoreA(ScrollBgYCurVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem)); // SCY
                EmitLabel(flushSkipBg);

                EmitLoadA(ScrollDirtyVar);
                EmitAsm("AND_IMM", new AsmOperand(ScrollDirtyWinMask, AddressMode.Immediate));
                EmitAsm("JR_Z", flushSkipWin);
                EmitLoadA(ScrollWinXNextVar);
                EmitStoreA(ScrollWinXCurVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem)); // WX
                EmitLoadA(ScrollWinYNextVar);
                EmitStoreA(ScrollWinYCurVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem)); // WY
                EmitLabel(flushSkipWin);

                EmitAsm("XOR_A");
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__scroll_split_commit

Mémorise la visibilité de base de la fenêtre et arme le premier événement de ligne de la table de coupures.

Syntaxe

c
void __scroll_split_commit();

Valeur renvoyée

Aucune valeur de retour. L’état modifié et le moment de sa prise d’effet sont décrits dans cette entrée.

Conditions et limites

La table partagée contient huit entrées. L’ajout ne les applique pas et ne les trie pas ; les entrées en excès sont ignorées. Reconstruisez-la avec reset→push→commit à une frontière de trame sûre et espacez assez les événements pour laisser le gestionnaire se terminer. La latence d’interruption peut décaler le premier pixel affecté par rapport à la ligne programmée. Une table non vide active VBlank/STAT dans IE et exécute EI. Une table vide désactive la source LYC, sans restaurer IE ni l’état des interruptions CPU.

Exemple d’utilisation

c
__scroll_split_commit();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Divisez l’écran en trois bandes de défilement avec BG X=0→8→16. Trois rectangles de 24×16 apparaissent en (32,16), (24,80) et (16,112). Les événements sont programmés à LY=64 et 104. Les rectangles sont noirs sur DMG et rouges sur CGB.

__scroll_split_commit: DMG
DMG
__scroll_split_commit: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_split_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_split_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implémentation et source

kitaqgb/lib/scroll.h:33

kitaqgb/kitaqgb/CodeGenerator.cs:7959

csharp
if (funcName == "__scroll_split_commit")
            {
                if (args.Length != 0) Error(expr, "__scroll_split_commit() expects 0 arguments");
                EnsureScrollState();
                UseScrollSplitHelpers = true;
                EmitAsm("CALL", new AsmOperand("__kq_scroll_split_commit_core", AddressMode.Absolute));
                return;
            }

void AppendScrollHelpersIfUsed(List<Expr> rawLines)
    {
        bool needScrollHelpers = UseScrollSplitHelpers;
        if (!needScrollHelpers)
        {
            foreach (var e in rawLines)
            {
                string m;
                AsmOperand o;
                if (!e.Match(Tag.Asm, out m, out o) || o == null || !o.Base.HasValue) continue;
                string target = o.Base.Value;
                if (target == "__kq_scroll_split_reset_core" ||
                    target == "__kq_scroll_split_push_core" ||
                    target == "__kq_scroll_split_commit_core")
                {
                    needScrollHelpers = true;
                    break;
                }
            }
        }

        if (!needScrollHelpers) return;

        EnsureScrollState();

        var injected = new List<Expr>();
        injected.Add(Expr.Make(Tag.Comment, "[KITAQGB] injected scroll split helpers"));

        // Reset the split count/index and disable STAT coincidence interrupts; existing table bytes are left in place.
        string resetDone = "__kq_scroll_split_reset_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_reset_core"));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, resetDone));
        injected.Add(Expr.MakeAsm("RET"));

        // Append staged LY, SCX, SCY, WX, WY and masked flags; silently ignore entries beyond the eight-slot capacity.
        string pushHaveRoom = "__kq_scroll_split_push_have_room";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_push_core"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("CP_IMM", new AsmOperand(ScrollSplitMaxEntries, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(pushHaveRoom, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("RET"));
        injected.Add(Expr.Make(Tag.Label, pushHaveRoom));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_L_A"));
        injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpLyVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpScxVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpScyVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpWxVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpWyVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpFlagsVar));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagMask, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("INC_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("RET"));

        // Schedule entry A by programming LYC and STAT bit 6, or disable that bit when A is outside the current count.
        string schedDisable = "__kq_scroll_schedule_next_disable";
        string schedDisabled = "__kq_scroll_schedule_next_disabled";
        string schedHaveTarget = "__kq_scroll_schedule_next_have_target";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_schedule_next"));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("CP_B"));
        injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(schedDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(schedDisable, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, schedHaveTarget));
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_L_A"));
        injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x45, AddressMode.HighMem))); // LYC
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("RET"));
        injected.Add(Expr.Make(Tag.Label, schedDisable));
        // Do not rewrite a disabled STAT source: on DMG even writing zero
        // can raise a fresh request and repeatedly re-enter the STAT handler.
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(schedDisabled, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, schedDisabled));
        injected.Add(Expr.MakeAsm("RET"));

        // Apply the fields selected by an entry's flags. Show follows hide when both are set; palette color zero reuses SCX/SCY bytes.
        string applySkipBg = "__kq_scroll_apply_entry_skip_bg";
        string applySkipWin = "__kq_scroll_apply_entry_skip_win";
        string applySkipHide = "__kq_scroll_apply_entry_skip_hide";
        string applySkipBgColor0 = "__kq_scroll_apply_entry_skip_bg_color0";
        string applyWaitPalette = "__kq_scroll_apply_entry_wait_palette";
        string applyDone = "__kq_scroll_apply_entry_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_apply_entry"));
        injected.Add(Expr.MakeAsm("INC_HL")); // scx
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // scy
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_C_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // wx
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_D_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // wy
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // flags
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagMask, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_H_A"));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagUseBg, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipBg, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem))); // SCX
        injected.Add(Expr.MakeAsm("LD_A_C"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem))); // SCY
        injected.Add(Expr.Make(Tag.Label, applySkipBg));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagUseWin, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipWin, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_D"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem))); // WX
        injected.Add(Expr.MakeAsm("LD_A_E"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem))); // WY
        injected.Add(Expr.Make(Tag.Label, applySkipWin));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagWinHide, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipHide, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xDF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, applySkipHide));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagWinShow, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipBgColor0, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x20, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, applySkipBgColor0));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagBgColor0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applyDone, AddressMode.Absolute)));
        // CGB palette data writes are blocked during pixel transfer. Wait through
        // OAM search as well, so both color bytes fit in the following HBlank.
        // The LY-zero entry runs in VBlank and does not wait here.
        injected.Add(Expr.Make(Tag.Label, applyWaitPalette));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x02, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(applyWaitPalette, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(0x80, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x68, AddressMode.HighMem))); // BCPS: palette 0, color 0, auto increment
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x69, AddressMode.HighMem))); // BCPD low
        injected.Add(Expr.MakeAsm("LD_A_C"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x69, AddressMode.HighMem))); // BCPD high
        injected.Add(Expr.Make(Tag.Label, applyDone));
        injected.Add(Expr.MakeAsm("RET"));

        // Capture base window visibility and arm a nonempty table at index zero, enabling VBlank/STAT interrupts and issuing EI.
        string commitDisable = "__kq_scroll_split_commit_disable";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_commit_core"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x20, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollWinVisibleVar));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(commitDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", new AsmOperand(0xFFFF, AddressMode.Absolute))); // IE
        injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x03, AddressMode.Immediate))); // VBlank | STAT
        injected.Add(Expr.MakeAsm("LD_MEM_A", new AsmOperand(0xFFFF, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("EI"));
        injected.Add(Expr.MakeAsm("RET"));
        injected.Add(Expr.Make(Tag.Label, commitDisable));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("RET"));

        // Save registers/SVBK, select WRAM bank 1, restore base scroll state and restart the split sequence for the frame.
        // A first entry at LY zero is applied immediately before scheduling the following entry.
        string vbDisable = "__kq_vblank_disable";
        string vbFirstNonZero = "__kq_vblank_first_nonzero";
        string vbDone = "__kq_vblank_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_vblank_vector"));
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("PUSH_BC"));
        injected.Add(Expr.MakeAsm("PUSH_DE"));
        injected.Add(Expr.MakeAsm("PUSH_HL"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x70, AddressMode.HighMem))); // SVBK
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        // Set the existing fixed-address frame-ready flag used by the VBlank path.
        injected.Add(Expr.MakeAsm("LD_MEM_A", new AsmOperand(0xC29C, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollBgXCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem))); // SCX
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollBgYCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem))); // SCY
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinXCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem))); // WX
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinYCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem))); // WY
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xDF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinVisibleVar));
        injected.Add(Expr.MakeAsm("OR_B"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(vbDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(vbDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("LD_A_MEM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(vbFirstNonZero, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_HL_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_apply_entry", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR", new AsmOperand(vbDone, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, vbFirstNonZero));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR", new AsmOperand(vbDone, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, vbDisable));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, vbDone));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem))); // restore SVBK
        injected.Add(Expr.MakeAsm("POP_HL"));
        injected.Add(Expr.MakeAsm("POP_DE"));
        injected.Add(Expr.MakeAsm("POP_BC"));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("RETI"));

        // Save registers/SVBK, apply the current entry and advance its index, or disable further coincidence interrupts at the end.
        string statDisable = "__kq_stat_disable";
        string statDone = "__kq_stat_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_stat_vector"));
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("PUSH_BC"));
        injected.Add(Expr.MakeAsm("PUSH_DE"));
        injected.Add(Expr.MakeAsm("PUSH_HL"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x70, AddressMode.HighMem))); // SVBK
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("CP_B"));
        injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(statDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_L_A"));
        injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        // DMG STAT writes can request an interrupt unrelated to LYC. Consume
        // this entry only on its scheduled line; leave the index intact otherwise.
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x44, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("CP_B"));
        injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(statDone, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_apply_entry", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("INC_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR", new AsmOperand(statDone, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, statDisable));
        // Acknowledge an already-disabled source without writing STAT again.
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDone, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, statDone));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem))); // restore SVBK
        injected.Add(Expr.MakeAsm("POP_HL"));
        injected.Add(Expr.MakeAsm("POP_DE"));
        injected.Add(Expr.MakeAsm("POP_BC"));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("RETI"));

        int insertAt = 0;
        while (insertAt < rawLines.Count && rawLines[insertAt].MatchTag(Tag.RstMap)) insertAt++;
        rawLines.InsertRange(insertAt, injected);
    }
__scroll_split_push

Ajoute une coupure qui modifie le défilement du fond lors de l’événement de ligne demandé.

Syntaxe

c
void __scroll_split_push(u8 ly, u8 scx, u8 scy);

Paramètres

ly

Numéro de ligne de l’événement ; utilisez 0–143 pour les événements visibles et ajoutez-les par ordre croissant. Une première entrée à zéro est appliquée pendant VBlank.

scx

Défilement horizontal du fond, de 0 à 255 pixels ; l’augmenter déplace le fond vers la gauche.

scy

Défilement vertical du fond, de 0 à 255 pixels ; l’augmenter déplace le fond vers le haut.

Valeur renvoyée

Aucune valeur de retour. L’état modifié et le moment de sa prise d’effet sont décrits dans cette entrée.

Conditions et limites

La table partagée contient huit entrées. L’ajout ne les applique pas et ne les trie pas ; les entrées en excès sont ignorées. Reconstruisez-la avec reset→push→commit à une frontière de trame sûre et espacez assez les événements pour laisser le gestionnaire se terminer. La latence d’interruption peut décaler le premier pixel affecté par rapport à la ligne programmée.

Exemple d’utilisation

c
__scroll_split_push(64,8,0);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Divisez l’écran en trois bandes de défilement avec BG X=0→8→16. Trois rectangles de 24×16 apparaissent en (32,16), (24,80) et (16,112). Les événements sont programmés à LY=64 et 104. Les rectangles sont noirs sur DMG et rouges sur CGB.

__scroll_split_push: DMG
DMG
__scroll_split_push: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_split_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_split_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implémentation et source

kitaqgb/lib/scroll.h:31

kitaqgb/kitaqgb/CodeGenerator.cs:7907

csharp
if (funcName == "__scroll_split_push")
            {
                if (args.Length != 3) Error(expr, "__scroll_split_push(ly, scx, scy) expects 3 arguments");
                EnsureScrollState();
                UseScrollSplitHelpers = true;

                CompileIntoA(args[0]);
                EmitStoreA(ScrollSplitTmpLyVar);
                CompileIntoA(args[1]);
                EmitStoreA(ScrollSplitTmpScxVar);
                CompileIntoA(args[2]);
                EmitStoreA(ScrollSplitTmpScyVar);
                EmitAsm("XOR_A");
                EmitStoreA(ScrollSplitTmpWxVar);
                EmitStoreA(ScrollSplitTmpWyVar);
                EmitAsm("LD_A_IMM", new AsmOperand(ScrollSplitFlagUseBg, AddressMode.Immediate));
                EmitStoreA(ScrollSplitTmpFlagsVar);
                EmitAsm("CALL", new AsmOperand("__kq_scroll_split_push_core", AddressMode.Absolute));
                return;
            }

void AppendScrollHelpersIfUsed(List<Expr> rawLines)
    {
        bool needScrollHelpers = UseScrollSplitHelpers;
        if (!needScrollHelpers)
        {
            foreach (var e in rawLines)
            {
                string m;
                AsmOperand o;
                if (!e.Match(Tag.Asm, out m, out o) || o == null || !o.Base.HasValue) continue;
                string target = o.Base.Value;
                if (target == "__kq_scroll_split_reset_core" ||
                    target == "__kq_scroll_split_push_core" ||
                    target == "__kq_scroll_split_commit_core")
                {
                    needScrollHelpers = true;
                    break;
                }
            }
        }

        if (!needScrollHelpers) return;

        EnsureScrollState();

        var injected = new List<Expr>();
        injected.Add(Expr.Make(Tag.Comment, "[KITAQGB] injected scroll split helpers"));

        // Reset the split count/index and disable STAT coincidence interrupts; existing table bytes are left in place.
        string resetDone = "__kq_scroll_split_reset_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_reset_core"));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, resetDone));
        injected.Add(Expr.MakeAsm("RET"));

        // Append staged LY, SCX, SCY, WX, WY and masked flags; silently ignore entries beyond the eight-slot capacity.
        string pushHaveRoom = "__kq_scroll_split_push_have_room";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_push_core"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("CP_IMM", new AsmOperand(ScrollSplitMaxEntries, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(pushHaveRoom, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("RET"));
        injected.Add(Expr.Make(Tag.Label, pushHaveRoom));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_L_A"));
        injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpLyVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpScxVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpScyVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpWxVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpWyVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpFlagsVar));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagMask, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("INC_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("RET"));

        // Schedule entry A by programming LYC and STAT bit 6, or disable that bit when A is outside the current count.
        string schedDisable = "__kq_scroll_schedule_next_disable";
        string schedDisabled = "__kq_scroll_schedule_next_disabled";
        string schedHaveTarget = "__kq_scroll_schedule_next_have_target";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_schedule_next"));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("CP_B"));
        injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(schedDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(schedDisable, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, schedHaveTarget));
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_L_A"));
        injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x45, AddressMode.HighMem))); // LYC
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("RET"));
        injected.Add(Expr.Make(Tag.Label, schedDisable));
        // Do not rewrite a disabled STAT source: on DMG even writing zero
        // can raise a fresh request and repeatedly re-enter the STAT handler.
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(schedDisabled, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, schedDisabled));
        injected.Add(Expr.MakeAsm("RET"));

        // Apply the fields selected by an entry's flags. Show follows hide when both are set; palette color zero reuses SCX/SCY bytes.
        string applySkipBg = "__kq_scroll_apply_entry_skip_bg";
        string applySkipWin = "__kq_scroll_apply_entry_skip_win";
        string applySkipHide = "__kq_scroll_apply_entry_skip_hide";
        string applySkipBgColor0 = "__kq_scroll_apply_entry_skip_bg_color0";
        string applyWaitPalette = "__kq_scroll_apply_entry_wait_palette";
        string applyDone = "__kq_scroll_apply_entry_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_apply_entry"));
        injected.Add(Expr.MakeAsm("INC_HL")); // scx
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // scy
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_C_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // wx
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_D_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // wy
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // flags
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagMask, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_H_A"));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagUseBg, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipBg, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem))); // SCX
        injected.Add(Expr.MakeAsm("LD_A_C"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem))); // SCY
        injected.Add(Expr.Make(Tag.Label, applySkipBg));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagUseWin, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipWin, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_D"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem))); // WX
        injected.Add(Expr.MakeAsm("LD_A_E"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem))); // WY
        injected.Add(Expr.Make(Tag.Label, applySkipWin));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagWinHide, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipHide, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xDF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, applySkipHide));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagWinShow, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipBgColor0, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x20, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, applySkipBgColor0));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagBgColor0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applyDone, AddressMode.Absolute)));
        // CGB palette data writes are blocked during pixel transfer. Wait through
        // OAM search as well, so both color bytes fit in the following HBlank.
        // The LY-zero entry runs in VBlank and does not wait here.
        injected.Add(Expr.Make(Tag.Label, applyWaitPalette));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x02, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(applyWaitPalette, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(0x80, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x68, AddressMode.HighMem))); // BCPS: palette 0, color 0, auto increment
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x69, AddressMode.HighMem))); // BCPD low
        injected.Add(Expr.MakeAsm("LD_A_C"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x69, AddressMode.HighMem))); // BCPD high
        injected.Add(Expr.Make(Tag.Label, applyDone));
        injected.Add(Expr.MakeAsm("RET"));

        // Capture base window visibility and arm a nonempty table at index zero, enabling VBlank/STAT interrupts and issuing EI.
        string commitDisable = "__kq_scroll_split_commit_disable";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_commit_core"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x20, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollWinVisibleVar));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(commitDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", new AsmOperand(0xFFFF, AddressMode.Absolute))); // IE
        injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x03, AddressMode.Immediate))); // VBlank | STAT
        injected.Add(Expr.MakeAsm("LD_MEM_A", new AsmOperand(0xFFFF, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("EI"));
        injected.Add(Expr.MakeAsm("RET"));
        injected.Add(Expr.Make(Tag.Label, commitDisable));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("RET"));

        // Save registers/SVBK, select WRAM bank 1, restore base scroll state and restart the split sequence for the frame.
        // A first entry at LY zero is applied immediately before scheduling the following entry.
        string vbDisable = "__kq_vblank_disable";
        string vbFirstNonZero = "__kq_vblank_first_nonzero";
        string vbDone = "__kq_vblank_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_vblank_vector"));
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("PUSH_BC"));
        injected.Add(Expr.MakeAsm("PUSH_DE"));
        injected.Add(Expr.MakeAsm("PUSH_HL"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x70, AddressMode.HighMem))); // SVBK
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        // Set the existing fixed-address frame-ready flag used by the VBlank path.
        injected.Add(Expr.MakeAsm("LD_MEM_A", new AsmOperand(0xC29C, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollBgXCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem))); // SCX
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollBgYCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem))); // SCY
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinXCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem))); // WX
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinYCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem))); // WY
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xDF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinVisibleVar));
        injected.Add(Expr.MakeAsm("OR_B"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(vbDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(vbDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("LD_A_MEM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(vbFirstNonZero, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_HL_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_apply_entry", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR", new AsmOperand(vbDone, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, vbFirstNonZero));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR", new AsmOperand(vbDone, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, vbDisable));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, vbDone));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem))); // restore SVBK
        injected.Add(Expr.MakeAsm("POP_HL"));
        injected.Add(Expr.MakeAsm("POP_DE"));
        injected.Add(Expr.MakeAsm("POP_BC"));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("RETI"));

        // Save registers/SVBK, apply the current entry and advance its index, or disable further coincidence interrupts at the end.
        string statDisable = "__kq_stat_disable";
        string statDone = "__kq_stat_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_stat_vector"));
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("PUSH_BC"));
        injected.Add(Expr.MakeAsm("PUSH_DE"));
        injected.Add(Expr.MakeAsm("PUSH_HL"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x70, AddressMode.HighMem))); // SVBK
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("CP_B"));
        injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(statDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_L_A"));
        injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        // DMG STAT writes can request an interrupt unrelated to LYC. Consume
        // this entry only on its scheduled line; leave the index intact otherwise.
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x44, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("CP_B"));
        injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(statDone, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_apply_entry", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("INC_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR", new AsmOperand(statDone, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, statDisable));
        // Acknowledge an already-disabled source without writing STAT again.
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDone, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, statDone));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem))); // restore SVBK
        injected.Add(Expr.MakeAsm("POP_HL"));
        injected.Add(Expr.MakeAsm("POP_DE"));
        injected.Add(Expr.MakeAsm("POP_BC"));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("RETI"));

        int insertAt = 0;
        while (insertAt < rawLines.Count && rawLines[insertAt].MatchTag(Tag.RstMap)) insertAt++;
        rawLines.InsertRange(insertAt, injected);
    }
__scroll_split_push_ex

Ajoute une coupure dont les drapeaux choisissent les opérations sur le fond, la fenêtre, sa visibilité ou la couleur zéro du fond.

Syntaxe

c
void __scroll_split_push_ex(u8 ly, u8 scx, u8 scy, u8 wx, u8 wy, u8 flags);

Paramètres

ly

Numéro de ligne de l’événement ; utilisez 0–143 pour les événements visibles et ajoutez-les par ordre croissant. Une première entrée à zéro est appliquée pendant VBlank.

scx

Défilement horizontal du fond, de 0 à 255 pixels ; l’augmenter déplace le fond vers la gauche.

scy

Défilement vertical du fond, de 0 à 255 pixels ; l’augmenter déplace le fond vers le haut.

wx

Octet WX brut, de 0 à 255 : ajoutez 7 au X voulu à l’écran ; le bord gauche utilise 7.

wy

Haut de la fenêtre en pixels Y de l’écran, de 0 à 255 ; les lignes visibles vont de 0 à 143.

flags

Bits d’opération : BG=1, position de fenêtre=2, afficher=4, masquer=8, palette de fond CGB 0/couleur 0=16. Les bits inconnus sont ignorés. Le mode couleur zéro réutilise scx/scy comme octets faible/fort de RGB15.

Valeur renvoyée

Aucune valeur de retour. L’état modifié et le moment de sa prise d’effet sont décrits dans cette entrée.

Conditions et limites

La table partagée contient huit entrées. L’ajout ne les applique pas et ne les trie pas ; les entrées en excès sont ignorées. Reconstruisez-la avec reset→push→commit à une frontière de trame sûre et espacez assez les événements pour laisser le gestionnaire se terminer. La latence d’interruption peut décaler le premier pixel affecté par rapport à la ligne programmée. Afficher est prioritaire si les deux bits de visibilité sont actifs. La couleur zéro est réservée au CGB ; la combiner au déplacement BG utilise les mêmes octets scx/scy pour les deux opérations.

Exemple d’utilisation

c
__scroll_split_push_ex(104,16,0,7,0,KQ_SCROLL_SPLIT_USE_BG|KQ_SCROLL_SPLIT_WIN_HIDE);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Divisez l’écran en trois bandes de défilement avec BG X=0→8→16. Trois rectangles de 24×16 apparaissent en (32,16), (24,80) et (16,112). Les événements sont programmés à LY=64 et 104. Les rectangles sont noirs sur DMG et rouges sur CGB.

__scroll_split_push_ex: DMG
DMG
__scroll_split_push_ex: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_split_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_split_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implémentation et source

kitaqgb/lib/scroll.h:32

kitaqgb/kitaqgb/CodeGenerator.cs:7929

csharp
if (funcName == "__scroll_split_push_ex")
            {
                if (args.Length != 6) Error(expr, "__scroll_split_push_ex(ly, scx, scy, wx, wy, flags) expects 6 arguments");
                EnsureScrollState();
                UseScrollSplitHelpers = true;

                if (TryGetU8Const(args[5], out int splitFlagsConst))
                {
                    int unknownBits = splitFlagsConst & ~ScrollSplitFlagMask;
                    if (unknownBits != 0)
                        Warning(args[5], string.Format("__scroll_split_push_ex() ignores unknown flag bits 0x{0:X2}", unknownBits));
                }

                CompileIntoA(args[0]);
                EmitStoreA(ScrollSplitTmpLyVar);
                CompileIntoA(args[1]);
                EmitStoreA(ScrollSplitTmpScxVar);
                CompileIntoA(args[2]);
                EmitStoreA(ScrollSplitTmpScyVar);
                CompileIntoA(args[3]);
                EmitStoreA(ScrollSplitTmpWxVar);
                CompileIntoA(args[4]);
                EmitStoreA(ScrollSplitTmpWyVar);
                CompileIntoA(args[5]);
                EmitStoreA(ScrollSplitTmpFlagsVar);
                EmitAsm("CALL", new AsmOperand("__kq_scroll_split_push_core", AddressMode.Absolute));
                return;
            }

void AppendScrollHelpersIfUsed(List<Expr> rawLines)
    {
        bool needScrollHelpers = UseScrollSplitHelpers;
        if (!needScrollHelpers)
        {
            foreach (var e in rawLines)
            {
                string m;
                AsmOperand o;
                if (!e.Match(Tag.Asm, out m, out o) || o == null || !o.Base.HasValue) continue;
                string target = o.Base.Value;
                if (target == "__kq_scroll_split_reset_core" ||
                    target == "__kq_scroll_split_push_core" ||
                    target == "__kq_scroll_split_commit_core")
                {
                    needScrollHelpers = true;
                    break;
                }
            }
        }

        if (!needScrollHelpers) return;

        EnsureScrollState();

        var injected = new List<Expr>();
        injected.Add(Expr.Make(Tag.Comment, "[KITAQGB] injected scroll split helpers"));

        // Reset the split count/index and disable STAT coincidence interrupts; existing table bytes are left in place.
        string resetDone = "__kq_scroll_split_reset_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_reset_core"));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, resetDone));
        injected.Add(Expr.MakeAsm("RET"));

        // Append staged LY, SCX, SCY, WX, WY and masked flags; silently ignore entries beyond the eight-slot capacity.
        string pushHaveRoom = "__kq_scroll_split_push_have_room";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_push_core"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("CP_IMM", new AsmOperand(ScrollSplitMaxEntries, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(pushHaveRoom, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("RET"));
        injected.Add(Expr.Make(Tag.Label, pushHaveRoom));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_L_A"));
        injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpLyVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpScxVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpScyVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpWxVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpWyVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpFlagsVar));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagMask, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("INC_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("RET"));

        // Schedule entry A by programming LYC and STAT bit 6, or disable that bit when A is outside the current count.
        string schedDisable = "__kq_scroll_schedule_next_disable";
        string schedDisabled = "__kq_scroll_schedule_next_disabled";
        string schedHaveTarget = "__kq_scroll_schedule_next_have_target";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_schedule_next"));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("CP_B"));
        injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(schedDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(schedDisable, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, schedHaveTarget));
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_L_A"));
        injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x45, AddressMode.HighMem))); // LYC
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("RET"));
        injected.Add(Expr.Make(Tag.Label, schedDisable));
        // Do not rewrite a disabled STAT source: on DMG even writing zero
        // can raise a fresh request and repeatedly re-enter the STAT handler.
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(schedDisabled, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, schedDisabled));
        injected.Add(Expr.MakeAsm("RET"));

        // Apply the fields selected by an entry's flags. Show follows hide when both are set; palette color zero reuses SCX/SCY bytes.
        string applySkipBg = "__kq_scroll_apply_entry_skip_bg";
        string applySkipWin = "__kq_scroll_apply_entry_skip_win";
        string applySkipHide = "__kq_scroll_apply_entry_skip_hide";
        string applySkipBgColor0 = "__kq_scroll_apply_entry_skip_bg_color0";
        string applyWaitPalette = "__kq_scroll_apply_entry_wait_palette";
        string applyDone = "__kq_scroll_apply_entry_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_apply_entry"));
        injected.Add(Expr.MakeAsm("INC_HL")); // scx
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // scy
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_C_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // wx
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_D_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // wy
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // flags
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagMask, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_H_A"));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagUseBg, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipBg, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem))); // SCX
        injected.Add(Expr.MakeAsm("LD_A_C"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem))); // SCY
        injected.Add(Expr.Make(Tag.Label, applySkipBg));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagUseWin, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipWin, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_D"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem))); // WX
        injected.Add(Expr.MakeAsm("LD_A_E"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem))); // WY
        injected.Add(Expr.Make(Tag.Label, applySkipWin));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagWinHide, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipHide, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xDF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, applySkipHide));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagWinShow, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipBgColor0, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x20, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, applySkipBgColor0));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagBgColor0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applyDone, AddressMode.Absolute)));
        // CGB palette data writes are blocked during pixel transfer. Wait through
        // OAM search as well, so both color bytes fit in the following HBlank.
        // The LY-zero entry runs in VBlank and does not wait here.
        injected.Add(Expr.Make(Tag.Label, applyWaitPalette));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x02, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(applyWaitPalette, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(0x80, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x68, AddressMode.HighMem))); // BCPS: palette 0, color 0, auto increment
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x69, AddressMode.HighMem))); // BCPD low
        injected.Add(Expr.MakeAsm("LD_A_C"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x69, AddressMode.HighMem))); // BCPD high
        injected.Add(Expr.Make(Tag.Label, applyDone));
        injected.Add(Expr.MakeAsm("RET"));

        // Capture base window visibility and arm a nonempty table at index zero, enabling VBlank/STAT interrupts and issuing EI.
        string commitDisable = "__kq_scroll_split_commit_disable";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_commit_core"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x20, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollWinVisibleVar));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(commitDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", new AsmOperand(0xFFFF, AddressMode.Absolute))); // IE
        injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x03, AddressMode.Immediate))); // VBlank | STAT
        injected.Add(Expr.MakeAsm("LD_MEM_A", new AsmOperand(0xFFFF, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("EI"));
        injected.Add(Expr.MakeAsm("RET"));
        injected.Add(Expr.Make(Tag.Label, commitDisable));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("RET"));

        // Save registers/SVBK, select WRAM bank 1, restore base scroll state and restart the split sequence for the frame.
        // A first entry at LY zero is applied immediately before scheduling the following entry.
        string vbDisable = "__kq_vblank_disable";
        string vbFirstNonZero = "__kq_vblank_first_nonzero";
        string vbDone = "__kq_vblank_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_vblank_vector"));
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("PUSH_BC"));
        injected.Add(Expr.MakeAsm("PUSH_DE"));
        injected.Add(Expr.MakeAsm("PUSH_HL"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x70, AddressMode.HighMem))); // SVBK
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        // Set the existing fixed-address frame-ready flag used by the VBlank path.
        injected.Add(Expr.MakeAsm("LD_MEM_A", new AsmOperand(0xC29C, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollBgXCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem))); // SCX
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollBgYCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem))); // SCY
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinXCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem))); // WX
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinYCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem))); // WY
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xDF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinVisibleVar));
        injected.Add(Expr.MakeAsm("OR_B"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(vbDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(vbDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("LD_A_MEM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(vbFirstNonZero, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_HL_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_apply_entry", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR", new AsmOperand(vbDone, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, vbFirstNonZero));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR", new AsmOperand(vbDone, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, vbDisable));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, vbDone));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem))); // restore SVBK
        injected.Add(Expr.MakeAsm("POP_HL"));
        injected.Add(Expr.MakeAsm("POP_DE"));
        injected.Add(Expr.MakeAsm("POP_BC"));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("RETI"));

        // Save registers/SVBK, apply the current entry and advance its index, or disable further coincidence interrupts at the end.
        string statDisable = "__kq_stat_disable";
        string statDone = "__kq_stat_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_stat_vector"));
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("PUSH_BC"));
        injected.Add(Expr.MakeAsm("PUSH_DE"));
        injected.Add(Expr.MakeAsm("PUSH_HL"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x70, AddressMode.HighMem))); // SVBK
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("CP_B"));
        injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(statDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_L_A"));
        injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        // DMG STAT writes can request an interrupt unrelated to LYC. Consume
        // this entry only on its scheduled line; leave the index intact otherwise.
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x44, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("CP_B"));
        injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(statDone, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_apply_entry", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("INC_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR", new AsmOperand(statDone, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, statDisable));
        // Acknowledge an already-disabled source without writing STAT again.
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDone, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, statDone));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem))); // restore SVBK
        injected.Add(Expr.MakeAsm("POP_HL"));
        injected.Add(Expr.MakeAsm("POP_DE"));
        injected.Add(Expr.MakeAsm("POP_BC"));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("RETI"));

        int insertAt = 0;
        while (insertAt < rawLines.Count && rawLines[insertAt].MatchTag(Tag.RstMap)) insertAt++;
        rawLines.InsertRange(insertAt, injected);
    }
__scroll_split_reset

Remet à zéro le nombre et l’indice des coupures, désactive leur traitement et efface le bit d’activation de l’interruption LYC dans STAT.

Syntaxe

c
void __scroll_split_reset();

Valeur renvoyée

Aucune valeur de retour. L’état modifié et le moment de sa prise d’effet sont décrits dans cette entrée.

Conditions et limites

La table partagée contient huit entrées. L’ajout ne les applique pas et ne les trie pas ; les entrées en excès sont ignorées. Reconstruisez-la avec reset→push→commit à une frontière de trame sûre et espacez assez les événements pour laisser le gestionnaire se terminer. La latence d’interruption peut décaler le premier pixel affecté par rapport à la ligne programmée. N’efface pas les octets de la table ni IE et ne désactive pas les interruptions du CPU.

Exemple d’utilisation

c
__scroll_split_reset();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Divisez l’écran en trois bandes de défilement avec BG X=0→8→16. Trois rectangles de 24×16 apparaissent en (32,16), (24,80) et (16,112). Les événements sont programmés à LY=64 et 104. Les rectangles sont noirs sur DMG et rouges sur CGB.

__scroll_split_reset: DMG
DMG
__scroll_split_reset: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_split_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_split_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implémentation et source

kitaqgb/lib/scroll.h:30

kitaqgb/kitaqgb/CodeGenerator.cs:7897

csharp
if (funcName == "__scroll_split_reset")
            {
                if (args.Length != 0) Error(expr, "__scroll_split_reset() expects 0 arguments");
                EnsureScrollState();
                UseScrollSplitHelpers = true;
                EmitAsm("CALL", new AsmOperand("__kq_scroll_split_reset_core", AddressMode.Absolute));
                return;
            }

void AppendScrollHelpersIfUsed(List<Expr> rawLines)
    {
        bool needScrollHelpers = UseScrollSplitHelpers;
        if (!needScrollHelpers)
        {
            foreach (var e in rawLines)
            {
                string m;
                AsmOperand o;
                if (!e.Match(Tag.Asm, out m, out o) || o == null || !o.Base.HasValue) continue;
                string target = o.Base.Value;
                if (target == "__kq_scroll_split_reset_core" ||
                    target == "__kq_scroll_split_push_core" ||
                    target == "__kq_scroll_split_commit_core")
                {
                    needScrollHelpers = true;
                    break;
                }
            }
        }

        if (!needScrollHelpers) return;

        EnsureScrollState();

        var injected = new List<Expr>();
        injected.Add(Expr.Make(Tag.Comment, "[KITAQGB] injected scroll split helpers"));

        // Reset the split count/index and disable STAT coincidence interrupts; existing table bytes are left in place.
        string resetDone = "__kq_scroll_split_reset_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_reset_core"));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, resetDone));
        injected.Add(Expr.MakeAsm("RET"));

        // Append staged LY, SCX, SCY, WX, WY and masked flags; silently ignore entries beyond the eight-slot capacity.
        string pushHaveRoom = "__kq_scroll_split_push_have_room";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_push_core"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("CP_IMM", new AsmOperand(ScrollSplitMaxEntries, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(pushHaveRoom, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("RET"));
        injected.Add(Expr.Make(Tag.Label, pushHaveRoom));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_L_A"));
        injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpLyVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpScxVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpScyVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpWxVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpWyVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpFlagsVar));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagMask, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("INC_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("RET"));

        // Schedule entry A by programming LYC and STAT bit 6, or disable that bit when A is outside the current count.
        string schedDisable = "__kq_scroll_schedule_next_disable";
        string schedDisabled = "__kq_scroll_schedule_next_disabled";
        string schedHaveTarget = "__kq_scroll_schedule_next_have_target";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_schedule_next"));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("CP_B"));
        injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(schedDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(schedDisable, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, schedHaveTarget));
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_L_A"));
        injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x45, AddressMode.HighMem))); // LYC
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("RET"));
        injected.Add(Expr.Make(Tag.Label, schedDisable));
        // Do not rewrite a disabled STAT source: on DMG even writing zero
        // can raise a fresh request and repeatedly re-enter the STAT handler.
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(schedDisabled, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, schedDisabled));
        injected.Add(Expr.MakeAsm("RET"));

        // Apply the fields selected by an entry's flags. Show follows hide when both are set; palette color zero reuses SCX/SCY bytes.
        string applySkipBg = "__kq_scroll_apply_entry_skip_bg";
        string applySkipWin = "__kq_scroll_apply_entry_skip_win";
        string applySkipHide = "__kq_scroll_apply_entry_skip_hide";
        string applySkipBgColor0 = "__kq_scroll_apply_entry_skip_bg_color0";
        string applyWaitPalette = "__kq_scroll_apply_entry_wait_palette";
        string applyDone = "__kq_scroll_apply_entry_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_apply_entry"));
        injected.Add(Expr.MakeAsm("INC_HL")); // scx
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // scy
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_C_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // wx
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_D_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // wy
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // flags
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagMask, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_H_A"));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagUseBg, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipBg, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem))); // SCX
        injected.Add(Expr.MakeAsm("LD_A_C"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem))); // SCY
        injected.Add(Expr.Make(Tag.Label, applySkipBg));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagUseWin, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipWin, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_D"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem))); // WX
        injected.Add(Expr.MakeAsm("LD_A_E"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem))); // WY
        injected.Add(Expr.Make(Tag.Label, applySkipWin));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagWinHide, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipHide, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xDF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, applySkipHide));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagWinShow, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipBgColor0, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x20, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, applySkipBgColor0));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagBgColor0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applyDone, AddressMode.Absolute)));
        // CGB palette data writes are blocked during pixel transfer. Wait through
        // OAM search as well, so both color bytes fit in the following HBlank.
        // The LY-zero entry runs in VBlank and does not wait here.
        injected.Add(Expr.Make(Tag.Label, applyWaitPalette));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x02, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(applyWaitPalette, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(0x80, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x68, AddressMode.HighMem))); // BCPS: palette 0, color 0, auto increment
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x69, AddressMode.HighMem))); // BCPD low
        injected.Add(Expr.MakeAsm("LD_A_C"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x69, AddressMode.HighMem))); // BCPD high
        injected.Add(Expr.Make(Tag.Label, applyDone));
        injected.Add(Expr.MakeAsm("RET"));

        // Capture base window visibility and arm a nonempty table at index zero, enabling VBlank/STAT interrupts and issuing EI.
        string commitDisable = "__kq_scroll_split_commit_disable";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_commit_core"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x20, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollWinVisibleVar));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(commitDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", new AsmOperand(0xFFFF, AddressMode.Absolute))); // IE
        injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x03, AddressMode.Immediate))); // VBlank | STAT
        injected.Add(Expr.MakeAsm("LD_MEM_A", new AsmOperand(0xFFFF, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("EI"));
        injected.Add(Expr.MakeAsm("RET"));
        injected.Add(Expr.Make(Tag.Label, commitDisable));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("RET"));

        // Save registers/SVBK, select WRAM bank 1, restore base scroll state and restart the split sequence for the frame.
        // A first entry at LY zero is applied immediately before scheduling the following entry.
        string vbDisable = "__kq_vblank_disable";
        string vbFirstNonZero = "__kq_vblank_first_nonzero";
        string vbDone = "__kq_vblank_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_vblank_vector"));
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("PUSH_BC"));
        injected.Add(Expr.MakeAsm("PUSH_DE"));
        injected.Add(Expr.MakeAsm("PUSH_HL"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x70, AddressMode.HighMem))); // SVBK
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        // Set the existing fixed-address frame-ready flag used by the VBlank path.
        injected.Add(Expr.MakeAsm("LD_MEM_A", new AsmOperand(0xC29C, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollBgXCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem))); // SCX
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollBgYCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem))); // SCY
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinXCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem))); // WX
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinYCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem))); // WY
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xDF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinVisibleVar));
        injected.Add(Expr.MakeAsm("OR_B"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(vbDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(vbDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("LD_A_MEM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(vbFirstNonZero, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_HL_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_apply_entry", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR", new AsmOperand(vbDone, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, vbFirstNonZero));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR", new AsmOperand(vbDone, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, vbDisable));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, vbDone));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem))); // restore SVBK
        injected.Add(Expr.MakeAsm("POP_HL"));
        injected.Add(Expr.MakeAsm("POP_DE"));
        injected.Add(Expr.MakeAsm("POP_BC"));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("RETI"));

        // Save registers/SVBK, apply the current entry and advance its index, or disable further coincidence interrupts at the end.
        string statDisable = "__kq_stat_disable";
        string statDone = "__kq_stat_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_stat_vector"));
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("PUSH_BC"));
        injected.Add(Expr.MakeAsm("PUSH_DE"));
        injected.Add(Expr.MakeAsm("PUSH_HL"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x70, AddressMode.HighMem))); // SVBK
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("CP_B"));
        injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(statDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_L_A"));
        injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        // DMG STAT writes can request an interrupt unrelated to LYC. Consume
        // this entry only on its scheduled line; leave the index intact otherwise.
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x44, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("CP_B"));
        injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(statDone, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_apply_entry", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("INC_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR", new AsmOperand(statDone, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, statDisable));
        // Acknowledge an already-disabled source without writing STAT again.
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDone, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, statDone));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem))); // restore SVBK
        injected.Add(Expr.MakeAsm("POP_HL"));
        injected.Add(Expr.MakeAsm("POP_DE"));
        injected.Add(Expr.MakeAsm("POP_BC"));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("RETI"));

        int insertAt = 0;
        while (insertAt < rawLines.Count && rawLines[insertAt].MatchTag(Tag.RstMap)) insertAt++;
        rawLines.InsertRange(insertAt, injected);
    }
__scroll_win_add

Ajoute des déplacements signés aux coordonnées suivies de la fenêtre et les applique immédiatement.

Syntaxe

c
void __scroll_win_add(s8 dx, s8 dy);

Paramètres

dx

Déplacement horizontal signé en pixels, de −128 à 127.

dy

Déplacement vertical signé en pixels, de −128 à 127.

Valeur renvoyée

Aucune valeur de retour. L’état modifié et le moment de sa prise d’effet sont décrits dans cette entrée.

Conditions et limites

Aucune attente n’est effectuée. Les réglages et additions immédiats synchronisent la paire de coordonnées en attente concernée avec les valeurs courantes et effacent son drapeau de modification. Même le réglage immédiat d’un seul axe annule la valeur en attente de l’autre axe de la même paire. Les résultats rebouclent modulo 256. Les additions partent des valeurs courantes suivies, pas des valeurs en attente ni des valeurs temporaires des registres raster.

Exemple d’utilisation

c
__scroll_win_add(4,-40);result[24]=M_WX;result[25]=M_WY;

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple distingue valeurs immédiates et en attente et illustre le rebouclage des additions signées. BG X vaut 250 avant application et 16 après ; WX passe de 23 à 39. Ajouter −20 à BG X donne 252 ; ajouter −40 à WY donne 248. L’exemple visuel montre aussi un rectangle de fond et un cadre de fenêtre indépendant.

__scroll_win_add: DMG
DMG
__scroll_win_add: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k

Exemple d’utilisation

c
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Avec la caméra en (8,16), le rectangle rouge de 32×24 apparaît à l’écran en (24,16). La fenêtre montre un cadre bleu de 64×64 en (96,80) et un carré vert de 16×16 en (112,96). Toutes les formes sont noires sur DMG.

__scroll_win_add: DMG
DMG
__scroll_win_add: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implémentation et source

kitaqgb/lib/scroll.h:38

kitaqgb/kitaqgb/CodeGenerator.cs:7866

csharp
if (funcName == "__scroll_win_add")
            {
                if (args.Length != 2) Error(expr, "__scroll_win_add(dx, dy) expects 2 arguments");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitPushAAsWord();
                CompileIntoA(args[1]);
                EmitAsm("LD_C_A");
                EmitAsm("POP_HL");
                EmitAsm("LD_B_L");

                EmitLoadA(ScrollWinXCurVar);
                EmitAsm("ADD_B");
                EmitStoreA(ScrollWinXCurVar);
                EmitStoreA(ScrollWinXNextVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem)); // WX

                EmitLoadA(ScrollWinYCurVar);
                EmitAsm("ADD_C");
                EmitStoreA(ScrollWinYCurVar);
                EmitStoreA(ScrollWinYNextVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem)); // WY

                EmitLoadA(ScrollDirtyVar);
                EmitAsm("AND_IMM", new AsmOperand(0xFF ^ ScrollDirtyWinMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__scroll_win_hide

Efface le bit de fenêtre LCDC et l’état suivi de visibilité de la fenêtre.

Syntaxe

c
void __scroll_win_hide();

Valeur renvoyée

Aucune valeur de retour. L’état modifié et le moment de sa prise d’effet sont décrits dans cette entrée.

Conditions et limites

Ne modifie ni les coordonnées ni le choix de la carte. Pour être visible, la fenêtre exige aussi que le LCD et les fonctions fond/fenêtre soient activés et qu’elle se trouve dans la zone visible.

Exemple d’utilisation

c
__scroll_win_hide();result[27]=M_LCDC&32;

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple distingue valeurs immédiates et en attente et illustre le rebouclage des additions signées. BG X vaut 250 avant application et 16 après ; WX passe de 23 à 39. Ajouter −20 à BG X donne 252 ; ajouter −40 à WY donne 248. L’exemple visuel montre aussi un rectangle de fond et un cadre de fenêtre indépendant.

__scroll_win_hide: DMG
DMG
__scroll_win_hide: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k

Exemple d’utilisation

c
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Avec la caméra en (8,16), le rectangle rouge de 32×24 apparaît à l’écran en (24,16). La fenêtre montre un cadre bleu de 64×64 en (96,80) et un carré vert de 16×16 en (112,96). Toutes les formes sont noires sur DMG.

__scroll_win_hide: DMG
DMG
__scroll_win_hide: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implémentation et source

kitaqgb/lib/scroll.h:36

kitaqgb/kitaqgb/CodeGenerator.cs:7821

csharp
if (funcName == "__scroll_win_hide")
            {
                if (args.Length != 0) Error(expr, "__scroll_win_hide() expects 0 arguments");
                EnsureScrollState();

                EmitAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem)); // LCDC
                EmitAsm("AND_IMM", new AsmOperand(0xDF, AddressMode.Immediate));
                EmitAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem));
                EmitAsm("XOR_A");
                EmitStoreA(ScrollWinVisibleVar);
                return;
            }
__scroll_win_set

Règle immédiatement WX et WY de la fenêtre.

Syntaxe

c
void __scroll_win_set(u8 wx, u8 wy);

Paramètres

wx

Octet WX brut, de 0 à 255 : ajoutez 7 au X voulu à l’écran ; le bord gauche utilise 7.

wy

Haut de la fenêtre en pixels Y de l’écran, de 0 à 255 ; les lignes visibles vont de 0 à 143.

Valeur renvoyée

Aucune valeur de retour. L’état modifié et le moment de sa prise d’effet sont décrits dans cette entrée.

Conditions et limites

Aucune attente n’est effectuée. Les réglages et additions immédiats synchronisent la paire de coordonnées en attente concernée avec les valeurs courantes et effacent son drapeau de modification. Même le réglage immédiat d’un seul axe annule la valeur en attente de l’autre axe de la même paire.

Exemple d’utilisation

c
__scroll_win_set(15,8);result[8]=M_WX;result[9]=M_WY;

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple distingue valeurs immédiates et en attente et illustre le rebouclage des additions signées. BG X vaut 250 avant application et 16 après ; WX passe de 23 à 39. Ajouter −20 à BG X donne 252 ; ajouter −40 à WY donne 248. L’exemple visuel montre aussi un rectangle de fond et un cadre de fenêtre indépendant.

__scroll_win_set: DMG
DMG
__scroll_win_set: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k

Exemple d’utilisation

c
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Avec la caméra en (8,16), le rectangle rouge de 32×24 apparaît à l’écran en (24,16). La fenêtre montre un cadre bleu de 64×64 en (96,80) et un carré vert de 16×16 en (112,96). Toutes les formes sont noires sur DMG.

__scroll_win_set: DMG
DMG
__scroll_win_set: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implémentation et source

kitaqgb/lib/scroll.h:16

kitaqgb/kitaqgb/CodeGenerator.cs:7589

csharp
if (funcName == "__scroll_win_set")
            {
                if (args.Length != 2) Error(expr, "__scroll_win_set(wx, wy) expects 2 arguments");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitPushAAsWord();
                CompileIntoA(args[1]);
                EmitAsm("LD_B_A");
                EmitAsm("POP_HL");

                EmitAsm("LD_A_L");
                EmitStoreA(ScrollWinXCurVar);
                EmitStoreA(ScrollWinXNextVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem)); // WX

                EmitAsm("LD_A_B");
                EmitStoreA(ScrollWinYCurVar);
                EmitStoreA(ScrollWinYNextVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem)); // WY

                EmitLoadA(ScrollDirtyVar);
                EmitAsm("AND_IMM", new AsmOperand(0xFF ^ ScrollDirtyWinMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__scroll_win_set_buffered

Prépare les deux coordonnées de la fenêtre pour une application ultérieure.

Syntaxe

c
void __scroll_win_set_buffered(u8 wx, u8 wy);

Paramètres

wx

Octet WX brut, de 0 à 255 : ajoutez 7 au X voulu à l’écran ; le bord gauche utilise 7.

wy

Haut de la fenêtre en pixels Y de l’écran, de 0 à 255 ; les lignes visibles vont de 0 à 143.

Valeur renvoyée

Aucune valeur de retour. L’état modifié et le moment de sa prise d’effet sont décrits dans cette entrée.

Conditions et limites

Ne modifie ni les valeurs courantes suivies ni les registres. Appliquez-les avec __scroll_flush ou Scroll_Flush ; la mise en attente n’attend pas VBlank.

Exemple d’utilisation

c
__scroll_win_set_buffered(39,32);result[16]=M_WX;result[17]=__scroll_win_x_get();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple distingue valeurs immédiates et en attente et illustre le rebouclage des additions signées. BG X vaut 250 avant application et 16 après ; WX passe de 23 à 39. Ajouter −20 à BG X donne 252 ; ajouter −40 à WY donne 248. L’exemple visuel montre aussi un rectangle de fond et un cadre de fenêtre indépendant.

__scroll_win_set_buffered: DMG
DMG
__scroll_win_set_buffered: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k

Exemple d’utilisation

c
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Avec la caméra en (8,16), le rectangle rouge de 32×24 apparaît à l’écran en (24,16). La fenêtre montre un cadre bleu de 64×64 en (96,80) et un carré vert de 16×16 en (112,96). Toutes les formes sont noires sur DMG.

__scroll_win_set_buffered: DMG
DMG
__scroll_win_set_buffered: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implémentation et source

kitaqgb/lib/scroll.h:25

kitaqgb/kitaqgb/CodeGenerator.cs:7721

csharp
if (funcName == "__scroll_win_set_buffered")
            {
                if (args.Length != 2) Error(expr, "__scroll_win_set_buffered(wx, wy) expects 2 arguments");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitPushAAsWord();
                CompileIntoA(args[1]);
                EmitAsm("LD_B_A");
                EmitAsm("POP_HL");

                EmitAsm("LD_A_L");
                EmitStoreA(ScrollWinXNextVar);
                EmitAsm("LD_A_B");
                EmitStoreA(ScrollWinYNextVar);
                EmitLoadA(ScrollDirtyVar);
                EmitAsm("OR_IMM", new AsmOperand(ScrollDirtyWinMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__scroll_win_show

Active le bit de fenêtre LCDC et l’état suivi de visibilité de la fenêtre.

Syntaxe

c
void __scroll_win_show();

Valeur renvoyée

Aucune valeur de retour. L’état modifié et le moment de sa prise d’effet sont décrits dans cette entrée.

Conditions et limites

Ne modifie ni les coordonnées ni le choix de la carte. Pour être visible, la fenêtre exige aussi que le LCD et les fonctions fond/fenêtre soient activés et qu’elle se trouve dans la zone visible.

Exemple d’utilisation

c
__scroll_win_show();result[26]=M_LCDC&32;

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple distingue valeurs immédiates et en attente et illustre le rebouclage des additions signées. BG X vaut 250 avant application et 16 après ; WX passe de 23 à 39. Ajouter −20 à BG X donne 252 ; ajouter −40 à WY donne 248. L’exemple visuel montre aussi un rectangle de fond et un cadre de fenêtre indépendant.

__scroll_win_show: DMG
DMG
__scroll_win_show: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k

Exemple d’utilisation

c
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Avec la caméra en (8,16), le rectangle rouge de 32×24 apparaît à l’écran en (24,16). La fenêtre montre un cadre bleu de 64×64 en (96,80) et un carré vert de 16×16 en (112,96). Toutes les formes sont noires sur DMG.

__scroll_win_show: DMG
DMG
__scroll_win_show: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implémentation et source

kitaqgb/lib/scroll.h:35

kitaqgb/kitaqgb/CodeGenerator.cs:7807

csharp
if (funcName == "__scroll_win_show")
            {
                if (args.Length != 0) Error(expr, "__scroll_win_show() expects 0 arguments");
                EnsureScrollState();

                EmitAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem)); // LCDC
                EmitAsm("OR_IMM", new AsmOperand(0x20, AddressMode.Immediate));
                EmitAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem));
                EmitAsm("LD_A_IMM", new AsmOperand(0x20, AddressMode.Immediate));
                EmitStoreA(ScrollWinVisibleVar);
                return;
            }
__scroll_win_x_get

Lit la valeur WX courante suivie de la fenêtre.

Syntaxe

c
u8 __scroll_win_x_get();

Valeur renvoyée

Renvoie l’octet courant suivi, de 0 à 255. WX comprend son décalage matériel de sept pixels.

Conditions et limites

Ce n’est pas une lecture de registre matériel. Les valeurs en tampon encore en attente, les écritures directes dans les registres et les valeurs temporaires des coupures raster en sont exclues.

Exemple d’utilisation

c
result[12]=__scroll_win_x_get();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple distingue valeurs immédiates et en attente et illustre le rebouclage des additions signées. BG X vaut 250 avant application et 16 après ; WX passe de 23 à 39. Ajouter −20 à BG X donne 252 ; ajouter −40 à WY donne 248. L’exemple visuel montre aussi un rectangle de fond et un cadre de fenêtre indépendant.

__scroll_win_x_get: DMG
DMG
__scroll_win_x_get: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k

Exemple d’utilisation

c
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Avec la caméra en (8,16), le rectangle rouge de 32×24 apparaît à l’écran en (24,16). La fenêtre montre un cadre bleu de 64×64 en (96,80) et un carré vert de 16×16 en (112,96). Toutes les formes sont noires sur DMG.

__scroll_win_x_get: DMG
DMG
__scroll_win_x_get: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implémentation et source

kitaqgb/lib/scroll.h:19

kitaqgb/kitaqgb/CodeGenerator.cs:7653

csharp
if (funcName == "__scroll_win_x_get")
            {
                if (args.Length != 0) Error(expr, "__scroll_win_x_get() expects 0 arguments");
                EnsureScrollState();
                EmitLoadA(ScrollWinXCurVar);
                return;
            }
__scroll_win_x_set

Règle immédiatement la seule coordonnée WX brute de la fenêtre.

Syntaxe

c
void __scroll_win_x_set(u8 wx);

Paramètres

wx

Octet WX brut, de 0 à 255 : ajoutez 7 au X voulu à l’écran ; le bord gauche utilise 7.

Valeur renvoyée

Aucune valeur de retour. L’état modifié et le moment de sa prise d’effet sont décrits dans cette entrée.

Conditions et limites

Aucune attente n’est effectuée. Les réglages et additions immédiats synchronisent la paire de coordonnées en attente concernée avec les valeurs courantes et effacent son drapeau de modification. Même le réglage immédiat d’un seul axe annule la valeur en attente de l’autre axe de la même paire.

Exemple d’utilisation

c
__scroll_win_x_set(23);result[10]=M_WX;

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple distingue valeurs immédiates et en attente et illustre le rebouclage des additions signées. BG X vaut 250 avant application et 16 après ; WX passe de 23 à 39. Ajouter −20 à BG X donne 252 ; ajouter −40 à WY donne 248. L’exemple visuel montre aussi un rectangle de fond et un cadre de fenêtre indépendant.

__scroll_win_x_set: DMG
DMG
__scroll_win_x_set: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k

Exemple d’utilisation

c
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Avec la caméra en (8,16), le rectangle rouge de 32×24 apparaît à l’écran en (24,16). La fenêtre montre un cadre bleu de 64×64 en (96,80) et un carré vert de 16×16 en (112,96). Toutes les formes sont noires sur DMG.

__scroll_win_x_set: DMG
DMG
__scroll_win_x_set: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implémentation et source

kitaqgb/lib/scroll.h:17

kitaqgb/kitaqgb/CodeGenerator.cs:7617

csharp
if (funcName == "__scroll_win_x_set")
            {
                if (args.Length != 1) Error(expr, "__scroll_win_x_set(wx) expects 1 argument");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitStoreA(ScrollWinXCurVar);
                EmitStoreA(ScrollWinXNextVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem)); // WX
                EmitLoadA(ScrollWinYCurVar);
                EmitStoreA(ScrollWinYNextVar);
                EmitLoadA(ScrollDirtyVar);
                EmitAsm("AND_IMM", new AsmOperand(0xFF ^ ScrollDirtyWinMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__scroll_win_x_set_buffered

Prépare uniquement WX de la fenêtre.

Syntaxe

c
void __scroll_win_x_set_buffered(u8 wx);

Paramètres

wx

Octet WX brut, de 0 à 255 : ajoutez 7 au X voulu à l’écran ; le bord gauche utilise 7.

Valeur renvoyée

Aucune valeur de retour. L’état modifié et le moment de sa prise d’effet sont décrits dans cette entrée.

Conditions et limites

Conserve l’autre axe en attente. Les valeurs courantes et les registres restent inchangés jusqu’à l’application ; initialisez d’abord la paire de coordonnées.

Exemple d’utilisation

c
__scroll_win_x_set_buffered(55);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple distingue valeurs immédiates et en attente et illustre le rebouclage des additions signées. BG X vaut 250 avant application et 16 après ; WX passe de 23 à 39. Ajouter −20 à BG X donne 252 ; ajouter −40 à WY donne 248. L’exemple visuel montre aussi un rectangle de fond et un cadre de fenêtre indépendant.

__scroll_win_x_set_buffered: DMG
DMG
__scroll_win_x_set_buffered: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k

Exemple d’utilisation

c
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Avec la caméra en (8,16), le rectangle rouge de 32×24 apparaît à l’écran en (24,16). La fenêtre montre un cadre bleu de 64×64 en (96,80) et un carré vert de 16×16 en (112,96). Toutes les formes sont noires sur DMG.

__scroll_win_x_set_buffered: DMG
DMG
__scroll_win_x_set_buffered: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implémentation et source

kitaqgb/lib/scroll.h:26

kitaqgb/kitaqgb/CodeGenerator.cs:7743

csharp
if (funcName == "__scroll_win_x_set_buffered")
            {
                if (args.Length != 1) Error(expr, "__scroll_win_x_set_buffered(wx) expects 1 argument");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitStoreA(ScrollWinXNextVar);
                EmitLoadA(ScrollDirtyVar);
                EmitAsm("OR_IMM", new AsmOperand(ScrollDirtyWinMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__scroll_win_y_get

Lit la valeur WY courante suivie de la fenêtre.

Syntaxe

c
u8 __scroll_win_y_get();

Valeur renvoyée

Renvoie l’octet courant suivi, de 0 à 255. WX comprend son décalage matériel de sept pixels.

Conditions et limites

Ce n’est pas une lecture de registre matériel. Les valeurs en tampon encore en attente, les écritures directes dans les registres et les valeurs temporaires des coupures raster en sont exclues.

Exemple d’utilisation

c
result[13]=__scroll_win_y_get();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple distingue valeurs immédiates et en attente et illustre le rebouclage des additions signées. BG X vaut 250 avant application et 16 après ; WX passe de 23 à 39. Ajouter −20 à BG X donne 252 ; ajouter −40 à WY donne 248. L’exemple visuel montre aussi un rectangle de fond et un cadre de fenêtre indépendant.

__scroll_win_y_get: DMG
DMG
__scroll_win_y_get: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k

Exemple d’utilisation

c
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Avec la caméra en (8,16), le rectangle rouge de 32×24 apparaît à l’écran en (24,16). La fenêtre montre un cadre bleu de 64×64 en (96,80) et un carré vert de 16×16 en (112,96). Toutes les formes sont noires sur DMG.

__scroll_win_y_get: DMG
DMG
__scroll_win_y_get: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implémentation et source

kitaqgb/lib/scroll.h:20

kitaqgb/kitaqgb/CodeGenerator.cs:7662

csharp
if (funcName == "__scroll_win_y_get")
            {
                if (args.Length != 0) Error(expr, "__scroll_win_y_get() expects 0 arguments");
                EnsureScrollState();
                EmitLoadA(ScrollWinYCurVar);
                return;
            }
__scroll_win_y_set

Règle immédiatement la seule coordonnée WY de la fenêtre.

Syntaxe

c
void __scroll_win_y_set(u8 wy);

Paramètres

wy

Haut de la fenêtre en pixels Y de l’écran, de 0 à 255 ; les lignes visibles vont de 0 à 143.

Valeur renvoyée

Aucune valeur de retour. L’état modifié et le moment de sa prise d’effet sont décrits dans cette entrée.

Conditions et limites

Aucune attente n’est effectuée. Les réglages et additions immédiats synchronisent la paire de coordonnées en attente concernée avec les valeurs courantes et effacent son drapeau de modification. Même le réglage immédiat d’un seul axe annule la valeur en attente de l’autre axe de la même paire.

Exemple d’utilisation

c
__scroll_win_y_set(16);result[11]=M_WY;

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple distingue valeurs immédiates et en attente et illustre le rebouclage des additions signées. BG X vaut 250 avant application et 16 après ; WX passe de 23 à 39. Ajouter −20 à BG X donne 252 ; ajouter −40 à WY donne 248. L’exemple visuel montre aussi un rectangle de fond et un cadre de fenêtre indépendant.

__scroll_win_y_set: DMG
DMG
__scroll_win_y_set: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k

Exemple d’utilisation

c
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Avec la caméra en (8,16), le rectangle rouge de 32×24 apparaît à l’écran en (24,16). La fenêtre montre un cadre bleu de 64×64 en (96,80) et un carré vert de 16×16 en (112,96). Toutes les formes sont noires sur DMG.

__scroll_win_y_set: DMG
DMG
__scroll_win_y_set: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implémentation et source

kitaqgb/lib/scroll.h:18

kitaqgb/kitaqgb/CodeGenerator.cs:7635

csharp
if (funcName == "__scroll_win_y_set")
            {
                if (args.Length != 1) Error(expr, "__scroll_win_y_set(wy) expects 1 argument");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitStoreA(ScrollWinYCurVar);
                EmitStoreA(ScrollWinYNextVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem)); // WY
                EmitLoadA(ScrollWinXCurVar);
                EmitStoreA(ScrollWinXNextVar);
                EmitLoadA(ScrollDirtyVar);
                EmitAsm("AND_IMM", new AsmOperand(0xFF ^ ScrollDirtyWinMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__scroll_win_y_set_buffered

Prépare uniquement WY de la fenêtre.

Syntaxe

c
void __scroll_win_y_set_buffered(u8 wy);

Paramètres

wy

Haut de la fenêtre en pixels Y de l’écran, de 0 à 255 ; les lignes visibles vont de 0 à 143.

Valeur renvoyée

Aucune valeur de retour. L’état modifié et le moment de sa prise d’effet sont décrits dans cette entrée.

Conditions et limites

Conserve l’autre axe en attente. Les valeurs courantes et les registres restent inchangés jusqu’à l’application ; initialisez d’abord la paire de coordonnées.

Exemple d’utilisation

c
__scroll_win_y_set_buffered(48);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Cet exemple distingue valeurs immédiates et en attente et illustre le rebouclage des additions signées. BG X vaut 250 avant application et 16 après ; WX passe de 23 à 39. Ajouter −20 à BG X donne 252 ; ajouter −40 à WY donne 248. L’exemple visuel montre aussi un rectangle de fond et un cadre de fenêtre indépendant.

__scroll_win_y_set_buffered: DMG
DMG
__scroll_win_y_set_buffered: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_scroll_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_scroll_intrinsics.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k

Exemple d’utilisation

c
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

Avec la caméra en (8,16), le rectangle rouge de 32×24 apparaît à l’écran en (24,16). La fenêtre montre un cadre bleu de 64×64 en (96,80) et un carré vert de 16×16 en (112,96). Toutes les formes sont noires sur DMG.

__scroll_win_y_set_buffered: DMG
DMG
__scroll_win_y_set_buffered: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch2_camera_view.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch2_camera_view.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k
Implémentation et source

kitaqgb/lib/scroll.h:27

kitaqgb/kitaqgb/CodeGenerator.cs:7757

csharp
if (funcName == "__scroll_win_y_set_buffered")
            {
                if (args.Length != 1) Error(expr, "__scroll_win_y_set_buffered(wy) expects 1 argument");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitStoreA(ScrollWinYNextVar);
                EmitLoadA(ScrollDirtyVar);
                EmitAsm("OR_IMM", new AsmOperand(ScrollDirtyWinMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }

sprite.h — Allocation d'OBJ, placement, métasprites et animation

Les sprites sont des objets matériels mobiles, généralement de 8×8 ou 8×16 pixels, décrits dans l’OAM, la mémoire des attributs d’objets. Réservez des entrées, préparez position, tuile et attributs dans la copie de l’OAM, puis transférez-la. Un métasprite réunit plusieurs entrées en un objet. Le GB offre 40 entrées avec une limite de 10 par ligne ; le FC, 64 avec une limite de 8 par ligne.

Attributs GB : le bit 4 sélectionne OBP1 sur DMG ; 5/6 retournent X/Y ; 7 place le sprite derrière les pixels de fond d’indice non nul. Sur CGB, 0..2 choisissent la palette d’objets et 3 la banque VRAM des tuiles ; le bit de palette DMG ne choisit pas les couleurs natives CGB.

L’exemple utilise la bibliothèque GB complète. KQ_SPRITE_MINIMAL_RUNTIME retire de sprite.c sprite_free, sprite_set_pos, sprite_count_used, les deux requêtes de lignes, metasprite_draw et anim_update. Ne le définissez pas si vous utilisez ces fonctions.

__oam_dma

Copie 160 octets d’une page mémoire vers l’OAM matérielle (0xFE00–0xFE9F) et remplace ainsi les 40 entrées de sprites. La fonction ne revient qu’une fois le transfert terminé.

Syntaxe

c
void __oam_dma(u16 src_ptr);

Paramètres

src_ptr

Adresse source en octets. Seul l’octet de poids fort est utilisé : 0xC480 sélectionne 0xC400–0xC49F. Préparez 160 octets lisibles à une adresse multiple de 256. Utilisez une page prise en charge par le matériel (0x00–0xDF) et gardez le banc ROM ou RAM requis accessible. Le compilateur ne vérifie ni l’adresse ni l’alignement.

Valeur renvoyée

Aucune (void).

Conditions et limites

Chaque entrée contient quatre octets : Y, X, numéro de tuile et attributs. Les coordonnées à l’écran sont (X−8, Y−16). Une valeur Y de 0 masque une entrée inutilisée. Cette fonction intrinsèque copie les données brutes ; elle n’alloue aucun sprite et ne met à jour ni les compteurs ni le tampon miroir de la bibliothèque de sprites.

Appelez-la pendant le VBlank ou lorsque le LCD est éteint. La fonction n’attend pas le VBlank et ne modifie pas les réglages du LCD. Le transfert matériel dure 160 cycles machine du CPU, auxquels s’ajoutent la préparation et le retour de la fonction.

Les programmes contenant cet appel réservent 8 octets de HRAM pour la routine de transfert. Celle-ci est initialisée à chaque appel et attend en HRAM tant que le DMA est actif. Les sources d’interruption sont temporairement masquées ; IE et l’état d’activation d’IME sont conservés. Le traitement des interruptions est différé jusqu’à la fin du transfert.

Exemple d’utilisation

c
// raw_oam is a 160-byte array at address 0xC400.
m_wait();
__oam_dma((u16)raw_oam);

Cet extrait montre l’appel. Avant de l’intégrer à un programme complet, respectez les conditions d’initialisation et de dimensionnement des tampons indiquées.

Résultat attendu

L’exemple construit directement une page OAM complète. Il affiche une flèche de 8×8 vers la droite en (88,32), son reflet horizontal en (88,56) et un carré de 8×8 en (88,80). Sur CGB, la flèche a des bords rouges, une tige verte et des détails bleus à la pointe ; le carré est vert. Le DMG utilise des nuances de gris. Toutes les autres entrées sont masquées. La vérification compare les pixels réels dans les deux modes matériels de l’émulateur ; aucun test sur console physique n’a été effectué.

__oam_dma: DMG
DMG
__oam_dma: CGB
CGB

Programme d’exemple complet

Commande de compilation

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\oam_dma.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\oam_dma.gb --cgb=cgb --profile=dev --rst-disable --stack-bank=fixed --no-cache --no-disasm
Implémentation et source

kitaqgb/lib/sprite.h:46

csharp
// Copy one 160-byte, page-aligned OAM source and return after DMA completes.
            if (funcName == "__oam_dma")
            {
                if (args.Length != 1) Error(expr, "__oam_dma(src_ptr) expects 1 argument");

                CompileIntoHL(args[0]); // Evaluate once; hardware uses only the high byte.
                // Mask interrupt sources without changing IME, including calls made inside an ISR.
                EmitAsm("LD_A_MEM", new AsmOperand(0xFFFF, AddressMode.Absolute));
                EmitAsm("PUSH_AF");
                EmitAsm("XOR_A");
                EmitAsm("LD_MEM_A", new AsmOperand(0xFFFF, AddressMode.Absolute));
                // LDH [FF46],A; LD B,40; DEC B; JR NZ,-3; RET.
                // Initialize on every call, so no startup copy or readiness flag is needed.
                byte[] dmaStub = { 0xE0, 0x46, 0x06, 0x28, 0x05, 0x20, 0xFD, 0xC9 };
                for (int i = 0; i < dmaStub.Length; i++)
                {
                    EmitAsm("LD_A_IMM", new AsmOperand(dmaStub[i], AddressMode.Immediate));
                    EmitAsm("LDH_MEM_A", new AsmOperand((OamDmaStubAddr + i) & 0xFF, AddressMode.HighMem));
                }
                EmitAsm("LD_A_H");
                // CALL pushes before DMA starts; RET reads the stack only after the wait.
                EmitAsm("CALL", new AsmOperand(OamDmaStubAddr, AddressMode.Absolute));
                EmitAsm("POP_AF");
                EmitAsm("LD_MEM_A", new AsmOperand(0xFFFF, AddressMode.Absolute));
                return;
            }

Référence de syntaxe des commandes

L'aide ci-dessous a été enregistrée pour cette édition des sources. Consultez-la pour les arguments facultatifs, les valeurs par défaut et les valeurs acceptées. Remplacez les noms de fichiers illustratifs par vos propres sorties. Les réponses enregistrées des outils conservent leur texte d'origine.

kitaqgb
help
KITAQGB native Rust compiler

Commands:
  disasm ROM -o dis.s
  header ROM -o patched.gb [--title=NAME --cgb=dmg|cgb|cgb_only ...]
  assemble-ir INPUT.assembly.json -o output.gb [header options]
  tokenize SOURCE.c ... -I INCLUDE_DIR -o tokens.json
  parse SOURCE.c ... -I INCLUDE_DIR -o ast.json
  lower SOURCE.c ... -I INCLUDE_DIR -o lowered.json
  emit-ir SOURCE.c ... -I INCLUDE_DIR -o raw.assembly.json
  compile SOURCE.c ... -I INCLUDE_DIR -o output.gb [-O0|-O1]
  optimize-ir INPUT.assembly.json -o optimized.json [-O0|-O1]
  strip-ir INPUT.assembly.json -o stripped.json [--strip-keep=LABELS]

C compilation, aggregates, banked calls and hardware intrinsics are implemented and covered by differential tests. Utility commands: kqhelp, symfind, src2asm, romdiff, template, snippet, conventions, fixhint, irsum, attrviz, recipe, test.

Development and recovery:
  SOURCE.c ... --watch [--watch-max-builds=N]
  devserver SOURCE.c ... [--once --poll-ms=N --debounce-ms=N --max-builds=N]
  recipe [--history=FILE --out=FILE --script=FILE --last=N --stdout]
  SOURCE.c ... --minimize[=FILE] [--minimize-out=FILE --minimize-work=DIR --minimize-quick --minimize-trace=final|all]
  SOURCE.c ... --minimize-on-fail --repro-pack[=DIR]
  SOURCE.c ... --debug-output --disasm-changed[=GIT_BASE]
Watch loops stop with Ctrl+C; the Rust API also accepts a CancellationToken. See PORT_STATUS.md for executed verification and compatibility limits.

Syntaxe des commandes de développement

Ces syntaxes proviennent de Program.DebugTools.cs et Program.VibeTools.cs. La comparaison de ROM, la recherche de symboles et les modèles sont des sous-commandes distinctes de la compilation C ordinaire.

usage
usage: kitaqgb kqhelp <KQxxxx>
usage
usage: kitaqgb symfind <pattern> [--map=<.map>] [--dbg=<.dbg>] [--regex] [--ignore-case] [--out=<file>|-]
usage
usage: kitaqgb src2asm <source.c:line> [--disasm=<dis.s>] [--funcsizes=<.funcsizes.txt>] [--context=N] [--out=<file>|-]
usage
usage: kitaqgb romdiff <old.gb> <new.gb> [--old-func=<.funcsizes.txt>] [--new-func=<.funcsizes.txt>] [--old-map=<.map>] [--new-map=<.map>] [--out=<file>|-]
usage
usage: kitaqgb template [out.c] [--out=<file.c>] [--cgb] [--overwrite] [--no-build-script] [--no-readme]
usage
usage: kitaqgb conventions [--out=<file>|--stdout]
usage
usage: kitaqgb snippet list | get <id> [--out=<file>|-] | emit <category|all> [--out=<file>|-]
usage
usage: kitaqgb fixhint [diag.json|stderr.log] [--in=<file>] [--out=<file>|-] [--max=N]
usage
usage: kitaqgb irsum [--ir=<syntax_tree_lowered.txt>] [--asm=<assembly_code.txt>] [--top=N] [--out=<file>|-]
usage
usage: kitaqgb devserver [--poll-ms=N] [--debounce-ms=N] [--once] [--max-builds=N] [--deps=<file>] <compile args...>
usage
usage: kitaqgb recipe [--history=<file>] [--last=N] [--out=<file>] [--script=<file>] [--stdout]

Options longues reconnues par l'analyseur

Les noms ont été extraits de src/cli.rs, y compris les options de compatibilité et d'investigation. Consultez l'aide et le gestionnaire correspondant pour déterminer si chaque option reçoit une valeur.

--abi--abi-diff-report--abi-verify--attach--auto-minimize--bank-sim--cache--cart--cgb--cgb-consistency--cross-bank-report--debug-out--debug-output--deps-out--dest--diag-json--disasm--disasm-changed--disasm-changed-out--emit-ai-metadata--emit-path-manifest--farcall-suggest--fast--fast-build--func-size-report--header-logo--help--hotspot-report--include-dir--machine-readable--max-errors--minimize-on-fail--no-banner--no-cache--no-disasm--no-header-logo--no-rst--opt-diff--permissive--profile--ramsize--repro-check--repro-pack--rom-header--rom-title--romsize--rst--rst-disable--rst-enable--rst-exclude--rst-max-calls--rst-max-vectors--rst-off--rst-on--rst-report--rst-safe--rst-speed-safe--rst-unsafe--rst-use-38--sgb--stack-bank--stack-reserve--stack-top--strict--trace--trace-out--verify-cgb-symbols--version--vlist--vlist-out

Annexe : index des instructions assembleur

Ce tableau est extrait d'src/asm_info.rs. Il décrit les noms internes du compilateur et les formats d'opérandes. Les entrées contenant des destinations de branchement ou des adresses mémoire sont des exemples de syntaxe, pas des programmes autonomes. Consultez les conventions d'appel et la génération de code pour les registres préservés et les changements d'indicateurs.

opcodeInstructionFormatExemple de syntaxe
00NOPIMPNOP
F3DIIMPDI
FBEIIMPEI
76HALTIMPHALT
10STOPIMM8STOP #1
C9RETIMPRET
C0RET_NZIMPRET_NZ
C8RET_ZIMPRET_Z
D0RET_NCIMPRET_NC
D8RET_CIMPRET_C
D9RETIIMPRETI
C3JPABSJP 0xC000
E9JP_HLIMPJP_HL
18JRRELJR +target
20JR_NZRELJR_NZ +target
28JR_ZRELJR_Z +target
30JR_NCRELJR_NC +target
38JR_CRELJR_C +target
CDCALLABSCALL 0xC000
C4CALL_NZABSCALL_NZ 0xC000
CCCALL_ZABSCALL_Z 0xC000
D4CALL_NCABSCALL_NC 0xC000
DCCALL_CABSCALL_C 0xC000
C2JP_NZABSJP_NZ 0xC000
CAJP_ZABSJP_Z 0xC000
D2JP_NCABSJP_NC 0xC000
DAJP_CABSJP_C 0xC000
C7RST_00IMPRST_00
CFRST_08IMPRST_08
D7RST_10IMPRST_10
DFRST_18IMPRST_18
E7RST_20IMPRST_20
EFRST_28IMPRST_28
F7RST_30IMPRST_30
FFRST_38IMPRST_38
3ELD_A_IMMIMM8LD_A_IMM #1
06LD_B_IMMIMM8LD_B_IMM #1
0ELD_C_IMMIMM8LD_C_IMM #1
16LD_D_IMMIMM8LD_D_IMM #1
1ELD_E_IMMIMM8LD_E_IMM #1
26LD_H_IMMIMM8LD_H_IMM #1
2ELD_L_IMMIMM8LD_L_IMM #1
7FLD_A_AIMPLD_A_A
78LD_A_BIMPLD_A_B
79LD_A_CIMPLD_A_C
7ALD_A_DIMPLD_A_D
7BLD_A_EIMPLD_A_E
7CLD_A_HIMPLD_A_H
7DLD_A_LIMPLD_A_L
47LD_B_AIMPLD_B_A
40LD_B_BIMPLD_B_B
41LD_B_CIMPLD_B_C
42LD_B_DIMPLD_B_D
43LD_B_EIMPLD_B_E
44LD_B_HIMPLD_B_H
45LD_B_LIMPLD_B_L
4FLD_C_AIMPLD_C_A
48LD_C_BIMPLD_C_B
49LD_C_CIMPLD_C_C
4ALD_C_DIMPLD_C_D
4BLD_C_EIMPLD_C_E
4CLD_C_HIMPLD_C_H
4DLD_C_LIMPLD_C_L
57LD_D_AIMPLD_D_A
50LD_D_BIMPLD_D_B
51LD_D_CIMPLD_D_C
52LD_D_DIMPLD_D_D
53LD_D_EIMPLD_D_E
54LD_D_HIMPLD_D_H
55LD_D_LIMPLD_D_L
5FLD_E_AIMPLD_E_A
58LD_E_BIMPLD_E_B
59LD_E_CIMPLD_E_C
5ALD_E_DIMPLD_E_D
5BLD_E_EIMPLD_E_E
5CLD_E_HIMPLD_E_H
5DLD_E_LIMPLD_E_L
67LD_H_AIMPLD_H_A
60LD_H_BIMPLD_H_B
61LD_H_CIMPLD_H_C
62LD_H_DIMPLD_H_D
63LD_H_EIMPLD_H_E
64LD_H_HIMPLD_H_H
65LD_H_LIMPLD_H_L
6FLD_L_AIMPLD_L_A
68LD_L_BIMPLD_L_B
69LD_L_CIMPLD_L_C
6ALD_L_DIMPLD_L_D
6BLD_L_EIMPLD_L_E
6CLD_L_HIMPLD_L_H
6DLD_L_LIMPLD_L_L
EALD_MEM_AABSLD_MEM_A 0xC000
FALD_A_MEMABSLD_A_MEM 0xC000
E0LDH_MEM_ALDHLDH_MEM_A 0x40
F0LDH_A_MEMLDHLDH_A_MEM 0x40
E2LD_C_MEM_AIMPLD_C_MEM_A
F2LD_A_MEM_CIMPLD_A_MEM_C
77LD_HL_AIMPLD_HL_A
7ELD_A_HLIMPLD_A_HL
46LD_B_HLIMPLD_B_HL
4ELD_C_HLIMPLD_C_HL
56LD_D_HLIMPLD_D_HL
5ELD_E_HLIMPLD_E_HL
66LD_H_HLIMPLD_H_HL
6ELD_L_HLIMPLD_L_HL
70LD_HL_BIMPLD_HL_B
71LD_HL_CIMPLD_HL_C
72LD_HL_DIMPLD_HL_D
73LD_HL_EIMPLD_HL_E
74LD_HL_HIMPLD_HL_H
75LD_HL_LIMPLD_HL_L
36LD_HL_REF_IMMIMM8LD_HL_REF_IMM #1
12LD_DE_AIMPLD_DE_A
1ALD_A_DEIMPLD_A_DE
02LD_BC_AIMPLD_BC_A
0ALD_A_BCIMPLD_A_BC
22LDI_HL_AIMPLDI_HL_A
2ALDI_A_HLIMPLDI_A_HL
32LDD_HL_AIMPLDD_HL_A
3ALDD_A_HLIMPLDD_A_HL
01LD_BC_IMMIMM16LD_BC_IMM #0xC000
11LD_DE_IMMIMM16LD_DE_IMM #0xC000
21LD_HL_IMMIMM16LD_HL_IMM #0xC000
31LD_SP_IMMIMM16LD_SP_IMM #0xC000
F9LD_SP_HLIMPLD_SP_HL
F8LD_HL_SP_IMMRELLD_HL_SP_IMM +target
E8ADD_SP_IMMRELADD_SP_IMM +target
08LD_MEM_SPIMM16LD_MEM_SP #0xC000
C5PUSH_BCIMPPUSH_BC
C1POP_BCIMPPOP_BC
D5PUSH_DEIMPPUSH_DE
D1POP_DEIMPPOP_DE
E5PUSH_HLIMPPUSH_HL
E1POP_HLIMPPOP_HL
F5PUSH_AFIMPPUSH_AF
F1POP_AFIMPPOP_AF
3CINC_AIMPINC_A
04INC_BIMPINC_B
0CINC_CIMPINC_C
14INC_DIMPINC_D
1CINC_EIMPINC_E
24INC_HIMPINC_H
2CINC_LIMPINC_L
34INC_HL_REFIMPINC_HL_REF
3DDEC_AIMPDEC_A
05DEC_BIMPDEC_B
0DDEC_CIMPDEC_C
15DEC_DIMPDEC_D
1DDEC_EIMPDEC_E
25DEC_HIMPDEC_H
2DDEC_LIMPDEC_L
35DEC_HL_REFIMPDEC_HL_REF
87ADD_AIMPADD_A
80ADD_BIMPADD_B
81ADD_CIMPADD_C
82ADD_DIMPADD_D
83ADD_EIMPADD_E
84ADD_HIMPADD_H
85ADD_LIMPADD_L
86ADD_HL_REFIMPADD_HL_REF
C6ADD_A_IMMIMM8ADD_A_IMM #1
8FADC_AIMPADC_A
88ADC_BIMPADC_B
89ADC_CIMPADC_C
8AADC_DIMPADC_D
8BADC_EIMPADC_E
8CADC_HIMPADC_H
8DADC_LIMPADC_L
8EADC_HL_REFIMPADC_HL_REF
CEADC_IMMIMM8ADC_IMM #1
97SUB_AIMPSUB_A
90SUB_BIMPSUB_B
91SUB_CIMPSUB_C
92SUB_DIMPSUB_D
93SUB_EIMPSUB_E
94SUB_HIMPSUB_H
95SUB_LIMPSUB_L
96SUB_HL_REFIMPSUB_HL_REF
D6SUB_IMMIMM8SUB_IMM #1
9FSBC_AIMPSBC_A
98SBC_BIMPSBC_B
99SBC_CIMPSBC_C
9ASBC_DIMPSBC_D
9BSBC_EIMPSBC_E
9CSBC_HIMPSBC_H
9DSBC_LIMPSBC_L
9ESBC_HL_REFIMPSBC_HL_REF
DESBC_IMMIMM8SBC_IMM #1
A7AND_AIMPAND_A
A0AND_BIMPAND_B
A1AND_CIMPAND_C
A2AND_DIMPAND_D
A3AND_EIMPAND_E
A4AND_HIMPAND_H
A5AND_LIMPAND_L
A6AND_HL_REFIMPAND_HL_REF
E6AND_IMMIMM8AND_IMM #1
B7OR_AIMPOR_A
B0OR_BIMPOR_B
B1OR_CIMPOR_C
B2OR_DIMPOR_D
B3OR_EIMPOR_E
B4OR_HIMPOR_H
B5OR_LIMPOR_L
B6OR_HL_REFIMPOR_HL_REF
F6OR_IMMIMM8OR_IMM #1
AFXOR_AIMPXOR_A
A8XOR_BIMPXOR_B
A9XOR_CIMPXOR_C
AAXOR_DIMPXOR_D
ABXOR_EIMPXOR_E
ACXOR_HIMPXOR_H
ADXOR_LIMPXOR_L
AEXOR_HL_REFIMPXOR_HL_REF
EEXOR_IMMIMM8XOR_IMM #1
BFCP_AIMPCP_A
B8CP_BIMPCP_B
B9CP_CIMPCP_C
BACP_DIMPCP_D
BBCP_EIMPCP_E
BCCP_HIMPCP_H
BDCP_LIMPCP_L
BECP_HL_REFIMPCP_HL_REF
FECP_IMMIMM8CP_IMM #1
03INC_BCIMPINC_BC
0BDEC_BCIMPDEC_BC
13INC_DEIMPINC_DE
1BDEC_DEIMPDEC_DE
23INC_HLIMPINC_HL
2BDEC_HLIMPDEC_HL
33INC_SPIMPINC_SP
3BDEC_SPIMPDEC_SP
09ADD_HL_BCIMPADD_HL_BC
19ADD_HL_DEIMPADD_HL_DE
29ADD_HL_HLIMPADD_HL_HL
39ADD_HL_SPIMPADD_HL_SP
07RLCAIMPRLCA
17RLAIMPRLA
0FRRCAIMPRRCA
1FRRAIMPRRA
CBPREFIX_CBIMPPREFIX_CB
2FCPLIMPCPL
3FCCFIMPCCF
37SCFIMPSCF
27DAAIMPDAA