KITAQ SERIES • ÉDITION DE RÉFÉRENCE
KITAQGB
Programmation C pour Game Boy
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.
| OS | kitaqgb |
|---|---|
| Windows x64 | kitaqgb.exe |
| Linux x64 (musl) | bin/linux-x86_64/kitaqgb |
| Linux x64 (GNU) | bin/linux-gnu-x86_64/kitaqgb |
| macOS ARM64 | bin/macos-arm64/kitaqgb |
| macOS Intel | bin/macos-x86_64/kitaqgb |
| macOS Universal | bin/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.
kitaqgb game.c -I lib -o game.gb --cart=romonly --romsize=32kExemples 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.

Télécharger le source / État de la vérification
.\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// 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.

Télécharger le source / État de la vérification
.\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// 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.

Télécharger le source / État de la vérification
.\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// 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.

Télécharger le source / État de la vérification
.\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// 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.](../verification/gb_aggregate.png)
Télécharger le source / État de la vérification
.\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// 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.

Télécharger le source / État de la vérification
.\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// 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.

Télécharger le source / État de la vérification
.\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// 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.

Télécharger le source / État de la vérification
.\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// 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.

Télécharger le source / État de la vérification
.\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// 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.

Télécharger le source / État de la vérification
.\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// 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.

Télécharger le source / État de la vérification
.\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// 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.

Télécharger le source / État de la vérification
.\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// 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.

Télécharger le source / État de la vérification
.\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// 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.

Télécharger le source / État de la vérification
.\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// 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.](../verification/gb_rle.png)
Télécharger le source / État de la vérification
.\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// 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.

Télécharger le source / État de la vérification
.\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// 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.

Télécharger le source / État de la vérification
.\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// 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(); }
}Gérer la fin d’un transfert série et le dépassement du délai gb_link.c
Objectif : Distingue un transfert terminé d’un transfert qui n’aboutit pas dans le délai d’attente.
Fonctionnement : Le maître envoie 42 et attend avec Link_WaitByte. À la fin du transfert, l’octet reçu est affiché ; en cas de délai dépassé, le transfert est annulé et zéro est affiché.
Commandes et résultat attendu : Un transfert terminé sans connexion peut renvoyer 255. Un délai dépassé affiche 000 ; avec un pair connecté, l’écran montre l’octet reçu. Le résultat n’est pas toujours 042.

Télécharger le source / État de la vérification
.\kitaqgb\kitaqgb.exe kitaqgb/lib/link_hwregs_gb.c kitaqgb/lib/link.c kitaq-docs/samples/gb_link.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_link.gb --profile=dev --rst-disable --stack-bank=fixed// Handle serial transfers that do not deliver a byte in time.
// Expected: 255 for disconnected link; 000 if timeout; peer value when connected
#include "gb_common.h"
#include "link.h"
// Start a master transfer of byte 42 and wait with a bounded timeout.
// Display the received byte on completion; cancel and display zero on timeout.
void main() {
m_init();
m_text(2,3,"LINK");
u8 value; Link_InitMaster(); Link_BeginTransfer(42);
if(Link_WaitByte(4,&value)) m_number(value);
else { Link_Cancel(); m_number(0); }
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.

Télécharger le source / État de la vérification
.\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// 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.

Télécharger le source / État de la vérification
.\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// 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é.

Télécharger le source / État de la vérification
.\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// 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.

Télécharger le source / État de la vérification
.\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// 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.

Télécharger le source / État de la vérification
.\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// 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.

Télécharger le source / État de la vérification
.\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// 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é.

Télécharger le source / État de la vérification
.\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// 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.

Télécharger le source / État de la vérification
.\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// 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.

Télécharger le source / État de la vérification
.\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// 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.

Télécharger le source / État de la vérification
.\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// 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.

Télécharger le source / État de la vérification
.\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// 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
void __farmemcpy(void* dst, u8 bank, const void* src, u16 len);Paramètres
dstDestination 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.
bankSé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.
srcPointeur proche vers des octets lisibles dans la banque ROM choisie. Il ne contient pas de numéro de banque.
lenNombre 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
__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.


Commande de compilation
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-disasmImplé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
void __vram_copy(u16 dst, const void* src, u16 len);Paramètres
dstAdresse 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.srcPointeur 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.
lenNombre 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
__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é.


Commande de compilation
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-disasmImplé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
void __farcall_ptr(u8 bank, const void* func);Paramètres
bankNumé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.
funcAdresse 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
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.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/bank.h:15
kitaqgb/kitaqgb/CodeGenerator.cs:8335
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
u16 __farpeek16(u8 bank, const void* addr);Paramètres
bankNumé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.
addrPointeur 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
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.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/bank.h:13
kitaqgb/kitaqgb/CodeGenerator.cs:8295
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
u8 __farpeek8(u8 bank, const void* addr);Paramètres
bankNumé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.
addrPointeur 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
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.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/bank.h:12
kitaqgb/kitaqgb/CodeGenerator.cs:8262
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
u8 __cgb_is_cgb();Valeur renvoyée
u8 : 0 en mode DMG, 1 en mode CGB.
Exemple d’utilisation
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.




Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_palette.h:8
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
void __cgb_safe_set_bcpd(u8 value);Paramètres
valueUn 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
__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.




Commande de compilation
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-disasmImplé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
void __cgb_safe_set_bcps(u8 value);Paramètres
valueIndice 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
__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.




Commande de compilation
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-disasmImplé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
void __cgb_safe_set_ocpd(u8 value);Paramètres
valueUn 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
__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.




Commande de compilation
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-disasmImplé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
void __cgb_safe_set_ocps(u8 value);Paramètres
valueIndice 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
__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.




Commande de compilation
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-disasmImplé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
void __cgb_safe_set_vbk(u8 value);Paramètres
valueSélectionne la banque VRAM de la CPU par VBK (
$FF4F) sur CGB. Seulvalue & 1est é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
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.




Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:28
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
void __settile(u8 x, u8 y, u8 tile);Paramètres
x, yCoordonné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.
tileNumé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
// __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.
A

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:31
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
void __settile_bulk(u16 dest, const u8* src, u8 count);Paramètres
destAdresse du premier octet en VRAM. Pour une carte, utilisez
0x9800–0x9FFFet gardez tout le transfert dans la carte visée.srcOctets 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é.
countNombre 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
// __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.
ABC

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:48
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
void __settile_bulk_fast(u16 dest, const u8* src, u8 count);Paramètres
destAdresse du premier octet en VRAM. Pour une carte, utilisez
0x9800–0x9FFFet gardez tout le transfert dans la carte visée.srcOctets 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é.
countNombre 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
// __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.
ABC

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:50
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
void __settile_unsafe(u8 x, u8 y, u8 tile);Paramètres
x, yCoordonné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.
tileNumé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
// __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.
A

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:33
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
void __settileat(u16 base, u8 x, u8 y, u8 tile);Paramètres
basebasechoisit la carte (0x9800ou0x9C00) ; l’adresse de l’octet estbase + 32*y + x.x, yCoordonné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.
tileNumé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
// __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.
A

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:35
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
void __settileat_unsafe(u16 base, u8 x, u8 y, u8 tile);Paramètres
basebasechoisit la carte (0x9800ou0x9C00) ; l’adresse de l’octet estbase + 32*y + x.x, yCoordonné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.
tileNumé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
// __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.
A

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:37
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
void __settileatattr(u16 base, u8 x, u8 y, u8 attr);Paramètres
basebasechoisit la carte (0x9800ou0x9C00) ; l’adresse de l’octet estbase + 32*y + x.x, yCoordonné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.
attrOctet 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
// __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.
A

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:62
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
void __settileatattr_unsafe(u16 base, u8 x, u8 y, u8 attr);Paramètres
basebasechoisit la carte (0x9800ou0x9C00) ; l’adresse de l’octet estbase + 32*y + x.x, yCoordonné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.
attrOctet 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
// __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.
A

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:64
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
void __settileatcgb(u16 base, u8 x, u8 y, u8 tile, u8 attr);Paramètres
basebasechoisit la carte (0x9800ou0x9C00) ; l’adresse de l’octet estbase + 32*y + x.x, yCoordonné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.
tileNumé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.
attrOctet 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
// __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.
A

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:66
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
void __settileatcgb_unsafe(u16 base, u8 x, u8 y, u8 tile, u8 attr);Paramètres
basebasechoisit la carte (0x9800ou0x9C00) ; l’adresse de l’octet estbase + 32*y + x.x, yCoordonné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.
tileNumé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.
attrOctet 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
// __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.
A

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:68
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
void __settileattr(u8 x, u8 y, u8 attr);Paramètres
x, yCoordonné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.
attrOctet 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
// __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.
A

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:53
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
void __settileattr_bulk(u16 dest, const u8* src, u8 count);Paramètres
destAdresse du premier octet en VRAM. Pour une carte, utilisez
0x9800–0x9FFFet gardez tout le transfert dans la carte visée.srcOctets 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é.
countNombre 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
// __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.
ABC

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:89
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
void __settileattr_bulk_fast(u16 dest, const u8* src, u8 count);Paramètres
destAdresse du premier octet en VRAM. Pour une carte, utilisez
0x9800–0x9FFFet gardez tout le transfert dans la carte visée.srcOctets 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é.
countNombre 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
// __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.
ABC

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:91
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
void __settileattr_unsafe(u8 x, u8 y, u8 attr);Paramètres
x, yCoordonné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.
attrOctet 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
// __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.
A

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:55
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
void __settilebg(u8 x, u8 y, u8 tile);Paramètres
x, yCoordonné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.
tileNumé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
// __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.
A

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:43
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
void __settilebg_unsafe(u8 x, u8 y, u8 tile);Paramètres
x, yCoordonné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.
tileNumé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
// __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.
A

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:45
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
void __settilebgattr(u8 x, u8 y, u8 attr);Paramètres
x, yCoordonné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.
attrOctet 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
// __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.
A

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:80
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
void __settilebgattr_unsafe(u8 x, u8 y, u8 attr);Paramètres
x, yCoordonné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.
attrOctet 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
// __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.
A

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:82
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
void __settilebgcgb(u8 x, u8 y, u8 tile, u8 attr);Paramètres
x, yCoordonné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.
tileNumé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.
attrOctet 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
// __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.
A

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:84
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
void __settilebgcgb_unsafe(u8 x, u8 y, u8 tile, u8 attr);Paramètres
x, yCoordonné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.
tileNumé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.
attrOctet 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
// __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.
A

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:86
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
void __settilecgb(u8 x, u8 y, u8 tile, u8 attr);Paramètres
x, yCoordonné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.
tileNumé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.
attrOctet 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
// __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.
A

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:57
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
void __settilecgb_bulk(u16 dest, const u8* tile_src, const u8* attr_src, u8 count);Paramètres
destAdresse du premier octet en VRAM. Pour une carte, utilisez
0x9800–0x9FFFet gardez tout le transfert dans la carte visée.tile_src, attr_srcOctets 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é.
countNombre 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
// __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.
ABC

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:93
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
void __settilecgb_bulk_fast(u16 dest, const u8* tile_src, const u8* attr_src, u8 count);Paramètres
destAdresse du premier octet en VRAM. Pour une carte, utilisez
0x9800–0x9FFFet gardez tout le transfert dans la carte visée.tile_src, attr_srcOctets 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é.
countNombre 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
// __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.
ABC

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:95
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
void __settilecgb_unsafe(u8 x, u8 y, u8 tile, u8 attr);Paramètres
x, yCoordonné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.
tileNumé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.
attrOctet 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
// __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.
A

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:59
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
void __settilewin(u8 x, u8 y, u8 tile);Paramètres
x, yCoordonné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.
tileNumé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
// __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.
A

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:39
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
void __settilewin_unsafe(u8 x, u8 y, u8 tile);Paramètres
x, yCoordonné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.
tileNumé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
// __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.
A

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:41
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
void __settilewinattr(u8 x, u8 y, u8 attr);Paramètres
x, yCoordonné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.
attrOctet 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
// __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.
A

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:71
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
void __settilewinattr_unsafe(u8 x, u8 y, u8 attr);Paramètres
x, yCoordonné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.
attrOctet 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
// __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.
A

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:73
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
void __settilewincgb(u8 x, u8 y, u8 tile, u8 attr);Paramètres
x, yCoordonné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.
tileNumé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.
attrOctet 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
// __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.
A

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:75
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
void __settilewincgb_unsafe(u8 x, u8 y, u8 tile, u8 attr);Paramètres
x, yCoordonné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.
tileNumé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.
attrOctet 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
// __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.
A

Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/cgb_tile.h:77
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
u16 __readpadex(u8 prev_keys);Paramètres
prev_keysLe 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
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.


Commande de compilation
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-cacheImplé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
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
symbolNom 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
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.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:432
kitaqgb/kitaqgb/CodeGenerator.cs:7360
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
void __bankswitch(u8 bank);Paramètres
bankNumé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
__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.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/bank.h:11
kitaqgb/kitaqgb/CodeGenerator.cs:7268
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
__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
valueUn 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
__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.




Commande de compilation
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-disasmImplé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
__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
valueIndice 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
__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.




Commande de compilation
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-disasmImplé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
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
valueValeur 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
__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.



Commande de compilation
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-disasmImplé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
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
valueValeur 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
__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.



Commande de compilation
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-disasmImplé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
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
valueValeur 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
__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.



Commande de compilation
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-disasmImplé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
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
valueValeur 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
__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.



Commande de compilation
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-disasmImplé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
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
valueValeur 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
__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.



Commande de compilation
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-disasmImplé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
__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
valueUn 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
__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.




Commande de compilation
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-disasmImplé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
__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
valueIndice 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
__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.




Commande de compilation
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-disasmImplé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
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
valueValeur 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
__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.

Commande de compilation
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-disasmImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:1862
__critical_enter
Incrémente la profondeur logicielle d’imbrication et exécute DI.
Syntaxe
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
*((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.


Commande de compilation
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=noneImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:7970
kitaqgb/kitaqgb/CodeGenerator.cs:7970
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
void __critical_leave(u8 token);Paramètres
tokenJeton 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
__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.


Commande de compilation
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=noneImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:7984
kitaqgb/kitaqgb/CodeGenerator.cs:7984
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
u16 __dot2_q8_8(u16 a0, u16 a1, u8 b0, u8 b1);Paramètres
a0Mot non signé de 0 à 65535.
a1Mot non signé de 0 à 65535.
b0Coefficient non signé de 0 à 255.
b1Coefficient 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
// 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.


Commande de compilation
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=noneImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:11575
kitaqgb/kitaqgb/CodeGenerator.cs:11575
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
u16 __dot3_q8_8(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);Paramètres
a0Mot non signé de 0 à 65535.
a1Mot non signé de 0 à 65535.
a2Mot non signé de 0 à 65535.
b0Coefficient non signé de 0 à 255.
b1Coefficient non signé de 0 à 255.
b2Coefficient 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
// 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.


Commande de compilation
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=noneImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:11550
kitaqgb/kitaqgb/CodeGenerator.cs:11550
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
__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
bankNumé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.
funcNom 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
__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.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:7288
kitaqgb/kitaqgb/CodeGenerator.cs:7288
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
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
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.


Commande de compilation
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=noneImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:10642
kitaqgb/kitaqgb/CodeGenerator.cs:10642
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
u16 __mac16(u16 acc, u16 a, u8 b);Paramètres
accAccumulateur sur 16 bits à ajouter.
aMot non signé de 0 à 65535.
bCoefficient 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
// 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.


Commande de compilation
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=noneImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:11499
kitaqgb/kitaqgb/CodeGenerator.cs:11499
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
u16 __mul16x8(u16 a, u8 b);Paramètres
aMot non signé de 0 à 65535.
bCoefficient 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
// 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.


Commande de compilation
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=noneImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:11467
kitaqgb/kitaqgb/CodeGenerator.cs:11467
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
u8 __mul8x8_hi(u8 a, u8 b);Paramètres
aOctet non signé de 0 à 255.
bOctet non signé de 0 à 255.
Valeur renvoyée
floor(a*b/256), dans l’intervalle 0..254.
Exemple d’utilisation
// 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.


Commande de compilation
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=noneImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:11452
kitaqgb/kitaqgb/CodeGenerator.cs:11452
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
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
statePointeur 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.keysMasque complet des boutons maintenus au format GB
BTN_*. Fournissez le résultat de__readpad, l’octet faible de__readpadexou celui deinput_current; la fonction de répétition ne lit pas le matériel.triggerBits nouvellement pressés pendant cette mise à jour, au même format que
keys: en généralkeys & ~previous_keysou l’octet fort de__readpadex. N’incluez que des bits également présents danskeys. Passer systématiquement le masque de maintien relance à tort le nouvel appui à chaque appel.maskBits 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
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.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:11249
{
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
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
statePointeur 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.keysMasque complet des boutons maintenus au format GB
BTN_*. Fournissez le résultat de__readpad, l’octet faible de__readpadexou celui deinput_current; la fonction de répétition ne lit pas le matériel.triggerBits nouvellement pressés pendant cette mise à jour, au même format que
keys: en généralkeys & ~previous_keysou l’octet fort de__readpadex. N’incluez que des bits également présents danskeys. 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
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.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:11069
{
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
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
statePointeur 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.dasDé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.
arrIntervalle 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
__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.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:10808
{
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
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
statePointeur 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.keysMasque complet des boutons maintenus au format GB
BTN_*. Fournissez le résultat de__readpad, l’octet faible de__readpadexou celui deinput_current; la fonction de répétition ne lit pas le matériel.triggerBits nouvellement pressés pendant cette mise à jour, au même format que
keys: en généralkeys & ~previous_keysou l’octet fort de__readpadex. N’incluez que des bits également présents danskeys. 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
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.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:10864
{
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
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
statePointeur 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.keysMasque complet des boutons maintenus au format GB
BTN_*. Fournissez le résultat de__readpad, l’octet faible de__readpadexou celui deinput_current; la fonction de répétition ne lit pas le matériel.triggerBits nouvellement pressés pendant cette mise à jour, au même format que
keys: en généralkeys & ~previous_keysou l’octet fort de__readpadex. N’incluez que des bits également présents danskeys. Passer systématiquement le masque de maintien relance à tort le nouvel appui à chaque appel.maskBits 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
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.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:11249
{
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
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
statePointeur 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
__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.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:10840
{
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
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
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.


Commande de compilation
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-cacheImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:10669
{
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
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
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.


Commande de compilation
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-cacheImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:10729
{
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
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
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.


Commande de compilation
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-cacheImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:10706
{
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
u16 __sdot2_q1_7(u16 a0, u16 a1, u8 b0, u8 b1);Paramètres
a0Motif de bits signé sur 16 bits représentant −32768 à 32767.
a1Motif de bits signé sur 16 bits représentant −32768 à 32767.
b0Motif de bits d’un octet signé, coefficient de −128 à 127.
b1Motif 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
// 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.


Commande de compilation
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=noneImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:11732
kitaqgb/kitaqgb/CodeGenerator.cs:11732
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
u16 __sdot2_q8_8(u16 a0, u16 a1, u8 b0, u8 b1);Paramètres
a0Motif de bits signé sur 16 bits représentant −32768 à 32767.
a1Motif de bits signé sur 16 bits représentant −32768 à 32767.
b0Motif de bits d’un octet signé, coefficient de −128 à 127.
b1Motif 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
// 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.


Commande de compilation
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=noneImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:11654
kitaqgb/kitaqgb/CodeGenerator.cs:11654
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
u16 __sdot3_q1_7(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);Paramètres
a0Motif de bits signé sur 16 bits représentant −32768 à 32767.
a1Motif de bits signé sur 16 bits représentant −32768 à 32767.
a2Motif de bits signé sur 16 bits représentant −32768 à 32767.
b0Motif de bits d’un octet signé, coefficient de −128 à 127.
b1Motif de bits d’un octet signé, coefficient de −128 à 127.
b2Motif 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
// 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.


Commande de compilation
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=noneImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:11790
kitaqgb/kitaqgb/CodeGenerator.cs:11790
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
u16 __sdot3_q8_8(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);Paramètres
a0Motif de bits signé sur 16 bits représentant −32768 à 32767.
a1Motif de bits signé sur 16 bits représentant −32768 à 32767.
a2Motif de bits signé sur 16 bits représentant −32768 à 32767.
b0Motif de bits d’un octet signé, coefficient de −128 à 127.
b1Motif de bits d’un octet signé, coefficient de −128 à 127.
b2Motif 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
// 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.


Commande de compilation
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=noneImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:11767
kitaqgb/kitaqgb/CodeGenerator.cs:11767
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
__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, yCoordonné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.
tileNumé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
// __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.
A

Commande de compilation
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-disasmImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:9321
{
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
__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
bufTampon 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 sont0,1,32,33.x, yCoordonné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.
tileNumé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
// __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.
AB
CD

Commande de compilation
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-disasmImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:10564
{
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
__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
bufTampon 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, countL’ordre est
y,x,count.yest une ligne de cellules de 16 pixels (0–15) ;xetcountsont en tuiles de 8 pixels. Respectezx+count <= 32. Pour une cellule en(2,3), utilisezy=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
// __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.
AB
CD

Commande de compilation
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-disasmImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:10581
{
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
__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, attrbufTampon 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 sont0,1,32,33.x, yCoordonné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.
tileNumé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.
attrOctet 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
// __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.
AB
CD

Commande de compilation
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-disasmImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:10572
{
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
__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, attrbufTampon 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, countL’ordre est
y,x,count.yest une ligne de cellules de 16 pixels (0–15) ;xetcountsont en tuiles de 8 pixels. Respectezx+count <= 32. Pour une cellule en(2,3), utilisezy=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
// __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.
AB
CD

Commande de compilation
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-disasmImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:10589
{
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
u16 __smac16(u16 acc, u16 a, u8 b);Paramètres
accAccumulateur sur 16 bits à ajouter.
aMotif de bits signé sur 16 bits représentant −32768 à 32767.
bMotif 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
// 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.


Commande de compilation
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=noneImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:11516
kitaqgb/kitaqgb/CodeGenerator.cs:11516
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
u16 __smac16_q1_7(u16 acc, u16 a, u8 b);Paramètres
accAccumulateur sur 16 bits à ajouter.
aMotif de bits signé sur 16 bits représentant −32768 à 32767.
bMotif 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
// 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.


Commande de compilation
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=noneImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:11533
kitaqgb/kitaqgb/CodeGenerator.cs:11533
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
u16 __smul16x8_q1_7(u16 a, u8 b);Paramètres
aMotif de bits signé sur 16 bits représentant −32768 à 32767.
bMotif 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
// 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.


Commande de compilation
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=noneImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:11490
kitaqgb/kitaqgb/CodeGenerator.cs:11490
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
u8 __sram_read8(u16 address);Paramètres
addressAdresse 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
*((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.


Commande de compilation
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=8kImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:8825
kitaqgb/kitaqgb/CodeGenerator.cs:8825
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
void __sram_write8(u16 address, u8 value);Paramètres
addressAdresse CPU dans $A000..$BFFF.
valueValeur 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
__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.


Commande de compilation
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=8kImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:8846
kitaqgb/kitaqgb/CodeGenerator.cs:8846
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
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
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.

Commande de compilation
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-disasmImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:7387
{
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
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
bankBanque 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
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.

Commande de compilation
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-disasmImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:7397
{
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
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
dstAdresse 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.srcPointeur 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.
lenNombre 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
// 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é.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:8104
{
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
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
dstAdresse 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.valueUn 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.
lenNombre 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
__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é.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:8196
{
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
void __wait_ly(u8 ly);Paramètres
lyLigne 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
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.


Commande de compilation
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=noneImplémentation et source
kitaqgb/kitaqgb/CodeGenerator.cs:7483
kitaqgb/kitaqgb/CodeGenerator.cs:7483
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
s16 __smul16x8(s16 a, s8 b);Paramètres
aValeur signée de 16 bits à mettre à l’échelle.
bCoefficient 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
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.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/physics2d.h:9
Multiply a signed word by a signed byte; divide by 256, truncating toward zero.
kitaqgb/kitaqgb/CodeGenerator.cs:11478
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
void __bit_clear(u8* p, u16 bit_index);Paramètres
pPointeur 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_indexIndice de bit
u16à partir de zéro (0–65535), et non décalage d’octet ou masque. L’indice 0 choisit le masque0x01dans l’octet 0 ; 7 choisit0x80dans l’octet 0 ; 8 choisit0x01dans l’octet 1. En général, le décalage estindex / 8et le masque1 << (index % 8). Pour un tableau de N octets, exigezindex < 8*Net 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
__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.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/rpg.h:187
kitaqgb/kitaqgb/CodeGenerator.cs:8582
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
void __bit_set(u8* p, u16 bit_index);Paramètres
pPointeur 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_indexIndice de bit
u16à partir de zéro (0–65535), et non décalage d’octet ou masque. L’indice 0 choisit le masque0x01dans l’octet 0 ; 7 choisit0x80dans l’octet 0 ; 8 choisit0x01dans l’octet 1. En général, le décalage estindex / 8et le masque1 << (index % 8). Pour un tableau de N octets, exigezindex < 8*Net 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
__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.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/rpg.h:186
kitaqgb/kitaqgb/CodeGenerator.cs:8582
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
u8 __bit_test(const u8* p, u16 bit_index);Paramètres
pPointeur 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_indexIndice de bit
u16à partir de zéro (0–65535), et non décalage d’octet ou masque. L’indice 0 choisit le masque0x01dans l’octet 0 ; 7 choisit0x80dans l’octet 0 ; 8 choisit0x01dans l’octet 1. En général, le décalage estindex / 8et le masque1 << (index % 8). Pour un tableau de N octets, exigezindex < 8*Net 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
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.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/rpg.h:185
kitaqgb/kitaqgb/CodeGenerator.cs:8582
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
void __bit_toggle(u8* p, u16 bit_index);Paramètres
pPointeur 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_indexIndice de bit
u16à partir de zéro (0–65535), et non décalage d’octet ou masque. L’indice 0 choisit le masque0x01dans l’octet 0 ; 7 choisit0x80dans l’octet 0 ; 8 choisit0x01dans l’octet 1. En général, le décalage estindex / 8et le masque1 << (index % 8). Pour un tableau de N octets, exigezindex < 8*Net 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
__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.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/rpg.h:188
kitaqgb/kitaqgb/CodeGenerator.cs:8582
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
void __copy16(void* dst, const void* src);Paramètres
dstPointeur 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.
srcPointeur 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
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é.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/rpg.h:177
kitaqgb/kitaqgb/CodeGenerator.cs:8247
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
void __copy32(void* dst, const void* src);Paramètres
dstPointeur 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.
srcPointeur 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
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é.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/rpg.h:178
kitaqgb/kitaqgb/CodeGenerator.cs:8247
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
void __far_memcpy(void* dst, u8 bank, const void* src, u16 len);Paramètres
dstDestination 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.
bankSé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.
srcPointeur proche vers des octets lisibles dans la banque ROM choisie. Il ne contient pas de numéro de banque.
lenNombre 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
__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.


Commande de compilation
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-disasmImplé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
void __fill_tilemap(u16 dst, u8 value, u16 len);Paramètres
dstAdresse 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.valueUn 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.
lenNombre 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
__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é.


Commande de compilation
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-disasmImplé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
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
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.


Commande de compilation
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=noneImplémentation et source
kitaqgb/lib/rpg.h:140
kitaqgb/kitaqgb/CodeGenerator.cs:10619
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
u8 __manhattan(u8 x1, u8 y1, u8 x2, u8 y2);Paramètres
x1Coordonnée X du premier point, de 0 à 255.
y1Coordonnée Y du premier point, de 0 à 255.
x2Coordonnée X du second point, de 0 à 255.
y2Coordonné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
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.


Commande de compilation
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=noneImplémentation et source
kitaqgb/lib/rpg.h:183
kitaqgb/kitaqgb/CodeGenerator.cs:8516
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
u16 __map_index(u8 x, u8 y, u8 width);Paramètres
xColonne de 0 à 255, normalement inférieure à width.
yLigne de 0 à 255.
widthNombre 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
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.


Commande de compilation
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=noneImplémentation et source
kitaqgb/lib/rpg.h:181
kitaqgb/kitaqgb/CodeGenerator.cs:8365
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
void __memcpy(void* dst, const void* src, u16 count);Paramètres
dstPointeur 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.
srcPointeur vers le premier octet lisible de la source. Toute la zone doit rester accessible et inchangée pendant la copie.
countNombre 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
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é.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/rpg.h:142
kitaqgb/kitaqgb/CodeGenerator.cs:9084
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
void __memcpy_small(void* dst, const void* src, u8 len);Paramètres
dstPointeur 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.
srcPointeur vers le premier octet lisible de la source. Toute la zone doit rester accessible et inchangée pendant la copie.
lenNombre 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
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é.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/rpg.h:175
kitaqgb/kitaqgb/CodeGenerator.cs:9084
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
void __memset(void* dst, u8 value, u16 count);Paramètres
dstPointeur 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.
valueOctet répété à chaque adresse de destination : 0–255. La valeur 0 remet la zone à zéro. Pour initialiser un tableau de
u16avec un nombre non nul, affectez ce nombre à chaque élément.countNombre 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
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é.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/rpg.h:141
kitaqgb/kitaqgb/CodeGenerator.cs:8926
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
void __memset_small(void* dst, u8 value, u8 len);Paramètres
dstPointeur 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.
valueOctet répété à chaque adresse de destination : 0–255. La valeur 0 remet la zone à zéro. Pour initialiser un tableau de
u16avec un nombre non nul, affectez ce nombre à chaque élément.lenNombre 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
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é.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/rpg.h:176
kitaqgb/kitaqgb/CodeGenerator.cs:8926
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
u16 __rle_decode_vram(u16 dst, u8 bank, const void* src);Paramètres
dstAdresse 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.
bankNumé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.
srcPointeur 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
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.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/rpg.h:191
Compiler intrinsic, separate from the RAM rle_decode helpers below; it is not a bounded-stream API.
kitaqgb/kitaqgb/CodeGenerator.cs:8673
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
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
__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.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/rpg.h:149
kitaqgb/kitaqgb/CodeGenerator.cs:8881
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
void __rng_seed(u16 seed);Paramètres
seedGraine 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
__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.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/rpg.h:150
kitaqgb/kitaqgb/CodeGenerator.cs:8868
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
void __scroll_bg_set(u8 scx, u8 scy);Paramètres
scxDéfilement horizontal de 0 à 255 pixels.
scyDé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
__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.


Commande de compilation
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=noneImplémentation et source
kitaqgb/lib/rpg.h:151
kitaqgb/kitaqgb/CodeGenerator.cs:7507
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
void __settile_col(u8 x, u8 y, const u8* src, u8 len);Paramètres
x, yCoordonné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.
srcOctets 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é.
lenNombre 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
// __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.
A
B
C

Commande de compilation
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-disasmImplé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
void __settile_rect(u8 x, u8 y, u8 w, u8 h, u8 tile);Paramètres
x, yCoordonné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, hLargeur et hauteur en tuiles de 8×8. Respectez
x+w <= 32ety+h <= 32. Une dimension nulle n’effectue aucune écriture.tileNumé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
// __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.
AAA
AAA

Commande de compilation
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-disasmImplé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
void __settile_row(u8 x, u8 y, const u8* src, u8 len);Paramètres
x, yCoordonné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.
srcOctets 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é.
lenNombre 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
// __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.
ABC

Commande de compilation
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-disasmImplé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
void __settile_xy(u8 x, u8 y, u8 tile);Paramètres
x, yCoordonné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.
tileNumé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
// __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.
A

Commande de compilation
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-disasmImplé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
void __settilemap_rect(u16 base, u8 x, u8 y, u8 w, u8 h, const u8* src);Paramètres
basebasechoisit la carte (0x9800ou0x9C00) ; l’adresse de l’octet estbase + 32*y + x.x, yCoordonné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, hLargeur et hauteur en tuiles de 8×8. Respectez
x+w <= 32ety+h <= 32. Une dimension nulle n’effectue aucune écriture.srcOctets 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
// __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.
ABC
DEF

Commande de compilation
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-disasmImplé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
u16 __tile_addr(u16 base, u8 x, u8 y);Paramètres
baseBase de la carte de tuiles, généralement $9800/$9C00.
xColonne, généralement de 0 à 31.
yLigne, 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
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.


Commande de compilation
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=noneImplémentation et source
kitaqgb/lib/rpg.h:180
kitaqgb/kitaqgb/CodeGenerator.cs:8355
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
void __vram_copy_dma(u16 dst, const void* src, u16 len);Paramètres
dstAdresse 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.srcPointeur 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.
lenNombre 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
__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é.


Commande de compilation
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-disasmImplé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
void __vram_copy_hblank(u16 dst, const void* src, u16 len);Paramètres
dstAdresse 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.srcPointeur 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.
lenNombre 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
__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é.


Commande de compilation
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-disasmImplé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
void __vram_fill(u16 dst, u8 value, u16 len);Paramètres
dstAdresse 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.valueUn 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.
lenNombre 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
__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é.


Commande de compilation
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-disasmImplé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
void __vram_memcpy(u16 dst, const void* src, u16 len);Paramètres
dstAdresse 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.srcPointeur 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.
lenNombre 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
__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é.


Commande de compilation
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-disasmImplé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
void __vram_memset(u16 dst, u8 value, u16 len);Paramètres
dstAdresse 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.valueUn 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.
lenNombre 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
__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é.


Commande de compilation
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-disasmImplé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
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
__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.


Commande de compilation
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=noneImplémentation et source
kitaqgb/lib/rpg.h:138
kitaqgb/kitaqgb/CodeGenerator.cs:7451
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
u8 __xy_in_rect(u8 x, u8 y, u8 rx, u8 ry, u8 rw, u8 rh);Paramètres
xX du point, de 0 à 255.
yY du point, de 0 à 255.
rxBord gauche du rectangle, de 0 à 255.
ryBord supérieur du rectangle, de 0 à 255.
rwLargeur de 0 à 255.
rhHauteur 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
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.


Commande de compilation
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=noneImplémentation et source
kitaqgb/lib/rpg.h:182
kitaqgb/kitaqgb/CodeGenerator.cs:8436
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
void __scroll_bg_add(s8 dx, s8 dy);Paramètres
dxDéplacement horizontal signé en pixels, de −128 à 127.
dyDé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
__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.


Commande de compilation
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=64kExemple d’utilisation
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.


Commande de compilation
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=64kImplémentation et source
kitaqgb/lib/scroll.h:37
kitaqgb/kitaqgb/CodeGenerator.cs:7835
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
void __scroll_bg_set_buffered(u8 scx, u8 scy);Paramètres
scxDéfilement horizontal du fond, de 0 à 255 pixels ; l’augmenter déplace le fond vers la gauche.
scyDé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
__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.


Commande de compilation
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=64kExemple d’utilisation
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.


Commande de compilation
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=64kImplémentation et source
kitaqgb/lib/scroll.h:22
kitaqgb/kitaqgb/CodeGenerator.cs:7671
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
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
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.


Commande de compilation
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=64kExemple d’utilisation
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.


Commande de compilation
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=64kImplémentation et source
kitaqgb/lib/scroll.h:13
kitaqgb/kitaqgb/CodeGenerator.cs:7571
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
void __scroll_bg_x_set(u8 scx);Paramètres
scxDé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
__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.


Commande de compilation
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=64kExemple d’utilisation
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.


Commande de compilation
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=64kImplémentation et source
kitaqgb/lib/scroll.h:11
kitaqgb/kitaqgb/CodeGenerator.cs:7535
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
void __scroll_bg_x_set_buffered(u8 scx);Paramètres
scxDé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
__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.


Commande de compilation
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=64kExemple d’utilisation
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.


Commande de compilation
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=64kImplémentation et source
kitaqgb/lib/scroll.h:23
kitaqgb/kitaqgb/CodeGenerator.cs:7693
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
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
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.


Commande de compilation
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=64kExemple d’utilisation
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.


Commande de compilation
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=64kImplémentation et source
kitaqgb/lib/scroll.h:14
kitaqgb/kitaqgb/CodeGenerator.cs:7580
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
void __scroll_bg_y_set(u8 scy);Paramètres
scyDé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
__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.


Commande de compilation
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=64kExemple d’utilisation
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.


Commande de compilation
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=64kImplémentation et source
kitaqgb/lib/scroll.h:12
kitaqgb/kitaqgb/CodeGenerator.cs:7553
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
void __scroll_bg_y_set_buffered(u8 scy);Paramètres
scyDé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
__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.


Commande de compilation
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=64kExemple d’utilisation
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.


Commande de compilation
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=64kImplémentation et source
kitaqgb/lib/scroll.h:24
kitaqgb/kitaqgb/CodeGenerator.cs:7707
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
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
__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.


Commande de compilation
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=64kExemple d’utilisation
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.


Commande de compilation
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=64kImplémentation et source
kitaqgb/lib/scroll.h:28
kitaqgb/kitaqgb/CodeGenerator.cs:7771
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
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
__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.


Commande de compilation
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=64kImplémentation et source
kitaqgb/lib/scroll.h:33
kitaqgb/kitaqgb/CodeGenerator.cs:7959
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
void __scroll_split_push(u8 ly, u8 scx, u8 scy);Paramètres
lyNumé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.
scxDéfilement horizontal du fond, de 0 à 255 pixels ; l’augmenter déplace le fond vers la gauche.
scyDé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
__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.


Commande de compilation
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=64kImplémentation et source
kitaqgb/lib/scroll.h:31
kitaqgb/kitaqgb/CodeGenerator.cs:7907
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
void __scroll_split_push_ex(u8 ly, u8 scx, u8 scy, u8 wx, u8 wy, u8 flags);Paramètres
lyNumé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.
scxDéfilement horizontal du fond, de 0 à 255 pixels ; l’augmenter déplace le fond vers la gauche.
scyDéfilement vertical du fond, de 0 à 255 pixels ; l’augmenter déplace le fond vers le haut.
wxOctet WX brut, de 0 à 255 : ajoutez 7 au X voulu à l’écran ; le bord gauche utilise 7.
wyHaut de la fenêtre en pixels Y de l’écran, de 0 à 255 ; les lignes visibles vont de 0 à 143.
flagsBits 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
__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.


Commande de compilation
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=64kImplémentation et source
kitaqgb/lib/scroll.h:32
kitaqgb/kitaqgb/CodeGenerator.cs:7929
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
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
__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.


Commande de compilation
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=64kImplémentation et source
kitaqgb/lib/scroll.h:30
kitaqgb/kitaqgb/CodeGenerator.cs:7897
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
void __scroll_win_add(s8 dx, s8 dy);Paramètres
dxDéplacement horizontal signé en pixels, de −128 à 127.
dyDé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
__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.


Commande de compilation
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=64kExemple d’utilisation
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.


Commande de compilation
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=64kImplémentation et source
kitaqgb/lib/scroll.h:38
kitaqgb/kitaqgb/CodeGenerator.cs:7866
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
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
__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.


Commande de compilation
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=64kExemple d’utilisation
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.


Commande de compilation
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=64kImplémentation et source
kitaqgb/lib/scroll.h:36
kitaqgb/kitaqgb/CodeGenerator.cs:7821
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
void __scroll_win_set(u8 wx, u8 wy);Paramètres
wxOctet WX brut, de 0 à 255 : ajoutez 7 au X voulu à l’écran ; le bord gauche utilise 7.
wyHaut 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
__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.


Commande de compilation
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=64kExemple d’utilisation
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.


Commande de compilation
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=64kImplémentation et source
kitaqgb/lib/scroll.h:16
kitaqgb/kitaqgb/CodeGenerator.cs:7589
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
void __scroll_win_set_buffered(u8 wx, u8 wy);Paramètres
wxOctet WX brut, de 0 à 255 : ajoutez 7 au X voulu à l’écran ; le bord gauche utilise 7.
wyHaut 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
__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.


Commande de compilation
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=64kExemple d’utilisation
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.


Commande de compilation
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=64kImplémentation et source
kitaqgb/lib/scroll.h:25
kitaqgb/kitaqgb/CodeGenerator.cs:7721
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
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
__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.


Commande de compilation
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=64kExemple d’utilisation
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.


Commande de compilation
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=64kImplémentation et source
kitaqgb/lib/scroll.h:35
kitaqgb/kitaqgb/CodeGenerator.cs:7807
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
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
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.


Commande de compilation
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=64kExemple d’utilisation
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.


Commande de compilation
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=64kImplémentation et source
kitaqgb/lib/scroll.h:19
kitaqgb/kitaqgb/CodeGenerator.cs:7653
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
void __scroll_win_x_set(u8 wx);Paramètres
wxOctet 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
__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.


Commande de compilation
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=64kExemple d’utilisation
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.


Commande de compilation
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=64kImplémentation et source
kitaqgb/lib/scroll.h:17
kitaqgb/kitaqgb/CodeGenerator.cs:7617
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
void __scroll_win_x_set_buffered(u8 wx);Paramètres
wxOctet 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
__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.


Commande de compilation
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=64kExemple d’utilisation
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.


Commande de compilation
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=64kImplémentation et source
kitaqgb/lib/scroll.h:26
kitaqgb/kitaqgb/CodeGenerator.cs:7743
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
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
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.


Commande de compilation
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=64kExemple d’utilisation
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.


Commande de compilation
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=64kImplémentation et source
kitaqgb/lib/scroll.h:20
kitaqgb/kitaqgb/CodeGenerator.cs:7662
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
void __scroll_win_y_set(u8 wy);Paramètres
wyHaut 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
__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.


Commande de compilation
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=64kExemple d’utilisation
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.


Commande de compilation
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=64kImplémentation et source
kitaqgb/lib/scroll.h:18
kitaqgb/kitaqgb/CodeGenerator.cs:7635
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
void __scroll_win_y_set_buffered(u8 wy);Paramètres
wyHaut 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
__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.


Commande de compilation
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=64kExemple d’utilisation
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.


Commande de compilation
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=64kImplémentation et source
kitaqgb/lib/scroll.h:27
kitaqgb/kitaqgb/CodeGenerator.cs:7757
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
void __oam_dma(u16 src_ptr);Paramètres
src_ptrAdresse source en octets. Seul l’octet de poids fort est utilisé :
0xC480sélectionne0xC400–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
// 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é.


Commande de compilation
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-disasmImplémentation et source
kitaqgb/lib/sprite.h:46
// 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
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: kitaqgb kqhelp <KQxxxx>usage: kitaqgb symfind <pattern> [--map=<.map>] [--dbg=<.dbg>] [--regex] [--ignore-case] [--out=<file>|-]usage: kitaqgb src2asm <source.c:line> [--disasm=<dis.s>] [--funcsizes=<.funcsizes.txt>] [--context=N] [--out=<file>|-]usage: kitaqgb romdiff <old.gb> <new.gb> [--old-func=<.funcsizes.txt>] [--new-func=<.funcsizes.txt>] [--old-map=<.map>] [--new-map=<.map>] [--out=<file>|-]usage: kitaqgb template [out.c] [--out=<file.c>] [--cgb] [--overwrite] [--no-build-script] [--no-readme]usage: kitaqgb conventions [--out=<file>|--stdout]usage: kitaqgb snippet list | get <id> [--out=<file>|-] | emit <category|all> [--out=<file>|-]usage: kitaqgb fixhint [diag.json|stderr.log] [--in=<file>] [--out=<file>|-] [--max=N]usage: kitaqgb irsum [--ir=<syntax_tree_lowered.txt>] [--asm=<assembly_code.txt>] [--top=N] [--out=<file>|-]usage: kitaqgb devserver [--poll-ms=N] [--debounce-ms=N] [--once] [--max-builds=N] [--deps=<file>] <compile args...>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-outAnnexe : 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.
| opcode | Instruction | Format | Exemple de syntaxe |
|---|---|---|---|
| 00 | NOP | IMP | NOP |
| F3 | DI | IMP | DI |
| FB | EI | IMP | EI |
| 76 | HALT | IMP | HALT |
| 10 | STOP | IMM8 | STOP #1 |
| C9 | RET | IMP | RET |
| C0 | RET_NZ | IMP | RET_NZ |
| C8 | RET_Z | IMP | RET_Z |
| D0 | RET_NC | IMP | RET_NC |
| D8 | RET_C | IMP | RET_C |
| D9 | RETI | IMP | RETI |
| C3 | JP | ABS | JP 0xC000 |
| E9 | JP_HL | IMP | JP_HL |
| 18 | JR | REL | JR +target |
| 20 | JR_NZ | REL | JR_NZ +target |
| 28 | JR_Z | REL | JR_Z +target |
| 30 | JR_NC | REL | JR_NC +target |
| 38 | JR_C | REL | JR_C +target |
| CD | CALL | ABS | CALL 0xC000 |
| C4 | CALL_NZ | ABS | CALL_NZ 0xC000 |
| CC | CALL_Z | ABS | CALL_Z 0xC000 |
| D4 | CALL_NC | ABS | CALL_NC 0xC000 |
| DC | CALL_C | ABS | CALL_C 0xC000 |
| C2 | JP_NZ | ABS | JP_NZ 0xC000 |
| CA | JP_Z | ABS | JP_Z 0xC000 |
| D2 | JP_NC | ABS | JP_NC 0xC000 |
| DA | JP_C | ABS | JP_C 0xC000 |
| C7 | RST_00 | IMP | RST_00 |
| CF | RST_08 | IMP | RST_08 |
| D7 | RST_10 | IMP | RST_10 |
| DF | RST_18 | IMP | RST_18 |
| E7 | RST_20 | IMP | RST_20 |
| EF | RST_28 | IMP | RST_28 |
| F7 | RST_30 | IMP | RST_30 |
| FF | RST_38 | IMP | RST_38 |
| 3E | LD_A_IMM | IMM8 | LD_A_IMM #1 |
| 06 | LD_B_IMM | IMM8 | LD_B_IMM #1 |
| 0E | LD_C_IMM | IMM8 | LD_C_IMM #1 |
| 16 | LD_D_IMM | IMM8 | LD_D_IMM #1 |
| 1E | LD_E_IMM | IMM8 | LD_E_IMM #1 |
| 26 | LD_H_IMM | IMM8 | LD_H_IMM #1 |
| 2E | LD_L_IMM | IMM8 | LD_L_IMM #1 |
| 7F | LD_A_A | IMP | LD_A_A |
| 78 | LD_A_B | IMP | LD_A_B |
| 79 | LD_A_C | IMP | LD_A_C |
| 7A | LD_A_D | IMP | LD_A_D |
| 7B | LD_A_E | IMP | LD_A_E |
| 7C | LD_A_H | IMP | LD_A_H |
| 7D | LD_A_L | IMP | LD_A_L |
| 47 | LD_B_A | IMP | LD_B_A |
| 40 | LD_B_B | IMP | LD_B_B |
| 41 | LD_B_C | IMP | LD_B_C |
| 42 | LD_B_D | IMP | LD_B_D |
| 43 | LD_B_E | IMP | LD_B_E |
| 44 | LD_B_H | IMP | LD_B_H |
| 45 | LD_B_L | IMP | LD_B_L |
| 4F | LD_C_A | IMP | LD_C_A |
| 48 | LD_C_B | IMP | LD_C_B |
| 49 | LD_C_C | IMP | LD_C_C |
| 4A | LD_C_D | IMP | LD_C_D |
| 4B | LD_C_E | IMP | LD_C_E |
| 4C | LD_C_H | IMP | LD_C_H |
| 4D | LD_C_L | IMP | LD_C_L |
| 57 | LD_D_A | IMP | LD_D_A |
| 50 | LD_D_B | IMP | LD_D_B |
| 51 | LD_D_C | IMP | LD_D_C |
| 52 | LD_D_D | IMP | LD_D_D |
| 53 | LD_D_E | IMP | LD_D_E |
| 54 | LD_D_H | IMP | LD_D_H |
| 55 | LD_D_L | IMP | LD_D_L |
| 5F | LD_E_A | IMP | LD_E_A |
| 58 | LD_E_B | IMP | LD_E_B |
| 59 | LD_E_C | IMP | LD_E_C |
| 5A | LD_E_D | IMP | LD_E_D |
| 5B | LD_E_E | IMP | LD_E_E |
| 5C | LD_E_H | IMP | LD_E_H |
| 5D | LD_E_L | IMP | LD_E_L |
| 67 | LD_H_A | IMP | LD_H_A |
| 60 | LD_H_B | IMP | LD_H_B |
| 61 | LD_H_C | IMP | LD_H_C |
| 62 | LD_H_D | IMP | LD_H_D |
| 63 | LD_H_E | IMP | LD_H_E |
| 64 | LD_H_H | IMP | LD_H_H |
| 65 | LD_H_L | IMP | LD_H_L |
| 6F | LD_L_A | IMP | LD_L_A |
| 68 | LD_L_B | IMP | LD_L_B |
| 69 | LD_L_C | IMP | LD_L_C |
| 6A | LD_L_D | IMP | LD_L_D |
| 6B | LD_L_E | IMP | LD_L_E |
| 6C | LD_L_H | IMP | LD_L_H |
| 6D | LD_L_L | IMP | LD_L_L |
| EA | LD_MEM_A | ABS | LD_MEM_A 0xC000 |
| FA | LD_A_MEM | ABS | LD_A_MEM 0xC000 |
| E0 | LDH_MEM_A | LDH | LDH_MEM_A 0x40 |
| F0 | LDH_A_MEM | LDH | LDH_A_MEM 0x40 |
| E2 | LD_C_MEM_A | IMP | LD_C_MEM_A |
| F2 | LD_A_MEM_C | IMP | LD_A_MEM_C |
| 77 | LD_HL_A | IMP | LD_HL_A |
| 7E | LD_A_HL | IMP | LD_A_HL |
| 46 | LD_B_HL | IMP | LD_B_HL |
| 4E | LD_C_HL | IMP | LD_C_HL |
| 56 | LD_D_HL | IMP | LD_D_HL |
| 5E | LD_E_HL | IMP | LD_E_HL |
| 66 | LD_H_HL | IMP | LD_H_HL |
| 6E | LD_L_HL | IMP | LD_L_HL |
| 70 | LD_HL_B | IMP | LD_HL_B |
| 71 | LD_HL_C | IMP | LD_HL_C |
| 72 | LD_HL_D | IMP | LD_HL_D |
| 73 | LD_HL_E | IMP | LD_HL_E |
| 74 | LD_HL_H | IMP | LD_HL_H |
| 75 | LD_HL_L | IMP | LD_HL_L |
| 36 | LD_HL_REF_IMM | IMM8 | LD_HL_REF_IMM #1 |
| 12 | LD_DE_A | IMP | LD_DE_A |
| 1A | LD_A_DE | IMP | LD_A_DE |
| 02 | LD_BC_A | IMP | LD_BC_A |
| 0A | LD_A_BC | IMP | LD_A_BC |
| 22 | LDI_HL_A | IMP | LDI_HL_A |
| 2A | LDI_A_HL | IMP | LDI_A_HL |
| 32 | LDD_HL_A | IMP | LDD_HL_A |
| 3A | LDD_A_HL | IMP | LDD_A_HL |
| 01 | LD_BC_IMM | IMM16 | LD_BC_IMM #0xC000 |
| 11 | LD_DE_IMM | IMM16 | LD_DE_IMM #0xC000 |
| 21 | LD_HL_IMM | IMM16 | LD_HL_IMM #0xC000 |
| 31 | LD_SP_IMM | IMM16 | LD_SP_IMM #0xC000 |
| F9 | LD_SP_HL | IMP | LD_SP_HL |
| F8 | LD_HL_SP_IMM | REL | LD_HL_SP_IMM +target |
| E8 | ADD_SP_IMM | REL | ADD_SP_IMM +target |
| 08 | LD_MEM_SP | IMM16 | LD_MEM_SP #0xC000 |
| C5 | PUSH_BC | IMP | PUSH_BC |
| C1 | POP_BC | IMP | POP_BC |
| D5 | PUSH_DE | IMP | PUSH_DE |
| D1 | POP_DE | IMP | POP_DE |
| E5 | PUSH_HL | IMP | PUSH_HL |
| E1 | POP_HL | IMP | POP_HL |
| F5 | PUSH_AF | IMP | PUSH_AF |
| F1 | POP_AF | IMP | POP_AF |
| 3C | INC_A | IMP | INC_A |
| 04 | INC_B | IMP | INC_B |
| 0C | INC_C | IMP | INC_C |
| 14 | INC_D | IMP | INC_D |
| 1C | INC_E | IMP | INC_E |
| 24 | INC_H | IMP | INC_H |
| 2C | INC_L | IMP | INC_L |
| 34 | INC_HL_REF | IMP | INC_HL_REF |
| 3D | DEC_A | IMP | DEC_A |
| 05 | DEC_B | IMP | DEC_B |
| 0D | DEC_C | IMP | DEC_C |
| 15 | DEC_D | IMP | DEC_D |
| 1D | DEC_E | IMP | DEC_E |
| 25 | DEC_H | IMP | DEC_H |
| 2D | DEC_L | IMP | DEC_L |
| 35 | DEC_HL_REF | IMP | DEC_HL_REF |
| 87 | ADD_A | IMP | ADD_A |
| 80 | ADD_B | IMP | ADD_B |
| 81 | ADD_C | IMP | ADD_C |
| 82 | ADD_D | IMP | ADD_D |
| 83 | ADD_E | IMP | ADD_E |
| 84 | ADD_H | IMP | ADD_H |
| 85 | ADD_L | IMP | ADD_L |
| 86 | ADD_HL_REF | IMP | ADD_HL_REF |
| C6 | ADD_A_IMM | IMM8 | ADD_A_IMM #1 |
| 8F | ADC_A | IMP | ADC_A |
| 88 | ADC_B | IMP | ADC_B |
| 89 | ADC_C | IMP | ADC_C |
| 8A | ADC_D | IMP | ADC_D |
| 8B | ADC_E | IMP | ADC_E |
| 8C | ADC_H | IMP | ADC_H |
| 8D | ADC_L | IMP | ADC_L |
| 8E | ADC_HL_REF | IMP | ADC_HL_REF |
| CE | ADC_IMM | IMM8 | ADC_IMM #1 |
| 97 | SUB_A | IMP | SUB_A |
| 90 | SUB_B | IMP | SUB_B |
| 91 | SUB_C | IMP | SUB_C |
| 92 | SUB_D | IMP | SUB_D |
| 93 | SUB_E | IMP | SUB_E |
| 94 | SUB_H | IMP | SUB_H |
| 95 | SUB_L | IMP | SUB_L |
| 96 | SUB_HL_REF | IMP | SUB_HL_REF |
| D6 | SUB_IMM | IMM8 | SUB_IMM #1 |
| 9F | SBC_A | IMP | SBC_A |
| 98 | SBC_B | IMP | SBC_B |
| 99 | SBC_C | IMP | SBC_C |
| 9A | SBC_D | IMP | SBC_D |
| 9B | SBC_E | IMP | SBC_E |
| 9C | SBC_H | IMP | SBC_H |
| 9D | SBC_L | IMP | SBC_L |
| 9E | SBC_HL_REF | IMP | SBC_HL_REF |
| DE | SBC_IMM | IMM8 | SBC_IMM #1 |
| A7 | AND_A | IMP | AND_A |
| A0 | AND_B | IMP | AND_B |
| A1 | AND_C | IMP | AND_C |
| A2 | AND_D | IMP | AND_D |
| A3 | AND_E | IMP | AND_E |
| A4 | AND_H | IMP | AND_H |
| A5 | AND_L | IMP | AND_L |
| A6 | AND_HL_REF | IMP | AND_HL_REF |
| E6 | AND_IMM | IMM8 | AND_IMM #1 |
| B7 | OR_A | IMP | OR_A |
| B0 | OR_B | IMP | OR_B |
| B1 | OR_C | IMP | OR_C |
| B2 | OR_D | IMP | OR_D |
| B3 | OR_E | IMP | OR_E |
| B4 | OR_H | IMP | OR_H |
| B5 | OR_L | IMP | OR_L |
| B6 | OR_HL_REF | IMP | OR_HL_REF |
| F6 | OR_IMM | IMM8 | OR_IMM #1 |
| AF | XOR_A | IMP | XOR_A |
| A8 | XOR_B | IMP | XOR_B |
| A9 | XOR_C | IMP | XOR_C |
| AA | XOR_D | IMP | XOR_D |
| AB | XOR_E | IMP | XOR_E |
| AC | XOR_H | IMP | XOR_H |
| AD | XOR_L | IMP | XOR_L |
| AE | XOR_HL_REF | IMP | XOR_HL_REF |
| EE | XOR_IMM | IMM8 | XOR_IMM #1 |
| BF | CP_A | IMP | CP_A |
| B8 | CP_B | IMP | CP_B |
| B9 | CP_C | IMP | CP_C |
| BA | CP_D | IMP | CP_D |
| BB | CP_E | IMP | CP_E |
| BC | CP_H | IMP | CP_H |
| BD | CP_L | IMP | CP_L |
| BE | CP_HL_REF | IMP | CP_HL_REF |
| FE | CP_IMM | IMM8 | CP_IMM #1 |
| 03 | INC_BC | IMP | INC_BC |
| 0B | DEC_BC | IMP | DEC_BC |
| 13 | INC_DE | IMP | INC_DE |
| 1B | DEC_DE | IMP | DEC_DE |
| 23 | INC_HL | IMP | INC_HL |
| 2B | DEC_HL | IMP | DEC_HL |
| 33 | INC_SP | IMP | INC_SP |
| 3B | DEC_SP | IMP | DEC_SP |
| 09 | ADD_HL_BC | IMP | ADD_HL_BC |
| 19 | ADD_HL_DE | IMP | ADD_HL_DE |
| 29 | ADD_HL_HL | IMP | ADD_HL_HL |
| 39 | ADD_HL_SP | IMP | ADD_HL_SP |
| 07 | RLCA | IMP | RLCA |
| 17 | RLA | IMP | RLA |
| 0F | RRCA | IMP | RRCA |
| 1F | RRA | IMP | RRA |
| CB | PREFIX_CB | IMP | PREFIX_CB |
| 2F | CPL | IMP | CPL |
| 3F | CCF | IMP | CCF |
| 37 | SCF | IMP | SCF |
| 27 | DAA | IMP | DAA |