KITAQ SERIES • REFERENZAUSGABE
KITAQGB
C-Programmierung für Game Boy
Quellenstand: 14. September 2026 · Englische Referenzausgabe: 14. September 2026
Nativer Rust-Compiler und Hilfswerkzeuge
Mit Rust 1.85 oder neuer lassen sich Compiler und sämtliche Hilfswerkzeuge für Windows, Linux, macOS ARM und macOS Intel bauen. Die nativen Programme benötigen kein .NET; die Werkzeuge für Grafikdaten benötigen auch weder Python noch Pillow.
Die Windows-Programme liegen im Repository-Stammverzeichnis, die Linux- und macOS-Programme in den unten aufgeführten bin/-Ordnern. Bewahren Sie lib/ und die Lizenzhinweise zusammen mit den Werkzeugen auf. Unter Linux/macOS setzen Sie mit chmod +x das Ausführungsrecht und ergänzen den PATH, oder verwenden den vollständigen Pfad.
| 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 · Nativer Rust-Compiler und Hilfswerkzeuge · GitHub Actions
Native Builds und Ausführungsprüfungen waren unter Windows, Linux, macOS ARM und macOS Intel erfolgreich. KITAQGB bestand pro Umgebung 48 Tests und 395 Werkzeugprüfungen, KITAQFC 55 und 401. Rust 1.85 wurde ebenfalls getestet. PUBLIC_DISTRIBUTION.json enthält die Hashwerte der installierten Programme und die Herkunft der Prüfergebnisse. Die öffentlichen GitHub-Actions-Workflows bauen und testen diese Quellen unabhängig.
Gespeicherte Referenzausgaben prüfen ROM-Bytes, Diagnosen und Werkzeugformate. Frühere C#-Emulatorprüfungen bleiben historische Nachweise mit ihren ursprünglichen Quellfingerabdrücken. Sie belegen nicht automatisch sämtliche Rust-APIs, echte Hardware oder einen vollständigen Spielstart über das FDS-BIOS. Das nicht verfügbare ursprüngliche PNG-Skript wurde nach seiner Spezifikation neu implementiert; Bytegleichheit mit ihm kann nicht bestätigt werden.
--attach wird aus Kompatibilitätsgründen akzeptiert, verbindet aber keinen verwalteten Debugger mit dem nativen Programm. Verwenden Sie für das PC-Programm einen nativen Debugger.
KITAQGB · SHA-256 · KITAQFC · SHA-256
Beispiele auf den einzelnen Systemen bauen
Die PowerShell-Skripte verwenden das Windows-Programm im Stammverzeichnis. Unter Linux/macOS übergeben Sie dieselben C-Eingaben und Optionen an den nativen Compiler oder verwenden PowerShell 7. CHR-Grafik und C-Quellen sind getrennte Eingaben; font.chr bewahrt die Schrift der Beispiele.
kitaqgb game.c -I lib -o game.gb --cart=romonly --romsize=32kVollständige Beispielprogramme
Enthalten sind alle Programme, die gemeinsamen Header, die bereitgestellte ascii.c und die daraus konvertierten Ressourcen. Namen mit m_ am Anfang bezeichnen Hilfsfunktionen für diese Handbücher. Lesen Sie jeden Build-Befehl zusammen mit dem erwarteten Ergebnis und dem Prüfprotokoll. Führen Sie die Befehle im übergeordneten Ordner der nebeneinanderliegenden Repositories aus.
Skript zur Stapelkompilierung / Ergebnisse und Bildschirmaufnahmen
Den mitgelieferten Zeichensatz anzeigen gb_font.c
Zweck: Zeigt, welche Buchstaben, Ziffern und Satzzeichen die mitgelieferte eigens erstellte Schrift enthält und wie sie gezeichnet werden.
Funktionsweise: m_init bereitet Anzeige und Schrift vor. m_text zeichnet jede Zeile; m_put zeichnet das doppelte Anführungszeichen mit Zeichencode 34.
Bedienung und erwartetes Ergebnis: Prüfen Sie, ob 26 Großbuchstaben, 26 Kleinbuchstaben, 10 Ziffern und 30 Satzzeichen erscheinen und alle Zeichen gut lesbar sind. Es ist keine Eingabe erforderlich.

Quelltext herunterladen / Prüfstatus
.\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(); }
}Die erste Textausgabe gb_hello.c
Zweck: Zeigt den grundlegenden Ablauf zum Starten einer ROM und zum Zeichnen von Text und einer Zahl im Hintergrund.
Funktionsweise: Nach m_init zeichnet m_text die Begrüßung und m_number formatiert 42. Die abschließende Schleife wartet auf Frames und lässt das Bild sichtbar.
Bedienung und erwartetes Ergebnis: Der Bildschirm zeigt HELLO WORLD und 042. Die führende Null entsteht durch die Dezimaldarstellung mit drei Stellen.

Quelltext herunterladen / Prüfstatus
.\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(); }
}Mit breiterem Zwischentyp rechnen gb_arithmetic.c
Zweck: Zeigt, wie ein Zwischenergebnis gespeichert wird, das nicht in acht Bit passt.
Funktionsweise: (u16)200 + 100 ergibt 300. Das Programm teilt das Ergebnis durch 10 und wandelt es anschließend zur Anzeige in u8 um. Beachten Sie, dass die Typverbreiterung vor der Addition erfolgt.
Bedienung und erwartetes Ergebnis: 030 steht für das berechnete Ergebnis 30.

Quelltext herunterladen / Prüfstatus
.\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(); }
}Verzweigungen und Schleifen gb_control.c
Zweck: Erprobt for, continue, while, do/while und switch in einer kleinen Berechnung.
Funktionsweise: Die Summe von 0 bis 7 ohne 3 ergibt 25. Die while-Schleife erhöht auf 30, do/while auf 32, und switch wählt case 32, um 42 zuzuweisen.
Bedienung und erwartetes Ergebnis: 042 ist das Endergebnis dieser Schleifen und Verzweigungen.

Quelltext herunterladen / Prüfstatus
.\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(); }
}Funktionen, Arrays, Strukturen und Zeiger gb_aggregate.c
Zweck: Zeigt, wie Werte gruppiert und über Funktionen und Zeiger angesprochen werden.
Funktionsweise: Das Programm kopiert einen Point mit (10,20) und schreibt dann über einen Zeiger 1 bis 4 in ein Array. Es addiert das Funktionsergebnis 30, die Arraywerte 1, 2 und 4 sowie die Konstante 5.
Bedienung und erwartetes Ergebnis: Die Summe wird als 042 angezeigt. Verfolgen Sie die Strukturkopie, die Schreibzugriffe auf ptr[i] und den Rückgabewert der Funktion.
![Funktionen, Arrays, Strukturen und Zeiger: Die Summe wird als 042 angezeigt. Verfolgen Sie die Strukturkopie, die Schreibzugriffe auf ptr[i] und den Rückgabewert der Funktion.](../verification/gb_aggregate.png)
Quelltext herunterladen / Prüfstatus
.\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(); }
}Bitflags und Pufferkopie gb_bits_memory.c
Zweck: Packt Flags in ein Byte und kopiert einen Puffer in einen getrennten Speicherbereich.
Funktionsweise: __memset löscht vier Bytes. Das Programm setzt Bit 3, invertiert Bit 1 und kopiert anschließend alle vier Bytes mit __memcpy.
Bedienung und erwartetes Ergebnis: Das erste Zielbyte wird als 010 angezeigt; Bit 3 trägt 8 und Bit 1 trägt 2 bei.

Quelltext herunterladen / Prüfstatus
.\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(); }
}Neue Tastendrücke zählen gb_input.c
Zweck: Unterscheidet einen neuen Tastendruck von einer weiterhin gedrückten Taste.
Funktionsweise: In jedem Frame wird input_update aufgerufen. Der Zähler steigt nur, wenn input_pressed(BTN_A) wahr ist.
Bedienung und erwartetes Ergebnis: Der Anfangswert ist 000. Drücken Sie A, um 001 zu erhalten; Gedrückthalten erhöht den Zähler nicht weiter. Loslassen und erneut drücken ergibt 002. Der Bytezähler springt nach 255 auf 000 zurück.

Quelltext herunterladen / Prüfstatus
.\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); } }
}Q8.8-Festkommawerte multiplizieren gb_fixed.c
Zweck: Zeigt die Umwandlung von Ganzzahlen in Q8.8, die Multiplikation und die Rückwandlung in eine Ganzzahl.
Funktionsweise: fix_from_int wandelt 3 und 4 in Q8.8 um. fix_mul multipliziert sie, und fix_to_int wandelt das Produkt wieder in eine Ganzzahl um.
Bedienung und erwartetes Ergebnis: 012 bedeutet 3 × 4 = 12. Es ist die umgewandelte Ganzzahl, nicht der intern mit 256 skalierte Wert.

Quelltext herunterladen / Prüfstatus
.\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(); }
}Aus einem festen Objektpool zuweisen gb_entity.c
Zweck: Erzeugt ein Objekt aus einer festen Menge von Speicherplätzen, ohne dynamische Speicherzuweisung.
Funktionsweise: entity_init setzt den Pool zurück. entity_create erzeugt Typ 1 bei (20,30); das Programm prüft auf die Fehler-ID 0xFF, bevor es vx auf 2 setzt.
Bedienung und erwartetes Ergebnis: 001 ist die Anzahl aktiver Objekte, keine ID oder Koordinate. Das Beispiel aktualisiert und zeichnet das Objekt nicht; daher erscheint keine bewegte Figur.

Quelltext herunterladen / Prüfstatus
.\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(); }
}Einen Diagnosewert im RAM aufzeichnen gb_debug.c
Zweck: Speichert einen benannten Spielwert getrennt von der Bildschirmanzeige in einem Diagnosepuffer.
Funktionsweise: Das Programm initialisiert die Diagnose, setzt die Framekennung auf 1 und zeichnet den Wert 42 unter dem Namen HP auf.
Bedienung und erwartetes Ergebnis: 001 ist die Anzahl der Trace-Einträge. HP=42 liegt im Diagnosepuffer; der Bildschirm allein zeigt oder bestätigt diesen Inhalt nicht.

Quelltext herunterladen / Prüfstatus
.\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(); }
}Einen Positionsverlauf speichern gb_chain.c
Zweck: Zeigt, wie ein Positionsverlauf in einem Ringpuffer gespeichert wird.
Funktionsweise: chain_init bindet ein Array mit Platz für vier Punkte ein. Anschließend zeichnet chain_push_head (20,30) auf.
Bedienung und erwartetes Ergebnis: 042 ist eine feste Abschlussmarkierung, keine Koordinate oder Anzahl. Prüfen Sie trail und points für den gespeicherten Verlauf. Dieses Programm zeichnet keinen nachfolgenden Körper.

Quelltext herunterladen / Prüfstatus
.\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(); }
}Den Framezähler der Bibliothek lesen gb_system.c
Zweck: Zeigt, wie Wartezeiten in Frames den Framezähler der Bibliothek beeinflussen.
Funktionsweise: Nach system_init ruft das Programm system_wait_vblank zweimal auf und zeigt system_get_frame8 an.
Bedienung und erwartetes Ergebnis: Das Ergebnis ist 002. Die Warteschleife verwendet die Handbuch-Hilfsfunktion m_wait; der angezeigte Wert steigt daher nicht weiter.

Quelltext herunterladen / Prüfstatus
.\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(); }
}Die aktuelle Szenen-ID wählen gb_scene.c
Zweck: Zeigt, wie eine ID zur Unterscheidung von Szenen wie Titelbildschirm und Spiel gewählt wird.
Funktionsweise: Das Programm registriert zwei mit null initialisierte SceneDef-Einträge und wählt mit scene_change die ID 1.
Bedienung und erwartetes Ergebnis: 001 ist die aktuelle von scene_get_current gelieferte ID. In diesem Beispiel sind weder Szenen-Callbacks noch Übergangszeichnungen registriert.

Quelltext herunterladen / Prüfstatus
.\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(); }
}Weltkoordinaten in Bildschirmkoordinaten umwandeln gb_camera.c
Zweck: Zieht die Kameraposition ab, um die Bildschirmposition eines Weltpunkts zu bestimmen.
Funktionsweise: Camera_Set erhält den Q8.8-Wert 2560, also X=10 Pixel. Anschließend wandelt Camera_WorldToScreenX die Weltkoordinate X=52 um.
Bedienung und erwartetes Ergebnis: 052 minus 010 ergibt die angezeigte 042. Dies ist eine Koordinatenberechnung; der Hintergrund wird nicht gescrollt.

Quelltext herunterladen / Prüfstatus
.\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(); }
}RLE-Daten in den RAM entpacken gb_rle.c
Zweck: Erprobt eine kompakte Darstellung wiederholter Bytes als Anzahl und Wert.
Funktionsweise: Die gepackten Bytes {3,42,0} bedeuten drei Kopien von 42, gefolgt von einer Nullanzahl als Endmarke. rle_decode schreibt in einen drei Byte großen Zielpuffer.
Bedienung und erwartetes Ergebnis: Das letzte Byte, out[2], wird als 042 angezeigt. Alle drei entpackten Bytes sollen den Wert 42 enthalten.
![RLE-Daten in den RAM entpacken: Das letzte Byte, out[2], wird als 042 angezeigt. Alle drei entpackten Bytes sollen den Wert 42 enthalten.](../verification/gb_rle.png)
Quelltext herunterladen / Prüfstatus
.\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(); }
}Ein Brettfeld lesen und schreiben gb_board.c
Zweck: Zeigt, wie Rasterkoordinaten eines Brettspiels auf Speicherpositionen abgebildet werden.
Funktionsweise: Ein 16-Byte-Array speichert ein 4×4-Brett. Das Programm löscht es, schreibt 42 in Zelle (2,1) und liest dieselbe Zelle wieder.
Bedienung und erwartetes Ergebnis: 042 ist der zurückgelesene Wert. Das Programm zeichnet das 4×4-Brett selbst nicht.

Quelltext herunterladen / Prüfstatus
.\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(); }
}Eine Box um einen Physikschritt bewegen gb_physics.c
Zweck: Initialisiert eine Welt und einen rechteckigen Körper und aktualisiert dann die Position anhand der Geschwindigkeit.
Funktionsweise: Der Körper startet bei (40,40) mit Geschwindigkeit (2,0) und Schwerkraft null. Das Programm ruft kq2d_step einmal auf.
Bedienung und erwartetes Ergebnis: 042 ist die resultierende X-Koordinate, hier in ganzen Pixeln. Es werden weder eine Boxform noch eine fortlaufende Animation gezeichnet.

Quelltext herunterladen / Prüfstatus
.\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(); }
}Abschluss und Timeout einer seriellen Übertragung behandeln gb_link.c
Zweck: Unterscheidet eine abgeschlossene Übertragung von einer Übertragung, die nicht innerhalb der Wartegrenze endet.
Funktionsweise: Der Master sendet 42 und wartet mit Link_WaitByte. Bei Abschluss wird das empfangene Byte angezeigt; bei Timeout wird die Übertragung abgebrochen und null angezeigt.
Bedienung und erwartetes Ergebnis: Eine ohne Verbindung abgeschlossene Übertragung kann 255 liefern. Ein Timeout zeigt 000; bei verbundenem Partner erscheint das empfangene Byte. Das Ergebnis ist nicht immer 042.

Quelltext herunterladen / Prüfstatus
.\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(); }
}ROM-Daten mit einem Bankargument lesen gb_bank.c
Zweck: Erklärt die Argumente zum Kopieren von ROM-Daten in den RAM.
Funktionsweise: far_data_read kopiert die beiden Bytes 7 und 42 mit Bankargument 0 und Länge 2 nach copied.
Bedienung und erwartetes Ergebnis: Das zweite Zielbyte wird als 042 angezeigt. Dieses Beispiel liest aus fest eingeblendetem ROM; es zeigt keine Übertragung zwischen verschiedenen umschaltbaren Banken.

Quelltext herunterladen / Prüfstatus
.\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(); }
}Daten anhand der Asset-ID laden gb_asset.c
Zweck: Registriert Position und Länge eines Assets und ruft anschließend seine Bytes anhand der ID ab.
Funktionsweise: Asset 0 hat Typ RAW, Bank 0, einen Zeiger auf {7,42} und Länge 2. asset_load_raw kopiert es in einen Zwei-Byte-RAM-Puffer.
Bedienung und erwartetes Ergebnis: Das zweite Byte wird als 042 angezeigt. Das Beispiel demonstriert das Auffinden und Kopieren von Assets, keine Bilddarstellung.

Quelltext herunterladen / Prüfstatus
.\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(); }
}Ein Geschoss zuweisen gb_danmaku.c
Zweck: Initialisiert den Geschosspool und prüft seine Belegung nach dem Erzeugen eines Geschosses.
Funktionsweise: Nach danmaku_reset ruft das Programm danmaku_spawn(40,40,16,0) auf und zeigt dm_count an.
Bedienung und erwartetes Ergebnis: 001 ist die Anzahl zugewiesener Geschosse. Es gibt hier weder eine Geschoss-Aktualisierungsschleife noch eine Hintergrundkomposition; bewegte Geschosse werden nicht gezeichnet.

Quelltext herunterladen / Prüfstatus
.\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(); }
}Eine CGB-Hintergrundpalette einstellen gb_cgb_palette.c
Zweck: Wählt Hintergrund- und Textfarben anhand der RGB-Komponenten des Game Boy Color.
Funktionsweise: Das Programm schaltet das LCD im VBlank aus, setzt Farbe 0 der Palette 0 auf Weiß (31,31,31) und Farbe 3 auf Violett (12,0,22) und aktiviert dann die Anzeige.
Bedienung und erwartetes Ergebnis: Führen Sie das Programm im CGB-Modus aus, um violetten Text und 042 auf weißem Hintergrund zu sehen. Die monochrome DMG-Ausgabe kann diese Farben nicht darstellen.

Quelltext herunterladen / Prüfstatus
.\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(); }
}Den Hintergrund horizontal scrollen gb_scroll.c
Zweck: Verschiebt die Ansicht, ohne den Hintergrundinhalt neu zu zeichnen.
Funktionsweise: Jeder Frame erhöht phase und übergibt den Wert an Scroll_SetBg. Die Acht-Bit-Position springt nach 255 auf null zurück.
Bedienung und erwartetes Ergebnis: Das Wort SCROLL bewegt sich nach links. Beobachten Sie seine Position über die Zeit; eine einzelne Aufnahme kann die Bewegung nicht zeigen.

Quelltext herunterladen / Prüfstatus
.\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); }
}Eine Tile-Aktualisierung einreihen gb_vram_queue.c
Zweck: Trennt eine Zeichenanforderung von der VRAM-Übertragung, die sie umsetzt.
Funktionsweise: vram_queue_bg_tile reiht Tile 52 für Tile-Koordinate (3,8) ein, und vram_flush überträgt es. In der mitgelieferten Schrift ist Tile 52 die Ziffer 4.
Bedienung und erwartetes Ergebnis: Achten Sie auf eine einzelne 4 bei Hintergrund-Tile-Koordinate (3,8). Dies ist eine Tile-Aktualisierung, kein Zahlenzähler.

Quelltext herunterladen / Prüfstatus
.\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(); }
}Ein Sprite per OAM-DMA anzeigen gb_sprite.c
Zweck: Weist ein Sprite zu und zeigt es unabhängig vom Hintergrund an.
Funktionsweise: Der neu initialisierte Pool liefert Platz 0. Das Programm wählt Tile 65, setzt Position (70,80), aktiviert die OBJ-Anzeige und überträgt das Schatten-OAM in jedem Frame.
Bedienung und erwartetes Ergebnis: An der angegebenen Sprite-Position erscheint ein unbewegtes A. Prüfen Sie im Spiel vor Verwendung der zurückgegebenen ID, ob die Zuweisung fehlgeschlagen ist.

Quelltext herunterladen / Prüfstatus
.\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(); }
}Einen reproduzierbaren Zufallswert erzeugen gb_rng.c
Zweck: Verwendet einen Startwert, um eine Pseudozufallsfolge zu reproduzieren.
Funktionsweise: rng_seed(1234) initialisiert den Generator; danach wird rng_range(10) einmal aufgerufen. Die Obergrenze 10 ist ausgeschlossen.
Bedienung und erwartetes Ergebnis: Der Bildschirm zeigt einen Wert von 000 bis 009. Ein Neustart desselben Programms mit demselben Startwert erzeugt ihn erneut; er ändert sich nicht in jedem Frame.

Quelltext herunterladen / Prüfstatus
.\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(); }
}Ein Spielzustandsflag speichern gb_flags.c
Zweck: Stellt einen booleschen Zustand, etwa den Abschluss eines Ereignisses, durch eine Flagnummer dar.
Funktionsweise: Das Programm löscht Flag 12, setzt es und liest es mit flag_get zurück.
Bedienung und erwartetes Ergebnis: 001 bedeutet, dass Flag 12 wahr ist. Das Beispiel arbeitet mit RAM-Flags und erhält sie nicht über Aus- und Einschalten hinweg.

Quelltext herunterladen / Prüfstatus
.\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(); }
}Einen Kreis um einen Physikschritt bewegen gb_circle.c
Zweck: Richtet einen kreisförmigen Körper ein und aktualisiert seine Position anhand der Geschwindigkeit.
Funktionsweise: Der aktive Kreis hat Radius 4, Position (40,40) und Geschwindigkeit (2,0). Die Schwerkraft ist null und linear_damping_q8 ist 255; kq2dc_step wird einmal ausgeführt.
Bedienung und erwartetes Ergebnis: 042 ist die resultierende ganzzahlige X-Koordinate. Das Beispiel zeichnet weder einen Kreisumriss noch eine Kollisionsanimation.

Quelltext herunterladen / Prüfstatus
.\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(); }
}Einen Soundeffekt in jedem Frame verarbeiten gb_sound.c
Zweck: Startet einen Soundeffekt-Datenstrom und lässt ihn durch regelmäßige Frameaktualisierungen fortschreiten.
Funktionsweise: Nach Audio_Init startet Audio_PlaySFX tone auf Kanal-ID 1. Die Hauptschleife ruft Audio_Update einmal pro Frame auf.
Bedienung und erwartetes Ergebnis: Ein kurzer Pulseffekt erklingt, während 042 sichtbar bleibt. Die Zahl ist eine feste Markierung, weder Lautstärke noch Wiedergabeposition. Beurteilen Sie den Klang über die Audioausgabe oder eine Aufnahme.

Quelltext herunterladen / Prüfstatus
.\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(); }
}Referenz der Compiler-Intrinsics
149 Einträge. Öffnen Sie einen Eintrag, um Syntax, Argumente, Quellhinweise und ein Anwendungsbeispiel zu sehen. Aufruffragmente benötigen zusätzliche Initialisierung und sind keine eigenständigen ROMs. Die Beispiele zeigen, wie vorbereitete Werte an die API übergeben werden; sie belegen keine Prüfung am Gerät.
asset.h — Ressourcen-IDs und Datenbeschreibungen
Die Ressourcenbibliothek findet Daten in ROM-Bänken über eine 8-Bit-ID. Binden Sie asset.c ein; auf dem FC zusätzlich bank.c. Ein gepackter AssetDesc belegt 6 Byte: Typ, Banknummer der Nutzdaten, 16-Bit-Adresse und Länge in Byte. Deskriptortabelle und Nutzdaten dürfen in verschiedenen ROM-Bänken liegen.
Registrieren Sie die Tabelle vor der ersten Suche. Die Registrierung ist ein gemeinsamer globaler Zustand und darf nicht gleichzeitig mit einer Suche geändert werden. Geprüft werden IDs und der Ausgabezeiger von asset_get, nicht jedoch Tabelle, Nutzdatenadressen oder Cartridge-Inhalt.
Sucht eine RAW- oder TILES-Ressource und überträgt alle desc.len Byte mit __vram_copy. Wählen Sie zuvor die ROM-Bank der Nutzdaten aus: Die GB-Funktion verwendet desc.ptr direkt und schaltet für den Transfer nicht auf desc.bank um.
__farmemcpy
Wählt vorübergehend eine ROM-Bank, kopiert len Byte mit aufsteigenden Adressen von src nach dst und stellt anschließend die vom Compiler gespeicherte Bank wieder her. Auf dieser Plattform verwenden __farmemcpy und __far_memcpy dieselbe Implementierung.
Syntax
void __farmemcpy(void* dst, u8 bank, const void* src, u16 len);Parameter
dstZiel in beschreibbarem Speicher mit genügend Platz für alle kopierten Byte. Abgesehen von einem Transfer mit Länge null erfolgt keine Prüfung auf Nullzeiger oder ungültige Zieladressen.
bank8-Bit-Bankauswahl für die Quell-ROM-Zuordnung. Die Bank muss zur Cartridge und zur Bankaufteilung des Compilers passen. Sie wird getrennt von der Quelladresse angegeben.
srcNahzeiger auf lesbare Bytes in der gewählten ROM-Bank. Er enthält keine Banknummer.
lenByteanzahl, 0–65535. Bei null wird kein Speicher kopiert; Bankauswahl und Wiederherstellung werden trotzdem ausgeführt.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Der gesamte Quellbereich muss in der vorgesehenen ROM-Zuordnung lesbar und der gesamte Zielbereich beschreibbar sein. Die Kopie schaltet am Fensterende nicht automatisch zur nächsten Bank, behandelt Überlappungen nicht wie memmove und steuert keine sicheren VRAM-Schreibzeitpunkte. Verwenden Sie für VRAM die Grafikübertragungs-APIs.
Die Beispiele legen den aufrufenden Code in die feste Bank 0, damit ein Wechsel der Quellbank den laufenden Code nicht ausblendet. Die Übertragungen sind synchron. Unterbrechen Sie sie nicht durch eine weitere bankwechselnde Kopie. Die Hilfsfunktion allein macht nicht die gesamte Anwendung interruptsicher.
Die GB-Hilfsroutine stellt die vom Compiler gemerkte ROM-Bank wieder her. Halten Sie diesen Zustand mit __bankswitch aktuell; direkte Mapper-Schreibzugriffe aktualisieren ihn nicht. Der HRAM-Speicherplatz für die Rückkehrbank wird gemeinsam genutzt. Verschachtelte Übertragungen dürfen ihn nicht überschreiben.
Anwendungsbeispiel
__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);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Kopiert zwei Muster mit insgesamt 32 Byte aus Bank 2, ausgehend von Bank 1. Das Programm vergleicht jedes Byte, prüft die Wiederherstellung von Bank 1 und den unveränderten Prüfwert 0x55 bei einer Kopie der Länge null.
Erwartetes Bild: Eine rote Reihe beginnt bei (16,32) und belegt 72×8 Pixel. Dreimal folgen ein 8×8-Dreieck, ein 8×8-Quadrat mit einem Pixel Rand und eine Kachel mit vier gefüllten linken Spalten. Das Dreieck wächst von einem Pixel oben links auf acht Pixel in der untersten Zeile.
Auf dem GB zeigen das blaue Dreieck und der Rahmen bei (16,64) das Ergebnis von __farmemcpy, das grüne Paar bei (16,96) das von __far_memcpy. Jedes Paar belegt 16×8 Pixel. Der CGB nutzt diese Farben, der DMG dieselben Formen in Schwarz auf Weiß.
Erwartet werden FAILED CHECKS 000 und FIRST FAILURE 00. Die Prüfung führt 300 Bilder aus und vergleicht Text, Formmasken und Farben mit den Sollwerten. Dies ist ein Emulatornachweis, kein Test auf physischer Hardware.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/asset.h:17
__vram_copy
__vram_copy ist ein Compileralias für __vram_memcpy. Es verwendet dieselbe Byteanzahl und dieselbe sofortige CPU-Übertragung einschließlich der LCD-Zugriffsregeln.
Kopiert sofort len Bytes von src nach dst in aufsteigender Adressreihenfolge. Kehrt nach Abschluss zurück und reiht keine Arbeit ein. Beide Adressen steigen jeweils um ein Byte; Kartenzeilenabstände oder Grafikformatumwandlungen werden nicht berücksichtigt.
Syntax
void __vram_copy(u16 dst, const void* src, u16 len);Parameter
dstAdresse des ersten Zielbytes, normalerweise im VRAM
0x8000–0x9FFF. Der gesamte Bereich muss gültig sein. Der CGB verwendet die aktuelle VRAM-Bank; im Kartenbereich enthält Bank 0 Kachelnummern und Bank 1 Attribute. Diese Routinen wählen keine Karte anhand von LCDC, führen keinen Zeilenumbruch aus und reservieren kein VRAM.srcZeiger auf die Quellbytes, während des gesamten Aufrufs gültig und korrekt eingeblendet. Daten werden sofort gelesen und nicht für einen späteren Flush gespeichert. Die Quellbank wird nicht automatisch gewechselt. Verwenden Sie nicht überlappende Bereiche; diese Vorwärtskopie bietet keine memmove-Semantik.
lenAnzahl aufeinanderfolgender Bytes von 0 bis 65535, keine Tile-Anzahl. Der gesamte Bereich muss in gültiger Quelle und beschreibbarem Ziel liegen; die API prüft keine Grenzen und verhindert keinen 16-Bit-Adressüberlauf.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Bei eingeschaltetem LCD wartet jeder Bytezugriff unter DI auf STAT-Modus 0 oder 1; danach folgt EI. Bei ausgeschaltetem LCD läuft eine gewöhnliche CPU-Schleife. Der Aufruf kann mehrere Frames dauern, erhält keine anfängliche Interruptsperre und richtet keine DMA-Register ein. Länge null überträgt keine Daten.
Anwendungsbeispiel
__vram_copy(0x9862,strip,3); // The copy alias uses the same CPU transfer.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Dieses Beispiel schreibt mit __vram_copy drei Kartenbytes ab Kachel (2,3), Pixel (16,24). Erwartet werden die Muster [1,2,3] von links nach rechts in einem Bereich von 24 × 8; benachbarte Felder bleiben leer. Das vollständige Programm zeigt jeden geprüften Kopier- oder Füllvorgang in einer eigenen Zeile.
Die mitgelieferten eigenen Grafiken verwenden Kachel 1 für ein ausgefülltes Quadrat von 8 × 8, Kachel 2 für dessen vier linke Spalten und Kachel 3 für ein linksbündiges Dreieck, das je Zeile um ein Pixel von 1 auf 8 wächst. DMG zeigt Schwarz auf Weiß; CGB unterscheidet die Operationen farblich und behält schwarzen Text bei.
Das Quadrat bei Pixel (16,88) muss nach sicheren und ungeprüften Aufrufen mit Länge null erhalten bleiben. Bei aktivem LCD ergänzen sicheres Kopieren und Füllen Quadrate bei (16,96) und (32,96). FAILED CHECKS muss 000 anzeigen. Die Emulatortests vergleichen Pixel und Zähler. Der CGB verwendet hier Bank 0; andere Banken und echte Hardware werden hier nicht geprüft.
Im CGB-Modus wiederholen die Zeilen von MEMCPY bis FILL UNSAFE die Folge Rot, Blau, Grün. Das Quadrat bei ZERO COUNT ist rot. Bei LCD ON ist das linke Quadrat blau und das rechte grün. Text bleibt schwarz. Die Prüfungen kontrollieren diese Farben an jedem gezeichneten Pixel.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/asset.h:18
bank.h — Bankabhängiger Zugriff auf Code und Daten
Eine ROM-Bank ist ein Speicherblock des Moduls, der in den CPU-Adressraum eingeblendet wird. Verschiedene Banken können an derselben CPU-Adresse liegen; ein 16-Bit-Zeiger allein identifiziert Bankdaten deshalb nicht eindeutig. BankPtr ist ein gepackter Deskriptor aus drei Bytes: einem Bankbyte und einem Zwei-Byte-Zeiger. Er wird mit farptr_make erstellt und mit farptr_read8, farptr_read16 oder farptr_read gelesen.
Das GB-Beispiel verwendet ein MBC5-Modul: Code in der festen Bank 0 bleibt unter $0000–$3FFF erreichbar, während die Banken 1, 2 und 3 unter $4000–$7FFF eingeblendet werden. Legen Sie bank_switch und den danach ausgeführten Code im festen ROM ab. Initialisieren Sie den Bibliothekszustand mit bank_switch, bevor Sie ihn abfragen. Auf GB akzeptiert far_call einen Callback-Zeiger; __farcall benötigt einen Funktionsnamen.
__farcall_ptr
Ruft einen Callback ohne Argumente an einer zur Laufzeit bestimmten Adresse in der angegebenen Bank auf. Die Hilfsroutine im festen ROM speichert jede Aufruferbank auf dem CPU-Stack und stellt sie bei der Rückkehr wieder her. Das Intrinsic liefert keinen Wert.
Syntax
void __farcall_ptr(u8 bank, const void* func);Parameter
bankVorzeichenlose 8-Bit-ROM-Banknummer. Bei Aufrufen muss sie zur Funktionsplatzierung passen, bei Lesezugriffen wählt sie die Quelle. Verwenden Sie den gültigen Bereich des Modultyps.
funcGültige ausführbare Adresse eines Callbacks ohne Parameter, angegeben als Funktionsname oder Laufzeitzeiger. Nullzeiger und falsche Bank-Adress-Paare werden zur Laufzeit nicht geprüft.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Diese Aufrufe reichen keine Benutzerargumente weiter und machen den Callback nicht interrupt-reentrant. Verwalten Sie seine Daten, seinen Stack und seine Interruptnutzung korrekt. Die Bankwiederherstellung ist mit verschachtelten und wiederholten Aufrufen geprüft; reale Hardware und beliebige Interruptverschachtelungen deckt dieses Beispiel nicht ab.
Diese GB-Bankschnittstellen verwenden acht Bankbits und setzen das neunte MBC5-Bankbit nicht. Modulregeln, etwa die Behandlung von Bank null, gelten weiterhin. Das Beispiel nutzt ausdrücklich MBC5-Banken 1–3; mischen Sie diesen Zustand nicht mit unprotokollierten direkten Mapper-Schreibzugriffen.
Anwendungsbeispiel
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);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
An dieser Stelle des vollständigen Programms erhöht __farcall_ptr den Wert von callback_count auf 3. Der Callback liest D3 aus Bank 3. Ein späterer Nahzugriff auf home2 mit Ergebnis B2 bestätigt die Rückkehr bei eingeblendeter Bank 2.
Das vollständige Programm zeigt Datenzugriffe über Bank und Nahadresse, bankübergreifende Callbacks und den Unterschied zwischen Bibliothekszustand und tatsächlicher Zuordnung. Alle Zahlen sind hexadezimal. BANK IN und BANK OUT müssen 02 zeigen, BYTE HEX D3, WORD HEX 5C7A sowie FAILED CHECKS und FIRST FAILURE jeweils 00. CGB und FC zeigen blaue Schrift, DMG schwarze.
CALL COUNT zeigt 03: far_call, __farcall und __farcall_ptr rufen den Callback jeweils einmal auf.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Liest bei kurzzeitig gewählter Bank einen 16-Bit-Wert in Little-Endian-Reihenfolge an addr und addr+1 und stellt die Bank wieder her. Dieses Intrinsic wird von far_data_read16 verwendet.
Syntax
u16 __farpeek16(u8 bank, const void* addr);Parameter
bankVorzeichenlose 8-Bit-ROM-Banknummer. Bei Aufrufen muss sie zur Funktionsplatzierung passen, bei Lesezugriffen wählt sie die Quelle. Verwenden Sie den gültigen Bereich des Modultyps.
addrNahzeiger auf lesbare Bytes in der gewählten ROM-Bank. Er enthält keine Banknummer.
Rückgabewert
Das gelesene Little-Endian-Wort als u16 (0–65535).
Bedingungen und Grenzen
Das gesamte Byte oder Wort muss in der gewählten CPU-Zuordnung lesbar sein. Zeiger werden nicht geprüft, Adressen nicht begrenzt und Fenstergrenzen lösen keinen automatischen Wechsel zur nächsten ROM-Bank aus. Ein Wort benötigt zwei gültige aufeinanderfolgende Bytes.
Fernlesezugriffe und Kopien arbeiten synchron und nutzen gemeinsamen Arbeitsspeicher des Compilers. Ein Interrupt darf währenddessen keinen weiteren Banktransfer über denselben Speicher ausführen. Auf GB nutzt die Kopierhilfe außerdem einen gemeinsamen HRAM-Platz für die gespeicherte Bank. Das Beispiel führt die Operationen aus festem Code ohne konkurrierenden Transfer-Handler aus.
Diese GB-Bankschnittstellen verwenden acht Bankbits und setzen das neunte MBC5-Bankbit nicht. Modulregeln, etwa die Behandlung von Bank null, gelten weiterhin. Das Beispiel nutzt ausdrücklich MBC5-Banken 1–3; mischen Sie diesen Zustand nicht mit unprotokollierten direkten Mapper-Schreibzugriffen.
Anwendungsbeispiel
bank_expect(__farpeek16(3,payload+1)==0x5C7A,14);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Dieser Ausschnitt liest mit __farpeek16 den Ausdruck payload+1 in Bank 3 und prüft 5C7A. Das vollständige Programm bestätigt durch einen Nahzugriff auf home2 außerdem die Wiederherstellung von Bank 2.
Das vollständige Programm zeigt Datenzugriffe über Bank und Nahadresse, bankübergreifende Callbacks und den Unterschied zwischen Bibliothekszustand und tatsächlicher Zuordnung. Alle Zahlen sind hexadezimal. BANK IN und BANK OUT müssen 02 zeigen, BYTE HEX D3, WORD HEX 5C7A sowie FAILED CHECKS und FIRST FAILURE jeweils 00. CGB und FC zeigen blaue Schrift, DMG schwarze.
CALL COUNT zeigt 03: far_call, __farcall und __farcall_ptr rufen den Callback jeweils einmal auf.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Wählt kurzzeitig die angegebene Bank, liest ein Byte an der CPU-Adresse und stellt die vorherige Bank wieder her. Dieses Intrinsic wird von far_data_read8 verwendet.
Syntax
u8 __farpeek8(u8 bank, const void* addr);Parameter
bankVorzeichenlose 8-Bit-ROM-Banknummer. Bei Aufrufen muss sie zur Funktionsplatzierung passen, bei Lesezugriffen wählt sie die Quelle. Verwenden Sie den gültigen Bereich des Modultyps.
addrNahzeiger auf lesbare Bytes in der gewählten ROM-Bank. Er enthält keine Banknummer.
Rückgabewert
Das gelesene Byte als u8 (0–255).
Bedingungen und Grenzen
Das gesamte Byte oder Wort muss in der gewählten CPU-Zuordnung lesbar sein. Zeiger werden nicht geprüft, Adressen nicht begrenzt und Fenstergrenzen lösen keinen automatischen Wechsel zur nächsten ROM-Bank aus. Ein Wort benötigt zwei gültige aufeinanderfolgende Bytes.
Fernlesezugriffe und Kopien arbeiten synchron und nutzen gemeinsamen Arbeitsspeicher des Compilers. Ein Interrupt darf währenddessen keinen weiteren Banktransfer über denselben Speicher ausführen. Auf GB nutzt die Kopierhilfe außerdem einen gemeinsamen HRAM-Platz für die gespeicherte Bank. Das Beispiel führt die Operationen aus festem Code ohne konkurrierenden Transfer-Handler aus.
Diese GB-Bankschnittstellen verwenden acht Bankbits und setzen das neunte MBC5-Bankbit nicht. Modulregeln, etwa die Behandlung von Bank null, gelten weiterhin. Das Beispiel nutzt ausdrücklich MBC5-Banken 1–3; mischen Sie diesen Zustand nicht mit unprotokollierten direkten Mapper-Schreibzugriffen.
Anwendungsbeispiel
bank_expect(__farpeek8(3,payload)==0xD3,13);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Dieser Ausschnitt liest mit __farpeek8 den Ausdruck payload in Bank 3 und prüft D3. Das vollständige Programm bestätigt durch einen Nahzugriff auf home2 außerdem die Wiederherstellung von Bank 2.
Das vollständige Programm zeigt Datenzugriffe über Bank und Nahadresse, bankübergreifende Callbacks und den Unterschied zwischen Bibliothekszustand und tatsächlicher Zuordnung. Alle Zahlen sind hexadezimal. BANK IN und BANK OUT müssen 02 zeigen, BYTE HEX D3, WORD HEX 5C7A sowie FAILED CHECKS und FIRST FAILURE jeweils 00. CGB und FC zeigen blaue Schrift, DMG schwarze.
CALL COUNT zeigt 03: far_call, __farcall und __farcall_ptr rufen den Callback jeweils einmal auf.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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 — CGB-Paletten für BG und OBJ
cgb_palette.h und cgb_palette.c bieten RGB15-Packung und sofortige CGB-Palettenaktualisierung. BG-Funktionen dienen Hintergrund/Fenster, OBJ-Funktionen den Sprites. Einzel- und Komponentenfunktionen ändern eine Farbe, Bereichsfunktionen aufeinanderfolgende Wörter, Ganzpalettenfunktionen vier Wörter. Raw-Bereiche verwenden absolute Farbplätze, Registerintrinsics Bytepositionen. Die Palettenzuweisung erfolgt getrennt über Kartenattribute oder OAM. Binden Sie die Implementierung wie im vollständigen Beispiel genau einmal ein.
Paletten- und geschützte Registerschreibzugriffe schreiben im DMG-Modus nichts. Safe bedeutet hier eine CGB-Modusprüfung, keine PPU-Zugriffswarteschleife und keinen kritischen Abschnitt. Verwenden Sie die Funktionen bei ausgeschaltetem LCD oder für kurze Aktualisierungen innerhalb einer zugänglichen PPU-Phase. Sie warten nicht auf VBlank und schützen mehrbyteige Updates nicht vor Interrupts.
BG-/Fenster- und OBJ-Paletten haben getrennte Speicher. Bibliothekszugriffe aktivieren die Indexerhöhung und lassen das Indexregister verändert. OBJ-Farbindex null ist beim Zeichnen von Sprites transparent, obwohl seine Bytes gespeichert und gelesen werden können.
__cgb_is_cgb
Liefert den CGB-Modus als booleschen Compilerwert. Ein ausdrücklich gesetztes --cgb=dmg wird zu 0, --cgb=cgb_only zu 1 gefaltet; das duale Ziel --cgb=cgb verwendet die Laufzeiterkennung. Gemeint ist der gewählte Ausführungsmodus, nicht die gesamte Ausstattung des physischen Geräts.
Syntax
u8 __cgb_is_cgb();Rückgabewert
u8: 0 für DMG-Modus, 1 für CGB-Modus.
Anwendungsbeispiel
color_mode=__cgb_is_cgb(); // 0 in DMG mode, 1 in CGB mode for this dual-mode ROM.
if (color_mode>1) failures++;Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Programm zeigt, wie BG- und OBJ-Paletten dasselbe Muster unabhängig einfärben. Jedes Dreieck ist 8 × 8 Pixel groß: oben ein Pixel links, unten eine volle Zeile. Bei x = 16, 32, 48, 64, 80, 96, 112 ist die BG-Reihe bei y = 24 rot, blau, grün, rot, blau, grün, rot; die OBJ-Reihe bei y = 64 ist blau, grün, rot, blau, grün, rot, blau. Die Spalten verwenden der Reihe nach Einzelfarbe, Komponenten, indizierten Bereich, ganze Palette, absoluten Bereich, kanonischen Registernamen und Registeralias.
Bei y = 88 zeigen sieben 8 × 8-Felder: rotes Dreieck (x = 16), blauen quadratischen Rahmen mit einem Pixel Strichstärke aus Bank 1 (32), horizontal gespiegeltes grünes Dreieck (48), vertikal gespiegeltes rotes Dreieck (64), in beiden Richtungen gespiegeltes blaues Dreieck (80), grünes Dreieck vor einem roten Sprite, das durch Farbindex null sichtbar bleibt (96), sowie ein volles rotes Sprite vor dem BG-Dreieck als Kontrolle ohne BG-Priorität (112). Koordinaten in Pixeln ab der linken oberen Ecke; weiße Freiflächen und schwarzer Text.
FAILED CHECKS 000 bestätigt RGB-Masken, Wahrheitsflags einschließlich Wert 2, normalisierte Palettenindizes, palettenübergreifende Zugriffe, Begrenzung bei Platz 31, Nullzeiger/Nullängen ohne Schreibzugriff und Farbrücklesen bei ausgeschaltetem LCD. Vier Kombinationen werden geprüft: duales ROM auf DMG/CGB, reines DMG-ROM auf DMG und reines CGB-ROM auf CGB. Auf DMG ist CGB MODE 0, Dreiecke sind schwarz, Bank-/Spiegelattribute ändern die Anzeige nicht und die letzten beiden unteren Felder sind schwarze Quadrate. Auf CGB ist CGB MODE 1. Echte Hardware und Palettenzugriffskonflikte bei eingeschaltetem LCD sind nicht geprüft.




Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Schreibt auf dem CGB ein Byte in das Palettendatenregister BCPD/BGPD ($FF69) für Hintergrund/Fenster. __cgb_safe_set_bgpd bezeichnet dasselbe Register. Das Indexregister bestimmt die Position; eine vollständige RGB15-Farbe benötigt ein niederwertiges und ein höherwertiges Byte.
Syntax
void __cgb_safe_set_bcpd(u8 value);Parameter
valueEin Byte, 0–255. Schreiben Sie das niederwertige vor dem höherwertigen RGB15-Byte; aktivieren Sie zuvor die automatische Indexerhöhung oder wählen Sie jede Position einzeln. Ein Datenschreibzugriff aktiviert die Indexerhöhung nicht selbst.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Paletten- und geschützte Registerschreibzugriffe schreiben im DMG-Modus nichts. Safe bedeutet hier eine CGB-Modusprüfung, keine PPU-Zugriffswarteschleife und keinen kritischen Abschnitt. Verwenden Sie die Funktionen bei ausgeschaltetem LCD oder für kurze Aktualisierungen innerhalb einer zugänglichen PPU-Phase. Sie warten nicht auf VBlank und schützen mehrbyteige Updates nicht vor Interrupts.
Anwendungsbeispiel
__cgb_safe_set_bcpd(0xE0); __cgb_safe_set_bcpd(0x03); // Green word 0x03E0, low then high.
expect_color(0,27,992);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Programm zeigt, wie BG- und OBJ-Paletten dasselbe Muster unabhängig einfärben. Jedes Dreieck ist 8 × 8 Pixel groß: oben ein Pixel links, unten eine volle Zeile. Bei x = 16, 32, 48, 64, 80, 96, 112 ist die BG-Reihe bei y = 24 rot, blau, grün, rot, blau, grün, rot; die OBJ-Reihe bei y = 64 ist blau, grün, rot, blau, grün, rot, blau. Die Spalten verwenden der Reihe nach Einzelfarbe, Komponenten, indizierten Bereich, ganze Palette, absoluten Bereich, kanonischen Registernamen und Registeralias.
Bei y = 88 zeigen sieben 8 × 8-Felder: rotes Dreieck (x = 16), blauen quadratischen Rahmen mit einem Pixel Strichstärke aus Bank 1 (32), horizontal gespiegeltes grünes Dreieck (48), vertikal gespiegeltes rotes Dreieck (64), in beiden Richtungen gespiegeltes blaues Dreieck (80), grünes Dreieck vor einem roten Sprite, das durch Farbindex null sichtbar bleibt (96), sowie ein volles rotes Sprite vor dem BG-Dreieck als Kontrolle ohne BG-Priorität (112). Koordinaten in Pixeln ab der linken oberen Ecke; weiße Freiflächen und schwarzer Text.
FAILED CHECKS 000 bestätigt RGB-Masken, Wahrheitsflags einschließlich Wert 2, normalisierte Palettenindizes, palettenübergreifende Zugriffe, Begrenzung bei Platz 31, Nullzeiger/Nullängen ohne Schreibzugriff und Farbrücklesen bei ausgeschaltetem LCD. Vier Kombinationen werden geprüft: duales ROM auf DMG/CGB, reines DMG-ROM auf DMG und reines CGB-ROM auf CGB. Auf DMG ist CGB MODE 0, Dreiecke sind schwarz, Bank-/Spiegelattribute ändern die Anzeige nicht und die letzten beiden unteren Felder sind schwarze Quadrate. Auf CGB ist CGB MODE 1. Echte Hardware und Palettenzugriffskonflikte bei eingeschaltetem LCD sind nicht geprüft.




Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/cgb_palette.h:10
__cgb_safe_set_bcps
Schreibt auf dem CGB das Byteindexregister BCPS/BGPI ($FF68) des Palettenspeichers für Hintergrund/Fenster. __cgb_safe_set_bgpi greift auf dasselbe Register zu. Der Aufruf wählt die Position des nächsten Datenregisterzugriffs aus und schreibt selbst noch keine Farbe.
Syntax
void __cgb_safe_set_bcps(u8 value);Parameter
valueByteindex in Bits 0–5 (0–63); Bit 7 aktiviert die automatische Erhöhung nach Datenschreibzugriffen. Bit 6 ist unbenutzt. Farbplatz n beginnt bei Byte 2n. Nach 63 folgt wieder null; direkte Registerzugriffe benötigen eine eigene Bereichskontrolle.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Paletten- und geschützte Registerschreibzugriffe schreiben im DMG-Modus nichts. Safe bedeutet hier eine CGB-Modusprüfung, keine PPU-Zugriffswarteschleife und keinen kritischen Abschnitt. Verwenden Sie die Funktionen bei ausgeschaltetem LCD oder für kurze Aktualisierungen innerhalb einer zugänglichen PPU-Phase. Sie warten nicht auf VBlank und schützen mehrbyteige Updates nicht vor Interrupts.
Anwendungsbeispiel
__cgb_safe_set_bcps(0xB6); // BG byte 54, with auto-increment enabled (slot 27 low byte).Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Programm zeigt, wie BG- und OBJ-Paletten dasselbe Muster unabhängig einfärben. Jedes Dreieck ist 8 × 8 Pixel groß: oben ein Pixel links, unten eine volle Zeile. Bei x = 16, 32, 48, 64, 80, 96, 112 ist die BG-Reihe bei y = 24 rot, blau, grün, rot, blau, grün, rot; die OBJ-Reihe bei y = 64 ist blau, grün, rot, blau, grün, rot, blau. Die Spalten verwenden der Reihe nach Einzelfarbe, Komponenten, indizierten Bereich, ganze Palette, absoluten Bereich, kanonischen Registernamen und Registeralias.
Bei y = 88 zeigen sieben 8 × 8-Felder: rotes Dreieck (x = 16), blauen quadratischen Rahmen mit einem Pixel Strichstärke aus Bank 1 (32), horizontal gespiegeltes grünes Dreieck (48), vertikal gespiegeltes rotes Dreieck (64), in beiden Richtungen gespiegeltes blaues Dreieck (80), grünes Dreieck vor einem roten Sprite, das durch Farbindex null sichtbar bleibt (96), sowie ein volles rotes Sprite vor dem BG-Dreieck als Kontrolle ohne BG-Priorität (112). Koordinaten in Pixeln ab der linken oberen Ecke; weiße Freiflächen und schwarzer Text.
FAILED CHECKS 000 bestätigt RGB-Masken, Wahrheitsflags einschließlich Wert 2, normalisierte Palettenindizes, palettenübergreifende Zugriffe, Begrenzung bei Platz 31, Nullzeiger/Nullängen ohne Schreibzugriff und Farbrücklesen bei ausgeschaltetem LCD. Vier Kombinationen werden geprüft: duales ROM auf DMG/CGB, reines DMG-ROM auf DMG und reines CGB-ROM auf CGB. Auf DMG ist CGB MODE 0, Dreiecke sind schwarz, Bank-/Spiegelattribute ändern die Anzeige nicht und die letzten beiden unteren Felder sind schwarze Quadrate. Auf CGB ist CGB MODE 1. Echte Hardware und Palettenzugriffskonflikte bei eingeschaltetem LCD sind nicht geprüft.




Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/cgb_palette.h:9
__cgb_safe_set_ocpd
Schreibt auf dem CGB ein Byte in das Palettendatenregister OCPD/OBPD ($FF6B) für Sprites (OBJ). __cgb_safe_set_obpd bezeichnet dasselbe Register. Das Indexregister bestimmt die Position; eine vollständige RGB15-Farbe benötigt ein niederwertiges und ein höherwertiges Byte.
Syntax
void __cgb_safe_set_ocpd(u8 value);Parameter
valueEin Byte, 0–255. Schreiben Sie das niederwertige vor dem höherwertigen RGB15-Byte; aktivieren Sie zuvor die automatische Indexerhöhung oder wählen Sie jede Position einzeln. Ein Datenschreibzugriff aktiviert die Indexerhöhung nicht selbst.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Paletten- und geschützte Registerschreibzugriffe schreiben im DMG-Modus nichts. Safe bedeutet hier eine CGB-Modusprüfung, keine PPU-Zugriffswarteschleife und keinen kritischen Abschnitt. Verwenden Sie die Funktionen bei ausgeschaltetem LCD oder für kurze Aktualisierungen innerhalb einer zugänglichen PPU-Phase. Sie warten nicht auf VBlank und schützen mehrbyteige Updates nicht vor Interrupts.
Anwendungsbeispiel
__cgb_safe_set_ocpd(31); __cgb_safe_set_ocpd(0); // Red sprite color.
expect_color(1,27,31);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Programm zeigt, wie BG- und OBJ-Paletten dasselbe Muster unabhängig einfärben. Jedes Dreieck ist 8 × 8 Pixel groß: oben ein Pixel links, unten eine volle Zeile. Bei x = 16, 32, 48, 64, 80, 96, 112 ist die BG-Reihe bei y = 24 rot, blau, grün, rot, blau, grün, rot; die OBJ-Reihe bei y = 64 ist blau, grün, rot, blau, grün, rot, blau. Die Spalten verwenden der Reihe nach Einzelfarbe, Komponenten, indizierten Bereich, ganze Palette, absoluten Bereich, kanonischen Registernamen und Registeralias.
Bei y = 88 zeigen sieben 8 × 8-Felder: rotes Dreieck (x = 16), blauen quadratischen Rahmen mit einem Pixel Strichstärke aus Bank 1 (32), horizontal gespiegeltes grünes Dreieck (48), vertikal gespiegeltes rotes Dreieck (64), in beiden Richtungen gespiegeltes blaues Dreieck (80), grünes Dreieck vor einem roten Sprite, das durch Farbindex null sichtbar bleibt (96), sowie ein volles rotes Sprite vor dem BG-Dreieck als Kontrolle ohne BG-Priorität (112). Koordinaten in Pixeln ab der linken oberen Ecke; weiße Freiflächen und schwarzer Text.
FAILED CHECKS 000 bestätigt RGB-Masken, Wahrheitsflags einschließlich Wert 2, normalisierte Palettenindizes, palettenübergreifende Zugriffe, Begrenzung bei Platz 31, Nullzeiger/Nullängen ohne Schreibzugriff und Farbrücklesen bei ausgeschaltetem LCD. Vier Kombinationen werden geprüft: duales ROM auf DMG/CGB, reines DMG-ROM auf DMG und reines CGB-ROM auf CGB. Auf DMG ist CGB MODE 0, Dreiecke sind schwarz, Bank-/Spiegelattribute ändern die Anzeige nicht und die letzten beiden unteren Felder sind schwarze Quadrate. Auf CGB ist CGB MODE 1. Echte Hardware und Palettenzugriffskonflikte bei eingeschaltetem LCD sind nicht geprüft.




Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/cgb_palette.h:12
__cgb_safe_set_ocps
Schreibt auf dem CGB das Byteindexregister OCPS/OBPI ($FF6A) des Palettenspeichers für Sprites (OBJ). __cgb_safe_set_obpi greift auf dasselbe Register zu. Der Aufruf wählt die Position des nächsten Datenregisterzugriffs aus und schreibt selbst noch keine Farbe.
Syntax
void __cgb_safe_set_ocps(u8 value);Parameter
valueByteindex in Bits 0–5 (0–63); Bit 7 aktiviert die automatische Erhöhung nach Datenschreibzugriffen. Bit 6 ist unbenutzt. Farbplatz n beginnt bei Byte 2n. Nach 63 folgt wieder null; direkte Registerzugriffe benötigen eine eigene Bereichskontrolle.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Paletten- und geschützte Registerschreibzugriffe schreiben im DMG-Modus nichts. Safe bedeutet hier eine CGB-Modusprüfung, keine PPU-Zugriffswarteschleife und keinen kritischen Abschnitt. Verwenden Sie die Funktionen bei ausgeschaltetem LCD oder für kurze Aktualisierungen innerhalb einer zugänglichen PPU-Phase. Sie warten nicht auf VBlank und schützen mehrbyteige Updates nicht vor Interrupts.
Anwendungsbeispiel
__cgb_safe_set_ocps(0xB6); // OBJ byte 54; this does not select a BG color.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Programm zeigt, wie BG- und OBJ-Paletten dasselbe Muster unabhängig einfärben. Jedes Dreieck ist 8 × 8 Pixel groß: oben ein Pixel links, unten eine volle Zeile. Bei x = 16, 32, 48, 64, 80, 96, 112 ist die BG-Reihe bei y = 24 rot, blau, grün, rot, blau, grün, rot; die OBJ-Reihe bei y = 64 ist blau, grün, rot, blau, grün, rot, blau. Die Spalten verwenden der Reihe nach Einzelfarbe, Komponenten, indizierten Bereich, ganze Palette, absoluten Bereich, kanonischen Registernamen und Registeralias.
Bei y = 88 zeigen sieben 8 × 8-Felder: rotes Dreieck (x = 16), blauen quadratischen Rahmen mit einem Pixel Strichstärke aus Bank 1 (32), horizontal gespiegeltes grünes Dreieck (48), vertikal gespiegeltes rotes Dreieck (64), in beiden Richtungen gespiegeltes blaues Dreieck (80), grünes Dreieck vor einem roten Sprite, das durch Farbindex null sichtbar bleibt (96), sowie ein volles rotes Sprite vor dem BG-Dreieck als Kontrolle ohne BG-Priorität (112). Koordinaten in Pixeln ab der linken oberen Ecke; weiße Freiflächen und schwarzer Text.
FAILED CHECKS 000 bestätigt RGB-Masken, Wahrheitsflags einschließlich Wert 2, normalisierte Palettenindizes, palettenübergreifende Zugriffe, Begrenzung bei Platz 31, Nullzeiger/Nullängen ohne Schreibzugriff und Farbrücklesen bei ausgeschaltetem LCD. Vier Kombinationen werden geprüft: duales ROM auf DMG/CGB, reines DMG-ROM auf DMG und reines CGB-ROM auf CGB. Auf DMG ist CGB MODE 0, Dreiecke sind schwarz, Bank-/Spiegelattribute ändern die Anzeige nicht und die letzten beiden unteren Felder sind schwarze Quadrate. Auf CGB ist CGB MODE 1. Echte Hardware und Palettenzugriffskonflikte bei eingeschaltetem LCD sind nicht geprüft.




Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/cgb_palette.h:11
cgb_tile.h — CGB-Kachelnummern und -attribute
cgb_tile.h bündelt Deklarationen für Kachelkarten-Intrinsics und Makros zum Erzeugen von CGB-Kachelattributen. Eine Kachelnummer wählt ein Muster; das Attribut wählt Palette, Muster-VRAM-Bank, Spiegelungen und Hintergrundpriorität. Attribute verändern keine Musterpixel.
Die Familie __settile adressiert Karte 0x9800; __settileat nutzt eine ausdrückliche Kartenbasis, __settilebg die von LCDC gewählte Hintergrundkarte und __settilewin dessen Fensterkarte. Die attr-Formen schreiben nur Attribute, die cgb-Formen Kachelnummern und Attribute. bulk-Formen übertragen fortlaufende Bytes an die angegebene Adresse. Wählen Sie nach Ziel und geschriebenen Daten.
Verwenden Sie Makros wie KQ_CGB_ATTR_PAL für einzelne Attributbits oder KQ_CGB_ATTR zum Kombinieren der Felder. Das Bankfeld wählt die VRAM-Bank des Musters, keine ROM-Bank. Gewöhnliche Kachelnummern-Schreibzugriffe nutzen die aktuelle VRAM-Bank; wählen Sie vor kombinierten Kachel-/Attributoperationen Bank null. Auf dem DMG bleiben reine Attributoperationen ohne Wirkung; kombinierte Operationen schreiben weiterhin die Kachelnummern.
Der Compiler erzeugt die Implementierung der __-Intrinsics; cgb_tile.c muss nicht eingebunden werden. Gewöhnliche Schreibpfade warten auf VRAM-Zugriff, ändern teilweise aber auch den Interruptfreigabezustand. _unsafe- und _fast-Formen lassen diese Wartezeiten aus. Verwenden Sie sie nur bei garantiert beschreibbarem VRAM, etwa bei ausgeschaltetem LCD. Prüfen Sie die Interrupt-, Bank- und Längenbedingungen jedes Eintrags.
__cgb_safe_set_vbk
Wählt auf dem CGB über VBK ($FF4F) die VRAM-Bank für CPU-Zugriffe. Geschrieben wird nur value & 1: gerade Werte wählen Bank 0, ungerade Bank 1. Betrifft CPU-Zugriffe auf $8000–$9FFF, nicht die Musterbank im Kartenattribut. Die vorherige Bank wird nicht wiederhergestellt.
Syntax
void __cgb_safe_set_vbk(u8 value);Parameter
valueWählt auf dem CGB über VBK (
$FF4F) die VRAM-Bank für CPU-Zugriffe. Geschrieben wird nurvalue & 1: gerade Werte wählen Bank 0, ungerade Bank 1. Betrifft CPU-Zugriffe auf$8000–$9FFF, nicht die Musterbank im Kartenattribut. Die vorherige Bank wird nicht wiederhergestellt.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Paletten- und geschützte Registerschreibzugriffe schreiben im DMG-Modus nichts. Safe bedeutet hier eine CGB-Modusprüfung, keine PPU-Zugriffswarteschleife und keinen kritischen Abschnitt. Verwenden Sie die Funktionen bei ausgeschaltetem LCD oder für kurze Aktualisierungen innerhalb einer zugänglichen PPU-Phase. Sie warten nicht auf VBlank und schützen mehrbyteige Updates nicht vor Interrupts.
Anwendungsbeispiel
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++;
}Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Programm zeigt, wie BG- und OBJ-Paletten dasselbe Muster unabhängig einfärben. Jedes Dreieck ist 8 × 8 Pixel groß: oben ein Pixel links, unten eine volle Zeile. Bei x = 16, 32, 48, 64, 80, 96, 112 ist die BG-Reihe bei y = 24 rot, blau, grün, rot, blau, grün, rot; die OBJ-Reihe bei y = 64 ist blau, grün, rot, blau, grün, rot, blau. Die Spalten verwenden der Reihe nach Einzelfarbe, Komponenten, indizierten Bereich, ganze Palette, absoluten Bereich, kanonischen Registernamen und Registeralias.
Bei y = 88 zeigen sieben 8 × 8-Felder: rotes Dreieck (x = 16), blauen quadratischen Rahmen mit einem Pixel Strichstärke aus Bank 1 (32), horizontal gespiegeltes grünes Dreieck (48), vertikal gespiegeltes rotes Dreieck (64), in beiden Richtungen gespiegeltes blaues Dreieck (80), grünes Dreieck vor einem roten Sprite, das durch Farbindex null sichtbar bleibt (96), sowie ein volles rotes Sprite vor dem BG-Dreieck als Kontrolle ohne BG-Priorität (112). Koordinaten in Pixeln ab der linken oberen Ecke; weiße Freiflächen und schwarzer Text.
FAILED CHECKS 000 bestätigt RGB-Masken, Wahrheitsflags einschließlich Wert 2, normalisierte Palettenindizes, palettenübergreifende Zugriffe, Begrenzung bei Platz 31, Nullzeiger/Nullängen ohne Schreibzugriff und Farbrücklesen bei ausgeschaltetem LCD. Vier Kombinationen werden geprüft: duales ROM auf DMG/CGB, reines DMG-ROM auf DMG und reines CGB-ROM auf CGB. Auf DMG ist CGB MODE 0, Dreiecke sind schwarz, Bank-/Spiegelattribute ändern die Anzeige nicht und die letzten beiden unteren Felder sind schwarze Quadrate. Auf CGB ist CGB MODE 1. Echte Hardware und Palettenzugriffskonflikte bei eingeschaltetem LCD sind nicht geprüft.




Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/cgb_tile.h:28
Write the VRAM bank selection only on CGB. Safe here means hardware-mode guarded, not PPU-timed.
__settile
Schreibt eine Tile-Nummer in eine Kartenposition. Das wählt ein vorhandenes Tile-Bild aus und ändert keine Pixeldaten.
Das Ziel ist stets 0x9800 + 32*y + x, unabhängig von der Hintergrund- und Fensterkartenauswahl in LCDC.
Wartet bei Bedarf auf beschreibbares VRAM. Bei aktivem LCD werden Interrupts gesperrt und anschließend möglicherweise aktiviert. Weder der vorherige Interruptzustand noch ein Abschluss innerhalb eines VBlank wird garantiert.
Syntax
void __settile(u8 x, u8 y, u8 tile);Parameter
x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
tileTile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Tile-Bytes werden in die gewählte VRAM-Bank geschrieben. Wählen Sie auf CGB Bank 0 für Tile-Nummern; der Aufruf wechselt die Bank nicht.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheinen die Beispielzeichen und die Ergebnisanzeige blau auf weißem Hintergrund. Die Vorbereitung setzt die Zeichenfarbe der Palette 0 auf Blau, ohne die Attribute der Tilemap zu ändern. Auf DMG erscheinen dieselben Zeichen schwarz auf weißem Hintergrund.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
A

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/cgb_tile.h:31
Write a tile number to map 9800, waiting for VRAM access when necessary.
__settile_bulk
Kopiert count aufeinanderfolgende Tile-Nummernbytes von src zur VRAM-Adresse dest. Gezählt werden Bytes, keine Tile-Bilder; Zeilenabstände werden nicht angepasst.
Wartet bei Bedarf auf beschreibbares VRAM. Bei aktivem LCD werden Interrupts gesperrt und anschließend möglicherweise aktiviert. Weder der vorherige Interruptzustand noch ein Abschluss innerhalb eines VBlank wird garantiert.
Syntax
void __settile_bulk(u16 dest, const u8* src, u8 count);Parameter
destVRAM-Byteadresse des Anfangs. Für Karten gilt
0x9800–0x9FFF; der gesamte Transfer muss innerhalb der gewünschten Karte bleiben.srcLückenlose Bytes im RAM oder einer sichtbaren ROM-Bank. Der Tile-Strom enthält Tile-Nummern, der Attributstrom CGB-Attribute. Die Kopie erfolgt sofort; der Zeiger wird nicht gespeichert.
countByteanzahl von 0 bis 255. Null kopiert nichts. Die Quelle muss mindestens so viele lesbare Bytes enthalten.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Tile-Bytes werden in die gewählte VRAM-Bank geschrieben. Wählen Sie auf CGB Bank 0 für Tile-Nummern; der Aufruf wechselt die Bank nicht.
Quellpuffer und zugehörige ROM-Bank müssen während der gesamten Kopie lesbar bleiben. Kein Rückgabewert.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheinen die Beispielzeichen und die Ergebnisanzeige blau auf weißem Hintergrund. Die Vorbereitung setzt die Zeichenfarbe der Palette 0 auf Blau, ohne die Attribute der Tilemap zu ändern. Auf DMG erscheinen dieselben Zeichen schwarz auf weißem Hintergrund.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
ABC

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/cgb_tile.h:48
Copy count sequential bytes to dest using the current VRAM bank and access-checked transfer.
__settile_bulk_fast
Kopiert count aufeinanderfolgende Tile-Nummernbytes von src zur VRAM-Adresse dest. Gezählt werden Bytes, keine Tile-Bilder; Zeilenabstände werden nicht angepasst.
Wartet nicht auf VRAM-Zugriff. Nur bei ausgeschaltetem LCD oder in einem vom Aufrufer gesicherten Schreibintervall verwenden, das für den gesamten Transfer reicht.
Syntax
void __settile_bulk_fast(u16 dest, const u8* src, u8 count);Parameter
destVRAM-Byteadresse des Anfangs. Für Karten gilt
0x9800–0x9FFF; der gesamte Transfer muss innerhalb der gewünschten Karte bleiben.srcLückenlose Bytes im RAM oder einer sichtbaren ROM-Bank. Der Tile-Strom enthält Tile-Nummern, der Attributstrom CGB-Attribute. Die Kopie erfolgt sofort; der Zeiger wird nicht gespeichert.
countByteanzahl von 0 bis 255. Null kopiert nichts. Die Quelle muss mindestens so viele lesbare Bytes enthalten.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Tile-Bytes werden in die gewählte VRAM-Bank geschrieben. Wählen Sie auf CGB Bank 0 für Tile-Nummern; der Aufruf wechselt die Bank nicht.
Quellpuffer und zugehörige ROM-Bank müssen während der gesamten Kopie lesbar bleiben. Kein Rückgabewert.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheinen die Beispielzeichen und die Ergebnisanzeige blau auf weißem Hintergrund. Die Vorbereitung setzt die Zeichenfarbe der Palette 0 auf Blau, ohne die Attribute der Tilemap zu ändern. Auf DMG erscheinen dieselben Zeichen schwarz auf weißem Hintergrund.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
ABC

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/cgb_tile.h:50
Copy count sequential bytes to dest without VRAM access waits; retain source-bank visibility.
__settile_unsafe
Schreibt eine Tile-Nummer in eine Kartenposition. Das wählt ein vorhandenes Tile-Bild aus und ändert keine Pixeldaten.
Das Ziel ist stets 0x9800 + 32*y + x, unabhängig von der Hintergrund- und Fensterkartenauswahl in LCDC.
Wartet nicht auf VRAM-Zugriff. Nur bei ausgeschaltetem LCD oder in einem vom Aufrufer gesicherten Schreibintervall verwenden, das für den gesamten Transfer reicht.
Syntax
void __settile_unsafe(u8 x, u8 y, u8 tile);Parameter
x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
tileTile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Tile-Bytes werden in die gewählte VRAM-Bank geschrieben. Wählen Sie auf CGB Bank 0 für Tile-Nummern; der Aufruf wechselt die Bank nicht.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheinen die Beispielzeichen und die Ergebnisanzeige blau auf weißem Hintergrund. Die Vorbereitung setzt die Zeichenfarbe der Palette 0 auf Blau, ohne die Attribute der Tilemap zu ändern. Auf DMG erscheinen dieselben Zeichen schwarz auf weißem Hintergrund.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
A

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/cgb_tile.h:33
Write a tile number to map 9800 without waiting for writable VRAM.
__settileat
Schreibt eine Tile-Nummer in eine Kartenposition. Das wählt ein vorhandenes Tile-Bild aus und ändert keine Pixeldaten.
base wählt die Karte (0x9800 oder 0x9C00); die Byteadresse lautet base + 32*y + x.
Wartet bei Bedarf auf beschreibbares VRAM. Bei aktivem LCD werden Interrupts gesperrt und anschließend möglicherweise aktiviert. Weder der vorherige Interruptzustand noch ein Abschluss innerhalb eines VBlank wird garantiert.
Syntax
void __settileat(u16 base, u8 x, u8 y, u8 tile);Parameter
basebasewählt die Karte (0x9800oder0x9C00); die Byteadresse lautetbase + 32*y + x.x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
tileTile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Tile-Bytes werden in die gewählte VRAM-Bank geschrieben. Wählen Sie auf CGB Bank 0 für Tile-Nummern; der Aufruf wechselt die Bank nicht.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheinen die Beispielzeichen und die Ergebnisanzeige blau auf weißem Hintergrund. Die Vorbereitung setzt die Zeichenfarbe der Palette 0 auf Blau, ohne die Attribute der Tilemap zu ändern. Auf DMG erscheinen dieselben Zeichen schwarz auf weißem Hintergrund.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
A

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/cgb_tile.h:35
Write at base+32*y+x with VRAM access checks; supply a valid tile-map base.
__settileat_unsafe
Schreibt eine Tile-Nummer in eine Kartenposition. Das wählt ein vorhandenes Tile-Bild aus und ändert keine Pixeldaten.
base wählt die Karte (0x9800 oder 0x9C00); die Byteadresse lautet base + 32*y + x.
Wartet nicht auf VRAM-Zugriff. Nur bei ausgeschaltetem LCD oder in einem vom Aufrufer gesicherten Schreibintervall verwenden, das für den gesamten Transfer reicht.
Syntax
void __settileat_unsafe(u16 base, u8 x, u8 y, u8 tile);Parameter
basebasewählt die Karte (0x9800oder0x9C00); die Byteadresse lautetbase + 32*y + x.x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
tileTile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Tile-Bytes werden in die gewählte VRAM-Bank geschrieben. Wählen Sie auf CGB Bank 0 für Tile-Nummern; der Aufruf wechselt die Bank nicht.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheinen die Beispielzeichen und die Ergebnisanzeige blau auf weißem Hintergrund. Die Vorbereitung setzt die Zeichenfarbe der Palette 0 auf Blau, ohne die Attribute der Tilemap zu ändern. Auf DMG erscheinen dieselben Zeichen schwarz auf weißem Hintergrund.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
A

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/cgb_tile.h:37
Use an explicit map base without waiting for VRAM access.
__settileatattr
Ändert nur das CGB-Attributbyte, nicht die Tile-Nummer. Die Bits wählen Palette, Bilddatenbank, Spiegelungen und Priorität.
base wählt die Karte (0x9800 oder 0x9C00); die Byteadresse lautet base + 32*y + x.
Wartet bei Bedarf auf beschreibbares VRAM. Bei aktivem LCD werden Interrupts gesperrt und anschließend möglicherweise aktiviert. Weder der vorherige Interruptzustand noch ein Abschluss innerhalb eines VBlank wird garantiert.
Syntax
void __settileatattr(u16 base, u8 x, u8 y, u8 attr);Parameter
basebasewählt die Karte (0x9800oder0x9C00); die Byteadresse lautetbase + 32*y + x.x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
attrCGB-Attributbyte: Bits 0–2 wählen Palette 0–7, Bit 3 die Tile-Datenbank, Bits 5 und 6 die horizontale und vertikale Spiegelung, Bit 7 die BG-/Fensterpriorität. Das unbenutzte Bit 4 bleibt null.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Auf CGB erfolgt der Attributzugriff in VRAM-Bank 1; danach gilt VBK=0. Auf DMG werden keine Attribute geschrieben.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheint das A blau: Attributwert 2 wählt die im Beispiel vorbereitete blaue Palette. Auf DMG ist dasselbe Zeichen schwarz, da CGB-Attribute dort nicht angewendet werden.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
A

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Ändert nur das CGB-Attributbyte, nicht die Tile-Nummer. Die Bits wählen Palette, Bilddatenbank, Spiegelungen und Priorität.
base wählt die Karte (0x9800 oder 0x9C00); die Byteadresse lautet base + 32*y + x.
Wartet nicht auf VRAM-Zugriff. Nur bei ausgeschaltetem LCD oder in einem vom Aufrufer gesicherten Schreibintervall verwenden, das für den gesamten Transfer reicht.
Syntax
void __settileatattr_unsafe(u16 base, u8 x, u8 y, u8 attr);Parameter
basebasewählt die Karte (0x9800oder0x9C00); die Byteadresse lautetbase + 32*y + x.x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
attrCGB-Attributbyte: Bits 0–2 wählen Palette 0–7, Bit 3 die Tile-Datenbank, Bits 5 und 6 die horizontale und vertikale Spiegelung, Bit 7 die BG-/Fensterpriorität. Das unbenutzte Bit 4 bleibt null.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Auf CGB erfolgt der Attributzugriff in VRAM-Bank 1; danach gilt VBK=0. Auf DMG werden keine Attribute geschrieben.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheint das A blau: Attributwert 2 wählt die im Beispiel vorbereitete blaue Palette. Auf DMG ist dasselbe Zeichen schwarz, da CGB-Attribute dort nicht angewendet werden.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
A

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Schreibt Tile-Nummer und CGB-Attribut. Auf DMG wird nur die Nummer geschrieben. Auf CGB muss vorher Bank 0 gewählt sein; Attribute verwenden Bank 1, danach gilt VBK=0.
base wählt die Karte (0x9800 oder 0x9C00); die Byteadresse lautet base + 32*y + x.
Wartet bei Bedarf auf beschreibbares VRAM. Bei aktivem LCD werden Interrupts gesperrt und anschließend möglicherweise aktiviert. Weder der vorherige Interruptzustand noch ein Abschluss innerhalb eines VBlank wird garantiert.
Syntax
void __settileatcgb(u16 base, u8 x, u8 y, u8 tile, u8 attr);Parameter
basebasewählt die Karte (0x9800oder0x9C00); die Byteadresse lautetbase + 32*y + x.x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
tileTile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.
attrCGB-Attributbyte: Bits 0–2 wählen Palette 0–7, Bit 3 die Tile-Datenbank, Bits 5 und 6 die horizontale und vertikale Spiegelung, Bit 7 die BG-/Fensterpriorität. Das unbenutzte Bit 4 bleibt null.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheint das A blau: Attributwert 2 wählt die im Beispiel vorbereitete blaue Palette. Auf DMG ist dasselbe Zeichen schwarz, da CGB-Attribute dort nicht angewendet werden.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
A

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Schreibt Tile-Nummer und CGB-Attribut. Auf DMG wird nur die Nummer geschrieben. Auf CGB muss vorher Bank 0 gewählt sein; Attribute verwenden Bank 1, danach gilt VBK=0.
base wählt die Karte (0x9800 oder 0x9C00); die Byteadresse lautet base + 32*y + x.
Wartet nicht auf VRAM-Zugriff. Nur bei ausgeschaltetem LCD oder in einem vom Aufrufer gesicherten Schreibintervall verwenden, das für den gesamten Transfer reicht.
Syntax
void __settileatcgb_unsafe(u16 base, u8 x, u8 y, u8 tile, u8 attr);Parameter
basebasewählt die Karte (0x9800oder0x9C00); die Byteadresse lautetbase + 32*y + x.x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
tileTile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.
attrCGB-Attributbyte: Bits 0–2 wählen Palette 0–7, Bit 3 die Tile-Datenbank, Bits 5 und 6 die horizontale und vertikale Spiegelung, Bit 7 die BG-/Fensterpriorität. Das unbenutzte Bit 4 bleibt null.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheint das A blau: Attributwert 2 wählt die im Beispiel vorbereitete blaue Palette. Auf DMG ist dasselbe Zeichen schwarz, da CGB-Attribute dort nicht angewendet werden.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
A

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Ändert nur das CGB-Attributbyte, nicht die Tile-Nummer. Die Bits wählen Palette, Bilddatenbank, Spiegelungen und Priorität.
Das Ziel ist stets 0x9800 + 32*y + x, unabhängig von der Hintergrund- und Fensterkartenauswahl in LCDC.
Wartet bei Bedarf auf beschreibbares VRAM. Bei aktivem LCD werden Interrupts gesperrt und anschließend möglicherweise aktiviert. Weder der vorherige Interruptzustand noch ein Abschluss innerhalb eines VBlank wird garantiert.
Syntax
void __settileattr(u8 x, u8 y, u8 attr);Parameter
x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
attrCGB-Attributbyte: Bits 0–2 wählen Palette 0–7, Bit 3 die Tile-Datenbank, Bits 5 und 6 die horizontale und vertikale Spiegelung, Bit 7 die BG-/Fensterpriorität. Das unbenutzte Bit 4 bleibt null.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Auf CGB erfolgt der Attributzugriff in VRAM-Bank 1; danach gilt VBK=0. Auf DMG werden keine Attribute geschrieben.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheint das A blau: Attributwert 2 wählt die im Beispiel vorbereitete blaue Palette. Auf DMG ist dasselbe Zeichen schwarz, da CGB-Attribute dort nicht angewendet werden.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
A

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Kopiert count aufeinanderfolgende CGB-Attributbytes von src nach dest in VRAM-Bank 1. Tile-Nummern bleiben unverändert; dest ist eine Adresse, kein (x,y)-Paar.
Wartet bei Bedarf auf beschreibbares VRAM. Bei aktivem LCD werden Interrupts gesperrt und anschließend möglicherweise aktiviert. Weder der vorherige Interruptzustand noch ein Abschluss innerhalb eines VBlank wird garantiert.
Syntax
void __settileattr_bulk(u16 dest, const u8* src, u8 count);Parameter
destVRAM-Byteadresse des Anfangs. Für Karten gilt
0x9800–0x9FFF; der gesamte Transfer muss innerhalb der gewünschten Karte bleiben.srcLückenlose Bytes im RAM oder einer sichtbaren ROM-Bank. Der Tile-Strom enthält Tile-Nummern, der Attributstrom CGB-Attribute. Die Kopie erfolgt sofort; der Zeiger wird nicht gespeichert.
countByteanzahl von 0 bis 255. Null kopiert nichts. Die Quelle muss mindestens so viele lesbare Bytes enthalten.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Auf CGB erfolgt der Attributzugriff in VRAM-Bank 1; danach gilt VBK=0. Auf DMG werden keine Attribute geschrieben.
Quellpuffer und zugehörige ROM-Bank müssen während der gesamten Kopie lesbar bleiben. Kein Rückgabewert.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheinen A, B und C rot, blau und grün: Die Attributbytes 1,2,3 wählen drei unterschiedliche Paletten. Auf DMG sind alle drei Buchstaben schwarz.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
ABC

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Kopiert count aufeinanderfolgende CGB-Attributbytes von src nach dest in VRAM-Bank 1. Tile-Nummern bleiben unverändert; dest ist eine Adresse, kein (x,y)-Paar.
Wartet nicht auf VRAM-Zugriff. Nur bei ausgeschaltetem LCD oder in einem vom Aufrufer gesicherten Schreibintervall verwenden, das für den gesamten Transfer reicht.
Syntax
void __settileattr_bulk_fast(u16 dest, const u8* src, u8 count);Parameter
destVRAM-Byteadresse des Anfangs. Für Karten gilt
0x9800–0x9FFF; der gesamte Transfer muss innerhalb der gewünschten Karte bleiben.srcLückenlose Bytes im RAM oder einer sichtbaren ROM-Bank. Der Tile-Strom enthält Tile-Nummern, der Attributstrom CGB-Attribute. Die Kopie erfolgt sofort; der Zeiger wird nicht gespeichert.
countByteanzahl von 0 bis 255. Null kopiert nichts. Die Quelle muss mindestens so viele lesbare Bytes enthalten.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Auf CGB erfolgt der Attributzugriff in VRAM-Bank 1; danach gilt VBK=0. Auf DMG werden keine Attribute geschrieben.
Quellpuffer und zugehörige ROM-Bank müssen während der gesamten Kopie lesbar bleiben. Kein Rückgabewert.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheinen A, B und C rot, blau und grün: Die Attributbytes 1,2,3 wählen drei unterschiedliche Paletten. Auf DMG sind alle drei Buchstaben schwarz.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
ABC

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Ändert nur das CGB-Attributbyte, nicht die Tile-Nummer. Die Bits wählen Palette, Bilddatenbank, Spiegelungen und Priorität.
Das Ziel ist stets 0x9800 + 32*y + x, unabhängig von der Hintergrund- und Fensterkartenauswahl in LCDC.
Wartet nicht auf VRAM-Zugriff. Nur bei ausgeschaltetem LCD oder in einem vom Aufrufer gesicherten Schreibintervall verwenden, das für den gesamten Transfer reicht.
Syntax
void __settileattr_unsafe(u8 x, u8 y, u8 attr);Parameter
x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
attrCGB-Attributbyte: Bits 0–2 wählen Palette 0–7, Bit 3 die Tile-Datenbank, Bits 5 und 6 die horizontale und vertikale Spiegelung, Bit 7 die BG-/Fensterpriorität. Das unbenutzte Bit 4 bleibt null.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Auf CGB erfolgt der Attributzugriff in VRAM-Bank 1; danach gilt VBK=0. Auf DMG werden keine Attribute geschrieben.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheint das A blau: Attributwert 2 wählt die im Beispiel vorbereitete blaue Palette. Auf DMG ist dasselbe Zeichen schwarz, da CGB-Attribute dort nicht angewendet werden.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
A

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Schreibt eine Tile-Nummer in eine Kartenposition. Das wählt ein vorhandenes Tile-Bild aus und ändert keine Pixeldaten.
LCDC-Bit 3 wählt die Hintergrundkarte: null bedeutet 0x9800, eins 0x9C00. Zum Basiswert kommt 32*y + x hinzu.
Wartet bei Bedarf auf beschreibbares VRAM. Bei aktivem LCD werden Interrupts gesperrt und anschließend möglicherweise aktiviert. Weder der vorherige Interruptzustand noch ein Abschluss innerhalb eines VBlank wird garantiert.
Syntax
void __settilebg(u8 x, u8 y, u8 tile);Parameter
x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
tileTile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Tile-Bytes werden in die gewählte VRAM-Bank geschrieben. Wählen Sie auf CGB Bank 0 für Tile-Nummern; der Aufruf wechselt die Bank nicht.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheinen die Beispielzeichen und die Ergebnisanzeige blau auf weißem Hintergrund. Die Vorbereitung setzt die Zeichenfarbe der Palette 0 auf Blau, ohne die Attribute der Tilemap zu ändern. Auf DMG erscheinen dieselben Zeichen schwarz auf weißem Hintergrund.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
A

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/cgb_tile.h:43
Select the background map from LCDC bit 3, then write with access checks.
__settilebg_unsafe
Schreibt eine Tile-Nummer in eine Kartenposition. Das wählt ein vorhandenes Tile-Bild aus und ändert keine Pixeldaten.
LCDC-Bit 3 wählt die Hintergrundkarte: null bedeutet 0x9800, eins 0x9C00. Zum Basiswert kommt 32*y + x hinzu.
Wartet nicht auf VRAM-Zugriff. Nur bei ausgeschaltetem LCD oder in einem vom Aufrufer gesicherten Schreibintervall verwenden, das für den gesamten Transfer reicht.
Syntax
void __settilebg_unsafe(u8 x, u8 y, u8 tile);Parameter
x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
tileTile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Tile-Bytes werden in die gewählte VRAM-Bank geschrieben. Wählen Sie auf CGB Bank 0 für Tile-Nummern; der Aufruf wechselt die Bank nicht.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheinen die Beispielzeichen und die Ergebnisanzeige blau auf weißem Hintergrund. Die Vorbereitung setzt die Zeichenfarbe der Palette 0 auf Blau, ohne die Attribute der Tilemap zu ändern. Auf DMG erscheinen dieselben Zeichen schwarz auf weißem Hintergrund.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
A

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/cgb_tile.h:45
Select the LCDC background map and write without access waits.
__settilebgattr
Ändert nur das CGB-Attributbyte, nicht die Tile-Nummer. Die Bits wählen Palette, Bilddatenbank, Spiegelungen und Priorität.
LCDC-Bit 3 wählt die Hintergrundkarte: null bedeutet 0x9800, eins 0x9C00. Zum Basiswert kommt 32*y + x hinzu.
Wartet bei Bedarf auf beschreibbares VRAM. Bei aktivem LCD werden Interrupts gesperrt und anschließend möglicherweise aktiviert. Weder der vorherige Interruptzustand noch ein Abschluss innerhalb eines VBlank wird garantiert.
Syntax
void __settilebgattr(u8 x, u8 y, u8 attr);Parameter
x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
attrCGB-Attributbyte: Bits 0–2 wählen Palette 0–7, Bit 3 die Tile-Datenbank, Bits 5 und 6 die horizontale und vertikale Spiegelung, Bit 7 die BG-/Fensterpriorität. Das unbenutzte Bit 4 bleibt null.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Auf CGB erfolgt der Attributzugriff in VRAM-Bank 1; danach gilt VBK=0. Auf DMG werden keine Attribute geschrieben.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheint das A blau: Attributwert 2 wählt die im Beispiel vorbereitete blaue Palette. Auf DMG ist dasselbe Zeichen schwarz, da CGB-Attribute dort nicht angewendet werden.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
A

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Ändert nur das CGB-Attributbyte, nicht die Tile-Nummer. Die Bits wählen Palette, Bilddatenbank, Spiegelungen und Priorität.
LCDC-Bit 3 wählt die Hintergrundkarte: null bedeutet 0x9800, eins 0x9C00. Zum Basiswert kommt 32*y + x hinzu.
Wartet nicht auf VRAM-Zugriff. Nur bei ausgeschaltetem LCD oder in einem vom Aufrufer gesicherten Schreibintervall verwenden, das für den gesamten Transfer reicht.
Syntax
void __settilebgattr_unsafe(u8 x, u8 y, u8 attr);Parameter
x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
attrCGB-Attributbyte: Bits 0–2 wählen Palette 0–7, Bit 3 die Tile-Datenbank, Bits 5 und 6 die horizontale und vertikale Spiegelung, Bit 7 die BG-/Fensterpriorität. Das unbenutzte Bit 4 bleibt null.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Auf CGB erfolgt der Attributzugriff in VRAM-Bank 1; danach gilt VBK=0. Auf DMG werden keine Attribute geschrieben.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheint das A blau: Attributwert 2 wählt die im Beispiel vorbereitete blaue Palette. Auf DMG ist dasselbe Zeichen schwarz, da CGB-Attribute dort nicht angewendet werden.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
A

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Schreibt Tile-Nummer und CGB-Attribut. Auf DMG wird nur die Nummer geschrieben. Auf CGB muss vorher Bank 0 gewählt sein; Attribute verwenden Bank 1, danach gilt VBK=0.
LCDC-Bit 3 wählt die Hintergrundkarte: null bedeutet 0x9800, eins 0x9C00. Zum Basiswert kommt 32*y + x hinzu.
Wartet bei Bedarf auf beschreibbares VRAM. Bei aktivem LCD werden Interrupts gesperrt und anschließend möglicherweise aktiviert. Weder der vorherige Interruptzustand noch ein Abschluss innerhalb eines VBlank wird garantiert.
Syntax
void __settilebgcgb(u8 x, u8 y, u8 tile, u8 attr);Parameter
x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
tileTile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.
attrCGB-Attributbyte: Bits 0–2 wählen Palette 0–7, Bit 3 die Tile-Datenbank, Bits 5 und 6 die horizontale und vertikale Spiegelung, Bit 7 die BG-/Fensterpriorität. Das unbenutzte Bit 4 bleibt null.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheint das A blau: Attributwert 2 wählt die im Beispiel vorbereitete blaue Palette. Auf DMG ist dasselbe Zeichen schwarz, da CGB-Attribute dort nicht angewendet werden.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
A

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Schreibt Tile-Nummer und CGB-Attribut. Auf DMG wird nur die Nummer geschrieben. Auf CGB muss vorher Bank 0 gewählt sein; Attribute verwenden Bank 1, danach gilt VBK=0.
LCDC-Bit 3 wählt die Hintergrundkarte: null bedeutet 0x9800, eins 0x9C00. Zum Basiswert kommt 32*y + x hinzu.
Wartet nicht auf VRAM-Zugriff. Nur bei ausgeschaltetem LCD oder in einem vom Aufrufer gesicherten Schreibintervall verwenden, das für den gesamten Transfer reicht.
Syntax
void __settilebgcgb_unsafe(u8 x, u8 y, u8 tile, u8 attr);Parameter
x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
tileTile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.
attrCGB-Attributbyte: Bits 0–2 wählen Palette 0–7, Bit 3 die Tile-Datenbank, Bits 5 und 6 die horizontale und vertikale Spiegelung, Bit 7 die BG-/Fensterpriorität. Das unbenutzte Bit 4 bleibt null.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheint das A blau: Attributwert 2 wählt die im Beispiel vorbereitete blaue Palette. Auf DMG ist dasselbe Zeichen schwarz, da CGB-Attribute dort nicht angewendet werden.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
A

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Schreibt Tile-Nummer und CGB-Attribut. Auf DMG wird nur die Nummer geschrieben. Auf CGB muss vorher Bank 0 gewählt sein; Attribute verwenden Bank 1, danach gilt VBK=0.
Das Ziel ist stets 0x9800 + 32*y + x, unabhängig von der Hintergrund- und Fensterkartenauswahl in LCDC.
Wartet bei Bedarf auf beschreibbares VRAM. Bei aktivem LCD werden Interrupts gesperrt und anschließend möglicherweise aktiviert. Weder der vorherige Interruptzustand noch ein Abschluss innerhalb eines VBlank wird garantiert.
Syntax
void __settilecgb(u8 x, u8 y, u8 tile, u8 attr);Parameter
x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
tileTile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.
attrCGB-Attributbyte: Bits 0–2 wählen Palette 0–7, Bit 3 die Tile-Datenbank, Bits 5 und 6 die horizontale und vertikale Spiegelung, Bit 7 die BG-/Fensterpriorität. Das unbenutzte Bit 4 bleibt null.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheint das A blau: Attributwert 2 wählt die im Beispiel vorbereitete blaue Palette. Auf DMG ist dasselbe Zeichen schwarz, da CGB-Attribute dort nicht angewendet werden.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
A

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Kopiert count Nummern aus tile_src und anschließend count Attribute aus attr_src in denselben Kartenbereich ab dest. Die Arrays sind getrennt, nicht abwechselnd angeordnet.
Wartet bei Bedarf auf beschreibbares VRAM. Bei aktivem LCD werden Interrupts gesperrt und anschließend möglicherweise aktiviert. Weder der vorherige Interruptzustand noch ein Abschluss innerhalb eines VBlank wird garantiert.
Syntax
void __settilecgb_bulk(u16 dest, const u8* tile_src, const u8* attr_src, u8 count);Parameter
destVRAM-Byteadresse des Anfangs. Für Karten gilt
0x9800–0x9FFF; der gesamte Transfer muss innerhalb der gewünschten Karte bleiben.tile_src, attr_srcLückenlose Bytes im RAM oder einer sichtbaren ROM-Bank. Der Tile-Strom enthält Tile-Nummern, der Attributstrom CGB-Attribute. Die Kopie erfolgt sofort; der Zeiger wird nicht gespeichert.
countByteanzahl von 0 bis 255. Null kopiert nichts. Die Quelle muss mindestens so viele lesbare Bytes enthalten.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Schreibt Tile-Nummer und CGB-Attribut. Auf DMG wird nur die Nummer geschrieben. Auf CGB muss vorher Bank 0 gewählt sein; Attribute verwenden Bank 1, danach gilt VBK=0.
Quellpuffer und zugehörige ROM-Bank müssen während der gesamten Kopie lesbar bleiben. Kein Rückgabewert.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheinen A, B und C rot, blau und grün: Die Attributbytes 1,2,3 wählen drei unterschiedliche Paletten. Auf DMG sind alle drei Buchstaben schwarz.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
ABC

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Kopiert count Nummern aus tile_src und anschließend count Attribute aus attr_src in denselben Kartenbereich ab dest. Die Arrays sind getrennt, nicht abwechselnd angeordnet.
Wartet nicht auf VRAM-Zugriff. Nur bei ausgeschaltetem LCD oder in einem vom Aufrufer gesicherten Schreibintervall verwenden, das für den gesamten Transfer reicht.
Syntax
void __settilecgb_bulk_fast(u16 dest, const u8* tile_src, const u8* attr_src, u8 count);Parameter
destVRAM-Byteadresse des Anfangs. Für Karten gilt
0x9800–0x9FFF; der gesamte Transfer muss innerhalb der gewünschten Karte bleiben.tile_src, attr_srcLückenlose Bytes im RAM oder einer sichtbaren ROM-Bank. Der Tile-Strom enthält Tile-Nummern, der Attributstrom CGB-Attribute. Die Kopie erfolgt sofort; der Zeiger wird nicht gespeichert.
countByteanzahl von 0 bis 255. Null kopiert nichts. Die Quelle muss mindestens so viele lesbare Bytes enthalten.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Schreibt Tile-Nummer und CGB-Attribut. Auf DMG wird nur die Nummer geschrieben. Auf CGB muss vorher Bank 0 gewählt sein; Attribute verwenden Bank 1, danach gilt VBK=0.
Quellpuffer und zugehörige ROM-Bank müssen während der gesamten Kopie lesbar bleiben. Kein Rückgabewert.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheinen A, B und C rot, blau und grün: Die Attributbytes 1,2,3 wählen drei unterschiedliche Paletten. Auf DMG sind alle drei Buchstaben schwarz.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
ABC

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Schreibt Tile-Nummer und CGB-Attribut. Auf DMG wird nur die Nummer geschrieben. Auf CGB muss vorher Bank 0 gewählt sein; Attribute verwenden Bank 1, danach gilt VBK=0.
Das Ziel ist stets 0x9800 + 32*y + x, unabhängig von der Hintergrund- und Fensterkartenauswahl in LCDC.
Wartet nicht auf VRAM-Zugriff. Nur bei ausgeschaltetem LCD oder in einem vom Aufrufer gesicherten Schreibintervall verwenden, das für den gesamten Transfer reicht.
Syntax
void __settilecgb_unsafe(u8 x, u8 y, u8 tile, u8 attr);Parameter
x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
tileTile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.
attrCGB-Attributbyte: Bits 0–2 wählen Palette 0–7, Bit 3 die Tile-Datenbank, Bits 5 und 6 die horizontale und vertikale Spiegelung, Bit 7 die BG-/Fensterpriorität. Das unbenutzte Bit 4 bleibt null.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheint das A blau: Attributwert 2 wählt die im Beispiel vorbereitete blaue Palette. Auf DMG ist dasselbe Zeichen schwarz, da CGB-Attribute dort nicht angewendet werden.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
A

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Schreibt eine Tile-Nummer in eine Kartenposition. Das wählt ein vorhandenes Tile-Bild aus und ändert keine Pixeldaten.
LCDC-Bit 6 wählt die Fensterkarte: null bedeutet 0x9800, eins 0x9C00. Die Koordinaten beziehen sich auf die Karte, nicht auf die Fensterposition am Bildschirm.
Wartet bei Bedarf auf beschreibbares VRAM. Bei aktivem LCD werden Interrupts gesperrt und anschließend möglicherweise aktiviert. Weder der vorherige Interruptzustand noch ein Abschluss innerhalb eines VBlank wird garantiert.
Syntax
void __settilewin(u8 x, u8 y, u8 tile);Parameter
x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
tileTile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Tile-Bytes werden in die gewählte VRAM-Bank geschrieben. Wählen Sie auf CGB Bank 0 für Tile-Nummern; der Aufruf wechselt die Bank nicht.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheinen die Beispielzeichen und die Ergebnisanzeige blau auf weißem Hintergrund. Die Vorbereitung setzt die Zeichenfarbe der Palette 0 auf Blau, ohne die Attribute der Tilemap zu ändern. Auf DMG erscheinen dieselben Zeichen schwarz auf weißem Hintergrund.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
A

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/cgb_tile.h:39
Select the window map from LCDC bit 6, then write a tile with access checks.
__settilewin_unsafe
Schreibt eine Tile-Nummer in eine Kartenposition. Das wählt ein vorhandenes Tile-Bild aus und ändert keine Pixeldaten.
LCDC-Bit 6 wählt die Fensterkarte: null bedeutet 0x9800, eins 0x9C00. Die Koordinaten beziehen sich auf die Karte, nicht auf die Fensterposition am Bildschirm.
Wartet nicht auf VRAM-Zugriff. Nur bei ausgeschaltetem LCD oder in einem vom Aufrufer gesicherten Schreibintervall verwenden, das für den gesamten Transfer reicht.
Syntax
void __settilewin_unsafe(u8 x, u8 y, u8 tile);Parameter
x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
tileTile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Tile-Bytes werden in die gewählte VRAM-Bank geschrieben. Wählen Sie auf CGB Bank 0 für Tile-Nummern; der Aufruf wechselt die Bank nicht.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheinen die Beispielzeichen und die Ergebnisanzeige blau auf weißem Hintergrund. Die Vorbereitung setzt die Zeichenfarbe der Palette 0 auf Blau, ohne die Attribute der Tilemap zu ändern. Auf DMG erscheinen dieselben Zeichen schwarz auf weißem Hintergrund.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
A

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/cgb_tile.h:41
Select the LCDC window map and write without access waits.
__settilewinattr
Ändert nur das CGB-Attributbyte, nicht die Tile-Nummer. Die Bits wählen Palette, Bilddatenbank, Spiegelungen und Priorität.
LCDC-Bit 6 wählt die Fensterkarte: null bedeutet 0x9800, eins 0x9C00. Die Koordinaten beziehen sich auf die Karte, nicht auf die Fensterposition am Bildschirm.
Wartet bei Bedarf auf beschreibbares VRAM. Bei aktivem LCD werden Interrupts gesperrt und anschließend möglicherweise aktiviert. Weder der vorherige Interruptzustand noch ein Abschluss innerhalb eines VBlank wird garantiert.
Syntax
void __settilewinattr(u8 x, u8 y, u8 attr);Parameter
x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
attrCGB-Attributbyte: Bits 0–2 wählen Palette 0–7, Bit 3 die Tile-Datenbank, Bits 5 und 6 die horizontale und vertikale Spiegelung, Bit 7 die BG-/Fensterpriorität. Das unbenutzte Bit 4 bleibt null.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Auf CGB erfolgt der Attributzugriff in VRAM-Bank 1; danach gilt VBK=0. Auf DMG werden keine Attribute geschrieben.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheint das A blau: Attributwert 2 wählt die im Beispiel vorbereitete blaue Palette. Auf DMG ist dasselbe Zeichen schwarz, da CGB-Attribute dort nicht angewendet werden.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
A

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Ändert nur das CGB-Attributbyte, nicht die Tile-Nummer. Die Bits wählen Palette, Bilddatenbank, Spiegelungen und Priorität.
LCDC-Bit 6 wählt die Fensterkarte: null bedeutet 0x9800, eins 0x9C00. Die Koordinaten beziehen sich auf die Karte, nicht auf die Fensterposition am Bildschirm.
Wartet nicht auf VRAM-Zugriff. Nur bei ausgeschaltetem LCD oder in einem vom Aufrufer gesicherten Schreibintervall verwenden, das für den gesamten Transfer reicht.
Syntax
void __settilewinattr_unsafe(u8 x, u8 y, u8 attr);Parameter
x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
attrCGB-Attributbyte: Bits 0–2 wählen Palette 0–7, Bit 3 die Tile-Datenbank, Bits 5 und 6 die horizontale und vertikale Spiegelung, Bit 7 die BG-/Fensterpriorität. Das unbenutzte Bit 4 bleibt null.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Auf CGB erfolgt der Attributzugriff in VRAM-Bank 1; danach gilt VBK=0. Auf DMG werden keine Attribute geschrieben.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheint das A blau: Attributwert 2 wählt die im Beispiel vorbereitete blaue Palette. Auf DMG ist dasselbe Zeichen schwarz, da CGB-Attribute dort nicht angewendet werden.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
A

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Schreibt Tile-Nummer und CGB-Attribut. Auf DMG wird nur die Nummer geschrieben. Auf CGB muss vorher Bank 0 gewählt sein; Attribute verwenden Bank 1, danach gilt VBK=0.
LCDC-Bit 6 wählt die Fensterkarte: null bedeutet 0x9800, eins 0x9C00. Die Koordinaten beziehen sich auf die Karte, nicht auf die Fensterposition am Bildschirm.
Wartet bei Bedarf auf beschreibbares VRAM. Bei aktivem LCD werden Interrupts gesperrt und anschließend möglicherweise aktiviert. Weder der vorherige Interruptzustand noch ein Abschluss innerhalb eines VBlank wird garantiert.
Syntax
void __settilewincgb(u8 x, u8 y, u8 tile, u8 attr);Parameter
x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
tileTile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.
attrCGB-Attributbyte: Bits 0–2 wählen Palette 0–7, Bit 3 die Tile-Datenbank, Bits 5 und 6 die horizontale und vertikale Spiegelung, Bit 7 die BG-/Fensterpriorität. Das unbenutzte Bit 4 bleibt null.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheint das A blau: Attributwert 2 wählt die im Beispiel vorbereitete blaue Palette. Auf DMG ist dasselbe Zeichen schwarz, da CGB-Attribute dort nicht angewendet werden.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
A

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Schreibt Tile-Nummer und CGB-Attribut. Auf DMG wird nur die Nummer geschrieben. Auf CGB muss vorher Bank 0 gewählt sein; Attribute verwenden Bank 1, danach gilt VBK=0.
LCDC-Bit 6 wählt die Fensterkarte: null bedeutet 0x9800, eins 0x9C00. Die Koordinaten beziehen sich auf die Karte, nicht auf die Fensterposition am Bildschirm.
Wartet nicht auf VRAM-Zugriff. Nur bei ausgeschaltetem LCD oder in einem vom Aufrufer gesicherten Schreibintervall verwenden, das für den gesamten Transfer reicht.
Syntax
void __settilewincgb_unsafe(u8 x, u8 y, u8 tile, u8 attr);Parameter
x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
tileTile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.
attrCGB-Attributbyte: Bits 0–2 wählen Palette 0–7, Bit 3 die Tile-Datenbank, Bits 5 und 6 die horizontale und vertikale Spiegelung, Bit 7 die BG-/Fensterpriorität. Das unbenutzte Bit 4 bleibt null.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheint das A blau: Attributwert 2 wählt die im Beispiel vorbereitete blaue Palette. Auf DMG ist dasselbe Zeichen schwarz, da CGB-Attribute dort nicht angewendet werden.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
A

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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 — Tastenzustand, Drücken, Loslassen und Wiederholung
Verwende input_down für fortlaufende Bewegung, input_pressed für einmalige Aktionen, input_released für Aktionen beim Loslassen und input_repeat für Menüs mit Tastenwiederholung. Einmal initialisieren, einmal pro Eingabetakt aktualisieren, danach beliebig oft abfragen. btn_down, btn_pressed, btn_released und btn_repeat sind Aliase dieser vier Abfragen.
Gemeinsame Masken: BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80. Ein gesetztes Bit bedeutet gedrückt. input_* verwendet dieses Format auf GB und FC; rohe FC-Pad-Abfragen verwenden eine andere Bitreihenfolge.
Die Intervalle zählen Updates, keine Millisekunden. Ein neuer Tastendruck erzeugt sofort einen Impuls. Bei INPUT_REPEAT_DELAY=18 und INPUT_REPEAT_RATE=5 folgt der nächste nach 19 Updates, weitere jeweils nach 6. Jede Taste hat einen eigenen Zähler. Die Makros müssen auch beim Kompilieren von input.c entsprechend gesetzt sein; nur den Header des aufrufenden Codes zu ändern, konfiguriert die Bibliothek nicht um.
__readpadex
Liest alle Tasten und vergleicht sie mit der übergebenen vorherigen Haltemaske. Das niedrige Byte enthält den aktuellen Zustand, das hohe neue Tastendrücke gemäß current & ~previous. Speichert keinen vorherigen Zustand intern und liefert weder Loslass- noch Wiederholungsmasken.
Syntax
u16 __readpadex(u8 prev_keys);Parameter
prev_keysDie vollständige Maske des vorherigen
__readpad()oder das niedrige Byte des letzten__readpadex(). Beim ersten Lesen 0 übergeben, danach das neue niedrige Byte für den nächsten Aufruf speichern.
Rückgabewert
Ein u16: (u8)result ist die aktuelle Maske, (u8)(result >> 8) enthält neue Tastendrücke. In einer 16-Bit-Variablen speichern, sonst gehen die Druckereignisse verloren.
Bedingungen und Grenzen
Gemeinsame Masken: BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80. Ein gesetztes Bit bedeutet gedrückt. input_* verwendet dieses Format auf GB und FC; rohe FC-Pad-Abfragen verwenden eine andere Bitreihenfolge.
Anwendungsbeispiel
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.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Dieses Programm vergleicht die drei Maskenformate und den Rückgabewert, der aktuellen Zustand und neue Tastendrücke zusammenfasst. Halten Sie A und Rechts gleichzeitig gedrückt. ALL KEYS ist 17; DIRECTIONS und BUTTONS LOW sind jeweils 1. Bei einer vorherigen Maske von 0 sind EX CURRENT und EX NEW PRESS beide 17. Wird die aktuelle Maske an den nächsten Leseaufruf übergeben, ist HELD PRESS 0. FAILED CHECKS muss 0 sein. Die erfassten Werte bleiben nach dem Loslassen sichtbar.


Vollständiges Beispielprogramm
Build-Befehl
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-cacheImplementierung und Quelltext
kitaqgb/lib/input.h:28
intrinsics — Compiler-Intrinsics
Dieser Abschnitt fasst Intrinsics zusammen, die der KITAQGB-Codegenerator direkt verarbeitet. intrinsics ist eine Dokumentationskategorie, keine Aufforderung, einen gemeinsamen GB-Header namens intrinsics.h einzubinden. Verwenden Sie Header und Initialisierung des jeweiligen vollständigen Beispiels. Die Implementierung des Intrinsics müssen Sie im Spiel nicht definieren.
Manche Intrinsics nutzen Buildinformationen, andere greifen während des Spiels auf Hardware oder Speicher zu. Beispielsweise ermittelt __bankof(symbol) die zugewiesene Bank eines benannten Objekts oder einer Funktion, während __bankswitch(bank) die sichtbare ROM-Einblendung zur Laufzeit ändert. Unterscheiden Sie bei Argumenten zwischen Banknummern, CPU-Adressen und Zeigern.
Ein führendes __ garantiert weder Grenzprüfungen noch VRAM-Zugriffswartezeiten oder sichere Nutzung aus Interrupts. So muss der Aufrufer von __vram_memcpy_unsafe einen Zeitraum mit beschreibbarem VRAM sicherstellen. Prüfen Sie vor der Auswahl eines Vorgangs dessen Einheiten, gültige Bereiche, Bankeinblendung und Interruptanforderungen.
__bankof
Ermittelt die logische ROM-Banknummer eines namentlich angegebenen Datenobjekts oder einer Funktion. Verwendet deren Platzierung; es wird weder die aktive Bank abgefragt noch ein Zeiger dereferenziert.
Syntax
u8 bank = __bankof(symbol);Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Parameter
symbolName eines ROM-Datenobjekts oder einer Funktion mit bekannter Compiler- oder Linkerplatzierung. Eine Zeigervariable bezeichnet hier nicht ihr Zielobjekt; Laufzeitausdrücke sind keine zulässigen Symbolnamen.
Rückgabewert
Eine u8-Banknummer. Null kann gemeinsames ROM kennzeichnen, ist aber nicht auf jedem Ziel ein gültiger Umschaltwert.
Anwendungsbeispiel
bank_expect(__bankof(payload)==3,3);
bank_expect(__bankof(callback)==3,4);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel prüft, dass payload und callback beide in Bank 3 liegen. Damit werden Platzierungsmetadaten geprüft; getrennte Lese- und Callback-Prüfungen bestätigen das Bankverhalten zur Laufzeit.
Das vollständige Programm zeigt Datenzugriffe über Bank und Nahadresse, bankübergreifende Callbacks und den Unterschied zwischen Bibliothekszustand und tatsächlicher Zuordnung. Alle Zahlen sind hexadezimal. BANK IN und BANK OUT müssen 02 zeigen, BYTE HEX D3, WORD HEX 5C7A sowie FAILED CHECKS und FIRST FAILURE jeweils 00. CGB und FC zeigen blaue Schrift, DMG schwarze.
CALL COUNT zeigt 03: far_call, __farcall und __farcall_ptr rufen den Callback jeweils einmal auf.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Schreibt das untere Bankbyte an Moduladresse $2000 und aktualisiert den Bankzustand des Compilers. Die Auswahl bleibt nach dem Aufruf bestehen; der von bank_get_current verwendete Bibliothekszustand wird nicht aktualisiert.
Syntax
void __bankswitch(u8 bank);Parameter
bankVorzeichenlose 8-Bit-ROM-Banknummer. Bei Aufrufen muss sie zur Funktionsplatzierung passen, bei Lesezugriffen wählt sie die Quelle. Verwenden Sie den gültigen Bereich des Modultyps.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Ein dauerhafter Bankwechsel darf weder den laufenden Code noch seine Rückkehradresse ausblenden. Legen Sie den Aufruf, gegebenenfalls bank_switch selbst und den anschließend ausgeführten Code wie im Beispiel in die gemeinsame beziehungsweise feste Bank 0.
Der Bibliothekszustand beginnt bei null und erkennt die Startzuordnung des Moduls nicht. Initialisieren Sie ihn mit bank_switch. Direkte Intrinsics und Mapper-Schreibzugriffe aktualisieren diesen separaten Zustand nicht; bank_get_current kann die tatsächliche Hardwarezuordnung daher nicht prüfen.
Diese GB-Bankschnittstellen verwenden acht Bankbits und setzen das neunte MBC5-Bankbit nicht. Modulregeln, etwa die Behandlung von Bank null, gelten weiterhin. Das Beispiel nutzt ausdrücklich MBC5-Banken 1–3; mischen Sie diesen Zustand nicht mit unprotokollierten direkten Mapper-Schreibzugriffen.
Anwendungsbeispiel
__bankswitch(1);
bank_expect(home1[0]==0xA1,19);
bank_expect(bank_get_current()==2,20); // Intrinsics bypass the library record.
__bankswitch(2);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Der anfängliche Aufruf bank_switch(2) speichert und wählt Bank 2. Ein späterer direkter Wechsel wählt Bank 1 und liest A1 aus home1, während bank_get_current weiterhin 2 meldet. Nach dem Zurückschalten liefert home2 wieder B2.
Das vollständige Programm zeigt Datenzugriffe über Bank und Nahadresse, bankübergreifende Callbacks und den Unterschied zwischen Bibliothekszustand und tatsächlicher Zuordnung. Alle Zahlen sind hexadezimal. BANK IN und BANK OUT müssen 02 zeigen, BYTE HEX D3, WORD HEX 5C7A sowie FAILED CHECKS und FIRST FAILURE jeweils 00. CGB und FC zeigen blaue Schrift, DMG schwarze.
CALL COUNT zeigt 03: far_call, __farcall und __farcall_ptr rufen den Callback jeweils einmal auf.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Schreibt auf dem CGB ein Byte in das Palettendatenregister BCPD/BGPD ($FF69) für Hintergrund/Fenster. __cgb_safe_set_bcpd bezeichnet dasselbe Register. Das Indexregister bestimmt die Position; eine vollständige RGB15-Farbe benötigt ein niederwertiges und ein höherwertiges Byte.
Syntax
__cgb_safe_set_bgpd(/* 書式は実装と呼び出し例を参照 */);Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Parameter
valueEin Byte, 0–255. Schreiben Sie das niederwertige vor dem höherwertigen RGB15-Byte; aktivieren Sie zuvor die automatische Indexerhöhung oder wählen Sie jede Position einzeln. Ein Datenschreibzugriff aktiviert die Indexerhöhung nicht selbst.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Paletten- und geschützte Registerschreibzugriffe schreiben im DMG-Modus nichts. Safe bedeutet hier eine CGB-Modusprüfung, keine PPU-Zugriffswarteschleife und keinen kritischen Abschnitt. Verwenden Sie die Funktionen bei ausgeschaltetem LCD oder für kurze Aktualisierungen innerhalb einer zugänglichen PPU-Phase. Sie warten nicht auf VBlank und schützen mehrbyteige Updates nicht vor Interrupts.
Anwendungsbeispiel
__cgb_safe_set_bgpd(31); __cgb_safe_set_bgpd(0); // Alias of BCPD: red at slot 31.
expect_color(0,31,31);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Programm zeigt, wie BG- und OBJ-Paletten dasselbe Muster unabhängig einfärben. Jedes Dreieck ist 8 × 8 Pixel groß: oben ein Pixel links, unten eine volle Zeile. Bei x = 16, 32, 48, 64, 80, 96, 112 ist die BG-Reihe bei y = 24 rot, blau, grün, rot, blau, grün, rot; die OBJ-Reihe bei y = 64 ist blau, grün, rot, blau, grün, rot, blau. Die Spalten verwenden der Reihe nach Einzelfarbe, Komponenten, indizierten Bereich, ganze Palette, absoluten Bereich, kanonischen Registernamen und Registeralias.
Bei y = 88 zeigen sieben 8 × 8-Felder: rotes Dreieck (x = 16), blauen quadratischen Rahmen mit einem Pixel Strichstärke aus Bank 1 (32), horizontal gespiegeltes grünes Dreieck (48), vertikal gespiegeltes rotes Dreieck (64), in beiden Richtungen gespiegeltes blaues Dreieck (80), grünes Dreieck vor einem roten Sprite, das durch Farbindex null sichtbar bleibt (96), sowie ein volles rotes Sprite vor dem BG-Dreieck als Kontrolle ohne BG-Priorität (112). Koordinaten in Pixeln ab der linken oberen Ecke; weiße Freiflächen und schwarzer Text.
FAILED CHECKS 000 bestätigt RGB-Masken, Wahrheitsflags einschließlich Wert 2, normalisierte Palettenindizes, palettenübergreifende Zugriffe, Begrenzung bei Platz 31, Nullzeiger/Nullängen ohne Schreibzugriff und Farbrücklesen bei ausgeschaltetem LCD. Vier Kombinationen werden geprüft: duales ROM auf DMG/CGB, reines DMG-ROM auf DMG und reines CGB-ROM auf CGB. Auf DMG ist CGB MODE 0, Dreiecke sind schwarz, Bank-/Spiegelattribute ändern die Anzeige nicht und die letzten beiden unteren Felder sind schwarze Quadrate. Auf CGB ist CGB MODE 1. Echte Hardware und Palettenzugriffskonflikte bei eingeschaltetem LCD sind nicht geprüft.




Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/kitaqgb/CodeGenerator.cs:1865
__cgb_safe_set_bgpi
Schreibt auf dem CGB das Byteindexregister BCPS/BGPI ($FF68) des Palettenspeichers für Hintergrund/Fenster. __cgb_safe_set_bcps greift auf dasselbe Register zu. Der Aufruf wählt die Position des nächsten Datenregisterzugriffs aus und schreibt selbst noch keine Farbe.
Syntax
__cgb_safe_set_bgpi(/* 書式は実装と呼び出し例を参照 */);Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Parameter
valueByteindex in Bits 0–5 (0–63); Bit 7 aktiviert die automatische Erhöhung nach Datenschreibzugriffen. Bit 6 ist unbenutzt. Farbplatz n beginnt bei Byte 2n. Nach 63 folgt wieder null; direkte Registerzugriffe benötigen eine eigene Bereichskontrolle.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Paletten- und geschützte Registerschreibzugriffe schreiben im DMG-Modus nichts. Safe bedeutet hier eine CGB-Modusprüfung, keine PPU-Zugriffswarteschleife und keinen kritischen Abschnitt. Verwenden Sie die Funktionen bei ausgeschaltetem LCD oder für kurze Aktualisierungen innerhalb einer zugänglichen PPU-Phase. Sie warten nicht auf VBlank und schützen mehrbyteige Updates nicht vor Interrupts.
Anwendungsbeispiel
__cgb_safe_set_bgpi(0xBE); // Alias of BCPS: BG byte 62, auto-increment enabled.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Programm zeigt, wie BG- und OBJ-Paletten dasselbe Muster unabhängig einfärben. Jedes Dreieck ist 8 × 8 Pixel groß: oben ein Pixel links, unten eine volle Zeile. Bei x = 16, 32, 48, 64, 80, 96, 112 ist die BG-Reihe bei y = 24 rot, blau, grün, rot, blau, grün, rot; die OBJ-Reihe bei y = 64 ist blau, grün, rot, blau, grün, rot, blau. Die Spalten verwenden der Reihe nach Einzelfarbe, Komponenten, indizierten Bereich, ganze Palette, absoluten Bereich, kanonischen Registernamen und Registeralias.
Bei y = 88 zeigen sieben 8 × 8-Felder: rotes Dreieck (x = 16), blauen quadratischen Rahmen mit einem Pixel Strichstärke aus Bank 1 (32), horizontal gespiegeltes grünes Dreieck (48), vertikal gespiegeltes rotes Dreieck (64), in beiden Richtungen gespiegeltes blaues Dreieck (80), grünes Dreieck vor einem roten Sprite, das durch Farbindex null sichtbar bleibt (96), sowie ein volles rotes Sprite vor dem BG-Dreieck als Kontrolle ohne BG-Priorität (112). Koordinaten in Pixeln ab der linken oberen Ecke; weiße Freiflächen und schwarzer Text.
FAILED CHECKS 000 bestätigt RGB-Masken, Wahrheitsflags einschließlich Wert 2, normalisierte Palettenindizes, palettenübergreifende Zugriffe, Begrenzung bei Platz 31, Nullzeiger/Nullängen ohne Schreibzugriff und Farbrücklesen bei ausgeschaltetem LCD. Vier Kombinationen werden geprüft: duales ROM auf DMG/CGB, reines DMG-ROM auf DMG und reines CGB-ROM auf CGB. Auf DMG ist CGB MODE 0, Dreiecke sind schwarz, Bank-/Spiegelattribute ändern die Anzeige nicht und die letzten beiden unteren Felder sind schwarze Quadrate. Auf CGB ist CGB MODE 1. Echte Hardware und Palettenzugriffskonflikte bei eingeschaltetem LCD sind nicht geprüft.




Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/kitaqgb/CodeGenerator.cs:1863
__cgb_safe_set_hdma1
Schreibt HDMA1 ($FF51), das höherwertige Byte der CGB-DMA-Quelladresse. Konfiguriert nur die Quelle; kopiert keine Daten und startet keinen Transfer. HDMA2 bestimmt das niederwertige Byte.
Syntax
void __cgb_safe_set_hdma1(u8 value);Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Parameter
valueRegisterwert als
u8(0–255). Die registerbezogene Bitbelegung steht oben; übergeben wird ein Byte, kein Zeiger und keine Byteanzahl.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Der Compiler schützt Schreibzugriffe durch die CGB-Modusprüfung; im DMG-Modus wird nichts geschrieben. Safe bezeichnet nur diese Prüfung. Der vorherige Registerwert wird weder gespeichert noch wiederhergestellt.
Ein Block umfasst 16 Bytes. Neue Transfers unterstützen 1–128 Blöcke (16–2048 Bytes): Steuerwert 0 bedeutet 16 Bytes, keinen leeren Transfer. GDMA hält die CPU bis zum Ende an. HBlank-DMA kopiert einen Block je sichtbarem HBlank und kann mehrere Frames dauern; vor Wiederverwendung der Daten muss der Abschluss feststehen.
GDMA bei ausgeschaltetem LCD oder in einem ausreichend langen Schreibzugriffsfenster verwenden. HBlank-DMA bei laufendem LCD außerhalb von STAT-Modus 0 starten; das Beispiel beginnt in VBlank. Währenddessen Quellbanken, Ziel-VBK und Quelldaten nicht ändern. HALT pausiert auch HBlank-DMA. Diese Setter ergänzen weder Wartezeiten noch Interruptschutz oder Bereichsprüfungen.
$FF55 getrennt lesen: Bit 7 gleich null bedeutet aktiv, $FF bedeutet abgeschlossen. Nach Abbruch ist Bit 7 gesetzt, Bits 0–6 enthalten verbleibende Blöcke minus eins. Kopierte Bytes werden nicht zurückgesetzt. Nur aktive Transfers abbrechen; andernfalls startet Bit 7 gleich null GDMA. Der gesamte Transfer muss in gültiger Quellspeicher- und Ziel-VRAM-Bankgrenze bleiben; nicht auf Adressüberlauf verlassen.
Diese HDMA-Setter steuern CGB-Hardware-DMA. __vram_copy_dma ist ein eigener Compileralias für eine CPU-Kopierschleife; der Name bedeutet nicht, dass dieser DMA-Controller verwendet wird.
Anwendungsbeispiel
__cgb_safe_set_hdma1(0xC6); // Source high byte: fixed-WRAM buffer at $C600.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Programm vergleicht drei Transferwege mit einem 16-Byte-ausgerichteten Puffer bei $C600. GDMA kopiert 32 Bytes nach $8010: bei (16,24) ein rotes 8 × 8-Dreieck, das von einem linken Pixel bis zur vollen unteren Zeile wächst; bei (24,24) ein roter quadratischer Rahmen mit einem Pixel Strichstärke. HBlank-DMA kopiert 32 Bytes nach $8030: bei (16,48) eine blaue linke 4 × 8-Hälfte; bei (24,48) ein blaues 8 × 8-Quadrat. Gezeigt werden tatsächlich übertragene Muster, keine Zahlenplatzhalter.
CANCELED zeigt eine grüne 4 × 8-Hälfte bei (16,72) und ein grünes 8 × 8-Quadrat bei (24,72), zuvor per CPU geschrieben. Ein HBlank-Transfer über zwei Blöcke wird im selben VBlank vor dem ersten Block gestartet und abgebrochen. $FF55 muss $81 melden; beide Muster müssen nach einem Frame unverändert sein. Auf DMG bleiben die ersten zwei Reihen leer, nur diese Kontrollformen erscheinen schwarz. Schwarzer Text auf Weiß. FAILED CHECKS 000 prüft auch Bytes und Abschlussstatus. KOKURA-Tests mit dualem DMG/CGB-ROM und reinem CGB-ROM; Teilabbruch, doppelte Geschwindigkeit und echte Hardware ungetestet.



Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/kitaqgb/CodeGenerator.cs:1871
__cgb_safe_set_hdma2
Schreibt HDMA2 ($FF52), das niederwertige Quelladressbyte. Die Hardware ignoriert Bits 0–3 und rundet auf die vorherige 16-Byte-Grenze ab. HDMA1=$C6, HDMA2=$0F wählt $C600. Startet keinen DMA-Transfer.
Syntax
void __cgb_safe_set_hdma2(u8 value);Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Parameter
valueRegisterwert als
u8(0–255). Die registerbezogene Bitbelegung steht oben; übergeben wird ein Byte, kein Zeiger und keine Byteanzahl.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Der Compiler schützt Schreibzugriffe durch die CGB-Modusprüfung; im DMG-Modus wird nichts geschrieben. Safe bezeichnet nur diese Prüfung. Der vorherige Registerwert wird weder gespeichert noch wiederhergestellt.
Ein Block umfasst 16 Bytes. Neue Transfers unterstützen 1–128 Blöcke (16–2048 Bytes): Steuerwert 0 bedeutet 16 Bytes, keinen leeren Transfer. GDMA hält die CPU bis zum Ende an. HBlank-DMA kopiert einen Block je sichtbarem HBlank und kann mehrere Frames dauern; vor Wiederverwendung der Daten muss der Abschluss feststehen.
GDMA bei ausgeschaltetem LCD oder in einem ausreichend langen Schreibzugriffsfenster verwenden. HBlank-DMA bei laufendem LCD außerhalb von STAT-Modus 0 starten; das Beispiel beginnt in VBlank. Währenddessen Quellbanken, Ziel-VBK und Quelldaten nicht ändern. HALT pausiert auch HBlank-DMA. Diese Setter ergänzen weder Wartezeiten noch Interruptschutz oder Bereichsprüfungen.
$FF55 getrennt lesen: Bit 7 gleich null bedeutet aktiv, $FF bedeutet abgeschlossen. Nach Abbruch ist Bit 7 gesetzt, Bits 0–6 enthalten verbleibende Blöcke minus eins. Kopierte Bytes werden nicht zurückgesetzt. Nur aktive Transfers abbrechen; andernfalls startet Bit 7 gleich null GDMA. Der gesamte Transfer muss in gültiger Quellspeicher- und Ziel-VRAM-Bankgrenze bleiben; nicht auf Adressüberlauf verlassen.
Diese HDMA-Setter steuern CGB-Hardware-DMA. __vram_copy_dma ist ein eigener Compileralias für eine CPU-Kopierschleife; der Name bedeutet nicht, dass dieser DMA-Controller verwendet wird.
Anwendungsbeispiel
__cgb_safe_set_hdma2(0x0F); // Low four bits are ignored by hardware: source remains $C600.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Programm vergleicht drei Transferwege mit einem 16-Byte-ausgerichteten Puffer bei $C600. GDMA kopiert 32 Bytes nach $8010: bei (16,24) ein rotes 8 × 8-Dreieck, das von einem linken Pixel bis zur vollen unteren Zeile wächst; bei (24,24) ein roter quadratischer Rahmen mit einem Pixel Strichstärke. HBlank-DMA kopiert 32 Bytes nach $8030: bei (16,48) eine blaue linke 4 × 8-Hälfte; bei (24,48) ein blaues 8 × 8-Quadrat. Gezeigt werden tatsächlich übertragene Muster, keine Zahlenplatzhalter.
CANCELED zeigt eine grüne 4 × 8-Hälfte bei (16,72) und ein grünes 8 × 8-Quadrat bei (24,72), zuvor per CPU geschrieben. Ein HBlank-Transfer über zwei Blöcke wird im selben VBlank vor dem ersten Block gestartet und abgebrochen. $FF55 muss $81 melden; beide Muster müssen nach einem Frame unverändert sein. Auf DMG bleiben die ersten zwei Reihen leer, nur diese Kontrollformen erscheinen schwarz. Schwarzer Text auf Weiß. FAILED CHECKS 000 prüft auch Bytes und Abschlussstatus. KOKURA-Tests mit dualem DMG/CGB-ROM und reinem CGB-ROM; Teilabbruch, doppelte Geschwindigkeit und echte Hardware ungetestet.



Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/kitaqgb/CodeGenerator.cs:1872
__cgb_safe_set_hdma3
Schreibt HDMA3 ($FF53), den oberen Teil der DMA-Zieladresse in VRAM. Nur Bits 0–4 zählen; das Ziel liegt stets in $8000–$9FFF. Zusammen mit HDMA4 verwenden. Wählt keine VRAM-Bank und startet keinen DMA-Transfer.
Syntax
void __cgb_safe_set_hdma3(u8 value);Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Parameter
valueRegisterwert als
u8(0–255). Die registerbezogene Bitbelegung steht oben; übergeben wird ein Byte, kein Zeiger und keine Byteanzahl.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Der Compiler schützt Schreibzugriffe durch die CGB-Modusprüfung; im DMG-Modus wird nichts geschrieben. Safe bezeichnet nur diese Prüfung. Der vorherige Registerwert wird weder gespeichert noch wiederhergestellt.
Ein Block umfasst 16 Bytes. Neue Transfers unterstützen 1–128 Blöcke (16–2048 Bytes): Steuerwert 0 bedeutet 16 Bytes, keinen leeren Transfer. GDMA hält die CPU bis zum Ende an. HBlank-DMA kopiert einen Block je sichtbarem HBlank und kann mehrere Frames dauern; vor Wiederverwendung der Daten muss der Abschluss feststehen.
GDMA bei ausgeschaltetem LCD oder in einem ausreichend langen Schreibzugriffsfenster verwenden. HBlank-DMA bei laufendem LCD außerhalb von STAT-Modus 0 starten; das Beispiel beginnt in VBlank. Währenddessen Quellbanken, Ziel-VBK und Quelldaten nicht ändern. HALT pausiert auch HBlank-DMA. Diese Setter ergänzen weder Wartezeiten noch Interruptschutz oder Bereichsprüfungen.
$FF55 getrennt lesen: Bit 7 gleich null bedeutet aktiv, $FF bedeutet abgeschlossen. Nach Abbruch ist Bit 7 gesetzt, Bits 0–6 enthalten verbleibende Blöcke minus eins. Kopierte Bytes werden nicht zurückgesetzt. Nur aktive Transfers abbrechen; andernfalls startet Bit 7 gleich null GDMA. Der gesamte Transfer muss in gültiger Quellspeicher- und Ziel-VRAM-Bankgrenze bleiben; nicht auf Adressüberlauf verlassen.
Diese HDMA-Setter steuern CGB-Hardware-DMA. __vram_copy_dma ist ein eigener Compileralias für eine CPU-Kopierschleife; der Name bedeutet nicht, dass dieser DMA-Controller verwendet wird.
Anwendungsbeispiel
__cgb_safe_set_hdma3(0xE0); // Only low five bits select the high VRAM offset.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Programm vergleicht drei Transferwege mit einem 16-Byte-ausgerichteten Puffer bei $C600. GDMA kopiert 32 Bytes nach $8010: bei (16,24) ein rotes 8 × 8-Dreieck, das von einem linken Pixel bis zur vollen unteren Zeile wächst; bei (24,24) ein roter quadratischer Rahmen mit einem Pixel Strichstärke. HBlank-DMA kopiert 32 Bytes nach $8030: bei (16,48) eine blaue linke 4 × 8-Hälfte; bei (24,48) ein blaues 8 × 8-Quadrat. Gezeigt werden tatsächlich übertragene Muster, keine Zahlenplatzhalter.
CANCELED zeigt eine grüne 4 × 8-Hälfte bei (16,72) und ein grünes 8 × 8-Quadrat bei (24,72), zuvor per CPU geschrieben. Ein HBlank-Transfer über zwei Blöcke wird im selben VBlank vor dem ersten Block gestartet und abgebrochen. $FF55 muss $81 melden; beide Muster müssen nach einem Frame unverändert sein. Auf DMG bleiben die ersten zwei Reihen leer, nur diese Kontrollformen erscheinen schwarz. Schwarzer Text auf Weiß. FAILED CHECKS 000 prüft auch Bytes und Abschlussstatus. KOKURA-Tests mit dualem DMG/CGB-ROM und reinem CGB-ROM; Teilabbruch, doppelte Geschwindigkeit und echte Hardware ungetestet.



Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/kitaqgb/CodeGenerator.cs:1873
__cgb_safe_set_hdma4
Schreibt HDMA4 ($FF54), das niederwertige VRAM-Zieladressbyte. Bits 0–3 werden ignoriert; das Ziel wird auf 16 Bytes abgerundet. HDMA3=$E0, HDMA4=$1F wählt $8010. Legt eine Adresse fest, keine Transferlänge.
Syntax
void __cgb_safe_set_hdma4(u8 value);Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Parameter
valueRegisterwert als
u8(0–255). Die registerbezogene Bitbelegung steht oben; übergeben wird ein Byte, kein Zeiger und keine Byteanzahl.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Der Compiler schützt Schreibzugriffe durch die CGB-Modusprüfung; im DMG-Modus wird nichts geschrieben. Safe bezeichnet nur diese Prüfung. Der vorherige Registerwert wird weder gespeichert noch wiederhergestellt.
Ein Block umfasst 16 Bytes. Neue Transfers unterstützen 1–128 Blöcke (16–2048 Bytes): Steuerwert 0 bedeutet 16 Bytes, keinen leeren Transfer. GDMA hält die CPU bis zum Ende an. HBlank-DMA kopiert einen Block je sichtbarem HBlank und kann mehrere Frames dauern; vor Wiederverwendung der Daten muss der Abschluss feststehen.
GDMA bei ausgeschaltetem LCD oder in einem ausreichend langen Schreibzugriffsfenster verwenden. HBlank-DMA bei laufendem LCD außerhalb von STAT-Modus 0 starten; das Beispiel beginnt in VBlank. Währenddessen Quellbanken, Ziel-VBK und Quelldaten nicht ändern. HALT pausiert auch HBlank-DMA. Diese Setter ergänzen weder Wartezeiten noch Interruptschutz oder Bereichsprüfungen.
$FF55 getrennt lesen: Bit 7 gleich null bedeutet aktiv, $FF bedeutet abgeschlossen. Nach Abbruch ist Bit 7 gesetzt, Bits 0–6 enthalten verbleibende Blöcke minus eins. Kopierte Bytes werden nicht zurückgesetzt. Nur aktive Transfers abbrechen; andernfalls startet Bit 7 gleich null GDMA. Der gesamte Transfer muss in gültiger Quellspeicher- und Ziel-VRAM-Bankgrenze bleiben; nicht auf Adressüberlauf verlassen.
Diese HDMA-Setter steuern CGB-Hardware-DMA. __vram_copy_dma ist ein eigener Compileralias für eine CPU-Kopierschleife; der Name bedeutet nicht, dass dieser DMA-Controller verwendet wird.
Anwendungsbeispiel
__cgb_safe_set_hdma4(0x1F); // Destination aligns to $8010, not $801F.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Programm vergleicht drei Transferwege mit einem 16-Byte-ausgerichteten Puffer bei $C600. GDMA kopiert 32 Bytes nach $8010: bei (16,24) ein rotes 8 × 8-Dreieck, das von einem linken Pixel bis zur vollen unteren Zeile wächst; bei (24,24) ein roter quadratischer Rahmen mit einem Pixel Strichstärke. HBlank-DMA kopiert 32 Bytes nach $8030: bei (16,48) eine blaue linke 4 × 8-Hälfte; bei (24,48) ein blaues 8 × 8-Quadrat. Gezeigt werden tatsächlich übertragene Muster, keine Zahlenplatzhalter.
CANCELED zeigt eine grüne 4 × 8-Hälfte bei (16,72) und ein grünes 8 × 8-Quadrat bei (24,72), zuvor per CPU geschrieben. Ein HBlank-Transfer über zwei Blöcke wird im selben VBlank vor dem ersten Block gestartet und abgebrochen. $FF55 muss $81 melden; beide Muster müssen nach einem Frame unverändert sein. Auf DMG bleiben die ersten zwei Reihen leer, nur diese Kontrollformen erscheinen schwarz. Schwarzer Text auf Weiß. FAILED CHECKS 000 prüft auch Bytes und Abschlussstatus. KOKURA-Tests mit dualem DMG/CGB-ROM und reinem CGB-ROM; Teilabbruch, doppelte Geschwindigkeit und echte Hardware ungetestet.



Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/kitaqgb/CodeGenerator.cs:1874
__cgb_safe_set_hdma5
Schreibt HDMA5 ($FF55), um DMA zu starten oder einen aktiven HBlank-Transfer abzubrechen. Bei neuen Transfers kodieren Bits 0–6 die Blockanzahl minus eins; Bit 7 wählt GDMA (0) oder HBlank-DMA (1). Während eines aktiven HBlank-Transfers bricht ein Schreibzugriff mit Bit 7 gleich null den Rest ab, ohne einen neuen GDMA-Transfer zu starten.
Syntax
void __cgb_safe_set_hdma5(u8 value);Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Parameter
valueRegisterwert als
u8(0–255). Die registerbezogene Bitbelegung steht oben; übergeben wird ein Byte, kein Zeiger und keine Byteanzahl.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Der Compiler schützt Schreibzugriffe durch die CGB-Modusprüfung; im DMG-Modus wird nichts geschrieben. Safe bezeichnet nur diese Prüfung. Der vorherige Registerwert wird weder gespeichert noch wiederhergestellt.
Ein Block umfasst 16 Bytes. Neue Transfers unterstützen 1–128 Blöcke (16–2048 Bytes): Steuerwert 0 bedeutet 16 Bytes, keinen leeren Transfer. GDMA hält die CPU bis zum Ende an. HBlank-DMA kopiert einen Block je sichtbarem HBlank und kann mehrere Frames dauern; vor Wiederverwendung der Daten muss der Abschluss feststehen.
GDMA bei ausgeschaltetem LCD oder in einem ausreichend langen Schreibzugriffsfenster verwenden. HBlank-DMA bei laufendem LCD außerhalb von STAT-Modus 0 starten; das Beispiel beginnt in VBlank. Währenddessen Quellbanken, Ziel-VBK und Quelldaten nicht ändern. HALT pausiert auch HBlank-DMA. Diese Setter ergänzen weder Wartezeiten noch Interruptschutz oder Bereichsprüfungen.
$FF55 getrennt lesen: Bit 7 gleich null bedeutet aktiv, $FF bedeutet abgeschlossen. Nach Abbruch ist Bit 7 gesetzt, Bits 0–6 enthalten verbleibende Blöcke minus eins. Kopierte Bytes werden nicht zurückgesetzt. Nur aktive Transfers abbrechen; andernfalls startet Bit 7 gleich null GDMA. Der gesamte Transfer muss in gültiger Quellspeicher- und Ziel-VRAM-Bankgrenze bleiben; nicht auf Adressüberlauf verlassen.
Diese HDMA-Setter steuern CGB-Hardware-DMA. __vram_copy_dma ist ein eigener Compileralias für eine CPU-Kopierschleife; der Name bedeutet nicht, dass dieser DMA-Controller verwendet wird.
Anwendungsbeispiel
__cgb_safe_set_hdma5(1); // LCD is off: GDMA, (1+1)*16 = 32 bytes.
if (color_mode && D_CONTROL!=255) failures++;Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Programm vergleicht drei Transferwege mit einem 16-Byte-ausgerichteten Puffer bei $C600. GDMA kopiert 32 Bytes nach $8010: bei (16,24) ein rotes 8 × 8-Dreieck, das von einem linken Pixel bis zur vollen unteren Zeile wächst; bei (24,24) ein roter quadratischer Rahmen mit einem Pixel Strichstärke. HBlank-DMA kopiert 32 Bytes nach $8030: bei (16,48) eine blaue linke 4 × 8-Hälfte; bei (24,48) ein blaues 8 × 8-Quadrat. Gezeigt werden tatsächlich übertragene Muster, keine Zahlenplatzhalter.
CANCELED zeigt eine grüne 4 × 8-Hälfte bei (16,72) und ein grünes 8 × 8-Quadrat bei (24,72), zuvor per CPU geschrieben. Ein HBlank-Transfer über zwei Blöcke wird im selben VBlank vor dem ersten Block gestartet und abgebrochen. $FF55 muss $81 melden; beide Muster müssen nach einem Frame unverändert sein. Auf DMG bleiben die ersten zwei Reihen leer, nur diese Kontrollformen erscheinen schwarz. Schwarzer Text auf Weiß. FAILED CHECKS 000 prüft auch Bytes und Abschlussstatus. KOKURA-Tests mit dualem DMG/CGB-ROM und reinem CGB-ROM; Teilabbruch, doppelte Geschwindigkeit und echte Hardware ungetestet.



Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/kitaqgb/CodeGenerator.cs:1875
__cgb_safe_set_obpd
Schreibt auf dem CGB ein Byte in das Palettendatenregister OCPD/OBPD ($FF6B) für Sprites (OBJ). __cgb_safe_set_ocpd bezeichnet dasselbe Register. Das Indexregister bestimmt die Position; eine vollständige RGB15-Farbe benötigt ein niederwertiges und ein höherwertiges Byte.
Syntax
__cgb_safe_set_obpd(/* 書式は実装と呼び出し例を参照 */);Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Parameter
valueEin Byte, 0–255. Schreiben Sie das niederwertige vor dem höherwertigen RGB15-Byte; aktivieren Sie zuvor die automatische Indexerhöhung oder wählen Sie jede Position einzeln. Ein Datenschreibzugriff aktiviert die Indexerhöhung nicht selbst.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Paletten- und geschützte Registerschreibzugriffe schreiben im DMG-Modus nichts. Safe bedeutet hier eine CGB-Modusprüfung, keine PPU-Zugriffswarteschleife und keinen kritischen Abschnitt. Verwenden Sie die Funktionen bei ausgeschaltetem LCD oder für kurze Aktualisierungen innerhalb einer zugänglichen PPU-Phase. Sie warten nicht auf VBlank und schützen mehrbyteige Updates nicht vor Interrupts.
Anwendungsbeispiel
__cgb_safe_set_obpd(0); __cgb_safe_set_obpd(0x7C); // Alias of OCPD: blue at slot 31.
expect_color(1,31,31744);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Programm zeigt, wie BG- und OBJ-Paletten dasselbe Muster unabhängig einfärben. Jedes Dreieck ist 8 × 8 Pixel groß: oben ein Pixel links, unten eine volle Zeile. Bei x = 16, 32, 48, 64, 80, 96, 112 ist die BG-Reihe bei y = 24 rot, blau, grün, rot, blau, grün, rot; die OBJ-Reihe bei y = 64 ist blau, grün, rot, blau, grün, rot, blau. Die Spalten verwenden der Reihe nach Einzelfarbe, Komponenten, indizierten Bereich, ganze Palette, absoluten Bereich, kanonischen Registernamen und Registeralias.
Bei y = 88 zeigen sieben 8 × 8-Felder: rotes Dreieck (x = 16), blauen quadratischen Rahmen mit einem Pixel Strichstärke aus Bank 1 (32), horizontal gespiegeltes grünes Dreieck (48), vertikal gespiegeltes rotes Dreieck (64), in beiden Richtungen gespiegeltes blaues Dreieck (80), grünes Dreieck vor einem roten Sprite, das durch Farbindex null sichtbar bleibt (96), sowie ein volles rotes Sprite vor dem BG-Dreieck als Kontrolle ohne BG-Priorität (112). Koordinaten in Pixeln ab der linken oberen Ecke; weiße Freiflächen und schwarzer Text.
FAILED CHECKS 000 bestätigt RGB-Masken, Wahrheitsflags einschließlich Wert 2, normalisierte Palettenindizes, palettenübergreifende Zugriffe, Begrenzung bei Platz 31, Nullzeiger/Nullängen ohne Schreibzugriff und Farbrücklesen bei ausgeschaltetem LCD. Vier Kombinationen werden geprüft: duales ROM auf DMG/CGB, reines DMG-ROM auf DMG und reines CGB-ROM auf CGB. Auf DMG ist CGB MODE 0, Dreiecke sind schwarz, Bank-/Spiegelattribute ändern die Anzeige nicht und die letzten beiden unteren Felder sind schwarze Quadrate. Auf CGB ist CGB MODE 1. Echte Hardware und Palettenzugriffskonflikte bei eingeschaltetem LCD sind nicht geprüft.




Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/kitaqgb/CodeGenerator.cs:1869
__cgb_safe_set_obpi
Schreibt auf dem CGB das Byteindexregister OCPS/OBPI ($FF6A) des Palettenspeichers für Sprites (OBJ). __cgb_safe_set_ocps greift auf dasselbe Register zu. Der Aufruf wählt die Position des nächsten Datenregisterzugriffs aus und schreibt selbst noch keine Farbe.
Syntax
__cgb_safe_set_obpi(/* 書式は実装と呼び出し例を参照 */);Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Parameter
valueByteindex in Bits 0–5 (0–63); Bit 7 aktiviert die automatische Erhöhung nach Datenschreibzugriffen. Bit 6 ist unbenutzt. Farbplatz n beginnt bei Byte 2n. Nach 63 folgt wieder null; direkte Registerzugriffe benötigen eine eigene Bereichskontrolle.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Paletten- und geschützte Registerschreibzugriffe schreiben im DMG-Modus nichts. Safe bedeutet hier eine CGB-Modusprüfung, keine PPU-Zugriffswarteschleife und keinen kritischen Abschnitt. Verwenden Sie die Funktionen bei ausgeschaltetem LCD oder für kurze Aktualisierungen innerhalb einer zugänglichen PPU-Phase. Sie warten nicht auf VBlank und schützen mehrbyteige Updates nicht vor Interrupts.
Anwendungsbeispiel
__cgb_safe_set_obpi(0xBE); // Alias of OCPS: select the OBJ store's last word.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Programm zeigt, wie BG- und OBJ-Paletten dasselbe Muster unabhängig einfärben. Jedes Dreieck ist 8 × 8 Pixel groß: oben ein Pixel links, unten eine volle Zeile. Bei x = 16, 32, 48, 64, 80, 96, 112 ist die BG-Reihe bei y = 24 rot, blau, grün, rot, blau, grün, rot; die OBJ-Reihe bei y = 64 ist blau, grün, rot, blau, grün, rot, blau. Die Spalten verwenden der Reihe nach Einzelfarbe, Komponenten, indizierten Bereich, ganze Palette, absoluten Bereich, kanonischen Registernamen und Registeralias.
Bei y = 88 zeigen sieben 8 × 8-Felder: rotes Dreieck (x = 16), blauen quadratischen Rahmen mit einem Pixel Strichstärke aus Bank 1 (32), horizontal gespiegeltes grünes Dreieck (48), vertikal gespiegeltes rotes Dreieck (64), in beiden Richtungen gespiegeltes blaues Dreieck (80), grünes Dreieck vor einem roten Sprite, das durch Farbindex null sichtbar bleibt (96), sowie ein volles rotes Sprite vor dem BG-Dreieck als Kontrolle ohne BG-Priorität (112). Koordinaten in Pixeln ab der linken oberen Ecke; weiße Freiflächen und schwarzer Text.
FAILED CHECKS 000 bestätigt RGB-Masken, Wahrheitsflags einschließlich Wert 2, normalisierte Palettenindizes, palettenübergreifende Zugriffe, Begrenzung bei Platz 31, Nullzeiger/Nullängen ohne Schreibzugriff und Farbrücklesen bei ausgeschaltetem LCD. Vier Kombinationen werden geprüft: duales ROM auf DMG/CGB, reines DMG-ROM auf DMG und reines CGB-ROM auf CGB. Auf DMG ist CGB MODE 0, Dreiecke sind schwarz, Bank-/Spiegelattribute ändern die Anzeige nicht und die letzten beiden unteren Felder sind schwarze Quadrate. Auf CGB ist CGB MODE 1. Echte Hardware und Palettenzugriffskonflikte bei eingeschaltetem LCD sind nicht geprüft.




Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/kitaqgb/CodeGenerator.cs:1867
__cgb_safe_set_svbk
Schreibt den rohen WRAM-Selektor SVBK ($FF70) auf dem CGB. Hardwareseitig zählen Bits 0–2; 0 und 1 bilden beide Bank 1 in $D000–$DFFF ab. Auch 8 wählt Bank 1. Anders als __svbk_set erlaubt dieser geschützte Setter duale Ziele und liefert die vorherige Bank nicht zurück.
Syntax
void __cgb_safe_set_svbk(u8 value);Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Parameter
valueRegisterwert als
u8(0–255). Die registerbezogene Bitbelegung steht oben; übergeben wird ein Byte, kein Zeiger und keine Byteanzahl.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Der Compiler schützt Schreibzugriffe durch die CGB-Modusprüfung; im DMG-Modus wird nichts geschrieben. Safe bezeichnet nur diese Prüfung. Der vorherige Registerwert wird weder gespeichert noch wiederhergestellt.
Ein Bankwechsel betrifft alle Zugriffe in $D000–$DFFF; festes WRAM in $C000–$CFFF bleibt unverändert. Stack und währenddessen benötigter Zustand müssen außerhalb des Bankfensters liegen. Das Beispiel verwendet --stack-bank=fixed und ausdrücklich im festen WRAM platzierte Variablen. Interrupts und bankabhängige DMA-Zugriffe selbst koordinieren; diese APIs erzeugen keinen kritischen Abschnitt.
Anwendungsbeispiel
__cgb_safe_set_svbk(0); // Raw selector 0 maps effective bank 1; this API returns void.
if ((W_SELECTOR&7)!=0) failures++;Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das reine CGB-Programm speichert 1 bei $D100 in WRAM-Bank 2 und 2 an derselben Adresse in Bank 3, liest beide Werte und stellt Bank 1 wieder her. Sie wählen ein rotes 8 × 8-Quadrat bei (16,24) und einen blauen 8 × 8-Rahmen mit einem Pixel Strichstärke bei (16,48). FAILED CHECKS 000 prüft Normalisierung, Banktrennung und Rückgabe der vorherigen Bank bei Konstanten, Variablen, Ausdrücken und Funktionsrückgaben. Sechs ungültige Builds prüfen KQ2103 für DMG/duale Ziele und KQ2102 für konstante Banken 0 und 8. Ausführung in KOKURA; echte Hardware und nebenläufige Bankwechsel per Interrupt ungetestet.

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/kitaqgb/CodeGenerator.cs:1862
__critical_enter
Erhöht die Software-Verschachtelungstiefe und führt DI aus.
Syntax
u8 __critical_enter();Rückgabewert
Gibt die vorherige Tiefe als u8-Token zurück: null für den äußeren Bereich, eins für dessen erste Verschachtelung.
Bedingungen und Grenzen
Übergeben Sie die Tokens in umgekehrter Reihenfolge an leave und vermeiden Sie einen Tiefenüberlauf bei 256. Der vorherige Hardware-IME-Zustand wird nicht gespeichert; verwenden Sie dieses Paar nicht, wenn ein bestehender DI-Zustand erhalten bleiben muss.
Anwendungsbeispiel
*((u8*)0xFFFF)=0;outer=__critical_enter();inner=__critical_enter();result[7]=outer;result[8]=inner;Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Lernen Sie Kartenauswahl, Scrolling, Verschachtelung und Rasterzeilenwarten. Die Basisadressen sind 38912/39936, die verschachtelten Tokens 0/1 und die beobachteten Ziel-LY-Werte 80/144. Prüfen Sie auch die Rückkehr aus Warteaufrufen bei ausgeschaltetem LCD.


Vollständiges Beispielprogramm
Build-Befehl
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=noneImplementierung und Quelltext
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
Verringert die Verschachtelungstiefe und führt für das äußere Token null EI aus.
Syntax
void __critical_leave(u8 token);Parameter
tokenToken des zugehörigen __critical_enter-Aufrufs.
Rückgabewert
Kein Rückgabewert.
Bedingungen und Grenzen
Tokens ungleich null führen EI nicht aus. Ein willkürlich übergebenes null kann trotz anderer aktiver Bereiche Interrupts aktivieren; paaren Sie die Aufrufe korrekt.
Anwendungsbeispiel
__critical_leave(inner);__critical_leave(outer);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Lernen Sie Kartenauswahl, Scrolling, Verschachtelung und Rasterzeilenwarten. Die Basisadressen sind 38912/39936, die verschachtelten Tokens 0/1 und die beobachteten Ziel-LY-Werte 80/144. Prüfen Sie auch die Rückkehr aus Warteaufrufen bei ausgeschaltetem LCD.


Vollständiges Beispielprogramm
Build-Befehl
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=noneImplementierung und Quelltext
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
Berechnet ein vorzeichenloses Skalarprodukt mit zwei Termen.
Syntax
u16 __dot2_q8_8(u16 a0, u16 a1, u8 b0, u8 b1);Parameter
a0Vorzeichenloses Wort, 0..65535.
a1Vorzeichenloses Wort, 0..65535.
b0Vorzeichenloser Koeffizient, 0..255.
b1Vorzeichenloser Koeffizient, 0..255.
Rückgabewert
Jeder Term ist floor(a*b/256). Die Terme werden addiert und die unteren 16 Bits zurückgegeben.
Bedingungen und Grenzen
Auf dem GB werden Produkte durch 256 geteilt; vorzeichenbehaftete Ergebnisse werden gegen null abgeschnitten. Q1.7-Varianten verdoppeln diesen abgeschnittenen Wert und können um eins von der exakten Division durch 128 abweichen. Die Terme werden einzeln gerundet; die abschließende Addition läuft bei 16 Bit um.
Anwendungsbeispiel
// 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);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Vergleichen Sie Literalwerte mit im RAM gespeicherten Werten. Repräsentative Ergebnisse erscheinen als vorzeichenlose 16-Bit-Werte; negative Werte nutzen Zweierkomplement-Bitmuster. Der GB skaliert Festkommaprodukte, der FC liefert Ganzzahlprodukte. Gleiche Eingaben müssen daher nicht gleiche Ergebnisse liefern.
Der Ausschnitt speichert folgende 16-Bit-Werte: result[36]=509, result[37]=509. Jede Berechnung mit Literalwerten stimmt mit der entsprechenden Berechnung aus RAM-Werten überein. Der Bildschirm zeigt repräsentative Werte; das Array result enthält alle Ergebnisse.


Vollständiges Beispielprogramm
Build-Befehl
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=noneImplementierung und Quelltext
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
Berechnet ein vorzeichenloses Skalarprodukt mit drei Termen.
Syntax
u16 __dot3_q8_8(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);Parameter
a0Vorzeichenloses Wort, 0..65535.
a1Vorzeichenloses Wort, 0..65535.
a2Vorzeichenloses Wort, 0..65535.
b0Vorzeichenloser Koeffizient, 0..255.
b1Vorzeichenloser Koeffizient, 0..255.
b2Vorzeichenloser Koeffizient, 0..255.
Rückgabewert
Jeder Term ist floor(a*b/256). Die Terme werden addiert und die unteren 16 Bits zurückgegeben.
Bedingungen und Grenzen
Auf dem GB werden Produkte durch 256 geteilt; vorzeichenbehaftete Ergebnisse werden gegen null abgeschnitten. Q1.7-Varianten verdoppeln diesen abgeschnittenen Wert und können um eins von der exakten Division durch 128 abweichen. Die Terme werden einzeln gerundet; die abschließende Addition läuft bei 16 Bit um.
Anwendungsbeispiel
// 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);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Vergleichen Sie Literalwerte mit im RAM gespeicherten Werten. Repräsentative Ergebnisse erscheinen als vorzeichenlose 16-Bit-Werte; negative Werte nutzen Zweierkomplement-Bitmuster. Der GB skaliert Festkommaprodukte, der FC liefert Ganzzahlprodukte. Gleiche Eingaben müssen daher nicht gleiche Ergebnisse liefern.
Der Ausschnitt speichert folgende 16-Bit-Werte: result[6]=521, result[7]=521. Jede Berechnung mit Literalwerten stimmt mit der entsprechenden Berechnung aus RAM-Werten überein. Der Bildschirm zeigt repräsentative Werte; das Array result enthält alle Ergebnisse.


Vollständiges Beispielprogramm
Build-Befehl
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=noneImplementierung und Quelltext
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
Ruft eine benannte Funktion ohne Argumente mit der angegebenen Banknummer auf. Ist eine Umschaltung nötig, stellt eine Hilfsroutine im festen ROM nach der Rückkehr die Bank des Aufrufers wieder her.
Syntax
__farcall(bank, function_name);Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Parameter
bankVorzeichenlose 8-Bit-ROM-Banknummer. Bei Aufrufen muss sie zur Funktionsplatzierung passen, bei Lesezugriffen wählt sie die Quelle. Verwenden Sie den gültigen Bereich des Modultyps.
funcDeklarierter Name einer Funktion ohne Parameter. Dieses Intrinsic für benannte Aufrufe akzeptiert keine Variable mit einer Funktionsadresse. Das Argument bank wird nicht als Callback-Parameter weitergereicht.
Rückgabewert
Der Ergebnistyp richtet sich nach der aufgerufenen Funktion. Ein void-Callback liefert keinen Wert; u8- und u16-Callbacks liefern ihren Skalarwert. Das gezeigte Beispiel verwendet void-Callbacks.
Bedingungen und Grenzen
Diese Aufrufe reichen keine Benutzerargumente weiter und machen den Callback nicht interrupt-reentrant. Verwalten Sie seine Daten, seinen Stack und seine Interruptnutzung korrekt. Die Bankwiederherstellung ist mit verschachtelten und wiederholten Aufrufen geprüft; reale Hardware und beliebige Interruptverschachtelungen deckt dieses Beispiel nicht ab.
Diese GB-Bankschnittstellen verwenden acht Bankbits und setzen das neunte MBC5-Bankbit nicht. Modulregeln, etwa die Behandlung von Bank null, gelten weiterhin. Das Beispiel nutzt ausdrücklich MBC5-Banken 1–3; mischen Sie diesen Zustand nicht mit unprotokollierten direkten Mapper-Schreibzugriffen.
Anwendungsbeispiel
__farcall(3,callback);
bank_expect(callback_count==2 && callback_byte==0xD3,16);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
An dieser Stelle des vollständigen Programms erhöht __farcall den Wert von callback_count auf 2. Der Callback liest D3 aus Bank 3. Ein späterer Nahzugriff auf home2 mit Ergebnis B2 bestätigt die Rückkehr bei eingeblendeter Bank 2.
Das vollständige Programm zeigt Datenzugriffe über Bank und Nahadresse, bankübergreifende Callbacks und den Unterschied zwischen Bibliothekszustand und tatsächlicher Zuordnung. Alle Zahlen sind hexadezimal. BANK IN und BANK OUT müssen 02 zeigen, BYTE HEX D3, WORD HEX 5C7A sowie FAILED CHECKS und FIRST FAILURE jeweils 00. CGB und FC zeigen blaue Schrift, DMG schwarze.
CALL COUNT zeigt 03: far_call, __farcall und __farcall_ptr rufen den Callback jeweils einmal auf.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Liest LCDC-Bit 6 zur Auswahl der Basisadresse der Fenster-Kachelkarte.
Syntax
u16 __getwinmapbase();Rückgabewert
Gibt $9800 oder $9C00 zurück: die Kachelkartenbasis, nicht die Adresse der Kachelgrafik.
Anwendungsbeispiel
M_LCDC=0;result[2]=__getwinmapbase();M_LCDC=0x40;result[3]=__getwinmapbase();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Lernen Sie Kartenauswahl, Scrolling, Verschachtelung und Rasterzeilenwarten. Die Basisadressen sind 38912/39936, die verschachtelten Tokens 0/1 und die beobachteten Ziel-LY-Werte 80/144. Prüfen Sie auch die Rückkehr aus Warteaufrufen bei ausgeschaltetem LCD.


Vollständiges Beispielprogramm
Build-Befehl
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=noneImplementierung und Quelltext
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
Berechnet eine vorzeichenlose Multiplikation mit Akkumulation.
Syntax
u16 __mac16(u16 acc, u16 a, u8 b);Parameter
accZu addierender 16-Bit-Akkumulator.
aVorzeichenloses Wort, 0..65535.
bVorzeichenloser Koeffizient, 0..255.
Rückgabewert
Jeder Term ist floor(a*b/256) und wird zu acc addiert; zurückgegeben werden die unteren 16 Bits.
Bedingungen und Grenzen
Auf dem GB werden Produkte durch 256 geteilt; vorzeichenbehaftete Ergebnisse werden gegen null abgeschnitten. Q1.7-Varianten verdoppeln diesen abgeschnittenen Wert und können um eins von der exakten Division durch 128 abweichen. Die Terme werden einzeln gerundet; die abschließende Addition läuft bei 16 Bit um.
Anwendungsbeispiel
// 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);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Vergleichen Sie Literalwerte mit im RAM gespeicherten Werten. Repräsentative Ergebnisse erscheinen als vorzeichenlose 16-Bit-Werte; negative Werte nutzen Zweierkomplement-Bitmuster. Der GB skaliert Festkommaprodukte, der FC liefert Ganzzahlprodukte. Gleiche Eingaben müssen daher nicht gleiche Ergebnisse liefern.
Der Ausschnitt speichert folgende 16-Bit-Werte: result[34]=218, result[35]=218. Jede Berechnung mit Literalwerten stimmt mit der entsprechenden Berechnung aus RAM-Werten überein. Der Bildschirm zeigt repräsentative Werte; das Array result enthält alle Ergebnisse.


Vollständiges Beispielprogramm
Build-Befehl
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=noneImplementierung und Quelltext
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
Berechnet eine vorzeichenlose Multiplikation.
Syntax
u16 __mul16x8(u16 a, u8 b);Parameter
aVorzeichenloses Wort, 0..65535.
bVorzeichenloser Koeffizient, 0..255.
Rückgabewert
Jeder Term ist floor(a*b/256); zurückgegeben werden die unteren 16 Bits.
Bedingungen und Grenzen
Auf dem GB werden Produkte durch 256 geteilt; vorzeichenbehaftete Ergebnisse werden gegen null abgeschnitten. Q1.7-Varianten verdoppeln diesen abgeschnittenen Wert und können um eins von der exakten Division durch 128 abweichen. Die Terme werden einzeln gerundet; die abschließende Addition läuft bei 16 Bit um.
Anwendungsbeispiel
// Compare literal coefficients with values read from RAM.
result[24]=__mul16x8(513,127);
v0=513;v1=127;
result[25]=__mul16x8(v0,v1);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Vergleichen Sie Literalwerte mit im RAM gespeicherten Werten. Repräsentative Ergebnisse erscheinen als vorzeichenlose 16-Bit-Werte; negative Werte nutzen Zweierkomplement-Bitmuster. Der GB skaliert Festkommaprodukte, der FC liefert Ganzzahlprodukte. Gleiche Eingaben müssen daher nicht gleiche Ergebnisse liefern.
Der Ausschnitt speichert folgende 16-Bit-Werte: result[24]=254, result[25]=254. Jede Berechnung mit Literalwerten stimmt mit der entsprechenden Berechnung aus RAM-Werten überein. Der Bildschirm zeigt repräsentative Werte; das Array result enthält alle Ergebnisse.


Vollständiges Beispielprogramm
Build-Befehl
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=noneImplementierung und Quelltext
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
Multipliziert zwei vorzeichenlose Bytes und gibt das obere Byte zurück.
Syntax
u8 __mul8x8_hi(u8 a, u8 b);Parameter
aVorzeichenloses Byte, 0..255.
bVorzeichenloses Byte, 0..255.
Rückgabewert
floor(a*b/256), im Bereich 0..254.
Anwendungsbeispiel
// Compare literal coefficients with values read from RAM.
result[4]=__mul8x8_hi(255,255);
v0=255;v1=255;
result[5]=__mul8x8_hi(v0,v1);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Vergleichen Sie Literalwerte mit im RAM gespeicherten Werten. Repräsentative Ergebnisse erscheinen als vorzeichenlose 16-Bit-Werte; negative Werte nutzen Zweierkomplement-Bitmuster. Der GB skaliert Festkommaprodukte, der FC liefert Ganzzahlprodukte. Gleiche Eingaben müssen daher nicht gleiche Ergebnisse liefern.
Der Ausschnitt speichert folgende 16-Bit-Werte: result[4]=254, result[5]=254. Jede Berechnung mit Literalwerten stimmt mit der entsprechenden Berechnung aus RAM-Werten überein. Der Bildschirm zeigt repräsentative Werte; das Array result enthält alle Ergebnisse.


Vollständiges Beispielprogramm
Build-Befehl
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=noneImplementierung und Quelltext
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
Wählt mit mask eine einzelne Aktion zur Wiederholung aus. Unter den neu gedrückten zulässigen Bits wird das niedrigste gesetzte Bit gewählt und sofort ausgegeben. Andernfalls bleibt die aktive Aktion erhalten, solange sie zulässig und gehalten ist. Endet sie, wird das niedrigste verbleibende gehaltene Bit ohne sofortigen Impuls übernommen. Ohne zulässiges gehaltenes Bit wird der Laufzeitzustand gelöscht.
Syntax
u8 __padrep(u8* state, u8 keys, u8 trigger, u8 mask);Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Parameter
stateZeiger auf fünf aufeinanderfolgende beschreibbare RAM-Bytes, etwa
u8 state[5]. Die Belegung lautet[0] aktives Tastenbit,[1] Anfangsverzögerung,[2] Wiederholungsintervall,[3] Zähler der verstrichenen Anfangsverzögerung,[4] Zähler des verstrichenen Intervalls. Jede unabhängige Wiederholung benötigt einen eigenen, vorab initialisierten Zustand. Nullzeiger, Bereiche unter fünf Bytes und das gemeinsame Verwenden eines Zustands für Richtungs- und allgemeine Varianten werden nicht unterstützt.keysVollständige Maske der aktuell gehaltenen Tasten im GB-Layout
BTN_*. Übergeben Sie einen Wert aus__readpad, das untere Byte von__readpadexoderinput_current; die Wiederholungsfunktion liest die Hardware nicht selbst.triggerNeu gedrückte Bits dieser Aktualisierung im selben Layout wie
keys, üblicherweisekeys & ~previous_keysoder das obere Byte von__readpadex. Es dürfen nur Bits enthalten sein, die auch inkeysgesetzt sind. Wird jedes Mal die Haltemaske übergeben, beginnt bei jedem Aufruf fälschlich ein neuer Tastendruck.maskZulässige GB-Tastenbits, mit
|kombiniert. Null deaktiviert alle Aktionen und löscht den Laufzeitzustand. Mehrere Bits erlauben mehrere Kandidaten; pro Aufruf wird jedoch nur ein ausgewähltes Bit statt einer kombinierten Impulsmaske zurückgegeben.
Rückgabewert
Null, wenn kein Aktionsimpuls fällig ist, andernfalls einer der Werte 1, 2, 4, 8, 16, 32, 64, 128. Ein Wert ungleich null ist ein Tastenbit, kein normalisierter Wahrheitswert und keine Anzahl von Wiederholungen.
Bedingungen und Grenzen
Gemeinsame Masken: BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80. Ein gesetztes Bit bedeutet gedrückt. input_* verwendet dieses Format auf GB und FC; rohe FC-Pad-Abfragen verwenden eine andere Bitreihenfolge.
Anfangsverzögerung von 0 bis 255 Aufrufen der Wiederholungsfunktion. Bei einem positiven Wert N erfolgt die erste Haltewiederholung genau N Aufrufe nach dem ersten Tastendruckimpuls. Bei 0 wird der Gleichheitsfall der Anfangsverzögerung übersprungen; das Intervall bestimmt dann den ersten Halteimpuls. Die Einheit ist nicht Millisekunden.
Intervall der weiteren Wiederholungen von 0 bis 255 Aufrufen. Ein positives N erzeugt nach der ersten Wiederholung alle N zulässigen Halteaktualisierungen einen Impuls. Null erzeugt nach der Anfangsverzögerung bei jeder zulässigen Aktualisierung einen Impuls; in dieser Phase liefern 0 und 1 somit dasselbe Tempo. Sind beide Zeitwerte 0, erzeugt jede Halteaktualisierung einen Impuls.
Rufen Sie die Funktion einmal pro vorgesehenem Eingabetakt auf. Jeder Aufruf verändert den Zustand und wartet nicht auf VBlank. Wird eine gehaltene Taste ohne neues Druckbit übernommen, liefert dieser Aufruf 0 und beginnt eine neue Verzögerung. Diese Funktionen wählen eine aktive Aktion pro Zustand; input_repeat verwaltet dagegen unabhängige Zeitgeber für alle acht Tasten.
Anwendungsbeispiel
result=__padrep(actions,keys,trigger,48); // A|B: select A, the lower bit.
if (result!=0) action_pulses++;Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Dieses Programm übergibt vorgegebene Tastenzustände an vier unabhängige Zustände und zeigt, bei welchem Aufruf eine Wiederholung erfolgt. Bei Verzögerung 3 und Intervall 2 erzeugen acht Aufrufe mit den Nummern 0 bis 7 Impulse bei 0, 3, 5 und 7. RIGHT PULSES, DOWN PULSES, ACTION PULSES und ALIAS PULSES zeigen daher jeweils 004. Die allgemeinen Varianten erhalten A und B gemeinsam und wählen A (16). Geprüft werden außerdem Initialisierung, Rücksetzen, Richtungsvorrang, Loslassen, Übernahme einer bereits gehaltenen Richtung, Zeitwerte von null und ein Ausdruck als Argument. CHECKS zeigt 057; FAILED CHECKS muss 000 sein. Dieses deterministische Beispiel verarbeitet vorgegebene Zustände und wartet nicht auf Ihre Tastendrücke.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Führt einen Wiederholungsschritt ausschließlich für Unten aus. Ein neuer Druck auf Unten liefert sofort 8 und startet die Zähler neu. Beim Halten folgen Impulse gemäß den gespeicherten Zeitwerten; beim Loslassen wird der Laufzeitzustand gelöscht. Andere Tasten lösen diese Wiederholung nicht aus.
Syntax
u8 __padrep_down(u8* state, u8 keys, u8 trigger);Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Parameter
stateZeiger auf fünf aufeinanderfolgende beschreibbare RAM-Bytes, etwa
u8 state[5]. Die Belegung lautet[0] aktives Tastenbit,[1] Anfangsverzögerung,[2] Wiederholungsintervall,[3] Zähler der verstrichenen Anfangsverzögerung,[4] Zähler des verstrichenen Intervalls. Jede unabhängige Wiederholung benötigt einen eigenen, vorab initialisierten Zustand. Nullzeiger, Bereiche unter fünf Bytes und das gemeinsame Verwenden eines Zustands für Richtungs- und allgemeine Varianten werden nicht unterstützt.keysVollständige Maske der aktuell gehaltenen Tasten im GB-Layout
BTN_*. Übergeben Sie einen Wert aus__readpad, das untere Byte von__readpadexoderinput_current; die Wiederholungsfunktion liest die Hardware nicht selbst.triggerNeu gedrückte Bits dieser Aktualisierung im selben Layout wie
keys, üblicherweisekeys & ~previous_keysoder das obere Byte von__readpadex. Es dürfen nur Bits enthalten sein, die auch inkeysgesetzt sind. Wird jedes Mal die Haltemaske übergeben, beginnt bei jedem Aufruf fälschlich ein neuer Tastendruck.
Rückgabewert
Null, wenn kein Aktionsimpuls fällig ist, andernfalls einer der Werte 8. Ein Wert ungleich null ist ein Tastenbit, kein normalisierter Wahrheitswert und keine Anzahl von Wiederholungen.
Bedingungen und Grenzen
Gemeinsame Masken: BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80. Ein gesetztes Bit bedeutet gedrückt. input_* verwendet dieses Format auf GB und FC; rohe FC-Pad-Abfragen verwenden eine andere Bitreihenfolge.
Anfangsverzögerung von 0 bis 255 Aufrufen der Wiederholungsfunktion. Bei einem positiven Wert N erfolgt die erste Haltewiederholung genau N Aufrufe nach dem ersten Tastendruckimpuls. Bei 0 wird der Gleichheitsfall der Anfangsverzögerung übersprungen; das Intervall bestimmt dann den ersten Halteimpuls. Die Einheit ist nicht Millisekunden.
Intervall der weiteren Wiederholungen von 0 bis 255 Aufrufen. Ein positives N erzeugt nach der ersten Wiederholung alle N zulässigen Halteaktualisierungen einen Impuls. Null erzeugt nach der Anfangsverzögerung bei jeder zulässigen Aktualisierung einen Impuls; in dieser Phase liefern 0 und 1 somit dasselbe Tempo. Sind beide Zeitwerte 0, erzeugt jede Halteaktualisierung einen Impuls.
Rufen Sie die Funktion einmal pro vorgesehenem Eingabetakt auf. Jeder Aufruf verändert den Zustand und wartet nicht auf VBlank. Wird eine gehaltene Taste ohne neues Druckbit übernommen, liefert dieser Aufruf 0 und beginnt eine neue Verzögerung. Diese Funktionen wählen eine aktive Aktion pro Zustand; input_repeat verwaltet dagegen unabhängige Zeitgeber für alle acht Tasten.
Anwendungsbeispiel
result=__padrep_down(down,keys,trigger);
if (result!=0) down_pulses++;Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Dieses Programm übergibt vorgegebene Tastenzustände an vier unabhängige Zustände und zeigt, bei welchem Aufruf eine Wiederholung erfolgt. Bei Verzögerung 3 und Intervall 2 erzeugen acht Aufrufe mit den Nummern 0 bis 7 Impulse bei 0, 3, 5 und 7. RIGHT PULSES, DOWN PULSES, ACTION PULSES und ALIAS PULSES zeigen daher jeweils 004. Die allgemeinen Varianten erhalten A und B gemeinsam und wählen A (16). Geprüft werden außerdem Initialisierung, Rücksetzen, Richtungsvorrang, Loslassen, Übernahme einer bereits gehaltenen Richtung, Zeitwerte von null und ein Ausdruck als Argument. CHECKS zeigt 057; FAILED CHECKS muss 000 sein. Dieses deterministische Beispiel verarbeitet vorgegebene Zustände und wartet nicht auf Ihre Tastendrücke.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Initialisiert den vom aufrufenden Programm bereitgestellten Zustand aus fünf Bytes: Die aktive Aktion und beide Zähler werden gelöscht, Anfangsverzögerung und Wiederholungsintervall gespeichert. Es werden weder Tasten eingelesen noch Impulse ausgegeben.
Syntax
void __padrep_init(u8* state, u8 das, u8 arr);Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Parameter
stateZeiger auf fünf aufeinanderfolgende beschreibbare RAM-Bytes, etwa
u8 state[5]. Die Belegung lautet[0] aktives Tastenbit,[1] Anfangsverzögerung,[2] Wiederholungsintervall,[3] Zähler der verstrichenen Anfangsverzögerung,[4] Zähler des verstrichenen Intervalls. Jede unabhängige Wiederholung benötigt einen eigenen, vorab initialisierten Zustand. Nullzeiger, Bereiche unter fünf Bytes und das gemeinsame Verwenden eines Zustands für Richtungs- und allgemeine Varianten werden nicht unterstützt.dasAnfangsverzögerung von 0 bis 255 Aufrufen der Wiederholungsfunktion. Bei einem positiven Wert N erfolgt die erste Haltewiederholung genau N Aufrufe nach dem ersten Tastendruckimpuls. Bei 0 wird der Gleichheitsfall der Anfangsverzögerung übersprungen; das Intervall bestimmt dann den ersten Halteimpuls. Die Einheit ist nicht Millisekunden.
arrIntervall der weiteren Wiederholungen von 0 bis 255 Aufrufen. Ein positives N erzeugt nach der ersten Wiederholung alle N zulässigen Halteaktualisierungen einen Impuls. Null erzeugt nach der Anfangsverzögerung bei jeder zulässigen Aktualisierung einen Impuls; in dieser Phase liefern 0 und 1 somit dasselbe Tempo. Sind beide Zeitwerte 0, erzeugt jede Halteaktualisierung einen Impuls.
Rückgabewert
Keiner (void).
Anwendungsbeispiel
__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);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Dieses Programm übergibt vorgegebene Tastenzustände an vier unabhängige Zustände und zeigt, bei welchem Aufruf eine Wiederholung erfolgt. Bei Verzögerung 3 und Intervall 2 erzeugen acht Aufrufe mit den Nummern 0 bis 7 Impulse bei 0, 3, 5 und 7. RIGHT PULSES, DOWN PULSES, ACTION PULSES und ALIAS PULSES zeigen daher jeweils 004. Die allgemeinen Varianten erhalten A und B gemeinsam und wählen A (16). Geprüft werden außerdem Initialisierung, Rücksetzen, Richtungsvorrang, Loslassen, Übernahme einer bereits gehaltenen Richtung, Zeitwerte von null und ein Ausdruck als Argument. CHECKS zeigt 057; FAILED CHECKS muss 000 sein. Dieses deterministische Beispiel verarbeitet vorgegebene Zustände und wartet nicht auf Ihre Tastendrücke.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Führt einen Wiederholungsschritt für Links oder Rechts aus. Ein neuer Druck auf Rechts liefert sofort 1, andernfalls ein neuer Druck auf Links 2. Bei zwei neuen Tastendrücken hat Rechts Vorrang. Ohne neuen Tastendruck liefert gleichzeitiges Halten beider Richtungen 0 und hält die Zähler an; sind beide losgelassen, wird der Laufzeitzustand gelöscht.
Syntax
u8 __padrep_lr(u8* state, u8 keys, u8 trigger);Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Parameter
stateZeiger auf fünf aufeinanderfolgende beschreibbare RAM-Bytes, etwa
u8 state[5]. Die Belegung lautet[0] aktives Tastenbit,[1] Anfangsverzögerung,[2] Wiederholungsintervall,[3] Zähler der verstrichenen Anfangsverzögerung,[4] Zähler des verstrichenen Intervalls. Jede unabhängige Wiederholung benötigt einen eigenen, vorab initialisierten Zustand. Nullzeiger, Bereiche unter fünf Bytes und das gemeinsame Verwenden eines Zustands für Richtungs- und allgemeine Varianten werden nicht unterstützt.keysVollständige Maske der aktuell gehaltenen Tasten im GB-Layout
BTN_*. Übergeben Sie einen Wert aus__readpad, das untere Byte von__readpadexoderinput_current; die Wiederholungsfunktion liest die Hardware nicht selbst.triggerNeu gedrückte Bits dieser Aktualisierung im selben Layout wie
keys, üblicherweisekeys & ~previous_keysoder das obere Byte von__readpadex. Es dürfen nur Bits enthalten sein, die auch inkeysgesetzt sind. Wird jedes Mal die Haltemaske übergeben, beginnt bei jedem Aufruf fälschlich ein neuer Tastendruck.
Rückgabewert
Null, wenn kein Aktionsimpuls fällig ist, andernfalls einer der Werte 1, 2. Ein Wert ungleich null ist ein Tastenbit, kein normalisierter Wahrheitswert und keine Anzahl von Wiederholungen.
Bedingungen und Grenzen
Gemeinsame Masken: BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80. Ein gesetztes Bit bedeutet gedrückt. input_* verwendet dieses Format auf GB und FC; rohe FC-Pad-Abfragen verwenden eine andere Bitreihenfolge.
Anfangsverzögerung von 0 bis 255 Aufrufen der Wiederholungsfunktion. Bei einem positiven Wert N erfolgt die erste Haltewiederholung genau N Aufrufe nach dem ersten Tastendruckimpuls. Bei 0 wird der Gleichheitsfall der Anfangsverzögerung übersprungen; das Intervall bestimmt dann den ersten Halteimpuls. Die Einheit ist nicht Millisekunden.
Intervall der weiteren Wiederholungen von 0 bis 255 Aufrufen. Ein positives N erzeugt nach der ersten Wiederholung alle N zulässigen Halteaktualisierungen einen Impuls. Null erzeugt nach der Anfangsverzögerung bei jeder zulässigen Aktualisierung einen Impuls; in dieser Phase liefern 0 und 1 somit dasselbe Tempo. Sind beide Zeitwerte 0, erzeugt jede Halteaktualisierung einen Impuls.
Rufen Sie die Funktion einmal pro vorgesehenem Eingabetakt auf. Jeder Aufruf verändert den Zustand und wartet nicht auf VBlank. Wird eine gehaltene Taste ohne neues Druckbit übernommen, liefert dieser Aufruf 0 und beginnt eine neue Verzögerung. Diese Funktionen wählen eine aktive Aktion pro Zustand; input_repeat verwaltet dagegen unabhängige Zeitgeber für alle acht Tasten.
Anwendungsbeispiel
result=__padrep_lr(lr,keys,trigger);
if (result!=0) pulses++;Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Dieses Programm übergibt vorgegebene Tastenzustände an vier unabhängige Zustände und zeigt, bei welchem Aufruf eine Wiederholung erfolgt. Bei Verzögerung 3 und Intervall 2 erzeugen acht Aufrufe mit den Nummern 0 bis 7 Impulse bei 0, 3, 5 und 7. RIGHT PULSES, DOWN PULSES, ACTION PULSES und ALIAS PULSES zeigen daher jeweils 004. Die allgemeinen Varianten erhalten A und B gemeinsam und wählen A (16). Geprüft werden außerdem Initialisierung, Rücksetzen, Richtungsvorrang, Loslassen, Übernahme einer bereits gehaltenen Richtung, Zeitwerte von null und ein Ausdruck als Argument. CHECKS zeigt 057; FAILED CHECKS muss 000 sein. Dieses deterministische Beispiel verarbeitet vorgegebene Zustände und wartet nicht auf Ihre Tastendrücke.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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 ist ein anderer Name für __padrep; beide Aufrufe verwenden dieselbe Compilerimplementierung. Aktionsauswahl, Zeitverhalten, Zustandslayout und Rückgabewerte sind identisch. Der ausdrückliche Name macht an der Aufrufstelle deutlicher, dass eine Maske die zulässigen Aktionen bestimmt.
Wählt mit mask eine einzelne Aktion zur Wiederholung aus. Unter den neu gedrückten zulässigen Bits wird das niedrigste gesetzte Bit gewählt und sofort ausgegeben. Andernfalls bleibt die aktive Aktion erhalten, solange sie zulässig und gehalten ist. Endet sie, wird das niedrigste verbleibende gehaltene Bit ohne sofortigen Impuls übernommen. Ohne zulässiges gehaltenes Bit wird der Laufzeitzustand gelöscht.
Syntax
u8 __padrep_mask(u8* state, u8 keys, u8 trigger, u8 mask);Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Parameter
stateZeiger auf fünf aufeinanderfolgende beschreibbare RAM-Bytes, etwa
u8 state[5]. Die Belegung lautet[0] aktives Tastenbit,[1] Anfangsverzögerung,[2] Wiederholungsintervall,[3] Zähler der verstrichenen Anfangsverzögerung,[4] Zähler des verstrichenen Intervalls. Jede unabhängige Wiederholung benötigt einen eigenen, vorab initialisierten Zustand. Nullzeiger, Bereiche unter fünf Bytes und das gemeinsame Verwenden eines Zustands für Richtungs- und allgemeine Varianten werden nicht unterstützt.keysVollständige Maske der aktuell gehaltenen Tasten im GB-Layout
BTN_*. Übergeben Sie einen Wert aus__readpad, das untere Byte von__readpadexoderinput_current; die Wiederholungsfunktion liest die Hardware nicht selbst.triggerNeu gedrückte Bits dieser Aktualisierung im selben Layout wie
keys, üblicherweisekeys & ~previous_keysoder das obere Byte von__readpadex. Es dürfen nur Bits enthalten sein, die auch inkeysgesetzt sind. Wird jedes Mal die Haltemaske übergeben, beginnt bei jedem Aufruf fälschlich ein neuer Tastendruck.maskZulässige GB-Tastenbits, mit
|kombiniert. Null deaktiviert alle Aktionen und löscht den Laufzeitzustand. Mehrere Bits erlauben mehrere Kandidaten; pro Aufruf wird jedoch nur ein ausgewähltes Bit statt einer kombinierten Impulsmaske zurückgegeben.
Rückgabewert
Null, wenn kein Aktionsimpuls fällig ist, andernfalls einer der Werte 1, 2, 4, 8, 16, 32, 64, 128. Ein Wert ungleich null ist ein Tastenbit, kein normalisierter Wahrheitswert und keine Anzahl von Wiederholungen.
Bedingungen und Grenzen
Gemeinsame Masken: BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80. Ein gesetztes Bit bedeutet gedrückt. input_* verwendet dieses Format auf GB und FC; rohe FC-Pad-Abfragen verwenden eine andere Bitreihenfolge.
Anfangsverzögerung von 0 bis 255 Aufrufen der Wiederholungsfunktion. Bei einem positiven Wert N erfolgt die erste Haltewiederholung genau N Aufrufe nach dem ersten Tastendruckimpuls. Bei 0 wird der Gleichheitsfall der Anfangsverzögerung übersprungen; das Intervall bestimmt dann den ersten Halteimpuls. Die Einheit ist nicht Millisekunden.
Intervall der weiteren Wiederholungen von 0 bis 255 Aufrufen. Ein positives N erzeugt nach der ersten Wiederholung alle N zulässigen Halteaktualisierungen einen Impuls. Null erzeugt nach der Anfangsverzögerung bei jeder zulässigen Aktualisierung einen Impuls; in dieser Phase liefern 0 und 1 somit dasselbe Tempo. Sind beide Zeitwerte 0, erzeugt jede Halteaktualisierung einen Impuls.
Rufen Sie die Funktion einmal pro vorgesehenem Eingabetakt auf. Jeder Aufruf verändert den Zustand und wartet nicht auf VBlank. Wird eine gehaltene Taste ohne neues Druckbit übernommen, liefert dieser Aufruf 0 und beginnt eine neue Verzögerung. Diese Funktionen wählen eine aktive Aktion pro Zustand; input_repeat verwaltet dagegen unabhängige Zeitgeber für alle acht Tasten.
Anwendungsbeispiel
result=__padrep_mask(alias_state,keys,trigger,48);
if (result!=0) alias_pulses++;Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Dieses Programm übergibt vorgegebene Tastenzustände an vier unabhängige Zustände und zeigt, bei welchem Aufruf eine Wiederholung erfolgt. Bei Verzögerung 3 und Intervall 2 erzeugen acht Aufrufe mit den Nummern 0 bis 7 Impulse bei 0, 3, 5 und 7. RIGHT PULSES, DOWN PULSES, ACTION PULSES und ALIAS PULSES zeigen daher jeweils 004. Die allgemeinen Varianten erhalten A und B gemeinsam und wählen A (16). Geprüft werden außerdem Initialisierung, Rücksetzen, Richtungsvorrang, Loslassen, Übernahme einer bereits gehaltenen Richtung, Zeitwerte von null und ein Ausdruck als Argument. CHECKS zeigt 057; FAILED CHECKS muss 000 sein. Dieses deterministische Beispiel verarbeitet vorgegebene Zustände und wartet nicht auf Ihre Tastendrücke.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Bricht die aktive Aktion ab und setzt beide Laufzeitzähler zurück; Verzögerung und Intervall bleiben erhalten. Verwenden Sie dies beim Schließen eines Menüs oder beim Wechsel des Eingabeziels, damit eine bereits laufende Wiederholung nicht in die nächste Bedienaktion übernommen wird.
Syntax
void __padrep_reset(u8* state);Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Parameter
stateZeiger auf fünf aufeinanderfolgende beschreibbare RAM-Bytes, etwa
u8 state[5]. Die Belegung lautet[0] aktives Tastenbit,[1] Anfangsverzögerung,[2] Wiederholungsintervall,[3] Zähler der verstrichenen Anfangsverzögerung,[4] Zähler des verstrichenen Intervalls. Jede unabhängige Wiederholung benötigt einen eigenen, vorab initialisierten Zustand. Nullzeiger, Bereiche unter fünf Bytes und das gemeinsame Verwenden eines Zustands für Richtungs- und allgemeine Varianten werden nicht unterstützt.
Rückgabewert
Keiner (void).
Anwendungsbeispiel
__padrep_reset(lr);
expect(lr[0],0); expect(lr[1],3); expect(lr[2],2);
expect(lr[3],0); expect(lr[4],0);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Dieses Programm übergibt vorgegebene Tastenzustände an vier unabhängige Zustände und zeigt, bei welchem Aufruf eine Wiederholung erfolgt. Bei Verzögerung 3 und Intervall 2 erzeugen acht Aufrufe mit den Nummern 0 bis 7 Impulse bei 0, 3, 5 und 7. RIGHT PULSES, DOWN PULSES, ACTION PULSES und ALIAS PULSES zeigen daher jeweils 004. Die allgemeinen Varianten erhalten A und B gemeinsam und wählen A (16). Geprüft werden außerdem Initialisierung, Rücksetzen, Richtungsvorrang, Loslassen, Übernahme einer bereits gehaltenen Richtung, Zeitwerte von null und ein Ausdruck als Argument. CHECKS zeigt 057; FAILED CHECKS muss 000 sein. Dieses deterministische Beispiel verarbeitet vorgegebene Zustände und wartet nicht auf Ihre Tastendrücke.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Liest Steuerkreuz und Aktionstasten aus JOYP und kombiniert sie zur gemeinsamen 8-Bit-Maske. Wählt jede Gruppe aus, liest zur Stabilisierung zweimal und setzt die Auswahlbits anschließend auf 0x30.
Syntax
u8 __readpad();Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Rückgabewert
Eine 8-Bit-Maske im gemeinsamen BTN_*-Format. Mehrere Tasten können zugleich gesetzt sein; 0 bedeutet keine.
Bedingungen und Grenzen
Gemeinsame Masken: BTN_RIGHT=0x01, BTN_LEFT=0x02, BTN_UP=0x04, BTN_DOWN=0x08, BTN_A=0x10, BTN_B=0x20, BTN_SELECT=0x40, BTN_START=0x80. Ein gesetztes Bit bedeutet gedrückt. input_* verwendet dieses Format auf GB und FC; rohe FC-Pad-Abfragen verwenden eine andere Bitreihenfolge.
Dieser Aufruf liest einen neuen Hardwarezustand. Er aktualisiert weder den gespeicherten input_*- noch den nes_pad1_*-Zustand und erzeugt keine Druck-, Loslass- oder Wiederholungsereignisse. Speichere das Ergebnis und vergleiche es bei Bedarf mit deiner vorherigen Maske.
Anwendungsbeispiel
all_keys=__readpad(); // A + Right: 0x11, decimal 17.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Dieses Programm vergleicht die drei Maskenformate und den Rückgabewert, der aktuellen Zustand und neue Tastendrücke zusammenfasst. Halten Sie A und Rechts gleichzeitig gedrückt. ALL KEYS ist 17; DIRECTIONS und BUTTONS LOW sind jeweils 1. Bei einer vorherigen Maske von 0 sind EX CURRENT und EX NEW PRESS beide 17. Wird die aktuelle Maske an den nächsten Leseaufruf übergeben, ist HELD PRESS 0. FAILED CHECKS muss 0 sein. Die erfassten Werte bleiben nach dem Loslassen sichtbar.


Vollständiges Beispielprogramm
Build-Befehl
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-cacheImplementierung und Quelltext
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
Liest nur die Aktionstasten von JOYP in den unteren vier Bits: A=1, B=2, Select=4, Start=8. Das sind nicht die Werte von BTN_A bis BTN_START, die in den oberen vier Bits liegen. Setzt die JOYP-Auswahl auf 0x30 zurück.
Syntax
u8 __readpadbtn();Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Rückgabewert
Eine Maske von 0 bis 15 in der beschriebenen Bitreihenfolge. Mehrere Bits können gesetzt sein; eine Taste mit result & bit prüfen, statt das gesamte Ergebnis mit 1 zu vergleichen.
Bedingungen und Grenzen
Dieser Aufruf liest einen neuen Hardwarezustand. Er aktualisiert weder den gespeicherten input_*- noch den nes_pad1_*-Zustand und erzeugt keine Druck-, Loslass- oder Wiederholungsereignisse. Speichere das Ergebnis und vergleiche es bei Bedarf mit deiner vorherigen Maske.
Anwendungsbeispiel
buttons=__readpadbtn(); // A: 1, not BTN_A (16).Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Dieses Programm vergleicht die drei Maskenformate und den Rückgabewert, der aktuellen Zustand und neue Tastendrücke zusammenfasst. Halten Sie A und Rechts gleichzeitig gedrückt. ALL KEYS ist 17; DIRECTIONS und BUTTONS LOW sind jeweils 1. Bei einer vorherigen Maske von 0 sind EX CURRENT und EX NEW PRESS beide 17. Wird die aktuelle Maske an den nächsten Leseaufruf übergeben, ist HELD PRESS 0. FAILED CHECKS muss 0 sein. Die erfassten Werte bleiben nach dem Loslassen sichtbar.


Vollständiges Beispielprogramm
Build-Befehl
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-cacheImplementierung und Quelltext
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
Liest nur die Richtungsgruppe von JOYP. Das Ergebnis nutzt Bits 0–3: rechts=1, links=2, oben=4, unten=8; Bits 4–7 sind 0. Aktionstasten bleiben unberücksichtigt, die JOYP-Auswahl wird auf 0x30 zurückgesetzt.
Syntax
u8 __readpaddir();Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Rückgabewert
Eine Maske von 0 bis 15 in der beschriebenen Bitreihenfolge. Mehrere Bits können gesetzt sein; eine Taste mit result & bit prüfen, statt das gesamte Ergebnis mit 1 zu vergleichen.
Bedingungen und Grenzen
Dieser Aufruf liest einen neuen Hardwarezustand. Er aktualisiert weder den gespeicherten input_*- noch den nes_pad1_*-Zustand und erzeugt keine Druck-, Loslass- oder Wiederholungsereignisse. Speichere das Ergebnis und vergleiche es bei Bedarf mit deiner vorherigen Maske.
Anwendungsbeispiel
directions=__readpaddir(); // Right: 1. The held A button is not included.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Dieses Programm vergleicht die drei Maskenformate und den Rückgabewert, der aktuellen Zustand und neue Tastendrücke zusammenfasst. Halten Sie A und Rechts gleichzeitig gedrückt. ALL KEYS ist 17; DIRECTIONS und BUTTONS LOW sind jeweils 1. Bei einer vorherigen Maske von 0 sind EX CURRENT und EX NEW PRESS beide 17. Wird die aktuelle Maske an den nächsten Leseaufruf übergeben, ist HELD PRESS 0. FAILED CHECKS muss 0 sein. Die erfassten Werte bleiben nach dem Loslassen sichtbar.


Vollständiges Beispielprogramm
Build-Befehl
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-cacheImplementierung und Quelltext
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
Berechnet ein vorzeichenbehaftetes Skalarprodukt mit zwei Termen.
Syntax
u16 __sdot2_q1_7(u16 a0, u16 a1, u8 b0, u8 b1);Parameter
a0Vorzeichenbehaftetes 16-Bit-Bitmuster für −32768..32767.
a1Vorzeichenbehaftetes 16-Bit-Bitmuster für −32768..32767.
b0Vorzeichenbehaftetes Byte-Bitmuster für einen Koeffizienten von −128..127.
b1Vorzeichenbehaftetes Byte-Bitmuster für einen Koeffizienten von −128..127.
Rückgabewert
Jeder Term ist 2*trunc(a*b/256). Die Terme werden addiert und die unteren 16 Bits zurückgegeben.
Bedingungen und Grenzen
Auf dem GB werden Produkte durch 256 geteilt; vorzeichenbehaftete Ergebnisse werden gegen null abgeschnitten. Q1.7-Varianten verdoppeln diesen abgeschnittenen Wert und können um eins von der exakten Division durch 128 abweichen. Die Terme werden einzeln gerundet; die abschließende Addition läuft bei 16 Bit um.
Anwendungsbeispiel
// 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);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Vergleichen Sie Literalwerte mit im RAM gespeicherten Werten. Repräsentative Ergebnisse erscheinen als vorzeichenlose 16-Bit-Werte; negative Werte nutzen Zweierkomplement-Bitmuster. Der GB skaliert Festkommaprodukte, der FC liefert Ganzzahlprodukte. Gleiche Eingaben müssen daher nicht gleiche Ergebnisse liefern.
Der Ausschnitt speichert folgende 16-Bit-Werte: result[20]=1018, result[21]=1018, result[22]=64518, result[23]=64518. Jede Berechnung mit Literalwerten stimmt mit der entsprechenden Berechnung aus RAM-Werten überein. Der Bildschirm zeigt repräsentative Werte; das Array result enthält alle Ergebnisse.


Vollständiges Beispielprogramm
Build-Befehl
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=noneImplementierung und Quelltext
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
Berechnet ein vorzeichenbehaftetes Skalarprodukt mit zwei Termen.
Syntax
u16 __sdot2_q8_8(u16 a0, u16 a1, u8 b0, u8 b1);Parameter
a0Vorzeichenbehaftetes 16-Bit-Bitmuster für −32768..32767.
a1Vorzeichenbehaftetes 16-Bit-Bitmuster für −32768..32767.
b0Vorzeichenbehaftetes Byte-Bitmuster für einen Koeffizienten von −128..127.
b1Vorzeichenbehaftetes Byte-Bitmuster für einen Koeffizienten von −128..127.
Rückgabewert
Jeder Term ist trunc(a*b/256). Die Terme werden addiert und die unteren 16 Bits zurückgegeben.
Bedingungen und Grenzen
Auf dem GB werden Produkte durch 256 geteilt; vorzeichenbehaftete Ergebnisse werden gegen null abgeschnitten. Q1.7-Varianten verdoppeln diesen abgeschnittenen Wert und können um eins von der exakten Division durch 128 abweichen. Die Terme werden einzeln gerundet; die abschließende Addition läuft bei 16 Bit um.
Anwendungsbeispiel
// 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);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Vergleichen Sie Literalwerte mit im RAM gespeicherten Werten. Repräsentative Ergebnisse erscheinen als vorzeichenlose 16-Bit-Werte; negative Werte nutzen Zweierkomplement-Bitmuster. Der GB skaliert Festkommaprodukte, der FC liefert Ganzzahlprodukte. Gleiche Eingaben müssen daher nicht gleiche Ergebnisse liefern.
Der Ausschnitt speichert folgende 16-Bit-Werte: result[30]=509, result[31]=509, result[32]=65027, result[33]=65027. Jede Berechnung mit Literalwerten stimmt mit der entsprechenden Berechnung aus RAM-Werten überein. Der Bildschirm zeigt repräsentative Werte; das Array result enthält alle Ergebnisse.


Vollständiges Beispielprogramm
Build-Befehl
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=noneImplementierung und Quelltext
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
Berechnet ein vorzeichenbehaftetes Skalarprodukt mit drei Termen.
Syntax
u16 __sdot3_q1_7(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);Parameter
a0Vorzeichenbehaftetes 16-Bit-Bitmuster für −32768..32767.
a1Vorzeichenbehaftetes 16-Bit-Bitmuster für −32768..32767.
a2Vorzeichenbehaftetes 16-Bit-Bitmuster für −32768..32767.
b0Vorzeichenbehaftetes Byte-Bitmuster für einen Koeffizienten von −128..127.
b1Vorzeichenbehaftetes Byte-Bitmuster für einen Koeffizienten von −128..127.
b2Vorzeichenbehaftetes Byte-Bitmuster für einen Koeffizienten von −128..127.
Rückgabewert
Jeder Term ist 2*trunc(a*b/256). Die Terme werden addiert und die unteren 16 Bits zurückgegeben.
Bedingungen und Grenzen
Auf dem GB werden Produkte durch 256 geteilt; vorzeichenbehaftete Ergebnisse werden gegen null abgeschnitten. Q1.7-Varianten verdoppeln diesen abgeschnittenen Wert und können um eins von der exakten Division durch 128 abweichen. Die Terme werden einzeln gerundet; die abschließende Addition läuft bei 16 Bit um.
Anwendungsbeispiel
// 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);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Vergleichen Sie Literalwerte mit im RAM gespeicherten Werten. Repräsentative Ergebnisse erscheinen als vorzeichenlose 16-Bit-Werte; negative Werte nutzen Zweierkomplement-Bitmuster. Der GB skaliert Festkommaprodukte, der FC liefert Ganzzahlprodukte. Gleiche Eingaben müssen daher nicht gleiche Ergebnisse liefern.
Der Ausschnitt speichert folgende 16-Bit-Werte: result[16]=1042, result[17]=1042, result[18]=64494, result[19]=64494. Jede Berechnung mit Literalwerten stimmt mit der entsprechenden Berechnung aus RAM-Werten überein. Der Bildschirm zeigt repräsentative Werte; das Array result enthält alle Ergebnisse.


Vollständiges Beispielprogramm
Build-Befehl
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=noneImplementierung und Quelltext
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
Berechnet ein vorzeichenbehaftetes Skalarprodukt mit drei Termen.
Syntax
u16 __sdot3_q8_8(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);Parameter
a0Vorzeichenbehaftetes 16-Bit-Bitmuster für −32768..32767.
a1Vorzeichenbehaftetes 16-Bit-Bitmuster für −32768..32767.
a2Vorzeichenbehaftetes 16-Bit-Bitmuster für −32768..32767.
b0Vorzeichenbehaftetes Byte-Bitmuster für einen Koeffizienten von −128..127.
b1Vorzeichenbehaftetes Byte-Bitmuster für einen Koeffizienten von −128..127.
b2Vorzeichenbehaftetes Byte-Bitmuster für einen Koeffizienten von −128..127.
Rückgabewert
Jeder Term ist trunc(a*b/256). Die Terme werden addiert und die unteren 16 Bits zurückgegeben.
Bedingungen und Grenzen
Auf dem GB werden Produkte durch 256 geteilt; vorzeichenbehaftete Ergebnisse werden gegen null abgeschnitten. Q1.7-Varianten verdoppeln diesen abgeschnittenen Wert und können um eins von der exakten Division durch 128 abweichen. Die Terme werden einzeln gerundet; die abschließende Addition läuft bei 16 Bit um.
Anwendungsbeispiel
// 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);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Vergleichen Sie Literalwerte mit im RAM gespeicherten Werten. Repräsentative Ergebnisse erscheinen als vorzeichenlose 16-Bit-Werte; negative Werte nutzen Zweierkomplement-Bitmuster. Der GB skaliert Festkommaprodukte, der FC liefert Ganzzahlprodukte. Gleiche Eingaben müssen daher nicht gleiche Ergebnisse liefern.
Der Ausschnitt speichert folgende 16-Bit-Werte: result[8]=521, result[9]=521, result[10]=65015, result[11]=65015. Jede Berechnung mit Literalwerten stimmt mit der entsprechenden Berechnung aus RAM-Werten überein. Der Bildschirm zeigt repräsentative Werte; das Array result enthält alle Ergebnisse.


Vollständiges Beispielprogramm
Build-Befehl
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=noneImplementierung und Quelltext
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
Schreibt eine Tile-Nummer in eine Kartenposition. Das wählt ein vorhandenes Tile-Bild aus und ändert keine Pixeldaten.
Das Ziel ist stets 0x9800 + 32*y + x, unabhängig von der Hintergrund- und Fensterkartenauswahl in LCDC.
Wartet nicht auf VRAM-Zugriff. Nur bei ausgeschaltetem LCD oder in einem vom Aufrufer gesicherten Schreibintervall verwenden, das für den gesamten Transfer reicht.
Syntax
__settile_fast(x, y, tile);Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Parameter
x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
tileTile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Tile-Bytes werden in die gewählte VRAM-Bank geschrieben. Wählen Sie auf CGB Bank 0 für Tile-Nummern; der Aufruf wechselt die Bank nicht.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheinen die Beispielzeichen und die Ergebnisanzeige blau auf weißem Hintergrund. Die Vorbereitung setzt die Zeichenfarbe der Palette 0 auf Blau, ohne die Attribute der Tilemap zu ändern. Auf DMG erscheinen dieselben Zeichen schwarz auf weißem Hintergrund.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
A

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Schreibt eine 16×16-Pixel-Zelle in einen Tile-Kartenpuffer im RAM: oben tile,tile+1, unten tile+2,tile+3. Die Werte laufen bei 256 über. Es erfolgt kein VRAM-Transfer.
Syntax
__settilebg16_buf(buf, x, y, tile);Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Parameter
bufRAM-Puffer mit 32 Bytes pro Tile-Zeile. Eine vollständige 32×32-Karte benötigt 1024 Bytes. Oben links liegt bei
64*y + 2*x; die vier relativen Offsets sind0,1,32,33.x, yKoordinaten in 16×16-Pixel-Zellen, jeweils von 0 bis 15 bei einem vollständigen 32×32-Tile-Puffer. Sie unterscheiden sich von den 8×8-Tile-Koordinaten der Einzelzugriffe.
tileTile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Die Zelle zeigt oben AB und unten CD, beginnend bei Tile (2,2): eine 16×16-Pixel-Zelle bei Zellkoordinaten (1,1). Pufferbeispiele prüfen zunächst, dass die RAM-Vorbereitung die VRAM-Daten nicht verändert, und übertragen dann die Zelle. Transferbeispiele setzen die RAM-Bytes vorher ausdrücklich.
Auf CGB erscheinen die Beispielzeichen und die Ergebnisanzeige blau auf weißem Hintergrund. Die Vorbereitung setzt die Zeichenfarbe der Palette 0 auf Blau, ohne die Attribute der Tilemap zu ändern. Auf DMG erscheinen dieselben Zeichen schwarz auf weißem Hintergrund.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=2), entsprechend Pixel (16,16). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
AB
CD

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Überträgt zwei benachbarte Tile-Zeilen aus dem RAM-Puffer in die durch LCDC gewählte BG-Karte. Der Quelloffset ist 64*y + x; aus dieser und der 32 Bytes tiefer liegenden Zeile werden jeweils count Bytes kopiert.
Wartet bei Bedarf auf beschreibbares VRAM. Bei aktivem LCD werden Interrupts gesperrt und anschließend möglicherweise aktiviert. Weder der vorherige Interruptzustand noch ein Abschluss innerhalb eines VBlank wird garantiert.
Syntax
__settilebg16_flush(buf, y, x, count);Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Parameter
bufLesbarer Kartenpuffer mit 32 Bytes Zeilenabstand. Eine vollständige Karte belegt 1024 Bytes. Die Variante mit zwei Puffern erhält getrennte Arrays für Tile-Nummern und Attribute mit gleichem Aufbau, keine verschachtelten Paare.
y, x, countDie Reihenfolge lautet
y,x,count.ybezeichnet eine 16-Pixel-Zellzeile (0–15),xundcountzählen 8-Pixel-Tiles. Es mussx+count <= 32gelten. Für eine Zelle bei(2,3)verwenden Siey=3,x=4,count=2.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Tile-Bytes werden in die gewählte VRAM-Bank geschrieben. Wählen Sie auf CGB Bank 0 für Tile-Nummern; der Aufruf wechselt die Bank nicht.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Die Zelle zeigt oben AB und unten CD, beginnend bei Tile (2,2): eine 16×16-Pixel-Zelle bei Zellkoordinaten (1,1). Pufferbeispiele prüfen zunächst, dass die RAM-Vorbereitung die VRAM-Daten nicht verändert, und übertragen dann die Zelle. Transferbeispiele setzen die RAM-Bytes vorher ausdrücklich.
Auf CGB erscheinen die Beispielzeichen und die Ergebnisanzeige blau auf weißem Hintergrund. Die Vorbereitung setzt die Zeichenfarbe der Palette 0 auf Blau, ohne die Attribute der Tilemap zu ändern. Auf DMG erscheinen dieselben Zeichen schwarz auf weißem Hintergrund.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=2), entsprechend Pixel (16,16). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
AB
CD

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Schreibt eine 16×16-Pixel-Zelle in einen Tile-Kartenpuffer im RAM: oben tile,tile+1, unten tile+2,tile+3. Die Werte laufen bei 256 über. Es erfolgt kein VRAM-Transfer.
Schreibt außerdem dasselbe attr-Byte in die vier entsprechenden Zellen des separaten Attributpuffers. Beide Puffer liegen im normalen RAM; die Vorbereitung läuft daher auch auf DMG.
Syntax
__settilebg16cgb_buf(tilebuf, attrbuf, x, y, tile, attr);Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Parameter
tilebuf, attrbufRAM-Puffer mit 32 Bytes pro Tile-Zeile. Eine vollständige 32×32-Karte benötigt 1024 Bytes. Oben links liegt bei
64*y + 2*x; die vier relativen Offsets sind0,1,32,33.x, yKoordinaten in 16×16-Pixel-Zellen, jeweils von 0 bis 15 bei einem vollständigen 32×32-Tile-Puffer. Sie unterscheiden sich von den 8×8-Tile-Koordinaten der Einzelzugriffe.
tileTile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.
attrCGB-Attributbyte: Bits 0–2 wählen Palette 0–7, Bit 3 die Tile-Datenbank, Bits 5 und 6 die horizontale und vertikale Spiegelung, Bit 7 die BG-/Fensterpriorität. Das unbenutzte Bit 4 bleibt null.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Die Zelle zeigt oben AB und unten CD, beginnend bei Tile (2,2): eine 16×16-Pixel-Zelle bei Zellkoordinaten (1,1). Pufferbeispiele prüfen zunächst, dass die RAM-Vorbereitung die VRAM-Daten nicht verändert, und übertragen dann die Zelle. Transferbeispiele setzen die RAM-Bytes vorher ausdrücklich.
Auf CGB sind alle vier Zeichen blau, da alle vier Attributbytes 2 sind. Auf DMG sind sie schwarz.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=2), entsprechend Pixel (16,16). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
AB
CD

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Überträgt zwei benachbarte Tile-Zeilen aus dem RAM-Puffer in die durch LCDC gewählte BG-Karte. Der Quelloffset ist 64*y + x; aus dieser und der 32 Bytes tiefer liegenden Zeile werden jeweils count Bytes kopiert.
Schreibt Tile-Nummer und CGB-Attribut. Auf DMG wird nur die Nummer geschrieben. Auf CGB muss vorher Bank 0 gewählt sein; Attribute verwenden Bank 1, danach gilt VBK=0.
Wartet bei Bedarf auf beschreibbares VRAM. Bei aktivem LCD werden Interrupts gesperrt und anschließend möglicherweise aktiviert. Weder der vorherige Interruptzustand noch ein Abschluss innerhalb eines VBlank wird garantiert.
Syntax
__settilebg16cgb_flush(tilebuf, attrbuf, y, x, count);Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Parameter
tilebuf, attrbufLesbarer Kartenpuffer mit 32 Bytes Zeilenabstand. Eine vollständige Karte belegt 1024 Bytes. Die Variante mit zwei Puffern erhält getrennte Arrays für Tile-Nummern und Attribute mit gleichem Aufbau, keine verschachtelten Paare.
y, x, countDie Reihenfolge lautet
y,x,count.ybezeichnet eine 16-Pixel-Zellzeile (0–15),xundcountzählen 8-Pixel-Tiles. Es mussx+count <= 32gelten. Für eine Zelle bei(2,3)verwenden Siey=3,x=4,count=2.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Die Zelle zeigt oben AB und unten CD, beginnend bei Tile (2,2): eine 16×16-Pixel-Zelle bei Zellkoordinaten (1,1). Pufferbeispiele prüfen zunächst, dass die RAM-Vorbereitung die VRAM-Daten nicht verändert, und übertragen dann die Zelle. Transferbeispiele setzen die RAM-Bytes vorher ausdrücklich.
Auf CGB sind alle vier Zeichen blau, da alle vier Attributbytes 2 sind. Auf DMG sind sie schwarz.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=2), entsprechend Pixel (16,16). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
AB
CD

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Berechnet eine vorzeichenbehaftete Multiplikation mit Akkumulation.
Syntax
u16 __smac16(u16 acc, u16 a, u8 b);Parameter
accZu addierender 16-Bit-Akkumulator.
aVorzeichenbehaftetes 16-Bit-Bitmuster für −32768..32767.
bVorzeichenbehaftetes Byte-Bitmuster für einen Koeffizienten von −128..127.
Rückgabewert
Jeder Term ist trunc(a*b/256) und wird zu acc addiert; zurückgegeben werden die unteren 16 Bits.
Bedingungen und Grenzen
Auf dem GB werden Produkte durch 256 geteilt; vorzeichenbehaftete Ergebnisse werden gegen null abgeschnitten. Q1.7-Varianten verdoppeln diesen abgeschnittenen Wert und können um eins von der exakten Division durch 128 abweichen. Die Terme werden einzeln gerundet; die abschließende Addition läuft bei 16 Bit um.
Anwendungsbeispiel
// 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);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Vergleichen Sie Literalwerte mit im RAM gespeicherten Werten. Repräsentative Ergebnisse erscheinen als vorzeichenlose 16-Bit-Werte; negative Werte nutzen Zweierkomplement-Bitmuster. Der GB skaliert Festkommaprodukte, der FC liefert Ganzzahlprodukte. Gleiche Eingaben müssen daher nicht gleiche Ergebnisse liefern.
Der Ausschnitt speichert folgende 16-Bit-Werte: result[26]=92, result[27]=92, result[28]=65372, result[29]=65372. Jede Berechnung mit Literalwerten stimmt mit der entsprechenden Berechnung aus RAM-Werten überein. Der Bildschirm zeigt repräsentative Werte; das Array result enthält alle Ergebnisse.


Vollständiges Beispielprogramm
Build-Befehl
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=noneImplementierung und Quelltext
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
Berechnet eine vorzeichenbehaftete Multiplikation mit Akkumulation.
Syntax
u16 __smac16_q1_7(u16 acc, u16 a, u8 b);Parameter
accZu addierender 16-Bit-Akkumulator.
aVorzeichenbehaftetes 16-Bit-Bitmuster für −32768..32767.
bVorzeichenbehaftetes Byte-Bitmuster für einen Koeffizienten von −128..127.
Rückgabewert
Jeder Term ist 2*trunc(a*b/256) und wird zu acc addiert; zurückgegeben werden die unteren 16 Bits.
Bedingungen und Grenzen
Auf dem GB werden Produkte durch 256 geteilt; vorzeichenbehaftete Ergebnisse werden gegen null abgeschnitten. Q1.7-Varianten verdoppeln diesen abgeschnittenen Wert und können um eins von der exakten Division durch 128 abweichen. Die Terme werden einzeln gerundet; die abschließende Addition läuft bei 16 Bit um.
Anwendungsbeispiel
// 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);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Vergleichen Sie Literalwerte mit im RAM gespeicherten Werten. Repräsentative Ergebnisse erscheinen als vorzeichenlose 16-Bit-Werte; negative Werte nutzen Zweierkomplement-Bitmuster. Der GB skaliert Festkommaprodukte, der FC liefert Ganzzahlprodukte. Gleiche Eingaben müssen daher nicht gleiche Ergebnisse liefern.
Der Ausschnitt speichert folgende 16-Bit-Werte: result[0]=220, result[1]=220, result[2]=65244, result[3]=65244. Jede Berechnung mit Literalwerten stimmt mit der entsprechenden Berechnung aus RAM-Werten überein. Der Bildschirm zeigt repräsentative Werte; das Array result enthält alle Ergebnisse.


Vollständiges Beispielprogramm
Build-Befehl
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=noneImplementierung und Quelltext
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
Berechnet eine vorzeichenbehaftete Multiplikation.
Syntax
u16 __smul16x8_q1_7(u16 a, u8 b);Parameter
aVorzeichenbehaftetes 16-Bit-Bitmuster für −32768..32767.
bVorzeichenbehaftetes Byte-Bitmuster für einen Koeffizienten von −128..127.
Rückgabewert
Jeder Term ist 2*trunc(a*b/256); zurückgegeben werden die unteren 16 Bits.
Bedingungen und Grenzen
Auf dem GB werden Produkte durch 256 geteilt; vorzeichenbehaftete Ergebnisse werden gegen null abgeschnitten. Q1.7-Varianten verdoppeln diesen abgeschnittenen Wert und können um eins von der exakten Division durch 128 abweichen. Die Terme werden einzeln gerundet; die abschließende Addition läuft bei 16 Bit um.
Anwendungsbeispiel
// 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);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Vergleichen Sie Literalwerte mit im RAM gespeicherten Werten. Repräsentative Ergebnisse erscheinen als vorzeichenlose 16-Bit-Werte; negative Werte nutzen Zweierkomplement-Bitmuster. Der GB skaliert Festkommaprodukte, der FC liefert Ganzzahlprodukte. Gleiche Eingaben müssen daher nicht gleiche Ergebnisse liefern.
Der Ausschnitt speichert folgende 16-Bit-Werte: result[12]=256, result[13]=256, result[14]=65280, result[15]=65280. Jede Berechnung mit Literalwerten stimmt mit der entsprechenden Berechnung aus RAM-Werten überein. Der Bildschirm zeigt repräsentative Werte; das Array result enthält alle Ergebnisse.


Vollständiges Beispielprogramm
Build-Befehl
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=noneImplementierung und Quelltext
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
Liest ein SRAM-Byte an einer CPU-Adresse.
Syntax
u8 __sram_read8(u16 address);Parameter
addressCPU-Adresse im Bereich $A000..$BFFF.
Rückgabewert
Gibt das Byte zurück, ohne das Vorhandensein von RAM oder die Datengültigkeit zu prüfen.
Bedingungen und Grenzen
Schreibt vor dem Zugriff 0x0A nach $0000 zum Aktivieren des RAM und danach null zum Deaktivieren. Wählt keine Bank und prüft die Adresse nicht; der aufrufende Code konfiguriert die RAM-Bank. Spielstandheader und Prüfsummen werden nicht verwaltet.
Anwendungsbeispiel
*((u8*)0x4000)=0;result[12]=__sram_read8(0xA006);result[19]=*((u8*)0xA006);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Speichern Sie drei Bytes, 7/42/255, und prüfen Sie falsche Länge, ungültigen Slot und beschädigte Prüfsumme. Erfolgreiches Laden ergibt das mittlere Byte 42; fehlgeschlagenes Lesen erhält Zielwert 77; die Prüfung nach dem Löschen liefert 0.


Vollständiges Beispielprogramm
Build-Befehl
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=8kImplementierung und Quelltext
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
Schreibt ein SRAM-Byte an eine CPU-Adresse.
Syntax
void __sram_write8(u16 address, u8 value);Parameter
addressCPU-Adresse im Bereich $A000..$BFFF.
valueByte-Wert 0..255.
Rückgabewert
Kein Rückgabewert.
Bedingungen und Grenzen
Schreibt vor dem Zugriff 0x0A nach $0000 zum Aktivieren des RAM und danach null zum Deaktivieren. Wählt keine Bank und prüft die Adresse nicht; der aufrufende Code konfiguriert die RAM-Bank. Spielstandheader und Prüfsummen werden nicht verwaltet.
Anwendungsbeispiel
__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];Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Speichern Sie drei Bytes, 7/42/255, und prüfen Sie falsche Länge, ungültigen Slot und beschädigte Prüfsumme. Erfolgreiches Laden ergibt das mittlere Byte 42; fehlgeschlagenes Lesen erhält Zielwert 77; die Prüfung nach dem Löschen liefert 0.


Vollständiges Beispielprogramm
Build-Befehl
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=8kImplementierung und Quelltext
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
Liest die wirksame WRAM-Bank in $D000–$DFFF. Der Compiler liest SVBK-Bits 0–2 und normalisiert Selektor 0 zu Bank 1. Liefert eine Banknummer, keine RAM-Kapazität, Adresse oder Zeiger.
Syntax
u8 __svbk_get();Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Rückgabewert
Aktuelle wirksame Bank als u8, 1–7; Selektor 0 wird als 1 gemeldet.
Bedingungen und Grenzen
__svbk_get und __svbk_set benötigen --cgb=cgb_only oder #pragma rom_cgb cgb_only. DMG- und duale Ziele lehnen sie mit KQ2103 ab, auch wenn eine Laufzeitbedingung den Aufruf überspringen würde. Den geschützten Rohsetter nur verwenden, wenn dessen anderer Vertrag zum Programm passt.
Ein Bankwechsel betrifft alle Zugriffe in $D000–$DFFF; festes WRAM in $C000–$CFFF bleibt unverändert. Stack und währenddessen benötigter Zustand müssen außerhalb des Bankfensters liegen. Das Beispiel verwendet --stack-bank=fixed und ausdrücklich im festen WRAM platzierte Variablen. Interrupts und bankabhängige DMA-Zugriffe selbst koordinieren; diese APIs erzeugen keinen kritischen Abschnitt.
Anwendungsbeispiel
if (__svbk_get()!=1) failures++; // Normalize raw selector zero to effective bank 1.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das reine CGB-Programm speichert 1 bei $D100 in WRAM-Bank 2 und 2 an derselben Adresse in Bank 3, liest beide Werte und stellt Bank 1 wieder her. Sie wählen ein rotes 8 × 8-Quadrat bei (16,24) und einen blauen 8 × 8-Rahmen mit einem Pixel Strichstärke bei (16,48). FAILED CHECKS 000 prüft Normalisierung, Banktrennung und Rückgabe der vorherigen Bank bei Konstanten, Variablen, Ausdrücken und Funktionsrückgaben. Sechs ungültige Builds prüfen KQ2103 für DMG/duale Ziele und KQ2102 für konstante Banken 0 und 8. Ausführung in KOKURA; echte Hardware und nebenläufige Bankwechsel per Interrupt ungetestet.

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Wechselt die WRAM-Bank in $D000–$DFFF und liefert die zuvor wirksame Bank zurück. Den Rückgabewert zur Wiederherstellung der aufrufenden Bank aufbewahren. Speicher wird weder kopiert noch gelöscht; die Bank wird nicht automatisch zurückgeschaltet.
Syntax
u8 __svbk_set(u8 bank);Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Parameter
bankGewünschte Bank als
u8, 1–7. Konstanten außerhalb dieses Bereichs erzeugen KQ2102. Dynamische Werte prüft dieses Intrinsic nicht; vor dem Aufruf validieren, statt sich auf Registermaskierung zu verlassen.
Rückgabewert
Wirksame Bank vor dem Wechsel als u8, 1–7. Gemeint ist die vorherige, nicht die neu gewählte Bank.
Bedingungen und Grenzen
__svbk_get und __svbk_set benötigen --cgb=cgb_only oder #pragma rom_cgb cgb_only. DMG- und duale Ziele lehnen sie mit KQ2103 ab, auch wenn eine Laufzeitbedingung den Aufruf überspringen würde. Den geschützten Rohsetter nur verwenden, wenn dessen anderer Vertrag zum Programm passt.
Ein Bankwechsel betrifft alle Zugriffe in $D000–$DFFF; festes WRAM in $C000–$CFFF bleibt unverändert. Stack und währenddessen benötigter Zustand müssen außerhalb des Bankfensters liegen. Das Beispiel verwendet --stack-bank=fixed und ausdrücklich im festen WRAM platzierte Variablen. Interrupts und bankabhängige DMA-Zugriffe selbst koordinieren; diese APIs erzeugen keinen kritischen Abschnitt.
Anwendungsbeispiel
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.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das reine CGB-Programm speichert 1 bei $D100 in WRAM-Bank 2 und 2 an derselben Adresse in Bank 3, liest beide Werte und stellt Bank 1 wieder her. Sie wählen ein rotes 8 × 8-Quadrat bei (16,24) und einen blauen 8 × 8-Rahmen mit einem Pixel Strichstärke bei (16,48). FAILED CHECKS 000 prüft Normalisierung, Banktrennung und Rückgabe der vorherigen Bank bei Konstanten, Variablen, Ausdrücken und Funktionsrückgaben. Sechs ungültige Builds prüfen KQ2103 für DMG/duale Ziele und KQ2102 für konstante Banken 0 und 8. Ausführung in KOKURA; echte Hardware und nebenläufige Bankwechsel per Interrupt ungetestet.

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Kopiert sofort len Bytes von src nach dst in aufsteigender Adressreihenfolge. Kehrt nach Abschluss zurück und reiht keine Arbeit ein. Beide Adressen steigen jeweils um ein Byte; Kartenzeilenabstände oder Grafikformatumwandlungen werden nicht berücksichtigt.
Syntax
void __vram_memcpy_unsafe(u16 dst, const void* src, u16 len);Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Parameter
dstAdresse des ersten Zielbytes, normalerweise im VRAM
0x8000–0x9FFF. Der gesamte Bereich muss gültig sein. Der CGB verwendet die aktuelle VRAM-Bank; im Kartenbereich enthält Bank 0 Kachelnummern und Bank 1 Attribute. Diese Routinen wählen keine Karte anhand von LCDC, führen keinen Zeilenumbruch aus und reservieren kein VRAM.srcZeiger auf die Quellbytes, während des gesamten Aufrufs gültig und korrekt eingeblendet. Daten werden sofort gelesen und nicht für einen späteren Flush gespeichert. Die Quellbank wird nicht automatisch gewechselt. Verwenden Sie nicht überlappende Bereiche; diese Vorwärtskopie bietet keine memmove-Semantik.
lenAnzahl aufeinanderfolgender Bytes von 0 bis 65535, keine Tile-Anzahl. Der gesamte Bereich muss in gültiger Quelle und beschreibbarem Ziel liegen; die API prüft keine Grenzen und verhindert keinen 16-Bit-Adressüberlauf.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Überspringt sämtliche LCD/STAT-Wartephasen und schreibt direkt per CPU. Schalten Sie das LCD aus oder stellen Sie sicher, dass die gesamte Übertragung in ein kontrolliertes Schreibzeitfenster passt. Der Eintritt in VBlank allein macht keine beliebig lange Übertragung sicher. Dieses Intrinsic erzeugt für die Übertragung weder DI noch EI und prüft oder begrenzt keine Bereiche.
Anwendungsbeispiel
// The LCD is off throughout these unchecked writes.
__vram_memcpy_unsafe(0x98C2,strip,computed_length());Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Dieses Beispiel schreibt mit __vram_memcpy_unsafe drei Kartenbytes ab Kachel (2,6), Pixel (16,48). Erwartet werden die Muster [1,2,3] von links nach rechts in einem Bereich von 24 × 8; benachbarte Felder bleiben leer. Das vollständige Programm zeigt jeden geprüften Kopier- oder Füllvorgang in einer eigenen Zeile.
Die mitgelieferten eigenen Grafiken verwenden Kachel 1 für ein ausgefülltes Quadrat von 8 × 8, Kachel 2 für dessen vier linke Spalten und Kachel 3 für ein linksbündiges Dreieck, das je Zeile um ein Pixel von 1 auf 8 wächst. DMG zeigt Schwarz auf Weiß; CGB unterscheidet die Operationen farblich und behält schwarzen Text bei.
Das Quadrat bei Pixel (16,88) muss nach sicheren und ungeprüften Aufrufen mit Länge null erhalten bleiben. Bei aktivem LCD ergänzen sicheres Kopieren und Füllen Quadrate bei (16,96) und (32,96). FAILED CHECKS muss 000 anzeigen. Die Emulatortests vergleichen Pixel und Zähler. Der CGB verwendet hier Bank 0; andere Banken und echte Hardware werden hier nicht geprüft.
Im CGB-Modus wiederholen die Zeilen von MEMCPY bis FILL UNSAFE die Folge Rot, Blau, Grün. Das Quadrat bei ZERO COUNT ist rot. Bei LCD ON ist das linke Quadrat blau und das rechte grün. Text bleibt schwarz. Die Prüfungen kontrollieren diese Farben an jedem gezeichneten Pixel.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Füllt sofort len aufeinanderfolgende Bytes ab dst mit dem Byte value. Wiederholt wird ein Byte, kein vollständiges Kachelmuster. In der Karte wiederholt dies eine Kachelnummer; in Musterdaten verändert es die Bitebenen der Pixel.
Syntax
void __vram_memset_unsafe(u16 dst, u8 value, u16 len);Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.
Parameter
dstAdresse des ersten Zielbytes, normalerweise im VRAM
0x8000–0x9FFF. Der gesamte Bereich muss gültig sein. Der CGB verwendet die aktuelle VRAM-Bank; im Kartenbereich enthält Bank 0 Kachelnummern und Bank 1 Attribute. Diese Routinen wählen keine Karte anhand von LCDC, führen keinen Zeilenumbruch aus und reservieren kein VRAM.valueEin wiederholtes Byte von 0 bis 255. Seine sichtbare Wirkung hängt davon ab, ob das Ziel Tile-Indizes, Pixelebenen oder eine andere Bytestruktur enthält.
lenAnzahl aufeinanderfolgender Bytes von 0 bis 65535, keine Tile-Anzahl. Der gesamte Bereich muss in gültiger Quelle und beschreibbarem Ziel liegen; die API prüft keine Grenzen und verhindert keinen 16-Bit-Adressüberlauf.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Überspringt sämtliche LCD/STAT-Wartephasen und schreibt direkt per CPU. Schalten Sie das LCD aus oder stellen Sie sicher, dass die gesamte Übertragung in ein kontrolliertes Schreibzeitfenster passt. Der Eintritt in VBlank allein macht keine beliebig lange Übertragung sicher. Dieses Intrinsic erzeugt für die Übertragung weder DI noch EI und prüft oder begrenzt keine Bereiche.
Anwendungsbeispiel
__vram_memset_unsafe(0x9942,1,computed_length()); // Safe here because the LCD is off.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Dieses Beispiel füllt mit __vram_memset_unsafe drei Kartenbytes ab Kachel (2,10), Pixel (16,80), mit Kachelnummer 1. Erwartet wird ein ausgefüllter Balken von 24 × 8 mit leeren Nachbarfeldern. Die Anzahl wird aus Variablen oder einem Funktionsergebnis berechnet; alle drei Bytes müssen dennoch den Wert 1 erhalten.
Die mitgelieferten eigenen Grafiken verwenden Kachel 1 für ein ausgefülltes Quadrat von 8 × 8, Kachel 2 für dessen vier linke Spalten und Kachel 3 für ein linksbündiges Dreieck, das je Zeile um ein Pixel von 1 auf 8 wächst. DMG zeigt Schwarz auf Weiß; CGB unterscheidet die Operationen farblich und behält schwarzen Text bei.
Das Quadrat bei Pixel (16,88) muss nach sicheren und ungeprüften Aufrufen mit Länge null erhalten bleiben. Bei aktivem LCD ergänzen sicheres Kopieren und Füllen Quadrate bei (16,96) und (32,96). FAILED CHECKS muss 000 anzeigen. Die Emulatortests vergleichen Pixel und Zähler. Der CGB verwendet hier Bank 0; andere Banken und echte Hardware werden hier nicht geprüft.
Im CGB-Modus wiederholen die Zeilen von MEMCPY bis FILL UNSAFE die Folge Rot, Blau, Grün. Das Quadrat bei ZERO COUNT ist rot. Bei LCD ON ist das linke Quadrat blau und das rechte grün. Text bleibt schwarz. Die Prüfungen kontrollieren diese Farben an jedem gezeichneten Pixel.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Wartet bei laufendem LCD, bis LY der Zielzeile entspricht.
Syntax
void __wait_ly(u8 ly);Parameter
lyRasterzeile 0..153; bei Werten über 153 endet das Warten bei laufendem LCD nicht.
Rückgabewert
Kein Rückgabewert.
Bedingungen und Grenzen
Kehrt auf der Zielzeile sofort zurück; ist sie bereits vorbei, wird bis zum nächsten Frame gewartet. Kehrt auch bei ausgeschaltetem LCD sofort zurück.
Anwendungsbeispiel
M_LCDC=0x91;__wait_ly(80);result[9]=*((u8*)0xFF44);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Lernen Sie Kartenauswahl, Scrolling, Verschachtelung und Rasterzeilenwarten. Die Basisadressen sind 38912/39936, die verschachtelten Tokens 0/1 und die beobachteten Ziel-LY-Werte 80/144. Prüfen Sie auch die Rückkehr aus Warteaufrufen bei ausgeschaltetem LCD.


Vollständiges Beispielprogramm
Build-Befehl
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=noneImplementierung und Quelltext
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 — 2D-Bewegung und Kollisionen
Bietet Boxintegration, AABB-Kontakte und einen optionalen Löser für Oberflächenreaktionen. Körper werden durch Mittelpunkt und halbe Ausdehnungen beschrieben; active=0 schließt einen Arrayplatz aus, inv_mass_q8<=0 macht ihn statisch. KQRect verwendet dagegen linke obere Ecke und Breite/Höhe. Kontakte zwischen Boxen nutzen das restitution-Feld des Körpers nicht; Oberflächenreaktionen verwenden KQSurface2D-Koeffizienten.
Verwenden Sie einheitliche ganzzahlige Einheiten für Positionen, Radien, halbe Ausdehnungen, Geschwindigkeiten und Schwerkraft. Ein Schritt entspricht einem Simulationstakt. Die Beispiele verwenden eine Einheit pro Pixel. Diese Routinen wandeln Positionen weder automatisch in Q8.8 um noch zeichnen sie diese.
Eine Welt nutzt ein vom aufrufenden Code bereitgestelltes Array. Dieser reserviert und besitzt den Speicher für die Körper und hält ihn gültig. Das Initialisieren der Welt initialisiert nicht die Arrayelemente.
__smul16x8
Multipliziert einen vorzeichenbehafteten 16-Bit-Wert mit einem vorzeichenbehafteten 8-Bit-Koeffizienten, teilt durch 256 und schneidet gegen null ab.
Syntax
s16 __smul16x8(s16 a, s8 b);Parameter
aVorzeichenbehafteter 16-Bit-Wert, auf den der Skalierungsfaktor angewendet wird.
bVorzeichenbehafteter Bytekoeffizient in -128..127.
Rückgabewert
Liefert das skalierte Produkt als vorzeichenbehafteten 16-Bit-Wert.
Bedingungen und Grenzen
Der Koeffizient liegt in -128..127, mit 256 als Darstellung von 1,0. Produkte werden für beide Vorzeichen gegen null abgeschnitten: -257*64/256 liefert -64.
Anwendungsbeispiel
result[13]=__smul16x8(-257,64);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Links ist der Anfangszustand zu sehen, rechts der Zustand nach einem Takt. Eine rote 16x16-Box mit Mittelpunkt (24,24) und Geschwindigkeit (4,16) erreicht (28,36); auf dem blauen Boden wird ihre Y-Geschwindigkeit null. Die Panelursprünge liegen bei den Bildschirmkoordinaten (8,48) und (88,48). Auf dem DMG sind alle Markierungen schwarz. RAM-Prüfungen decken außerdem Initialisierung, Setzfunktionen, Integration, Rechteck-/Punktabfragen und vorzeichenbehaftete Skalierung ab.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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 — Zufallszahlen, Text, Karten, Spielstände und RPG-/ADV-/SLG-Funktionen
Die Texthelfer mit absoluten Positionen zeichnen direkt auf die Hintergrundkarte. text_window öffnet einen umrahmten Bereich, text_clear_rect füllt eine Region und text_print_xy schreibt Tile-Bytes ohne Steuercodeauswertung. text_print_u8, text_print_u16 und text_print_s16 wandeln Zahlen in Dezimaldarstellung um und belegen nur die nötigen Zellen. Reservieren und löschen Sie beim Ersetzen unterschiedlich langer Werte das gesamte Feld.
Die in rpg.h deklarierten RNG-Funktionen sind in lib/rng.c implementiert. Sie teilen den GB-Generator des Compilers: rng_seed setzt ihn zurück, rng8/rng_next8 verbrauchen ein Byte, rng16/rng_next16 zwei, und rand_range/rng_range, rng_chance sowie weighted_choice passen Ziehungen an Spielentscheidungen an. Kompilieren Sie rng.c mit dem Programm, wenn Sie diese Funktionen aufrufen. Reproduzierbare Tests benötigen festen Startwert und Aufrufreihenfolge; modulo-basierte Auswahlen garantieren keine exakten Wahrscheinlichkeiten.
RLE-Decodierung verwendet count/value-Bytepaare und count null als Abschluss. rle_decode liest einen normalen Zeiger und schreibt in RAM; rle_decode_far liest einen Zeiger mit ROM-Bankangabe nach RAM; __rle_decode_vram entpackt Bankdaten mit LCD-gerechten Schreibzugriffen direkt in VRAM. Die RAM-Funktionen liegen in lib/rle.c, die direkte VRAM-Operation wird vom Compiler erzeugt. Alle drei liefern die Anzahl ausgegebener Bytes und benötigen einen gültigen Strom sowie genügend Zielspeicher.
Den in rpg.h deklarierten Spielzustandsspeicher implementiert lib/flags.c. Die drei Flagfunktionen arbeiten auf 2048 unabhängigen booleschen Bits; quest_state und quest_set_state auf 64 unabhängigen Bytes für spieldefinierte Stufen. Kompilieren Sie flags.c bei der Nutzung, beachten Sie die ID-Grenzen und verwenden Sie ein separates Speichersystem, wenn Fortschritt das Ausschalten überdauern soll.
Fensterdialoge teilen sich ein aktives Rechteck, einen Cursor und eine Zeichengeschwindigkeit. text_open initialisiert den Bereich, text_close löscht ihn und text_set_speed setzt VBlank-Warten je Zeichen. text_print und text_print_far interpretieren Steuerströme; text_choice führt eine blockierende vertikale Auswahl aus. Laden Sie Kacheln, dimensionieren Sie den Bereich und planen Sie den Umgang mit blockierenden Aufrufen im Spiel.
Bietet Karten-/Metakachelzeichnung, Skripte, SRAM-Spielstände und Wegsuche. Gewöhnliche Zellen sind 8×8 Pixel groß, Metakacheln 16×16. Laden/Zeichnen, Bewegungs- und Angriffsmasken sind getrennte Operationen; das Spiel koordiniert Grafikladen und Ereignisausführung.
Die Suche verwendet etwa 4 KiB gemeinsamen, nicht reentranten Arbeitsspeicher. Begrenzen Sie Karten auf 1024 Zellen und untersuchte Entfernungen auf 254. Distanz 0xFF bedeutet „nicht erreicht“; eine Route mit 255 Schritten ist daher nicht darstellbar. Kosten- und Maskenausgaben benötigen width*height beschreibbare Bytes.
Standardmäßig gibt es vier 512-Byte-Slots mit je sechs Headerbytes und bis zu 506 Nutzdatenbytes. Das Beispiel verwendet MBC5 mit 8 KiB SRAM. Aufrufe enden mit deaktiviertem RAM, ohne die vorherige Bank oder den Aktivierungszustand wiederherzustellen.
Menüs unterstützen sowohl eine blockierende Auswahl als auch getrennte Eingabe- und Zeichenaufrufe aus der Spielschleife. Das Inventarmenü zeigt Gegenstandsnamen und Mengen an.
__bit_clear
Löscht das gewählte Flag auf 0, ohne den Rest des Bytes zu löschen. Entfernen Sie so eine gespeicherte Bedingung, etwa einen vorübergehenden Status. Erneutes Löschen desselben Bits lässt es bei 0.
Syntax
void __bit_clear(u8* p, u16 bit_index);Parameter
pZeiger auf das erste Byte des Flagarrays in CPU-zugänglichem Speicher. Der Aufruf reserviert und initialisiert das Array nicht. Verwenden Sie beschreibbaren RAM zum Setzen, Löschen und Umschalten; die Prüfung liest nur.
bit_indexBei null beginnender Bitindex als
u16(0–65535), kein Byte-Offset und keine Bitmaske. Index 0 wählt Maske0x01in Byte 0, Index 7 wählt0x80in Byte 0 und Index 8 wählt0x01in Byte 1. Allgemein ist der Byte-Offsetindex / 8und die Maske1 << (index % 8). Für ein N-Byte-Array müssenindex < 8*Nund ein gültiger CPU-Adressbereich gelten.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Der aufrufende Code muss genügend Speicher bereitstellen. Es gibt weder Längenargument noch Bereichsbegrenzung, Bankwechsel oder automatische Grafikübertragung. Eine große Bitnummer kann ein Byte weit außerhalb des reservierten Arrays auswählen. Halten Sie die Speicherzuordnung stabil; verwenden Sie diese RAM-Flagfunktionen nicht als Abkürzung für Hardwareregister mit Nebenwirkungen beim Lesen oder Schreiben.
Setzen, Löschen und Umschalten lesen ein Byte, ändern das gewählte Bit und schreiben das Byte zurück. Diese Folge ist nicht atomar. Koordinieren Sie Schreibzugriffe von Hauptschleife und Interrupt-Handlern, damit eine zwischenzeitliche Änderung an einem anderen Bit desselben Bytes nicht verloren geht.
Anwendungsbeispiel
__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.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das vollständige Programm erreicht diesen Schritt mit Byte 0 gleich 0xA6. Löschen von Bit 2 ergibt 0xA2 (binär 10100010, dezimal 162); CLEAR BIT2 muss 162 zeigen. Erneutes Löschen von Bit 2 ändert nichts. Die anderen gespeicherten Flags bleiben bei beiden Aufrufen erhalten.
Erwartete GB-Zeilen in Dezimaldarstellung: TEST OFF 000, TEST ON 001, SET BIT1 166, CLEAR BIT2 162, TOGGLE BIT7 034, RESTORE 162, NEXT BYTE 130, FAILED 000. NEXT BYTE entsteht durch Setzen von Index 9 im zweiten Byte: 0x80 wird 0x82, Byte 0 bleibt unverändert. DMG zeigt schwarze Schrift auf Weiß, CGB blaue Schrift auf Weiß.
Das Programm prüft jeden angezeigten Zustandswechsel und zeigt die Fehlerzahl. Setzen, Löschen und Umschalten liefern selbst keinen Wert; die Zeilen zeigen das resultierende RAM-Byte. Separate Emulatorprüfungen vergleichen alle 520 Bytes geschützter Puffer in 136 Fällen je Plattform: konstante und laufzeitabhängige Indizes über Byte- und 256-Byte-Grenzen sowie alle acht möglichen Bitprüfmasken ungleich null. Dies sind RAM-Zustandsprüfungen, keine Zeichendemonstration. Physische Hardware und gleichzeitige Interrupt-Schreibzugriffe wurden nicht getestet.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Setzt das gewählte Flag auf 1 und erhält die übrigen sieben Bits desselben Bytes. Markieren Sie damit etwa einen erledigten Auftrag oder einen eingesammelten Gegenstand. Ein bereits gesetztes Bit bleibt unverändert; dies ist kein Umschalten.
Syntax
void __bit_set(u8* p, u16 bit_index);Parameter
pZeiger auf das erste Byte des Flagarrays in CPU-zugänglichem Speicher. Der Aufruf reserviert und initialisiert das Array nicht. Verwenden Sie beschreibbaren RAM zum Setzen, Löschen und Umschalten; die Prüfung liest nur.
bit_indexBei null beginnender Bitindex als
u16(0–65535), kein Byte-Offset und keine Bitmaske. Index 0 wählt Maske0x01in Byte 0, Index 7 wählt0x80in Byte 0 und Index 8 wählt0x01in Byte 1. Allgemein ist der Byte-Offsetindex / 8und die Maske1 << (index % 8). Für ein N-Byte-Array müssenindex < 8*Nund ein gültiger CPU-Adressbereich gelten.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Der aufrufende Code muss genügend Speicher bereitstellen. Es gibt weder Längenargument noch Bereichsbegrenzung, Bankwechsel oder automatische Grafikübertragung. Eine große Bitnummer kann ein Byte weit außerhalb des reservierten Arrays auswählen. Halten Sie die Speicherzuordnung stabil; verwenden Sie diese RAM-Flagfunktionen nicht als Abkürzung für Hardwareregister mit Nebenwirkungen beim Lesen oder Schreiben.
Setzen, Löschen und Umschalten lesen ein Byte, ändern das gewählte Bit und schreiben das Byte zurück. Diese Folge ist nicht atomar. Koordinieren Sie Schreibzugriffe von Hauptschleife und Interrupt-Handlern, damit eine zwischenzeitliche Änderung an einem anderen Bit desselben Bytes nicht verloren geht.
Anwendungsbeispiel
__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.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Setzen Sie Bit 1 von 0xA4: 10100100 | 00000010 ergibt 10100110, also 0xA6 (dezimal 166). SET BIT1 muss 166 zeigen. Das Beispiel setzt das Bit erneut und prüft, dass das Byte 0xA6 bleibt; wiederholtes Setzen invertiert das Flag also nicht.
Erwartete GB-Zeilen in Dezimaldarstellung: TEST OFF 000, TEST ON 001, SET BIT1 166, CLEAR BIT2 162, TOGGLE BIT7 034, RESTORE 162, NEXT BYTE 130, FAILED 000. NEXT BYTE entsteht durch Setzen von Index 9 im zweiten Byte: 0x80 wird 0x82, Byte 0 bleibt unverändert. DMG zeigt schwarze Schrift auf Weiß, CGB blaue Schrift auf Weiß.
Das Programm prüft jeden angezeigten Zustandswechsel und zeigt die Fehlerzahl. Setzen, Löschen und Umschalten liefern selbst keinen Wert; die Zeilen zeigen das resultierende RAM-Byte. Separate Emulatorprüfungen vergleichen alle 520 Bytes geschützter Puffer in 136 Fällen je Plattform: konstante und laufzeitabhängige Indizes über Byte- und 256-Byte-Grenzen sowie alle acht möglichen Bitprüfmasken ungleich null. Dies sind RAM-Zustandsprüfungen, keine Zeichendemonstration. Physische Hardware und gleichzeitige Interrupt-Schreibzugriffe wurden nicht getestet.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Liest ein Flag aus einem gepackten Bitarray, ohne das Array zu ändern. So teilen sich acht unabhängige Ein/Aus-Flags ein RAM-Byte. Das Ergebnis zeigt, ob das gewählte Bit gelöscht oder gesetzt ist; seine numerische Darstellung hängt vom Zielsystem ab.
Syntax
u8 __bit_test(const u8* p, u16 bit_index);Parameter
pZeiger auf das erste Byte des Flagarrays in CPU-zugänglichem Speicher. Der Aufruf reserviert und initialisiert das Array nicht. Verwenden Sie beschreibbaren RAM zum Setzen, Löschen und Umschalten; die Prüfung liest nur.
bit_indexBei null beginnender Bitindex als
u16(0–65535), kein Byte-Offset und keine Bitmaske. Index 0 wählt Maske0x01in Byte 0, Index 7 wählt0x80in Byte 0 und Index 8 wählt0x01in Byte 1. Allgemein ist der Byte-Offsetindex / 8und die Maske1 << (index % 8). Für ein N-Byte-Array müssenindex < 8*Nund ein gültiger CPU-Adressbereich gelten.
Rückgabewert
u8: exakt 0 bei gelöschtem Bit oder 1 bei gesetztem Bit. Die GB-Implementierung normalisiert das maskierte Byte auf diese beiden Werte.
Bedingungen und Grenzen
Der aufrufende Code muss genügend Speicher bereitstellen. Es gibt weder Längenargument noch Bereichsbegrenzung, Bankwechsel oder automatische Grafikübertragung. Eine große Bitnummer kann ein Byte weit außerhalb des reservierten Arrays auswählen. Halten Sie die Speicherzuordnung stabil; verwenden Sie diese RAM-Flagfunktionen nicht als Abkürzung für Hardwareregister mit Nebenwirkungen beim Lesen oder Schreiben.
Anwendungsbeispiel
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.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Beginnen Sie mit Byte 0 gleich 0xA4 (binär 10100100). Die Prüfung von Bit 1 liefert 0, die von Bit 7 auf GB 1 und auf FC 128. Das Beispiel wandelt den Wert ungleich null außerdem in ein gemeinsames Ein/Aus-Flag um und prüft das unveränderte Originalbyte. Beachten Sie TEST OFF und TEST ON im Bild unten.
Erwartete GB-Zeilen in Dezimaldarstellung: TEST OFF 000, TEST ON 001, SET BIT1 166, CLEAR BIT2 162, TOGGLE BIT7 034, RESTORE 162, NEXT BYTE 130, FAILED 000. NEXT BYTE entsteht durch Setzen von Index 9 im zweiten Byte: 0x80 wird 0x82, Byte 0 bleibt unverändert. DMG zeigt schwarze Schrift auf Weiß, CGB blaue Schrift auf Weiß.
Das Programm prüft jeden angezeigten Zustandswechsel und zeigt die Fehlerzahl. Setzen, Löschen und Umschalten liefern selbst keinen Wert; die Zeilen zeigen das resultierende RAM-Byte. Separate Emulatorprüfungen vergleichen alle 520 Bytes geschützter Puffer in 136 Fällen je Plattform: konstante und laufzeitabhängige Indizes über Byte- und 256-Byte-Grenzen sowie alle acht möglichen Bitprüfmasken ungleich null. Dies sind RAM-Zustandsprüfungen, keine Zeichendemonstration. Physische Hardware und gleichzeitige Interrupt-Schreibzugriffe wurden nicht getestet.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Invertiert das gewählte Flag: 0 wird 1 und 1 wird 0. Die übrigen Bits bleiben erhalten. Zwei Aufrufe auf dasselbe Bit stellen den ursprünglichen Zustand wieder her, sofern dazwischen nichts anderes dieses Bit schreibt.
Syntax
void __bit_toggle(u8* p, u16 bit_index);Parameter
pZeiger auf das erste Byte des Flagarrays in CPU-zugänglichem Speicher. Der Aufruf reserviert und initialisiert das Array nicht. Verwenden Sie beschreibbaren RAM zum Setzen, Löschen und Umschalten; die Prüfung liest nur.
bit_indexBei null beginnender Bitindex als
u16(0–65535), kein Byte-Offset und keine Bitmaske. Index 0 wählt Maske0x01in Byte 0, Index 7 wählt0x80in Byte 0 und Index 8 wählt0x01in Byte 1. Allgemein ist der Byte-Offsetindex / 8und die Maske1 << (index % 8). Für ein N-Byte-Array müssenindex < 8*Nund ein gültiger CPU-Adressbereich gelten.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Der aufrufende Code muss genügend Speicher bereitstellen. Es gibt weder Längenargument noch Bereichsbegrenzung, Bankwechsel oder automatische Grafikübertragung. Eine große Bitnummer kann ein Byte weit außerhalb des reservierten Arrays auswählen. Halten Sie die Speicherzuordnung stabil; verwenden Sie diese RAM-Flagfunktionen nicht als Abkürzung für Hardwareregister mit Nebenwirkungen beim Lesen oder Schreiben.
Setzen, Löschen und Umschalten lesen ein Byte, ändern das gewählte Bit und schreiben das Byte zurück. Diese Folge ist nicht atomar. Koordinieren Sie Schreibzugriffe von Hauptschleife und Interrupt-Handlern, damit eine zwischenzeitliche Änderung an einem anderen Bit desselben Bytes nicht verloren geht.
Anwendungsbeispiel
__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.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das vollständige Programm beginnt diesen Schritt mit 0xA2. Umschalten von Bit 7 ergibt 0x22 (dezimal 34), also zeigt TOGGLE BIT7 034. Erneutes Umschalten stellt 0xA2 wieder her; RESTORE zeigt 162. Dies belegt beide Richtungen der Invertierung bei Erhalt der übrigen Bits.
Erwartete GB-Zeilen in Dezimaldarstellung: TEST OFF 000, TEST ON 001, SET BIT1 166, CLEAR BIT2 162, TOGGLE BIT7 034, RESTORE 162, NEXT BYTE 130, FAILED 000. NEXT BYTE entsteht durch Setzen von Index 9 im zweiten Byte: 0x80 wird 0x82, Byte 0 bleibt unverändert. DMG zeigt schwarze Schrift auf Weiß, CGB blaue Schrift auf Weiß.
Das Programm prüft jeden angezeigten Zustandswechsel und zeigt die Fehlerzahl. Setzen, Löschen und Umschalten liefern selbst keinen Wert; die Zeilen zeigen das resultierende RAM-Byte. Separate Emulatorprüfungen vergleichen alle 520 Bytes geschützter Puffer in 136 Fällen je Plattform: konstante und laufzeitabhängige Indizes über Byte- und 256-Byte-Grenzen sowie alle acht möglichen Bitprüfmasken ungleich null. Dies sind RAM-Zustandsprüfungen, keine Zeichendemonstration. Physische Hardware und gleichzeitige Interrupt-Schreibzugriffe wurden nicht getestet.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Kopiert genau 16 Bytes von src nach dst, ohne Längenargument. Damit lässt sich etwa ein 16-Byte-Datensatz oder eine GB/NES-Kachel im CPU-zugänglichen Speicher kopieren. 16 bezeichnet Bytes, nicht Bits; Grafikdaten werden dadurch nicht an die Anzeige übertragen.
Syntax
void __copy16(void* dst, const void* src);Parameter
dstZeiger auf das erste beschreibbare Zielbyte. Stellen Sie genügend zusammenhängenden Speicher für die gesamte Operation bereit; ein Puffer wird nicht automatisch angelegt.
srcZeiger auf das erste lesbare Quellbyte. Der gesamte Bereich muss während der Kopie zugänglich und unverändert bleiben.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Verwenden Sie Quell- und Zielbereiche, die sich nicht überlappen. Diese Routinen kopieren vorwärts und bieten nicht das überlappungssichere Verhalten von memmove. Schreibzugriffe auf noch ungelesene Quellbytes können die spätere Kopie verfälschen.
Die Operation wird vor der Rückkehr abgeschlossen. Sie schaltet keine ROM/RAM-Bänke um und begrenzt den Bereich nicht automatisch auf Puffergrenzen. Halten Sie die Speicherabbildung stabil, verwenden Sie gültige Bereiche und stimmen Sie Zugriffe mit Interrupt-Routinen ab, die dieselben Puffer verwenden.
Auf dem GB sind dies gewöhnliche CPU-Speicherzugriffe. Sie warten nicht auf ein sicheres VRAM/OAM-Zugriffsfenster, starten kein DMA und wählen keine CGB-VRAM/WRAM-Bank aus. Verwenden Sie dafür die speziellen Grafikübertragungs-APIs.
Anwendungsbeispiel
memory_reset();
__copy16(memory_destination+1,memory_source+1);
memory_copy_16=memory_check(16,0); // Exactly 16 bytes, not 16 bits.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel kopiert mit __copy16 ab Offset 1 ein bekanntes Muster von 16 Bytes. memory_check vergleicht die vollständigen Quell- und Zielarrays; die Zeile COPY16 muss 016 anzeigen. Das erste Byte bleibt als Schutzbereich frei. Auch die Bytes nach dem Nutzdatenbereich müssen unverändert bleiben.
Das vollständige Programm initialisiert vor jeder Operation zwei 264-Byte-Puffer und prüft danach jedes Quell- und Zielbyte sowie die unveränderten Schutzbereiche. Erwartet werden COPY WORD 257, FILL WORD 257, COPY BYTE 255, FILL BYTE 255, COPY16 016, COPY32 032, ZERO CALLS 002 und FAILED 000. WORD/BYTE bezeichnen die Zählerbreite, nicht die Größe der kopierten Elemente. Die Zahlen stammen aus den Prüfungen des Beispiels; diese void-Funktionen liefern keine Rückgabewerte.
Die Aufnahmen zeigen schwarze Schrift auf Weiß beim DMG, blaue Schrift auf Weiß beim CGB und blaue Schrift auf Schwarz beim FC. Das Beispiel prüft RAM-Operationen; die Schrift stellt die Ergebnisse dar und demonstriert keine Zeichenprimitive. Weitere Emulatorprüfungen decken je Plattform 88 Speicherfälle ab: konstante und zur Laufzeit bestimmte Längen von 0, 1, 16, 17, 32, 33, 64, 65 und 255 Bytes sowie 256 und 257 bei 16-Bit-Zählern. Auf echter Hardware wurde nicht getestet.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Kopiert genau 32 Bytes von src nach dst, ohne Längenargument. Das eignet sich für einen festen 32-Byte-Datensatz oder zwei benachbarte 16-Byte-Kachelmuster im CPU-zugänglichen Speicher. Die Kopie erfolgt byteweise vorwärts, weder als einzelner 32-Bit-Lesezugriff noch mit Bankumschaltung.
Syntax
void __copy32(void* dst, const void* src);Parameter
dstZeiger auf das erste beschreibbare Zielbyte. Stellen Sie genügend zusammenhängenden Speicher für die gesamte Operation bereit; ein Puffer wird nicht automatisch angelegt.
srcZeiger auf das erste lesbare Quellbyte. Der gesamte Bereich muss während der Kopie zugänglich und unverändert bleiben.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Verwenden Sie Quell- und Zielbereiche, die sich nicht überlappen. Diese Routinen kopieren vorwärts und bieten nicht das überlappungssichere Verhalten von memmove. Schreibzugriffe auf noch ungelesene Quellbytes können die spätere Kopie verfälschen.
Die Operation wird vor der Rückkehr abgeschlossen. Sie schaltet keine ROM/RAM-Bänke um und begrenzt den Bereich nicht automatisch auf Puffergrenzen. Halten Sie die Speicherabbildung stabil, verwenden Sie gültige Bereiche und stimmen Sie Zugriffe mit Interrupt-Routinen ab, die dieselben Puffer verwenden.
Auf dem GB sind dies gewöhnliche CPU-Speicherzugriffe. Sie warten nicht auf ein sicheres VRAM/OAM-Zugriffsfenster, starten kein DMA und wählen keine CGB-VRAM/WRAM-Bank aus. Verwenden Sie dafür die speziellen Grafikübertragungs-APIs.
Anwendungsbeispiel
memory_reset();
__copy32(memory_destination+1,memory_source+1);
memory_copy_32=memory_check(32,0);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel kopiert mit __copy32 ab Offset 1 ein bekanntes Muster von 32 Bytes. memory_check vergleicht die vollständigen Quell- und Zielarrays; die Zeile COPY32 muss 032 anzeigen. Das erste Byte bleibt als Schutzbereich frei. Auch die Bytes nach dem Nutzdatenbereich müssen unverändert bleiben.
Das vollständige Programm initialisiert vor jeder Operation zwei 264-Byte-Puffer und prüft danach jedes Quell- und Zielbyte sowie die unveränderten Schutzbereiche. Erwartet werden COPY WORD 257, FILL WORD 257, COPY BYTE 255, FILL BYTE 255, COPY16 016, COPY32 032, ZERO CALLS 002 und FAILED 000. WORD/BYTE bezeichnen die Zählerbreite, nicht die Größe der kopierten Elemente. Die Zahlen stammen aus den Prüfungen des Beispiels; diese void-Funktionen liefern keine Rückgabewerte.
Die Aufnahmen zeigen schwarze Schrift auf Weiß beim DMG, blaue Schrift auf Weiß beim CGB und blaue Schrift auf Schwarz beim FC. Das Beispiel prüft RAM-Operationen; die Schrift stellt die Ergebnisse dar und demonstriert keine Zeichenprimitive. Weitere Emulatorprüfungen decken je Plattform 88 Speicherfälle ab: konstante und zur Laufzeit bestimmte Längen von 0, 1, 16, 17, 32, 33, 64, 65 und 255 Bytes sowie 256 und 257 bei 16-Bit-Zählern. Auf echter Hardware wurde nicht getestet.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Wählt vorübergehend eine ROM-Bank, kopiert len Byte mit aufsteigenden Adressen von src nach dst und stellt anschließend die vom Compiler gespeicherte Bank wieder her. Auf dieser Plattform verwenden __farmemcpy und __far_memcpy dieselbe Implementierung.
Syntax
void __far_memcpy(void* dst, u8 bank, const void* src, u16 len);Parameter
dstZiel in beschreibbarem Speicher mit genügend Platz für alle kopierten Byte. Abgesehen von einem Transfer mit Länge null erfolgt keine Prüfung auf Nullzeiger oder ungültige Zieladressen.
bank8-Bit-Bankauswahl für die Quell-ROM-Zuordnung. Die Bank muss zur Cartridge und zur Bankaufteilung des Compilers passen. Sie wird getrennt von der Quelladresse angegeben.
srcNahzeiger auf lesbare Bytes in der gewählten ROM-Bank. Er enthält keine Banknummer.
lenByteanzahl, 0–65535. Bei null wird kein Speicher kopiert; Bankauswahl und Wiederherstellung werden trotzdem ausgeführt.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Der gesamte Quellbereich muss in der vorgesehenen ROM-Zuordnung lesbar und der gesamte Zielbereich beschreibbar sein. Die Kopie schaltet am Fensterende nicht automatisch zur nächsten Bank, behandelt Überlappungen nicht wie memmove und steuert keine sicheren VRAM-Schreibzeitpunkte. Verwenden Sie für VRAM die Grafikübertragungs-APIs.
Die Beispiele legen den aufrufenden Code in die feste Bank 0, damit ein Wechsel der Quellbank den laufenden Code nicht ausblendet. Die Übertragungen sind synchron. Unterbrechen Sie sie nicht durch eine weitere bankwechselnde Kopie. Die Hilfsfunktion allein macht nicht die gesamte Anwendung interruptsicher.
Die GB-Hilfsroutine stellt die vom Compiler gemerkte ROM-Bank wieder her. Halten Sie diesen Zustand mit __bankswitch aktuell; direkte Mapper-Schreibzugriffe aktualisieren ihn nicht. Der HRAM-Speicherplatz für die Rückkehrbank wird gemeinsam genutzt. Verschachtelte Übertragungen dürfen ihn nicht überschreiben.
Anwendungsbeispiel
__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);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Kopiert zwei Muster mit insgesamt 32 Byte aus Bank 2, ausgehend von Bank 1. Das Programm vergleicht jedes Byte, prüft die Wiederherstellung von Bank 1 und den unveränderten Prüfwert 0x55 bei einer Kopie der Länge null.
Erwartetes Bild: Eine rote Reihe beginnt bei (16,32) und belegt 72×8 Pixel. Dreimal folgen ein 8×8-Dreieck, ein 8×8-Quadrat mit einem Pixel Rand und eine Kachel mit vier gefüllten linken Spalten. Das Dreieck wächst von einem Pixel oben links auf acht Pixel in der untersten Zeile.
Auf dem GB zeigen das blaue Dreieck und der Rahmen bei (16,64) das Ergebnis von __farmemcpy, das grüne Paar bei (16,96) das von __far_memcpy. Jedes Paar belegt 16×8 Pixel. Der CGB nutzt diese Farben, der DMG dieselben Formen in Schwarz auf Weiß.
Erwartet werden FAILED CHECKS 000 und FIRST FAILURE 00. Die Prüfung führt 300 Bilder aus und vergleicht Text, Formmasken und Farben mit den Sollwerten. Dies ist ein Emulatornachweis, kein Test auf physischer Hardware.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/rpg.h:148
__fill_tilemap
__fill_tilemap ist ein Compileralias für __vram_memset. Es verwendet dieselbe Byteanzahl und dieselbe sofortige CPU-Übertragung einschließlich der LCD-Zugriffsregeln.
Füllt sofort len aufeinanderfolgende Bytes ab dst mit dem Byte value. Wiederholt wird ein Byte, kein vollständiges Kachelmuster. In der Karte wiederholt dies eine Kachelnummer; in Musterdaten verändert es die Bitebenen der Pixel.
Syntax
void __fill_tilemap(u16 dst, u8 value, u16 len);Parameter
dstAdresse des ersten Zielbytes, normalerweise im VRAM
0x8000–0x9FFF. Der gesamte Bereich muss gültig sein. Der CGB verwendet die aktuelle VRAM-Bank; im Kartenbereich enthält Bank 0 Kachelnummern und Bank 1 Attribute. Diese Routinen wählen keine Karte anhand von LCDC, führen keinen Zeilenumbruch aus und reservieren kein VRAM.valueEin wiederholtes Byte von 0 bis 255. Seine sichtbare Wirkung hängt davon ab, ob das Ziel Tile-Indizes, Pixelebenen oder eine andere Bytestruktur enthält.
lenAnzahl aufeinanderfolgender Bytes von 0 bis 65535, keine Tile-Anzahl. Der gesamte Bereich muss in gültiger Quelle und beschreibbarem Ziel liegen; die API prüft keine Grenzen und verhindert keinen 16-Bit-Adressüberlauf.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Geben Sie Startadresse und Byteanzahl ausdrücklich an. Dieser Alias wählt weder die Hintergrundkarte aus noch füllt er automatisch die ganze Karte. Für eine vollständige Karte mit 32 × 32 Kachelnummern wählen Sie VRAM-Bank 0 und geben die Basisadresse sowie 1024 Bytes an.
Bei eingeschaltetem LCD wartet jeder Bytezugriff unter DI auf STAT-Modus 0 oder 1; danach folgt EI. Bei ausgeschaltetem LCD läuft eine gewöhnliche CPU-Schleife. Der Aufruf kann mehrere Frames dauern, erhält keine anfängliche Interruptsperre und richtet keine DMA-Register ein. Länge null überträgt keine Daten.
Anwendungsbeispiel
__fill_tilemap(0x9922,1,length_base+length_extra); // Explicit address and byte count, not a whole-map clear.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Dieses Beispiel füllt mit __fill_tilemap drei Kartenbytes ab Kachel (2,9), Pixel (16,72), mit Kachelnummer 1. Erwartet wird ein ausgefüllter Balken von 24 × 8 mit leeren Nachbarfeldern. Die Anzahl wird aus Variablen oder einem Funktionsergebnis berechnet; alle drei Bytes müssen dennoch den Wert 1 erhalten.
Die mitgelieferten eigenen Grafiken verwenden Kachel 1 für ein ausgefülltes Quadrat von 8 × 8, Kachel 2 für dessen vier linke Spalten und Kachel 3 für ein linksbündiges Dreieck, das je Zeile um ein Pixel von 1 auf 8 wächst. DMG zeigt Schwarz auf Weiß; CGB unterscheidet die Operationen farblich und behält schwarzen Text bei.
Das Quadrat bei Pixel (16,88) muss nach sicheren und ungeprüften Aufrufen mit Länge null erhalten bleiben. Bei aktivem LCD ergänzen sicheres Kopieren und Füllen Quadrate bei (16,96) und (32,96). FAILED CHECKS muss 000 anzeigen. Die Emulatortests vergleichen Pixel und Zähler. Der CGB verwendet hier Bank 0; andere Banken und echte Hardware werden hier nicht geprüft.
Im CGB-Modus wiederholen die Zeilen von MEMCPY bis FILL UNSAFE die Folge Rot, Blau, Grün. Das Quadrat bei ZERO COUNT ist rot. Bei LCD ON ist das linke Quadrat blau und das rechte grün. Text bleibt schwarz. Die Prüfungen kontrollieren diese Farben an jedem gezeichneten Pixel.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/rpg.h:157
__getbgmapbase
Liest LCDC-Bit 3 zur Auswahl der Basisadresse der Hintergrund-Kachelkarte.
Syntax
u16 __getbgmapbase();Rückgabewert
Gibt $9800 oder $9C00 zurück: die Kachelkartenbasis, nicht die Adresse der Kachelgrafik.
Anwendungsbeispiel
M_LCDC=0;result[0]=__getbgmapbase();M_LCDC=8;result[1]=__getbgmapbase();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Lernen Sie Kartenauswahl, Scrolling, Verschachtelung und Rasterzeilenwarten. Die Basisadressen sind 38912/39936, die verschachtelten Tokens 0/1 und die beobachteten Ziel-LY-Werte 80/144. Prüfen Sie auch die Rückkehr aus Warteaufrufen bei ausgeschaltetem LCD.


Vollständiges Beispielprogramm
Build-Befehl
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=noneImplementierung und Quelltext
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
Addiert die absoluten X- und Y-Differenzen zweier Punkte.
Syntax
u8 __manhattan(u8 x1, u8 y1, u8 x2, u8 y2);Parameter
x1X-Koordinate des ersten Punkts, 0..255.
y1Y-Koordinate des ersten Punkts, 0..255.
x2X-Koordinate des zweiten Punkts, 0..255.
y2Y-Koordinate des zweiten Punkts, 0..255.
Rückgabewert
Gibt das untere Byte von |x1−x2|+|y1−y2| zurück; Distanzen über 255 laufen um.
Anwendungsbeispiel
result[5]=__manhattan(2,3,9,11);result[6]=__manhattan(0,0,255,255);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Prüfen Sie Innenraum, Grenzen, einen äußeren Punkt und Breite null: INSIDE=1, RIGHT EDGE=0, LEFT EDGE=0, HIGH X=1. Prüfen Sie Distanz 15, umgelaufene Distanz 254 und Array-Indizes 64/65280.


Vollständiges Beispielprogramm
Build-Befehl
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=noneImplementierung und Quelltext
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
Wandelt eine Zellkoordinate in einen zeilenweisen Array-Index um.
Syntax
u16 __map_index(u8 x, u8 y, u8 width);Parameter
xSpalte 0..255, gewöhnlich kleiner als width.
yZeile, 0..255.
widthZellen je Zeile, 0..255.
Rückgabewert
Gibt y*width+x als u16 zurück; prüft die Koordinaten nicht.
Anwendungsbeispiel
result[7]=__map_index(4,3,20);result[8]=__map_index(255,255,255);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Prüfen Sie Innenraum, Grenzen, einen äußeren Punkt und Breite null: INSIDE=1, RIGHT EDGE=0, LEFT EDGE=0, HIGH X=1. Prüfen Sie Distanz 15, umgelaufene Distanz 254 und Array-Indizes 64/65280.


Vollständiges Beispielprogramm
Build-Befehl
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=noneImplementierung und Quelltext
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
Kopiert die angegebene Anzahl Bytes von src nach dst. Der 16-Bit-Zähler erlaubt auch Übertragungen mit mehr als 255 Bytes, etwa eine vollständige Tabelle im RAM. Gelesen und geschrieben wird in aufsteigender Adressfolge.
Syntax
void __memcpy(void* dst, const void* src, u16 count);Parameter
dstZeiger auf das erste beschreibbare Zielbyte. Stellen Sie genügend zusammenhängenden Speicher für die gesamte Operation bereit; ein Puffer wird nicht automatisch angelegt.
srcZeiger auf das erste lesbare Quellbyte. Der gesamte Bereich muss während der Kopie zugänglich und unverändert bleiben.
countByteanzahl als
u16(0–65535), keine Elementanzahl. Der Höchstwert des Typs bedeutet nicht, dass auf der Zielmaschine ein gültiger Puffer dieser Größe verfügbar ist. Der gesamte Bereich muss in die gewählte Speicherabbildung passen.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Bei Länge null werden keine Bytes übertragen. Seiteneffekte in den Argumentausdrücken finden trotzdem statt: Das Beispiel übergibt zwei Funktionen, die Zeiger liefern, und bestätigt beide Aufrufe trotz Kopierlänge null.
Verwenden Sie Quell- und Zielbereiche, die sich nicht überlappen. Diese Routinen kopieren vorwärts und bieten nicht das überlappungssichere Verhalten von memmove. Schreibzugriffe auf noch ungelesene Quellbytes können die spätere Kopie verfälschen.
Die Operation wird vor der Rückkehr abgeschlossen. Sie schaltet keine ROM/RAM-Bänke um und begrenzt den Bereich nicht automatisch auf Puffergrenzen. Halten Sie die Speicherabbildung stabil, verwenden Sie gültige Bereiche und stimmen Sie Zugriffe mit Interrupt-Routinen ab, die dieselben Puffer verwenden.
Auf dem GB sind dies gewöhnliche CPU-Speicherzugriffe. Sie warten nicht auf ein sicheres VRAM/OAM-Zugriffsfenster, starten kein DMA und wählen keine CGB-VRAM/WRAM-Bank aus. Verwenden Sie dafür die speziellen Grafikübertragungs-APIs.
Anwendungsbeispiel
memory_reset();
__memcpy(memory_destination+1,memory_source+1,257);
memory_copy_word=memory_check(257,0); // 257 copied bytes; guards unchanged.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel kopiert mit __memcpy ab Offset 1 ein bekanntes Muster von 257 Bytes. memory_check vergleicht die vollständigen Quell- und Zielarrays; die Zeile COPY WORD muss 257 anzeigen. Das erste Byte bleibt als Schutzbereich frei. Auch die Bytes nach dem Nutzdatenbereich müssen unverändert bleiben.
Das vollständige Programm initialisiert vor jeder Operation zwei 264-Byte-Puffer und prüft danach jedes Quell- und Zielbyte sowie die unveränderten Schutzbereiche. Erwartet werden COPY WORD 257, FILL WORD 257, COPY BYTE 255, FILL BYTE 255, COPY16 016, COPY32 032, ZERO CALLS 002 und FAILED 000. WORD/BYTE bezeichnen die Zählerbreite, nicht die Größe der kopierten Elemente. Die Zahlen stammen aus den Prüfungen des Beispiels; diese void-Funktionen liefern keine Rückgabewerte.
Die Aufnahmen zeigen schwarze Schrift auf Weiß beim DMG, blaue Schrift auf Weiß beim CGB und blaue Schrift auf Schwarz beim FC. Das Beispiel prüft RAM-Operationen; die Schrift stellt die Ergebnisse dar und demonstriert keine Zeichenprimitive. Weitere Emulatorprüfungen decken je Plattform 88 Speicherfälle ab: konstante und zur Laufzeit bestimmte Längen von 0, 1, 16, 17, 32, 33, 64, 65 und 255 Bytes sowie 256 und 257 bei 16-Bit-Zählern. Auf echter Hardware wurde nicht getestet.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Kopiert mit einem 8-Bit-Zähler einen Block von höchstens 255 Bytes. Diese Form eignet sich, wenn die Länge in ein Byte passt. _small bezeichnet die Breite des Zählers und garantiert weder eine bestimmte Laufzeit noch eine bestimmte Codegröße.
Syntax
void __memcpy_small(void* dst, const void* src, u8 len);Parameter
dstZeiger auf das erste beschreibbare Zielbyte. Stellen Sie genügend zusammenhängenden Speicher für die gesamte Operation bereit; ein Puffer wird nicht automatisch angelegt.
srcZeiger auf das erste lesbare Quellbyte. Der gesamte Bereich muss während der Kopie zugänglich und unverändert bleiben.
lenByteanzahl als
u8: 0–255. Übergeben Sie nicht 256, um einen vollständigen 256-Byte-Block anzugeben; diese Länge passt nicht in den Parameter. Ab 256 Bytes verwenden Sie die entsprechende Funktion ohne_small.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Bei Länge null werden keine Bytes übertragen. Seiteneffekte in den Argumentausdrücken finden trotzdem statt: Das Beispiel übergibt zwei Funktionen, die Zeiger liefern, und bestätigt beide Aufrufe trotz Kopierlänge null.
Verwenden Sie Quell- und Zielbereiche, die sich nicht überlappen. Diese Routinen kopieren vorwärts und bieten nicht das überlappungssichere Verhalten von memmove. Schreibzugriffe auf noch ungelesene Quellbytes können die spätere Kopie verfälschen.
Die Operation wird vor der Rückkehr abgeschlossen. Sie schaltet keine ROM/RAM-Bänke um und begrenzt den Bereich nicht automatisch auf Puffergrenzen. Halten Sie die Speicherabbildung stabil, verwenden Sie gültige Bereiche und stimmen Sie Zugriffe mit Interrupt-Routinen ab, die dieselben Puffer verwenden.
Auf dem GB sind dies gewöhnliche CPU-Speicherzugriffe. Sie warten nicht auf ein sicheres VRAM/OAM-Zugriffsfenster, starten kein DMA und wählen keine CGB-VRAM/WRAM-Bank aus. Verwenden Sie dafür die speziellen Grafikübertragungs-APIs.
Anwendungsbeispiel
memory_reset();
__memcpy_small(memory_destination+1,memory_source+1,255);
memory_copy_byte=memory_check(255,0); // Maximum representable byte count.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel kopiert mit __memcpy_small ab Offset 1 ein bekanntes Muster von 255 Bytes. memory_check vergleicht die vollständigen Quell- und Zielarrays; die Zeile COPY BYTE muss 255 anzeigen. Das erste Byte bleibt als Schutzbereich frei. Auch die Bytes nach dem Nutzdatenbereich müssen unverändert bleiben.
Das vollständige Programm initialisiert vor jeder Operation zwei 264-Byte-Puffer und prüft danach jedes Quell- und Zielbyte sowie die unveränderten Schutzbereiche. Erwartet werden COPY WORD 257, FILL WORD 257, COPY BYTE 255, FILL BYTE 255, COPY16 016, COPY32 032, ZERO CALLS 002 und FAILED 000. WORD/BYTE bezeichnen die Zählerbreite, nicht die Größe der kopierten Elemente. Die Zahlen stammen aus den Prüfungen des Beispiels; diese void-Funktionen liefern keine Rückgabewerte.
Die Aufnahmen zeigen schwarze Schrift auf Weiß beim DMG, blaue Schrift auf Weiß beim CGB und blaue Schrift auf Schwarz beim FC. Das Beispiel prüft RAM-Operationen; die Schrift stellt die Ergebnisse dar und demonstriert keine Zeichenprimitive. Weitere Emulatorprüfungen decken je Plattform 88 Speicherfälle ab: konstante und zur Laufzeit bestimmte Längen von 0, 1, 16, 17, 32, 33, 64, 65 und 255 Bytes sowie 256 und 257 bei 16-Bit-Zählern. Auf echter Hardware wurde nicht getestet.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Füllt den Zielbereich mit demselben Bytewert. Der 16-Bit-Zähler eignet sich zum Leeren größerer Puffer oder zum Initialisieren einer Tabelle. Mit 0x5A wird beispielsweise 5A 5A 5A… geschrieben; eine mehrbyteige Ganzzahl wird dabei nicht wiederholt.
Syntax
void __memset(void* dst, u8 value, u16 count);Parameter
dstZeiger auf das erste beschreibbare Zielbyte. Stellen Sie genügend zusammenhängenden Speicher für die gesamte Operation bereit; ein Puffer wird nicht automatisch angelegt.
valueBytewert von 0 bis 255, der an jede Zieladresse geschrieben wird. Mit 0 wird der Bereich gelöscht. Soll ein
u16-Array einen numerischen Wert ungleich null erhalten, weisen Sie diesen jedem Element einzeln zu.countByteanzahl als
u16(0–65535), keine Elementanzahl. Der Höchstwert des Typs bedeutet nicht, dass auf der Zielmaschine ein gültiger Puffer dieser Größe verfügbar ist. Der gesamte Bereich muss in die gewählte Speicherabbildung passen.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Bei Länge null werden keine Bytes übertragen. Seiteneffekte in den Argumentausdrücken finden trotzdem statt: Das Beispiel übergibt zwei Funktionen, die Zeiger liefern, und bestätigt beide Aufrufe trotz Kopierlänge null.
Die Operation wird vor der Rückkehr abgeschlossen. Sie schaltet keine ROM/RAM-Bänke um und begrenzt den Bereich nicht automatisch auf Puffergrenzen. Halten Sie die Speicherabbildung stabil, verwenden Sie gültige Bereiche und stimmen Sie Zugriffe mit Interrupt-Routinen ab, die dieselben Puffer verwenden.
Auf dem GB sind dies gewöhnliche CPU-Speicherzugriffe. Sie warten nicht auf ein sicheres VRAM/OAM-Zugriffsfenster, starten kein DMA und wählen keine CGB-VRAM/WRAM-Bank aus. Verwenden Sie dafür die speziellen Grafikübertragungs-APIs.
Anwendungsbeispiel
memory_reset();
__memset(memory_destination+1,0x5A,257);
memory_fill_word=memory_check(257,1); // Repeat one byte, not a 16-bit word.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel füllt mit __memset ab Offset 1 genau 257 Bytes mit 0x5A. memory_check verlangt für jedes Zielbyte 0x5A, für die Schutzbytes weiterhin 0xCC und einen unveränderten separaten Quellpuffer. Die Zeile FILL WORD muss 257 anzeigen.
Das vollständige Programm initialisiert vor jeder Operation zwei 264-Byte-Puffer und prüft danach jedes Quell- und Zielbyte sowie die unveränderten Schutzbereiche. Erwartet werden COPY WORD 257, FILL WORD 257, COPY BYTE 255, FILL BYTE 255, COPY16 016, COPY32 032, ZERO CALLS 002 und FAILED 000. WORD/BYTE bezeichnen die Zählerbreite, nicht die Größe der kopierten Elemente. Die Zahlen stammen aus den Prüfungen des Beispiels; diese void-Funktionen liefern keine Rückgabewerte.
Die Aufnahmen zeigen schwarze Schrift auf Weiß beim DMG, blaue Schrift auf Weiß beim CGB und blaue Schrift auf Schwarz beim FC. Das Beispiel prüft RAM-Operationen; die Schrift stellt die Ergebnisse dar und demonstriert keine Zeichenprimitive. Weitere Emulatorprüfungen decken je Plattform 88 Speicherfälle ab: konstante und zur Laufzeit bestimmte Längen von 0, 1, 16, 17, 32, 33, 64, 65 und 255 Bytes sowie 256 und 257 bei 16-Bit-Zählern. Auf echter Hardware wurde nicht getestet.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Füllt mit einem 8-Bit-Zähler bis zu 255 aufeinanderfolgende Bytes mit demselben Wert. Das eignet sich zum Initialisieren eines kleinen Arbeitspuffers. Für mehr als 255 Bytes verwenden Sie __memset.
Syntax
void __memset_small(void* dst, u8 value, u8 len);Parameter
dstZeiger auf das erste beschreibbare Zielbyte. Stellen Sie genügend zusammenhängenden Speicher für die gesamte Operation bereit; ein Puffer wird nicht automatisch angelegt.
valueBytewert von 0 bis 255, der an jede Zieladresse geschrieben wird. Mit 0 wird der Bereich gelöscht. Soll ein
u16-Array einen numerischen Wert ungleich null erhalten, weisen Sie diesen jedem Element einzeln zu.lenByteanzahl als
u8: 0–255. Übergeben Sie nicht 256, um einen vollständigen 256-Byte-Block anzugeben; diese Länge passt nicht in den Parameter. Ab 256 Bytes verwenden Sie die entsprechende Funktion ohne_small.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Bei Länge null werden keine Bytes übertragen. Seiteneffekte in den Argumentausdrücken finden trotzdem statt: Das Beispiel übergibt zwei Funktionen, die Zeiger liefern, und bestätigt beide Aufrufe trotz Kopierlänge null.
Die Operation wird vor der Rückkehr abgeschlossen. Sie schaltet keine ROM/RAM-Bänke um und begrenzt den Bereich nicht automatisch auf Puffergrenzen. Halten Sie die Speicherabbildung stabil, verwenden Sie gültige Bereiche und stimmen Sie Zugriffe mit Interrupt-Routinen ab, die dieselben Puffer verwenden.
Auf dem GB sind dies gewöhnliche CPU-Speicherzugriffe. Sie warten nicht auf ein sicheres VRAM/OAM-Zugriffsfenster, starten kein DMA und wählen keine CGB-VRAM/WRAM-Bank aus. Verwenden Sie dafür die speziellen Grafikübertragungs-APIs.
Anwendungsbeispiel
memory_reset();
__memset_small(memory_destination+1,0x5A,255);
memory_fill_byte=memory_check(255,1);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel füllt mit __memset_small ab Offset 1 genau 255 Bytes mit 0x5A. memory_check verlangt für jedes Zielbyte 0x5A, für die Schutzbytes weiterhin 0xCC und einen unveränderten separaten Quellpuffer. Die Zeile FILL BYTE muss 255 anzeigen.
Das vollständige Programm initialisiert vor jeder Operation zwei 264-Byte-Puffer und prüft danach jedes Quell- und Zielbyte sowie die unveränderten Schutzbereiche. Erwartet werden COPY WORD 257, FILL WORD 257, COPY BYTE 255, FILL BYTE 255, COPY16 016, COPY32 032, ZERO CALLS 002 und FAILED 000. WORD/BYTE bezeichnen die Zählerbreite, nicht die Größe der kopierten Elemente. Die Zahlen stammen aus den Prüfungen des Beispiels; diese void-Funktionen liefern keine Rückgabewerte.
Die Aufnahmen zeigen schwarze Schrift auf Weiß beim DMG, blaue Schrift auf Weiß beim CGB und blaue Schrift auf Schwarz beim FC. Das Beispiel prüft RAM-Operationen; die Schrift stellt die Ergebnisse dar und demonstriert keine Zeichenprimitive. Weitere Emulatorprüfungen decken je Plattform 88 Speicherfälle ab: konstante und zur Laufzeit bestimmte Längen von 0, 1, 16, 17, 32, 33, 64, 65 und 255 Bytes sowie 256 und 257 bei 16-Bit-Zählern. Auf echter Hardware wurde nicht getestet.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Entpackt einen count/value-Strom mit Bankangabe direkt in VRAM, ohne RAM-Zwischenpuffer für die Ausgabe. Der Compiler erzeugt eine Schleife, die jedes Paar liest, dessen Wiederholungen mit der LCD-gerechten VRAM-Schreibhilfe füllt und die Anzahl ausgegebener Bytes liefert. Geeignet für komprimierte Kacheldaten oder byteorientierte Kachelkarten.
Syntax
u16 __rle_decode_vram(u16 dst, u8 bank, const void* src);Parameter
dst16-Bit-CPU-Adresse des ersten Ausgabebytes innerhalb von 0x8000–0x9FFF. Reservieren Sie ausreichend zusammenhängende Bytes, ohne 0x9FFF zu überschreiten. Beispielsweise ist 0x8860 Kachel 134 im Kacheldatenlayout mit vorzeichenlosen Indizes. Die Funktion reserviert keine Kacheln und prüft den Zielbereich nicht.
bankROM-Quellbanknummer als u8. Bank und CPU-Adresse müssen zu Cartridge-Mapper und Ressourcenablage passen. Nach jedem codierten Byte wird die Bankauswahl wiederhergestellt. Das Argument wählt keine VRAM-Zielbank und kann keine Neunbit-MBC5-Banknummer darstellen.
srcCPU-Zeiger auf den codierten Strom in der angegebenen Bank. Die Adresse wird erhöht, die Banknummer bleibt jedoch fest. Halten Sie den gesamten Strom einschließlich Endnull in einer lesbaren Zuordnung; das Überschreiten von 0x7FFF setzt ihn nicht automatisch in der nächsten ROM-Bank fort.
Rückgabewert
Anzahl geschriebener Bytes als u16. Ein sofortiger Nullzähler erzeugt keine Bytes und liefert 0. Der Abschluss wird nicht kopiert. Die entpackte Länge muss in u16 passen; der Rückgabewert enthält keinen separaten Fehlercode und ist keine Kachelanzahl.
Bedingungen und Grenzen
Die Codierung verarbeitet Bytes, keinen Text. Beispielsweise wird {3, 0x12, 2, 0xA5, 0} zu fünf Bytes: 12 12 12 A5 A5. Diese Ausgabeliste verwendet Hexadezimalwerte. Ein Nullwert bei einem Paar mit count ungleich null ist normales Datum, kein Endzeichen.
Es gibt keine Argumente für codierte Größe, Ausgabekapazität oder fehlerhafte Daten. Prüfen Sie Ressourcen beim Erstellen oder Laden, setzen Sie die abschließende null und berechnen Sie vor dem Decodieren die entpackte Größe. Ein ungültiger oder nicht abgeschlossener Strom kann über Quelle und Ziel hinaus lesen beziehungsweise schreiben.
Bei ausgeschaltetem LCD werden Läufe direkt geschrieben. Bei eingeschaltetem LCD wartet die Hilfe für jedes Ausgabebyte auf einen beschreibbaren LCD-Modus und umschließt Warten und Schreiben mit DI/EI. Ein langer Strom kann daher mehrere Frames beanspruchen; der Aufruf ist synchron und keine Warteschlangenübertragung. Dieser Pfad aktiviert Interrupts nach jedem Byte, statt einen anfänglich deaktivierten Zustand zu erhalten.
Auf dem CGB gehen Schreibzugriffe in die aktuell gewählte VBK-Bank, ohne diese Auswahl zu ändern. Wählen Sie VBK 0 für die Kacheldaten des Beispiels. Das Bankargument wählt weiterhin die ROM-Eingabe, auch bei VBK 1 als Ausgabe. Der Decoder verwaltet weder CGB-Paletten noch Kartenattribute.
Während eines Zugriffs mit Bankangabe dürfen Interruptroutinen weder die ROM-Zuordnung ändern noch die Far-Copy-Hilfe erneut verwenden; die Hilfen teilen ihre Bankverwaltung. Platzieren Sie den Decoder für normale Zeiger und seine Quelle in kompatiblen Zuordnungen. Ein Wechsel zur Funktionsbank darf einen normalen Quellzeiger nicht verdecken.
rle_decode und rle_decode_far sind in rpg.h deklariert und in lib/rle.c implementiert. Kompilieren Sie diese Implementierung einmal mit dem Spiel. Das vollständige Beispiel bindet rle.c nach #pragma bank 0 ein, damit Aufrufer und Decoder in der gemeinsamen Bank bleiben; übergeben Sie rle.c dafür nicht zusätzlich als eigene Eingabe. Die intrinsische Funktion __rle_decode_vram benötigt selbst keine Bibliotheksimplementierungsdatei.
Anwendungsbeispiel
vram_count=__rle_decode_vram(0x8860,2,banked_rle); // Expand directly to 48 bytes of tile data.
if(*rle_bank_marker!=0xA1)failures++;Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Dieses Beispiel entpackt den Strom aus Bank 2 ohne RAM-Ausgabepuffer direkt nach VRAM 0x8860. DIRECT VRAM zeigt 048. Die untere Reihe ist auf dem CGB blau und muss dieselben drei Kachelmuster wie die RAM-basierten Reihen enthalten. Dieses Lehrprogramm überträgt bei ausgeschaltetem LCD.
Jede Reihe enthält von links nach rechts ein gefülltes 8×8-Quadrat, einen 8×8-Rahmen mit einem Pixel Stärke und ein gefülltes 4×8-Rechteck in der linken Hälfte einer 8×8-Kachel. Die Kachelspalten sind 2, 5 und 8 (Pixel-x = 16, 40 und 64). Die drei Reihen beginnen bei Pixel-y = 32, 72 und 112. Auf dem DMG sind die Formen schwarz, auf dem CGB je Reihe rot, grün und blau. FAILED muss 000 sein. Die Zahlen zählen Bytes: Drei GB-Kacheln zu je 16 Bytes erfordern 48 entpackte Bytes.
Die Prüfmatrix umfasst 42 Fälle: leerer Strom, Einbyte-Lauf, Nullwertdaten, 255-Byte-Lauf, 510-Byte-Ausgabe über eine Zielseitengrenze und codierte Daten über eine Quellseitengrenze. Verglichen werden alle 528 beobachteten Ziel-/Schutzbytes, Ausgabelänge und wiederhergestellte ROM-Zuordnung. Direkte VRAM-Fälle umfassen LCD an/aus auf DMG und CGB sowie CGB-VBK 1. Die beiden Lehrbildschirme werden nach 240 Emulatorframes geprüft. Ungültige Ressourcen, gleichzeitige Interruptnutzung und echte Hardware sind nicht abgedeckt.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Schaltet den gemeinsamen GB-Pseudozufallsgenerator einmal weiter und liefert sein neues High-Byte. Nutzen Sie diese intrinsische Funktion für ein Byte, ohne die Bibliothekswrapper aus rng.c einzubinden.
Syntax
u8 __rng8();Rückgabewert
Ein u8-Ergebnis (0–255) nach einmaligem Fortschalten des gemeinsamen Generators. Dies beschreibt Typ und Grenzen, nicht gleiche Wahrscheinlichkeit für jedes Byte.
Bedingungen und Grenzen
Der GB-Schritt addiert 0x5D17 mit Umlauf zum 16-Bit-Zustand, verknüpft das aktualisierte Low-Byte per XOR mit dem High-Byte und addiert dieses Low-Byte zum um ein Bit nach links rotierten High-Byte. Beide aktualisierten Bytes werden gespeichert; zurückgegeben wird das High-Byte.
Zustand null ist zulässig. Bei der nächsten Byteziehung ersetzt ihn der GB-Generator vor dem Fortschalten durch 0x5AA5; __rng_seed(0) gefolgt von __rng8() liefert daher 122. Wiederholtes Setzen des Startwerts startet die Folge neu, statt Zufälligkeit hinzuzufügen.
Diese Aufrufe teilen einen persistenten Zustand. Reproduktion erfordert dasselbe Zielsystem, denselben Startwert und dieselbe Aufrufreihenfolge einschließlich anderer Hilfsfunktionen. Führen Sie Startwertsetzung und Ziehungen in einem Ausführungskontext aus; die Prüfungen belegen keine sichere gleichzeitige Nutzung aus einem Interrupthandler.
GB und FC verwenden unterschiedliche Algorithmen und versprechen bei gleichem Startwert keine gleiche Folge. Dies sind deterministische Spielgeneratoren, keine Quellen kryptografischer Zufälligkeit. Die Tests prüfen Implementierungsverhalten, nicht statistische Qualität oder Periode.
Binden Sie rpg.h ein und kompilieren Sie lib/rng.c mit dem Spiel, wenn Sie die Bibliotheksfunktionen nutzen. __rng_seed und __rng8 werden als intrinsische Funktionen vom Compiler erzeugt; ein Programm mit nur diesen beiden benötigt kein rng.c-Objekt.
Anwendungsbeispiel
__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.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Dieses Beispiel zeigt das Fortschalten bei jeder Ziehung. Nach Startwert 0x1234 zeigen die ersten beiden __rng8-Aufrufe FIRST 00002 und SECOND 00034. Erneutes Setzen startet wieder bei 00002.
Das vollständige Programm zeigt Dezimalergebnisse auf Weiß: schwarze Schrift auf DMG, blaue auf CGB. FAILED muss 00000 anzeigen. Die Bilder zeigen die tatsächliche Emulatorausgabe dieses Beispiels.
Die Prüfung verglich je Fall 32 Ausgaben und acht anschließende Byteziehungen mit einem unabhängigen Hostmodell: 93 GB- und 18 FC-Fälle, einschließlich konstanter und zur Laufzeit gesetzter Startwerte sowie GB-Grenzfällen für Bereiche, Chancen und Gewichte. Die Lehrbildschirme wurden nach 240 Emulatorframes geprüft. Echte Hardware und statistische Qualität wurden nicht getestet.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Ersetzt den 16-Bit-Zustand des gemeinsamen GB-Generators. Verwenden Sie zum Wiederholen derselben Folge beim Debuggen einen festen Startwert. Das Setzen des Startwerts zieht selbst keinen Zufallswert.
Syntax
void __rng_seed(u16 seed);Parameter
seedVorzeichenloser 16-Bit-Startwert in 0–65535. Beide Bytes werden unverändert gespeichert. Wählen und notieren Sie den Startwert vor der Folge von Spielziehungen, die Sie reproduzieren möchten.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Zustand null ist zulässig. Bei der nächsten Byteziehung ersetzt ihn der GB-Generator vor dem Fortschalten durch 0x5AA5; __rng_seed(0) gefolgt von __rng8() liefert daher 122. Wiederholtes Setzen des Startwerts startet die Folge neu, statt Zufälligkeit hinzuzufügen.
Diese Aufrufe teilen einen persistenten Zustand. Reproduktion erfordert dasselbe Zielsystem, denselben Startwert und dieselbe Aufrufreihenfolge einschließlich anderer Hilfsfunktionen. Führen Sie Startwertsetzung und Ziehungen in einem Ausführungskontext aus; die Prüfungen belegen keine sichere gleichzeitige Nutzung aus einem Interrupthandler.
GB und FC verwenden unterschiedliche Algorithmen und versprechen bei gleichem Startwert keine gleiche Folge. Dies sind deterministische Spielgeneratoren, keine Quellen kryptografischer Zufälligkeit. Die Tests prüfen Implementierungsverhalten, nicht statistische Qualität oder Periode.
Binden Sie rpg.h ein und kompilieren Sie lib/rng.c mit dem Spiel, wenn Sie die Bibliotheksfunktionen nutzen. __rng_seed und __rng8 werden als intrinsische Funktionen vom Compiler erzeugt; ein Programm mit nur diesen beiden benötigt kein rng.c-Objekt.
Anwendungsbeispiel
__rng_seed(0x1234);
repeated=__rng8(); // Reseeding reproduces the first byte, 2.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Dieses Beispiel zeigt das Zurücksetzen einer Testfolge. Nach Startwert 0x1234 zeigen FIRST und RESEEDED jeweils 00002, SECOND zeigt 00034. Der Bibliotheksstartwert setzt denselben Zustand zurück; daher zeigt auch RNG8 den Wert 00002.
Das vollständige Programm zeigt Dezimalergebnisse auf Weiß: schwarze Schrift auf DMG, blaue auf CGB. FAILED muss 00000 anzeigen. Die Bilder zeigen die tatsächliche Emulatorausgabe dieses Beispiels.
Die Prüfung verglich je Fall 32 Ausgaben und acht anschließende Byteziehungen mit einem unabhängigen Hostmodell: 93 GB- und 18 FC-Fälle, einschließlich konstanter und zur Laufzeit gesetzter Startwerte sowie GB-Grenzfällen für Bereiche, Chancen und Gewichte. Die Lehrbildschirme wurden nach 240 Emulatorframes geprüft. Echte Hardware und statistische Qualität wurden nicht getestet.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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
Schreibt Hintergrund-SCX/SCY sofort und synchronisiert aktuelle sowie vorgemerkte Softwarewerte.
Syntax
void __scroll_bg_set(u8 scx, u8 scy);Parameter
scxHorizontale Verschiebung, 0..255 Pixel.
scyVertikale Verschiebung, 0..255 Pixel.
Rückgabewert
Kein Rückgabewert.
Bedingungen und Grenzen
Löscht das Flag für ausstehende Hintergrundaktualisierung. Schreibzugriffe innerhalb eines Frames beeinflussen die nachfolgende Abtastung; rufen Sie die Funktion gewöhnlich während VBlank auf.
Anwendungsbeispiel
__scroll_bg_set(19,27);result[5]=*((u8*)0xFF43);result[6]=*((u8*)0xFF42);__scroll_bg_set(0,0);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Lernen Sie Kartenauswahl, Scrolling, Verschachtelung und Rasterzeilenwarten. Die Basisadressen sind 38912/39936, die verschachtelten Tokens 0/1 und die beobachteten Ziel-LY-Werte 80/144. Prüfen Sie auch die Rückkehr aus Warteaufrufen bei ausgeschaltetem LCD.


Vollständiges Beispielprogramm
Build-Befehl
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=noneImplementierung und Quelltext
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
Kopiert len aufeinanderfolgende Tile-Nummern aus src senkrecht ab (x,y) in die durch LCDC gewählte BG-Karte. Die Quelle steigt pro Tile um ein Byte, das Ziel um 32 Bytes.
Wartet bei Bedarf auf beschreibbares VRAM. Bei aktivem LCD werden Interrupts gesperrt und anschließend möglicherweise aktiviert. Weder der vorherige Interruptzustand noch ein Abschluss innerhalb eines VBlank wird garantiert.
Syntax
void __settile_col(u8 x, u8 y, const u8* src, u8 len);Parameter
x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
srcLückenlose Bytes im RAM oder einer sichtbaren ROM-Bank. Der Tile-Strom enthält Tile-Nummern, der Attributstrom CGB-Attribute. Die Kopie erfolgt sofort; der Zeiger wird nicht gespeichert.
lenByteanzahl von 0 bis 255. Null kopiert nichts. Die Quelle muss mindestens so viele lesbare Bytes enthalten.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Tile-Bytes werden in die gewählte VRAM-Bank geschrieben. Wählen Sie auf CGB Bank 0 für Tile-Nummern; der Aufruf wechselt die Bank nicht.
Waagerecht muss x+len <= 32, senkrecht y+len <= 32 gelten. An Kartenrändern findet kein automatischer Koordinatenumbruch statt.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheinen die Beispielzeichen und die Ergebnisanzeige blau auf weißem Hintergrund. Die Vorbereitung setzt die Zeichenfarbe der Palette 0 auf Blau, ohne die Attribute der Tilemap zu ändern. Auf DMG erscheinen dieselben Zeichen schwarz auf weißem Hintergrund.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
A
B
C

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/rpg.h:156
__settile_rect
Füllt ein Rechteck von w mal h in der durch LCDC gewählten BG-Karte mit einer Tile-Nummer. Der Zeilenabstand beträgt 32 Bytes; es wird ein Tile wiederholt, kein Bildarray kopiert.
Wartet bei Bedarf auf beschreibbares VRAM. Bei aktivem LCD werden Interrupts gesperrt und anschließend möglicherweise aktiviert. Weder der vorherige Interruptzustand noch ein Abschluss innerhalb eines VBlank wird garantiert.
Syntax
void __settile_rect(u8 x, u8 y, u8 w, u8 h, u8 tile);Parameter
x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
w, hBreite und Höhe in 8×8-Tiles. Es müssen
x+w <= 32undy+h <= 32gelten. Bei einer Nulldimension wird nichts geschrieben.tileTile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Tile-Bytes werden in die gewählte VRAM-Bank geschrieben. Wählen Sie auf CGB Bank 0 für Tile-Nummern; der Aufruf wechselt die Bank nicht.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheinen die Beispielzeichen und die Ergebnisanzeige blau auf weißem Hintergrund. Die Vorbereitung setzt die Zeichenfarbe der Palette 0 auf Blau, ohne die Attribute der Tilemap zu ändern. Auf DMG erscheinen dieselben Zeichen schwarz auf weißem Hintergrund.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
AAA
AAA

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/rpg.h:154
__settile_row
Kopiert len aufeinanderfolgende Tile-Nummern aus src waagerecht ab (x,y) in die durch LCDC gewählte BG-Karte. Quell- und Zieladresse steigen pro Tile um ein Byte.
Wartet bei Bedarf auf beschreibbares VRAM. Bei aktivem LCD werden Interrupts gesperrt und anschließend möglicherweise aktiviert. Weder der vorherige Interruptzustand noch ein Abschluss innerhalb eines VBlank wird garantiert.
Syntax
void __settile_row(u8 x, u8 y, const u8* src, u8 len);Parameter
x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
srcLückenlose Bytes im RAM oder einer sichtbaren ROM-Bank. Der Tile-Strom enthält Tile-Nummern, der Attributstrom CGB-Attribute. Die Kopie erfolgt sofort; der Zeiger wird nicht gespeichert.
lenByteanzahl von 0 bis 255. Null kopiert nichts. Die Quelle muss mindestens so viele lesbare Bytes enthalten.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Tile-Bytes werden in die gewählte VRAM-Bank geschrieben. Wählen Sie auf CGB Bank 0 für Tile-Nummern; der Aufruf wechselt die Bank nicht.
Waagerecht muss x+len <= 32, senkrecht y+len <= 32 gelten. An Kartenrändern findet kein automatischer Koordinatenumbruch statt.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheinen die Beispielzeichen und die Ergebnisanzeige blau auf weißem Hintergrund. Die Vorbereitung setzt die Zeichenfarbe der Palette 0 auf Blau, ohne die Attribute der Tilemap zu ändern. Auf DMG erscheinen dieselben Zeichen schwarz auf weißem Hintergrund.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
ABC

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/rpg.h:155
__settile_xy
Schreibt eine Tile-Nummer in eine Kartenposition. Das wählt ein vorhandenes Tile-Bild aus und ändert keine Pixeldaten.
LCDC-Bit 3 wählt die Hintergrundkarte: null bedeutet 0x9800, eins 0x9C00. Zum Basiswert kommt 32*y + x hinzu.
Wartet bei Bedarf auf beschreibbares VRAM. Bei aktivem LCD werden Interrupts gesperrt und anschließend möglicherweise aktiviert. Weder der vorherige Interruptzustand noch ein Abschluss innerhalb eines VBlank wird garantiert.
Syntax
void __settile_xy(u8 x, u8 y, u8 tile);Parameter
x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
tileTile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Tile-Bytes werden in die gewählte VRAM-Bank geschrieben. Wählen Sie auf CGB Bank 0 für Tile-Nummern; der Aufruf wechselt die Bank nicht.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheinen die Beispielzeichen und die Ergebnisanzeige blau auf weißem Hintergrund. Die Vorbereitung setzt die Zeichenfarbe der Palette 0 auf Blau, ohne die Attribute der Tilemap zu ändern. Auf DMG erscheinen dieselben Zeichen schwarz auf weißem Hintergrund.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
A

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/rpg.h:153
__settilemap_rect
Kopiert ein lückenloses Array mit w*h Tile-Nummern in ein Rechteck ab (x,y) der mit base gewählten Karte. Quellzeilen enthalten w Bytes; Zielzeilen haben 32 Bytes Abstand.
Wartet bei Bedarf auf beschreibbares VRAM. Bei aktivem LCD werden Interrupts gesperrt und anschließend möglicherweise aktiviert. Weder der vorherige Interruptzustand noch ein Abschluss innerhalb eines VBlank wird garantiert.
Syntax
void __settilemap_rect(u16 base, u8 x, u8 y, u8 w, u8 h, const u8* src);Parameter
basebasewählt die Karte (0x9800oder0x9C00); die Byteadresse lautetbase + 32*y + x.x, yKoordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.
w, hBreite und Höhe in 8×8-Tiles. Es müssen
x+w <= 32undy+h <= 32gelten. Bei einer Nulldimension wird nichts geschrieben.srcLückenlose Bytes im RAM oder einer sichtbaren ROM-Bank. Der Tile-Strom enthält Tile-Nummern, der Attributstrom CGB-Attribute. Die Kopie erfolgt sofort; der Zeiger wird nicht gespeichert.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Tile-Bytes werden in die gewählte VRAM-Bank geschrieben. Wählen Sie auf CGB Bank 0 für Tile-Nummern; der Aufruf wechselt die Bank nicht.
Das Beispiel verwendet gb_common.h: m_init() lädt die mitgelieferte ASCII-Schrift mit Zeichencodes als Tile-Nummern. m_wait(); M_LCDC=0; schaltet das LCD während VBlank für Zugriffe ohne Warteprüfung aus. Der abschließende LCDC-Wert wählt die sichtbare Karte.
Anwendungsbeispiel
// __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);
}Erwartetes Ergebnis
Die gewählte Karte zeigt die geschriebenen Zeichen oder die gefüllte Fläche. Das Programm liest die Tile-Bytes und auf CGB auch die Attribute zurück; FAILED CHECKS 000 bedeutet, dass alle Vergleiche stimmen. Beide Karten sind zunächst null, und vor dem Aufruf wird für BG/Fenster 0x9C00 gewählt. So lassen sich feste und ausgewählte Karten unterscheiden. Geschrieben wird bei ausgeschaltetem LCD; das Beispiel misst kein Timing bei eingeschaltetem LCD.
Auf CGB erscheinen die Beispielzeichen und die Ergebnisanzeige blau auf weißem Hintergrund. Die Vorbereitung setzt die Zeichenfarbe der Palette 0 auf Blau, ohne die Attribute der Tilemap zu ändern. Auf DMG erscheinen dieselben Zeichen schwarz auf weißem Hintergrund.
Erwartetes Bild: Das folgende Tile-Muster beginnt bei Tile-Koordinaten (x=2, y=3), entsprechend Pixel (16,24). Jedes Zeichen belegt ein 8×8-Tile. Lesen Sie von oben nach unten und links nach rechts; die umgebenden Tiles bleiben leer.
ABC
DEF

Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/rpg.h:158
__tile_addr
Berechnet die CPU-Adresse einer Zelle in einer 32 Kacheln breiten Karte.
Syntax
u16 __tile_addr(u16 base, u8 x, u8 y);Parameter
baseKachelkartenbasis, gewöhnlich $9800/$9C00.
xSpalte, gewöhnlich 0..31.
yZeile, gewöhnlich 0..31.
Rückgabewert
Gibt (base + y*32 + x) modulo 65536 ohne Speicherzugriff zurück.
Anwendungsbeispiel
result[4]=__tile_addr(0x9800,3,2);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Lernen Sie Kartenauswahl, Scrolling, Verschachtelung und Rasterzeilenwarten. Die Basisadressen sind 38912/39936, die verschachtelten Tokens 0/1 und die beobachteten Ziel-LY-Werte 80/144. Prüfen Sie auch die Rückkehr aus Warteaufrufen bei ausgeschaltetem LCD.


Vollständiges Beispielprogramm
Build-Befehl
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=noneImplementierung und Quelltext
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 ist ein Compileralias für __vram_memcpy. Es verwendet dieselbe Byteanzahl und dieselbe sofortige CPU-Übertragung einschließlich der LCD-Zugriffsregeln.
Kopiert sofort len Bytes von src nach dst in aufsteigender Adressreihenfolge. Kehrt nach Abschluss zurück und reiht keine Arbeit ein. Beide Adressen steigen jeweils um ein Byte; Kartenzeilenabstände oder Grafikformatumwandlungen werden nicht berücksichtigt.
Syntax
void __vram_copy_dma(u16 dst, const void* src, u16 len);Parameter
dstAdresse des ersten Zielbytes, normalerweise im VRAM
0x8000–0x9FFF. Der gesamte Bereich muss gültig sein. Der CGB verwendet die aktuelle VRAM-Bank; im Kartenbereich enthält Bank 0 Kachelnummern und Bank 1 Attribute. Diese Routinen wählen keine Karte anhand von LCDC, führen keinen Zeilenumbruch aus und reservieren kein VRAM.srcZeiger auf die Quellbytes, während des gesamten Aufrufs gültig und korrekt eingeblendet. Daten werden sofort gelesen und nicht für einen späteren Flush gespeichert. Die Quellbank wird nicht automatisch gewechselt. Verwenden Sie nicht überlappende Bereiche; diese Vorwärtskopie bietet keine memmove-Semantik.
lenAnzahl aufeinanderfolgender Bytes von 0 bis 65535, keine Tile-Anzahl. Der gesamte Bereich muss in gültiger Quelle und beschreibbarem Ziel liegen; die API prüft keine Grenzen und verhindert keinen 16-Bit-Adressüberlauf.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Der Name wählt keine DMA-Einheit aus und plant keine Arbeit für einen späteren HBlank. Der Aufrufer wartet bis zum Abschluss. Bei aktivem LCD akzeptiert dieselbe CPU-Abfrage Modus 0 (HBlank) oder 1 (VBlank); CGB-HDMA wird nicht eingerichtet.
Bei eingeschaltetem LCD wartet jeder Bytezugriff unter DI auf STAT-Modus 0 oder 1; danach folgt EI. Bei ausgeschaltetem LCD läuft eine gewöhnliche CPU-Schleife. Der Aufruf kann mehrere Frames dauern, erhält keine anfängliche Interruptsperre und richtet keine DMA-Register ein. Länge null überträgt keine Daten.
Anwendungsbeispiel
__vram_copy_dma(0x98A2,strip,3); // This alias also uses CPU writes, not a DMA engine.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Dieses Beispiel schreibt mit __vram_copy_dma drei Kartenbytes ab Kachel (2,5), Pixel (16,40). Erwartet werden die Muster [1,2,3] von links nach rechts in einem Bereich von 24 × 8; benachbarte Felder bleiben leer. Das vollständige Programm zeigt jeden geprüften Kopier- oder Füllvorgang in einer eigenen Zeile.
Die mitgelieferten eigenen Grafiken verwenden Kachel 1 für ein ausgefülltes Quadrat von 8 × 8, Kachel 2 für dessen vier linke Spalten und Kachel 3 für ein linksbündiges Dreieck, das je Zeile um ein Pixel von 1 auf 8 wächst. DMG zeigt Schwarz auf Weiß; CGB unterscheidet die Operationen farblich und behält schwarzen Text bei.
Das Quadrat bei Pixel (16,88) muss nach sicheren und ungeprüften Aufrufen mit Länge null erhalten bleiben. Bei aktivem LCD ergänzen sicheres Kopieren und Füllen Quadrate bei (16,96) und (32,96). FAILED CHECKS muss 000 anzeigen. Die Emulatortests vergleichen Pixel und Zähler. Der CGB verwendet hier Bank 0; andere Banken und echte Hardware werden hier nicht geprüft.
Im CGB-Modus wiederholen die Zeilen von MEMCPY bis FILL UNSAFE die Folge Rot, Blau, Grün. Das Quadrat bei ZERO COUNT ist rot. Bei LCD ON ist das linke Quadrat blau und das rechte grün. Text bleibt schwarz. Die Prüfungen kontrollieren diese Farben an jedem gezeichneten Pixel.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/rpg.h:163
__vram_copy_hblank
__vram_copy_hblank ist ein Compileralias für __vram_memcpy. Es verwendet dieselbe Byteanzahl und dieselbe sofortige CPU-Übertragung einschließlich der LCD-Zugriffsregeln.
Kopiert sofort len Bytes von src nach dst in aufsteigender Adressreihenfolge. Kehrt nach Abschluss zurück und reiht keine Arbeit ein. Beide Adressen steigen jeweils um ein Byte; Kartenzeilenabstände oder Grafikformatumwandlungen werden nicht berücksichtigt.
Syntax
void __vram_copy_hblank(u16 dst, const void* src, u16 len);Parameter
dstAdresse des ersten Zielbytes, normalerweise im VRAM
0x8000–0x9FFF. Der gesamte Bereich muss gültig sein. Der CGB verwendet die aktuelle VRAM-Bank; im Kartenbereich enthält Bank 0 Kachelnummern und Bank 1 Attribute. Diese Routinen wählen keine Karte anhand von LCDC, führen keinen Zeilenumbruch aus und reservieren kein VRAM.srcZeiger auf die Quellbytes, während des gesamten Aufrufs gültig und korrekt eingeblendet. Daten werden sofort gelesen und nicht für einen späteren Flush gespeichert. Die Quellbank wird nicht automatisch gewechselt. Verwenden Sie nicht überlappende Bereiche; diese Vorwärtskopie bietet keine memmove-Semantik.
lenAnzahl aufeinanderfolgender Bytes von 0 bis 65535, keine Tile-Anzahl. Der gesamte Bereich muss in gültiger Quelle und beschreibbarem Ziel liegen; die API prüft keine Grenzen und verhindert keinen 16-Bit-Adressüberlauf.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Der Name wählt keine DMA-Einheit aus und plant keine Arbeit für einen späteren HBlank. Der Aufrufer wartet bis zum Abschluss. Bei aktivem LCD akzeptiert dieselbe CPU-Abfrage Modus 0 (HBlank) oder 1 (VBlank); CGB-HDMA wird nicht eingerichtet.
Bei eingeschaltetem LCD wartet jeder Bytezugriff unter DI auf STAT-Modus 0 oder 1; danach folgt EI. Bei ausgeschaltetem LCD läuft eine gewöhnliche CPU-Schleife. Der Aufruf kann mehrere Frames dauern, erhält keine anfängliche Interruptsperre und richtet keine DMA-Register ein. Länge null überträgt keine Daten.
Anwendungsbeispiel
__vram_copy_hblank(0x9882,strip,3); // Immediate safe copy; no scheduled HBlank DMA.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Dieses Beispiel schreibt mit __vram_copy_hblank drei Kartenbytes ab Kachel (2,4), Pixel (16,32). Erwartet werden die Muster [1,2,3] von links nach rechts in einem Bereich von 24 × 8; benachbarte Felder bleiben leer. Das vollständige Programm zeigt jeden geprüften Kopier- oder Füllvorgang in einer eigenen Zeile.
Die mitgelieferten eigenen Grafiken verwenden Kachel 1 für ein ausgefülltes Quadrat von 8 × 8, Kachel 2 für dessen vier linke Spalten und Kachel 3 für ein linksbündiges Dreieck, das je Zeile um ein Pixel von 1 auf 8 wächst. DMG zeigt Schwarz auf Weiß; CGB unterscheidet die Operationen farblich und behält schwarzen Text bei.
Das Quadrat bei Pixel (16,88) muss nach sicheren und ungeprüften Aufrufen mit Länge null erhalten bleiben. Bei aktivem LCD ergänzen sicheres Kopieren und Füllen Quadrate bei (16,96) und (32,96). FAILED CHECKS muss 000 anzeigen. Die Emulatortests vergleichen Pixel und Zähler. Der CGB verwendet hier Bank 0; andere Banken und echte Hardware werden hier nicht geprüft.
Im CGB-Modus wiederholen die Zeilen von MEMCPY bis FILL UNSAFE die Folge Rot, Blau, Grün. Das Quadrat bei ZERO COUNT ist rot. Bei LCD ON ist das linke Quadrat blau und das rechte grün. Text bleibt schwarz. Die Prüfungen kontrollieren diese Farben an jedem gezeichneten Pixel.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/rpg.h:162
__vram_fill
__vram_fill ist ein Compileralias für __vram_memset. Es verwendet dieselbe Byteanzahl und dieselbe sofortige CPU-Übertragung einschließlich der LCD-Zugriffsregeln.
Füllt sofort len aufeinanderfolgende Bytes ab dst mit dem Byte value. Wiederholt wird ein Byte, kein vollständiges Kachelmuster. In der Karte wiederholt dies eine Kachelnummer; in Musterdaten verändert es die Bitebenen der Pixel.
Syntax
void __vram_fill(u16 dst, u8 value, u16 len);Parameter
dstAdresse des ersten Zielbytes, normalerweise im VRAM
0x8000–0x9FFF. Der gesamte Bereich muss gültig sein. Der CGB verwendet die aktuelle VRAM-Bank; im Kartenbereich enthält Bank 0 Kachelnummern und Bank 1 Attribute. Diese Routinen wählen keine Karte anhand von LCDC, führen keinen Zeilenumbruch aus und reservieren kein VRAM.valueEin wiederholtes Byte von 0 bis 255. Seine sichtbare Wirkung hängt davon ab, ob das Ziel Tile-Indizes, Pixelebenen oder eine andere Bytestruktur enthält.
lenAnzahl aufeinanderfolgender Bytes von 0 bis 65535, keine Tile-Anzahl. Der gesamte Bereich muss in gültiger Quelle und beschreibbarem Ziel liegen; die API prüft keine Grenzen und verhindert keinen 16-Bit-Adressüberlauf.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Bei eingeschaltetem LCD wartet jeder Bytezugriff unter DI auf STAT-Modus 0 oder 1; danach folgt EI. Bei ausgeschaltetem LCD läuft eine gewöhnliche CPU-Schleife. Der Aufruf kann mehrere Frames dauern, erhält keine anfängliche Interruptsperre und richtet keine DMA-Register ein. Länge null überträgt keine Daten.
Anwendungsbeispiel
__vram_fill(0x9902,1,computed_length());Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Dieses Beispiel füllt mit __vram_fill drei Kartenbytes ab Kachel (2,8), Pixel (16,64), mit Kachelnummer 1. Erwartet wird ein ausgefüllter Balken von 24 × 8 mit leeren Nachbarfeldern. Die Anzahl wird aus Variablen oder einem Funktionsergebnis berechnet; alle drei Bytes müssen dennoch den Wert 1 erhalten.
Die mitgelieferten eigenen Grafiken verwenden Kachel 1 für ein ausgefülltes Quadrat von 8 × 8, Kachel 2 für dessen vier linke Spalten und Kachel 3 für ein linksbündiges Dreieck, das je Zeile um ein Pixel von 1 auf 8 wächst. DMG zeigt Schwarz auf Weiß; CGB unterscheidet die Operationen farblich und behält schwarzen Text bei.
Das Quadrat bei Pixel (16,88) muss nach sicheren und ungeprüften Aufrufen mit Länge null erhalten bleiben. Bei aktivem LCD ergänzen sicheres Kopieren und Füllen Quadrate bei (16,96) und (32,96). FAILED CHECKS muss 000 anzeigen. Die Emulatortests vergleichen Pixel und Zähler. Der CGB verwendet hier Bank 0; andere Banken und echte Hardware werden hier nicht geprüft.
Im CGB-Modus wiederholen die Zeilen von MEMCPY bis FILL UNSAFE die Folge Rot, Blau, Grün. Das Quadrat bei ZERO COUNT ist rot. Bei LCD ON ist das linke Quadrat blau und das rechte grün. Text bleibt schwarz. Die Prüfungen kontrollieren diese Farben an jedem gezeichneten Pixel.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/rpg.h:161
__vram_memcpy
Kopiert sofort len Bytes von src nach dst in aufsteigender Adressreihenfolge. Kehrt nach Abschluss zurück und reiht keine Arbeit ein. Beide Adressen steigen jeweils um ein Byte; Kartenzeilenabstände oder Grafikformatumwandlungen werden nicht berücksichtigt.
Syntax
void __vram_memcpy(u16 dst, const void* src, u16 len);Parameter
dstAdresse des ersten Zielbytes, normalerweise im VRAM
0x8000–0x9FFF. Der gesamte Bereich muss gültig sein. Der CGB verwendet die aktuelle VRAM-Bank; im Kartenbereich enthält Bank 0 Kachelnummern und Bank 1 Attribute. Diese Routinen wählen keine Karte anhand von LCDC, führen keinen Zeilenumbruch aus und reservieren kein VRAM.srcZeiger auf die Quellbytes, während des gesamten Aufrufs gültig und korrekt eingeblendet. Daten werden sofort gelesen und nicht für einen späteren Flush gespeichert. Die Quellbank wird nicht automatisch gewechselt. Verwenden Sie nicht überlappende Bereiche; diese Vorwärtskopie bietet keine memmove-Semantik.
lenAnzahl aufeinanderfolgender Bytes von 0 bis 65535, keine Tile-Anzahl. Der gesamte Bereich muss in gültiger Quelle und beschreibbarem Ziel liegen; die API prüft keine Grenzen und verhindert keinen 16-Bit-Adressüberlauf.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Bei eingeschaltetem LCD wartet jeder Bytezugriff unter DI auf STAT-Modus 0 oder 1; danach folgt EI. Bei ausgeschaltetem LCD läuft eine gewöhnliche CPU-Schleife. Der Aufruf kann mehrere Frames dauern, erhält keine anfängliche Interruptsperre und richtet keine DMA-Register ein. Länge null überträgt keine Daten.
Anwendungsbeispiel
__vram_memcpy(0x9842,strip,length_base+length_extra); // Three map bytes at tile (2,2).Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Dieses Beispiel schreibt mit __vram_memcpy drei Kartenbytes ab Kachel (2,2), Pixel (16,16). Erwartet werden die Muster [1,2,3] von links nach rechts in einem Bereich von 24 × 8; benachbarte Felder bleiben leer. Das vollständige Programm zeigt jeden geprüften Kopier- oder Füllvorgang in einer eigenen Zeile.
Die mitgelieferten eigenen Grafiken verwenden Kachel 1 für ein ausgefülltes Quadrat von 8 × 8, Kachel 2 für dessen vier linke Spalten und Kachel 3 für ein linksbündiges Dreieck, das je Zeile um ein Pixel von 1 auf 8 wächst. DMG zeigt Schwarz auf Weiß; CGB unterscheidet die Operationen farblich und behält schwarzen Text bei.
Das Quadrat bei Pixel (16,88) muss nach sicheren und ungeprüften Aufrufen mit Länge null erhalten bleiben. Bei aktivem LCD ergänzen sicheres Kopieren und Füllen Quadrate bei (16,96) und (32,96). FAILED CHECKS muss 000 anzeigen. Die Emulatortests vergleichen Pixel und Zähler. Der CGB verwendet hier Bank 0; andere Banken und echte Hardware werden hier nicht geprüft.
Im CGB-Modus wiederholen die Zeilen von MEMCPY bis FILL UNSAFE die Folge Rot, Blau, Grün. Das Quadrat bei ZERO COUNT ist rot. Bei LCD ON ist das linke Quadrat blau und das rechte grün. Text bleibt schwarz. Die Prüfungen kontrollieren diese Farben an jedem gezeichneten Pixel.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/rpg.h:146
__vram_memset
Füllt sofort len aufeinanderfolgende Bytes ab dst mit dem Byte value. Wiederholt wird ein Byte, kein vollständiges Kachelmuster. In der Karte wiederholt dies eine Kachelnummer; in Musterdaten verändert es die Bitebenen der Pixel.
Syntax
void __vram_memset(u16 dst, u8 value, u16 len);Parameter
dstAdresse des ersten Zielbytes, normalerweise im VRAM
0x8000–0x9FFF. Der gesamte Bereich muss gültig sein. Der CGB verwendet die aktuelle VRAM-Bank; im Kartenbereich enthält Bank 0 Kachelnummern und Bank 1 Attribute. Diese Routinen wählen keine Karte anhand von LCDC, führen keinen Zeilenumbruch aus und reservieren kein VRAM.valueEin wiederholtes Byte von 0 bis 255. Seine sichtbare Wirkung hängt davon ab, ob das Ziel Tile-Indizes, Pixelebenen oder eine andere Bytestruktur enthält.
lenAnzahl aufeinanderfolgender Bytes von 0 bis 65535, keine Tile-Anzahl. Der gesamte Bereich muss in gültiger Quelle und beschreibbarem Ziel liegen; die API prüft keine Grenzen und verhindert keinen 16-Bit-Adressüberlauf.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Bei eingeschaltetem LCD wartet jeder Bytezugriff unter DI auf STAT-Modus 0 oder 1; danach folgt EI. Bei ausgeschaltetem LCD läuft eine gewöhnliche CPU-Schleife. Der Aufruf kann mehrere Frames dauern, erhält keine anfängliche Interruptsperre und richtet keine DMA-Register ein. Länge null überträgt keine Daten.
Anwendungsbeispiel
__vram_memset(0x98E2,1,length_base+length_extra); // Three solid tiles, not three tile patterns.Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Dieses Beispiel füllt mit __vram_memset drei Kartenbytes ab Kachel (2,7), Pixel (16,56), mit Kachelnummer 1. Erwartet wird ein ausgefüllter Balken von 24 × 8 mit leeren Nachbarfeldern. Die Anzahl wird aus Variablen oder einem Funktionsergebnis berechnet; alle drei Bytes müssen dennoch den Wert 1 erhalten.
Die mitgelieferten eigenen Grafiken verwenden Kachel 1 für ein ausgefülltes Quadrat von 8 × 8, Kachel 2 für dessen vier linke Spalten und Kachel 3 für ein linksbündiges Dreieck, das je Zeile um ein Pixel von 1 auf 8 wächst. DMG zeigt Schwarz auf Weiß; CGB unterscheidet die Operationen farblich und behält schwarzen Text bei.
Das Quadrat bei Pixel (16,88) muss nach sicheren und ungeprüften Aufrufen mit Länge null erhalten bleiben. Bei aktivem LCD ergänzen sicheres Kopieren und Füllen Quadrate bei (16,96) und (32,96). FAILED CHECKS muss 000 anzeigen. Die Emulatortests vergleichen Pixel und Zähler. Der CGB verwendet hier Bank 0; andere Banken und echte Hardware werden hier nicht geprüft.
Im CGB-Modus wiederholen die Zeilen von MEMCPY bis FILL UNSAFE die Folge Rot, Blau, Grün. Das Quadrat bei ZERO COUNT ist rot. Bei LCD ON ist das linke Quadrat blau und das rechte grün. Text bleibt schwarz. Die Prüfungen kontrollieren diese Farben an jedem gezeichneten Pixel.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
kitaqgb/lib/rpg.h:147
__wait_vblank
Wartet bei laufendem LCD das Ende eines aktuellen VBlank ab und anschließend auf das nächste LY≥144.
Syntax
void __wait_vblank();Rückgabewert
Kein Rückgabewert.
Bedingungen und Grenzen
Kehrt bei ausgeschaltetem LCD sofort zurück. Fragt LY ab, ohne Interrupts zu aktivieren oder einen Bibliotheks-Frame-Zähler zu erhöhen.
Anwendungsbeispiel
__wait_vblank();result[10]=*((u8*)0xFF44);M_LCDC=0;__wait_vblank();__wait_ly(200);result[11]=1;Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Lernen Sie Kartenauswahl, Scrolling, Verschachtelung und Rasterzeilenwarten. Die Basisadressen sind 38912/39936, die verschachtelten Tokens 0/1 und die beobachteten Ziel-LY-Werte 80/144. Prüfen Sie auch die Rückkehr aus Warteaufrufen bei ausgeschaltetem LCD.


Vollständiges Beispielprogramm
Build-Befehl
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=noneImplementierung und Quelltext
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
Prüft die Zugehörigkeit zu einem Rechteck einschließlich oberer/linker und ausschließlich unterer/rechter Kante.
Syntax
u8 __xy_in_rect(u8 x, u8 y, u8 rx, u8 ry, u8 rw, u8 rh);Parameter
xPunkt-X, 0..255.
yPunkt-Y, 0..255.
rxLinke Rechteckkante, 0..255.
ryObere Rechteckkante, 0..255.
rwBreite, 0..255.
rhHöhe, 0..255.
Rückgabewert
Gibt innerhalb 1 zurück, sonst 0. Bei Breite oder Höhe null ist das Rechteck leer.
Bedingungen und Grenzen
Verwendet Koordinatendifferenzen: Eine rechte Grenze über 255 läuft nicht nach links um.
Anwendungsbeispiel
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);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Prüfen Sie Innenraum, Grenzen, einen äußeren Punkt und Breite null: INSIDE=1, RIGHT EDGE=0, LEFT EDGE=0, HIGH X=1. Prüfen Sie Distanz 15, umgelaufene Distanz 254 und Array-Indizes 64/65280.


Vollständiges Beispielprogramm
Build-Befehl
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=noneImplementierung und Quelltext
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 — Hintergrund, Fenster und geteiltes Scrollen
Steuert sofortige und gepufferte Hintergrund-/Fensterverschiebung sowie bis zu acht Rasteraufteilungen. Unterscheiden Sie die von Lesefunktionen verwalteten aktuellen Werte, tatsächliche Hardwareregister und vorgemerkte Werte.
__scroll_bg_add
Addiert vorzeichenbehaftete Verschiebungen zu den nachgeführten Hintergrundkoordinaten und wendet sie sofort an.
Syntax
void __scroll_bg_add(s8 dx, s8 dy);Parameter
dxVorzeichenbehaftete horizontale Pixelverschiebung, −128 bis 127.
dyVorzeichenbehaftete vertikale Pixelverschiebung, −128 bis 127.
Rückgabewert
Kein Rückgabewert. Der betroffene Zustand und der Zeitpunkt der Wirkung werden in diesem Eintrag beschrieben.
Bedingungen und Grenzen
Es wird nicht gewartet. Sofortige Zuweisungen und Additionen gleichen das betroffene vorgemerkte Koordinatenpaar an die aktuellen Werte an und löschen dessen Änderungsflag. Auch das sofortige Setzen nur einer Achse verwirft den vorgemerkten Wert der anderen Achse desselben Paars. Ergebnisse laufen modulo 256 um. Additionen gehen von den nachgeführten aktuellen Werten aus, nicht von vorgemerkten Werten oder temporären Rasterregisterwerten.
Anwendungsbeispiel
__scroll_bg_add(-20,3);result[22]=M_SCX;result[23]=M_SCY;Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel erklärt sofortige und vorgemerkte Werte sowie den Umlauf bei vorzeichenbehafteter Addition. BG X ist vor der Übertragung 250 und danach 16; WX wechselt von 23 auf 39. −20 zu BG X ergibt 252; −40 zu WY ergibt 248. Das visuelle Beispiel zeigt außerdem ein Hintergrundrechteck und einen unabhängigen Fensterrahmen.


Vollständiges Beispielprogramm
Build-Befehl
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=64kAnwendungsbeispiel
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Bei einer Kamera an (8,16) erscheint das rote 32×24-Rechteck an Bildschirmposition (24,16). Das Fenster zeigt einen blauen 64×64-Rahmen bei (96,80) und ein grünes 16×16-Quadrat bei (112,96). Auf dem DMG sind alle Formen schwarz.


Vollständiges Beispielprogramm
Build-Befehl
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=64kImplementierung und Quelltext
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
Merkt beide Hintergrund-Scrollkoordinaten zur späteren Übertragung vor.
Syntax
void __scroll_bg_set_buffered(u8 scx, u8 scy);Parameter
scxHorizontale Hintergrundverschiebung, 0–255 Pixel; ein größerer Wert bewegt den Hintergrund nach links.
scyVertikale Hintergrundverschiebung, 0–255 Pixel; ein größerer Wert bewegt den Hintergrund nach oben.
Rückgabewert
Kein Rückgabewert. Der betroffene Zustand und der Zeitpunkt der Wirkung werden in diesem Eintrag beschrieben.
Bedingungen und Grenzen
Ändert weder nachgeführte aktuelle Werte noch Register. Übertragen Sie sie mit __scroll_flush oder Scroll_Flush; das Vormerken selbst wartet nicht auf VBlank.
Anwendungsbeispiel
__scroll_bg_set_buffered(16,24);result[14]=M_SCX;result[15]=__scroll_bg_x_get();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel erklärt sofortige und vorgemerkte Werte sowie den Umlauf bei vorzeichenbehafteter Addition. BG X ist vor der Übertragung 250 und danach 16; WX wechselt von 23 auf 39. −20 zu BG X ergibt 252; −40 zu WY ergibt 248. Das visuelle Beispiel zeigt außerdem ein Hintergrundrechteck und einen unabhängigen Fensterrahmen.


Vollständiges Beispielprogramm
Build-Befehl
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=64kAnwendungsbeispiel
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Bei einer Kamera an (8,16) erscheint das rote 32×24-Rechteck an Bildschirmposition (24,16). Das Fenster zeigt einen blauen 64×64-Rahmen bei (96,80) und ein grünes 16×16-Quadrat bei (112,96). Auf dem DMG sind alle Formen schwarz.


Vollständiges Beispielprogramm
Build-Befehl
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=64kImplementierung und Quelltext
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
Liest den nachgeführten aktuellen Wert der horizontalen Hintergrundverschiebung.
Syntax
u8 __scroll_bg_x_get();Rückgabewert
Gibt den nachgeführten aktuellen Byte-Wert von 0–255 zurück. WX enthält den hardwarebedingten Versatz von sieben Pixeln.
Bedingungen und Grenzen
Dies liest kein Hardwareregister. Noch ausstehende Pufferwerte, direkte Registerzugriffe und temporäre Werte aus Rasteraufteilungen sind nicht enthalten.
Anwendungsbeispiel
result[6]=__scroll_bg_x_get();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel erklärt sofortige und vorgemerkte Werte sowie den Umlauf bei vorzeichenbehafteter Addition. BG X ist vor der Übertragung 250 und danach 16; WX wechselt von 23 auf 39. −20 zu BG X ergibt 252; −40 zu WY ergibt 248. Das visuelle Beispiel zeigt außerdem ein Hintergrundrechteck und einen unabhängigen Fensterrahmen.


Vollständiges Beispielprogramm
Build-Befehl
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=64kAnwendungsbeispiel
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Bei einer Kamera an (8,16) erscheint das rote 32×24-Rechteck an Bildschirmposition (24,16). Das Fenster zeigt einen blauen 64×64-Rahmen bei (96,80) und ein grünes 16×16-Quadrat bei (112,96). Auf dem DMG sind alle Formen schwarz.


Vollständiges Beispielprogramm
Build-Befehl
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=64kImplementierung und Quelltext
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
Setzt die horizontale Hintergrundverschiebung sofort.
Syntax
void __scroll_bg_x_set(u8 scx);Parameter
scxHorizontale Hintergrundverschiebung, 0–255 Pixel; ein größerer Wert bewegt den Hintergrund nach links.
Rückgabewert
Kein Rückgabewert. Der betroffene Zustand und der Zeitpunkt der Wirkung werden in diesem Eintrag beschrieben.
Bedingungen und Grenzen
Es wird nicht gewartet. Sofortige Zuweisungen und Additionen gleichen das betroffene vorgemerkte Koordinatenpaar an die aktuellen Werte an und löschen dessen Änderungsflag. Auch das sofortige Setzen nur einer Achse verwirft den vorgemerkten Wert der anderen Achse desselben Paars.
Anwendungsbeispiel
__scroll_bg_x_set(250);result[4]=M_SCX;Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel erklärt sofortige und vorgemerkte Werte sowie den Umlauf bei vorzeichenbehafteter Addition. BG X ist vor der Übertragung 250 und danach 16; WX wechselt von 23 auf 39. −20 zu BG X ergibt 252; −40 zu WY ergibt 248. Das visuelle Beispiel zeigt außerdem ein Hintergrundrechteck und einen unabhängigen Fensterrahmen.


Vollständiges Beispielprogramm
Build-Befehl
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=64kAnwendungsbeispiel
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Bei einer Kamera an (8,16) erscheint das rote 32×24-Rechteck an Bildschirmposition (24,16). Das Fenster zeigt einen blauen 64×64-Rahmen bei (96,80) und ein grünes 16×16-Quadrat bei (112,96). Auf dem DMG sind alle Formen schwarz.


Vollständiges Beispielprogramm
Build-Befehl
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=64kImplementierung und Quelltext
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
Merkt nur Hintergrund-X vor.
Syntax
void __scroll_bg_x_set_buffered(u8 scx);Parameter
scxHorizontale Hintergrundverschiebung, 0–255 Pixel; ein größerer Wert bewegt den Hintergrund nach links.
Rückgabewert
Kein Rückgabewert. Der betroffene Zustand und der Zeitpunkt der Wirkung werden in diesem Eintrag beschrieben.
Bedingungen und Grenzen
Behält den vorgemerkten Wert der anderen Achse bei. Aktuelle Werte und Register bleiben bis zur Übertragung unverändert; initialisieren Sie zuerst das Koordinatenpaar.
Anwendungsbeispiel
__scroll_bg_x_set_buffered(32);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel erklärt sofortige und vorgemerkte Werte sowie den Umlauf bei vorzeichenbehafteter Addition. BG X ist vor der Übertragung 250 und danach 16; WX wechselt von 23 auf 39. −20 zu BG X ergibt 252; −40 zu WY ergibt 248. Das visuelle Beispiel zeigt außerdem ein Hintergrundrechteck und einen unabhängigen Fensterrahmen.


Vollständiges Beispielprogramm
Build-Befehl
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=64kAnwendungsbeispiel
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Bei einer Kamera an (8,16) erscheint das rote 32×24-Rechteck an Bildschirmposition (24,16). Das Fenster zeigt einen blauen 64×64-Rahmen bei (96,80) und ein grünes 16×16-Quadrat bei (112,96). Auf dem DMG sind alle Formen schwarz.


Vollständiges Beispielprogramm
Build-Befehl
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=64kImplementierung und Quelltext
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
Liest den nachgeführten aktuellen Wert der vertikalen Hintergrundverschiebung.
Syntax
u8 __scroll_bg_y_get();Rückgabewert
Gibt den nachgeführten aktuellen Byte-Wert von 0–255 zurück. WX enthält den hardwarebedingten Versatz von sieben Pixeln.
Bedingungen und Grenzen
Dies liest kein Hardwareregister. Noch ausstehende Pufferwerte, direkte Registerzugriffe und temporäre Werte aus Rasteraufteilungen sind nicht enthalten.
Anwendungsbeispiel
result[7]=__scroll_bg_y_get();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel erklärt sofortige und vorgemerkte Werte sowie den Umlauf bei vorzeichenbehafteter Addition. BG X ist vor der Übertragung 250 und danach 16; WX wechselt von 23 auf 39. −20 zu BG X ergibt 252; −40 zu WY ergibt 248. Das visuelle Beispiel zeigt außerdem ein Hintergrundrechteck und einen unabhängigen Fensterrahmen.


Vollständiges Beispielprogramm
Build-Befehl
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=64kAnwendungsbeispiel
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Bei einer Kamera an (8,16) erscheint das rote 32×24-Rechteck an Bildschirmposition (24,16). Das Fenster zeigt einen blauen 64×64-Rahmen bei (96,80) und ein grünes 16×16-Quadrat bei (112,96). Auf dem DMG sind alle Formen schwarz.


Vollständiges Beispielprogramm
Build-Befehl
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=64kImplementierung und Quelltext
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
Setzt die vertikale Hintergrundverschiebung sofort.
Syntax
void __scroll_bg_y_set(u8 scy);Parameter
scyVertikale Hintergrundverschiebung, 0–255 Pixel; ein größerer Wert bewegt den Hintergrund nach oben.
Rückgabewert
Kein Rückgabewert. Der betroffene Zustand und der Zeitpunkt der Wirkung werden in diesem Eintrag beschrieben.
Bedingungen und Grenzen
Es wird nicht gewartet. Sofortige Zuweisungen und Additionen gleichen das betroffene vorgemerkte Koordinatenpaar an die aktuellen Werte an und löschen dessen Änderungsflag. Auch das sofortige Setzen nur einer Achse verwirft den vorgemerkten Wert der anderen Achse desselben Paars.
Anwendungsbeispiel
__scroll_bg_y_set(2);result[5]=M_SCY;Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel erklärt sofortige und vorgemerkte Werte sowie den Umlauf bei vorzeichenbehafteter Addition. BG X ist vor der Übertragung 250 und danach 16; WX wechselt von 23 auf 39. −20 zu BG X ergibt 252; −40 zu WY ergibt 248. Das visuelle Beispiel zeigt außerdem ein Hintergrundrechteck und einen unabhängigen Fensterrahmen.


Vollständiges Beispielprogramm
Build-Befehl
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=64kAnwendungsbeispiel
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Bei einer Kamera an (8,16) erscheint das rote 32×24-Rechteck an Bildschirmposition (24,16). Das Fenster zeigt einen blauen 64×64-Rahmen bei (96,80) und ein grünes 16×16-Quadrat bei (112,96). Auf dem DMG sind alle Formen schwarz.


Vollständiges Beispielprogramm
Build-Befehl
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=64kImplementierung und Quelltext
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
Merkt nur Hintergrund-Y vor.
Syntax
void __scroll_bg_y_set_buffered(u8 scy);Parameter
scyVertikale Hintergrundverschiebung, 0–255 Pixel; ein größerer Wert bewegt den Hintergrund nach oben.
Rückgabewert
Kein Rückgabewert. Der betroffene Zustand und der Zeitpunkt der Wirkung werden in diesem Eintrag beschrieben.
Bedingungen und Grenzen
Behält den vorgemerkten Wert der anderen Achse bei. Aktuelle Werte und Register bleiben bis zur Übertragung unverändert; initialisieren Sie zuerst das Koordinatenpaar.
Anwendungsbeispiel
__scroll_bg_y_set_buffered(40);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel erklärt sofortige und vorgemerkte Werte sowie den Umlauf bei vorzeichenbehafteter Addition. BG X ist vor der Übertragung 250 und danach 16; WX wechselt von 23 auf 39. −20 zu BG X ergibt 252; −40 zu WY ergibt 248. Das visuelle Beispiel zeigt außerdem ein Hintergrundrechteck und einen unabhängigen Fensterrahmen.


Vollständiges Beispielprogramm
Build-Befehl
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=64kAnwendungsbeispiel
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Bei einer Kamera an (8,16) erscheint das rote 32×24-Rechteck an Bildschirmposition (24,16). Das Fenster zeigt einen blauen 64×64-Rahmen bei (96,80) und ein grünes 16×16-Quadrat bei (112,96). Auf dem DMG sind alle Formen schwarz.


Vollständiges Beispielprogramm
Build-Befehl
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=64kImplementierung und Quelltext
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
Überträgt geänderte Hintergrund- und Fensterkoordinaten in den nachgeführten Zustand und die Hardware und löscht anschließend ihre Vormerkflags.
Syntax
void __scroll_flush();Rückgabewert
Kein Rückgabewert. Der betroffene Zustand und der Zeitpunkt der Wirkung werden in diesem Eintrag beschrieben.
Bedingungen und Grenzen
Ein unverändertes Koordinatenpaar bleibt unangetastet. Es werden weder Interrupts maskiert noch wird auf VBlank gewartet; der aufrufende Code muss den passenden Zeitpunkt sicherstellen.
Anwendungsbeispiel
__scroll_flush();result[18]=M_SCX;result[19]=M_SCY;result[20]=M_WX;result[21]=M_WY;Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel erklärt sofortige und vorgemerkte Werte sowie den Umlauf bei vorzeichenbehafteter Addition. BG X ist vor der Übertragung 250 und danach 16; WX wechselt von 23 auf 39. −20 zu BG X ergibt 252; −40 zu WY ergibt 248. Das visuelle Beispiel zeigt außerdem ein Hintergrundrechteck und einen unabhängigen Fensterrahmen.


Vollständiges Beispielprogramm
Build-Befehl
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=64kAnwendungsbeispiel
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Bei einer Kamera an (8,16) erscheint das rote 32×24-Rechteck an Bildschirmposition (24,16). Das Fenster zeigt einen blauen 64×64-Rahmen bei (96,80) und ein grünes 16×16-Quadrat bei (112,96). Auf dem DMG sind alle Formen schwarz.


Vollständiges Beispielprogramm
Build-Befehl
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=64kImplementierung und Quelltext
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
Erfasst die grundlegende Fenstersichtbarkeit und aktiviert das erste Rasterzeilenereignis der Aufteilungstabelle.
Syntax
void __scroll_split_commit();Rückgabewert
Kein Rückgabewert. Der betroffene Zustand und der Zeitpunkt der Wirkung werden in diesem Eintrag beschrieben.
Bedingungen und Grenzen
Die gemeinsame Tabelle enthält acht Einträge. Das Anhängen wendet sie weder an noch sortiert es sie; überzählige Einträge werden ignoriert. Bauen Sie die Tabelle an einer sicheren Frame-Grenze mit reset→push→commit neu auf und lassen Sie zwischen Ereignissen genügend Abstand für den Handler. Wegen der Interruptlatenz müssen die geplante Zeile und das erste betroffene Pixel nicht zusammenfallen. Eine nichtleere Tabelle aktiviert VBlank/STAT in IE und führt EI aus. Eine leere Tabelle deaktiviert die LYC-Quelle, stellt aber weder IE noch den CPU-Interruptzustand wieder her.
Anwendungsbeispiel
__scroll_split_commit();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Teilen Sie den Bildschirm mit BG X=0→8→16 in drei Scrollstreifen. Drei 24×16-Rechtecke erscheinen bei (32,16), (24,80) und (16,112). Die Ereignisse sind bei LY=64 und 104 geplant. Auf dem DMG sind die Rechtecke schwarz, auf dem CGB rot.


Vollständiges Beispielprogramm
Build-Befehl
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=64kImplementierung und Quelltext
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
Fügt einen Eintrag hinzu, der die Hintergrundverschiebung beim gewünschten Rasterzeilenereignis ändert.
Syntax
void __scroll_split_push(u8 ly, u8 scx, u8 scy);Parameter
lyRasterzeilennummer des Ereignisses; verwenden Sie 0–143 für sichtbare Ereignisse und fügen Sie sie aufsteigend hinzu. Ein erster Eintrag bei null wird während VBlank angewandt.
scxHorizontale Hintergrundverschiebung, 0–255 Pixel; ein größerer Wert bewegt den Hintergrund nach links.
scyVertikale Hintergrundverschiebung, 0–255 Pixel; ein größerer Wert bewegt den Hintergrund nach oben.
Rückgabewert
Kein Rückgabewert. Der betroffene Zustand und der Zeitpunkt der Wirkung werden in diesem Eintrag beschrieben.
Bedingungen und Grenzen
Die gemeinsame Tabelle enthält acht Einträge. Das Anhängen wendet sie weder an noch sortiert es sie; überzählige Einträge werden ignoriert. Bauen Sie die Tabelle an einer sicheren Frame-Grenze mit reset→push→commit neu auf und lassen Sie zwischen Ereignissen genügend Abstand für den Handler. Wegen der Interruptlatenz müssen die geplante Zeile und das erste betroffene Pixel nicht zusammenfallen.
Anwendungsbeispiel
__scroll_split_push(64,8,0);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Teilen Sie den Bildschirm mit BG X=0→8→16 in drei Scrollstreifen. Drei 24×16-Rechtecke erscheinen bei (32,16), (24,80) und (16,112). Die Ereignisse sind bei LY=64 und 104 geplant. Auf dem DMG sind die Rechtecke schwarz, auf dem CGB rot.


Vollständiges Beispielprogramm
Build-Befehl
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=64kImplementierung und Quelltext
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
Fügt einen Aufteilungseintrag hinzu, dessen Flags Operationen für Hintergrund, Fenster, Sichtbarkeit oder Hintergrundfarbe null auswählen.
Syntax
void __scroll_split_push_ex(u8 ly, u8 scx, u8 scy, u8 wx, u8 wy, u8 flags);Parameter
lyRasterzeilennummer des Ereignisses; verwenden Sie 0–143 für sichtbare Ereignisse und fügen Sie sie aufsteigend hinzu. Ein erster Eintrag bei null wird während VBlank angewandt.
scxHorizontale Hintergrundverschiebung, 0–255 Pixel; ein größerer Wert bewegt den Hintergrund nach links.
scyVertikale Hintergrundverschiebung, 0–255 Pixel; ein größerer Wert bewegt den Hintergrund nach oben.
wxRoher WX-Byte-Wert, 0–255: Addieren Sie 7 zur gewünschten Bildschirm-X-Koordinate; für den linken Rand verwenden Sie 7.
wyOberkante des Fensters in Bildschirm-Y-Pixeln, 0–255; sichtbar sind die Zeilen 0–143.
flagsOperationsbits: BG=1, Fensterposition=2, Anzeigen=4, Ausblenden=8, CGB-Hintergrundpalette 0/Farbe 0=16. Unbekannte Bits werden verworfen. Der Modus für Farbe null nutzt scx/scy als niederwertiges/höherwertiges Byte des RGB15-Werts.
Rückgabewert
Kein Rückgabewert. Der betroffene Zustand und der Zeitpunkt der Wirkung werden in diesem Eintrag beschrieben.
Bedingungen und Grenzen
Die gemeinsame Tabelle enthält acht Einträge. Das Anhängen wendet sie weder an noch sortiert es sie; überzählige Einträge werden ignoriert. Bauen Sie die Tabelle an einer sicheren Frame-Grenze mit reset→push→commit neu auf und lassen Sie zwischen Ereignissen genügend Abstand für den Handler. Wegen der Interruptlatenz müssen die geplante Zeile und das erste betroffene Pixel nicht zusammenfallen. Sind beide Sichtbarkeitsbits gesetzt, hat Anzeigen Vorrang. Farbe null ist nur auf dem CGB verfügbar; bei Kombination mit BG-Bewegung dienen dieselben scx/scy-Bytes beiden Zwecken.
Anwendungsbeispiel
__scroll_split_push_ex(104,16,0,7,0,KQ_SCROLL_SPLIT_USE_BG|KQ_SCROLL_SPLIT_WIN_HIDE);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Teilen Sie den Bildschirm mit BG X=0→8→16 in drei Scrollstreifen. Drei 24×16-Rechtecke erscheinen bei (32,16), (24,80) und (16,112). Die Ereignisse sind bei LY=64 und 104 geplant. Auf dem DMG sind die Rechtecke schwarz, auf dem CGB rot.


Vollständiges Beispielprogramm
Build-Befehl
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=64kImplementierung und Quelltext
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
Löscht Anzahl und Index der Aufteilungen, deaktiviert ihre Verarbeitung und löscht das LYC-Interruptfreigabebit in STAT.
Syntax
void __scroll_split_reset();Rückgabewert
Kein Rückgabewert. Der betroffene Zustand und der Zeitpunkt der Wirkung werden in diesem Eintrag beschrieben.
Bedingungen und Grenzen
Die gemeinsame Tabelle enthält acht Einträge. Das Anhängen wendet sie weder an noch sortiert es sie; überzählige Einträge werden ignoriert. Bauen Sie die Tabelle an einer sicheren Frame-Grenze mit reset→push→commit neu auf und lassen Sie zwischen Ereignissen genügend Abstand für den Handler. Wegen der Interruptlatenz müssen die geplante Zeile und das erste betroffene Pixel nicht zusammenfallen. Löscht weder Tabellenbytes noch IE und deaktiviert keine CPU-Interrupts.
Anwendungsbeispiel
__scroll_split_reset();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Teilen Sie den Bildschirm mit BG X=0→8→16 in drei Scrollstreifen. Drei 24×16-Rechtecke erscheinen bei (32,16), (24,80) und (16,112). Die Ereignisse sind bei LY=64 und 104 geplant. Auf dem DMG sind die Rechtecke schwarz, auf dem CGB rot.


Vollständiges Beispielprogramm
Build-Befehl
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=64kImplementierung und Quelltext
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
Addiert vorzeichenbehaftete Verschiebungen zu den nachgeführten Fensterkoordinaten und wendet sie sofort an.
Syntax
void __scroll_win_add(s8 dx, s8 dy);Parameter
dxVorzeichenbehaftete horizontale Pixelverschiebung, −128 bis 127.
dyVorzeichenbehaftete vertikale Pixelverschiebung, −128 bis 127.
Rückgabewert
Kein Rückgabewert. Der betroffene Zustand und der Zeitpunkt der Wirkung werden in diesem Eintrag beschrieben.
Bedingungen und Grenzen
Es wird nicht gewartet. Sofortige Zuweisungen und Additionen gleichen das betroffene vorgemerkte Koordinatenpaar an die aktuellen Werte an und löschen dessen Änderungsflag. Auch das sofortige Setzen nur einer Achse verwirft den vorgemerkten Wert der anderen Achse desselben Paars. Ergebnisse laufen modulo 256 um. Additionen gehen von den nachgeführten aktuellen Werten aus, nicht von vorgemerkten Werten oder temporären Rasterregisterwerten.
Anwendungsbeispiel
__scroll_win_add(4,-40);result[24]=M_WX;result[25]=M_WY;Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel erklärt sofortige und vorgemerkte Werte sowie den Umlauf bei vorzeichenbehafteter Addition. BG X ist vor der Übertragung 250 und danach 16; WX wechselt von 23 auf 39. −20 zu BG X ergibt 252; −40 zu WY ergibt 248. Das visuelle Beispiel zeigt außerdem ein Hintergrundrechteck und einen unabhängigen Fensterrahmen.


Vollständiges Beispielprogramm
Build-Befehl
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=64kAnwendungsbeispiel
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Bei einer Kamera an (8,16) erscheint das rote 32×24-Rechteck an Bildschirmposition (24,16). Das Fenster zeigt einen blauen 64×64-Rahmen bei (96,80) und ein grünes 16×16-Quadrat bei (112,96). Auf dem DMG sind alle Formen schwarz.


Vollständiges Beispielprogramm
Build-Befehl
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=64kImplementierung und Quelltext
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
Löscht das Fensterbit in LCDC und den nachgeführten Sichtbarkeitszustand des Fensters.
Syntax
void __scroll_win_hide();Rückgabewert
Kein Rückgabewert. Der betroffene Zustand und der Zeitpunkt der Wirkung werden in diesem Eintrag beschrieben.
Bedingungen und Grenzen
Ändert weder Koordinaten noch Kartenauswahl. Sichtbarkeit setzt außerdem ein eingeschaltetes LCD, aktivierte Hintergrund-/Fensterfunktionen und eine Fensterposition im sichtbaren Bereich voraus.
Anwendungsbeispiel
__scroll_win_hide();result[27]=M_LCDC&32;Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel erklärt sofortige und vorgemerkte Werte sowie den Umlauf bei vorzeichenbehafteter Addition. BG X ist vor der Übertragung 250 und danach 16; WX wechselt von 23 auf 39. −20 zu BG X ergibt 252; −40 zu WY ergibt 248. Das visuelle Beispiel zeigt außerdem ein Hintergrundrechteck und einen unabhängigen Fensterrahmen.


Vollständiges Beispielprogramm
Build-Befehl
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=64kAnwendungsbeispiel
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Bei einer Kamera an (8,16) erscheint das rote 32×24-Rechteck an Bildschirmposition (24,16). Das Fenster zeigt einen blauen 64×64-Rahmen bei (96,80) und ein grünes 16×16-Quadrat bei (112,96). Auf dem DMG sind alle Formen schwarz.


Vollständiges Beispielprogramm
Build-Befehl
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=64kImplementierung und Quelltext
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
Setzt WX und WY des Fensters sofort.
Syntax
void __scroll_win_set(u8 wx, u8 wy);Parameter
wxRoher WX-Byte-Wert, 0–255: Addieren Sie 7 zur gewünschten Bildschirm-X-Koordinate; für den linken Rand verwenden Sie 7.
wyOberkante des Fensters in Bildschirm-Y-Pixeln, 0–255; sichtbar sind die Zeilen 0–143.
Rückgabewert
Kein Rückgabewert. Der betroffene Zustand und der Zeitpunkt der Wirkung werden in diesem Eintrag beschrieben.
Bedingungen und Grenzen
Es wird nicht gewartet. Sofortige Zuweisungen und Additionen gleichen das betroffene vorgemerkte Koordinatenpaar an die aktuellen Werte an und löschen dessen Änderungsflag. Auch das sofortige Setzen nur einer Achse verwirft den vorgemerkten Wert der anderen Achse desselben Paars.
Anwendungsbeispiel
__scroll_win_set(15,8);result[8]=M_WX;result[9]=M_WY;Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel erklärt sofortige und vorgemerkte Werte sowie den Umlauf bei vorzeichenbehafteter Addition. BG X ist vor der Übertragung 250 und danach 16; WX wechselt von 23 auf 39. −20 zu BG X ergibt 252; −40 zu WY ergibt 248. Das visuelle Beispiel zeigt außerdem ein Hintergrundrechteck und einen unabhängigen Fensterrahmen.


Vollständiges Beispielprogramm
Build-Befehl
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=64kAnwendungsbeispiel
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Bei einer Kamera an (8,16) erscheint das rote 32×24-Rechteck an Bildschirmposition (24,16). Das Fenster zeigt einen blauen 64×64-Rahmen bei (96,80) und ein grünes 16×16-Quadrat bei (112,96). Auf dem DMG sind alle Formen schwarz.


Vollständiges Beispielprogramm
Build-Befehl
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=64kImplementierung und Quelltext
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
Merkt beide Fensterkoordinaten zur späteren Übertragung vor.
Syntax
void __scroll_win_set_buffered(u8 wx, u8 wy);Parameter
wxRoher WX-Byte-Wert, 0–255: Addieren Sie 7 zur gewünschten Bildschirm-X-Koordinate; für den linken Rand verwenden Sie 7.
wyOberkante des Fensters in Bildschirm-Y-Pixeln, 0–255; sichtbar sind die Zeilen 0–143.
Rückgabewert
Kein Rückgabewert. Der betroffene Zustand und der Zeitpunkt der Wirkung werden in diesem Eintrag beschrieben.
Bedingungen und Grenzen
Ändert weder nachgeführte aktuelle Werte noch Register. Übertragen Sie sie mit __scroll_flush oder Scroll_Flush; das Vormerken selbst wartet nicht auf VBlank.
Anwendungsbeispiel
__scroll_win_set_buffered(39,32);result[16]=M_WX;result[17]=__scroll_win_x_get();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel erklärt sofortige und vorgemerkte Werte sowie den Umlauf bei vorzeichenbehafteter Addition. BG X ist vor der Übertragung 250 und danach 16; WX wechselt von 23 auf 39. −20 zu BG X ergibt 252; −40 zu WY ergibt 248. Das visuelle Beispiel zeigt außerdem ein Hintergrundrechteck und einen unabhängigen Fensterrahmen.


Vollständiges Beispielprogramm
Build-Befehl
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=64kAnwendungsbeispiel
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Bei einer Kamera an (8,16) erscheint das rote 32×24-Rechteck an Bildschirmposition (24,16). Das Fenster zeigt einen blauen 64×64-Rahmen bei (96,80) und ein grünes 16×16-Quadrat bei (112,96). Auf dem DMG sind alle Formen schwarz.


Vollständiges Beispielprogramm
Build-Befehl
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=64kImplementierung und Quelltext
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
Aktiviert das Fensterbit in LCDC und den nachgeführten Sichtbarkeitszustand des Fensters.
Syntax
void __scroll_win_show();Rückgabewert
Kein Rückgabewert. Der betroffene Zustand und der Zeitpunkt der Wirkung werden in diesem Eintrag beschrieben.
Bedingungen und Grenzen
Ändert weder Koordinaten noch Kartenauswahl. Sichtbarkeit setzt außerdem ein eingeschaltetes LCD, aktivierte Hintergrund-/Fensterfunktionen und eine Fensterposition im sichtbaren Bereich voraus.
Anwendungsbeispiel
__scroll_win_show();result[26]=M_LCDC&32;Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel erklärt sofortige und vorgemerkte Werte sowie den Umlauf bei vorzeichenbehafteter Addition. BG X ist vor der Übertragung 250 und danach 16; WX wechselt von 23 auf 39. −20 zu BG X ergibt 252; −40 zu WY ergibt 248. Das visuelle Beispiel zeigt außerdem ein Hintergrundrechteck und einen unabhängigen Fensterrahmen.


Vollständiges Beispielprogramm
Build-Befehl
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=64kAnwendungsbeispiel
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Bei einer Kamera an (8,16) erscheint das rote 32×24-Rechteck an Bildschirmposition (24,16). Das Fenster zeigt einen blauen 64×64-Rahmen bei (96,80) und ein grünes 16×16-Quadrat bei (112,96). Auf dem DMG sind alle Formen schwarz.


Vollständiges Beispielprogramm
Build-Befehl
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=64kImplementierung und Quelltext
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
Liest den nachgeführten aktuellen WX-Wert des Fensters.
Syntax
u8 __scroll_win_x_get();Rückgabewert
Gibt den nachgeführten aktuellen Byte-Wert von 0–255 zurück. WX enthält den hardwarebedingten Versatz von sieben Pixeln.
Bedingungen und Grenzen
Dies liest kein Hardwareregister. Noch ausstehende Pufferwerte, direkte Registerzugriffe und temporäre Werte aus Rasteraufteilungen sind nicht enthalten.
Anwendungsbeispiel
result[12]=__scroll_win_x_get();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel erklärt sofortige und vorgemerkte Werte sowie den Umlauf bei vorzeichenbehafteter Addition. BG X ist vor der Übertragung 250 und danach 16; WX wechselt von 23 auf 39. −20 zu BG X ergibt 252; −40 zu WY ergibt 248. Das visuelle Beispiel zeigt außerdem ein Hintergrundrechteck und einen unabhängigen Fensterrahmen.


Vollständiges Beispielprogramm
Build-Befehl
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=64kAnwendungsbeispiel
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Bei einer Kamera an (8,16) erscheint das rote 32×24-Rechteck an Bildschirmposition (24,16). Das Fenster zeigt einen blauen 64×64-Rahmen bei (96,80) und ein grünes 16×16-Quadrat bei (112,96). Auf dem DMG sind alle Formen schwarz.


Vollständiges Beispielprogramm
Build-Befehl
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=64kImplementierung und Quelltext
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
Setzt sofort nur die rohe WX-Koordinate des Fensters.
Syntax
void __scroll_win_x_set(u8 wx);Parameter
wxRoher WX-Byte-Wert, 0–255: Addieren Sie 7 zur gewünschten Bildschirm-X-Koordinate; für den linken Rand verwenden Sie 7.
Rückgabewert
Kein Rückgabewert. Der betroffene Zustand und der Zeitpunkt der Wirkung werden in diesem Eintrag beschrieben.
Bedingungen und Grenzen
Es wird nicht gewartet. Sofortige Zuweisungen und Additionen gleichen das betroffene vorgemerkte Koordinatenpaar an die aktuellen Werte an und löschen dessen Änderungsflag. Auch das sofortige Setzen nur einer Achse verwirft den vorgemerkten Wert der anderen Achse desselben Paars.
Anwendungsbeispiel
__scroll_win_x_set(23);result[10]=M_WX;Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel erklärt sofortige und vorgemerkte Werte sowie den Umlauf bei vorzeichenbehafteter Addition. BG X ist vor der Übertragung 250 und danach 16; WX wechselt von 23 auf 39. −20 zu BG X ergibt 252; −40 zu WY ergibt 248. Das visuelle Beispiel zeigt außerdem ein Hintergrundrechteck und einen unabhängigen Fensterrahmen.


Vollständiges Beispielprogramm
Build-Befehl
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=64kAnwendungsbeispiel
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Bei einer Kamera an (8,16) erscheint das rote 32×24-Rechteck an Bildschirmposition (24,16). Das Fenster zeigt einen blauen 64×64-Rahmen bei (96,80) und ein grünes 16×16-Quadrat bei (112,96). Auf dem DMG sind alle Formen schwarz.


Vollständiges Beispielprogramm
Build-Befehl
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=64kImplementierung und Quelltext
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
Merkt nur Fenster-WX vor.
Syntax
void __scroll_win_x_set_buffered(u8 wx);Parameter
wxRoher WX-Byte-Wert, 0–255: Addieren Sie 7 zur gewünschten Bildschirm-X-Koordinate; für den linken Rand verwenden Sie 7.
Rückgabewert
Kein Rückgabewert. Der betroffene Zustand und der Zeitpunkt der Wirkung werden in diesem Eintrag beschrieben.
Bedingungen und Grenzen
Behält den vorgemerkten Wert der anderen Achse bei. Aktuelle Werte und Register bleiben bis zur Übertragung unverändert; initialisieren Sie zuerst das Koordinatenpaar.
Anwendungsbeispiel
__scroll_win_x_set_buffered(55);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel erklärt sofortige und vorgemerkte Werte sowie den Umlauf bei vorzeichenbehafteter Addition. BG X ist vor der Übertragung 250 und danach 16; WX wechselt von 23 auf 39. −20 zu BG X ergibt 252; −40 zu WY ergibt 248. Das visuelle Beispiel zeigt außerdem ein Hintergrundrechteck und einen unabhängigen Fensterrahmen.


Vollständiges Beispielprogramm
Build-Befehl
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=64kAnwendungsbeispiel
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Bei einer Kamera an (8,16) erscheint das rote 32×24-Rechteck an Bildschirmposition (24,16). Das Fenster zeigt einen blauen 64×64-Rahmen bei (96,80) und ein grünes 16×16-Quadrat bei (112,96). Auf dem DMG sind alle Formen schwarz.


Vollständiges Beispielprogramm
Build-Befehl
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=64kImplementierung und Quelltext
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
Liest den nachgeführten aktuellen WY-Wert des Fensters.
Syntax
u8 __scroll_win_y_get();Rückgabewert
Gibt den nachgeführten aktuellen Byte-Wert von 0–255 zurück. WX enthält den hardwarebedingten Versatz von sieben Pixeln.
Bedingungen und Grenzen
Dies liest kein Hardwareregister. Noch ausstehende Pufferwerte, direkte Registerzugriffe und temporäre Werte aus Rasteraufteilungen sind nicht enthalten.
Anwendungsbeispiel
result[13]=__scroll_win_y_get();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel erklärt sofortige und vorgemerkte Werte sowie den Umlauf bei vorzeichenbehafteter Addition. BG X ist vor der Übertragung 250 und danach 16; WX wechselt von 23 auf 39. −20 zu BG X ergibt 252; −40 zu WY ergibt 248. Das visuelle Beispiel zeigt außerdem ein Hintergrundrechteck und einen unabhängigen Fensterrahmen.


Vollständiges Beispielprogramm
Build-Befehl
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=64kAnwendungsbeispiel
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Bei einer Kamera an (8,16) erscheint das rote 32×24-Rechteck an Bildschirmposition (24,16). Das Fenster zeigt einen blauen 64×64-Rahmen bei (96,80) und ein grünes 16×16-Quadrat bei (112,96). Auf dem DMG sind alle Formen schwarz.


Vollständiges Beispielprogramm
Build-Befehl
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=64kImplementierung und Quelltext
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
Setzt sofort nur die WY-Koordinate des Fensters.
Syntax
void __scroll_win_y_set(u8 wy);Parameter
wyOberkante des Fensters in Bildschirm-Y-Pixeln, 0–255; sichtbar sind die Zeilen 0–143.
Rückgabewert
Kein Rückgabewert. Der betroffene Zustand und der Zeitpunkt der Wirkung werden in diesem Eintrag beschrieben.
Bedingungen und Grenzen
Es wird nicht gewartet. Sofortige Zuweisungen und Additionen gleichen das betroffene vorgemerkte Koordinatenpaar an die aktuellen Werte an und löschen dessen Änderungsflag. Auch das sofortige Setzen nur einer Achse verwirft den vorgemerkten Wert der anderen Achse desselben Paars.
Anwendungsbeispiel
__scroll_win_y_set(16);result[11]=M_WY;Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel erklärt sofortige und vorgemerkte Werte sowie den Umlauf bei vorzeichenbehafteter Addition. BG X ist vor der Übertragung 250 und danach 16; WX wechselt von 23 auf 39. −20 zu BG X ergibt 252; −40 zu WY ergibt 248. Das visuelle Beispiel zeigt außerdem ein Hintergrundrechteck und einen unabhängigen Fensterrahmen.


Vollständiges Beispielprogramm
Build-Befehl
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=64kAnwendungsbeispiel
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Bei einer Kamera an (8,16) erscheint das rote 32×24-Rechteck an Bildschirmposition (24,16). Das Fenster zeigt einen blauen 64×64-Rahmen bei (96,80) und ein grünes 16×16-Quadrat bei (112,96). Auf dem DMG sind alle Formen schwarz.


Vollständiges Beispielprogramm
Build-Befehl
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=64kImplementierung und Quelltext
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
Merkt nur Fenster-WY vor.
Syntax
void __scroll_win_y_set_buffered(u8 wy);Parameter
wyOberkante des Fensters in Bildschirm-Y-Pixeln, 0–255; sichtbar sind die Zeilen 0–143.
Rückgabewert
Kein Rückgabewert. Der betroffene Zustand und der Zeitpunkt der Wirkung werden in diesem Eintrag beschrieben.
Bedingungen und Grenzen
Behält den vorgemerkten Wert der anderen Achse bei. Aktuelle Werte und Register bleiben bis zur Übertragung unverändert; initialisieren Sie zuerst das Koordinatenpaar.
Anwendungsbeispiel
__scroll_win_y_set_buffered(48);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel erklärt sofortige und vorgemerkte Werte sowie den Umlauf bei vorzeichenbehafteter Addition. BG X ist vor der Übertragung 250 und danach 16; WX wechselt von 23 auf 39. −20 zu BG X ergibt 252; −40 zu WY ergibt 248. Das visuelle Beispiel zeigt außerdem ein Hintergrundrechteck und einen unabhängigen Fensterrahmen.


Vollständiges Beispielprogramm
Build-Befehl
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=64kAnwendungsbeispiel
Camera_Init(&view);
Camera_Set(&view,2048,4096);
Camera_ApplyBg(&view);
Scroll_SetWindow(103,80);
Scroll_WindowShow();Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Bei einer Kamera an (8,16) erscheint das rote 32×24-Rechteck an Bildschirmposition (24,16). Das Fenster zeigt einen blauen 64×64-Rahmen bei (96,80) und ein grünes 16×16-Quadrat bei (112,96). Auf dem DMG sind alle Formen schwarz.


Vollständiges Beispielprogramm
Build-Befehl
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=64kImplementierung und Quelltext
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 — OBJ-Reservierung, Positionierung, Metasprites und Animation
Sprites sind bewegliche Hardwareobjekte, meist 8×8 oder 8×16 Pixel groß, die durch Einträge im Object Attribute Memory (OAM) beschrieben werden. Reservieren Sie Einträge, setzen Sie Position, Tile und Attribute im Arbeitspuffer und übertragen Sie ihn anschließend. Ein Metasprite fasst mehrere Einträge zu einem Objekt zusammen. GB bietet 40 Einträge mit höchstens 10 pro Zeile; FC bietet 64 mit höchstens 8 pro Zeile.
GB-Attribute: Bit 4 wählt OBP1 auf DMG, Bits 5/6 spiegeln X/Y, Bit 7 legt das Sprite hinter Hintergrundpixel mit Farbindex ungleich null. Auf CGB wählen Bits 0..2 die Objektpalette und Bit 3 die VRAM-Bank der Tile-Daten; das DMG-Palettenbit wählt keine nativen CGB-Farben.
Das Beispiel nutzt die vollständige GB-Bibliothek. KQ_SPRITE_MINIMAL_RUNTIME entfernt sprite_free, sprite_set_pos, sprite_count_used, beide Zeilenabfragen, metasprite_draw und anim_update aus sprite.c. Definieren Sie es nicht, wenn Sie diese Funktionen verwenden.
__oam_dma
Kopiert 160 Byte aus einer Speicherseite in das Hardware-OAM (0xFE00–0xFE9F) und ersetzt damit alle 40 Sprite-Einträge. Die Funktion kehrt nach Abschluss der Übertragung zurück.
Syntax
void __oam_dma(u16 src_ptr);Parameter
src_ptrQuelladresse in Byte. Nur das obere Byte wird verwendet:
0xC480wählt0xC400–0xC49F. Stellen Sie 160 lesbare Byte an einer durch 256 teilbaren Adresse bereit. Verwenden Sie eine unterstützte Quellseite (0x00–0xDF) und halten Sie die benötigte ROM- oder RAM-Bank eingeblendet. Der Compiler prüft weder die Adresse noch die Ausrichtung.
Rückgabewert
Keiner (void).
Bedingungen und Grenzen
Jeder Eintrag umfasst vier Byte: Y, X, Kachelnummer und Attribute. Die Bildschirmkoordinaten sind (X−8, Y−16). Mit Y gleich 0 wird ein unbenutzter Eintrag ausgeblendet. Diese intrinsische Funktion kopiert Rohdaten; sie reserviert keine Sprites und aktualisiert weder Zähler noch Schattenpuffer der Sprite-Bibliothek.
Rufen Sie die Funktion während des VBlank oder bei ausgeschaltetem LCD auf. Sie wartet nicht auf VBlank und ändert keine LCD-Einstellungen. Die Hardware-Übertragung dauert 160 CPU-Maschinenzyklen; Vorbereitung und Rückkehr der Funktion benötigen zusätzliche Zeit.
Programme mit diesem Aufruf reservieren 8 Byte HRAM für die Übertragungsroutine. Sie wird bei jedem Aufruf initialisiert und wartet während des DMA in HRAM. Interrupt-Quellen werden vorübergehend maskiert; IE und der Aktivierungszustand von IME bleiben erhalten. Die Interrupt-Behandlung wird bis zum Ende der Übertragung verzögert.
Anwendungsbeispiel
// raw_oam is a 160-byte array at address 0xC400.
m_wait();
__oam_dma((u16)raw_oam);Dieser Ausschnitt zeigt den Aufruf. Beachten Sie beim Einbau in ein vollständiges Programm die angegebenen Anforderungen an Initialisierung und Puffer.
Erwartetes Ergebnis
Das Beispiel erstellt direkt eine vollständige OAM-Seite. Es zeigt einen 8×8-Pfeil nach rechts bei (88,32), sein horizontales Spiegelbild bei (88,56) und ein 8×8-Quadrat bei (88,80). Auf dem CGB hat der Pfeil rote Ränder, einen grünen Schaft und blaue Details an der Spitze; das Quadrat ist grün. Der DMG verwendet Graustufen. Alle übrigen Einträge sind ausgeblendet. Die Prüfung vergleicht die tatsächlichen Bildpixel in beiden Hardware-Modi des Emulators; echte Hardware wurde nicht getestet.


Vollständiges Beispielprogramm
Build-Befehl
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-disasmImplementierung und Quelltext
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;
}Referenz der Befehlssyntax
Die folgende Hilfe wurde für diese Quellenausgabe aufgezeichnet. Sie erläutert optionale Argumente, Standardwerte und zulässige Werte. Ersetzen Sie die beispielhaften Eingabedateinamen durch Ihre eigenen Ausgaben. Aufgezeichnete Werkzeugantworten behalten ihren ursprünglichen Wortlaut.
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.Syntax der Entwicklungsbefehle
Diese Aufrufschemata stammen aus Program.DebugTools.cs und Program.VibeTools.cs. ROM-Vergleich, Symbolsuche und Vorlagen sind eigene Unterbefehle neben der gewöhnlichen C-Kompilierung.
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]Vom Parser erkannte lange Optionen
Die Schreibweisen wurden aus src/cli.rs entnommen, einschließlich der Kompatibilitäts- und Untersuchungsoptionen. Ob eine Option einen Wert erwartet, entnehmen Sie der Hilfe und der zugehörigen Verarbeitungsroutine.
--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-outAnhang: Verzeichnis der Assemblerbefehle
Diese Tabelle wurde aus src/asm_info.rs erstellt. Sie beschreibt die internen Befehlsnamen und Operandenformate des Compilers. Einträge mit Sprungzielen oder Speicheradressen sind Syntaxbeispiele und keine eigenständigen Programme. Informationen zu erhaltenen Registern und veränderten Flags finden Sie in den Aufrufkonventionen und der Codegenerierung.
| opcode | Befehl | Format | Syntaxbeispiel |
|---|---|---|---|
| 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 |