KITAQ ENTWICKLUNGSSYSTEM
BENUTZERHANDBUCH 2026.09

KITAQ SERIES • REFERENZAUSGABE

KITAQGB

C-Programmierung für Game Boy

Von der ersten Codezeile zur Ausführung, Beobachtung und erneuten Prüfung.
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.

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

kitaqgb · 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.

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

1. KITAQGB kennenlernen

KITAQGB ist ein Compiler der C-Sprachfamilie für GB/CGB, der aus Zachtronics’ NORCAL hervorgegangen ist.

NORCAL ist nach Nordkalifornien (Northern California) benannt. Diese geografische Namensgebung inspirierte den Autor dazu, KITAQGB nach Kitakyushu zu benennen, der Stadt, in der er geboren wurde und aufgewachsen ist. KITAQ + GB verbindet Game Boy mit 北九 (キタキュー, Kitakyū), dem Kurznamen von Kitakyushu in der japanischen Präfektur Fukuoka. KITAQ wird wie das japanische „キタキュー“ ausgesprochen. Als englische Aussprachehilfe dient kee-tah-KYOO, in Lautschrift /ˌkiːtɑːˈkjuː/. Das abschließende Q klingt wie der englische Buchstabenname Q. KITAQGB lautet auf Englisch kee-tah-KYOO jee bee; G und B werden einzeln ausgesprochen.

Der Name KITAQGB hat zwei Bedeutungen. Kernel-Informed Toolchain for AI-Quality Game Boy Development steht für das Ziel einer Toolchain, die die Zielhardware versteht und sowohl Menschen beim Programmieren als auch generative KI unterstützt.

Die zweite Bedeutung lautet Kids' Imagination Transformed into Actual Quests in Game Boy Forests: ein Werkzeug, das die Fantasie von Kindern in echte Abenteuer in den Wäldern des Game Boy verwandelt. Darin steckt der kreative Wunsch, aus kleinen Ideen, Kritzeleien und mit KI erstellten Prototypen Abenteuer zu machen, die man tatsächlich spielen kann.

Er liest C-Dateien, erzeugt CPU-Befehle und verpackt sie in eine ROM. Sprache, Bibliotheken und Aufrufkonventionen unterscheiden sich von einem C-Compiler für Desktop-Computer.

Der GB besitzt eine 8-Bit-CPU und wenig Speicher. Die Bildschirmgrafik besteht überwiegend aus Kacheln mit 8 × 8 Pixeln. Sprites sind kleine Bilder, die sich unabhängig positionieren lassen. Auch Text benötigt Kachelgrafik; eine allgemeine eingebaute Textausgabe wird nicht vorausgesetzt. Die Beispiele verwenden die Glyphen aus der ascii.c des Autors, für GB nach ASCII-Codes angeordnet, ohne ihre Bits zu verändern. Die FC-Ausgabe überträgt dieselben Formen in das NES-CHR-Layout.

2. Voraussetzungen und Compiler-Build

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.

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

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.

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

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.

3. Das erste Programm

Die mitgelieferte samples/gb_hello.c nutzt Bildschirm- und Texthilfen aus gb_common.h. Bewahren Sie diesen Header zusammen mit den Schriftdateien auf. #include liest Deklarationen oder Definitionen aus einer anderen Datei ein.

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

Auf dem Bildschirm sollen HELLO WORLD und 042 erscheinen. Namen mit m_ sind Lernhilfen aus dem gemeinsamen Beispielheader, keine Standardbefehle von KITAQGB. Zu den Compiler-Intrinsics gehören dagegen etwa __wait_vblank und __vram_copy.

4. Grundsyntax und Typen

Anweisungen enden mit ;, Anweisungsblöcke stehen in { }. // beginnt einen Zeilenkommentar, /* ... */ einen Blockkommentar. Groß- und Kleinschreibung werden unterschieden. Verwenden Sie void main() als Einstiegspunkt. In dieser GB-Version führt void main(void) zu einem Syntaxfehler. Übernehmen Sie deshalb die FC-Deklaration nicht unverändert.

TypBedeutung und Wertebereich
u8Vorzeichenlose 8-Bit-Ganzzahl, 0 bis 255
s8Vorzeichenbehaftete 8-Bit-Ganzzahl, −128 bis 127
u16Vorzeichenlose 16-Bit-Ganzzahl, 0 bis 65535
s16Vorzeichenbehaftete 16-Bit-Ganzzahl, −32768 bis 32767
voidKein Rückgabewert
T*Zeiger auf Daten des Typs T

Beginnen Sie mit diesen kurzen Typnamen. Setzen Sie die Desktop-Bedeutung von int, long, float, double oder Standardheadern nicht voraus. Diese Version behandelt char als vorzeichenlose 8-Bit-Daten. Verwenden Sie ausdrücklich s8 oder s16, wenn Sie vorzeichenbehaftete Arithmetik benötigen.

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

(u16) ist eine Typumwandlung. Wenn ein kleiner Wert bereits übergelaufen ist, stellt die Zuweisung an eine größere Variable die verlorenen Bits nicht wieder her. Erweitern Sie die Operanden vor der Rechnung. Für Bewegungen mit Bruchteilen verwenden Sie die Festkommabibliothek.

5. Ausdrücke und Operatoren

GruppeOperatorenBeispiel oder Bedeutung
Arithmetik+ - * / %n / 10 liefert den ganzzahligen Quotienten, n % 10 den Rest
Vergleich== != < <= > >=lives == 0 prüft auf Gleichheit
Logik! && / ||Verneinung, beide Bedingungen, mindestens eine Bedingung
Bits& / | / ^ ~ << >>Tastenmasken und andere Bitmengen
Zuweisung= += -= und verwandte Formenx += 1 verändert einen Wert
Inkrement++ --i++ erhöht um eins
Auswahlcondition ? A : BJe nach Bedingung einen Wert auswählen
Zeiger&variable / *pAdresse ermitteln oder auf das Ziel zugreifen

Unterscheiden Sie = und ==. Klammern machen komplexe Ausdrücke verständlicher. Packen Sie nicht zu viele Aufrufe und Nebenwirkungen in eine Anweisung. Vermeiden Sie Division durch null und Array-Zugriffe außerhalb der Grenzen. sizeof liefert eine Größe in Bytes, offsetof den Versatz eines Strukturmitglieds.

Ergebnisse und Auswertung von Ausdrücken

Die Vergleichsoperatoren ==, !=, <, <=, >, >= und die logischen Operatoren !, &&, || liefern 0 für falsch und 1 für wahr. Das Ergebnis lässt sich in einem u16 speichern, als Argument übergeben, zurückgeben oder in Berechnungen verwenden. Beispielsweise ergibt score = 500 + (lives != 0); den Wert 501, wenn noch ein Leben vorhanden ist, andernfalls 500.

&& wertet den rechten Operanden nicht aus, wenn der linke null ist. || überspringt den rechten Operanden, wenn der linke ungleich null ist. Bei pointer != 0 && pointer->active != 0 verhindert ein Nullpointer den Memberzugriff. Beide Bytes eines 16-Bit-Werts gehen in die Wahrheitsprüfung ein; 256 ist daher wahr. Die bitweisen Operatoren & und | haben keine Kurzschlussauswertung.

++value liefert den aktualisierten Wert, value++ den ursprünglichen. Diese Operatoren sind auch auf Arrayelemente, dereferenzierte Pointer und Strukturmember anwendbar. buffer[index()]++ ruft index() genau einmal auf. Bei u16 *p rückt p++ um zwei Bytes zum nächsten Element vor; (*p)++ erhöht dagegen den Wert am Ziel des Pointers.

sizeof(array) liefert die Größe des gesamten Arrays in Bytes; sizeof(pointer) ist 2. Für u16 values[9]; ist sizeof(values) gleich 18. Das gilt auch für ROM-Arrays, lokale Arrays und Arraymember. sizeof(function()) untersucht den Rückgabetyp, ohne die Funktion aufzurufen.

Deklaration und Definition einer Funktion müssen in Parametertypen und Reihenfolge übereinstimmen. Die Parameternamen dürfen verschieden sein; im Funktionsrumpf gelten die Namen der Definition. Beispielsweise kann u8 next(u8 input); als u8 next(u8 value) { return (u8)(value + 1); } definiert werden. Parameter und lokale Variablen verdecken gleichnamige globale Variablen.

Die Auswahl von Arrays oder Zeichenketten mit ?: ergibt einen Pointer auf den gewählten Elementtyp. Dieser lässt sich direkt übergeben, etwa mit show(ready ? "READY" : "WAIT");. Bei den u16-Arrays a und b rückt (ready ? a : b) + 1 um zwei Bytes zum zweiten Element des gewählten Arrays vor. Das Array wird dabei nicht kopiert.

condition ? yes : no wertet zuerst die Bedingung und danach nur den gewählten Zweig aus. Bedingung und Zweige dürfen Schiebeoperationen mit variabler Bitanzahl enthalten. Beispielsweise wählt on = (pattern & (0x80 >> bit)) != 0 ? 4 : 2; abhängig vom bezeichneten Bit vier oder zwei. Funktionsaufrufe in einem durch && oder || übersprungenen rechten Operanden werden ebenfalls nicht ausgeführt. Ein continue in einer for-Schleife führt vor der erneuten Bedingungsprüfung den Aktualisierungsausdruck einmal aus; bei while und do ... while springt es zur Bedingung.

6. Verzweigungen und Schleifen

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

break verlässt eine Schleife oder switch-Anweisung, continue beginnt den nächsten Schleifendurchlauf und return verlässt eine Funktion. Wenn ein switch-Zweig ausdrücklich im folgenden Zweig fortgesetzt werden soll, schreiben Sie fallthrough;. Unbeabsichtigtes Weiterlaufen wird diagnostiziert. Das vollständige Beispiel finden Sie in gb_control.c.

7. Funktionen, Arrays und Strukturen

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

Array-Indizes beginnen bei null. Ein Array mit vier Elementen hat die Indizes 0 bis 3. player.x wählt ein Mitglied aus, pointer->x greift über einen Zeiger darauf zu. Strukturen, Unions und Enums werden eingelesen; ihre Speicheranordnung hängt jedoch von den Typen und den Attributen __packed / __aligned ab. Prüfen Sie sizeof, bevor Sie Daten mit Hardware oder binären Formaten austauschen.

Die standardmäßige Legacy-ABI legt Argumente und lokalen Speicher an festen Adressen ab. Setzen Sie keine Rekursion oder Wiedereintritt aus Interrupts wie auf einem Desktop voraus. __stackcall und --abi=stack sind fortgeschrittene Optionen für die Aufrufkonvention. Prüfen Sie bei gemischten Konventionen die ABI-Berichte und das Laufzeitverhalten. Callback-Aufrufe über Funktionspointer werden mit --abi=stack nicht unterstützt. Bauen Sie die vollständigen System- und Szenen-Callback-Beispiele mit der standardmäßigen Legacy-ABI, wie in ihren Buildbefehlen angegeben.

8. Mehrere Dateien und Präprozessor

Schreiben Sie Typen, Konstanten und Deklarationen in Header, Funktionskörper in .c-Dateien. #pragma once oder Include-Guards verhindern mehrfaches Einlesen. Für bedingte Kompilierung stehen #define, #undef, #if, #ifdef, #ifndef, #elif, #else und #endif bereit.

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

-I ergänzt ein Suchverzeichnis für Header. Ein Bibliotheksheader bindet die Implementierung nicht automatisch ein. Nennen Sie die nötigen .c-Dateien im Build-Befehl. Wählen Sie die benötigten Einheiten gezielt aus, damit Registerdefinitionen oder Interrupt-Handler nicht doppelt vorkommen.

9. ROM, Speicher und Banken

ROM enthält Code und Konstanten, WRAM die Variablen, VRAM die Grafik und OAM die Sprite-Beschreibungen. Banking legt fest, welcher physische Speicher an einer CPU-Adresse sichtbar ist. Ein 16-Bit-Zeiger allein bezeichnet Daten in einer anderen Bank nicht eindeutig.

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

__prg_rom legt Daten in ROM ab. Attribute wie __location(0xFF40) wählen eine feste Adresse, __wram / __hram einen Speicherbereich. Prüfen Sie bei #pragma bank und #pragma fixed_bank die Map. Von Interrupts benötigter Code und benötigte Daten müssen erreichbar bleiben.

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

--cgb=dmg kennzeichnet DMG-Software, --cgb=cgb Software für beide Modi und --cgb=cgb_only reine CGB-Software. Programme für beide Modi müssen die Hardware erkennen, bevor sie CGB-Funktionen verwenden. Ein Header-Flag implementiert diese Unterscheidung nicht im Spielcode.

10. Grafik sicher aktualisieren

VBlank ist die Pause zwischen den dargestellten Frames. Unpassend getaktete VRAM- und OAM-Schreibzugriffe können Daten verfälschen oder verloren gehen. Laden Sie anfängliche Ressourcen bei ausgeschaltetem Display. Verwenden Sie für laufende Aktualisierungen sichere Intrinsics oder die VRAM-Warteschlange. Bei Funktionen mit _unsafe oder _fast muss der Aufrufer selbst ein zulässiges Übertragungsfenster gewährleisten.

Richten Sie den OAM-DMA-Puffer an einer 256-Byte-Grenze aus. Auf GB erhält __oam_dma die Quelladresse. Das gleichnamige FC-Intrinsic hat dagegen kein Argument.

11. Build-Befehle und Ausgaben

-o wählt die Ausgabedatei, -O0 / -O1 die Optimierung und --profile=dev|release|test eine Gruppe von Einstellungen. --no-disasm unterdrückt die Disassemblierung. --debug-out=... und --trace-out=... legen Analyseausgaben fest. Wählen Sie den Umfang passend zu einem schnellen Build oder einer ausführlichen Untersuchung.

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

.map hält Namen und Speicherbelegung fest, .funcsizes.txt die Funktionsgrößen. .dbg2.json / .source_map.txt verknüpfen Ausführungspositionen mit Quellen, .build_report.json fasst den Build zusammen. Eine ausdrücklich mit --emit-ai-metadata erzeugte JSON-Datei erleichtert die Übergabe an SARAKURA.

12. Fehler von Anfang an lesen

Beginnen Sie beim ersten Fehler mit Dateiname, Zeile und KQ-Diagnosenummer. Spätere Meldungen können Folge des ersten Syntaxfehlers sein. Prüfen Sie bei unbekannten Symbolen Deklaration, Implementierung und Einbindung in den Build. Bei ROM-Überlauf helfen Ressourcen- und Funktionsgrößen sowie die Bankbelegung.

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

13. Inline-Assembler

__asm { ... } akzeptiert die Befehlsnamen von KITAQGB. Quelltext für andere GB-Assembler wird nicht beliebig übernommen. Zu den internen Schreibweisen gehört beispielsweise LD_A_IMM. Klären Sie Argumente, Rückgabewerte, zu erhaltende Register und Stack-Verhalten, bevor Sie Inline-Assembler einsetzen. Der Anhang listet Befehlsnamen und Operandenformen auf.

c
void nop_example() {
    __asm { NOP }
}

14. Praxisbeispiel: ein Spiel mit KI entwickeln

Tragen Sie die Anforderungen ein und geben Sie den vollständigen Prompt an Ihre KI weiter. Er umfasst die Implementierung, Emulator-Tests, die Analyse mit SARAKURA und die erneute Prüfung nach Korrekturen.

· Markdown herunterladen

Tragen Sie die Anforderungen ein und geben Sie dieses gesamte Dokument an die KI weiter. Die Befehle setzen voraus, dass die Repositories kitaqgb, kitaqfc, kokura, kurosaki, sarakura und kitaq-docs sowie das Projekt game-gb oder game-fc im selben übergeordneten Ordner liegen. Führen Sie die Befehle dort aus und passen Sie die Pfade an die tatsächliche Umgebung an.

Anforderungen

  • Spieltitel: <ausfüllen>
  • Genre und zentrale Spielmechanik: <ausfüllen>
  • Steuerung sowie Erfolgs- und Misserfolgsbedingungen: <ausfüllen>
  • Erforderliche Bildschirme, Level, Gegner und Gegenstände: <ausfüllen>
  • Grafikstil, Musik und Geräusche: <ausfüllen; bereitgestellte Dateien angeben>
  • Speichern, Kommunikation, Zusatzgeräte und weitere Anforderungen: <ausfüllen oder keine>
  • Projektordner: <ausfüllen>
  • Bedingungen für die Weitergabe: <etwa eigener Code und eigene Medien, die unter MIT veröffentlicht werden können>
  • Zielgerät: <ursprünglicher Game Boy / GB und CGB / nur CGB>
  • Leistungsziel: <etwa 60 Aktualisierungen der Spiellogik pro Sekunde im normalen Spiel; akzeptables Verhalten in aufwendigen Szenen festlegen>

Arbeitsauftrag

Setzen Sie das Spiel mit KITAQGB und seinen Bibliotheken um. Verwenden Sie KOKURA zum Ausführen und Debuggen und SARAKURA zum Auswerten der Diagnosen sowie zum Vergleich vor und nach einer Korrektur.

Wiederholen Sie diesen Ablauf, bis die Abnahmekriterien erfüllt sind: Spezifikation konkretisieren → kleine Änderung umsetzen → bauen → Eingaben ausführen und beobachten → Ursache untersuchen → korrigieren → unter gleichen Bedingungen erneut testen. Ein Plan, ausgegebener Quellcode oder ein erfolgreicher Compilerlauf allein schließen den Auftrag nicht ab.

Umgebung und Abnahmekriterien klären

  1. Lesen Sie die Anweisungen im Arbeitsverzeichnis, READMEs, HTML-Handbücher sowie Header und Implementierungen der verwendeten Bibliotheken. Erfassen Sie die Pfade der Programme und ihre Versionen oder SHA-256-Werte. Prüfen Sie Befehle anhand der tatsächlichen --help-Ausgabe und APIs anhand des Quellcodes.
  2. Legen Sie überprüfbare Kriterien für Eingaben, Bild, Ton, Spielverlauf und Aktualisierungsrate fest. Beispiele: START drücken und loslassen beginnt das Spiel; eine Kollision kostet ein Leben; die Pause schaltet die vorgesehenen Töne stumm, Fortsetzen nimmt die Wiedergabe wieder auf.
  3. Fragen Sie nur bei wesentlichen Unklarheiten nach. Treffen Sie übliche, rückgängig zu machende Implementierungsentscheidungen selbstständig. Schwächen Sie Anforderungen und Abnahmekriterien nicht ab.
  4. Führen Sie zunächst ein kleines mitgeliefertes Beispiel durch Compiler, Emulator und SARAKURA. Das prüft deren Zusammenspiel, nicht die Fertigstellung des beauftragten Spiels.

Eine kleine spielbare Fassung umsetzen

  • Verwenden Sie den C-Dialekt von KITAQGB und void main(). Setzen Sie Desktop-C- oder GBDK-APIs nicht als verfügbar voraus. Binden Sie die benötigten .c-Implementierungen ein, nicht nur Deklarationen; prüfen Sie Initialisierung, Einheiten, Vorzeichen, Wertebereiche, Pufferlebensdauer und ROM-Bänke.
  • Planen Sie VRAM/OAM-Aktualisierungen, VBlank, Interrupts, Stack, ROM/WRAM-Bänke sowie Grenzen für Tiles und Sprites. Gesamt- und Restkapazität der Übertragungswarteschlange sind etwas anderes als Kapazität und freier Platz des physischen VRAM.
  • Ein DMG-Spiel darf nicht von CGB-exklusiven Funktionen abhängen. Bei Unterstützung beider Geräte sind beide Hardwaremodi zu prüfen.
  • Verwenden Sie für Buchstaben, Ziffern und Symbole die bereitgestellte eigene Schrift aus ascii.c und prüfen Sie die Zuordnung von Zeichen zu Tiles.
  • Verbinden Sie zunächst Start, Titelbild, steuerbare Spielfigur, Erfolg oder Misserfolg und Neustart. Erweitern Sie danach den Inhalt.
  • Bewahren Sie bearbeitbare Originale von Grafik, Musik und Geräuschen sowie deren Erzeugungsschritte auf. Prüfen Sie, dass der Build tatsächlich die exportierten Daten verwendet.
  • Schreiben Sie Codekommentare auf Englisch und Fortschrittsberichte auf Deutsch. Lassen Sie die Standardberichte von SARAKURA auf Englisch.

Jeden Build seiner Ausführung zuordnen

Trennen Sie Ausgaben nach Iteration, etwa mit out/iter-001. Protokollieren Sie Befehle, Rückgabecodes und Hashes von Quellcode, Medien, Werkzeugen, ROM und Metadaten. Führen Sie nach einem fehlgeschlagenen Build niemals eine alte ROM aus. Speicherbelegungspläne, Quellcodezuordnungen und Debuginformationen müssen aus demselben Build wie die ROM stammen.

Das folgende Beispiel ist eine grundlegende DMG-Prüfung. Stellen Sie main.c und alle erforderlichen Bibliotheksimplementierungen bereit und passen Sie Optionen und Eingabefolge an das Spiel an.

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

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

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

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

--hardware dmg wählt den ursprünglichen Game Boy. Stimmen Sie bei CGB oder Unterstützung beider Geräte den ROM-Header und den Hardwaremodus des Emulators aufeinander ab. Die Eingabefolge drückt START einmal zwischen Abschnitten mit losgelassenen Tasten. 300 Frames prüfen nicht das gesamte Spiel.

Bild, Ton, Zustand und Leistung prüfen

  • Speichern Sie Eingabeszenarien mit getrenntem Drücken, Halten und Loslassen. Durchlaufen Sie alle vorgesehenen Wege: Start, Spielbeginn, Bewegung, Aktionen, Kollisionen, Scrolling, Levelwechsel, Spielende, Neustart, Pause und gegebenenfalls Speichern oder Kommunikation.
  • Bewahren Sie PNGs relevanter Frames, Eingaben, Laufberichte, Diagnose-JSONL, WAVs und nötige Zustands- oder Speicherbeobachtungen auf. Prüfen Sie erreichte Frames und Abbruchgrund. Öffnen Sie die Bilder tatsächlich; ein einzelner Screenshot belegt weder Bewegung noch Eingabereaktion. Vergleichen Sie Zähler, Positionen und Zustandswechsel mit Sollwerten; prüfen Sie Bildschirmränder, Tile- und Attributgrenzen und Szenen mit vielen Sprites.
  • Prüfen Sie Musik, Geräusche, gleichzeitige Wiedergabe, Aussetzer, Pause und Fortsetzen. Eine erzeugte WAV-Datei allein belegt keinen korrekten Klang. Ist Anhören nicht möglich, trennen Sie ausgeführte Wellenform- und Zahlenprüfungen von unbestätigten Höreigenschaften.
  • Messen Sie aufwendige Szenen, Arbeit der Ziel-CPU, Spielaktualisierungen und Transfers; auf FC auch die NMI-Arbeit. Emulatordurchsatz auf dem Host ist weder Spielaktualisierungsrate noch Nachweis für reale Hardwaregeschwindigkeit. Fortsetzen mit --allow-unimplemented belegt keine Unterstützung der fehlenden Funktion.

Analysieren, korrigieren und erneut testen

  • Übergeben Sie SARAKURA die Buildmetadaten der geprüften ROM und Diagnose-JSONL aus dem zugehörigen Lauf. Ein CPU-Trace oder gewöhnlicher Laufbericht ersetzt dies nicht. --frames legt Analysebedingungen fest; SARAKURA führt weder ROMs aus noch ändert es automatisch Quellcode.
  • Lesen Sie report.html, ai_diagnostics.json, repair_prompt.md und retest_plan.json. Gleichen Sie Diagnosen mit Reproduktionsschritten, Bildern, Ton und Quellcode ab. Trennen Sie vermutete Quellstellen oder Ursachen von bestätigten Tatsachen und normale Warteschleifen von Hängern. Bewerten Sie Warnungen einzeln und dokumentieren Sie nicht unterstützte Ereignisse sowie Analysegrenzen. Verbergen Sie Warnungen nicht durch Filter und verkürzen Sie Tests nicht, um ein Bestehen zu erreichen.
  • Reduzieren Sie Fehler auf minimale Reproduktionen, beheben Sie die Ursache und bauen Sie neu. Liegt der Fehler im Compiler oder Emulator, grenzen Sie ihn vom Spielcode ab und ergänzen Sie eine Regressionsprüfung für die Werkzeugkorrektur.
  • Wiederholen Sie Tests mit gleichen Eingaben, Zufallsstartwerten, Hardware- und Videomodi, Mappern, Beobachtungsframes und Diagnoseeinstellungen. Nutzen Sie für jede ROM passende Metadaten; verwenden Sie nach Änderungen an Code oder RAM-Belegung nicht blind alte Speicherzustände weiter.
powershell
& '.\sarakura\sarakura.exe' baseline-delta `
  --baseline '.\game-gb\out\iter-001\analysis' `
  --current '.\game-gb\out\iter-002\analysis' `
  --out '.\game-gb\out\delta.json' --markdown '.\game-gb\out\delta.md' `
  --fail-on-new error --fail-on-regression error --enforce

Nutzen Sie Diagnosedifferenzen gemeinsam mit den Abnahmekriterien für Steuerung, Grafik und Ton. Wiederholt sich derselbe Fehler, überprüfen Sie Belege und Hypothese, statt beliebige weitere Änderungen vorzunehmen.

Abschlusskriterien und Lieferumfang

Führen Sie alle Pflichtszenarien erneut mit der finalen ROM aus, die aus den gelieferten Quellen und Einstellungen gebaut wurde. Unverwundbarkeit, automatische Testeingaben oder ein anderer Mapper allein prüfen kein normales Spiel in der Endfassung. Liefern Sie eine Zuordnung von Anforderungen zu Tests, Gründe für verbleibende Warnungen und klare Angaben zu ungeprüften oder nicht unterstützten Punkten. Kennzeichnen Sie ausdrücklich, wenn nicht auf physischer Hardware getestet wurde.

Liefern Sie Quellcode, Kennungen von Werkzeugen und Bibliotheken, bearbeitbare Medien, reproduzierbare Build- und Testskripte, ROM, abschließende Nachweise und eine README mit Einrichtung, Steuerung und bekannten Grenzen. Fügen Sie bei Bedarf Replays und das Testprogramm hinzu. Veröffentlichen oder versenden Sie Dateien nur im ausdrücklich erlaubten Umfang. Entfernen Sie unnötige Zwischenbuilds und temporäre Traces nach der Prüfung, bewahren Sie jedoch Quellen, Medien, Endergebnisse und erforderliche Regressionsnachweise auf.

Verhindern Umgebung oder Berechtigungen eine Pflichtprüfung, nennen Sie die genauen Reproduktionsschritte und die nötige Maßnahme. Kennzeichnen Sie die Arbeit nicht als abgeschlossen.

Quellpfade

Die Compilerquellen und die Projektdatei liegen im gleichnamigen Unterverzeichnis des Repositorys. Die ausführbare Datei liegt im Hauptverzeichnis, die Bibliotheken liegen in lib/. Die Verzeichnisübersicht nennt die Projektpfade und Build-Voraussetzungen.

Vollstä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.

Den mitgelieferten Zeichensatz anzeigen: 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.
Anzeigebeispiel: 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

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


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

Die erste Textausgabe: Der Bildschirm zeigt HELLO WORLD und 042. Die führende Null entsteht durch die Dezimaldarstellung mit drei Stellen.
Anzeigebeispiel: Der Bildschirm zeigt HELLO WORLD und 042. Die führende Null entsteht durch die Dezimaldarstellung mit drei Stellen.

Quelltext herunterladen / Prüfstatus

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


// Initialize the display/font, write HELLO WORLD and a three-digit 042,
// then wait for frames so the result remains visible.
void main() {
    m_init();
    m_text(2,3,"HELLO");
    m_text(2,5,"HELLO WORLD");
    m_number(42);
    while (1) { m_wait();  }
}
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.

Mit breiterem Zwischentyp rechnen: 030 steht für das berechnete Ergebnis 30.
Anzeigebeispiel: 030 steht für das berechnete Ergebnis 30.

Quelltext herunterladen / Prüfstatus

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


// Widen before adding 200 + 100, divide the 16-bit sum, and display 30.
// The cast demonstrates avoiding an 8-bit intermediate result.
void main() {
    m_init();
    m_text(2,3,"ARITHMETIC");
    u16 sum;
    sum = (u16)200 + 100;
    m_number((u8)(sum / 10));
    while (1) { m_wait();  }
}
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.

Verzweigungen und Schleifen: 042 ist das Endergebnis dieser Schleifen und Verzweigungen.
Anzeigebeispiel: 042 ist das Endergebnis dieser Schleifen und Verzweigungen.

Quelltext herunterladen / Prüfstatus

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


// Exercise loop control and a final branch, then display the selected value 42.
void main() {
    m_init();
    m_text(2,3,"CONTROL");
    u8 i; u8 sum; sum=0;
    for (i=0;i<8;i++) { if (i==3) continue; sum=(u8)(sum+i); }
    while (sum<30) { sum++; }
    do { sum++; } while (sum<32);
    switch (sum) { case 32: sum=42; break; default: sum=0; break; }
    m_number(sum);
    while (1) { m_wait();  }
}
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.
Anzeigebeispiel: Die Summe wird als 042 angezeigt. Verfolgen Sie die Strukturkopie, die Schreibzugriffe auf ptr[i] und den Rückgabewert der Funktion.

Quelltext herunterladen / Prüfstatus

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

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

Bitflags und Pufferkopie: Das erste Zielbyte wird als 010 angezeigt; Bit 3 trägt 8 und Bit 1 trägt 2 bei.
Anzeigebeispiel: Das erste Zielbyte wird als 010 angezeigt; Bit 3 trägt 8 und Bit 1 trägt 2 bei.

Quelltext herunterladen / Prüfstatus

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

// Clear the source bytes, set bits 3 and 1, copy the buffer, and display 8 + 2 = 10.
void main() {
    m_init();
    m_text(2,3,"BITS MEMORY");
    u8 data[4]; u8 copy[4];
    __memset(data,0,4); __bit_set(data,3); __bit_toggle(data,1);
    __memcpy(copy,data,4); m_number(copy[0]);
    while (1) { m_wait();  }
}
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.

Neue Tastendrücke zählen: 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.
Anzeigebeispiel: 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

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

Q8.8-Festkommawerte multiplizieren: 012 bedeutet 3 × 4 = 12. Es ist die umgewandelte Ganzzahl, nicht der intern mit 256 skalierte Wert.
Anzeigebeispiel: 012 bedeutet 3 × 4 = 12. Es ist die umgewandelte Ganzzahl, nicht der intern mit 256 skalierte Wert.

Quelltext herunterladen / Prüfstatus

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

// Convert 3 and 4 to Q8.8, multiply them, convert the result back to an
// integer, and display 12.
void main() {
    m_init();
    m_text(2,3,"FIXED");
    fix8 a; fix8 b; a=fix_from_int(3); b=fix_from_int(4);
    m_number((u8)fix_to_int(fix_mul(a,b)));
    while (1) { m_wait();  }
}
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.

Aus einem festen Objektpool zuweisen: 001 ist die Anzahl aktiver Objekte, keine ID oder Koordinate. Das Beispiel aktualisiert und zeichnet das Objekt nicht; daher erscheint keine bewegte Figur.
Anzeigebeispiel: 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

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

// Create one entity and check the 0xFF allocation-failure sentinel before
// accessing its velocity. Display the pool occupancy, not the entity ID.
void main() {
    m_init();
    m_text(2,3,"ENTITY");
    u8 id; entity_init(); id=entity_create(1,20,30);
    if(id!=0xFF) entity_get(id)->vx=2; m_number(entity_count_active());
    while (1) { m_wait();  }
}
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.

Einen Diagnosewert im RAM aufzeichnen: 001 ist die Anzahl der Trace-Einträge. HP=42 liegt im Diagnosepuffer; der Bildschirm allein zeigt oder bestätigt diesen Inhalt nicht.
Anzeigebeispiel: 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

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

// Record HP = 42 with frame tag 1 and display the number of trace entries.
// The trace buffer contains the value; the screen displays the count, not HP.
void main() {
    m_init();
    m_text(2,3,"DEBUG");
    debug_init(); debug_set_frame(1); debug_trace_u8("HP",42);
    m_number(debug_get_trace_count());
    while (1) { m_wait();  }
}
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.

Einen Positionsverlauf speichern: 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.
Anzeigebeispiel: 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

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

Den Framezähler der Bibliothek lesen: Das Ergebnis ist 002. Die Warteschleife verwendet die Handbuch-Hilfsfunktion m_wait; der angezeigte Wert steigt daher nicht weiter.
Anzeigebeispiel: Das Ergebnis ist 002. Die Warteschleife verwendet die Handbuch-Hilfsfunktion m_wait; der angezeigte Wert steigt daher nicht weiter.

Quelltext herunterladen / Prüfstatus

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

// Reset the library frame count, perform two counted waits, and display 2.
// Subsequent m_wait calls leave that library counter unchanged.
void main() {
    m_init();
    m_text(2,3,"SYSTEM");
    system_init(); system_wait_vblank(); system_wait_vblank();
    m_number(system_get_frame8());
    while (1) { m_wait();  }
}
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.

Die aktuelle Szenen-ID wählen: 001 ist die aktuelle von scene_get_current gelieferte ID. In diesem Beispiel sind weder Szenen-Callbacks noch Übergangszeichnungen registriert.
Anzeigebeispiel: 001 ist die aktuelle von scene_get_current gelieferte ID. In diesem Beispiel sind weder Szenen-Callbacks noch Übergangszeichnungen registriert.

Quelltext herunterladen / Prüfstatus

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

Weltkoordinaten in Bildschirmkoordinaten umwandeln: 052 minus 010 ergibt die angezeigte 042. Dies ist eine Koordinatenberechnung; der Hintergrund wird nicht gescrollt.
Anzeigebeispiel: 052 minus 010 ergibt die angezeigte 042. Dies ist eine Koordinatenberechnung; der Hintergrund wird nicht gescrollt.

Quelltext herunterladen / Prüfstatus

powershell
.\kitaqgb\kitaqgb.exe kitaqgb/lib/scroll.c kitaqgb/lib/camera.c kitaq-docs/samples/gb_camera.c -I kitaqgb/lib -I kitaq-docs/samples -o out/gb_camera.gb --profile=dev --rst-disable --stack-bank=fixed
c
// Convert world coordinates to screen coordinates.
// Expected: 042: world x=52 minus camera x=10
#include "gb_common.h"
#include "camera.h"
Camera8_8 cam;
// Set the camera X coordinate to 10 pixels in Q8.8 (10 * 256), then subtract
// it from world X = 52 to display screen X = 42.
void main() {
    m_init();
    m_text(2,3,"CAMERA");
    Camera_Init(&cam); Camera_Set(&cam,2560,0);
    m_number((u8)Camera_WorldToScreenX(&cam,52));
    while (1) { m_wait();  }
}
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.
Anzeigebeispiel: Das letzte Byte, out[2], wird als 042 angezeigt. Alle drei entpackten Bytes sollen den Wert 42 enthalten.

Quelltext herunterladen / Prüfstatus

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

Ein Brettfeld lesen und schreiben: 042 ist der zurückgelesene Wert. Das Programm zeichnet das 4×4-Brett selbst nicht.
Anzeigebeispiel: 042 ist der zurückgelesene Wert. Das Programm zeichnet das 4×4-Brett selbst nicht.

Quelltext herunterladen / Prüfstatus

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

Eine Box um einen Physikschritt bewegen: 042 ist die resultierende X-Koordinate, hier in ganzen Pixeln. Es werden weder eine Boxform noch eine fortlaufende Animation gezeichnet.
Anzeigebeispiel: 042 ist die resultierende X-Koordinate, hier in ganzen Pixeln. Es werden weder eine Boxform noch eine fortlaufende Animation gezeichnet.

Quelltext herunterladen / Prüfstatus

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

ROM-Daten mit einem Bankargument lesen: Das zweite Zielbyte wird als 042 angezeigt. Dieses Beispiel liest aus fest eingeblendetem ROM; es zeigt keine Übertragung zwischen verschiedenen umschaltbaren Banken.
Anzeigebeispiel: 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

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

Daten anhand der Asset-ID laden: Das zweite Byte wird als 042 angezeigt. Das Beispiel demonstriert das Auffinden und Kopieren von Assets, keine Bilddarstellung.
Anzeigebeispiel: Das zweite Byte wird als 042 angezeigt. Das Beispiel demonstriert das Auffinden und Kopieren von Assets, keine Bilddarstellung.

Quelltext herunterladen / Prüfstatus

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

Ein Geschoss zuweisen: 001 ist die Anzahl zugewiesener Geschosse. Es gibt hier weder eine Geschoss-Aktualisierungsschleife noch eine Hintergrundkomposition; bewegte Geschosse werden nicht gezeichnet.
Anzeigebeispiel: 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

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

// Reset the bullet pool, spawn one bullet and display the active count.
// This numeric example does not install the background bullet compositor.
void main() {
    m_init();
    m_text(2,3,"DANMAKU");
    danmaku_reset(); danmaku_spawn(40,40,16,0);
    m_number(dm_count);
    while (1) { m_wait();  }
}
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.

Eine CGB-Hintergrundpalette einstellen: 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.
Anzeigebeispiel: 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

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

// Set palette-zero background colors to white and purple, then display 42.
// Run the ROM in CGB mode to observe the color result.
void main() {
    m_init();
    m_text(2,3,"CGB PALETTE");
    // Disable the LCD during VBlank so both palette bytes remain accessible.
    m_wait(); M_LCDC = 0;
    cgb_bg_rgb(0,0,31,31,31); cgb_bg_rgb(0,3,12,0,22);
    M_LCDC = 0x91;
    m_number(42);
    while (1) { m_wait();  }
}
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.

Den Hintergrund horizontal scrollen: Das Wort SCROLL bewegt sich nach links. Beobachten Sie seine Position über die Zeit; eine einzelne Aufnahme kann die Bewegung nicht zeigen.
Anzeigebeispiel: 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

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

Eine Tile-Aktualisierung einreihen: Achten Sie auf eine einzelne 4 bei Hintergrund-Tile-Koordinate (3,8). Dies ist eine Tile-Aktualisierung, kein Zahlenzähler.
Anzeigebeispiel: Achten Sie auf eine einzelne 4 bei Hintergrund-Tile-Koordinate (3,8). Dies ist eine Tile-Aktualisierung, kein Zahlenzähler.

Quelltext herunterladen / Prüfstatus

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

// Queue font tile 52 (the digit 4) at tile coordinate (3,8), then wait for
// VBlank and execute the queued transfer.
void main() {
    m_init();
    m_text(2,3,"VRAM QUEUE");
    vram_init();
    vram_queue_bg_tile(3,8,52); vram_flush();
    while (1) { m_wait();  }
}
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.

Ein Sprite per OAM-DMA anzeigen: An der angegebenen Spriteposition erscheint ein unbewegliches A. Prüfen Sie im Spiel, ob die Zuweisung fehlgeschlagen ist, bevor Sie die zurückgegebene ID verwenden.
Anzeigebeispiel: 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

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

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

Einen reproduzierbaren Zufallswert erzeugen: 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.
Anzeigebeispiel: 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

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

// Seed the generator with 1234 and display one value in [0,10).
// Restarting with the same seed reproduces the sequence.
void main() {
    m_init();
    m_text(2,3,"RNG");
    rng_seed(1234); m_number(rng_range(10));
    while (1) { m_wait();  }
}
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.

Ein Spielzustandsflag speichern: 001 bedeutet, dass Flag 12 wahr ist. Das Beispiel arbeitet mit RAM-Flags und erhält sie nicht über Aus- und Einschalten hinweg.
Anzeigebeispiel: 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

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

// Clear and set flag 12, then display its Boolean value. This does not write
// a save file or demonstrate persistence across power cycles.
void main() {
    m_init();
    m_text(2,3,"FLAGS");
    flag_clear(12); flag_set(12); m_number(flag_get(12));
    while (1) { m_wait();  }
}
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.

Einen Kreis um einen Physikschritt bewegen: 042 ist die resultierende ganzzahlige X-Koordinate. Das Beispiel zeichnet weder einen Kreisumriss noch eine Kollisionsanimation.
Anzeigebeispiel: 042 ist die resultierende ganzzahlige X-Koordinate. Das Beispiel zeichnet weder einen Kreisumriss noch eine Kollisionsanimation.

Quelltext herunterladen / Prüfstatus

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

Einen Soundeffekt in jedem Frame verarbeiten: 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.
Anzeigebeispiel: 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

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

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

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

Parameter

dst

Ziel 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.

bank

8-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.

src

Nahzeiger auf lesbare Bytes in der gewählten ROM-Bank. Er enthält keine Banknummer.

len

Byteanzahl, 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

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

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.

__farmemcpy: DMG
DMG
__farmemcpy: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\asset_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\asset_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm
Implementierung 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

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

Parameter

dst

Adresse 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.

src

Zeiger 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.

len

Anzahl 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

c
__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.

__vram_copy: DMG
DMG
__vram_copy: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\vram_memory_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_memory_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung 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

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

Parameter

bank

Vorzeichenlose 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.

func

Gü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

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

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.

__farcall_ptr: DMG
DMG
__farcall_ptr: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/bank.h:15

kitaqgb/kitaqgb/CodeGenerator.cs:8335

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

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

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

Parameter

bank

Vorzeichenlose 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.

addr

Nahzeiger 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

c
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.

__farpeek16: DMG
DMG
__farpeek16: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/bank.h:13

kitaqgb/kitaqgb/CodeGenerator.cs:8295

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

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

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

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

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

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

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

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

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

Parameter

bank

Vorzeichenlose 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.

addr

Nahzeiger 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

c
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.

__farpeek8: DMG
DMG
__farpeek8: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/bank.h:12

kitaqgb/kitaqgb/CodeGenerator.cs:8262

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

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

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

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

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

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

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

cgb_palette.h — 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

c
u8 __cgb_is_cgb();

Rückgabewert

u8: 0 für DMG-Modus, 1 für CGB-Modus.

Anwendungsbeispiel

c
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.

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

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/cgb_palette.h:8

Deklarationshinweise im Original
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

c
void __cgb_safe_set_bcpd(u8 value);

Parameter

value

Ein 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

c
__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.

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

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung 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

c
void __cgb_safe_set_bcps(u8 value);

Parameter

value

Byteindex 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

c
__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.

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

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung 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

c
void __cgb_safe_set_ocpd(u8 value);

Parameter

value

Ein 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

c
__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.

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

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung 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

c
void __cgb_safe_set_ocps(u8 value);

Parameter

value

Byteindex 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

c
__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.

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

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung 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

c
void __cgb_safe_set_vbk(u8 value);

Parameter

value

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.

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

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

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.

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

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/cgb_tile.h:28

Deklarationshinweise im Original
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

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

Parameter

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

tile

Tile-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

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

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

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.

text
A
__settile: DMG
DMG
__settile: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/cgb_tile.h:31

Deklarationshinweise im Original
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

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

Parameter

dest

VRAM-Byteadresse des Anfangs. Für Karten gilt 0x9800–0x9FFF; der gesamte Transfer muss innerhalb der gewünschten Karte bleiben.

src

Lü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.

count

Byteanzahl 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

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

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

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.

text
ABC
__settile_bulk: DMG
DMG
__settile_bulk: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/cgb_tile.h:48

Deklarationshinweise im Original
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

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

Parameter

dest

VRAM-Byteadresse des Anfangs. Für Karten gilt 0x9800–0x9FFF; der gesamte Transfer muss innerhalb der gewünschten Karte bleiben.

src

Lü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.

count

Byteanzahl 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

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

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

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.

text
ABC
__settile_bulk_fast: DMG
DMG
__settile_bulk_fast: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/cgb_tile.h:50

Deklarationshinweise im Original
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

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

Parameter

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

tile

Tile-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

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

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

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.

text
A
__settile_unsafe: DMG
DMG
__settile_unsafe: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/cgb_tile.h:33

Deklarationshinweise im Original
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

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

Parameter

base

base wählt die Karte (0x9800 oder 0x9C00); die Byteadresse lautet base + 32*y + x.

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

tile

Tile-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

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

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

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.

text
A
__settileat: DMG
DMG
__settileat: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/cgb_tile.h:35

Deklarationshinweise im Original
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

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

Parameter

base

base wählt die Karte (0x9800 oder 0x9C00); die Byteadresse lautet base + 32*y + x.

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

tile

Tile-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

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

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

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.

text
A
__settileat_unsafe: DMG
DMG
__settileat_unsafe: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/cgb_tile.h:37

Deklarationshinweise im Original
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

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

Parameter

base

base wählt die Karte (0x9800 oder 0x9C00); die Byteadresse lautet base + 32*y + x.

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

attr

CGB-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

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

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

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.

text
A
__settileatattr: DMG
DMG
__settileatattr: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/cgb_tile.h:62

Deklarationshinweise im Original
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

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

Parameter

base

base wählt die Karte (0x9800 oder 0x9C00); die Byteadresse lautet base + 32*y + x.

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

attr

CGB-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

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

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

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.

text
A
__settileatattr_unsafe: DMG
DMG
__settileatattr_unsafe: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/cgb_tile.h:64

Deklarationshinweise im Original
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

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

Parameter

base

base wählt die Karte (0x9800 oder 0x9C00); die Byteadresse lautet base + 32*y + x.

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

tile

Tile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.

attr

CGB-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

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

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

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.

text
A
__settileatcgb: DMG
DMG
__settileatcgb: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/cgb_tile.h:66

Deklarationshinweise im Original
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

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

Parameter

base

base wählt die Karte (0x9800 oder 0x9C00); die Byteadresse lautet base + 32*y + x.

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

tile

Tile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.

attr

CGB-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

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

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

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.

text
A
__settileatcgb_unsafe: DMG
DMG
__settileatcgb_unsafe: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/cgb_tile.h:68

Deklarationshinweise im Original
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

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

Parameter

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

attr

CGB-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

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

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

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.

text
A
__settileattr: DMG
DMG
__settileattr: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/cgb_tile.h:53

Deklarationshinweise im Original
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

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

Parameter

dest

VRAM-Byteadresse des Anfangs. Für Karten gilt 0x9800–0x9FFF; der gesamte Transfer muss innerhalb der gewünschten Karte bleiben.

src

Lü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.

count

Byteanzahl 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

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

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

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.

text
ABC
__settileattr_bulk: DMG
DMG
__settileattr_bulk: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/cgb_tile.h:89

Deklarationshinweise im Original
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

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

Parameter

dest

VRAM-Byteadresse des Anfangs. Für Karten gilt 0x9800–0x9FFF; der gesamte Transfer muss innerhalb der gewünschten Karte bleiben.

src

Lü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.

count

Byteanzahl 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

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

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

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.

text
ABC
__settileattr_bulk_fast: DMG
DMG
__settileattr_bulk_fast: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/cgb_tile.h:91

Deklarationshinweise im Original
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

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

Parameter

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

attr

CGB-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

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

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

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.

text
A
__settileattr_unsafe: DMG
DMG
__settileattr_unsafe: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/cgb_tile.h:55

Deklarationshinweise im Original
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

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

Parameter

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

tile

Tile-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

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

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

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.

text
A
__settilebg: DMG
DMG
__settilebg: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/cgb_tile.h:43

Deklarationshinweise im Original
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

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

Parameter

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

tile

Tile-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

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

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

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.

text
A
__settilebg_unsafe: DMG
DMG
__settilebg_unsafe: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/cgb_tile.h:45

Deklarationshinweise im Original
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

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

Parameter

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

attr

CGB-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

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

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

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.

text
A
__settilebgattr: DMG
DMG
__settilebgattr: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/cgb_tile.h:80

Deklarationshinweise im Original
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

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

Parameter

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

attr

CGB-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

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

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

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.

text
A
__settilebgattr_unsafe: DMG
DMG
__settilebgattr_unsafe: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/cgb_tile.h:82

Deklarationshinweise im Original
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

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

Parameter

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

tile

Tile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.

attr

CGB-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

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

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

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.

text
A
__settilebgcgb: DMG
DMG
__settilebgcgb: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/cgb_tile.h:84

Deklarationshinweise im Original
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

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

Parameter

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

tile

Tile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.

attr

CGB-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

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

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

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.

text
A
__settilebgcgb_unsafe: DMG
DMG
__settilebgcgb_unsafe: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/cgb_tile.h:86

Deklarationshinweise im Original
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

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

Parameter

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

tile

Tile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.

attr

CGB-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

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

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

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.

text
A
__settilecgb: DMG
DMG
__settilecgb: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/cgb_tile.h:57

Deklarationshinweise im Original
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

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

Parameter

dest

VRAM-Byteadresse des Anfangs. Für Karten gilt 0x9800–0x9FFF; der gesamte Transfer muss innerhalb der gewünschten Karte bleiben.

tile_src, attr_src

Lü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.

count

Byteanzahl 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

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

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

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.

text
ABC
__settilecgb_bulk: DMG
DMG
__settilecgb_bulk: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/cgb_tile.h:93

Deklarationshinweise im Original
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

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

Parameter

dest

VRAM-Byteadresse des Anfangs. Für Karten gilt 0x9800–0x9FFF; der gesamte Transfer muss innerhalb der gewünschten Karte bleiben.

tile_src, attr_src

Lü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.

count

Byteanzahl 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

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

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

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.

text
ABC
__settilecgb_bulk_fast: DMG
DMG
__settilecgb_bulk_fast: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/cgb_tile.h:95

Deklarationshinweise im Original
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

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

Parameter

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

tile

Tile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.

attr

CGB-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

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

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

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.

text
A
__settilecgb_unsafe: DMG
DMG
__settilecgb_unsafe: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilecgb_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilecgb_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implementierung und Quelltext

kitaqgb/lib/cgb_tile.h:59

Deklarationshinweise im Original
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

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

Parameter

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

tile

Tile-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

c
// __settilewin
// Write one tile-number byte to the tile map; this selects an existing tile image, not
// new pixel data. LCDC bit 6 selects the window map: zero uses `0x9800`, one uses
// `0x9C00`. These are map coordinates, not the window's screen position.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"

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

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.

text
A
__settilewin: DMG
DMG
__settilewin: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilewin.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilewin.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implementierung und Quelltext

kitaqgb/lib/cgb_tile.h:39

Deklarationshinweise im Original
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

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

Parameter

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

tile

Tile-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

c
// __settilewin_unsafe
// Write one tile-number byte to the tile map; this selects an existing tile image, not
// new pixel data. LCDC bit 6 selects the window map: zero uses `0x9800`, one uses
// `0x9C00`. These are map coordinates, not the window's screen position.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"

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

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.

text
A
__settilewin_unsafe: DMG
DMG
__settilewin_unsafe: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilewin_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilewin_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implementierung und Quelltext

kitaqgb/lib/cgb_tile.h:41

Deklarationshinweise im Original
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

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

Parameter

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

attr

CGB-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

c
// __settilewinattr
// Write only the CGB attribute byte; leave the tile number unchanged. Attribute bits
// select palette, tile-data bank, flips and priority. LCDC bit 6 selects the window map:
// zero uses `0x9800`, one uses `0x9C00`. These are map coordinates, not the window's
// screen position.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"

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

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.

text
A
__settilewinattr: DMG
DMG
__settilewinattr: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilewinattr.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilewinattr.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implementierung und Quelltext

kitaqgb/lib/cgb_tile.h:71

Deklarationshinweise im Original
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

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

Parameter

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

attr

CGB-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

c
// __settilewinattr_unsafe
// Write only the CGB attribute byte; leave the tile number unchanged. Attribute bits
// select palette, tile-data bank, flips and priority. LCDC bit 6 selects the window map:
// zero uses `0x9800`, one uses `0x9C00`. These are map coordinates, not the window's
// screen position.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"

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

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.

text
A
__settilewinattr_unsafe: DMG
DMG
__settilewinattr_unsafe: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilewinattr_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilewinattr_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implementierung und Quelltext

kitaqgb/lib/cgb_tile.h:73

Deklarationshinweise im Original
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

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

Parameter

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

tile

Tile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.

attr

CGB-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

c
// __settilewincgb
// Write a tile number and its CGB attribute. On DMG, only the tile-number write is
// performed. On CGB, enter with VRAM bank 0 selected; attributes use bank 1 and the call
// finishes with `VBK=0`. LCDC bit 6 selects the window map: zero uses `0x9800`, one uses
// `0x9C00`. These are map coordinates, not the window's screen position.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"

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

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.

text
A
__settilewincgb: DMG
DMG
__settilewincgb: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilewincgb.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilewincgb.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implementierung und Quelltext

kitaqgb/lib/cgb_tile.h:75

Deklarationshinweise im Original
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

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

Parameter

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

tile

Tile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.

attr

CGB-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

c
// __settilewincgb_unsafe
// Write a tile number and its CGB attribute. On DMG, only the tile-number write is
// performed. On CGB, enter with VRAM bank 0 selected; attributes use bank 1 and the call
// finishes with `VBK=0`. LCDC bit 6 selects the window map: zero uses `0x9800`, one uses
// `0x9C00`. These are map coordinates, not the window's screen position.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"

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

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.

text
A
__settilewincgb_unsafe: DMG
DMG
__settilewincgb_unsafe: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilewincgb_unsafe.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilewincgb_unsafe.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implementierung und Quelltext

kitaqgb/lib/cgb_tile.h:77

Deklarationshinweise im Original
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

c
u16 __readpadex(u8 prev_keys);

Parameter

prev_keys

Die 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

c
packed_first=__readpadex(0); // First sample: current 0x11, new presses 0x11.
current_keys=(u8)packed_first;
press_keys=(u8)(packed_first>>8);
packed_held=__readpadex(current_keys); // Held without a change: new presses are 0.

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.

__readpadex: DMG
DMG
__readpadex: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\input_raw.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_raw.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache
Implementierung 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

c
u8 bank = __bankof(symbol);

Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.

Parameter

symbol

Name 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

c
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.

__bankof: DMG
DMG
__bankof: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/kitaqgb/CodeGenerator.cs:432

kitaqgb/kitaqgb/CodeGenerator.cs:7360

csharp
if (funcName == "__bankof")
            {
                if (args.Length != 1) Program.Error("__bankof(symbol) expects 1 argument");
                if (!args[0].Match(Tag.Name, out string symName)) Program.Error("__bankof(symbol) requires a symbol name argument");
                if (TryGetKnownRomBankForSymbol(symName, out int knownBank))
                {
                    EmitAsm("LD_A_IMM", new AsmOperand(knownBank & 0xFF, AddressMode.Immediate));
                    return;
                }
                EmitAsm("LD_A_IMM", new AsmOperand(symName, ImmediateModifier.Bank));
                return;
            }
__bankswitch

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

c
void __bankswitch(u8 bank);

Parameter

bank

Vorzeichenlose 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

c
__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.

__bankswitch: DMG
DMG
__bankswitch: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/bank.h:11

kitaqgb/kitaqgb/CodeGenerator.cs:7268

csharp
if (funcName == "__bankswitch")
            {
                if (args.Length != 1) Program.Error("__bankswitch(bank) expects 1 argument");
                CompileIntoA(args[0]);
                if (CurrentFunctionBank == 0)
                {
                    EmitAsm("LD_MEM_A", new AsmOperand(0x2000, AddressMode.Absolute));
                    EmitStoreA(RomBankVar);
                }
                else
                {
                    UseBankSwitchBank0Helper = true;
                    EmitAsm("CALL", new AsmOperand("__kq_bankswitch_bank0", AddressMode.Absolute));
                }
                return;
            }
__cgb_safe_set_bgpd

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

c
__cgb_safe_set_bgpd(/* 書式は実装と呼び出し例を参照 */);

Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.

Parameter

value

Ein 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

c
__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.

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

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung 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

c
__cgb_safe_set_bgpi(/* 書式は実装と呼び出し例を参照 */);

Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.

Parameter

value

Byteindex 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

c
__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.

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

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung 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

c
void __cgb_safe_set_hdma1(u8 value);

Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.

Parameter

value

Registerwert 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

c
__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.

__cgb_safe_set_hdma1: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_hdma1: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_hdma1: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_dma_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_dma_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung 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

c
void __cgb_safe_set_hdma2(u8 value);

Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.

Parameter

value

Registerwert 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

c
__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.

__cgb_safe_set_hdma2: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_hdma2: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_hdma2: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_dma_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_dma_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung 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

c
void __cgb_safe_set_hdma3(u8 value);

Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.

Parameter

value

Registerwert 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

c
__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.

__cgb_safe_set_hdma3: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_hdma3: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_hdma3: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_dma_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_dma_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung 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

c
void __cgb_safe_set_hdma4(u8 value);

Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.

Parameter

value

Registerwert 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

c
__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.

__cgb_safe_set_hdma4: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_hdma4: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_hdma4: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_dma_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_dma_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung 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

c
void __cgb_safe_set_hdma5(u8 value);

Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.

Parameter

value

Registerwert 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

c
__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.

__cgb_safe_set_hdma5: --cgb=cgb / DMG
--cgb=cgb / DMG
__cgb_safe_set_hdma5: --cgb=cgb / CGB
--cgb=cgb / CGB
__cgb_safe_set_hdma5: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_dma_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_dma_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung 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

c
__cgb_safe_set_obpd(/* 書式は実装と呼び出し例を参照 */);

Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.

Parameter

value

Ein 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

c
__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.

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

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung 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

c
__cgb_safe_set_obpi(/* 書式は実装と呼び出し例を参照 */);

Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.

Parameter

value

Byteindex 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

c
__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.

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

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_palette_attributes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_palette_attributes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung 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

c
void __cgb_safe_set_svbk(u8 value);

Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.

Parameter

value

Registerwert 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

c
__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.

__cgb_safe_set_svbk: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_wram_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_wram_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb_only --no-cache --no-disasm
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:1862

__critical_enter

Erhöht die Software-Verschachtelungstiefe und führt DI aus.

Syntax

c
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

c
*((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.

__critical_enter: DMG
DMG
__critical_enter: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_display.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_display.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:7970

kitaqgb/kitaqgb/CodeGenerator.cs:7970

csharp
if (funcName == "__critical_enter")
            {
                if (args.Length != 0) Error(expr, "__critical_enter() expects 0 arguments");

                EmitLoadA(CriticalDepthVar);
                EmitAsm("LD_B_A"); // token = old depth
                EmitAsm("INC_A");
                EmitStoreA(CriticalDepthVar);
                EmitAsm("DI");
                EmitAsm("LD_A_B");
                return;
            }
__critical_leave

Verringert die Verschachtelungstiefe und führt für das äußere Token null EI aus.

Syntax

c
void __critical_leave(u8 token);

Parameter

token

Token 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

c
__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.

__critical_leave: DMG
DMG
__critical_leave: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_display.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_display.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:7984

kitaqgb/kitaqgb/CodeGenerator.cs:7984

csharp
if (funcName == "__critical_leave")
            {
                if (args.Length != 1) Error(expr, "__critical_leave(token) expects 1 argument");

                CompileIntoA(args[0]);
                EmitAsm("LD_B_A"); // token

                AsmOperand clAfterDec = MakeUniqueLabel("critleave_afterdec");
                AsmOperand clDone = MakeUniqueLabel("critleave_done");

                EmitLoadA(CriticalDepthVar);
                EmitAsm("OR_A");
                EmitAsm("JR_Z", clAfterDec);
                EmitAsm("DEC_A");
                EmitStoreA(CriticalDepthVar);
                EmitLabel(clAfterDec);

                // Re-enable only for outermost token (token==0).
                EmitAsm("LD_A_B");
                EmitAsm("OR_A");
                EmitAsm("JR_NZ", clDone);
                EmitAsm("EI");
                EmitLabel(clDone);
                return;
            }
__dot2_q8_8

Berechnet ein vorzeichenloses Skalarprodukt mit zwei Termen.

Syntax

c
u16 __dot2_q8_8(u16 a0, u16 a1, u8 b0, u8 b1);

Parameter

a0

Vorzeichenloses Wort, 0..65535.

a1

Vorzeichenloses Wort, 0..65535.

b0

Vorzeichenloser Koeffizient, 0..255.

b1

Vorzeichenloser 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

c
// Compare literal coefficients with values read from RAM.
result[36]=__dot2_q8_8(513,769,64,127);
v0=513;v1=769;v2=64;v3=127;
result[37]=__dot2_q8_8(v0,v1,v2,v3);

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.

__dot2_q8_8: DMG
DMG
__dot2_q8_8: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:11575

kitaqgb/kitaqgb/CodeGenerator.cs:11575

csharp
if (funcName == "__dot2_q8_8")
{
    if (args.Length != 4) Error(expr, "__dot2_q8_8 requires 4 arguments");
    LastCallReturnsHL = true;

    // Fast paths when coefficients are compile-time constants.
    // Special-case: 0 / power-of-two coefficients (u8) => shift instead of mul.
    if (TryGetU8Const(args[2], out int ax0) && (ax0 == 0) && TryGetU8Const(args[3], out int ay0) && (ay0 == 0))
    {
        CompileDiscard(args[0]);
        CompileDiscard(args[1]);
        EmitAsm("XOR_A");
        EmitAsm("LD_H_A");
        EmitAsm("LD_L_A");
        return;
    }
    if (TryGetU8Const(args[2], out int axOnly) && (axOnly == 0))
    {
        CompileDiscard(args[0]);
        // only y*ay
        if (!TryEmitDot2Pow2Term(args[1], args[3], signed: false, q1_7: false, out _))
        {
            CompileIntoHL(args[1]);
            EmitAsm("PUSH_HL");
            CompileIntoA(args[3]);
            EmitAsm("LD_C_A");
            EmitAsm("POP_HL");
            EmitMul16x8Shr8_HL("__dot2_y");
        }
        return;
    }
    if (TryGetU8Const(args[3], out int ayOnly) && (ayOnly == 0))
    {
        // only x*ax
        if (!TryEmitDot2Pow2Term(args[0], args[2], signed: false, q1_7: false, out _))
        {
            CompileIntoHL(args[0]);
            EmitAsm("PUSH_HL");
            CompileIntoA(args[2]);
            EmitAsm("LD_C_A");
            EmitAsm("POP_HL");
            EmitMul16x8Shr8_HL("__dot2_x");
        }
        // Preserve the scaled result while evaluating the zero-product input.
        EmitAsm("PUSH_HL");
        CompileDiscard(args[1]);
        EmitAsm("POP_HL");
        return;
    }

    // x*ax
    if (!TryEmitDot2Pow2Term(args[0], args[2], signed: false, q1_7: false, out _))
    {
        CompileIntoHL(args[0]);
        EmitAsm("PUSH_HL");
        CompileIntoA(args[2]);
        EmitAsm("LD_C_A");
        EmitAsm("POP_HL");
        EmitMul16x8Shr8_HL("__dot2_x");
    }
    EmitAsm("PUSH_HL");

    // y*ay
    if (!TryEmitDot2Pow2Term(args[1], args[3], signed: false, q1_7: false, out _))
    {
        CompileIntoHL(args[1]);
        EmitAsm("PUSH_HL");
        CompileIntoA(args[3]);
        EmitAsm("LD_C_A");
        EmitAsm("POP_HL");
        EmitMul16x8Shr8_HL("__dot2_y");
    }
    EmitAsm("POP_DE");
    EmitAsm("ADD_HL_DE");
    return;
}
__dot3_q8_8

Berechnet ein vorzeichenloses Skalarprodukt mit drei Termen.

Syntax

c
u16 __dot3_q8_8(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);

Parameter

a0

Vorzeichenloses Wort, 0..65535.

a1

Vorzeichenloses Wort, 0..65535.

a2

Vorzeichenloses Wort, 0..65535.

b0

Vorzeichenloser Koeffizient, 0..255.

b1

Vorzeichenloser Koeffizient, 0..255.

b2

Vorzeichenloser 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

c
// Compare literal coefficients with values read from RAM.
result[6]=__dot3_q8_8(513,769,1025,64,127,3);
v0=513;v1=769;v2=1025;v3=64;v4=127;v5=3;
result[7]=__dot3_q8_8(v0,v1,v2,v3,v4,v5);

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.

__dot3_q8_8: DMG
DMG
__dot3_q8_8: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:11550

kitaqgb/kitaqgb/CodeGenerator.cs:11550

csharp
if (funcName == "__dot3_q8_8")
            {
                if (args.Length != 6) Error(expr, "__dot3_q8_8 requires 6 arguments");
                LastCallReturnsHL = true;

                // x*ax
                EmitDotTermToHL(args[0], args[3], signed: false, q1_7: false, labelPrefix: "__dot3_x");
                EmitAsm("PUSH_HL");

                // y*ay
                EmitDotTermToHL(args[1], args[4], signed: false, q1_7: false, labelPrefix: "__dot3_y");
                EmitAsm("POP_DE");
                EmitAsm("ADD_HL_DE");
                EmitAsm("PUSH_HL");

                // z*az
                EmitDotTermToHL(args[2], args[5], signed: false, q1_7: false, labelPrefix: "__dot3_z");
                EmitAsm("POP_DE");
                EmitAsm("ADD_HL_DE");
                return;
            }
__farcall

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

c
__farcall(bank, function_name);

Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.

Parameter

bank

Vorzeichenlose 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.

func

Deklarierter 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

c
__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.

__farcall: DMG
DMG
__farcall: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/kitaqgb/CodeGenerator.cs:7288

kitaqgb/kitaqgb/CodeGenerator.cs:7288

csharp
if (funcName == "__farcall")
            {
                if (args.Length != 2) Program.Error("__farcall(bank, func) expects 2 arguments");
                if (!args[1].Match(Tag.Name, out string targetName)) Program.Error("__farcall(bank, func) requires 2nd argument to be a function name");

                // A named far call supplies no arguments. Reject data symbols
                // and callbacks that would otherwise read stale argument storage.
                CFunctionInfo targetInfo;
                if (!Functions.TryGetValue(targetName, out targetInfo))
                {
                    Error(expr, "__farcall requires a declared function name");
                    return;
                }
                if ((targetInfo.Parameters ?? Array.Empty<FieldInfo>()).Length != 0)
                {
                    Error(expr, "__farcall requires a callback with no parameters");
                    return;
                }
                Expr bankExpr = FoldConstants(args[0]);
                int targetBank = -1;
                if (targetInfo != null)
                {
                    targetBank = targetInfo.RomBank;
                    PrepareStructReturnDestination(expr, targetInfo.ReturnType, targetInfo, targetName);
                    if (targetInfo.ReturnType != null)
                        LastCallReturnsHL = ReturnsInHL(expr, targetInfo.ReturnType);
                }

                // A known bank permits direct dispatch when safe, otherwise request a fixed-bank thunk.
                if (TryResolveBankExprToConstU8(args[0], out int bankLiteral))
                {
                    int optimizedBank = bankLiteral & 0xFF;
                    bool farcallDirectOK = IsDirectCallBankSafe(optimizedBank, CurrentFunctionBank);
                    if (!farcallDirectOK)
                    {
                        string farcallThunkName = EnsureBank0Thunk(
                            targetName,
                            optimizedBank,
                            isStackCall: targetInfo != null && targetInfo.IsStackCall,
                            stackArgBytes: 0);
                        EmitAsm("CALL", new AsmOperand(farcallThunkName, AddressMode.Absolute));
                        RecordCallEdge(expr, targetName, optimizedBank, "farcall_bank_thunk", viaThunk: true, viaFarcall: true, actualArgSizes: new int[0], expectedArgSizes: new int[0]);
                    }
                    else
                    {
                        if (Program.CheckBankCalls && !InUnsafe && optimizedBank != 0)
                        {
                            AsmOperand ok = MakeUniqueLabel("kq_bank_ok");
                            EmitLoadA(RomBankVar);
                            EmitAsm("CP_IMM", new AsmOperand(optimizedBank & 0xFF, AddressMode.Immediate));
                            EmitAsm("JP_Z", ok);
                            EmitAsm("JP", EnsureCheckTrap());
                            EmitLabel(ok);
                        }

                        EmitAsm("CALL", new AsmOperand(targetName, AddressMode.Absolute));
                        RecordCallEdge(expr, targetName, optimizedBank, "farcall_direct", viaThunk: false, viaFarcall: true, actualArgSizes: new int[0], expectedArgSizes: new int[0]);
                    }
                    return;
                }

                // A = requested bank argument (runtime value)
                CompileIntoA(bankExpr);

                // Always dispatch via bank0 helper thunk.
                string thunkName = EnsureBank0RuntimeFarcallThunk(targetName);
                EmitAsm("CALL", new AsmOperand(thunkName, AddressMode.Absolute));
                RecordCallEdge(expr, targetName, targetBank, "farcall_intrinsic", viaThunk: true, viaFarcall: true, actualArgSizes: new int[0], expectedArgSizes: new int[0]);
                return;
            }
__getwinmapbase

Liest LCDC-Bit 6 zur Auswahl der Basisadresse der Fenster-Kachelkarte.

Syntax

c
u16 __getwinmapbase();

Rückgabewert

Gibt $9800 oder $9C00 zurück: die Kachelkartenbasis, nicht die Adresse der Kachelgrafik.

Anwendungsbeispiel

c
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.

__getwinmapbase: DMG
DMG
__getwinmapbase: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_display.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_display.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:10642

kitaqgb/kitaqgb/CodeGenerator.cs:10642

csharp
if (funcName == "__getwinmapbase")
{
    if (args.Length != 0) Error(expr, "__getwinmapbase requires 0 arguments");

    AsmOperand win_base0 = MakeUniqueLabel("getwinbase_base0");
    AsmOperand win_done  = MakeUniqueLabel("getwinbase_done");

    EmitAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem)); // LCDC
    EmitAsm("AND_IMM", new AsmOperand(0x40, AddressMode.Immediate)); // bit6 (Window tile map display select)
    EmitAsm("JR_Z", win_base0);
    EmitAsm("LD_HL_IMM", new AsmOperand(0x9C00, AddressMode.Immediate16));
    EmitAsm("JR", win_done);
    EmitLabel(win_base0);
    EmitAsm("LD_HL_IMM", new AsmOperand(0x9800, AddressMode.Immediate16));
    EmitLabel(win_done);
    LastCallReturnsHL = true;
    return;
}
__mac16

Berechnet eine vorzeichenlose Multiplikation mit Akkumulation.

Syntax

c
u16 __mac16(u16 acc, u16 a, u8 b);

Parameter

acc

Zu addierender 16-Bit-Akkumulator.

a

Vorzeichenloses Wort, 0..65535.

b

Vorzeichenloser 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

c
// Compare literal coefficients with values read from RAM.
result[34]=__mac16(65500,513,127);
v0=65500;v1=513;v2=127;
result[35]=__mac16(v0,v1,v2);

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.

__mac16: DMG
DMG
__mac16: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:11499

kitaqgb/kitaqgb/CodeGenerator.cs:11499

csharp
if (funcName == "__mac16")
            {
                if (args.Length != 3) Error(expr, "__mac16 requires 3 arguments");

                // mul = (a*b)>>8
                EmitDotTermToHL(args[1], args[2], signed: false, q1_7: false, labelPrefix: "__mac16_mul");

                // acc + mul
                EmitAsm("PUSH_HL");
                CompileIntoHL(args[0]);
                EmitAsm("POP_DE");
                EmitAsm("ADD_HL_DE");
                LastCallReturnsHL = true;
                return;
            }
__mul16x8

Berechnet eine vorzeichenlose Multiplikation.

Syntax

c
u16 __mul16x8(u16 a, u8 b);

Parameter

a

Vorzeichenloses Wort, 0..65535.

b

Vorzeichenloser 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

c
// 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.

__mul16x8: DMG
DMG
__mul16x8: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:11467

kitaqgb/kitaqgb/CodeGenerator.cs:11467

csharp
if (funcName == "__mul16x8")
            {
                if (args.Length != 2) Error(expr, "__mul16x8 requires 2 arguments");

                EmitDotTermToHL(args[0], args[1], signed: false, q1_7: false, labelPrefix: "__mul16x8");
                LastCallReturnsHL = true;
                return;
            }
__mul8x8_hi

Multipliziert zwei vorzeichenlose Bytes und gibt das obere Byte zurück.

Syntax

c
u8 __mul8x8_hi(u8 a, u8 b);

Parameter

a

Vorzeichenloses Byte, 0..255.

b

Vorzeichenloses Byte, 0..255.

Rückgabewert

floor(a*b/256), im Bereich 0..254.

Anwendungsbeispiel

c
// Compare literal coefficients with values read from RAM.
result[4]=__mul8x8_hi(255,255);
v0=255;v1=255;
result[5]=__mul8x8_hi(v0,v1);

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.

__mul8x8_hi: DMG
DMG
__mul8x8_hi: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:11452

kitaqgb/kitaqgb/CodeGenerator.cs:11452

csharp
if (funcName == "__mul8x8_hi")
            {
                if (args.Length != 2) Error(expr, "__mul8x8_hi requires 2 arguments");

                CompileIntoA(args[0]);
                EmitAsm("PUSH_AF");
                CompileIntoA(args[1]);
                EmitAsm("LD_C_A");
                EmitAsm("POP_AF");
                EmitAsm("LD_E_A");
                EmitMul8x8ToHL_EC("__mul8x8_hi");
                EmitAsm("LD_A_H");
                return;
            }
__padrep

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

c
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

state

Zeiger 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.

keys

Vollständige Maske der aktuell gehaltenen Tasten im GB-Layout BTN_*. Übergeben Sie einen Wert aus __readpad, das untere Byte von __readpadex oder input_current; die Wiederholungsfunktion liest die Hardware nicht selbst.

trigger

Neu gedrückte Bits dieser Aktualisierung im selben Layout wie keys, üblicherweise keys & ~previous_keys oder das obere Byte von __readpadex. Es dürfen nur Bits enthalten sein, die auch in keys gesetzt sind. Wird jedes Mal die Haltemaske übergeben, beginnt bei jedem Aufruf fälschlich ein neuer Tastendruck.

mask

Zulä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

c
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.

__padrep: DMG
DMG
__padrep: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\pad_repeat_state.c -I .\kitaq-docs\samples -I .\kitaqgb\lib -o .\out\pad_repeat_state.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:11249

csharp
{
    if (args.Length != 4) Error(expr, "__padrep requires 4 arguments (state*, keys, trigger, mask)");

    AsmOperand pm_done        = MakeUniqueLabel("padrepM_done");
    AsmOperand pm_ret0        = MakeUniqueLabel("padrepM_ret0");
    AsmOperand pm_no_trig     = MakeUniqueLabel("padrepM_notrig");
    AsmOperand pm_reset_all   = MakeUniqueLabel("padrepM_reset");
    AsmOperand pm_dir0        = MakeUniqueLabel("padrepM_dir0");
    AsmOperand pm_have_dir    = MakeUniqueLabel("padrepM_havedir");
    AsmOperand pm_first_rep   = MakeUniqueLabel("padrepM_firstrep");
    AsmOperand pm_ret_dir     = MakeUniqueLabel("padrepM_retdir");
    AsmOperand pm_skip_dasinc = MakeUniqueLabel("padrepM_skipdasinc");
    AsmOperand pm_skip_arrinc = MakeUniqueLabel("padrepM_skiparrinc");

    // Evaluate args safely:
    // HL = state*, B = keys, C = trigger, E = mask
    CompileIntoHL(args[0]);
    EmitAsm("PUSH_HL");
    CompileIntoA(args[1]); EmitAsm("LD_B_A"); // keys
    CompileIntoA(args[2]); EmitAsm("LD_C_A"); // trigger
    CompileIntoA(args[3]); EmitAsm("LD_E_A"); // mask
    EmitAsm("POP_HL");

    // trigMasked = trigger & mask -> D
    EmitAsm("LD_A_C");
    EmitAsm("AND_E");
    EmitAsm("LD_D_A");
    EmitAsm("OR_A");
    EmitAsm("JP_Z", pm_no_trig);

    // select = lowbit(trigMasked) = x & -x
    EmitAsm("LD_A_D");
    EmitAsm("CPL");
    EmitAsm("INC_A");
    EmitAsm("AND_D");
    EmitAsm("LD_C_A"); // save selected in C

    // dirMask=selected, dasCnt=0, arrCnt=0
    EmitAsm("LD_A_C");
    EmitAsm("LD_HL_A"); // [0]=selected
    EmitAsm("PUSH_HL");
    EmitAsm("INC_HL"); Emi
__padrep_down

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

c
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

state

Zeiger 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.

keys

Vollständige Maske der aktuell gehaltenen Tasten im GB-Layout BTN_*. Übergeben Sie einen Wert aus __readpad, das untere Byte von __readpadex oder input_current; die Wiederholungsfunktion liest die Hardware nicht selbst.

trigger

Neu gedrückte Bits dieser Aktualisierung im selben Layout wie keys, üblicherweise keys & ~previous_keys oder das obere Byte von __readpadex. Es dürfen nur Bits enthalten sein, die auch in keys gesetzt 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

c
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.

__padrep_down: DMG
DMG
__padrep_down: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\pad_repeat_state.c -I .\kitaq-docs\samples -I .\kitaqgb\lib -o .\out\pad_repeat_state.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:11069

csharp
{
    if (args.Length != 3) Error(expr, "__padrep_down requires 3 arguments (state*, keys, trigger)");

    AsmOperand pd_done        = MakeUniqueLabel("padrepD_done");
    AsmOperand pd_ret0        = MakeUniqueLabel("padrepD_ret0");
    AsmOperand pd_no_trig     = MakeUniqueLabel("padrepD_notrig");
    AsmOperand pd_dir0        = MakeUniqueLabel("padrepD_dir0");
    AsmOperand pd_have_dir    = MakeUniqueLabel("padrepD_havedir");
    AsmOperand pd_first_rep   = MakeUniqueLabel("padrepD_firstrep");
    AsmOperand pd_ret_dir     = MakeUniqueLabel("padrepD_retdir");
    AsmOperand pd_skip_dasinc = MakeUniqueLabel("padrepD_skipdasinc");
    AsmOperand pd_skip_arrinc = MakeUniqueLabel("padrepD_skiparrinc");
    AsmOperand pd_reset_all   = MakeUniqueLabel("padrepD_reset");

    // Evaluate args safely:
    // HL = state*, B = keys, C = trigger
    CompileIntoHL(args[0]);
    EmitAsm("PUSH_HL");
    CompileIntoA(args[1]); EmitAsm("LD_B_A"); // keys
    CompileIntoA(args[2]); EmitAsm("LD_C_A"); // trigger
    EmitAsm("POP_HL");

    // --- Trigger Down? (bit3 = 0x08) ---
    EmitAsm("LD_A_C");
    EmitAsm("AND_IMM", new AsmOperand(0x08, AddressMode.Immediate));
    EmitAsm("JP_Z", pd_no_trig);

    // dirMask=0x08, dasCnt=0, arrCnt=0
    EmitAsm("LD_A_IMM", new AsmOperand(0x08, AddressMode.Immediate));
    EmitAsm("LD_HL_A"); // [0]=0x08
    EmitAsm("PUSH_HL");
    EmitAsm("INC_HL"); EmitAsm("INC_HL"); EmitAsm("INC_HL"); // -> [3]
    EmitAsm("XOR_A");
    EmitAsm("LD_HL_A"); // dasCnt=0
    EmitAsm("INC_HL");
    EmitAsm("LD_HL_A"); // arrCnt=0
    EmitAsm("POP_HL");

    EmitAsm
__padrep_init

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

c
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

state

Zeiger 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.

das

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.

arr

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.

Rückgabewert

Keiner (void).

Anwendungsbeispiel

c
__padrep_init(lr,3,2);
// Active action and counters are zero; delay and interval are retained.
expect(lr[0],0); expect(lr[1],3); expect(lr[2],2);
expect(lr[3],0); expect(lr[4],0);

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.

__padrep_init: DMG
DMG
__padrep_init: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\pad_repeat_state.c -I .\kitaq-docs\samples -I .\kitaqgb\lib -o .\out\pad_repeat_state.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:10808

csharp
{
    if (args.Length != 3) Error(expr, "__padrep_init requires 3 arguments (state*, das, arr)");

    // Evaluate args with register safety
    CompileIntoHL(args[0]);
    EmitAsm("PUSH_HL");
    CompileIntoA(args[1]); EmitAsm("LD_B_A"); // das
    CompileIntoA(args[2]); EmitAsm("LD_C_A"); // arr
    EmitAsm("POP_HL");

    // dirMask = 0; dasCnt=0; arrCnt=0; store params
    EmitAsm("XOR_A");
    EmitAsm("LD_HL_A"); // [0]=0
    EmitAsm("INC_HL");
    EmitAsm("LD_A_B");
    EmitAsm("LD_HL_A"); // [1]=das
    EmitAsm("INC_HL");
    EmitAsm("LD_A_C");
    EmitAsm("LD_HL_A"); // [2]=arr
    EmitAsm("INC_HL");
    EmitAsm("XOR_A");
    EmitAsm("LD_HL_A"); // [3]=dasCnt
    EmitAsm("INC_HL");
    EmitAsm("LD_HL_A"); // [4]=arrCnt
    return;
}

// 2.21 __padrep_reset(state_ptr) -> void
// Resets runtime counters for PadRepeatState while keeping das/arr params:
// dirMask=0, dasCnt=0, arrCnt=0
// Treated as a compiler intrinsic (no C definition required).
if (funcName == "__padrep_reset")
{
    if (args.Length != 1) Error(expr, "__padrep_reset requires 1 argument (state*)");

    CompileIntoHL(args[0]);

    EmitAsm("XOR_A");
    EmitAsm("LD_HL_A"); // [0]=0 (dirMask)

    // [3]=0, [4]=0
    EmitAsm("PUSH_HL");
    EmitAsm("INC_HL"); EmitAsm("INC_HL"); EmitAsm("INC_HL"); // -> [3]
    EmitAsm("LD_HL_A");
    EmitAsm("INC_HL"); // -> [4]
    EmitAsm("LD_HL_A");
    EmitAsm("POP_HL");
    return;
}

// __padrep_lr(state*, keys, trigger) returns Right (1), Left (2), or zero.
// New triggers take priority, with Right selected first when both trigger bits are set.
// Without a new
__padrep_lr

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

c
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

state

Zeiger 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.

keys

Vollständige Maske der aktuell gehaltenen Tasten im GB-Layout BTN_*. Übergeben Sie einen Wert aus __readpad, das untere Byte von __readpadex oder input_current; die Wiederholungsfunktion liest die Hardware nicht selbst.

trigger

Neu gedrückte Bits dieser Aktualisierung im selben Layout wie keys, üblicherweise keys & ~previous_keys oder das obere Byte von __readpadex. Es dürfen nur Bits enthalten sein, die auch in keys gesetzt 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

c
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.

__padrep_lr: DMG
DMG
__padrep_lr: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\pad_repeat_state.c -I .\kitaq-docs\samples -I .\kitaqgb\lib -o .\out\pad_repeat_state.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:10864

csharp
{
    if (args.Length != 3) Error(expr, "__padrep_lr requires 3 arguments (state*, keys, trigger)");

    AsmOperand pr_done        = MakeUniqueLabel("padrep_done");
    AsmOperand pr_ret0        = MakeUniqueLabel("padrep_ret0");
    AsmOperand pr_check_left  = MakeUniqueLabel("padrep_chkL");
    AsmOperand pr_no_trig     = MakeUniqueLabel("padrep_notrig");
    AsmOperand pr_reset_all   = MakeUniqueLabel("padrep_reset");
    AsmOperand pr_dir0        = MakeUniqueLabel("padrep_dir0");
    AsmOperand pr_have_dir    = MakeUniqueLabel("padrep_havedir");
    AsmOperand pr_first_rep   = MakeUniqueLabel("padrep_firstrep");
    AsmOperand pr_ret_dir     = MakeUniqueLabel("padrep_retdir");
    AsmOperand pr_skip_dasinc = MakeUniqueLabel("padrep_skipdasinc");
    AsmOperand pr_skip_arrinc = MakeUniqueLabel("padrep_skiparrinc");

    // Evaluate args safely:
    // HL = state*, B = keys, C = trigger
    CompileIntoHL(args[0]);
    EmitAsm("PUSH_HL");
    CompileIntoA(args[1]); EmitAsm("LD_B_A"); // keys
    CompileIntoA(args[2]); EmitAsm("LD_C_A"); // trigger
    EmitAsm("POP_HL");

    // --- Trigger Right? (bit0) ---
    EmitAsm("LD_A_C");
    EmitAsm("AND_IMM", new AsmOperand(0x01, AddressMode.Immediate));
    EmitAsm("JP_Z", pr_check_left);

    // dirMask=0x01, dasCnt=0, arrCnt=0
    EmitAsm("LD_A_IMM", new AsmOperand(0x01, AddressMode.Immediate));
    EmitAsm("LD_HL_A"); // [0]=0x01
    EmitAsm("PUSH_HL");
    EmitAsm("INC_HL"); EmitAsm("INC_HL"); EmitAsm("INC_HL"); // -> [3]
    EmitAsm("XOR_A");
    EmitAsm("LD_HL_A"); // dasCnt=0
    EmitAsm("INC_HL");
    EmitAsm("LD_HL_A")
__padrep_mask

__padrep_mask 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

c
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

state

Zeiger 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.

keys

Vollständige Maske der aktuell gehaltenen Tasten im GB-Layout BTN_*. Übergeben Sie einen Wert aus __readpad, das untere Byte von __readpadex oder input_current; die Wiederholungsfunktion liest die Hardware nicht selbst.

trigger

Neu gedrückte Bits dieser Aktualisierung im selben Layout wie keys, üblicherweise keys & ~previous_keys oder das obere Byte von __readpadex. Es dürfen nur Bits enthalten sein, die auch in keys gesetzt sind. Wird jedes Mal die Haltemaske übergeben, beginnt bei jedem Aufruf fälschlich ein neuer Tastendruck.

mask

Zulä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

c
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.

__padrep_mask: DMG
DMG
__padrep_mask: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\pad_repeat_state.c -I .\kitaq-docs\samples -I .\kitaqgb\lib -o .\out\pad_repeat_state.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:11249

csharp
{
    if (args.Length != 4) Error(expr, "__padrep requires 4 arguments (state*, keys, trigger, mask)");

    AsmOperand pm_done        = MakeUniqueLabel("padrepM_done");
    AsmOperand pm_ret0        = MakeUniqueLabel("padrepM_ret0");
    AsmOperand pm_no_trig     = MakeUniqueLabel("padrepM_notrig");
    AsmOperand pm_reset_all   = MakeUniqueLabel("padrepM_reset");
    AsmOperand pm_dir0        = MakeUniqueLabel("padrepM_dir0");
    AsmOperand pm_have_dir    = MakeUniqueLabel("padrepM_havedir");
    AsmOperand pm_first_rep   = MakeUniqueLabel("padrepM_firstrep");
    AsmOperand pm_ret_dir     = MakeUniqueLabel("padrepM_retdir");
    AsmOperand pm_skip_dasinc = MakeUniqueLabel("padrepM_skipdasinc");
    AsmOperand pm_skip_arrinc = MakeUniqueLabel("padrepM_skiparrinc");

    // Evaluate args safely:
    // HL = state*, B = keys, C = trigger, E = mask
    CompileIntoHL(args[0]);
    EmitAsm("PUSH_HL");
    CompileIntoA(args[1]); EmitAsm("LD_B_A"); // keys
    CompileIntoA(args[2]); EmitAsm("LD_C_A"); // trigger
    CompileIntoA(args[3]); EmitAsm("LD_E_A"); // mask
    EmitAsm("POP_HL");

    // trigMasked = trigger & mask -> D
    EmitAsm("LD_A_C");
    EmitAsm("AND_E");
    EmitAsm("LD_D_A");
    EmitAsm("OR_A");
    EmitAsm("JP_Z", pm_no_trig);

    // select = lowbit(trigMasked) = x & -x
    EmitAsm("LD_A_D");
    EmitAsm("CPL");
    EmitAsm("INC_A");
    EmitAsm("AND_D");
    EmitAsm("LD_C_A"); // save selected in C

    // dirMask=selected, dasCnt=0, arrCnt=0
    EmitAsm("LD_A_C");
    EmitAsm("LD_HL_A"); // [0]=selected
    EmitAsm("PUSH_HL");
    EmitAsm("INC_HL"); Emi
__padrep_reset

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

c
void __padrep_reset(u8* state);

Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.

Parameter

state

Zeiger 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

c
__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.

__padrep_reset: DMG
DMG
__padrep_reset: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\pad_repeat_state.c -I .\kitaq-docs\samples -I .\kitaqgb\lib -o .\out\pad_repeat_state.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:10840

csharp
{
    if (args.Length != 1) Error(expr, "__padrep_reset requires 1 argument (state*)");

    CompileIntoHL(args[0]);

    EmitAsm("XOR_A");
    EmitAsm("LD_HL_A"); // [0]=0 (dirMask)

    // [3]=0, [4]=0
    EmitAsm("PUSH_HL");
    EmitAsm("INC_HL"); EmitAsm("INC_HL"); EmitAsm("INC_HL"); // -> [3]
    EmitAsm("LD_HL_A");
    EmitAsm("INC_HL"); // -> [4]
    EmitAsm("LD_HL_A");
    EmitAsm("POP_HL");
    return;
}

// __padrep_lr(state*, keys, trigger) returns Right (1), Left (2), or zero.
// New triggers take priority, with Right selected first when both trigger bits are set.
// Without a new trigger, simultaneous Left/Right holds suppress movement without resetting counters.
// A single held direction is adopted without immediate movement when no direction is active.
// Subsequent updates use the five-byte state's initial delay and repeat interval; ARR=0 repeats each update.
if (funcName == "__padrep_lr")
{
    if (args.Length != 3) Error(expr, "__padrep_lr requires 3 arguments (state*, keys, trigger)");

    AsmOperand pr_done        = MakeUniqueLabel("padrep_done");
    AsmOperand pr_ret0        = MakeUniqueLabel("padrep_ret0");
    AsmOperand pr_check_left  = MakeUniqueLabel("padrep_chkL");
    AsmOperand pr_no_trig     = MakeUniqueLabel("padrep_notrig");
    AsmOperand pr_reset_all   = MakeUniqueLabel("padrep_reset");
    AsmOperand pr_dir0        = MakeUniqueLabel("padrep_dir0");
    AsmOperand pr_have_dir    = MakeUniqueLabel("padrep_havedir");
    AsmOperand pr_first_rep   = MakeUniqueLabel("padrep_firstrep");
    AsmOperand pr_ret_dir     = MakeUniqueLabel("padrep
__readpad

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

c
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

c
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.

__readpad: DMG
DMG
__readpad: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\input_raw.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_raw.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:10669

csharp
{
    if (args.Length != 0) Error(expr, "__readpad requires 0 arguments");

    AsmOperand p1 = new AsmOperand(0x00, AddressMode.HighMem); // P1 / JOYP

    // Directions (P1=0x20)
    EmitAsm("LD_A_IMM", new AsmOperand(0x20, AddressMode.Immediate));
    EmitAsm("LDH_MEM_A", p1);
    EmitAsm("LDH_A_MEM", p1); // dummy reads for settle
    EmitAsm("LDH_A_MEM", p1);
    EmitAsm("CPL");
    EmitAsm("AND_IMM", new AsmOperand(0x0F, AddressMode.Immediate));
    EmitAsm("LD_C_A"); // dpad in C

    // Buttons (P1=0x10)
    EmitAsm("LD_A_IMM", new AsmOperand(0x10, AddressMode.Immediate));
    EmitAsm("LDH_MEM_A", p1);
    EmitAsm("LDH_A_MEM", p1);
    EmitAsm("LDH_A_MEM", p1);
    EmitAsm("CPL");
    EmitAsm("AND_IMM", new AsmOperand(0x0F, AddressMode.Immediate));
    // shift into upper nibble (<<4)
    EmitAsm("RLCA"); EmitAsm("RLCA"); EmitAsm("RLCA"); EmitAsm("RLCA");
    EmitAsm("OR_C");

    // Restore P1 selection (0x30) without losing return value
    EmitAsm("LD_C_A");
    EmitAsm("LD_A_IMM", new AsmOperand(0x30, AddressMode.Immediate));
    EmitAsm("LDH_MEM_A", p1);
    EmitAsm("LD_A_C");
    return;
}

// 2.17 __readpaddir() -> u8 in A (bits0..3 only)
// Returns dpad nibble (Right,Left,Up,Down) with 1 when pressed.
// Treated as a compiler intrinsic (no C definition required).
if (funcName == "__readpaddir")
{
    if (args.Length != 0) Error(expr, "__readpaddir requires 0 arguments");

    AsmOperand p1 = new AsmOperand(0x00, AddressMode.HighMem); // P1 / JOYP

    EmitAsm("LD_A_IMM", new AsmOperand(0x20, AddressMode.Immediate));
    EmitAsm("LDH_MEM_A", p1);
    EmitAsm
__readpadbtn

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

c
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

c
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.

__readpadbtn: DMG
DMG
__readpadbtn: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\input_raw.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_raw.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:10729

csharp
{
    if (args.Length != 0) Error(expr, "__readpadbtn requires 0 arguments");

    AsmOperand p1 = new AsmOperand(0x00, AddressMode.HighMem); // P1 / JOYP

    EmitAsm("LD_A_IMM", new AsmOperand(0x10, AddressMode.Immediate));
    EmitAsm("LDH_MEM_A", p1);
    EmitAsm("LDH_A_MEM", p1);
    EmitAsm("LDH_A_MEM", p1);
    EmitAsm("CPL");
    EmitAsm("AND_IMM", new AsmOperand(0x0F, AddressMode.Immediate));

    EmitAsm("LD_C_A");
    EmitAsm("LD_A_IMM", new AsmOperand(0x30, AddressMode.Immediate));
    EmitAsm("LDH_MEM_A", p1);
    EmitAsm("LD_A_C");
    return;
}

// 2.19 __readpadex(prev_u8) -> u16 in HL
// Returns: L = keys (same encoding as __readpad), H = trigger (newly pressed edge: keys & ~prev).
// This mirrors the common pattern:
// prev = keys; keys = __readpad(); trigger = keys & ~prev;
// Treated as a compiler intrinsic (no C definition required).
if (funcName == "__readpadex")
{
    if (args.Length != 1) Error(expr, "__readpadex requires 1 argument (prev_keys)");

    // prev -> B
    CompileIntoA(args[0]);
    EmitAsm("LD_B_A");

    AsmOperand p1 = new AsmOperand(0x00, AddressMode.HighMem); // P1 / JOYP

    // Directions (P1=0x20)
    EmitAsm("LD_A_IMM", new AsmOperand(0x20, AddressMode.Immediate));
    EmitAsm("LDH_MEM_A", p1);
    EmitAsm("LDH_A_MEM", p1);
    EmitAsm("LDH_A_MEM", p1);
    EmitAsm("CPL");
    EmitAsm("AND_IMM", new AsmOperand(0x0F, AddressMode.Immediate));
    EmitAsm("LD_C_A"); // dpad in C

    // Buttons (P1=0x10)
    EmitAsm("LD_A_IMM", new AsmOperand(0x10, AddressMode.Immediate));
    EmitAsm("LDH_MEM_A", p1);
    EmitAsm("LDH_A_MEM", p1)
__readpaddir

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

c
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

c
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.

__readpaddir: DMG
DMG
__readpaddir: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\input_raw.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\input_raw.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm --no-cache
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:10706

csharp
{
    if (args.Length != 0) Error(expr, "__readpaddir requires 0 arguments");

    AsmOperand p1 = new AsmOperand(0x00, AddressMode.HighMem); // P1 / JOYP

    EmitAsm("LD_A_IMM", new AsmOperand(0x20, AddressMode.Immediate));
    EmitAsm("LDH_MEM_A", p1);
    EmitAsm("LDH_A_MEM", p1);
    EmitAsm("LDH_A_MEM", p1);
    EmitAsm("CPL");
    EmitAsm("AND_IMM", new AsmOperand(0x0F, AddressMode.Immediate));

    EmitAsm("LD_C_A");
    EmitAsm("LD_A_IMM", new AsmOperand(0x30, AddressMode.Immediate));
    EmitAsm("LDH_MEM_A", p1);
    EmitAsm("LD_A_C");
    return;
}

// 2.18 __readpadbtn() -> u8 in A (bits0..3 only)
// Returns buttons nibble (A,B,Select,Start) with 1 when pressed.
// Treated as a compiler intrinsic (no C definition required).
if (funcName == "__readpadbtn")
{
    if (args.Length != 0) Error(expr, "__readpadbtn requires 0 arguments");

    AsmOperand p1 = new AsmOperand(0x00, AddressMode.HighMem); // P1 / JOYP

    EmitAsm("LD_A_IMM", new AsmOperand(0x10, AddressMode.Immediate));
    EmitAsm("LDH_MEM_A", p1);
    EmitAsm("LDH_A_MEM", p1);
    EmitAsm("LDH_A_MEM", p1);
    EmitAsm("CPL");
    EmitAsm("AND_IMM", new AsmOperand(0x0F, AddressMode.Immediate));

    EmitAsm("LD_C_A");
    EmitAsm("LD_A_IMM", new AsmOperand(0x30, AddressMode.Immediate));
    EmitAsm("LDH_MEM_A", p1);
    EmitAsm("LD_A_C");
    return;
}

// 2.19 __readpadex(prev_u8) -> u16 in HL
// Returns: L = keys (same encoding as __readpad), H = trigger (newly pressed edge: keys & ~prev).
// This mirrors the common pattern:
// prev = keys; keys = __readpad(); trigger = keys & ~prev;
// Treated as a c
__sdot2_q1_7

Berechnet ein vorzeichenbehaftetes Skalarprodukt mit zwei Termen.

Syntax

c
u16 __sdot2_q1_7(u16 a0, u16 a1, u8 b0, u8 b1);

Parameter

a0

Vorzeichenbehaftetes 16-Bit-Bitmuster für −32768..32767.

a1

Vorzeichenbehaftetes 16-Bit-Bitmuster für −32768..32767.

b0

Vorzeichenbehaftetes Byte-Bitmuster für einen Koeffizienten von −128..127.

b1

Vorzeichenbehaftetes 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

c
// Compare literal coefficients with values read from RAM.
result[20]=__sdot2_q1_7(-513,769,-64,127);
v0=-513;v1=769;v2=-64;v3=127;
result[21]=__sdot2_q1_7(v0,v1,v2,v3);
// Reverse coefficient signs and verify negative products.
result[22]=__sdot2_q1_7(-513,769,64,-127);
v0=-513;v1=769;v2=64;v3=-127;
result[23]=__sdot2_q1_7(v0,v1,v2,v3);

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.

__sdot2_q1_7: DMG
DMG
__sdot2_q1_7: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:11732

kitaqgb/kitaqgb/CodeGenerator.cs:11732

csharp
if (funcName == "__sdot2_q1_7")
{
    if (args.Length != 4) Error(expr, "__sdot2_q1_7 requires 4 arguments");
    LastCallReturnsHL = true;

    // x*ax
    if (!TryEmitDot2Pow2Term(args[0], args[2], signed: true, q1_7: true, out _))
    {
        CompileIntoHL(args[0]);
        EmitAsm("PUSH_HL");
        CompileIntoA(args[2]);
        EmitAsm("LD_C_A");
        EmitAsm("POP_HL");
        EmitSMul16x8Shr8_HL("__sdot2_q1_7_x");
        EmitAsm("ADD_HL_HL");
    }
    EmitAsm("PUSH_HL");

    // y*ay
    if (!TryEmitDot2Pow2Term(args[1], args[3], signed: true, q1_7: true, out _))
    {
        CompileIntoHL(args[1]);
        EmitAsm("PUSH_HL");
        CompileIntoA(args[3]);
        EmitAsm("LD_C_A");
        EmitAsm("POP_HL");
        EmitSMul16x8Shr8_HL("__sdot2_q1_7_y");
        EmitAsm("ADD_HL_HL");
    }
    EmitAsm("POP_DE");
    EmitAsm("ADD_HL_DE");
    return;
}
__sdot2_q8_8

Berechnet ein vorzeichenbehaftetes Skalarprodukt mit zwei Termen.

Syntax

c
u16 __sdot2_q8_8(u16 a0, u16 a1, u8 b0, u8 b1);

Parameter

a0

Vorzeichenbehaftetes 16-Bit-Bitmuster für −32768..32767.

a1

Vorzeichenbehaftetes 16-Bit-Bitmuster für −32768..32767.

b0

Vorzeichenbehaftetes Byte-Bitmuster für einen Koeffizienten von −128..127.

b1

Vorzeichenbehaftetes 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

c
// Compare literal coefficients with values read from RAM.
result[30]=__sdot2_q8_8(-513,769,-64,127);
v0=-513;v1=769;v2=-64;v3=127;
result[31]=__sdot2_q8_8(v0,v1,v2,v3);
// Reverse coefficient signs and verify negative products.
result[32]=__sdot2_q8_8(-513,769,64,-127);
v0=-513;v1=769;v2=64;v3=-127;
result[33]=__sdot2_q8_8(v0,v1,v2,v3);

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.

__sdot2_q8_8: DMG
DMG
__sdot2_q8_8: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:11654

kitaqgb/kitaqgb/CodeGenerator.cs:11654

csharp
if (funcName == "__sdot2_q8_8")
{
    if (args.Length != 4) Error(expr, "__sdot2_q8_8 requires 4 arguments");
    LastCallReturnsHL = true;

    // Fast paths when coefficients are compile-time constants.
    // Special-case: 0 / power-of-two coefficients (s8 two's complement) => arithmetic shift instead of mul.
    if (TryGetS8Const(args[2], out int sax0) && (sax0 == 0) && TryGetS8Const(args[3], out int say0) && (say0 == 0))
    {
        CompileDiscard(args[0]);
        CompileDiscard(args[1]);
        EmitAsm("XOR_A");
        EmitAsm("LD_H_A");
        EmitAsm("LD_L_A");
        return;
    }
    if (TryGetS8Const(args[2], out int saxOnly) && (saxOnly == 0))
    {
        CompileDiscard(args[0]);
        // only y*ay
        if (!TryEmitDot2Pow2Term(args[1], args[3], signed: true, q1_7: false, out _))
        {
            CompileIntoHL(args[1]);
            EmitAsm("PUSH_HL");
            CompileIntoA(args[3]);
            EmitAsm("LD_C_A");
            EmitAsm("POP_HL");
            EmitSMul16x8Shr8_HL("__sdot2_y");
        }
        return;
    }
    if (TryGetS8Const(args[3], out int sayOnly) && (sayOnly == 0))
    {
        // only x*ax
        if (!TryEmitDot2Pow2Term(args[0], args[2], signed: true, q1_7: false, out _))
        {
            CompileIntoHL(args[0]);
            EmitAsm("PUSH_HL");
            CompileIntoA(args[2]);
            EmitAsm("LD_C_A");
            EmitAsm("POP_HL");
            EmitSMul16x8Shr8_HL("__sdot2_x");
        }
        // Preserve the scaled result while evaluating the zero-product input.
        EmitAsm("PUSH_HL");
        CompileDiscard(args[1]);
        EmitAsm("POP_HL");
        return;
    }

    // x*ax
    if (!TryEmitDot2Pow2Term(args[0], args[2], signed: true, q1_7: false, out _))
    {
        CompileIntoHL(args[0]);
        EmitAsm("PUSH_HL");
        CompileIntoA(args[2]);
        EmitAsm("LD_C_A");
        EmitAsm("POP_HL");
        EmitSMul16x8Shr8_HL("__sdot2_x");
    }
    EmitAsm("PUSH_HL");

    // y*ay
    if (!TryEmitDot2Pow2Term(args[1], args[3], signed: true, q1_7: false, out _))
    {
        CompileIntoHL(args[1]);
        EmitAsm("PUSH_HL");
        CompileIntoA(args[3]);
        EmitAsm("LD_C_A");
        EmitAsm("POP_HL");
        EmitSMul16x8Shr8_HL("__sdot2_y");
    }
    EmitAsm("POP_DE");
    EmitAsm("ADD_HL_DE");
    return;
}
__sdot3_q1_7

Berechnet ein vorzeichenbehaftetes Skalarprodukt mit drei Termen.

Syntax

c
u16 __sdot3_q1_7(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);

Parameter

a0

Vorzeichenbehaftetes 16-Bit-Bitmuster für −32768..32767.

a1

Vorzeichenbehaftetes 16-Bit-Bitmuster für −32768..32767.

a2

Vorzeichenbehaftetes 16-Bit-Bitmuster für −32768..32767.

b0

Vorzeichenbehaftetes Byte-Bitmuster für einen Koeffizienten von −128..127.

b1

Vorzeichenbehaftetes Byte-Bitmuster für einen Koeffizienten von −128..127.

b2

Vorzeichenbehaftetes 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

c
// Compare literal coefficients with values read from RAM.
result[16]=__sdot3_q1_7(-513,769,-1025,-64,127,-3);
v0=-513;v1=769;v2=-1025;v3=-64;v4=127;v5=-3;
result[17]=__sdot3_q1_7(v0,v1,v2,v3,v4,v5);
// Reverse coefficient signs and verify negative products.
result[18]=__sdot3_q1_7(-513,769,-1025,64,-127,3);
v0=-513;v1=769;v2=-1025;v3=64;v4=-127;v5=3;
result[19]=__sdot3_q1_7(v0,v1,v2,v3,v4,v5);

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.

__sdot3_q1_7: DMG
DMG
__sdot3_q1_7: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:11790

kitaqgb/kitaqgb/CodeGenerator.cs:11790

csharp
if (funcName == "__sdot3_q1_7")
            {
                if (args.Length != 6) Error(expr, "__sdot3_q1_7 requires 6 arguments");
                LastCallReturnsHL = true;

                // x*ax
                EmitDotTermToHL(args[0], args[3], signed: true, q1_7: true, labelPrefix: "__sdot3_q1_7_x");
                EmitAsm("PUSH_HL");

                // y*ay
                EmitDotTermToHL(args[1], args[4], signed: true, q1_7: true, labelPrefix: "__sdot3_q1_7_y");
                EmitAsm("POP_DE");
                EmitAsm("ADD_HL_DE");
                EmitAsm("PUSH_HL");

                // z*az
                EmitDotTermToHL(args[2], args[5], signed: true, q1_7: true, labelPrefix: "__sdot3_q1_7_z");
                EmitAsm("POP_DE");
                EmitAsm("ADD_HL_DE");
                return;
            }
__sdot3_q8_8

Berechnet ein vorzeichenbehaftetes Skalarprodukt mit drei Termen.

Syntax

c
u16 __sdot3_q8_8(u16 a0, u16 a1, u16 a2, u8 b0, u8 b1, u8 b2);

Parameter

a0

Vorzeichenbehaftetes 16-Bit-Bitmuster für −32768..32767.

a1

Vorzeichenbehaftetes 16-Bit-Bitmuster für −32768..32767.

a2

Vorzeichenbehaftetes 16-Bit-Bitmuster für −32768..32767.

b0

Vorzeichenbehaftetes Byte-Bitmuster für einen Koeffizienten von −128..127.

b1

Vorzeichenbehaftetes Byte-Bitmuster für einen Koeffizienten von −128..127.

b2

Vorzeichenbehaftetes 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

c
// Compare literal coefficients with values read from RAM.
result[8]=__sdot3_q8_8(-513,769,-1025,-64,127,-3);
v0=-513;v1=769;v2=-1025;v3=-64;v4=127;v5=-3;
result[9]=__sdot3_q8_8(v0,v1,v2,v3,v4,v5);
// Reverse coefficient signs and verify negative products.
result[10]=__sdot3_q8_8(-513,769,-1025,64,-127,3);
v0=-513;v1=769;v2=-1025;v3=64;v4=-127;v5=3;
result[11]=__sdot3_q8_8(v0,v1,v2,v3,v4,v5);

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.

__sdot3_q8_8: DMG
DMG
__sdot3_q8_8: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:11767

kitaqgb/kitaqgb/CodeGenerator.cs:11767

csharp
if (funcName == "__sdot3_q8_8")
            {
                if (args.Length != 6) Error(expr, "__sdot3_q8_8 requires 6 arguments");
                LastCallReturnsHL = true;

                // x*ax
                EmitDotTermToHL(args[0], args[3], signed: true, q1_7: false, labelPrefix: "__sdot3_x");
                EmitAsm("PUSH_HL");

                // y*ay
                EmitDotTermToHL(args[1], args[4], signed: true, q1_7: false, labelPrefix: "__sdot3_y");
                EmitAsm("POP_DE");
                EmitAsm("ADD_HL_DE");
                EmitAsm("PUSH_HL");

                // z*az
                EmitDotTermToHL(args[2], args[5], signed: true, q1_7: false, labelPrefix: "__sdot3_z");
                EmitAsm("POP_DE");
                EmitAsm("ADD_HL_DE");
                return;
            }
__settile_fast

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

c
__settile_fast(x, y, tile);

Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.

Parameter

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

tile

Tile-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

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

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

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.

text
A
__settile_fast: DMG
DMG
__settile_fast: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settile_fast.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settile_fast.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:9321

csharp
{
                if (args.Length != 3) Error(expr, "__settile_unsafe requires 3 arguments");

                // Evaluate args left-to-right (matches normal call semantics)
                // Preserve x,y while evaluating tile.
                CompileIntoA(args[0]);
                EmitAsm("LD_L_A");
                EmitAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate));
                EmitAsm("PUSH_HL"); // x

                CompileIntoA(args[1]);
                EmitAsm("LD_L_A");
                EmitAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate));
                EmitAsm("PUSH_HL"); // y

                CompileIntoA(args[2]);
                EmitAsm("LD_C_A"); // tile

                // Restore y -> A, x -> B
                EmitAsm("POP_HL");
                EmitAsm("LD_A_L");
                EmitAsm("LD_D_A"); // y temp

                EmitAsm("POP_HL");
                EmitAsm("LD_A_L");
                EmitAsm("LD_B_A"); // x

                EmitAsm("LD_A_D"); // y

                UsedSetTileFastCore = true;
                EmitAsm("CALL", new AsmOperand("__settile_fast_core", AddressMode.Absolute));
                return;
            }

            // 2.6b __settile_bulk(dest_u16, src_u16, count_u8)
            // v9: bulk tile-map copy (LCD ON safe with wait+DI/EI inside core).
            if (funcName == "__settile_bulk")
            {
                if (args.Length != 3) Error(expr, "__settile_bulk requires 3 arguments");

                Expr bulkCountExpr = FoldConstants(args[2]);

                // dest
__settilebg16_buf

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

c
__settilebg16_buf(buf, x, y, tile);

Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.

Parameter

buf

RAM-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 sind 0,1,32,33.

x, y

Koordinaten 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.

tile

Tile-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

c
// __settilebg16_buf
// Write a 16×16-pixel cell into a RAM tile-map buffer: the four 8×8 tile numbers are
// `tile,tile+1` on top and `tile+2,tile+3` below. Values wrap at 256. This does not
// transfer anything to VRAM.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
u8 example_tiles[1024];
u8 example_attrs[1024];
void __memset(void* dest, u8 value, u16 len);
void __settilebg16_buf(u8* buf, u8 x, u8 y, u8 tile);
void __settilebg16cgb_buf(u8* tilebuf, u8* attrbuf, u8 x, u8 y, u8 tile, u8 attr);
void __settilebg16_flush(const u8* buf, u8 y, u8 x, u8 count);
void __settilebg16cgb_flush(const u8* tilebuf, const u8* attrbuf, u8 y, u8 x, u8 count);

void main() {
    u8 failures;
    failures = 0;
    tile_color_example_begin();
    __memset(example_tiles, 0, 1024);
    __memset(example_attrs, 0, 1024);
    __settilebg16_buf(example_tiles, 1, 1, 'A');
    // The buffer operation must not write to VRAM yet.
    if (tile_example_read(0x9C42, 0) != 0) { failures++; }
    // Transfer the prepared cell so its four characters can be seen.
    __settilebg16_flush(example_tiles, 1, 2, 2);
    if (tile_example_read(0x9C42, 0) != 'A') { failures++; }
    if (tile_example_read(0x9C43, 0) != 'B') { failures++; }
    if (tile_example_read(0x9C62, 0) != 'C') { failures++; }
    if (tile_example_read(0x9C63, 0) != 'D') { failures++; }
    if (example_tiles[66] != 'A') { failures++; }
    if (example_tiles[99] != 'D') { failures++; }
    if (tile_example_read(0x9C44, 0) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

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.

text
AB
CD
__settilebg16_buf: DMG
DMG
__settilebg16_buf: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilebg16_buf.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilebg16_buf.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:10564

csharp
{
                if (args.Length != 4) Error(expr, "__settilebg16_buf requires 4 arguments");
                EmitTile16BufferedWriteIntrinsic(args[0], args[1], args[2], args[3], incrementQuad: true, labelPrefix: "settilebg16buf");
                return;
            }

            // Update tile and attribute RAM buffers separately, repeating the attribute across the cell.
            if (funcName == "__settilebg16cgb_buf")
            {
                if (args.Length != 6) Error(expr, "__settilebg16cgb_buf requires 6 arguments");
                EmitTile16BufferedWriteIntrinsic(args[0], args[2], args[3], args[4], incrementQuad: true, labelPrefix: "settilebg16cgbbuf_tile");
                EmitTile16BufferedWriteIntrinsic(args[1], args[2], args[3], args[5], incrementQuad: false, labelPrefix: "settilebg16cgbbuf_attr");
                return;
            }

            // Flush both tile rows belonging to the requested buffered cell row.
            if (funcName == "__settilebg16_flush")
            {
                if (args.Length != 4) Error(expr, "__settilebg16_flush requires 4 arguments");
                EmitTile16FlushRowsIntrinsic(args[0], args[1], args[2], args[3], cgbAttr: false, labelPrefix: "settilebg16flush");
                return;
            }

            // Flush the tile buffer first and copy attributes only when CGB hardware is available.
            if (funcName == "__settilebg16cgb_flush")
            {
                if (args.Length != 5) Error(expr, "__settilebg16cgb_flush requires 5 arguments");
                EmitTile16FlushRowsIn
__settilebg16_flush

Ü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

c
__settilebg16_flush(buf, y, x, count);

Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.

Parameter

buf

Lesbarer 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, count

Die Reihenfolge lautet y,x,count. y bezeichnet eine 16-Pixel-Zellzeile (0–15), x und count zählen 8-Pixel-Tiles. Es muss x+count <= 32 gelten. Für eine Zelle bei (2,3) verwenden Sie y=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

c
// __settilebg16_flush
// Transfer two adjacent tile rows from the RAM buffer to the LCDC-selected BG map. The
// source offset is `64*y + x`; `count` bytes are copied from that row and from the row
// 32 bytes below it. Waits for writable VRAM when necessary. The LCD-on path uses
// interrupt masking and can enable interrupts afterward; it is not a promise to preserve
// the caller's interrupt state or to finish within one VBlank.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
u8 example_tiles[1024];
u8 example_attrs[1024];
void __memset(void* dest, u8 value, u16 len);
void __settilebg16_buf(u8* buf, u8 x, u8 y, u8 tile);
void __settilebg16cgb_buf(u8* tilebuf, u8* attrbuf, u8 x, u8 y, u8 tile, u8 attr);
void __settilebg16_flush(const u8* buf, u8 y, u8 x, u8 count);
void __settilebg16cgb_flush(const u8* tilebuf, const u8* attrbuf, u8 y, u8 x, u8 count);

void main() {
    u8 failures;
    failures = 0;
    tile_color_example_begin();
    __memset(example_tiles, 0, 1024);
    __memset(example_attrs, 0, 1024);
    example_tiles[66]='A'; example_tiles[67]='B';
    example_tiles[98]='C'; example_tiles[99]='D';
    __settilebg16_flush(example_tiles, 1, 2, 2);
    if (tile_example_read(0x9C42, 0) != 'A') { failures++; }
    if (tile_example_read(0x9C43, 0) != 'B') { failures++; }
    if (tile_example_read(0x9C62, 0) != 'C') { failures++; }
    if (tile_example_read(0x9C63, 0) != 'D') { failures++; }
    if (example_tiles[66] != 'A') { failures++; }
    if (example_tiles[99] != 'D') { failures++; }
    if (tile_example_read(0x9C44, 0) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

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.

text
AB
CD
__settilebg16_flush: DMG
DMG
__settilebg16_flush: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilebg16_flush.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilebg16_flush.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:10581

csharp
{
                if (args.Length != 4) Error(expr, "__settilebg16_flush requires 4 arguments");
                EmitTile16FlushRowsIntrinsic(args[0], args[1], args[2], args[3], cgbAttr: false, labelPrefix: "settilebg16flush");
                return;
            }

            // Flush the tile buffer first and copy attributes only when CGB hardware is available.
            if (funcName == "__settilebg16cgb_flush")
            {
                if (args.Length != 5) Error(expr, "__settilebg16cgb_flush requires 5 arguments");
                EmitTile16FlushRowsIntrinsic(args[0], args[2], args[3], args[4], cgbAttr: false, labelPrefix: "settilebg16cgbflush_tile");
                if (IsKnownCgbRuntimeTrue())
                {
                    EmitSetVbkUnchecked(1);
                    EmitTile16FlushRowsIntrinsic(args[1], args[2], args[3], args[4], cgbAttr: true, labelPrefix: "settilebg16cgbflush_attr");
                }
                else
                {
                    AsmOperand skip = MakeUniqueLabel("settilebg16cgbflush_skip");
                    AsmOperand done = MakeUniqueLabel("settilebg16cgbflush_done");
                    EmitRuntimeCgbCheckCall();
                    EmitAsm("OR_A");
                    // Two unrolled row copies can exceed the relative-branch range.
                    EmitAsm("JP_Z", skip);
                    EmitSetVbkUnchecked(1);
                    EmitTile16FlushRowsIntrinsic(args[1], args[2], args[3], args[4], cgbAttr: true, labelPrefix: "settilebg16cgbflush_attr");
                    EmitAsm("JR", done);
__settilebg16cgb_buf

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

c
__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, attrbuf

RAM-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 sind 0,1,32,33.

x, y

Koordinaten 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.

tile

Tile-Nummer von 0 bis 255. LCDC-Bit 4 bestimmt, wie sie die Tile-Bilddaten adressiert. Sie ist keine VRAM-Byteadresse.

attr

CGB-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

c
// __settilebg16cgb_buf
// Write a 16×16-pixel cell into a RAM tile-map buffer: the four 8×8 tile numbers are
// `tile,tile+1` on top and `tile+2,tile+3` below. Values wrap at 256. This does not
// transfer anything to VRAM. Also write the same `attr` byte to all four corresponding
// cells in the separate attribute buffer. Both buffers are ordinary RAM, so this
// preparation also runs on DMG.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
u8 example_tiles[1024];
u8 example_attrs[1024];
void __memset(void* dest, u8 value, u16 len);
void __settilebg16_buf(u8* buf, u8 x, u8 y, u8 tile);
void __settilebg16cgb_buf(u8* tilebuf, u8* attrbuf, u8 x, u8 y, u8 tile, u8 attr);
void __settilebg16_flush(const u8* buf, u8 y, u8 x, u8 count);
void __settilebg16cgb_flush(const u8* tilebuf, const u8* attrbuf, u8 y, u8 x, u8 count);

void main() {
    u8 failures;
    failures = 0;
    tile_example_begin();
    __memset(example_tiles, 0, 1024);
    __memset(example_attrs, 0, 1024);
    __settilebg16cgb_buf(example_tiles, example_attrs, 1, 1, 'A', 2);
    // The buffer operation must not write to VRAM yet.
    if (tile_example_read(0x9C42, 0) != 0) { failures++; }
    // Transfer the prepared cell so its four characters can be seen.
    __settilebg16cgb_flush(example_tiles, example_attrs, 1, 2, 2);
    if (tile_example_read(0x9C42, 0) != 'A') { failures++; }
    if (tile_example_read(0x9C43, 0) != 'B') { failures++; }
    if (tile_example_read(0x9C62, 0) != 'C') { failures++; }
    if (tile_example_read(0x9C63, 0) != 'D') { failures++; }
    if (example_tiles[66] != 'A') { failures++; }
    if (example_tiles[99] != 'D') { failures++; }
    if (tile_example_read(0x9C44, 0) != 0) { failures++; }
    if (example_attrs[66] != 2) { failures++; }
    if (example_attrs[99] != 2) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9C42, 1) != 2) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9C63, 1) != 2) { failures++; }
    tile_example_finish(failures, 1);
}

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.

text
AB
CD
__settilebg16cgb_buf: DMG
DMG
__settilebg16cgb_buf: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilebg16cgb_buf.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilebg16cgb_buf.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:10572

csharp
{
                if (args.Length != 6) Error(expr, "__settilebg16cgb_buf requires 6 arguments");
                EmitTile16BufferedWriteIntrinsic(args[0], args[2], args[3], args[4], incrementQuad: true, labelPrefix: "settilebg16cgbbuf_tile");
                EmitTile16BufferedWriteIntrinsic(args[1], args[2], args[3], args[5], incrementQuad: false, labelPrefix: "settilebg16cgbbuf_attr");
                return;
            }

            // Flush both tile rows belonging to the requested buffered cell row.
            if (funcName == "__settilebg16_flush")
            {
                if (args.Length != 4) Error(expr, "__settilebg16_flush requires 4 arguments");
                EmitTile16FlushRowsIntrinsic(args[0], args[1], args[2], args[3], cgbAttr: false, labelPrefix: "settilebg16flush");
                return;
            }

            // Flush the tile buffer first and copy attributes only when CGB hardware is available.
            if (funcName == "__settilebg16cgb_flush")
            {
                if (args.Length != 5) Error(expr, "__settilebg16cgb_flush requires 5 arguments");
                EmitTile16FlushRowsIntrinsic(args[0], args[2], args[3], args[4], cgbAttr: false, labelPrefix: "settilebg16cgbflush_tile");
                if (IsKnownCgbRuntimeTrue())
                {
                    EmitSetVbkUnchecked(1);
                    EmitTile16FlushRowsIntrinsic(args[1], args[2], args[3], args[4], cgbAttr: true, labelPrefix: "settilebg16cgbflush_attr");
                }
                else
                {
                    AsmOperand ski
__settilebg16cgb_flush

Ü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

c
__settilebg16cgb_flush(tilebuf, attrbuf, y, x, count);

Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.

Parameter

tilebuf, attrbuf

Lesbarer 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, count

Die Reihenfolge lautet y,x,count. y bezeichnet eine 16-Pixel-Zellzeile (0–15), x und count zählen 8-Pixel-Tiles. Es muss x+count <= 32 gelten. Für eine Zelle bei (2,3) verwenden Sie y=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

c
// __settilebg16cgb_flush
// Transfer two adjacent tile rows from the RAM buffer to the LCDC-selected BG map. The
// source offset is `64*y + x`; `count` bytes are copied from that row and from the row
// 32 bytes below it. Write a tile number and its CGB attribute. On DMG, only the tile-
// number write is performed. On CGB, enter with VRAM bank 0 selected; attributes use
// bank 1 and the call finishes with `VBK=0`.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_example.h"
u8 example_tiles[1024];
u8 example_attrs[1024];
void __memset(void* dest, u8 value, u16 len);
void __settilebg16_buf(u8* buf, u8 x, u8 y, u8 tile);
void __settilebg16cgb_buf(u8* tilebuf, u8* attrbuf, u8 x, u8 y, u8 tile, u8 attr);
void __settilebg16_flush(const u8* buf, u8 y, u8 x, u8 count);
void __settilebg16cgb_flush(const u8* tilebuf, const u8* attrbuf, u8 y, u8 x, u8 count);

void main() {
    u8 failures;
    failures = 0;
    tile_example_begin();
    __memset(example_tiles, 0, 1024);
    __memset(example_attrs, 0, 1024);
    example_tiles[66]='A'; example_tiles[67]='B';
    example_tiles[98]='C'; example_tiles[99]='D';
    example_attrs[66]=2; example_attrs[67]=2;
    example_attrs[98]=2; example_attrs[99]=2;
    __settilebg16cgb_flush(example_tiles, example_attrs, 1, 2, 2);
    if (tile_example_read(0x9C42, 0) != 'A') { failures++; }
    if (tile_example_read(0x9C43, 0) != 'B') { failures++; }
    if (tile_example_read(0x9C62, 0) != 'C') { failures++; }
    if (tile_example_read(0x9C63, 0) != 'D') { failures++; }
    if (example_tiles[66] != 'A') { failures++; }
    if (example_tiles[99] != 'D') { failures++; }
    if (tile_example_read(0x9C44, 0) != 0) { failures++; }
    if (example_attrs[66] != 2) { failures++; }
    if (example_attrs[99] != 2) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9C42, 1) != 2) { failures++; }
    if (__cgb_is_cgb() && tile_example_read(0x9C63, 1) != 2) { failures++; }
    tile_example_finish(failures, 1);
}

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.

text
AB
CD
__settilebg16cgb_flush: DMG
DMG
__settilebg16cgb_flush: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilebg16cgb_flush.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilebg16cgb_flush.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:10589

csharp
{
                if (args.Length != 5) Error(expr, "__settilebg16cgb_flush requires 5 arguments");
                EmitTile16FlushRowsIntrinsic(args[0], args[2], args[3], args[4], cgbAttr: false, labelPrefix: "settilebg16cgbflush_tile");
                if (IsKnownCgbRuntimeTrue())
                {
                    EmitSetVbkUnchecked(1);
                    EmitTile16FlushRowsIntrinsic(args[1], args[2], args[3], args[4], cgbAttr: true, labelPrefix: "settilebg16cgbflush_attr");
                }
                else
                {
                    AsmOperand skip = MakeUniqueLabel("settilebg16cgbflush_skip");
                    AsmOperand done = MakeUniqueLabel("settilebg16cgbflush_done");
                    EmitRuntimeCgbCheckCall();
                    EmitAsm("OR_A");
                    // Two unrolled row copies can exceed the relative-branch range.
                    EmitAsm("JP_Z", skip);
                    EmitSetVbkUnchecked(1);
                    EmitTile16FlushRowsIntrinsic(args[1], args[2], args[3], args[4], cgbAttr: true, labelPrefix: "settilebg16cgbflush_attr");
                    EmitAsm("JR", done);
                    EmitLabel(skip);
                    EmitLabel(done);
                }
                return;
            }

            // 2.14 __getbgmapbase() -> u16 in HL
// Returns the BG tilemap base address selected by LCDC bit3:
// bit3=0 => 0x9800, bit3=1 => 0x9C00
// Treated as a compiler intrinsic (no C definition required).
if (funcName == "__getbgmapbase")
{
    if (args.Length != 0) Error(expr, "__getb
__smac16

Berechnet eine vorzeichenbehaftete Multiplikation mit Akkumulation.

Syntax

c
u16 __smac16(u16 acc, u16 a, u8 b);

Parameter

acc

Zu addierender 16-Bit-Akkumulator.

a

Vorzeichenbehaftetes 16-Bit-Bitmuster für −32768..32767.

b

Vorzeichenbehaftetes 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

c
// Compare literal coefficients with values read from RAM.
result[26]=__smac16(65500,-513,-64);
v0=65500;v1=-513;v2=-64;
result[27]=__smac16(v0,v1,v2);
// Reverse coefficient signs and verify negative products.
result[28]=__smac16(65500,-513,64);
v0=65500;v1=-513;v2=64;
result[29]=__smac16(v0,v1,v2);

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.

__smac16: DMG
DMG
__smac16: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:11516

kitaqgb/kitaqgb/CodeGenerator.cs:11516

csharp
if (funcName == "__smac16")
            {
                if (args.Length != 3) Error(expr, "__smac16 requires 3 arguments");

                // mul = (a*b)>>8 (signed)
                EmitDotTermToHL(args[1], args[2], signed: true, q1_7: false, labelPrefix: "__smac16_mul");

                // acc + mul
                EmitAsm("PUSH_HL");
                CompileIntoHL(args[0]);
                EmitAsm("POP_DE");
                EmitAsm("ADD_HL_DE");
                LastCallReturnsHL = true;
                return;
            }
__smac16_q1_7

Berechnet eine vorzeichenbehaftete Multiplikation mit Akkumulation.

Syntax

c
u16 __smac16_q1_7(u16 acc, u16 a, u8 b);

Parameter

acc

Zu addierender 16-Bit-Akkumulator.

a

Vorzeichenbehaftetes 16-Bit-Bitmuster für −32768..32767.

b

Vorzeichenbehaftetes 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

c
// Compare literal coefficients with values read from RAM.
result[0]=__smac16_q1_7(65500,-513,-64);
v0=65500;v1=-513;v2=-64;
result[1]=__smac16_q1_7(v0,v1,v2);
// Reverse coefficient signs and verify negative products.
result[2]=__smac16_q1_7(65500,-513,64);
v0=65500;v1=-513;v2=64;
result[3]=__smac16_q1_7(v0,v1,v2);

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.

__smac16_q1_7: DMG
DMG
__smac16_q1_7: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:11533

kitaqgb/kitaqgb/CodeGenerator.cs:11533

csharp
if (funcName == "__smac16_q1_7")
            {
                if (args.Length != 3) Error(expr, "__smac16_q1_7 requires 3 arguments");

                // mul = (a*b)>>7 (approx via >>8 then *2)
                EmitDotTermToHL(args[1], args[2], signed: true, q1_7: true, labelPrefix: "__smac16_q1_7_mul");

                // acc + mul
                EmitAsm("PUSH_HL");
                CompileIntoHL(args[0]);
                EmitAsm("POP_DE");
                EmitAsm("ADD_HL_DE");
                LastCallReturnsHL = true;
                return;
            }
__smul16x8_q1_7

Berechnet eine vorzeichenbehaftete Multiplikation.

Syntax

c
u16 __smul16x8_q1_7(u16 a, u8 b);

Parameter

a

Vorzeichenbehaftetes 16-Bit-Bitmuster für −32768..32767.

b

Vorzeichenbehaftetes 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

c
// Compare literal coefficients with values read from RAM.
result[12]=__smul16x8_q1_7(-513,-64);
v0=-513;v1=-64;
result[13]=__smul16x8_q1_7(v0,v1);
// Reverse coefficient signs and verify negative products.
result[14]=__smul16x8_q1_7(-513,64);
v0=-513;v1=64;
result[15]=__smul16x8_q1_7(v0,v1);

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.

__smul16x8_q1_7: DMG
DMG
__smul16x8_q1_7: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_math.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_math.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:11490

kitaqgb/kitaqgb/CodeGenerator.cs:11490

csharp
if (funcName == "__smul16x8_q1_7")
            {
                if (args.Length != 2) Error(expr, "__smul16x8_q1_7 requires 2 arguments");

                EmitDotTermToHL(args[0], args[1], signed: true, q1_7: true, labelPrefix: "__smul16x8_q1_7");
                LastCallReturnsHL = true;
                return;
            }
__sram_read8

Liest ein SRAM-Byte an einer CPU-Adresse.

Syntax

c
u8 __sram_read8(u16 address);

Parameter

address

CPU-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

c
*((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.

__sram_read8: DMG
DMG
__sram_read8: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_save.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_save.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=8k
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:8825

kitaqgb/kitaqgb/CodeGenerator.cs:8825

csharp
if (funcName == "__sram_read8")
            {
                if (args.Length != 1) Error(expr, "__sram_read8(addr) expects 1 argument");

                CompileIntoHL(args[0]);
                EmitAsm("PUSH_HL");

                EmitAsm("LD_A_IMM", new AsmOperand(0x0A, AddressMode.Immediate));
                EmitAsm("LD_MEM_A", new AsmOperand(0x0000, AddressMode.Absolute));

                EmitAsm("POP_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("LD_B_A");

                EmitAsm("LD_A_IMM", new AsmOperand(0x00, AddressMode.Immediate));
                EmitAsm("LD_MEM_A", new AsmOperand(0x0000, AddressMode.Absolute));
                EmitAsm("LD_A_B");
                return;
            }
__sram_write8

Schreibt ein SRAM-Byte an eine CPU-Adresse.

Syntax

c
void __sram_write8(u16 address, u8 value);

Parameter

address

CPU-Adresse im Bereich $A000..$BFFF.

value

Byte-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

c
__sram_write8(0xA006,8);result[20]=*((u8*)0xA006);result[13]=save_exists(0);readback[0]=77;result[14]=save_read(0,readback,3);result[15]=readback[0];

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.

__sram_write8: DMG
DMG
__sram_write8: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_save.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_save.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=8k
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:8846

kitaqgb/kitaqgb/CodeGenerator.cs:8846

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

                CompileIntoHL(args[0]);
                EmitAsm("PUSH_HL");
                CompileIntoA(args[1]);
                EmitAsm("LD_C_A");

                EmitAsm("LD_A_IMM", new AsmOperand(0x0A, AddressMode.Immediate));
                EmitAsm("LD_MEM_A", new AsmOperand(0x0000, AddressMode.Absolute));

                EmitAsm("POP_HL");
                EmitAsm("LD_A_C");
                EmitAsm("LD_HL_A");

                EmitAsm("LD_A_IMM", new AsmOperand(0x00, AddressMode.Immediate));
                EmitAsm("LD_MEM_A", new AsmOperand(0x0000, AddressMode.Absolute));
                return;
            }
__svbk_get

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

c
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

c
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.

__svbk_get: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_wram_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_wram_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb_only --no-cache --no-disasm
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:7387

csharp
{
                if (args.Length != 0) Error(expr, "__svbk_get() expects 0 arguments");
                if (!Program.IsCgbOnlyTargetRequested())
                    Error(expr, ErrorCode.BankedWramRequiresCgbOnly, "__svbk_get() requires #pragma rom_cgb cgb_only or --cgb=cgb_only");
                EmitReadNormalizedSvbkIntoA();
                return;
            }

            // Select a WRAM bank and return the prior normalized bank; diagnose out-of-range constant requests.
            if (funcName == "__svbk_set")
            {
                if (args.Length != 1) Error(expr, "__svbk_set(bank_u8) expects 1 argument");
                if (!Program.IsCgbOnlyTargetRequested())
                    Error(expr, ErrorCode.BankedWramRequiresCgbOnly, "__svbk_set() requires #pragma rom_cgb cgb_only or --cgb=cgb_only");

                Expr bankExpr = FoldConstants(args[0]);
                if (bankExpr.Match(Tag.Integer, out int bankLiteral) && (bankLiteral < 1 || bankLiteral > 7))
                    Error(expr, ErrorCode.InvalidWramXBank, "__svbk_set() bank must be in range 1..7 (got {0})", bankLiteral);

                EmitReadNormalizedSvbkIntoA();
                EmitAsm("LD_B_A");
                CompileIntoA(bankExpr);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem));
                EmitAsm("LD_A_B");
                return;
            }

            // __cgb_safe_set_* (safe CGB-only register write wrappers)
            if (TryGetCgbSafeRegister(funcName, out int cgbOffset, out string cgbReg))
            {
__svbk_set

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

c
u8 __svbk_set(u8 bank);

Die Parameternamen erläutern die Compiler-Intrinsik. Sie stellen keinen zusätzlichen C-Prototyp dar.

Parameter

bank

Gewü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

c
previous_bank=__svbk_set(requested_bank);
if(previous_bank!=1 || __svbk_get()!=2) failures++;
*((u8*)0xD100)=1; // This byte belongs to bank 2.
previous_bank=__svbk_set(next_bank()); // Dynamic argument evaluates to 3.
if(previous_bank!=2 || __svbk_get()!=3) failures++;
*((u8*)0xD100)=2; // Same CPU address, different physical RAM.

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.

__svbk_set: --cgb=cgb_only / CGB
--cgb=cgb_only / CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\cgb_wram_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\cgb_wram_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb_only --no-cache --no-disasm
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:7397

csharp
{
                if (args.Length != 1) Error(expr, "__svbk_set(bank_u8) expects 1 argument");
                if (!Program.IsCgbOnlyTargetRequested())
                    Error(expr, ErrorCode.BankedWramRequiresCgbOnly, "__svbk_set() requires #pragma rom_cgb cgb_only or --cgb=cgb_only");

                Expr bankExpr = FoldConstants(args[0]);
                if (bankExpr.Match(Tag.Integer, out int bankLiteral) && (bankLiteral < 1 || bankLiteral > 7))
                    Error(expr, ErrorCode.InvalidWramXBank, "__svbk_set() bank must be in range 1..7 (got {0})", bankLiteral);

                EmitReadNormalizedSvbkIntoA();
                EmitAsm("LD_B_A");
                CompileIntoA(bankExpr);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem));
                EmitAsm("LD_A_B");
                return;
            }

            // __cgb_safe_set_* (safe CGB-only register write wrappers)
            if (TryGetCgbSafeRegister(funcName, out int cgbOffset, out string cgbReg))
            {
                if (args.Length != 1) Program.Error(funcName + "(value_u8) expects 1 argument");

                if (IsKnownCgbRuntimeTrue())
                {
                    CompileIntoA(args[0]);
                    if (cgbOffset == 0x4F)
                        EmitAsm("AND_IMM", new AsmOperand(0x01, AddressMode.Immediate));
                    EmitAsm("LDH_MEM_A", new AsmOperand(cgbOffset, AddressMode.HighMem));
                    CgbGuardedWriteCount++;
                    CgbGuardedRegisters.Add(cgbReg);
                    return;
__vram_memcpy_unsafe

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

c
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

dst

Adresse 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.

src

Zeiger 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.

len

Anzahl 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

c
// 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.

__vram_memcpy_unsafe: DMG
DMG
__vram_memcpy_unsafe: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/kitaqgb/CodeGenerator.cs:8104

csharp
{
                if (args.Length != 3) Error(expr, "__vram_memcpy_unsafe(dst, src, len) expects 3 arguments");

                CompileIntoHL(args[0]); EmitAsm("PUSH_HL"); // dst
                CompileIntoHL(args[1]); EmitAsm("PUSH_HL"); // src
                // Use the same count specialization without emitting LCD-mode waits.
                Expr vramCopyUnsafeLen = FoldConstants(args[2]);
                EmitLengthExprIntoBC(vramCopyUnsafeLen);
                EmitAsm("POP_DE"); // src
                EmitAsm("POP_HL"); // dst

                if (vramCopyUnsafeLen.Match(Tag.Integer, out int vramCopyUnsafeLenConst) &&
                    TryEmitVramMemcpyConstCount(false, vramCopyUnsafeLenConst, "vramcpyu_small"))
                    return;

                AsmOperand vcuLoop = MakeUniqueLabel("vramcpyu_loop");
                AsmOperand vcuDone = MakeUniqueLabel("vramcpyu_done");
                EmitLabel(vcuLoop);
                EmitAsm("LD_A_B");
                EmitAsm("OR_C");
                EmitAsm("JR_Z", vcuDone);
                EmitAsm("LD_A_DE");
                EmitAsm("LDI_HL_A");
                EmitAsm("INC_DE");
                EmitAsm("DEC_BC");
                EmitAsm("JR", vcuLoop);
                EmitLabel(vcuDone);
                return;
            }

            // __vram_memset(dst_ptr, value_u8, len_u16)
            // Safe VRAM fill: if LCD is on, each byte write is gated by STAT mode 0/1.
            if (funcName == "__vram_memset")
            {
                if (args.Length != 3) Error(expr, "__vram_memset(dst, value, l
__vram_memset_unsafe

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

c
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

dst

Adresse 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.

value

Ein 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.

len

Anzahl 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

c
__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.

__vram_memset_unsafe: DMG
DMG
__vram_memset_unsafe: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/kitaqgb/CodeGenerator.cs:8196

csharp
{
                if (args.Length != 3) Error(expr, "__vram_memset_unsafe(dst, value, len) expects 3 arguments");

                CompileIntoHL(args[0]); EmitAsm("PUSH_HL"); // dst
                CompileIntoA(args[1]); EmitAsm("LD_D_A"); // value
                // Specialize a constant unsafe fill or fall back to its unchecked byte loop.
                Expr vramSetUnsafeLen = FoldConstants(args[2]);
                EmitLengthExprIntoBC(vramSetUnsafeLen);
                EmitAsm("POP_HL"); // dst

                if (vramSetUnsafeLen.Match(Tag.Integer, out int vramSetUnsafeLenConst) &&
                    TryEmitVramMemsetConstCount(false, vramSetUnsafeLenConst, "vramsetu_small"))
                    return;

                AsmOperand vsuLoop = MakeUniqueLabel("vramsetu_loop");
                AsmOperand vsuDone = MakeUniqueLabel("vramsetu_done");
                EmitLabel(vsuLoop);
                EmitAsm("LD_A_B");
                EmitAsm("OR_C");
                EmitAsm("JR_Z", vsuDone);
                EmitAsm("LD_A_D");
                EmitAsm("LDI_HL_A");
                EmitAsm("DEC_BC");
                EmitAsm("JR", vsuLoop);
                EmitLabel(vsuDone);
                return;
            }

            // __far_memcpy(dst_ptr, bank_u8, src_ptr, len_u16)
            // Copies from switchable ROM bank via a bank0 helper and restores original bank.
            if (funcName == "__far_memcpy")
            {
                if (args.Length != 4) Error(expr, "__far_memcpy(dst, bank, src, len) expects 4 arguments");

                CompileIntoHL(
__wait_ly

Wartet bei laufendem LCD, bis LY der Zielzeile entspricht.

Syntax

c
void __wait_ly(u8 ly);

Parameter

ly

Rasterzeile 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

c
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.

__wait_ly: DMG
DMG
__wait_ly: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_display.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_display.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implementierung und Quelltext

kitaqgb/kitaqgb/CodeGenerator.cs:7483

kitaqgb/kitaqgb/CodeGenerator.cs:7483

csharp
if (funcName == "__wait_ly")
            {
                if (args.Length != 1) Error(expr, "__wait_ly(target) expects 1 argument");

                CompileIntoA(args[0]);
                EmitAsm("LD_B_A");

                AsmOperand wlyLoop = MakeUniqueLabel("waitly_loop");
                AsmOperand wlyDone = MakeUniqueLabel("waitly_done");

                // If LCD is off, LY is not meaningful; return.
                EmitAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem)); // LCDC
                EmitAsm("AND_IMM", new AsmOperand(0x80, AddressMode.Immediate));
                EmitAsm("JR_Z", wlyDone);

                EmitLabel(wlyLoop);
                EmitAsm("LDH_A_MEM", new AsmOperand(0x44, AddressMode.HighMem)); // LY
                EmitAsm("CP_B");
                EmitAsm("JR_NZ", wlyLoop);
                EmitLabel(wlyDone);
                return;
            }

physics2d.h — 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

c
s16 __smul16x8(s16 a, s8 b);

Parameter

a

Vorzeichenbehafteter 16-Bit-Wert, auf den der Skalierungsfaktor angewendet wird.

b

Vorzeichenbehafteter 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

c
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.

__smul16x8: DMG — 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.
DMG — 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.
__smul16x8: CGB — 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.
CGB — 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

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\physics_body2d.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\physics_body2d.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=128k --no-cache --no-disasm
Implementierung und Quelltext

kitaqgb/lib/physics2d.h:9

Deklarationshinweise im Original
Multiply a signed word by a signed byte; divide by 256, truncating toward zero.

kitaqgb/kitaqgb/CodeGenerator.cs:11478

csharp
if (funcName == "__smul16x8")
            {
                if (args.Length != 2) Error(expr, "__smul16x8 requires 2 arguments");

                EmitDotTermToHL(args[0], args[1], signed: true, q1_7: false, labelPrefix: "__smul16x8");
                LastCallReturnsHL = true;
                return;
            }

rpg.h — 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

c
void __bit_clear(u8* p, u16 bit_index);

Parameter

p

Zeiger 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_index

Bei null beginnender Bitindex als u16 (0–65535), kein Byte-Offset und keine Bitmaske. Index 0 wählt Maske 0x01 in Byte 0, Index 7 wählt 0x80 in Byte 0 und Index 8 wählt 0x01 in Byte 1. Allgemein ist der Byte-Offset index / 8 und die Maske 1 << (index % 8). Für ein N-Byte-Array müssen index < 8*N und 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

c
__bit_clear(bit_flags,2);
bit_clear_value=bit_flags[0]; // A6 & FB = A2, decimal 162.
__bit_clear(bit_flags,2);     // Clearing it twice leaves it clear.

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.

__bit_clear: DMG
DMG
__bit_clear: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\bit_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\bit_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung und Quelltext

kitaqgb/lib/rpg.h:187

kitaqgb/kitaqgb/CodeGenerator.cs:8582

csharp
if (funcName == "__bit_test" || funcName == "__bit_set" || funcName == "__bit_clear" || funcName == "__bit_toggle")
            {
                if (args.Length != 2) Error(expr, funcName + "(ptr, bit_index) expects 2 arguments");

                CompileIntoHL(args[0]); EmitAsm("PUSH_HL"); // ptr
                CompileIntoHL(args[1]); EmitAsm("PUSH_HL"); // bit_index

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("AND_IMM", new AsmOperand(7, AddressMode.Immediate));
                EmitAsm("LD_B_A"); // bit number 0..7

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_E_A");
                EmitAsm("INC_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("LD_D_A");
                EmitAsm("LD_H_D");
                EmitAsm("LD_L_E");
                EmitShiftRightLogicalHL(3);
                EmitAsm("PUSH_HL"); // byte offset

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

                EmitAsm("POP_HL");
                EmitAsm("ADD_HL_DE"); // HL = ptr + (bit_index >> 3)

                AsmOperand maskReady = MakeUniqueLabel("bit_mask_ready");
                AsmOperand maskLoop = MakeUniqueLabel("bit_mask_loop");
                EmitAsm("LD_A_B");
                EmitAsm("OR_A");
                EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
                EmitAsm("JR_Z", maskReady);
                EmitLabel(maskLoop);
                EmitAsm("ADD_A");
                EmitAsm("DEC_B");
                EmitAsm("JR_NZ", maskLoop);
                EmitLabel(maskReady);
                EmitAsm("LD_B_A"); // mask

                if (funcName == "__bit_test")
                {
                    AsmOperand retZero = MakeUniqueLabel("bit_test_zero");
                    AsmOperand done = MakeUniqueLabel("bit_test_done");
                    EmitAsm("LD_A_HL");
                    EmitAsm("AND_B");
                    EmitAsm("JR_Z", retZero);
                    EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
                    EmitAsm("JR", done);
                    EmitLabel(retZero);
                    EmitAsm("XOR_A");
                    EmitLabel(done);
                    EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                    return;
                }

                if (funcName == "__bit_set")
                {
                    EmitAsm("LD_A_HL");
                    EmitAsm("OR_B");
                    EmitAsm("LD_HL_A");
                    EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                    return;
                }

                if (funcName == "__bit_clear")
                {
                    EmitAsm("LD_A_B");
                    EmitAsm("CPL");
                    EmitAsm("LD_B_A");
                    EmitAsm("LD_A_HL");
                    EmitAsm("AND_B");
                    EmitAsm("LD_HL_A");
                    EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                    return;
                }

                EmitAsm("LD_A_HL");
                EmitAsm("XOR_B");
                EmitAsm("LD_HL_A");
                EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                return;
            }
__bit_set

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

c
void __bit_set(u8* p, u16 bit_index);

Parameter

p

Zeiger 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_index

Bei null beginnender Bitindex als u16 (0–65535), kein Byte-Offset und keine Bitmaske. Index 0 wählt Maske 0x01 in Byte 0, Index 7 wählt 0x80 in Byte 0 und Index 8 wählt 0x01 in Byte 1. Allgemein ist der Byte-Offset index / 8 und die Maske 1 << (index % 8). Für ein N-Byte-Array müssen index < 8*N und 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

c
__bit_set(bit_flags,1);
bit_set_value=bit_flags[0]; // A4 | 02 = A6, decimal 166.
__bit_set(bit_flags,1);     // Setting it twice leaves it set.

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.

__bit_set: DMG
DMG
__bit_set: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\bit_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\bit_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung und Quelltext

kitaqgb/lib/rpg.h:186

kitaqgb/kitaqgb/CodeGenerator.cs:8582

csharp
if (funcName == "__bit_test" || funcName == "__bit_set" || funcName == "__bit_clear" || funcName == "__bit_toggle")
            {
                if (args.Length != 2) Error(expr, funcName + "(ptr, bit_index) expects 2 arguments");

                CompileIntoHL(args[0]); EmitAsm("PUSH_HL"); // ptr
                CompileIntoHL(args[1]); EmitAsm("PUSH_HL"); // bit_index

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("AND_IMM", new AsmOperand(7, AddressMode.Immediate));
                EmitAsm("LD_B_A"); // bit number 0..7

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_E_A");
                EmitAsm("INC_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("LD_D_A");
                EmitAsm("LD_H_D");
                EmitAsm("LD_L_E");
                EmitShiftRightLogicalHL(3);
                EmitAsm("PUSH_HL"); // byte offset

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

                EmitAsm("POP_HL");
                EmitAsm("ADD_HL_DE"); // HL = ptr + (bit_index >> 3)

                AsmOperand maskReady = MakeUniqueLabel("bit_mask_ready");
                AsmOperand maskLoop = MakeUniqueLabel("bit_mask_loop");
                EmitAsm("LD_A_B");
                EmitAsm("OR_A");
                EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
                EmitAsm("JR_Z", maskReady);
                EmitLabel(maskLoop);
                EmitAsm("ADD_A");
                EmitAsm("DEC_B");
                EmitAsm("JR_NZ", maskLoop);
                EmitLabel(maskReady);
                EmitAsm("LD_B_A"); // mask

                if (funcName == "__bit_test")
                {
                    AsmOperand retZero = MakeUniqueLabel("bit_test_zero");
                    AsmOperand done = MakeUniqueLabel("bit_test_done");
                    EmitAsm("LD_A_HL");
                    EmitAsm("AND_B");
                    EmitAsm("JR_Z", retZero);
                    EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
                    EmitAsm("JR", done);
                    EmitLabel(retZero);
                    EmitAsm("XOR_A");
                    EmitLabel(done);
                    EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                    return;
                }

                if (funcName == "__bit_set")
                {
                    EmitAsm("LD_A_HL");
                    EmitAsm("OR_B");
                    EmitAsm("LD_HL_A");
                    EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                    return;
                }

                if (funcName == "__bit_clear")
                {
                    EmitAsm("LD_A_B");
                    EmitAsm("CPL");
                    EmitAsm("LD_B_A");
                    EmitAsm("LD_A_HL");
                    EmitAsm("AND_B");
                    EmitAsm("LD_HL_A");
                    EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                    return;
                }

                EmitAsm("LD_A_HL");
                EmitAsm("XOR_B");
                EmitAsm("LD_HL_A");
                EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                return;
            }
__bit_test

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

c
u8 __bit_test(const u8* p, u16 bit_index);

Parameter

p

Zeiger 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_index

Bei null beginnender Bitindex als u16 (0–65535), kein Byte-Offset und keine Bitmaske. Index 0 wählt Maske 0x01 in Byte 0, Index 7 wählt 0x80 in Byte 0 und Index 8 wählt 0x01 in Byte 1. Allgemein ist der Byte-Offset index / 8 und die Maske 1 << (index % 8). Für ein N-Byte-Array müssen index < 8*N und 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

c
bit_off=__bit_test(bit_flags,1); // Bit 1 of A4 is clear: zero.
bit_on=__bit_test(bit_flags,7);  // Bit 7 is set: GB returns 1; FC returns 128.
bit_is_on=0;
if(bit_on!=0)bit_is_on=1; // The same nonzero test works on both targets.

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.

__bit_test: DMG
DMG
__bit_test: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\bit_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\bit_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung und Quelltext

kitaqgb/lib/rpg.h:185

kitaqgb/kitaqgb/CodeGenerator.cs:8582

csharp
if (funcName == "__bit_test" || funcName == "__bit_set" || funcName == "__bit_clear" || funcName == "__bit_toggle")
            {
                if (args.Length != 2) Error(expr, funcName + "(ptr, bit_index) expects 2 arguments");

                CompileIntoHL(args[0]); EmitAsm("PUSH_HL"); // ptr
                CompileIntoHL(args[1]); EmitAsm("PUSH_HL"); // bit_index

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("AND_IMM", new AsmOperand(7, AddressMode.Immediate));
                EmitAsm("LD_B_A"); // bit number 0..7

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_E_A");
                EmitAsm("INC_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("LD_D_A");
                EmitAsm("LD_H_D");
                EmitAsm("LD_L_E");
                EmitShiftRightLogicalHL(3);
                EmitAsm("PUSH_HL"); // byte offset

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

                EmitAsm("POP_HL");
                EmitAsm("ADD_HL_DE"); // HL = ptr + (bit_index >> 3)

                AsmOperand maskReady = MakeUniqueLabel("bit_mask_ready");
                AsmOperand maskLoop = MakeUniqueLabel("bit_mask_loop");
                EmitAsm("LD_A_B");
                EmitAsm("OR_A");
                EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
                EmitAsm("JR_Z", maskReady);
                EmitLabel(maskLoop);
                EmitAsm("ADD_A");
                EmitAsm("DEC_B");
                EmitAsm("JR_NZ", maskLoop);
                EmitLabel(maskReady);
                EmitAsm("LD_B_A"); // mask

                if (funcName == "__bit_test")
                {
                    AsmOperand retZero = MakeUniqueLabel("bit_test_zero");
                    AsmOperand done = MakeUniqueLabel("bit_test_done");
                    EmitAsm("LD_A_HL");
                    EmitAsm("AND_B");
                    EmitAsm("JR_Z", retZero);
                    EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
                    EmitAsm("JR", done);
                    EmitLabel(retZero);
                    EmitAsm("XOR_A");
                    EmitLabel(done);
                    EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                    return;
                }

                if (funcName == "__bit_set")
                {
                    EmitAsm("LD_A_HL");
                    EmitAsm("OR_B");
                    EmitAsm("LD_HL_A");
                    EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                    return;
                }

                if (funcName == "__bit_clear")
                {
                    EmitAsm("LD_A_B");
                    EmitAsm("CPL");
                    EmitAsm("LD_B_A");
                    EmitAsm("LD_A_HL");
                    EmitAsm("AND_B");
                    EmitAsm("LD_HL_A");
                    EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                    return;
                }

                EmitAsm("LD_A_HL");
                EmitAsm("XOR_B");
                EmitAsm("LD_HL_A");
                EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                return;
            }
__bit_toggle

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

c
void __bit_toggle(u8* p, u16 bit_index);

Parameter

p

Zeiger 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_index

Bei null beginnender Bitindex als u16 (0–65535), kein Byte-Offset und keine Bitmaske. Index 0 wählt Maske 0x01 in Byte 0, Index 7 wählt 0x80 in Byte 0 und Index 8 wählt 0x01 in Byte 1. Allgemein ist der Byte-Offset index / 8 und die Maske 1 << (index % 8). Für ein N-Byte-Array müssen index < 8*N und 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

c
__bit_toggle(bit_flags,7);
bit_toggle_value=bit_flags[0]; // A2 ^ 80 = 22, decimal 34.
__bit_toggle(bit_flags,7);
bit_restore_value=bit_flags[0]; // Two toggles restore A2.

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.

__bit_toggle: DMG
DMG
__bit_toggle: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\bit_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\bit_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung und Quelltext

kitaqgb/lib/rpg.h:188

kitaqgb/kitaqgb/CodeGenerator.cs:8582

csharp
if (funcName == "__bit_test" || funcName == "__bit_set" || funcName == "__bit_clear" || funcName == "__bit_toggle")
            {
                if (args.Length != 2) Error(expr, funcName + "(ptr, bit_index) expects 2 arguments");

                CompileIntoHL(args[0]); EmitAsm("PUSH_HL"); // ptr
                CompileIntoHL(args[1]); EmitAsm("PUSH_HL"); // bit_index

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("AND_IMM", new AsmOperand(7, AddressMode.Immediate));
                EmitAsm("LD_B_A"); // bit number 0..7

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_E_A");
                EmitAsm("INC_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("LD_D_A");
                EmitAsm("LD_H_D");
                EmitAsm("LD_L_E");
                EmitShiftRightLogicalHL(3);
                EmitAsm("PUSH_HL"); // byte offset

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

                EmitAsm("POP_HL");
                EmitAsm("ADD_HL_DE"); // HL = ptr + (bit_index >> 3)

                AsmOperand maskReady = MakeUniqueLabel("bit_mask_ready");
                AsmOperand maskLoop = MakeUniqueLabel("bit_mask_loop");
                EmitAsm("LD_A_B");
                EmitAsm("OR_A");
                EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
                EmitAsm("JR_Z", maskReady);
                EmitLabel(maskLoop);
                EmitAsm("ADD_A");
                EmitAsm("DEC_B");
                EmitAsm("JR_NZ", maskLoop);
                EmitLabel(maskReady);
                EmitAsm("LD_B_A"); // mask

                if (funcName == "__bit_test")
                {
                    AsmOperand retZero = MakeUniqueLabel("bit_test_zero");
                    AsmOperand done = MakeUniqueLabel("bit_test_done");
                    EmitAsm("LD_A_HL");
                    EmitAsm("AND_B");
                    EmitAsm("JR_Z", retZero);
                    EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
                    EmitAsm("JR", done);
                    EmitLabel(retZero);
                    EmitAsm("XOR_A");
                    EmitLabel(done);
                    EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                    return;
                }

                if (funcName == "__bit_set")
                {
                    EmitAsm("LD_A_HL");
                    EmitAsm("OR_B");
                    EmitAsm("LD_HL_A");
                    EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                    return;
                }

                if (funcName == "__bit_clear")
                {
                    EmitAsm("LD_A_B");
                    EmitAsm("CPL");
                    EmitAsm("LD_B_A");
                    EmitAsm("LD_A_HL");
                    EmitAsm("AND_B");
                    EmitAsm("LD_HL_A");
                    EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                    return;
                }

                EmitAsm("LD_A_HL");
                EmitAsm("XOR_B");
                EmitAsm("LD_HL_A");
                EmitAsm("ADD_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                return;
            }
__copy16

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

c
void __copy16(void* dst, const void* src);

Parameter

dst

Zeiger auf das erste beschreibbare Zielbyte. Stellen Sie genügend zusammenhängenden Speicher für die gesamte Operation bereit; ein Puffer wird nicht automatisch angelegt.

src

Zeiger 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

c
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.

__copy16: DMG
DMG
__copy16: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\memory_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\memory_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung und Quelltext

kitaqgb/lib/rpg.h:177

kitaqgb/kitaqgb/CodeGenerator.cs:8247

csharp
if (funcName == "__copy16" || funcName == "__copy32")
            {
                if (args.Length != 2) Error(expr, funcName + "(dst, src) expects 2 arguments");

                int copyLen = (funcName == "__copy16") ? 16 : 32;
                CompileIntoHL(args[0]); EmitAsm("PUSH_HL"); // dst
                CompileIntoHL(args[1]); EmitAsm("PUSH_HL"); // src
                EmitAsm("LD_BC_IMM", new AsmOperand(copyLen, AddressMode.Immediate16));
                EmitAsm("POP_DE"); // src
                EmitAsm("POP_HL"); // dst
                EmitRamMemcpyLoop(funcName.TrimStart('_'));
                return;
            }

void EmitRamMemcpyLoop(string labelPrefix)
    {
        AsmOperand loop = MakeUniqueLabel(labelPrefix + "_loop");
        AsmOperand done = MakeUniqueLabel(labelPrefix + "_done");

        EmitLabel(loop);
        EmitAsm("LD_A_B");
        EmitAsm("OR_C");
        EmitAsm("JR_Z", done);
        EmitAsm("LD_A_DE");
        EmitAsm("LDI_HL_A");
        EmitAsm("INC_DE");
        EmitAsm("DEC_BC");
        EmitAsm("JR", loop);
        EmitLabel(done);
    }

// kitaqgb/kitaqgb/Lowerer.cs:2145
if (expr.MatchAny(Tag.Call, out callFunc, out callArgs))
        {
            string knownCallName;
            if (callFunc != null && callFunc.Match(Tag.Name, out knownCallName) &&
                knownCallName == "__cgb_is_cgb" &&
                callArgs != null && callArgs.Length == 0 &&
                // A resolved target configuration can replace the zero-argument CGB query with a literal.
                Program.TryGetKnownCgbRuntimeValue(out int knownCgbValue))
            {
                return Expr.Make(Tag.Integer, knownCgbValue).WithSource(expr.Source);
            }

            Expr newFunc = LowerExpr(ctx, callFunc, false, prefix);

            // Optimization (post-A3): when we must spill a non-atomic argument into a temp,
            // prefer a u8 temp if the *parameter type* is u8-like.
            // This preserves semantics (implicit truncation to u8) while avoiding needless
            // u16 temporaries created by integer promotions.
            CType[] paramTypes = null;
            string __fname = null;
            if (newFunc.Match(Tag.Name, out __fname))
            {
                paramTypes = ctx.FindFuncParams(__fname);
                if (paramTypes == null)
                {
                    CType fnTypeFromName;
                    if (TryGetCallableFunctionType(ctx.FindTypeOfName(__fname), out fnTypeFromName))
                        paramTypes = fnTypeFromName.ParamTypes;
                }
            }
            else
            {
                CType fnTypeFromExpr;
                if (TryGetCallableFunctionType(InferType(ctx, newFunc), out fnTypeFromExpr))
                    paramTypes = fnTypeFromExpr.ParamTypes;
            }

            Expr[] newArgs = new Expr[callArgs.Length];
            for (int i = 0; i < callArgs.Length; i++)
            {
                Expr a = LowerExpr(ctx, callArgs[i], allowExtract, prefix);
                bool preserveConstantArgIntrinsic = false;
                if (a.MatchAny(Tag.Call, out Expr argFunc, out Expr[] _argCallArgs) &&
                    argFunc.Match(Tag.Name, out string argFuncName))
                {
                    preserveConstantArgIntrinsic = (argFuncName == "__bankof" || argFuncName == "__cgb_is_cgb");
                }
                // Keep evaluable constants and special intrinsic/bank operands visible to code generation instead of spilling them.
                bool preserveCompileTimeConstantArg = ctx.Global.TryEvalConstInt(a, out _);
                bool preserveFarcallBankArg = (__fname == "__farcall" && i == 0);

                if (allowExtract && !preserveFarcallBankArg && !preserveConstantArgIntrinsic && !preserveCompileTimeConstantArg && !IsAtom(a))
                {
                    // If we know the callee's parameter type, use it to choose a smaller temp.
                    // Example: (u8)(row*32) passed to a u8 param should spill as u8, not u16.
                    CType spillAs = null;
                    if (paramTypes != null && i < paramTypes.Length)
                    {
                        CType pt = paramTypes[i];
                        if (IsU8Like(pt)) spillAs = CType.UInt8;
                    }
                    if (spillAs == null) spillAs = InferType(ctx, a);
                    if (!IsAggregateType(spillAs) && SizeOfType(spillAs) <= 2)
                        a = SpillToTemp(ctx, a, spillAs, prefix);
                }
                newArgs[i] = a;
            }

            // Lint: const discard for argument passing
            // Warn when passing a const-qualified pointer (or const array decay) to a non-const pointer parameter.
            // Suppress when the argument has an explicit cast, or inside __unsafe.
            if (!ctx.InUnsafe && paramTypes != null)
            {
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    CType pt = paramTypes[i];
                    if (pt == null) continue;
                    if (!IsPointerToNonConst(pt)) continue;
                    if (HasTopLevelCast(newArgs[i])) continue;

                    CType at = InferType(ctx, newArgs[i]);
                    if (at != null && (IsPointerToConst(at) || IsArrayOfConst(at)))
                    {
                        string fn = __fname ?? "<call>";
                        WarnConstDiscard(expr, $"argument {i} to '{fn}'");
                        break; // keep it quiet: one warning per call is enough
                    }
                }
            }

            if (paramTypes != null)
            {
                string fn = __fname ?? "<call>";
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    FilePosition argPos = newArgs[i].Source;
                    if (string.IsNullOrEmpty(argPos.Filename) || argPos.Filename == "<unknown>")
                        argPos = expr.Source;
                    WarnImplicitNarrowingIfNeeded(ctx, newArgs[i], InferType(ctx, newArgs[i]), paramTypes[i], argPos,
                        "argument " + i + " to '" + fn + "'");
                }
            }

            // Lint: __enum_strict argument passing.
            if (paramTypes != null)
            {
                string fn = __fname ?? "<call>";
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    CType pt = paramTypes[i];
                    if (pt == null) continue;

                    CType at = InferType(ctx, newArgs[i]);
                    if (at == null) continue;

                    if (HasTopLevelCast(newArgs[i])) continue;

                    if (IsStrictEnum(pt))
                    {
                        if (!IsSameEnum(pt, at))
                        {
                            Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
                                "argument {0} to '{1}' expects __enum_strict {2}, got {3}; cast explicitly to silence",
                                i, fn, pt.Show(), at.Show());
                        }
                    }
                    else if (IsPlainInteger(pt) && IsStrictEnum(at))
                    {
                        Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
                            "argument {0} to '{1}' passes __enum_strict {2} to integer parameter {3}; cast explicitly to silence",
                            i, fn, at.Show(), pt.Show());
                    }
                }
            }

            if (!ctx.InUnsafe)
                RestrictAliasLint(ctx, newFunc, newArgs, expr.Source);

            // __slice(ptr,len) helper: represent a "slice" (ptr+len) in the IR.
            // Semantics: evaluate both arguments, result type is the pointer/array (same as arg0).
            // Bounds checks are injected only with -Zcheck (see Program.CheckSliceBounds).
            // Note: keep the intrinsic name conservative to avoid collisions ("slice" is allowed only
            // when there is no declared function named "slice").
            if (newFunc.Match(Tag.Name, out string __sfname))
            {
                bool isSliceIntrinsic = (__sfname == "__slice") || (__sfname == "slice" && ctx.FindFuncParams("slice") == null);
                if (isSliceIntrinsic)
                {
                    if (newArgs.Length != 2)
                    {
                        Program.Error(expr.Source, ErrorCode.ParseError, "{0}: expected 2 arguments: __slice(ptr,len)", __sfname);
                    }
                    else
                    {
                        CType pty = InferType(ctx, newArgs[0]);
                        if (pty == null || !(pty.IsPointer || pty.IsArray))
                            Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: first argument must be a pointer or array, got: {1}", __sfname, (pty == null ? "<unknown>" : pty.Show()));

                        CType lty = InferType(ctx, newArgs[1]);
                        if (lty == null || !lty.IsInteger)
                            Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: second argument must be an integer length, got: {1}", __sfname, (lty == null ? "<unknown>" : lty.Show()));
                    }

                    return Expr.Make(Tag.Slice, newArgs[0], newArgs[1]).WithSource(expr.Source);
                }
            }

            return MakeCall(newFunc, newArgs).WithSource(expr.Source);
        }
__copy32

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

c
void __copy32(void* dst, const void* src);

Parameter

dst

Zeiger auf das erste beschreibbare Zielbyte. Stellen Sie genügend zusammenhängenden Speicher für die gesamte Operation bereit; ein Puffer wird nicht automatisch angelegt.

src

Zeiger 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

c
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.

__copy32: DMG
DMG
__copy32: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\memory_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\memory_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung und Quelltext

kitaqgb/lib/rpg.h:178

kitaqgb/kitaqgb/CodeGenerator.cs:8247

csharp
if (funcName == "__copy16" || funcName == "__copy32")
            {
                if (args.Length != 2) Error(expr, funcName + "(dst, src) expects 2 arguments");

                int copyLen = (funcName == "__copy16") ? 16 : 32;
                CompileIntoHL(args[0]); EmitAsm("PUSH_HL"); // dst
                CompileIntoHL(args[1]); EmitAsm("PUSH_HL"); // src
                EmitAsm("LD_BC_IMM", new AsmOperand(copyLen, AddressMode.Immediate16));
                EmitAsm("POP_DE"); // src
                EmitAsm("POP_HL"); // dst
                EmitRamMemcpyLoop(funcName.TrimStart('_'));
                return;
            }

void EmitRamMemcpyLoop(string labelPrefix)
    {
        AsmOperand loop = MakeUniqueLabel(labelPrefix + "_loop");
        AsmOperand done = MakeUniqueLabel(labelPrefix + "_done");

        EmitLabel(loop);
        EmitAsm("LD_A_B");
        EmitAsm("OR_C");
        EmitAsm("JR_Z", done);
        EmitAsm("LD_A_DE");
        EmitAsm("LDI_HL_A");
        EmitAsm("INC_DE");
        EmitAsm("DEC_BC");
        EmitAsm("JR", loop);
        EmitLabel(done);
    }

// kitaqgb/kitaqgb/Lowerer.cs:2145
if (expr.MatchAny(Tag.Call, out callFunc, out callArgs))
        {
            string knownCallName;
            if (callFunc != null && callFunc.Match(Tag.Name, out knownCallName) &&
                knownCallName == "__cgb_is_cgb" &&
                callArgs != null && callArgs.Length == 0 &&
                // A resolved target configuration can replace the zero-argument CGB query with a literal.
                Program.TryGetKnownCgbRuntimeValue(out int knownCgbValue))
            {
                return Expr.Make(Tag.Integer, knownCgbValue).WithSource(expr.Source);
            }

            Expr newFunc = LowerExpr(ctx, callFunc, false, prefix);

            // Optimization (post-A3): when we must spill a non-atomic argument into a temp,
            // prefer a u8 temp if the *parameter type* is u8-like.
            // This preserves semantics (implicit truncation to u8) while avoiding needless
            // u16 temporaries created by integer promotions.
            CType[] paramTypes = null;
            string __fname = null;
            if (newFunc.Match(Tag.Name, out __fname))
            {
                paramTypes = ctx.FindFuncParams(__fname);
                if (paramTypes == null)
                {
                    CType fnTypeFromName;
                    if (TryGetCallableFunctionType(ctx.FindTypeOfName(__fname), out fnTypeFromName))
                        paramTypes = fnTypeFromName.ParamTypes;
                }
            }
            else
            {
                CType fnTypeFromExpr;
                if (TryGetCallableFunctionType(InferType(ctx, newFunc), out fnTypeFromExpr))
                    paramTypes = fnTypeFromExpr.ParamTypes;
            }

            Expr[] newArgs = new Expr[callArgs.Length];
            for (int i = 0; i < callArgs.Length; i++)
            {
                Expr a = LowerExpr(ctx, callArgs[i], allowExtract, prefix);
                bool preserveConstantArgIntrinsic = false;
                if (a.MatchAny(Tag.Call, out Expr argFunc, out Expr[] _argCallArgs) &&
                    argFunc.Match(Tag.Name, out string argFuncName))
                {
                    preserveConstantArgIntrinsic = (argFuncName == "__bankof" || argFuncName == "__cgb_is_cgb");
                }
                // Keep evaluable constants and special intrinsic/bank operands visible to code generation instead of spilling them.
                bool preserveCompileTimeConstantArg = ctx.Global.TryEvalConstInt(a, out _);
                bool preserveFarcallBankArg = (__fname == "__farcall" && i == 0);

                if (allowExtract && !preserveFarcallBankArg && !preserveConstantArgIntrinsic && !preserveCompileTimeConstantArg && !IsAtom(a))
                {
                    // If we know the callee's parameter type, use it to choose a smaller temp.
                    // Example: (u8)(row*32) passed to a u8 param should spill as u8, not u16.
                    CType spillAs = null;
                    if (paramTypes != null && i < paramTypes.Length)
                    {
                        CType pt = paramTypes[i];
                        if (IsU8Like(pt)) spillAs = CType.UInt8;
                    }
                    if (spillAs == null) spillAs = InferType(ctx, a);
                    if (!IsAggregateType(spillAs) && SizeOfType(spillAs) <= 2)
                        a = SpillToTemp(ctx, a, spillAs, prefix);
                }
                newArgs[i] = a;
            }

            // Lint: const discard for argument passing
            // Warn when passing a const-qualified pointer (or const array decay) to a non-const pointer parameter.
            // Suppress when the argument has an explicit cast, or inside __unsafe.
            if (!ctx.InUnsafe && paramTypes != null)
            {
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    CType pt = paramTypes[i];
                    if (pt == null) continue;
                    if (!IsPointerToNonConst(pt)) continue;
                    if (HasTopLevelCast(newArgs[i])) continue;

                    CType at = InferType(ctx, newArgs[i]);
                    if (at != null && (IsPointerToConst(at) || IsArrayOfConst(at)))
                    {
                        string fn = __fname ?? "<call>";
                        WarnConstDiscard(expr, $"argument {i} to '{fn}'");
                        break; // keep it quiet: one warning per call is enough
                    }
                }
            }

            if (paramTypes != null)
            {
                string fn = __fname ?? "<call>";
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    FilePosition argPos = newArgs[i].Source;
                    if (string.IsNullOrEmpty(argPos.Filename) || argPos.Filename == "<unknown>")
                        argPos = expr.Source;
                    WarnImplicitNarrowingIfNeeded(ctx, newArgs[i], InferType(ctx, newArgs[i]), paramTypes[i], argPos,
                        "argument " + i + " to '" + fn + "'");
                }
            }

            // Lint: __enum_strict argument passing.
            if (paramTypes != null)
            {
                string fn = __fname ?? "<call>";
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    CType pt = paramTypes[i];
                    if (pt == null) continue;

                    CType at = InferType(ctx, newArgs[i]);
                    if (at == null) continue;

                    if (HasTopLevelCast(newArgs[i])) continue;

                    if (IsStrictEnum(pt))
                    {
                        if (!IsSameEnum(pt, at))
                        {
                            Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
                                "argument {0} to '{1}' expects __enum_strict {2}, got {3}; cast explicitly to silence",
                                i, fn, pt.Show(), at.Show());
                        }
                    }
                    else if (IsPlainInteger(pt) && IsStrictEnum(at))
                    {
                        Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
                            "argument {0} to '{1}' passes __enum_strict {2} to integer parameter {3}; cast explicitly to silence",
                            i, fn, at.Show(), pt.Show());
                    }
                }
            }

            if (!ctx.InUnsafe)
                RestrictAliasLint(ctx, newFunc, newArgs, expr.Source);

            // __slice(ptr,len) helper: represent a "slice" (ptr+len) in the IR.
            // Semantics: evaluate both arguments, result type is the pointer/array (same as arg0).
            // Bounds checks are injected only with -Zcheck (see Program.CheckSliceBounds).
            // Note: keep the intrinsic name conservative to avoid collisions ("slice" is allowed only
            // when there is no declared function named "slice").
            if (newFunc.Match(Tag.Name, out string __sfname))
            {
                bool isSliceIntrinsic = (__sfname == "__slice") || (__sfname == "slice" && ctx.FindFuncParams("slice") == null);
                if (isSliceIntrinsic)
                {
                    if (newArgs.Length != 2)
                    {
                        Program.Error(expr.Source, ErrorCode.ParseError, "{0}: expected 2 arguments: __slice(ptr,len)", __sfname);
                    }
                    else
                    {
                        CType pty = InferType(ctx, newArgs[0]);
                        if (pty == null || !(pty.IsPointer || pty.IsArray))
                            Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: first argument must be a pointer or array, got: {1}", __sfname, (pty == null ? "<unknown>" : pty.Show()));

                        CType lty = InferType(ctx, newArgs[1]);
                        if (lty == null || !lty.IsInteger)
                            Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: second argument must be an integer length, got: {1}", __sfname, (lty == null ? "<unknown>" : lty.Show()));
                    }

                    return Expr.Make(Tag.Slice, newArgs[0], newArgs[1]).WithSource(expr.Source);
                }
            }

            return MakeCall(newFunc, newArgs).WithSource(expr.Source);
        }
__far_memcpy

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

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

Parameter

dst

Ziel 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.

bank

8-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.

src

Nahzeiger auf lesbare Bytes in der gewählten ROM-Bank. Er enthält keine Banknummer.

len

Byteanzahl, 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

c
__far_memcpy(buffer,2,asset_shapes,32); // Same backend as __farmemcpy.
expect(*asset_bank_marker==0xA1,29);
for(i=0;i<32;i++) expect(buffer[(__safe_index u8)i]==expected_shape_byte(i),32);
buffer[32]=0x55;
__far_memcpy(buffer+32,2,asset_shapes,0); // Zero count writes no bytes and restores the bank.
expect(buffer[32]==0x55 && *asset_bank_marker==0xA1,30);

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.

__far_memcpy: DMG
DMG
__far_memcpy: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\asset_banks.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\asset_banks.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --no-cache --no-disasm
Implementierung 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

c
void __fill_tilemap(u16 dst, u8 value, u16 len);

Parameter

dst

Adresse 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.

value

Ein 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.

len

Anzahl 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

c
__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.

__fill_tilemap: DMG
DMG
__fill_tilemap: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/rpg.h:157

__getbgmapbase

Liest LCDC-Bit 3 zur Auswahl der Basisadresse der Hintergrund-Kachelkarte.

Syntax

c
u16 __getbgmapbase();

Rückgabewert

Gibt $9800 oder $9C00 zurück: die Kachelkartenbasis, nicht die Adresse der Kachelgrafik.

Anwendungsbeispiel

c
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.

__getbgmapbase: DMG
DMG
__getbgmapbase: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_display.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_display.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implementierung und Quelltext

kitaqgb/lib/rpg.h:140

kitaqgb/kitaqgb/CodeGenerator.cs:10619

csharp
if (funcName == "__getbgmapbase")
{
    if (args.Length != 0) Error(expr, "__getbgmapbase requires 0 arguments");

    AsmOperand gb_base0 = MakeUniqueLabel("getbgbase_base0");
    AsmOperand gb_done  = MakeUniqueLabel("getbgbase_done");

    EmitAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem)); // LCDC
    EmitAsm("AND_IMM", new AsmOperand(0x08, AddressMode.Immediate)); // bit3 (BG tile map display select)
    EmitAsm("JR_Z", gb_base0);
    EmitAsm("LD_HL_IMM", new AsmOperand(0x9C00, AddressMode.Immediate16));
    EmitAsm("JR", gb_done);
    EmitLabel(gb_base0);
    EmitAsm("LD_HL_IMM", new AsmOperand(0x9800, AddressMode.Immediate16));
    EmitLabel(gb_done);
    LastCallReturnsHL = true;
    return;
}
__manhattan

Addiert die absoluten X- und Y-Differenzen zweier Punkte.

Syntax

c
u8 __manhattan(u8 x1, u8 y1, u8 x2, u8 y2);

Parameter

x1

X-Koordinate des ersten Punkts, 0..255.

y1

Y-Koordinate des ersten Punkts, 0..255.

x2

X-Koordinate des zweiten Punkts, 0..255.

y2

Y-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

c
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.

__manhattan: DMG
DMG
__manhattan: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_coordinates.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_coordinates.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implementierung und Quelltext

kitaqgb/lib/rpg.h:183

kitaqgb/kitaqgb/CodeGenerator.cs:8516

csharp
if (funcName == "__manhattan")
            {
                if (args.Length != 4) Error(expr, "__manhattan(x1, y1, x2, y2) expects 4 arguments");

                Expr[] foldedManhattanArgs = args.Select(FoldConstants).ToArray();
                if (foldedManhattanArgs[0].Match(Tag.Integer, out int constX1) &&
                    foldedManhattanArgs[1].Match(Tag.Integer, out int constY1) &&
                    foldedManhattanArgs[2].Match(Tag.Integer, out int constX2) &&
                    foldedManhattanArgs[3].Match(Tag.Integer, out int constY2))
                {
                    int dx = Math.Abs((constX1 & 0xFF) - (constX2 & 0xFF));
                    int dy = Math.Abs((constY1 & 0xFF) - (constY2 & 0xFF));
                    EmitAsm("LD_A_IMM", new AsmOperand((dx + dy) & 0xFF, AddressMode.Immediate));
                    return;
                }

                EmitPushExprAsWord(args[0]); // x1
                EmitPushExprAsWord(args[1]); // y1
                EmitPushExprAsWord(args[2]); // x2
                EmitPushExprAsWord(args[3]); // y2

                AsmOperand dxGe = MakeUniqueLabel("manhattan_dx_ge");
                AsmOperand dxDone = MakeUniqueLabel("manhattan_dx_done");
                AsmOperand dyGe = MakeUniqueLabel("manhattan_dy_ge");
                AsmOperand dyDone = MakeUniqueLabel("manhattan_dy_done");

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(6, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_B_A"); // x1
                EmitAsm("LD_HL_SP_IMM", new AsmOperand(2, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_C_A"); // x2
                EmitAsm("LD_A_B");
                EmitAsm("CP_C");
                EmitAsm("JR_NC", dxGe);
                EmitAsm("LD_A_C");
                EmitAsm("SUB_B");
                EmitAsm("JR", dxDone);
                EmitLabel(dxGe);
                EmitAsm("LD_A_B");
                EmitAsm("SUB_C");
                EmitLabel(dxDone);
                EmitAsm("LD_B_A"); // dx

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_C_A"); // y1
                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_D_A"); // y2
                EmitAsm("LD_A_C");
                EmitAsm("CP_D");
                EmitAsm("JR_NC", dyGe);
                EmitAsm("LD_A_D");
                EmitAsm("SUB_C");
                EmitAsm("JR", dyDone);
                EmitLabel(dyGe);
                EmitAsm("LD_A_C");
                EmitAsm("SUB_D");
                EmitLabel(dyDone);
                EmitAsm("ADD_B");
                EmitAsm("ADD_SP_IMM", new AsmOperand(8, AddressMode.Relative));
                return;
            }
__map_index

Wandelt eine Zellkoordinate in einen zeilenweisen Array-Index um.

Syntax

c
u16 __map_index(u8 x, u8 y, u8 width);

Parameter

x

Spalte 0..255, gewöhnlich kleiner als width.

y

Zeile, 0..255.

width

Zellen je Zeile, 0..255.

Rückgabewert

Gibt y*width+x als u16 zurück; prüft die Koordinaten nicht.

Anwendungsbeispiel

c
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.

__map_index: DMG
DMG
__map_index: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_coordinates.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_coordinates.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implementierung und Quelltext

kitaqgb/lib/rpg.h:181

kitaqgb/kitaqgb/CodeGenerator.cs:8365

csharp
if (funcName == "__map_index")
            {
                if (args.Length != 3) Error(expr, "__map_index(x, y, width) expects 3 arguments");

                Expr xExpr = FoldConstants(args[0]);
                Expr yExpr = FoldConstants(args[1]);
                Expr widthExpr = FoldConstants(args[2]);
                // Apply the width parameter's byte conversion before choosing a shift specialization.
                if (widthExpr.Match(Tag.Integer, out int byteWidth))
                    widthExpr = Expr.Make(Tag.Integer, byteWidth & 0xFF).WithSource(widthExpr.Source);
                if (xExpr.Match(Tag.Integer, out int fullXConst) &&
                    yExpr.Match(Tag.Integer, out int fullYConst) &&
                    widthExpr.Match(Tag.Integer, out int fullWidthConst))
                {
                    // Apply the byte argument widths before calculating the word-sized constant index.
                    int fullIndexConst = (((fullYConst & 0xFF) * (fullWidthConst & 0xFF)) + (fullXConst & 0xFF)) & 0xFFFF;
                    EmitAsm("LD_HL_IMM", new AsmOperand(fullIndexConst, AddressMode.Immediate16));
                    LastCallReturnsHL = true;
                    return;
                }

                if (widthExpr.Match(Tag.Integer, out int widthConst) && widthConst > 0 && IsPow2(widthConst))
                {
                    CompileIntoA(yExpr);
                    EmitAsm("LD_L_A");
                    EmitAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate));
                    int sh = Log2Pow2(widthConst);
                    for (int i = 0; i < sh; i++) EmitAsm("ADD_HL_HL");

                    if (xExpr.Match(Tag.Integer, out int xConst) && xConst == 0)
                    {
                        LastCallReturnsHL = true;
                        return;
                    }

                    CompileIntoA(xExpr);
                    EmitAsm("LD_E_A");
                    EmitAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate));
                    EmitAsm("ADD_HL_DE");
                    LastCallReturnsHL = true;
                    return;
                }

                CompileIntoA(args[0]); EmitPushAAsWord(); // x
                CompileIntoA(args[1]); EmitPushAAsWord(); // y
                CompileIntoA(args[2]); EmitPushAAsWord(); // width

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_C_A"); // width

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(2, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_E_A"); // y

                EmitMul8x8ToHL_EC("__map_index");

                // Loading the saved X argument uses HL, so preserve the multiplication result first.
                EmitAsm("PUSH_HL");
                EmitAsm("LD_HL_SP_IMM", new AsmOperand(6, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_E_A");
                EmitAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate));
                EmitAsm("POP_HL");
                EmitAsm("ADD_HL_DE");
                EmitAsm("ADD_SP_IMM", new AsmOperand(6, AddressMode.Relative));
                LastCallReturnsHL = true;
                return;
            }
__memcpy

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

c
void __memcpy(void* dst, const void* src, u16 count);

Parameter

dst

Zeiger auf das erste beschreibbare Zielbyte. Stellen Sie genügend zusammenhängenden Speicher für die gesamte Operation bereit; ein Puffer wird nicht automatisch angelegt.

src

Zeiger auf das erste lesbare Quellbyte. Der gesamte Bereich muss während der Kopie zugänglich und unverändert bleiben.

count

Byteanzahl 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

c
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.

__memcpy: DMG
DMG
__memcpy: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\memory_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\memory_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung und Quelltext

kitaqgb/lib/rpg.h:142

kitaqgb/kitaqgb/CodeGenerator.cs:9084

csharp
if (funcName == "__memcpy" || funcName == "__memcpy_small")
            {
                // Select byte or word count semantics before optional fixed-array bounds diagnostics.
                bool useU8Count = (funcName == "__memcpy_small");
                if (args.Length != 3) { Error(expr, "__memcpy requires 3 arguments"); return; }

                bool doCheck = Program.CheckMemCopy && !InUnsafe;
                bool hasDst = false, hasSrc = false;
                int dstBytes = 0, srcBytes = 0;
                string dstName = null, srcName = null;
                if (doCheck)
                {
                    hasDst = TryGetFixedArrayByteLength(args[0], out dstBytes, out dstName);
                    hasSrc = TryGetFixedArrayByteLength(args[1], out srcBytes, out srcName);
                }

                bool isConstCountAny = args[2].Match(Tag.Integer, out int constCountAny);
                int normalizedConstCount = constCountAny;
                if (isConstCountAny && useU8Count)
                    normalizedConstCount &= 0xFF;

                if (doCheck && isConstCountAny)
                {
                    if (hasDst && normalizedConstCount >= 0 && normalizedConstCount > dstBytes)
                    {
                        Program.Warning(args[2].Source, "__memcpy count {0} exceeds fixed dst array '{1}' size {2} bytes (-Zcheck)", normalizedConstCount, dstName, dstBytes);
                        EmitAsm("JP", EnsureCheckTrap());
                        return;
                    }
                    if (hasSrc && normalizedConstCount >= 0 && normalizedConstCount > srcBytes)
                    {
                        Program.Warning(args[2].Source, "__memcpy count {0} exceeds fixed src array '{1}' size {2} bytes (-Zcheck)", normalizedConstCount, srcName, srcBytes);
                        EmitAsm("JP", EnsureCheckTrap());
                        return;
                    }
                }

                if (isConstCountAny && normalizedConstCount >= 0 && normalizedConstCount <= 16)
                {
                    if (normalizedConstCount == 0) return;

                    CompileIntoHL(args[0]);
                    EmitAsm("PUSH_HL");
                    CompileIntoHL(args[1]);
                    EmitAsm("PUSH_HL");
                    EmitAsm("POP_DE");
                    EmitAsm("POP_HL");

                    for (int k = 0; k < normalizedConstCount; k++)
                    {
                        EmitAsm("LD_A_DE");
                        EmitAsm("LDI_HL_A");
                        EmitAsm("INC_DE");
                    }
                    return;
                }

                if (isConstCountAny && normalizedConstCount > 16 && normalizedConstCount <= 255)
                {
                    CompileIntoHL(args[0]);
                    EmitAsm("PUSH_HL");
                    CompileIntoHL(args[1]);
                    EmitAsm("PUSH_HL");
                    EmitAsm("POP_DE");
                    EmitAsm("POP_HL");

                    if (normalizedConstCount <= 64)
                    {
                        int blocks8 = normalizedConstCount / 8;
                        int rem8 = normalizedConstCount % 8;
                        if (blocks8 > 0)
                        {
                            EmitAsm("LD_B_IMM", new AsmOperand(blocks8, AddressMode.Immediate));
                            // Advance source and destination through eight-byte groups, then emit the remaining constant bytes.
                            AsmOperand loop8 = MakeUniqueLabel("memcpy8_loop");
                            EmitLabel(loop8);
                            for (int k = 0; k < 8; k++)
                            {
                                EmitAsm("LD_A_DE");
                                EmitAsm("LDI_HL_A");
                                EmitAsm("INC_DE");
                            }
                            EmitAsm("DEC_B");
                            EmitAsm("JP_NZ", loop8);
                        }
                        for (int k = 0; k < rem8; k++)
                        {
                            EmitAsm("LD_A_DE");
                            EmitAsm("LDI_HL_A");
                            EmitAsm("INC_DE");
                        }
                        return;
                    }

                    int blocks16 = normalizedConstCount / 16;
                    int rem16 = normalizedConstCount % 16;
                    if (blocks16 > 0)
                    {
                        EmitAsm("LD_B_IMM", new AsmOperand(blocks16, AddressMode.Immediate));
                        // Use sixteen-byte groups while keeping the residual copy length known at compile time.
                        AsmOperand loop16 = MakeUniqueLabel("memcpy16_loop");
                        EmitLabel(loop16);
                        for (int k = 0; k < 16; k++)
                        {
                            EmitAsm("LD_A_DE");
                            EmitAsm("LDI_HL_A");
                            EmitAsm("INC_DE");
                        }
                        EmitAsm("DEC_B");
                        EmitAsm("JP_NZ", loop16);
                    }
                    for (int k = 0; k < rem16; k++)
                    {
                        EmitAsm("LD_A_DE");
                        EmitAsm("LDI_HL_A");
                        EmitAsm("INC_DE");
                    }
                    return;
                }

                if (useU8Count)
                {
                    CompileIntoHL(args[0]);
                    EmitAsm("PUSH_HL");

                    CompileIntoHL(args[1]);
                    EmitAsm("PUSH_HL");

                    CompileIntoA(args[2]);
                    EmitAsm("LD_B_A");
                    if (doCheck)
                    {
                        if (hasDst) EmitU8LeqConstOrTrapInA(dstBytes);
                        if (hasSrc) EmitU8LeqConstOrTrapInA(srcBytes);
                    }

                    AsmOperand doneLabel = MakeUniqueLabel("memcpy_small_done");
                    AsmOperand smallLoopLabel = MakeUniqueLabel("memcpy_small_loop");
                    EmitAsm("OR_A");
                    EmitAsm("POP_DE");
                    EmitAsm("POP_HL");
                    EmitAsm("JP_Z", doneLabel);

                    EmitLabel(smallLoopLabel);
                    EmitAsm("LD_A_DE");
                    EmitAsm("LDI_HL_A");
                    EmitAsm("INC_DE");
                    EmitAsm("DEC_B");
                    EmitAsm("JP_NZ", smallLoopLabel);
                    EmitLabel(doneLabel);
                    return;
                }

                CompileIntoHL(args[0]);
                EmitAsm("PUSH_HL");

                CompileIntoHL(args[1]);
                EmitAsm("PUSH_HL");

                CompileIntoHL(args[2]);
                if (doCheck && !isConstCountAny)
                {
                    if (hasDst) EmitU16LeqConstOrTrap(dstBytes);
                    if (hasSrc) EmitU16LeqConstOrTrap(srcBytes);
                }
                EmitAsm("LD_B_H");
                EmitAsm("LD_C_L");

                EmitAsm("POP_DE");
                EmitAsm("POP_HL");

                AsmOperand loopLabel = MakeUniqueLabel("memcpy_loop");
                AsmOperand wordDoneLabel = MakeUniqueLabel("memcpy_done");
                // A zero word count must skip the first write as well as the loop.
                EmitAsm("LD_A_B");
                EmitAsm("OR_C");
                EmitAsm("JP_Z", wordDoneLabel);
                EmitLabel(loopLabel);

                EmitAsm("LD_A_DE");
                EmitAsm("LDI_HL_A");
                EmitAsm("INC_DE");

                EmitAsm("DEC_BC");
                EmitAsm("LD_A_B");
                EmitAsm("OR_C");
                EmitAsm("JP_NZ", loopLabel);
                EmitLabel(wordDoneLabel);

                return;
            }

// kitaqgb/kitaqgb/Lowerer.cs:2145
if (expr.MatchAny(Tag.Call, out callFunc, out callArgs))
        {
            string knownCallName;
            if (callFunc != null && callFunc.Match(Tag.Name, out knownCallName) &&
                knownCallName == "__cgb_is_cgb" &&
                callArgs != null && callArgs.Length == 0 &&
                // A resolved target configuration can replace the zero-argument CGB query with a literal.
                Program.TryGetKnownCgbRuntimeValue(out int knownCgbValue))
            {
                return Expr.Make(Tag.Integer, knownCgbValue).WithSource(expr.Source);
            }

            Expr newFunc = LowerExpr(ctx, callFunc, false, prefix);

            // Optimization (post-A3): when we must spill a non-atomic argument into a temp,
            // prefer a u8 temp if the *parameter type* is u8-like.
            // This preserves semantics (implicit truncation to u8) while avoiding needless
            // u16 temporaries created by integer promotions.
            CType[] paramTypes = null;
            string __fname = null;
            if (newFunc.Match(Tag.Name, out __fname))
            {
                paramTypes = ctx.FindFuncParams(__fname);
                if (paramTypes == null)
                {
                    CType fnTypeFromName;
                    if (TryGetCallableFunctionType(ctx.FindTypeOfName(__fname), out fnTypeFromName))
                        paramTypes = fnTypeFromName.ParamTypes;
                }
            }
            else
            {
                CType fnTypeFromExpr;
                if (TryGetCallableFunctionType(InferType(ctx, newFunc), out fnTypeFromExpr))
                    paramTypes = fnTypeFromExpr.ParamTypes;
            }

            Expr[] newArgs = new Expr[callArgs.Length];
            for (int i = 0; i < callArgs.Length; i++)
            {
                Expr a = LowerExpr(ctx, callArgs[i], allowExtract, prefix);
                bool preserveConstantArgIntrinsic = false;
                if (a.MatchAny(Tag.Call, out Expr argFunc, out Expr[] _argCallArgs) &&
                    argFunc.Match(Tag.Name, out string argFuncName))
                {
                    preserveConstantArgIntrinsic = (argFuncName == "__bankof" || argFuncName == "__cgb_is_cgb");
                }
                // Keep evaluable constants and special intrinsic/bank operands visible to code generation instead of spilling them.
                bool preserveCompileTimeConstantArg = ctx.Global.TryEvalConstInt(a, out _);
                bool preserveFarcallBankArg = (__fname == "__farcall" && i == 0);

                if (allowExtract && !preserveFarcallBankArg && !preserveConstantArgIntrinsic && !preserveCompileTimeConstantArg && !IsAtom(a))
                {
                    // If we know the callee's parameter type, use it to choose a smaller temp.
                    // Example: (u8)(row*32) passed to a u8 param should spill as u8, not u16.
                    CType spillAs = null;
                    if (paramTypes != null && i < paramTypes.Length)
                    {
                        CType pt = paramTypes[i];
                        if (IsU8Like(pt)) spillAs = CType.UInt8;
                    }
                    if (spillAs == null) spillAs = InferType(ctx, a);
                    if (!IsAggregateType(spillAs) && SizeOfType(spillAs) <= 2)
                        a = SpillToTemp(ctx, a, spillAs, prefix);
                }
                newArgs[i] = a;
            }

            // Lint: const discard for argument passing
            // Warn when passing a const-qualified pointer (or const array decay) to a non-const pointer parameter.
            // Suppress when the argument has an explicit cast, or inside __unsafe.
            if (!ctx.InUnsafe && paramTypes != null)
            {
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    CType pt = paramTypes[i];
                    if (pt == null) continue;
                    if (!IsPointerToNonConst(pt)) continue;
                    if (HasTopLevelCast(newArgs[i])) continue;

                    CType at = InferType(ctx, newArgs[i]);
                    if (at != null && (IsPointerToConst(at) || IsArrayOfConst(at)))
                    {
                        string fn = __fname ?? "<call>";
                        WarnConstDiscard(expr, $"argument {i} to '{fn}'");
                        break; // keep it quiet: one warning per call is enough
                    }
                }
            }

            if (paramTypes != null)
            {
                string fn = __fname ?? "<call>";
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    FilePosition argPos = newArgs[i].Source;
                    if (string.IsNullOrEmpty(argPos.Filename) || argPos.Filename == "<unknown>")
                        argPos = expr.Source;
                    WarnImplicitNarrowingIfNeeded(ctx, newArgs[i], InferType(ctx, newArgs[i]), paramTypes[i], argPos,
                        "argument " + i + " to '" + fn + "'");
                }
            }

            // Lint: __enum_strict argument passing.
            if (paramTypes != null)
            {
                string fn = __fname ?? "<call>";
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    CType pt = paramTypes[i];
                    if (pt == null) continue;

                    CType at = InferType(ctx, newArgs[i]);
                    if (at == null) continue;

                    if (HasTopLevelCast(newArgs[i])) continue;

                    if (IsStrictEnum(pt))
                    {
                        if (!IsSameEnum(pt, at))
                        {
                            Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
                                "argument {0} to '{1}' expects __enum_strict {2}, got {3}; cast explicitly to silence",
                                i, fn, pt.Show(), at.Show());
                        }
                    }
                    else if (IsPlainInteger(pt) && IsStrictEnum(at))
                    {
                        Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
                            "argument {0} to '{1}' passes __enum_strict {2} to integer parameter {3}; cast explicitly to silence",
                            i, fn, at.Show(), pt.Show());
                    }
                }
            }

            if (!ctx.InUnsafe)
                RestrictAliasLint(ctx, newFunc, newArgs, expr.Source);

            // __slice(ptr,len) helper: represent a "slice" (ptr+len) in the IR.
            // Semantics: evaluate both arguments, result type is the pointer/array (same as arg0).
            // Bounds checks are injected only with -Zcheck (see Program.CheckSliceBounds).
            // Note: keep the intrinsic name conservative to avoid collisions ("slice" is allowed only
            // when there is no declared function named "slice").
            if (newFunc.Match(Tag.Name, out string __sfname))
            {
                bool isSliceIntrinsic = (__sfname == "__slice") || (__sfname == "slice" && ctx.FindFuncParams("slice") == null);
                if (isSliceIntrinsic)
                {
                    if (newArgs.Length != 2)
                    {
                        Program.Error(expr.Source, ErrorCode.ParseError, "{0}: expected 2 arguments: __slice(ptr,len)", __sfname);
                    }
                    else
                    {
                        CType pty = InferType(ctx, newArgs[0]);
                        if (pty == null || !(pty.IsPointer || pty.IsArray))
                            Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: first argument must be a pointer or array, got: {1}", __sfname, (pty == null ? "<unknown>" : pty.Show()));

                        CType lty = InferType(ctx, newArgs[1]);
                        if (lty == null || !lty.IsInteger)
                            Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: second argument must be an integer length, got: {1}", __sfname, (lty == null ? "<unknown>" : lty.Show()));
                    }

                    return Expr.Make(Tag.Slice, newArgs[0], newArgs[1]).WithSource(expr.Source);
                }
            }

            return MakeCall(newFunc, newArgs).WithSource(expr.Source);
        }
__memcpy_small

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

c
void __memcpy_small(void* dst, const void* src, u8 len);

Parameter

dst

Zeiger auf das erste beschreibbare Zielbyte. Stellen Sie genügend zusammenhängenden Speicher für die gesamte Operation bereit; ein Puffer wird nicht automatisch angelegt.

src

Zeiger auf das erste lesbare Quellbyte. Der gesamte Bereich muss während der Kopie zugänglich und unverändert bleiben.

len

Byteanzahl 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

c
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.

__memcpy_small: DMG
DMG
__memcpy_small: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\memory_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\memory_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung und Quelltext

kitaqgb/lib/rpg.h:175

kitaqgb/kitaqgb/CodeGenerator.cs:9084

csharp
if (funcName == "__memcpy" || funcName == "__memcpy_small")
            {
                // Select byte or word count semantics before optional fixed-array bounds diagnostics.
                bool useU8Count = (funcName == "__memcpy_small");
                if (args.Length != 3) { Error(expr, "__memcpy requires 3 arguments"); return; }

                bool doCheck = Program.CheckMemCopy && !InUnsafe;
                bool hasDst = false, hasSrc = false;
                int dstBytes = 0, srcBytes = 0;
                string dstName = null, srcName = null;
                if (doCheck)
                {
                    hasDst = TryGetFixedArrayByteLength(args[0], out dstBytes, out dstName);
                    hasSrc = TryGetFixedArrayByteLength(args[1], out srcBytes, out srcName);
                }

                bool isConstCountAny = args[2].Match(Tag.Integer, out int constCountAny);
                int normalizedConstCount = constCountAny;
                if (isConstCountAny && useU8Count)
                    normalizedConstCount &= 0xFF;

                if (doCheck && isConstCountAny)
                {
                    if (hasDst && normalizedConstCount >= 0 && normalizedConstCount > dstBytes)
                    {
                        Program.Warning(args[2].Source, "__memcpy count {0} exceeds fixed dst array '{1}' size {2} bytes (-Zcheck)", normalizedConstCount, dstName, dstBytes);
                        EmitAsm("JP", EnsureCheckTrap());
                        return;
                    }
                    if (hasSrc && normalizedConstCount >= 0 && normalizedConstCount > srcBytes)
                    {
                        Program.Warning(args[2].Source, "__memcpy count {0} exceeds fixed src array '{1}' size {2} bytes (-Zcheck)", normalizedConstCount, srcName, srcBytes);
                        EmitAsm("JP", EnsureCheckTrap());
                        return;
                    }
                }

                if (isConstCountAny && normalizedConstCount >= 0 && normalizedConstCount <= 16)
                {
                    if (normalizedConstCount == 0) return;

                    CompileIntoHL(args[0]);
                    EmitAsm("PUSH_HL");
                    CompileIntoHL(args[1]);
                    EmitAsm("PUSH_HL");
                    EmitAsm("POP_DE");
                    EmitAsm("POP_HL");

                    for (int k = 0; k < normalizedConstCount; k++)
                    {
                        EmitAsm("LD_A_DE");
                        EmitAsm("LDI_HL_A");
                        EmitAsm("INC_DE");
                    }
                    return;
                }

                if (isConstCountAny && normalizedConstCount > 16 && normalizedConstCount <= 255)
                {
                    CompileIntoHL(args[0]);
                    EmitAsm("PUSH_HL");
                    CompileIntoHL(args[1]);
                    EmitAsm("PUSH_HL");
                    EmitAsm("POP_DE");
                    EmitAsm("POP_HL");

                    if (normalizedConstCount <= 64)
                    {
                        int blocks8 = normalizedConstCount / 8;
                        int rem8 = normalizedConstCount % 8;
                        if (blocks8 > 0)
                        {
                            EmitAsm("LD_B_IMM", new AsmOperand(blocks8, AddressMode.Immediate));
                            // Advance source and destination through eight-byte groups, then emit the remaining constant bytes.
                            AsmOperand loop8 = MakeUniqueLabel("memcpy8_loop");
                            EmitLabel(loop8);
                            for (int k = 0; k < 8; k++)
                            {
                                EmitAsm("LD_A_DE");
                                EmitAsm("LDI_HL_A");
                                EmitAsm("INC_DE");
                            }
                            EmitAsm("DEC_B");
                            EmitAsm("JP_NZ", loop8);
                        }
                        for (int k = 0; k < rem8; k++)
                        {
                            EmitAsm("LD_A_DE");
                            EmitAsm("LDI_HL_A");
                            EmitAsm("INC_DE");
                        }
                        return;
                    }

                    int blocks16 = normalizedConstCount / 16;
                    int rem16 = normalizedConstCount % 16;
                    if (blocks16 > 0)
                    {
                        EmitAsm("LD_B_IMM", new AsmOperand(blocks16, AddressMode.Immediate));
                        // Use sixteen-byte groups while keeping the residual copy length known at compile time.
                        AsmOperand loop16 = MakeUniqueLabel("memcpy16_loop");
                        EmitLabel(loop16);
                        for (int k = 0; k < 16; k++)
                        {
                            EmitAsm("LD_A_DE");
                            EmitAsm("LDI_HL_A");
                            EmitAsm("INC_DE");
                        }
                        EmitAsm("DEC_B");
                        EmitAsm("JP_NZ", loop16);
                    }
                    for (int k = 0; k < rem16; k++)
                    {
                        EmitAsm("LD_A_DE");
                        EmitAsm("LDI_HL_A");
                        EmitAsm("INC_DE");
                    }
                    return;
                }

                if (useU8Count)
                {
                    CompileIntoHL(args[0]);
                    EmitAsm("PUSH_HL");

                    CompileIntoHL(args[1]);
                    EmitAsm("PUSH_HL");

                    CompileIntoA(args[2]);
                    EmitAsm("LD_B_A");
                    if (doCheck)
                    {
                        if (hasDst) EmitU8LeqConstOrTrapInA(dstBytes);
                        if (hasSrc) EmitU8LeqConstOrTrapInA(srcBytes);
                    }

                    AsmOperand doneLabel = MakeUniqueLabel("memcpy_small_done");
                    AsmOperand smallLoopLabel = MakeUniqueLabel("memcpy_small_loop");
                    EmitAsm("OR_A");
                    EmitAsm("POP_DE");
                    EmitAsm("POP_HL");
                    EmitAsm("JP_Z", doneLabel);

                    EmitLabel(smallLoopLabel);
                    EmitAsm("LD_A_DE");
                    EmitAsm("LDI_HL_A");
                    EmitAsm("INC_DE");
                    EmitAsm("DEC_B");
                    EmitAsm("JP_NZ", smallLoopLabel);
                    EmitLabel(doneLabel);
                    return;
                }

                CompileIntoHL(args[0]);
                EmitAsm("PUSH_HL");

                CompileIntoHL(args[1]);
                EmitAsm("PUSH_HL");

                CompileIntoHL(args[2]);
                if (doCheck && !isConstCountAny)
                {
                    if (hasDst) EmitU16LeqConstOrTrap(dstBytes);
                    if (hasSrc) EmitU16LeqConstOrTrap(srcBytes);
                }
                EmitAsm("LD_B_H");
                EmitAsm("LD_C_L");

                EmitAsm("POP_DE");
                EmitAsm("POP_HL");

                AsmOperand loopLabel = MakeUniqueLabel("memcpy_loop");
                AsmOperand wordDoneLabel = MakeUniqueLabel("memcpy_done");
                // A zero word count must skip the first write as well as the loop.
                EmitAsm("LD_A_B");
                EmitAsm("OR_C");
                EmitAsm("JP_Z", wordDoneLabel);
                EmitLabel(loopLabel);

                EmitAsm("LD_A_DE");
                EmitAsm("LDI_HL_A");
                EmitAsm("INC_DE");

                EmitAsm("DEC_BC");
                EmitAsm("LD_A_B");
                EmitAsm("OR_C");
                EmitAsm("JP_NZ", loopLabel);
                EmitLabel(wordDoneLabel);

                return;
            }

// kitaqgb/kitaqgb/Lowerer.cs:2145
if (expr.MatchAny(Tag.Call, out callFunc, out callArgs))
        {
            string knownCallName;
            if (callFunc != null && callFunc.Match(Tag.Name, out knownCallName) &&
                knownCallName == "__cgb_is_cgb" &&
                callArgs != null && callArgs.Length == 0 &&
                // A resolved target configuration can replace the zero-argument CGB query with a literal.
                Program.TryGetKnownCgbRuntimeValue(out int knownCgbValue))
            {
                return Expr.Make(Tag.Integer, knownCgbValue).WithSource(expr.Source);
            }

            Expr newFunc = LowerExpr(ctx, callFunc, false, prefix);

            // Optimization (post-A3): when we must spill a non-atomic argument into a temp,
            // prefer a u8 temp if the *parameter type* is u8-like.
            // This preserves semantics (implicit truncation to u8) while avoiding needless
            // u16 temporaries created by integer promotions.
            CType[] paramTypes = null;
            string __fname = null;
            if (newFunc.Match(Tag.Name, out __fname))
            {
                paramTypes = ctx.FindFuncParams(__fname);
                if (paramTypes == null)
                {
                    CType fnTypeFromName;
                    if (TryGetCallableFunctionType(ctx.FindTypeOfName(__fname), out fnTypeFromName))
                        paramTypes = fnTypeFromName.ParamTypes;
                }
            }
            else
            {
                CType fnTypeFromExpr;
                if (TryGetCallableFunctionType(InferType(ctx, newFunc), out fnTypeFromExpr))
                    paramTypes = fnTypeFromExpr.ParamTypes;
            }

            Expr[] newArgs = new Expr[callArgs.Length];
            for (int i = 0; i < callArgs.Length; i++)
            {
                Expr a = LowerExpr(ctx, callArgs[i], allowExtract, prefix);
                bool preserveConstantArgIntrinsic = false;
                if (a.MatchAny(Tag.Call, out Expr argFunc, out Expr[] _argCallArgs) &&
                    argFunc.Match(Tag.Name, out string argFuncName))
                {
                    preserveConstantArgIntrinsic = (argFuncName == "__bankof" || argFuncName == "__cgb_is_cgb");
                }
                // Keep evaluable constants and special intrinsic/bank operands visible to code generation instead of spilling them.
                bool preserveCompileTimeConstantArg = ctx.Global.TryEvalConstInt(a, out _);
                bool preserveFarcallBankArg = (__fname == "__farcall" && i == 0);

                if (allowExtract && !preserveFarcallBankArg && !preserveConstantArgIntrinsic && !preserveCompileTimeConstantArg && !IsAtom(a))
                {
                    // If we know the callee's parameter type, use it to choose a smaller temp.
                    // Example: (u8)(row*32) passed to a u8 param should spill as u8, not u16.
                    CType spillAs = null;
                    if (paramTypes != null && i < paramTypes.Length)
                    {
                        CType pt = paramTypes[i];
                        if (IsU8Like(pt)) spillAs = CType.UInt8;
                    }
                    if (spillAs == null) spillAs = InferType(ctx, a);
                    if (!IsAggregateType(spillAs) && SizeOfType(spillAs) <= 2)
                        a = SpillToTemp(ctx, a, spillAs, prefix);
                }
                newArgs[i] = a;
            }

            // Lint: const discard for argument passing
            // Warn when passing a const-qualified pointer (or const array decay) to a non-const pointer parameter.
            // Suppress when the argument has an explicit cast, or inside __unsafe.
            if (!ctx.InUnsafe && paramTypes != null)
            {
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    CType pt = paramTypes[i];
                    if (pt == null) continue;
                    if (!IsPointerToNonConst(pt)) continue;
                    if (HasTopLevelCast(newArgs[i])) continue;

                    CType at = InferType(ctx, newArgs[i]);
                    if (at != null && (IsPointerToConst(at) || IsArrayOfConst(at)))
                    {
                        string fn = __fname ?? "<call>";
                        WarnConstDiscard(expr, $"argument {i} to '{fn}'");
                        break; // keep it quiet: one warning per call is enough
                    }
                }
            }

            if (paramTypes != null)
            {
                string fn = __fname ?? "<call>";
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    FilePosition argPos = newArgs[i].Source;
                    if (string.IsNullOrEmpty(argPos.Filename) || argPos.Filename == "<unknown>")
                        argPos = expr.Source;
                    WarnImplicitNarrowingIfNeeded(ctx, newArgs[i], InferType(ctx, newArgs[i]), paramTypes[i], argPos,
                        "argument " + i + " to '" + fn + "'");
                }
            }

            // Lint: __enum_strict argument passing.
            if (paramTypes != null)
            {
                string fn = __fname ?? "<call>";
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    CType pt = paramTypes[i];
                    if (pt == null) continue;

                    CType at = InferType(ctx, newArgs[i]);
                    if (at == null) continue;

                    if (HasTopLevelCast(newArgs[i])) continue;

                    if (IsStrictEnum(pt))
                    {
                        if (!IsSameEnum(pt, at))
                        {
                            Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
                                "argument {0} to '{1}' expects __enum_strict {2}, got {3}; cast explicitly to silence",
                                i, fn, pt.Show(), at.Show());
                        }
                    }
                    else if (IsPlainInteger(pt) && IsStrictEnum(at))
                    {
                        Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
                            "argument {0} to '{1}' passes __enum_strict {2} to integer parameter {3}; cast explicitly to silence",
                            i, fn, at.Show(), pt.Show());
                    }
                }
            }

            if (!ctx.InUnsafe)
                RestrictAliasLint(ctx, newFunc, newArgs, expr.Source);

            // __slice(ptr,len) helper: represent a "slice" (ptr+len) in the IR.
            // Semantics: evaluate both arguments, result type is the pointer/array (same as arg0).
            // Bounds checks are injected only with -Zcheck (see Program.CheckSliceBounds).
            // Note: keep the intrinsic name conservative to avoid collisions ("slice" is allowed only
            // when there is no declared function named "slice").
            if (newFunc.Match(Tag.Name, out string __sfname))
            {
                bool isSliceIntrinsic = (__sfname == "__slice") || (__sfname == "slice" && ctx.FindFuncParams("slice") == null);
                if (isSliceIntrinsic)
                {
                    if (newArgs.Length != 2)
                    {
                        Program.Error(expr.Source, ErrorCode.ParseError, "{0}: expected 2 arguments: __slice(ptr,len)", __sfname);
                    }
                    else
                    {
                        CType pty = InferType(ctx, newArgs[0]);
                        if (pty == null || !(pty.IsPointer || pty.IsArray))
                            Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: first argument must be a pointer or array, got: {1}", __sfname, (pty == null ? "<unknown>" : pty.Show()));

                        CType lty = InferType(ctx, newArgs[1]);
                        if (lty == null || !lty.IsInteger)
                            Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: second argument must be an integer length, got: {1}", __sfname, (lty == null ? "<unknown>" : lty.Show()));
                    }

                    return Expr.Make(Tag.Slice, newArgs[0], newArgs[1]).WithSource(expr.Source);
                }
            }

            return MakeCall(newFunc, newArgs).WithSource(expr.Source);
        }
__memset

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

c
void __memset(void* dst, u8 value, u16 count);

Parameter

dst

Zeiger auf das erste beschreibbare Zielbyte. Stellen Sie genügend zusammenhängenden Speicher für die gesamte Operation bereit; ein Puffer wird nicht automatisch angelegt.

value

Bytewert 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.

count

Byteanzahl 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

c
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.

__memset: DMG
DMG
__memset: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\memory_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\memory_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung und Quelltext

kitaqgb/lib/rpg.h:141

kitaqgb/kitaqgb/CodeGenerator.cs:8926

csharp
if (funcName == "__memset" || funcName == "__memset_small")
            {
                // The small API truncates its count to a byte; the general API uses a word count.
                bool useU8Count = (funcName == "__memset_small");
                if (args.Length != 3) { Error(expr, "__memset requires 3 arguments"); return; }

                // -Zcheck: fixed-array range check for memset length
                bool doCheck = Program.CheckMemCopy && !InUnsafe;
                bool hasDst = false;
                int dstBytes = 0;
                string dstName = null;
                if (doCheck)
                    hasDst = TryGetFixedArrayByteLength(args[0], out dstBytes, out dstName);

                bool isConstCountAny = args[2].Match(Tag.Integer, out int constCountAny);
                int normalizedConstCount = constCountAny;
                if (isConstCountAny && useU8Count)
                    normalizedConstCount &= 0xFF;

                if (doCheck && hasDst && isConstCountAny && normalizedConstCount >= 0 && normalizedConstCount > dstBytes)
                {
                    Program.Warning(args[2].Source, "__memset count {0} exceeds fixed array '{1}' size {2} bytes (-Zcheck)", normalizedConstCount, dstName, dstBytes);
                    EmitAsm("JP", EnsureCheckTrap());
                    return;
                }

                // Size-specialized memset for small constant counts (<=16): unrolled stores.
                if (isConstCountAny && normalizedConstCount >= 0 && normalizedConstCount <= 16)
                {
                    if (normalizedConstCount == 0) return;

                    CompileIntoHL(args[0]);
                    EmitAsm("PUSH_HL");
                    CompileIntoA(args[1]);
                    EmitAsm("POP_HL");

                    for (int k = 0; k < normalizedConstCount; k++)
                        EmitAsm("LDI_HL_A");
                    return;
                }

                // Size-specialized memset for constant counts beyond 16
                if (isConstCountAny && normalizedConstCount >= 0)
                {
                    if (normalizedConstCount == 0) return;

                    if (normalizedConstCount > 16 && normalizedConstCount <= 64)
                    {
                        int blocks8 = normalizedConstCount / 8;
                        int rem = normalizedConstCount % 8;

                        CompileIntoHL(args[0]);
                        EmitAsm("PUSH_HL");
                        CompileIntoA(args[1]);
                        EmitAsm("POP_HL");

                        if (blocks8 > 0)
                        {
                            EmitAsm("LD_B_IMM", new AsmOperand(blocks8, AddressMode.Immediate));
                            // Emit groups of eight stores followed by the constant remainder for medium fills.
                            AsmOperand loop8 = MakeUniqueLabel("memset8_loop");
                            EmitLabel(loop8);
                            for (int k = 0; k < 8; k++) EmitAsm("LDI_HL_A");
                            EmitAsm("DEC_B");
                            EmitAsm("JP_NZ", loop8);
                        }
                        for (int k = 0; k < rem; k++) EmitAsm("LDI_HL_A");
                        return;
                    }

                    if (normalizedConstCount <= 255)
                    {
                        int blocks16 = normalizedConstCount / 16;
                        int rem = normalizedConstCount % 16;

                        CompileIntoHL(args[0]);
                        EmitAsm("PUSH_HL");
                        CompileIntoA(args[1]);
                        EmitAsm("POP_HL");

                        if (blocks16 > 0)
                        {
                            EmitAsm("LD_B_IMM", new AsmOperand(blocks16, AddressMode.Immediate));
                            // Use sixteen-store groups for larger byte-range constant fills.
                            AsmOperand loop16 = MakeUniqueLabel("memset16_loop");
                            EmitLabel(loop16);
                            for (int k = 0; k < 16; k++) EmitAsm("LDI_HL_A");
                            EmitAsm("DEC_B");
                            EmitAsm("JP_NZ", loop16);
                        }
                        for (int k = 0; k < rem; k++) EmitAsm("LDI_HL_A");
                        return;
                    }
                }

                if (useU8Count)
                {
                    CompileIntoHL(args[0]);
                    EmitAsm("PUSH_HL");

                    CompileIntoA(args[1]);
                    EmitAsm("LD_D_A");

                    CompileIntoA(args[2]);
                    EmitAsm("LD_B_A");
                    if (doCheck && hasDst)
                        EmitU8LeqConstOrTrapInA(dstBytes);

                    AsmOperand doneLabel = MakeUniqueLabel("memset_small_done");
                    AsmOperand smallLoopLabel = MakeUniqueLabel("memset_small_loop");
                    EmitAsm("OR_A");
                    EmitAsm("POP_HL");
                    EmitAsm("JP_Z", doneLabel);
                    EmitAsm("LD_A_D");
                    EmitLabel(smallLoopLabel);
                    EmitAsm("LDI_HL_A");
                    EmitAsm("DEC_B");
                    EmitAsm("JP_NZ", smallLoopLabel);
                    EmitLabel(doneLabel);
                    return;
                }

                CompileIntoHL(args[0]);
                EmitAsm("PUSH_HL");

                CompileIntoHL(args[2]);
                if (doCheck && hasDst && !isConstCountAny)
                    EmitU16LeqConstOrTrap(dstBytes);
                EmitAsm("LD_B_H");
                EmitAsm("LD_C_L");

                CompileIntoA(args[1]);
                EmitAsm("LD_D_A");

                EmitAsm("POP_HL");

                AsmOperand loopLabel = MakeUniqueLabel("memset_loop");
                AsmOperand wordDoneLabel = MakeUniqueLabel("memset_done");
                // A zero word count must skip the first write as well as the loop.
                EmitAsm("LD_A_B");
                EmitAsm("OR_C");
                EmitAsm("JP_Z", wordDoneLabel);
                EmitLabel(loopLabel);

                EmitAsm("LD_A_D");
                EmitAsm("LDI_HL_A");

                EmitAsm("DEC_BC");
                EmitAsm("LD_A_B");
                EmitAsm("OR_C");
                EmitAsm("JP_NZ", loopLabel);
                EmitLabel(wordDoneLabel);

                return;
            }

// kitaqgb/kitaqgb/Lowerer.cs:2145
if (expr.MatchAny(Tag.Call, out callFunc, out callArgs))
        {
            string knownCallName;
            if (callFunc != null && callFunc.Match(Tag.Name, out knownCallName) &&
                knownCallName == "__cgb_is_cgb" &&
                callArgs != null && callArgs.Length == 0 &&
                // A resolved target configuration can replace the zero-argument CGB query with a literal.
                Program.TryGetKnownCgbRuntimeValue(out int knownCgbValue))
            {
                return Expr.Make(Tag.Integer, knownCgbValue).WithSource(expr.Source);
            }

            Expr newFunc = LowerExpr(ctx, callFunc, false, prefix);

            // Optimization (post-A3): when we must spill a non-atomic argument into a temp,
            // prefer a u8 temp if the *parameter type* is u8-like.
            // This preserves semantics (implicit truncation to u8) while avoiding needless
            // u16 temporaries created by integer promotions.
            CType[] paramTypes = null;
            string __fname = null;
            if (newFunc.Match(Tag.Name, out __fname))
            {
                paramTypes = ctx.FindFuncParams(__fname);
                if (paramTypes == null)
                {
                    CType fnTypeFromName;
                    if (TryGetCallableFunctionType(ctx.FindTypeOfName(__fname), out fnTypeFromName))
                        paramTypes = fnTypeFromName.ParamTypes;
                }
            }
            else
            {
                CType fnTypeFromExpr;
                if (TryGetCallableFunctionType(InferType(ctx, newFunc), out fnTypeFromExpr))
                    paramTypes = fnTypeFromExpr.ParamTypes;
            }

            Expr[] newArgs = new Expr[callArgs.Length];
            for (int i = 0; i < callArgs.Length; i++)
            {
                Expr a = LowerExpr(ctx, callArgs[i], allowExtract, prefix);
                bool preserveConstantArgIntrinsic = false;
                if (a.MatchAny(Tag.Call, out Expr argFunc, out Expr[] _argCallArgs) &&
                    argFunc.Match(Tag.Name, out string argFuncName))
                {
                    preserveConstantArgIntrinsic = (argFuncName == "__bankof" || argFuncName == "__cgb_is_cgb");
                }
                // Keep evaluable constants and special intrinsic/bank operands visible to code generation instead of spilling them.
                bool preserveCompileTimeConstantArg = ctx.Global.TryEvalConstInt(a, out _);
                bool preserveFarcallBankArg = (__fname == "__farcall" && i == 0);

                if (allowExtract && !preserveFarcallBankArg && !preserveConstantArgIntrinsic && !preserveCompileTimeConstantArg && !IsAtom(a))
                {
                    // If we know the callee's parameter type, use it to choose a smaller temp.
                    // Example: (u8)(row*32) passed to a u8 param should spill as u8, not u16.
                    CType spillAs = null;
                    if (paramTypes != null && i < paramTypes.Length)
                    {
                        CType pt = paramTypes[i];
                        if (IsU8Like(pt)) spillAs = CType.UInt8;
                    }
                    if (spillAs == null) spillAs = InferType(ctx, a);
                    if (!IsAggregateType(spillAs) && SizeOfType(spillAs) <= 2)
                        a = SpillToTemp(ctx, a, spillAs, prefix);
                }
                newArgs[i] = a;
            }

            // Lint: const discard for argument passing
            // Warn when passing a const-qualified pointer (or const array decay) to a non-const pointer parameter.
            // Suppress when the argument has an explicit cast, or inside __unsafe.
            if (!ctx.InUnsafe && paramTypes != null)
            {
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    CType pt = paramTypes[i];
                    if (pt == null) continue;
                    if (!IsPointerToNonConst(pt)) continue;
                    if (HasTopLevelCast(newArgs[i])) continue;

                    CType at = InferType(ctx, newArgs[i]);
                    if (at != null && (IsPointerToConst(at) || IsArrayOfConst(at)))
                    {
                        string fn = __fname ?? "<call>";
                        WarnConstDiscard(expr, $"argument {i} to '{fn}'");
                        break; // keep it quiet: one warning per call is enough
                    }
                }
            }

            if (paramTypes != null)
            {
                string fn = __fname ?? "<call>";
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    FilePosition argPos = newArgs[i].Source;
                    if (string.IsNullOrEmpty(argPos.Filename) || argPos.Filename == "<unknown>")
                        argPos = expr.Source;
                    WarnImplicitNarrowingIfNeeded(ctx, newArgs[i], InferType(ctx, newArgs[i]), paramTypes[i], argPos,
                        "argument " + i + " to '" + fn + "'");
                }
            }

            // Lint: __enum_strict argument passing.
            if (paramTypes != null)
            {
                string fn = __fname ?? "<call>";
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    CType pt = paramTypes[i];
                    if (pt == null) continue;

                    CType at = InferType(ctx, newArgs[i]);
                    if (at == null) continue;

                    if (HasTopLevelCast(newArgs[i])) continue;

                    if (IsStrictEnum(pt))
                    {
                        if (!IsSameEnum(pt, at))
                        {
                            Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
                                "argument {0} to '{1}' expects __enum_strict {2}, got {3}; cast explicitly to silence",
                                i, fn, pt.Show(), at.Show());
                        }
                    }
                    else if (IsPlainInteger(pt) && IsStrictEnum(at))
                    {
                        Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
                            "argument {0} to '{1}' passes __enum_strict {2} to integer parameter {3}; cast explicitly to silence",
                            i, fn, at.Show(), pt.Show());
                    }
                }
            }

            if (!ctx.InUnsafe)
                RestrictAliasLint(ctx, newFunc, newArgs, expr.Source);

            // __slice(ptr,len) helper: represent a "slice" (ptr+len) in the IR.
            // Semantics: evaluate both arguments, result type is the pointer/array (same as arg0).
            // Bounds checks are injected only with -Zcheck (see Program.CheckSliceBounds).
            // Note: keep the intrinsic name conservative to avoid collisions ("slice" is allowed only
            // when there is no declared function named "slice").
            if (newFunc.Match(Tag.Name, out string __sfname))
            {
                bool isSliceIntrinsic = (__sfname == "__slice") || (__sfname == "slice" && ctx.FindFuncParams("slice") == null);
                if (isSliceIntrinsic)
                {
                    if (newArgs.Length != 2)
                    {
                        Program.Error(expr.Source, ErrorCode.ParseError, "{0}: expected 2 arguments: __slice(ptr,len)", __sfname);
                    }
                    else
                    {
                        CType pty = InferType(ctx, newArgs[0]);
                        if (pty == null || !(pty.IsPointer || pty.IsArray))
                            Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: first argument must be a pointer or array, got: {1}", __sfname, (pty == null ? "<unknown>" : pty.Show()));

                        CType lty = InferType(ctx, newArgs[1]);
                        if (lty == null || !lty.IsInteger)
                            Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: second argument must be an integer length, got: {1}", __sfname, (lty == null ? "<unknown>" : lty.Show()));
                    }

                    return Expr.Make(Tag.Slice, newArgs[0], newArgs[1]).WithSource(expr.Source);
                }
            }

            return MakeCall(newFunc, newArgs).WithSource(expr.Source);
        }
__memset_small

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

c
void __memset_small(void* dst, u8 value, u8 len);

Parameter

dst

Zeiger auf das erste beschreibbare Zielbyte. Stellen Sie genügend zusammenhängenden Speicher für die gesamte Operation bereit; ein Puffer wird nicht automatisch angelegt.

value

Bytewert 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.

len

Byteanzahl 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

c
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.

__memset_small: DMG
DMG
__memset_small: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\memory_intrinsics.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\memory_intrinsics.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung und Quelltext

kitaqgb/lib/rpg.h:176

kitaqgb/kitaqgb/CodeGenerator.cs:8926

csharp
if (funcName == "__memset" || funcName == "__memset_small")
            {
                // The small API truncates its count to a byte; the general API uses a word count.
                bool useU8Count = (funcName == "__memset_small");
                if (args.Length != 3) { Error(expr, "__memset requires 3 arguments"); return; }

                // -Zcheck: fixed-array range check for memset length
                bool doCheck = Program.CheckMemCopy && !InUnsafe;
                bool hasDst = false;
                int dstBytes = 0;
                string dstName = null;
                if (doCheck)
                    hasDst = TryGetFixedArrayByteLength(args[0], out dstBytes, out dstName);

                bool isConstCountAny = args[2].Match(Tag.Integer, out int constCountAny);
                int normalizedConstCount = constCountAny;
                if (isConstCountAny && useU8Count)
                    normalizedConstCount &= 0xFF;

                if (doCheck && hasDst && isConstCountAny && normalizedConstCount >= 0 && normalizedConstCount > dstBytes)
                {
                    Program.Warning(args[2].Source, "__memset count {0} exceeds fixed array '{1}' size {2} bytes (-Zcheck)", normalizedConstCount, dstName, dstBytes);
                    EmitAsm("JP", EnsureCheckTrap());
                    return;
                }

                // Size-specialized memset for small constant counts (<=16): unrolled stores.
                if (isConstCountAny && normalizedConstCount >= 0 && normalizedConstCount <= 16)
                {
                    if (normalizedConstCount == 0) return;

                    CompileIntoHL(args[0]);
                    EmitAsm("PUSH_HL");
                    CompileIntoA(args[1]);
                    EmitAsm("POP_HL");

                    for (int k = 0; k < normalizedConstCount; k++)
                        EmitAsm("LDI_HL_A");
                    return;
                }

                // Size-specialized memset for constant counts beyond 16
                if (isConstCountAny && normalizedConstCount >= 0)
                {
                    if (normalizedConstCount == 0) return;

                    if (normalizedConstCount > 16 && normalizedConstCount <= 64)
                    {
                        int blocks8 = normalizedConstCount / 8;
                        int rem = normalizedConstCount % 8;

                        CompileIntoHL(args[0]);
                        EmitAsm("PUSH_HL");
                        CompileIntoA(args[1]);
                        EmitAsm("POP_HL");

                        if (blocks8 > 0)
                        {
                            EmitAsm("LD_B_IMM", new AsmOperand(blocks8, AddressMode.Immediate));
                            // Emit groups of eight stores followed by the constant remainder for medium fills.
                            AsmOperand loop8 = MakeUniqueLabel("memset8_loop");
                            EmitLabel(loop8);
                            for (int k = 0; k < 8; k++) EmitAsm("LDI_HL_A");
                            EmitAsm("DEC_B");
                            EmitAsm("JP_NZ", loop8);
                        }
                        for (int k = 0; k < rem; k++) EmitAsm("LDI_HL_A");
                        return;
                    }

                    if (normalizedConstCount <= 255)
                    {
                        int blocks16 = normalizedConstCount / 16;
                        int rem = normalizedConstCount % 16;

                        CompileIntoHL(args[0]);
                        EmitAsm("PUSH_HL");
                        CompileIntoA(args[1]);
                        EmitAsm("POP_HL");

                        if (blocks16 > 0)
                        {
                            EmitAsm("LD_B_IMM", new AsmOperand(blocks16, AddressMode.Immediate));
                            // Use sixteen-store groups for larger byte-range constant fills.
                            AsmOperand loop16 = MakeUniqueLabel("memset16_loop");
                            EmitLabel(loop16);
                            for (int k = 0; k < 16; k++) EmitAsm("LDI_HL_A");
                            EmitAsm("DEC_B");
                            EmitAsm("JP_NZ", loop16);
                        }
                        for (int k = 0; k < rem; k++) EmitAsm("LDI_HL_A");
                        return;
                    }
                }

                if (useU8Count)
                {
                    CompileIntoHL(args[0]);
                    EmitAsm("PUSH_HL");

                    CompileIntoA(args[1]);
                    EmitAsm("LD_D_A");

                    CompileIntoA(args[2]);
                    EmitAsm("LD_B_A");
                    if (doCheck && hasDst)
                        EmitU8LeqConstOrTrapInA(dstBytes);

                    AsmOperand doneLabel = MakeUniqueLabel("memset_small_done");
                    AsmOperand smallLoopLabel = MakeUniqueLabel("memset_small_loop");
                    EmitAsm("OR_A");
                    EmitAsm("POP_HL");
                    EmitAsm("JP_Z", doneLabel);
                    EmitAsm("LD_A_D");
                    EmitLabel(smallLoopLabel);
                    EmitAsm("LDI_HL_A");
                    EmitAsm("DEC_B");
                    EmitAsm("JP_NZ", smallLoopLabel);
                    EmitLabel(doneLabel);
                    return;
                }

                CompileIntoHL(args[0]);
                EmitAsm("PUSH_HL");

                CompileIntoHL(args[2]);
                if (doCheck && hasDst && !isConstCountAny)
                    EmitU16LeqConstOrTrap(dstBytes);
                EmitAsm("LD_B_H");
                EmitAsm("LD_C_L");

                CompileIntoA(args[1]);
                EmitAsm("LD_D_A");

                EmitAsm("POP_HL");

                AsmOperand loopLabel = MakeUniqueLabel("memset_loop");
                AsmOperand wordDoneLabel = MakeUniqueLabel("memset_done");
                // A zero word count must skip the first write as well as the loop.
                EmitAsm("LD_A_B");
                EmitAsm("OR_C");
                EmitAsm("JP_Z", wordDoneLabel);
                EmitLabel(loopLabel);

                EmitAsm("LD_A_D");
                EmitAsm("LDI_HL_A");

                EmitAsm("DEC_BC");
                EmitAsm("LD_A_B");
                EmitAsm("OR_C");
                EmitAsm("JP_NZ", loopLabel);
                EmitLabel(wordDoneLabel);

                return;
            }

// kitaqgb/kitaqgb/Lowerer.cs:2145
if (expr.MatchAny(Tag.Call, out callFunc, out callArgs))
        {
            string knownCallName;
            if (callFunc != null && callFunc.Match(Tag.Name, out knownCallName) &&
                knownCallName == "__cgb_is_cgb" &&
                callArgs != null && callArgs.Length == 0 &&
                // A resolved target configuration can replace the zero-argument CGB query with a literal.
                Program.TryGetKnownCgbRuntimeValue(out int knownCgbValue))
            {
                return Expr.Make(Tag.Integer, knownCgbValue).WithSource(expr.Source);
            }

            Expr newFunc = LowerExpr(ctx, callFunc, false, prefix);

            // Optimization (post-A3): when we must spill a non-atomic argument into a temp,
            // prefer a u8 temp if the *parameter type* is u8-like.
            // This preserves semantics (implicit truncation to u8) while avoiding needless
            // u16 temporaries created by integer promotions.
            CType[] paramTypes = null;
            string __fname = null;
            if (newFunc.Match(Tag.Name, out __fname))
            {
                paramTypes = ctx.FindFuncParams(__fname);
                if (paramTypes == null)
                {
                    CType fnTypeFromName;
                    if (TryGetCallableFunctionType(ctx.FindTypeOfName(__fname), out fnTypeFromName))
                        paramTypes = fnTypeFromName.ParamTypes;
                }
            }
            else
            {
                CType fnTypeFromExpr;
                if (TryGetCallableFunctionType(InferType(ctx, newFunc), out fnTypeFromExpr))
                    paramTypes = fnTypeFromExpr.ParamTypes;
            }

            Expr[] newArgs = new Expr[callArgs.Length];
            for (int i = 0; i < callArgs.Length; i++)
            {
                Expr a = LowerExpr(ctx, callArgs[i], allowExtract, prefix);
                bool preserveConstantArgIntrinsic = false;
                if (a.MatchAny(Tag.Call, out Expr argFunc, out Expr[] _argCallArgs) &&
                    argFunc.Match(Tag.Name, out string argFuncName))
                {
                    preserveConstantArgIntrinsic = (argFuncName == "__bankof" || argFuncName == "__cgb_is_cgb");
                }
                // Keep evaluable constants and special intrinsic/bank operands visible to code generation instead of spilling them.
                bool preserveCompileTimeConstantArg = ctx.Global.TryEvalConstInt(a, out _);
                bool preserveFarcallBankArg = (__fname == "__farcall" && i == 0);

                if (allowExtract && !preserveFarcallBankArg && !preserveConstantArgIntrinsic && !preserveCompileTimeConstantArg && !IsAtom(a))
                {
                    // If we know the callee's parameter type, use it to choose a smaller temp.
                    // Example: (u8)(row*32) passed to a u8 param should spill as u8, not u16.
                    CType spillAs = null;
                    if (paramTypes != null && i < paramTypes.Length)
                    {
                        CType pt = paramTypes[i];
                        if (IsU8Like(pt)) spillAs = CType.UInt8;
                    }
                    if (spillAs == null) spillAs = InferType(ctx, a);
                    if (!IsAggregateType(spillAs) && SizeOfType(spillAs) <= 2)
                        a = SpillToTemp(ctx, a, spillAs, prefix);
                }
                newArgs[i] = a;
            }

            // Lint: const discard for argument passing
            // Warn when passing a const-qualified pointer (or const array decay) to a non-const pointer parameter.
            // Suppress when the argument has an explicit cast, or inside __unsafe.
            if (!ctx.InUnsafe && paramTypes != null)
            {
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    CType pt = paramTypes[i];
                    if (pt == null) continue;
                    if (!IsPointerToNonConst(pt)) continue;
                    if (HasTopLevelCast(newArgs[i])) continue;

                    CType at = InferType(ctx, newArgs[i]);
                    if (at != null && (IsPointerToConst(at) || IsArrayOfConst(at)))
                    {
                        string fn = __fname ?? "<call>";
                        WarnConstDiscard(expr, $"argument {i} to '{fn}'");
                        break; // keep it quiet: one warning per call is enough
                    }
                }
            }

            if (paramTypes != null)
            {
                string fn = __fname ?? "<call>";
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    FilePosition argPos = newArgs[i].Source;
                    if (string.IsNullOrEmpty(argPos.Filename) || argPos.Filename == "<unknown>")
                        argPos = expr.Source;
                    WarnImplicitNarrowingIfNeeded(ctx, newArgs[i], InferType(ctx, newArgs[i]), paramTypes[i], argPos,
                        "argument " + i + " to '" + fn + "'");
                }
            }

            // Lint: __enum_strict argument passing.
            if (paramTypes != null)
            {
                string fn = __fname ?? "<call>";
                for (int i = 0; i < newArgs.Length && i < paramTypes.Length; i++)
                {
                    CType pt = paramTypes[i];
                    if (pt == null) continue;

                    CType at = InferType(ctx, newArgs[i]);
                    if (at == null) continue;

                    if (HasTopLevelCast(newArgs[i])) continue;

                    if (IsStrictEnum(pt))
                    {
                        if (!IsSameEnum(pt, at))
                        {
                            Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
                                "argument {0} to '{1}' expects __enum_strict {2}, got {3}; cast explicitly to silence",
                                i, fn, pt.Show(), at.Show());
                        }
                    }
                    else if (IsPlainInteger(pt) && IsStrictEnum(at))
                    {
                        Program.Warning(expr.Source, ErrorCode.EnumStrictMix,
                            "argument {0} to '{1}' passes __enum_strict {2} to integer parameter {3}; cast explicitly to silence",
                            i, fn, at.Show(), pt.Show());
                    }
                }
            }

            if (!ctx.InUnsafe)
                RestrictAliasLint(ctx, newFunc, newArgs, expr.Source);

            // __slice(ptr,len) helper: represent a "slice" (ptr+len) in the IR.
            // Semantics: evaluate both arguments, result type is the pointer/array (same as arg0).
            // Bounds checks are injected only with -Zcheck (see Program.CheckSliceBounds).
            // Note: keep the intrinsic name conservative to avoid collisions ("slice" is allowed only
            // when there is no declared function named "slice").
            if (newFunc.Match(Tag.Name, out string __sfname))
            {
                bool isSliceIntrinsic = (__sfname == "__slice") || (__sfname == "slice" && ctx.FindFuncParams("slice") == null);
                if (isSliceIntrinsic)
                {
                    if (newArgs.Length != 2)
                    {
                        Program.Error(expr.Source, ErrorCode.ParseError, "{0}: expected 2 arguments: __slice(ptr,len)", __sfname);
                    }
                    else
                    {
                        CType pty = InferType(ctx, newArgs[0]);
                        if (pty == null || !(pty.IsPointer || pty.IsArray))
                            Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: first argument must be a pointer or array, got: {1}", __sfname, (pty == null ? "<unknown>" : pty.Show()));

                        CType lty = InferType(ctx, newArgs[1]);
                        if (lty == null || !lty.IsInteger)
                            Program.Error(expr.Source, ErrorCode.ExpectedType, "{0}: second argument must be an integer length, got: {1}", __sfname, (lty == null ? "<unknown>" : lty.Show()));
                    }

                    return Expr.Make(Tag.Slice, newArgs[0], newArgs[1]).WithSource(expr.Source);
                }
            }

            return MakeCall(newFunc, newArgs).WithSource(expr.Source);
        }
__rle_decode_vram

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

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

Parameter

dst

16-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.

bank

ROM-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.

src

CPU-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

c
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.

__rle_decode_vram: DMG
DMG
__rle_decode_vram: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/rpg.h:191

Deklarationshinweise im Original
Compiler intrinsic, separate from the RAM rle_decode helpers below; it is not a bounded-stream API.

kitaqgb/kitaqgb/CodeGenerator.cs:8673

csharp
if (funcName == "__rle_decode_vram")
            {
                if (args.Length != 3) Error(expr, "__rle_decode_vram(dst, bank, src) expects 3 arguments");

                CompileIntoHL(args[0]); EmitAsm("PUSH_HL"); // dst
                CompileIntoA(args[1]); EmitPushAAsWord(); // bank
                CompileIntoHL(args[2]); EmitAsm("PUSH_HL"); // src
                EmitAsm("ADD_SP_IMM", new AsmOperand(-4, AddressMode.Relative)); // total(2) + scratch(2)

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("XOR_A");
                EmitAsm("LD_HL_A");
                EmitAsm("INC_HL");
                EmitAsm("LD_HL_A");

                AsmOperand loop = MakeUniqueLabel("rlevram_loop");
                // Keep total, scratch, source, bank and destination in stack slots across repeated far-memory calls.
                AsmOperand countDone = MakeUniqueLabel("rlevram_count_inc_done");
                AsmOperand valueDone = MakeUniqueLabel("rlevram_value_inc_done");
                AsmOperand finished = MakeUniqueLabel("rlevram_finished");

                EmitLabel(loop);

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

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

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

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

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(2, AddressMode.Relative));
                EmitAsm("LD_A_HL"); // count
                EmitAsm("OR_A");
                EmitAsm("JP_Z", finished);
                EmitAsm("LD_HL_SP_IMM", new AsmOperand(3, AddressMode.Relative));
                EmitAsm("LD_HL_A"); // save count in scratch+1

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("ADD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
                EmitAsm("LD_HL_A");
                EmitAsm("JR_NC", countDone);
                EmitAsm("INC_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("ADC_IMM", new AsmOperand(0, AddressMode.Immediate));
                EmitAsm("LD_HL_A");
                EmitLabel(countDone);

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

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

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

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

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(2, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_D_A"); // value

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(3, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_C_A");
                EmitAsm("LD_B_IMM", new AsmOperand(0, AddressMode.Immediate)); // count

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(8, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_E_A");
                EmitAsm("INC_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("LD_D_A");
                EmitAsm("LD_H_D");
                EmitAsm("LD_L_E"); // dst
                // Loading dst used D for its high byte. Recover the run value
                // from stack scratch while preserving the completed HL pointer.
                EmitAsm("PUSH_HL");
                EmitAsm("LD_HL_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_D_A");
                EmitAsm("POP_HL");

                // Use the LCD-aware fill loop for each decoded run, then store its advanced destination pointer.
                EmitVramMemsetLoop(true, "rlevram_fill");

                EmitAsm("PUSH_HL");
                EmitAsm("LD_HL_SP_IMM", new AsmOperand(10, AddressMode.Relative));
                EmitAsm("POP_DE");
                EmitAsm("LD_A_E");
                EmitAsm("LD_HL_A");
                EmitAsm("INC_HL");
                EmitAsm("LD_A_D");
                EmitAsm("LD_HL_A"); // save dst

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(3, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_B_A"); // count
                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("ADD_B");
                EmitAsm("LD_HL_A");
                EmitAsm("INC_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("ADC_IMM", new AsmOperand(0, AddressMode.Immediate));
                EmitAsm("LD_HL_A"); // total += count

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("ADD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
                EmitAsm("LD_HL_A");
                EmitAsm("JR_NC", valueDone);
                EmitAsm("INC_HL");
                EmitAsm("LD_A_HL");
                EmitAsm("ADC_IMM", new AsmOperand(0, AddressMode.Immediate));
                EmitAsm("LD_HL_A");
                EmitLabel(valueDone);

                EmitAsm("JP", loop);

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

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

c
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

c
__rng_seed(0x1234);
first=__rng8();  // 2: the first draw from this seed on GB.
second=__rng8(); // 34: a second draw advances the same shared state.

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.

__rng8: DMG
DMG
__rng8: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rng_values.c .\kitaqgb\lib\rng.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rng_values.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung und Quelltext

kitaqgb/lib/rpg.h:149

kitaqgb/kitaqgb/CodeGenerator.cs:8881

csharp
if (funcName == "__rng8")
            {
                if (args.Length != 0) Error(expr, "__rng8() expects 0 arguments");

                EmitLoadA(RngStateLoVar);
                EmitAsm("LD_B_A");
                EmitLoadA(RngStateHiVar);
                EmitAsm("LD_C_A");

                // Zero state fallback for deterministic startup.
                AsmOperand rngSeeded = MakeUniqueLabel("rng_seeded");
                EmitAsm("LD_A_B");
                EmitAsm("OR_C");
                EmitAsm("JR_NZ", rngSeeded);
                EmitAsm("LD_A_IMM", new AsmOperand(0xA5, AddressMode.Immediate));
                EmitAsm("LD_B_A");
                EmitAsm("LD_A_IMM", new AsmOperand(0x5A, AddressMode.Immediate));
                EmitAsm("LD_C_A");
                EmitLabel(rngSeeded);

                // 16-bit mixed add/xor/rotate step.
                EmitAsm("LD_A_B");
                EmitAsm("ADD_A_IMM", new AsmOperand(0x17, AddressMode.Immediate));
                EmitAsm("LD_B_A");
                EmitAsm("LD_A_C");
                EmitAsm("ADC_IMM", new AsmOperand(0x5D, AddressMode.Immediate));
                EmitAsm("LD_C_A");
                EmitAsm("LD_A_B");
                EmitAsm("XOR_C");
                EmitAsm("LD_B_A");
                EmitAsm("LD_A_C");
                EmitAsm("RLCA");
                EmitAsm("ADD_B");
                EmitAsm("LD_C_A");

                EmitAsm("LD_A_B");
                EmitStoreA(RngStateLoVar);
                EmitAsm("LD_A_C");
                EmitStoreA(RngStateHiVar);
                EmitAsm("LD_A_C");
                return;
            }
__rng_seed

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

c
void __rng_seed(u16 seed);

Parameter

seed

Vorzeichenloser 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

c
__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.

__rng_seed: DMG
DMG
__rng_seed: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\rng_values.c .\kitaqgb\lib\rng.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\rng_values.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung und Quelltext

kitaqgb/lib/rpg.h:150

kitaqgb/kitaqgb/CodeGenerator.cs:8868

csharp
if (funcName == "__rng_seed")
            {
                if (args.Length != 1) Error(expr, "__rng_seed(seed) expects 1 argument");

                CompileIntoHL(args[0]);
                EmitAsm("LD_A_L");
                EmitStoreA(RngStateLoVar);
                EmitAsm("LD_A_H");
                EmitStoreA(RngStateHiVar);
                return;
            }
__scroll_bg_set

Schreibt Hintergrund-SCX/SCY sofort und synchronisiert aktuelle sowie vorgemerkte Softwarewerte.

Syntax

c
void __scroll_bg_set(u8 scx, u8 scy);

Parameter

scx

Horizontale Verschiebung, 0..255 Pixel.

scy

Vertikale 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

c
__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.

__scroll_bg_set: DMG
DMG
__scroll_bg_set: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_display.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_display.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implementierung und Quelltext

kitaqgb/lib/rpg.h:151

kitaqgb/kitaqgb/CodeGenerator.cs:7507

csharp
if (funcName == "__scroll_bg_set")
            {
                if (args.Length != 2) Error(expr, "__scroll_bg_set(scx, scy) expects 2 arguments");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitPushAAsWord();
                CompileIntoA(args[1]);
                EmitAsm("LD_B_A");
                EmitAsm("POP_HL");

                EmitAsm("LD_A_L");
                EmitStoreA(ScrollBgXCurVar);
                EmitStoreA(ScrollBgXNextVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem)); // SCX

                EmitAsm("LD_A_B");
                EmitStoreA(ScrollBgYCurVar);
                EmitStoreA(ScrollBgYNextVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem)); // SCY

                EmitLoadA(ScrollDirtyVar);
                EmitAsm("AND_IMM", new AsmOperand(0xFF ^ ScrollDirtyBgMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__settile_col

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

c
void __settile_col(u8 x, u8 y, const u8* src, u8 len);

Parameter

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

src

Lü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.

len

Byteanzahl 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

c
// __settile_col
// Copy `len` successive tile numbers from `src` vertically, starting at `(x,y)` in the
// LCDC-selected BG map. The source advances one byte and the destination 32 bytes per
// tile. Waits for writable VRAM when necessary. The LCD-on path uses interrupt masking
// and can enable interrupts afterward; it is not a promise to preserve the caller's
// interrupt state or to finish within one VBlank.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
const u8 example_tiles[] = {'A','B','C'};

void main() {
    u8 failures;
    failures = 0;
    tile_color_example_begin();
    __settile_col(2, 3, example_tiles, 3);
    if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
    if (tile_example_read(0x9CA2, 0) != 'C') { failures++; }
    if (tile_example_read(0x9C63, 0) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

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.

text
A
B
C
__settile_col: DMG
DMG
__settile_col: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settile_col.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settile_col.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implementierung 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

c
void __settile_rect(u8 x, u8 y, u8 w, u8 h, u8 tile);

Parameter

x, y

Koordinaten 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, h

Breite und Höhe in 8×8-Tiles. Es müssen x+w <= 32 und y+h <= 32 gelten. Bei einer Nulldimension wird nichts geschrieben.

tile

Tile-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

c
// __settile_rect
// Fill a `w` by `h` rectangle in the LCDC-selected BG map with one tile number. Rows are
// 32 bytes apart; this repeats a tile, rather than copying an image array. Waits for
// writable VRAM when necessary. The LCD-on path uses interrupt masking and can enable
// interrupts afterward; it is not a promise to preserve the caller's interrupt state or
// to finish within one VBlank.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"

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

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.

text
AAA
AAA
__settile_rect: DMG
DMG
__settile_rect: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settile_rect.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settile_rect.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implementierung 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

c
void __settile_row(u8 x, u8 y, const u8* src, u8 len);

Parameter

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

src

Lü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.

len

Byteanzahl 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

c
// __settile_row
// Copy `len` successive tile numbers from `src` horizontally, starting at `(x,y)` in the
// LCDC-selected BG map. Source and destination advance by one byte per tile. Waits for
// writable VRAM when necessary. The LCD-on path uses interrupt masking and can enable
// interrupts afterward; it is not a promise to preserve the caller's interrupt state or
// to finish within one VBlank.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
const u8 example_tiles[] = {'A','B','C'};

void main() {
    u8 failures;
    failures = 0;
    tile_color_example_begin();
    __settile_row(2, 3, example_tiles, 3);
    if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
    if (tile_example_read(0x9C64, 0) != 'C') { failures++; }
    if (tile_example_read(0x9C82, 0) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

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.

text
ABC
__settile_row: DMG
DMG
__settile_row: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settile_row.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settile_row.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implementierung 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

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

Parameter

x, y

Koordinaten in 8×8-Tiles, jeweils von 0 bis 31. Der gesamte Schreibbereich muss in der 32×32-Karte liegen; Koordinaten werden nicht automatisch begrenzt.

tile

Tile-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

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

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

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.

text
A
__settile_xy: DMG
DMG
__settile_xy: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settile_xy.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settile_xy.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implementierung 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

c
void __settilemap_rect(u16 base, u8 x, u8 y, u8 w, u8 h, const u8* src);

Parameter

base

base wählt die Karte (0x9800 oder 0x9C00); die Byteadresse lautet base + 32*y + x.

x, y

Koordinaten 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, h

Breite und Höhe in 8×8-Tiles. Es müssen x+w <= 32 und y+h <= 32 gelten. Bei einer Nulldimension wird nichts geschrieben.

src

Lü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

c
// __settilemap_rect
// Copy a tightly packed `w*h` array of tile numbers to a rectangle at `(x,y)` in the map
// selected by `base`. Source rows have `w` bytes; destination rows have a 32-byte
// stride. Waits for writable VRAM when necessary. The LCD-on path uses interrupt masking
// and can enable interrupts afterward; it is not a promise to preserve the caller's
// interrupt state or to finish within one VBlank.
// Expected: FAILED CHECKS 000, with the written tile data in the selected map.
#include "gb_tile_color_example.h"
const u8 example_tiles[] = {'A','B','C','D','E','F'};

void main() {
    u8 failures;
    failures = 0;
    tile_color_example_begin();
    __settilemap_rect(0x9C00, 2, 3, 3, 2, example_tiles);
    if (tile_example_read(0x9C62, 0) != 'A') { failures++; }
    if (tile_example_read(0x9C84, 0) != 'F') { failures++; }
    if (tile_example_read(0x9C65, 0) != 0) { failures++; }
    tile_example_finish(failures, 1);
}

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.

text
ABC
DEF
__settilemap_rect: DMG
DMG
__settilemap_rect: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\__settilemap_rect.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\__settilemap_rect.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-disasm
Implementierung und Quelltext

kitaqgb/lib/rpg.h:158

__tile_addr

Berechnet die CPU-Adresse einer Zelle in einer 32 Kacheln breiten Karte.

Syntax

c
u16 __tile_addr(u16 base, u8 x, u8 y);

Parameter

base

Kachelkartenbasis, gewöhnlich $9800/$9C00.

x

Spalte, gewöhnlich 0..31.

y

Zeile, gewöhnlich 0..31.

Rückgabewert

Gibt (base + y*32 + x) modulo 65536 ohne Speicherzugriff zurück.

Anwendungsbeispiel

c
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.

__tile_addr: DMG
DMG
__tile_addr: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_display.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_display.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implementierung und Quelltext

kitaqgb/lib/rpg.h:180

kitaqgb/kitaqgb/CodeGenerator.cs:8355

csharp
if (funcName == "__tile_addr")
            {
                if (args.Length != 3) Error(expr, "__tile_addr(base, x, y) expects 3 arguments");

                EmitTileMapAddressIntoHL(args[0], args[1], args[2]);
                LastCallReturnsHL = true;
                return;
            }
__vram_copy_dma

__vram_copy_dma 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

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

Parameter

dst

Adresse 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.

src

Zeiger 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.

len

Anzahl 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

c
__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.

__vram_copy_dma: DMG
DMG
__vram_copy_dma: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\vram_memory_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_memory_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung 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

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

Parameter

dst

Adresse 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.

src

Zeiger 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.

len

Anzahl 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

c
__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.

__vram_copy_hblank: DMG
DMG
__vram_copy_hblank: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\vram_memory_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_memory_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung 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

c
void __vram_fill(u16 dst, u8 value, u16 len);

Parameter

dst

Adresse 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.

value

Ein 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.

len

Anzahl 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

c
__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.

__vram_fill: DMG
DMG
__vram_fill: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\vram_memory_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_memory_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung 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

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

Parameter

dst

Adresse 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.

src

Zeiger 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.

len

Anzahl 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

c
__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.

__vram_memcpy: DMG
DMG
__vram_memcpy: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\vram_memory_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_memory_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung 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

c
void __vram_memset(u16 dst, u8 value, u16 len);

Parameter

dst

Adresse 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.

value

Ein 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.

len

Anzahl 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

c
__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.

__vram_memset: DMG
DMG
__vram_memset: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\vram_memory_shapes.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\vram_memory_shapes.gb --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --no-cache --no-disasm
Implementierung 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

c
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

c
__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.

__wait_vblank: DMG
DMG
__wait_vblank: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_display.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_display.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implementierung und Quelltext

kitaqgb/lib/rpg.h:138

kitaqgb/kitaqgb/CodeGenerator.cs:7451

csharp
if (funcName == "__wait_vblank")
            {
                if (args.Length != 0) Error(expr, "__wait_vblank() expects 0 arguments");

                AsmOperand wbRet = MakeUniqueLabel("waitvb_ret");
                AsmOperand wbExitVblank = MakeUniqueLabel("waitvb_exit");
                AsmOperand wbEnterVblank = MakeUniqueLabel("waitvb_enter");

                // If LCD is off, return immediately.
                EmitAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem)); // LCDC
                EmitAsm("AND_IMM", new AsmOperand(0x80, AddressMode.Immediate));
                EmitAsm("JR_Z", wbRet);

                // while (LY >= 144) {}
                EmitLabel(wbExitVblank);
                EmitAsm("LDH_A_MEM", new AsmOperand(0x44, AddressMode.HighMem)); // LY
                EmitAsm("CP_IMM", new AsmOperand(144, AddressMode.Immediate));
                EmitAsm("JR_C", wbEnterVblank);
                EmitAsm("JR", wbExitVblank);

                // while (LY < 144) {}
                EmitLabel(wbEnterVblank);
                EmitAsm("LDH_A_MEM", new AsmOperand(0x44, AddressMode.HighMem)); // LY
                EmitAsm("CP_IMM", new AsmOperand(144, AddressMode.Immediate));
                EmitAsm("JR_C", wbEnterVblank);

                EmitLabel(wbRet);
                return;
            }
__xy_in_rect

Prüft die Zugehörigkeit zu einem Rechteck einschließlich oberer/linker und ausschließlich unterer/rechter Kante.

Syntax

c
u8 __xy_in_rect(u8 x, u8 y, u8 rx, u8 ry, u8 rw, u8 rh);

Parameter

x

Punkt-X, 0..255.

y

Punkt-Y, 0..255.

rx

Linke Rechteckkante, 0..255.

ry

Obere Rechteckkante, 0..255.

rw

Breite, 0..255.

rh

Hö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

c
result[0]=__xy_in_rect(10,20,10,20,3,2);result[1]=__xy_in_rect(13,20,10,20,3,2);result[2]=__xy_in_rect(9,20,10,20,3,2);result[3]=__xy_in_rect(255,1,250,0,10,2);result[4]=__xy_in_rect(10,20,10,20,0,2);

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.

__xy_in_rect: DMG
DMG
__xy_in_rect: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\batch3_coordinates.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\batch3_coordinates.gb --no-cache --no-disasm --profile=dev --rst-disable --stack-bank=fixed --cgb=cgb --cart=mbc5 --romsize=64k --ramsize=none
Implementierung und Quelltext

kitaqgb/lib/rpg.h:182

kitaqgb/kitaqgb/CodeGenerator.cs:8436

csharp
if (funcName == "__xy_in_rect")
            {
                if (args.Length != 6) Error(expr, "__xy_in_rect(x, y, rx, ry, rw, rh) expects 6 arguments");

                Expr[] foldedRectArgs = args.Select(FoldConstants).ToArray();
                if (foldedRectArgs[0].Match(Tag.Integer, out int constX) &&
                    foldedRectArgs[1].Match(Tag.Integer, out int constY) &&
                    foldedRectArgs[2].Match(Tag.Integer, out int constRx) &&
                    foldedRectArgs[3].Match(Tag.Integer, out int constRy) &&
                    foldedRectArgs[4].Match(Tag.Integer, out int constRw) &&
                    foldedRectArgs[5].Match(Tag.Integer, out int constRh))
                {
                    int x = constX & 0xFF;
                    int y = constY & 0xFF;
                    int rx = constRx & 0xFF;
                    int ry = constRy & 0xFF;
                    int rw = constRw & 0xFF;
                    int rh = constRh & 0xFF;
                    bool inside = x >= rx && y >= ry && (x - rx) < rw && (y - ry) < rh;
                    if (inside) EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
                    else EmitAsm("XOR_A");
                    return;
                }

                EmitPushExprAsWord(args[0]); // x
                EmitPushExprAsWord(args[1]); // y
                EmitPushExprAsWord(args[2]); // rx
                EmitPushExprAsWord(args[3]); // ry
                EmitPushExprAsWord(args[4]); // rw
                EmitPushExprAsWord(args[5]); // rh

                AsmOperand ret0 = MakeUniqueLabel("xyrect_ret0");
                AsmOperand done = MakeUniqueLabel("xyrect_done");

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(10, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_B_A"); // x
                EmitAsm("LD_HL_SP_IMM", new AsmOperand(6, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_C_A"); // rx
                EmitAsm("LD_A_B");
                EmitAsm("CP_C");
                EmitAsm("JR_C", ret0);
                EmitAsm("SUB_C");
                EmitAsm("LD_B_A");
                EmitAsm("LD_HL_SP_IMM", new AsmOperand(2, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_C_A"); // rw
                EmitAsm("LD_A_B");
                EmitAsm("CP_C");
                EmitAsm("JR_NC", ret0);

                EmitAsm("LD_HL_SP_IMM", new AsmOperand(8, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_B_A"); // y
                EmitAsm("LD_HL_SP_IMM", new AsmOperand(4, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_C_A"); // ry
                EmitAsm("LD_A_B");
                EmitAsm("CP_C");
                EmitAsm("JR_C", ret0);
                EmitAsm("SUB_C");
                EmitAsm("LD_B_A");
                EmitAsm("LD_HL_SP_IMM", new AsmOperand(0, AddressMode.Relative));
                EmitAsm("LD_A_HL");
                EmitAsm("LD_C_A"); // rh
                EmitAsm("LD_A_B");
                EmitAsm("CP_C");
                EmitAsm("JR_NC", ret0);

                EmitAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate));
                EmitAsm("JR", done);
                EmitLabel(ret0);
                EmitAsm("XOR_A");
                EmitLabel(done);
                EmitAsm("ADD_SP_IMM", new AsmOperand(12, AddressMode.Relative));
                return;
            }

scroll.h — 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

c
void __scroll_bg_add(s8 dx, s8 dy);

Parameter

dx

Vorzeichenbehaftete horizontale Pixelverschiebung, −128 bis 127.

dy

Vorzeichenbehaftete 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

c
__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.

__scroll_bg_add: DMG
DMG
__scroll_bg_add: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

Anwendungsbeispiel

c
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.

__scroll_bg_add: DMG
DMG
__scroll_bg_add: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/scroll.h:37

kitaqgb/kitaqgb/CodeGenerator.cs:7835

csharp
if (funcName == "__scroll_bg_add")
            {
                if (args.Length != 2) Error(expr, "__scroll_bg_add(dx, dy) expects 2 arguments");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitPushAAsWord();
                CompileIntoA(args[1]);
                EmitAsm("LD_C_A");
                EmitAsm("POP_HL");
                EmitAsm("LD_B_L");

                EmitLoadA(ScrollBgXCurVar);
                EmitAsm("ADD_B");
                EmitStoreA(ScrollBgXCurVar);
                EmitStoreA(ScrollBgXNextVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem)); // SCX

                EmitLoadA(ScrollBgYCurVar);
                EmitAsm("ADD_C");
                EmitStoreA(ScrollBgYCurVar);
                EmitStoreA(ScrollBgYNextVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem)); // SCY

                EmitLoadA(ScrollDirtyVar);
                EmitAsm("AND_IMM", new AsmOperand(0xFF ^ ScrollDirtyBgMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__scroll_bg_set_buffered

Merkt beide Hintergrund-Scrollkoordinaten zur späteren Übertragung vor.

Syntax

c
void __scroll_bg_set_buffered(u8 scx, u8 scy);

Parameter

scx

Horizontale Hintergrundverschiebung, 0–255 Pixel; ein größerer Wert bewegt den Hintergrund nach links.

scy

Vertikale 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

c
__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.

__scroll_bg_set_buffered: DMG
DMG
__scroll_bg_set_buffered: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

Anwendungsbeispiel

c
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.

__scroll_bg_set_buffered: DMG
DMG
__scroll_bg_set_buffered: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/scroll.h:22

kitaqgb/kitaqgb/CodeGenerator.cs:7671

csharp
if (funcName == "__scroll_bg_set_buffered")
            {
                if (args.Length != 2) Error(expr, "__scroll_bg_set_buffered(scx, scy) expects 2 arguments");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitPushAAsWord();
                CompileIntoA(args[1]);
                EmitAsm("LD_B_A");
                EmitAsm("POP_HL");

                EmitAsm("LD_A_L");
                EmitStoreA(ScrollBgXNextVar);
                EmitAsm("LD_A_B");
                EmitStoreA(ScrollBgYNextVar);
                EmitLoadA(ScrollDirtyVar);
                EmitAsm("OR_IMM", new AsmOperand(ScrollDirtyBgMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__scroll_bg_x_get

Liest den nachgeführten aktuellen Wert der horizontalen Hintergrundverschiebung.

Syntax

c
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

c
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.

__scroll_bg_x_get: DMG
DMG
__scroll_bg_x_get: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

Anwendungsbeispiel

c
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.

__scroll_bg_x_get: DMG
DMG
__scroll_bg_x_get: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/scroll.h:13

kitaqgb/kitaqgb/CodeGenerator.cs:7571

csharp
if (funcName == "__scroll_bg_x_get")
            {
                if (args.Length != 0) Error(expr, "__scroll_bg_x_get() expects 0 arguments");
                EnsureScrollState();
                EmitLoadA(ScrollBgXCurVar);
                return;
            }
__scroll_bg_x_set

Setzt die horizontale Hintergrundverschiebung sofort.

Syntax

c
void __scroll_bg_x_set(u8 scx);

Parameter

scx

Horizontale 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

c
__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.

__scroll_bg_x_set: DMG
DMG
__scroll_bg_x_set: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

Anwendungsbeispiel

c
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.

__scroll_bg_x_set: DMG
DMG
__scroll_bg_x_set: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/scroll.h:11

kitaqgb/kitaqgb/CodeGenerator.cs:7535

csharp
if (funcName == "__scroll_bg_x_set")
            {
                if (args.Length != 1) Error(expr, "__scroll_bg_x_set(scx) expects 1 argument");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitStoreA(ScrollBgXCurVar);
                EmitStoreA(ScrollBgXNextVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem)); // SCX
                EmitLoadA(ScrollBgYCurVar);
                EmitStoreA(ScrollBgYNextVar);
                EmitLoadA(ScrollDirtyVar);
                EmitAsm("AND_IMM", new AsmOperand(0xFF ^ ScrollDirtyBgMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__scroll_bg_x_set_buffered

Merkt nur Hintergrund-X vor.

Syntax

c
void __scroll_bg_x_set_buffered(u8 scx);

Parameter

scx

Horizontale 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

c
__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.

__scroll_bg_x_set_buffered: DMG
DMG
__scroll_bg_x_set_buffered: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

Anwendungsbeispiel

c
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.

__scroll_bg_x_set_buffered: DMG
DMG
__scroll_bg_x_set_buffered: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/scroll.h:23

kitaqgb/kitaqgb/CodeGenerator.cs:7693

csharp
if (funcName == "__scroll_bg_x_set_buffered")
            {
                if (args.Length != 1) Error(expr, "__scroll_bg_x_set_buffered(scx) expects 1 argument");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitStoreA(ScrollBgXNextVar);
                EmitLoadA(ScrollDirtyVar);
                EmitAsm("OR_IMM", new AsmOperand(ScrollDirtyBgMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__scroll_bg_y_get

Liest den nachgeführten aktuellen Wert der vertikalen Hintergrundverschiebung.

Syntax

c
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

c
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.

__scroll_bg_y_get: DMG
DMG
__scroll_bg_y_get: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

Anwendungsbeispiel

c
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.

__scroll_bg_y_get: DMG
DMG
__scroll_bg_y_get: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/scroll.h:14

kitaqgb/kitaqgb/CodeGenerator.cs:7580

csharp
if (funcName == "__scroll_bg_y_get")
            {
                if (args.Length != 0) Error(expr, "__scroll_bg_y_get() expects 0 arguments");
                EnsureScrollState();
                EmitLoadA(ScrollBgYCurVar);
                return;
            }
__scroll_bg_y_set

Setzt die vertikale Hintergrundverschiebung sofort.

Syntax

c
void __scroll_bg_y_set(u8 scy);

Parameter

scy

Vertikale 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

c
__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.

__scroll_bg_y_set: DMG
DMG
__scroll_bg_y_set: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

Anwendungsbeispiel

c
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.

__scroll_bg_y_set: DMG
DMG
__scroll_bg_y_set: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/scroll.h:12

kitaqgb/kitaqgb/CodeGenerator.cs:7553

csharp
if (funcName == "__scroll_bg_y_set")
            {
                if (args.Length != 1) Error(expr, "__scroll_bg_y_set(scy) expects 1 argument");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitStoreA(ScrollBgYCurVar);
                EmitStoreA(ScrollBgYNextVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem)); // SCY
                EmitLoadA(ScrollBgXCurVar);
                EmitStoreA(ScrollBgXNextVar);
                EmitLoadA(ScrollDirtyVar);
                EmitAsm("AND_IMM", new AsmOperand(0xFF ^ ScrollDirtyBgMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__scroll_bg_y_set_buffered

Merkt nur Hintergrund-Y vor.

Syntax

c
void __scroll_bg_y_set_buffered(u8 scy);

Parameter

scy

Vertikale 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

c
__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.

__scroll_bg_y_set_buffered: DMG
DMG
__scroll_bg_y_set_buffered: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

Anwendungsbeispiel

c
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.

__scroll_bg_y_set_buffered: DMG
DMG
__scroll_bg_y_set_buffered: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/scroll.h:24

kitaqgb/kitaqgb/CodeGenerator.cs:7707

csharp
if (funcName == "__scroll_bg_y_set_buffered")
            {
                if (args.Length != 1) Error(expr, "__scroll_bg_y_set_buffered(scy) expects 1 argument");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitStoreA(ScrollBgYNextVar);
                EmitLoadA(ScrollDirtyVar);
                EmitAsm("OR_IMM", new AsmOperand(ScrollDirtyBgMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__scroll_flush

Überträgt geänderte Hintergrund- und Fensterkoordinaten in den nachgeführten Zustand und die Hardware und löscht anschließend ihre Vormerkflags.

Syntax

c
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

c
__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.

__scroll_flush: DMG
DMG
__scroll_flush: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

Anwendungsbeispiel

c
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.

__scroll_flush: DMG
DMG
__scroll_flush: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/scroll.h:28

kitaqgb/kitaqgb/CodeGenerator.cs:7771

csharp
if (funcName == "__scroll_flush")
            {
                if (args.Length != 0) Error(expr, "__scroll_flush() expects 0 arguments");
                EnsureScrollState();

                AsmOperand flushSkipBg = MakeUniqueLabel("scroll_flush_skip_bg");
                AsmOperand flushSkipWin = MakeUniqueLabel("scroll_flush_skip_win");

                EmitLoadA(ScrollDirtyVar);
                EmitAsm("AND_IMM", new AsmOperand(ScrollDirtyBgMask, AddressMode.Immediate));
                EmitAsm("JR_Z", flushSkipBg);
                EmitLoadA(ScrollBgXNextVar);
                EmitStoreA(ScrollBgXCurVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem)); // SCX
                EmitLoadA(ScrollBgYNextVar);
                EmitStoreA(ScrollBgYCurVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem)); // SCY
                EmitLabel(flushSkipBg);

                EmitLoadA(ScrollDirtyVar);
                EmitAsm("AND_IMM", new AsmOperand(ScrollDirtyWinMask, AddressMode.Immediate));
                EmitAsm("JR_Z", flushSkipWin);
                EmitLoadA(ScrollWinXNextVar);
                EmitStoreA(ScrollWinXCurVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem)); // WX
                EmitLoadA(ScrollWinYNextVar);
                EmitStoreA(ScrollWinYCurVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem)); // WY
                EmitLabel(flushSkipWin);

                EmitAsm("XOR_A");
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__scroll_split_commit

Erfasst die grundlegende Fenstersichtbarkeit und aktiviert das erste Rasterzeilenereignis der Aufteilungstabelle.

Syntax

c
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

c
__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.

__scroll_split_commit: DMG
DMG
__scroll_split_commit: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/scroll.h:33

kitaqgb/kitaqgb/CodeGenerator.cs:7959

csharp
if (funcName == "__scroll_split_commit")
            {
                if (args.Length != 0) Error(expr, "__scroll_split_commit() expects 0 arguments");
                EnsureScrollState();
                UseScrollSplitHelpers = true;
                EmitAsm("CALL", new AsmOperand("__kq_scroll_split_commit_core", AddressMode.Absolute));
                return;
            }

void AppendScrollHelpersIfUsed(List<Expr> rawLines)
    {
        bool needScrollHelpers = UseScrollSplitHelpers;
        if (!needScrollHelpers)
        {
            foreach (var e in rawLines)
            {
                string m;
                AsmOperand o;
                if (!e.Match(Tag.Asm, out m, out o) || o == null || !o.Base.HasValue) continue;
                string target = o.Base.Value;
                if (target == "__kq_scroll_split_reset_core" ||
                    target == "__kq_scroll_split_push_core" ||
                    target == "__kq_scroll_split_commit_core")
                {
                    needScrollHelpers = true;
                    break;
                }
            }
        }

        if (!needScrollHelpers) return;

        EnsureScrollState();

        var injected = new List<Expr>();
        injected.Add(Expr.Make(Tag.Comment, "[KITAQGB] injected scroll split helpers"));

        // Reset the split count/index and disable STAT coincidence interrupts; existing table bytes are left in place.
        string resetDone = "__kq_scroll_split_reset_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_reset_core"));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, resetDone));
        injected.Add(Expr.MakeAsm("RET"));

        // Append staged LY, SCX, SCY, WX, WY and masked flags; silently ignore entries beyond the eight-slot capacity.
        string pushHaveRoom = "__kq_scroll_split_push_have_room";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_push_core"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("CP_IMM", new AsmOperand(ScrollSplitMaxEntries, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(pushHaveRoom, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("RET"));
        injected.Add(Expr.Make(Tag.Label, pushHaveRoom));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_L_A"));
        injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpLyVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpScxVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpScyVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpWxVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpWyVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpFlagsVar));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagMask, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("INC_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("RET"));

        // Schedule entry A by programming LYC and STAT bit 6, or disable that bit when A is outside the current count.
        string schedDisable = "__kq_scroll_schedule_next_disable";
        string schedDisabled = "__kq_scroll_schedule_next_disabled";
        string schedHaveTarget = "__kq_scroll_schedule_next_have_target";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_schedule_next"));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("CP_B"));
        injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(schedDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(schedDisable, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, schedHaveTarget));
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_L_A"));
        injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x45, AddressMode.HighMem))); // LYC
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("RET"));
        injected.Add(Expr.Make(Tag.Label, schedDisable));
        // Do not rewrite a disabled STAT source: on DMG even writing zero
        // can raise a fresh request and repeatedly re-enter the STAT handler.
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(schedDisabled, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, schedDisabled));
        injected.Add(Expr.MakeAsm("RET"));

        // Apply the fields selected by an entry's flags. Show follows hide when both are set; palette color zero reuses SCX/SCY bytes.
        string applySkipBg = "__kq_scroll_apply_entry_skip_bg";
        string applySkipWin = "__kq_scroll_apply_entry_skip_win";
        string applySkipHide = "__kq_scroll_apply_entry_skip_hide";
        string applySkipBgColor0 = "__kq_scroll_apply_entry_skip_bg_color0";
        string applyWaitPalette = "__kq_scroll_apply_entry_wait_palette";
        string applyDone = "__kq_scroll_apply_entry_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_apply_entry"));
        injected.Add(Expr.MakeAsm("INC_HL")); // scx
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // scy
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_C_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // wx
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_D_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // wy
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // flags
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagMask, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_H_A"));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagUseBg, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipBg, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem))); // SCX
        injected.Add(Expr.MakeAsm("LD_A_C"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem))); // SCY
        injected.Add(Expr.Make(Tag.Label, applySkipBg));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagUseWin, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipWin, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_D"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem))); // WX
        injected.Add(Expr.MakeAsm("LD_A_E"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem))); // WY
        injected.Add(Expr.Make(Tag.Label, applySkipWin));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagWinHide, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipHide, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xDF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, applySkipHide));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagWinShow, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipBgColor0, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x20, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, applySkipBgColor0));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagBgColor0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applyDone, AddressMode.Absolute)));
        // CGB palette data writes are blocked during pixel transfer. Wait through
        // OAM search as well, so both color bytes fit in the following HBlank.
        // The LY-zero entry runs in VBlank and does not wait here.
        injected.Add(Expr.Make(Tag.Label, applyWaitPalette));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x02, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(applyWaitPalette, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(0x80, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x68, AddressMode.HighMem))); // BCPS: palette 0, color 0, auto increment
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x69, AddressMode.HighMem))); // BCPD low
        injected.Add(Expr.MakeAsm("LD_A_C"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x69, AddressMode.HighMem))); // BCPD high
        injected.Add(Expr.Make(Tag.Label, applyDone));
        injected.Add(Expr.MakeAsm("RET"));

        // Capture base window visibility and arm a nonempty table at index zero, enabling VBlank/STAT interrupts and issuing EI.
        string commitDisable = "__kq_scroll_split_commit_disable";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_commit_core"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x20, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollWinVisibleVar));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(commitDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", new AsmOperand(0xFFFF, AddressMode.Absolute))); // IE
        injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x03, AddressMode.Immediate))); // VBlank | STAT
        injected.Add(Expr.MakeAsm("LD_MEM_A", new AsmOperand(0xFFFF, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("EI"));
        injected.Add(Expr.MakeAsm("RET"));
        injected.Add(Expr.Make(Tag.Label, commitDisable));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("RET"));

        // Save registers/SVBK, select WRAM bank 1, restore base scroll state and restart the split sequence for the frame.
        // A first entry at LY zero is applied immediately before scheduling the following entry.
        string vbDisable = "__kq_vblank_disable";
        string vbFirstNonZero = "__kq_vblank_first_nonzero";
        string vbDone = "__kq_vblank_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_vblank_vector"));
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("PUSH_BC"));
        injected.Add(Expr.MakeAsm("PUSH_DE"));
        injected.Add(Expr.MakeAsm("PUSH_HL"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x70, AddressMode.HighMem))); // SVBK
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        // Set the existing fixed-address frame-ready flag used by the VBlank path.
        injected.Add(Expr.MakeAsm("LD_MEM_A", new AsmOperand(0xC29C, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollBgXCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem))); // SCX
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollBgYCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem))); // SCY
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinXCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem))); // WX
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinYCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem))); // WY
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xDF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinVisibleVar));
        injected.Add(Expr.MakeAsm("OR_B"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(vbDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(vbDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("LD_A_MEM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(vbFirstNonZero, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_HL_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_apply_entry", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR", new AsmOperand(vbDone, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, vbFirstNonZero));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR", new AsmOperand(vbDone, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, vbDisable));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, vbDone));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem))); // restore SVBK
        injected.Add(Expr.MakeAsm("POP_HL"));
        injected.Add(Expr.MakeAsm("POP_DE"));
        injected.Add(Expr.MakeAsm("POP_BC"));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("RETI"));

        // Save registers/SVBK, apply the current entry and advance its index, or disable further coincidence interrupts at the end.
        string statDisable = "__kq_stat_disable";
        string statDone = "__kq_stat_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_stat_vector"));
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("PUSH_BC"));
        injected.Add(Expr.MakeAsm("PUSH_DE"));
        injected.Add(Expr.MakeAsm("PUSH_HL"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x70, AddressMode.HighMem))); // SVBK
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("CP_B"));
        injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(statDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_L_A"));
        injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        // DMG STAT writes can request an interrupt unrelated to LYC. Consume
        // this entry only on its scheduled line; leave the index intact otherwise.
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x44, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("CP_B"));
        injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(statDone, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_apply_entry", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("INC_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR", new AsmOperand(statDone, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, statDisable));
        // Acknowledge an already-disabled source without writing STAT again.
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDone, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, statDone));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem))); // restore SVBK
        injected.Add(Expr.MakeAsm("POP_HL"));
        injected.Add(Expr.MakeAsm("POP_DE"));
        injected.Add(Expr.MakeAsm("POP_BC"));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("RETI"));

        int insertAt = 0;
        while (insertAt < rawLines.Count && rawLines[insertAt].MatchTag(Tag.RstMap)) insertAt++;
        rawLines.InsertRange(insertAt, injected);
    }
__scroll_split_push

Fügt einen Eintrag hinzu, der die Hintergrundverschiebung beim gewünschten Rasterzeilenereignis ändert.

Syntax

c
void __scroll_split_push(u8 ly, u8 scx, u8 scy);

Parameter

ly

Rasterzeilennummer 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.

scx

Horizontale Hintergrundverschiebung, 0–255 Pixel; ein größerer Wert bewegt den Hintergrund nach links.

scy

Vertikale 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

c
__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.

__scroll_split_push: DMG
DMG
__scroll_split_push: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/scroll.h:31

kitaqgb/kitaqgb/CodeGenerator.cs:7907

csharp
if (funcName == "__scroll_split_push")
            {
                if (args.Length != 3) Error(expr, "__scroll_split_push(ly, scx, scy) expects 3 arguments");
                EnsureScrollState();
                UseScrollSplitHelpers = true;

                CompileIntoA(args[0]);
                EmitStoreA(ScrollSplitTmpLyVar);
                CompileIntoA(args[1]);
                EmitStoreA(ScrollSplitTmpScxVar);
                CompileIntoA(args[2]);
                EmitStoreA(ScrollSplitTmpScyVar);
                EmitAsm("XOR_A");
                EmitStoreA(ScrollSplitTmpWxVar);
                EmitStoreA(ScrollSplitTmpWyVar);
                EmitAsm("LD_A_IMM", new AsmOperand(ScrollSplitFlagUseBg, AddressMode.Immediate));
                EmitStoreA(ScrollSplitTmpFlagsVar);
                EmitAsm("CALL", new AsmOperand("__kq_scroll_split_push_core", AddressMode.Absolute));
                return;
            }

void AppendScrollHelpersIfUsed(List<Expr> rawLines)
    {
        bool needScrollHelpers = UseScrollSplitHelpers;
        if (!needScrollHelpers)
        {
            foreach (var e in rawLines)
            {
                string m;
                AsmOperand o;
                if (!e.Match(Tag.Asm, out m, out o) || o == null || !o.Base.HasValue) continue;
                string target = o.Base.Value;
                if (target == "__kq_scroll_split_reset_core" ||
                    target == "__kq_scroll_split_push_core" ||
                    target == "__kq_scroll_split_commit_core")
                {
                    needScrollHelpers = true;
                    break;
                }
            }
        }

        if (!needScrollHelpers) return;

        EnsureScrollState();

        var injected = new List<Expr>();
        injected.Add(Expr.Make(Tag.Comment, "[KITAQGB] injected scroll split helpers"));

        // Reset the split count/index and disable STAT coincidence interrupts; existing table bytes are left in place.
        string resetDone = "__kq_scroll_split_reset_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_reset_core"));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, resetDone));
        injected.Add(Expr.MakeAsm("RET"));

        // Append staged LY, SCX, SCY, WX, WY and masked flags; silently ignore entries beyond the eight-slot capacity.
        string pushHaveRoom = "__kq_scroll_split_push_have_room";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_push_core"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("CP_IMM", new AsmOperand(ScrollSplitMaxEntries, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(pushHaveRoom, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("RET"));
        injected.Add(Expr.Make(Tag.Label, pushHaveRoom));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_L_A"));
        injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpLyVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpScxVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpScyVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpWxVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpWyVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpFlagsVar));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagMask, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("INC_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("RET"));

        // Schedule entry A by programming LYC and STAT bit 6, or disable that bit when A is outside the current count.
        string schedDisable = "__kq_scroll_schedule_next_disable";
        string schedDisabled = "__kq_scroll_schedule_next_disabled";
        string schedHaveTarget = "__kq_scroll_schedule_next_have_target";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_schedule_next"));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("CP_B"));
        injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(schedDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(schedDisable, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, schedHaveTarget));
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_L_A"));
        injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x45, AddressMode.HighMem))); // LYC
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("RET"));
        injected.Add(Expr.Make(Tag.Label, schedDisable));
        // Do not rewrite a disabled STAT source: on DMG even writing zero
        // can raise a fresh request and repeatedly re-enter the STAT handler.
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(schedDisabled, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, schedDisabled));
        injected.Add(Expr.MakeAsm("RET"));

        // Apply the fields selected by an entry's flags. Show follows hide when both are set; palette color zero reuses SCX/SCY bytes.
        string applySkipBg = "__kq_scroll_apply_entry_skip_bg";
        string applySkipWin = "__kq_scroll_apply_entry_skip_win";
        string applySkipHide = "__kq_scroll_apply_entry_skip_hide";
        string applySkipBgColor0 = "__kq_scroll_apply_entry_skip_bg_color0";
        string applyWaitPalette = "__kq_scroll_apply_entry_wait_palette";
        string applyDone = "__kq_scroll_apply_entry_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_apply_entry"));
        injected.Add(Expr.MakeAsm("INC_HL")); // scx
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // scy
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_C_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // wx
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_D_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // wy
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // flags
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagMask, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_H_A"));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagUseBg, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipBg, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem))); // SCX
        injected.Add(Expr.MakeAsm("LD_A_C"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem))); // SCY
        injected.Add(Expr.Make(Tag.Label, applySkipBg));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagUseWin, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipWin, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_D"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem))); // WX
        injected.Add(Expr.MakeAsm("LD_A_E"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem))); // WY
        injected.Add(Expr.Make(Tag.Label, applySkipWin));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagWinHide, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipHide, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xDF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, applySkipHide));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagWinShow, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipBgColor0, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x20, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, applySkipBgColor0));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagBgColor0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applyDone, AddressMode.Absolute)));
        // CGB palette data writes are blocked during pixel transfer. Wait through
        // OAM search as well, so both color bytes fit in the following HBlank.
        // The LY-zero entry runs in VBlank and does not wait here.
        injected.Add(Expr.Make(Tag.Label, applyWaitPalette));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x02, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(applyWaitPalette, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(0x80, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x68, AddressMode.HighMem))); // BCPS: palette 0, color 0, auto increment
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x69, AddressMode.HighMem))); // BCPD low
        injected.Add(Expr.MakeAsm("LD_A_C"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x69, AddressMode.HighMem))); // BCPD high
        injected.Add(Expr.Make(Tag.Label, applyDone));
        injected.Add(Expr.MakeAsm("RET"));

        // Capture base window visibility and arm a nonempty table at index zero, enabling VBlank/STAT interrupts and issuing EI.
        string commitDisable = "__kq_scroll_split_commit_disable";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_commit_core"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x20, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollWinVisibleVar));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(commitDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", new AsmOperand(0xFFFF, AddressMode.Absolute))); // IE
        injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x03, AddressMode.Immediate))); // VBlank | STAT
        injected.Add(Expr.MakeAsm("LD_MEM_A", new AsmOperand(0xFFFF, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("EI"));
        injected.Add(Expr.MakeAsm("RET"));
        injected.Add(Expr.Make(Tag.Label, commitDisable));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("RET"));

        // Save registers/SVBK, select WRAM bank 1, restore base scroll state and restart the split sequence for the frame.
        // A first entry at LY zero is applied immediately before scheduling the following entry.
        string vbDisable = "__kq_vblank_disable";
        string vbFirstNonZero = "__kq_vblank_first_nonzero";
        string vbDone = "__kq_vblank_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_vblank_vector"));
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("PUSH_BC"));
        injected.Add(Expr.MakeAsm("PUSH_DE"));
        injected.Add(Expr.MakeAsm("PUSH_HL"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x70, AddressMode.HighMem))); // SVBK
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        // Set the existing fixed-address frame-ready flag used by the VBlank path.
        injected.Add(Expr.MakeAsm("LD_MEM_A", new AsmOperand(0xC29C, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollBgXCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem))); // SCX
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollBgYCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem))); // SCY
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinXCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem))); // WX
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinYCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem))); // WY
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xDF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinVisibleVar));
        injected.Add(Expr.MakeAsm("OR_B"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(vbDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(vbDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("LD_A_MEM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(vbFirstNonZero, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_HL_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_apply_entry", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR", new AsmOperand(vbDone, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, vbFirstNonZero));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR", new AsmOperand(vbDone, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, vbDisable));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, vbDone));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem))); // restore SVBK
        injected.Add(Expr.MakeAsm("POP_HL"));
        injected.Add(Expr.MakeAsm("POP_DE"));
        injected.Add(Expr.MakeAsm("POP_BC"));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("RETI"));

        // Save registers/SVBK, apply the current entry and advance its index, or disable further coincidence interrupts at the end.
        string statDisable = "__kq_stat_disable";
        string statDone = "__kq_stat_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_stat_vector"));
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("PUSH_BC"));
        injected.Add(Expr.MakeAsm("PUSH_DE"));
        injected.Add(Expr.MakeAsm("PUSH_HL"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x70, AddressMode.HighMem))); // SVBK
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("CP_B"));
        injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(statDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_L_A"));
        injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        // DMG STAT writes can request an interrupt unrelated to LYC. Consume
        // this entry only on its scheduled line; leave the index intact otherwise.
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x44, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("CP_B"));
        injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(statDone, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_apply_entry", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("INC_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR", new AsmOperand(statDone, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, statDisable));
        // Acknowledge an already-disabled source without writing STAT again.
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDone, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, statDone));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem))); // restore SVBK
        injected.Add(Expr.MakeAsm("POP_HL"));
        injected.Add(Expr.MakeAsm("POP_DE"));
        injected.Add(Expr.MakeAsm("POP_BC"));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("RETI"));

        int insertAt = 0;
        while (insertAt < rawLines.Count && rawLines[insertAt].MatchTag(Tag.RstMap)) insertAt++;
        rawLines.InsertRange(insertAt, injected);
    }
__scroll_split_push_ex

Fügt einen Aufteilungseintrag hinzu, dessen Flags Operationen für Hintergrund, Fenster, Sichtbarkeit oder Hintergrundfarbe null auswählen.

Syntax

c
void __scroll_split_push_ex(u8 ly, u8 scx, u8 scy, u8 wx, u8 wy, u8 flags);

Parameter

ly

Rasterzeilennummer 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.

scx

Horizontale Hintergrundverschiebung, 0–255 Pixel; ein größerer Wert bewegt den Hintergrund nach links.

scy

Vertikale Hintergrundverschiebung, 0–255 Pixel; ein größerer Wert bewegt den Hintergrund nach oben.

wx

Roher WX-Byte-Wert, 0–255: Addieren Sie 7 zur gewünschten Bildschirm-X-Koordinate; für den linken Rand verwenden Sie 7.

wy

Oberkante des Fensters in Bildschirm-Y-Pixeln, 0–255; sichtbar sind die Zeilen 0–143.

flags

Operationsbits: 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

c
__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.

__scroll_split_push_ex: DMG
DMG
__scroll_split_push_ex: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/scroll.h:32

kitaqgb/kitaqgb/CodeGenerator.cs:7929

csharp
if (funcName == "__scroll_split_push_ex")
            {
                if (args.Length != 6) Error(expr, "__scroll_split_push_ex(ly, scx, scy, wx, wy, flags) expects 6 arguments");
                EnsureScrollState();
                UseScrollSplitHelpers = true;

                if (TryGetU8Const(args[5], out int splitFlagsConst))
                {
                    int unknownBits = splitFlagsConst & ~ScrollSplitFlagMask;
                    if (unknownBits != 0)
                        Warning(args[5], string.Format("__scroll_split_push_ex() ignores unknown flag bits 0x{0:X2}", unknownBits));
                }

                CompileIntoA(args[0]);
                EmitStoreA(ScrollSplitTmpLyVar);
                CompileIntoA(args[1]);
                EmitStoreA(ScrollSplitTmpScxVar);
                CompileIntoA(args[2]);
                EmitStoreA(ScrollSplitTmpScyVar);
                CompileIntoA(args[3]);
                EmitStoreA(ScrollSplitTmpWxVar);
                CompileIntoA(args[4]);
                EmitStoreA(ScrollSplitTmpWyVar);
                CompileIntoA(args[5]);
                EmitStoreA(ScrollSplitTmpFlagsVar);
                EmitAsm("CALL", new AsmOperand("__kq_scroll_split_push_core", AddressMode.Absolute));
                return;
            }

void AppendScrollHelpersIfUsed(List<Expr> rawLines)
    {
        bool needScrollHelpers = UseScrollSplitHelpers;
        if (!needScrollHelpers)
        {
            foreach (var e in rawLines)
            {
                string m;
                AsmOperand o;
                if (!e.Match(Tag.Asm, out m, out o) || o == null || !o.Base.HasValue) continue;
                string target = o.Base.Value;
                if (target == "__kq_scroll_split_reset_core" ||
                    target == "__kq_scroll_split_push_core" ||
                    target == "__kq_scroll_split_commit_core")
                {
                    needScrollHelpers = true;
                    break;
                }
            }
        }

        if (!needScrollHelpers) return;

        EnsureScrollState();

        var injected = new List<Expr>();
        injected.Add(Expr.Make(Tag.Comment, "[KITAQGB] injected scroll split helpers"));

        // Reset the split count/index and disable STAT coincidence interrupts; existing table bytes are left in place.
        string resetDone = "__kq_scroll_split_reset_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_reset_core"));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, resetDone));
        injected.Add(Expr.MakeAsm("RET"));

        // Append staged LY, SCX, SCY, WX, WY and masked flags; silently ignore entries beyond the eight-slot capacity.
        string pushHaveRoom = "__kq_scroll_split_push_have_room";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_push_core"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("CP_IMM", new AsmOperand(ScrollSplitMaxEntries, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(pushHaveRoom, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("RET"));
        injected.Add(Expr.Make(Tag.Label, pushHaveRoom));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_L_A"));
        injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpLyVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpScxVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpScyVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpWxVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpWyVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpFlagsVar));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagMask, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("INC_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("RET"));

        // Schedule entry A by programming LYC and STAT bit 6, or disable that bit when A is outside the current count.
        string schedDisable = "__kq_scroll_schedule_next_disable";
        string schedDisabled = "__kq_scroll_schedule_next_disabled";
        string schedHaveTarget = "__kq_scroll_schedule_next_have_target";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_schedule_next"));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("CP_B"));
        injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(schedDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(schedDisable, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, schedHaveTarget));
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_L_A"));
        injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x45, AddressMode.HighMem))); // LYC
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("RET"));
        injected.Add(Expr.Make(Tag.Label, schedDisable));
        // Do not rewrite a disabled STAT source: on DMG even writing zero
        // can raise a fresh request and repeatedly re-enter the STAT handler.
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(schedDisabled, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, schedDisabled));
        injected.Add(Expr.MakeAsm("RET"));

        // Apply the fields selected by an entry's flags. Show follows hide when both are set; palette color zero reuses SCX/SCY bytes.
        string applySkipBg = "__kq_scroll_apply_entry_skip_bg";
        string applySkipWin = "__kq_scroll_apply_entry_skip_win";
        string applySkipHide = "__kq_scroll_apply_entry_skip_hide";
        string applySkipBgColor0 = "__kq_scroll_apply_entry_skip_bg_color0";
        string applyWaitPalette = "__kq_scroll_apply_entry_wait_palette";
        string applyDone = "__kq_scroll_apply_entry_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_apply_entry"));
        injected.Add(Expr.MakeAsm("INC_HL")); // scx
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // scy
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_C_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // wx
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_D_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // wy
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // flags
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagMask, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_H_A"));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagUseBg, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipBg, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem))); // SCX
        injected.Add(Expr.MakeAsm("LD_A_C"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem))); // SCY
        injected.Add(Expr.Make(Tag.Label, applySkipBg));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagUseWin, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipWin, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_D"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem))); // WX
        injected.Add(Expr.MakeAsm("LD_A_E"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem))); // WY
        injected.Add(Expr.Make(Tag.Label, applySkipWin));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagWinHide, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipHide, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xDF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, applySkipHide));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagWinShow, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipBgColor0, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x20, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, applySkipBgColor0));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagBgColor0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applyDone, AddressMode.Absolute)));
        // CGB palette data writes are blocked during pixel transfer. Wait through
        // OAM search as well, so both color bytes fit in the following HBlank.
        // The LY-zero entry runs in VBlank and does not wait here.
        injected.Add(Expr.Make(Tag.Label, applyWaitPalette));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x02, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(applyWaitPalette, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(0x80, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x68, AddressMode.HighMem))); // BCPS: palette 0, color 0, auto increment
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x69, AddressMode.HighMem))); // BCPD low
        injected.Add(Expr.MakeAsm("LD_A_C"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x69, AddressMode.HighMem))); // BCPD high
        injected.Add(Expr.Make(Tag.Label, applyDone));
        injected.Add(Expr.MakeAsm("RET"));

        // Capture base window visibility and arm a nonempty table at index zero, enabling VBlank/STAT interrupts and issuing EI.
        string commitDisable = "__kq_scroll_split_commit_disable";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_commit_core"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x20, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollWinVisibleVar));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(commitDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", new AsmOperand(0xFFFF, AddressMode.Absolute))); // IE
        injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x03, AddressMode.Immediate))); // VBlank | STAT
        injected.Add(Expr.MakeAsm("LD_MEM_A", new AsmOperand(0xFFFF, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("EI"));
        injected.Add(Expr.MakeAsm("RET"));
        injected.Add(Expr.Make(Tag.Label, commitDisable));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("RET"));

        // Save registers/SVBK, select WRAM bank 1, restore base scroll state and restart the split sequence for the frame.
        // A first entry at LY zero is applied immediately before scheduling the following entry.
        string vbDisable = "__kq_vblank_disable";
        string vbFirstNonZero = "__kq_vblank_first_nonzero";
        string vbDone = "__kq_vblank_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_vblank_vector"));
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("PUSH_BC"));
        injected.Add(Expr.MakeAsm("PUSH_DE"));
        injected.Add(Expr.MakeAsm("PUSH_HL"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x70, AddressMode.HighMem))); // SVBK
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        // Set the existing fixed-address frame-ready flag used by the VBlank path.
        injected.Add(Expr.MakeAsm("LD_MEM_A", new AsmOperand(0xC29C, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollBgXCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem))); // SCX
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollBgYCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem))); // SCY
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinXCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem))); // WX
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinYCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem))); // WY
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xDF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinVisibleVar));
        injected.Add(Expr.MakeAsm("OR_B"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(vbDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(vbDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("LD_A_MEM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(vbFirstNonZero, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_HL_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_apply_entry", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR", new AsmOperand(vbDone, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, vbFirstNonZero));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR", new AsmOperand(vbDone, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, vbDisable));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, vbDone));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem))); // restore SVBK
        injected.Add(Expr.MakeAsm("POP_HL"));
        injected.Add(Expr.MakeAsm("POP_DE"));
        injected.Add(Expr.MakeAsm("POP_BC"));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("RETI"));

        // Save registers/SVBK, apply the current entry and advance its index, or disable further coincidence interrupts at the end.
        string statDisable = "__kq_stat_disable";
        string statDone = "__kq_stat_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_stat_vector"));
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("PUSH_BC"));
        injected.Add(Expr.MakeAsm("PUSH_DE"));
        injected.Add(Expr.MakeAsm("PUSH_HL"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x70, AddressMode.HighMem))); // SVBK
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("CP_B"));
        injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(statDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_L_A"));
        injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        // DMG STAT writes can request an interrupt unrelated to LYC. Consume
        // this entry only on its scheduled line; leave the index intact otherwise.
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x44, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("CP_B"));
        injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(statDone, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_apply_entry", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("INC_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR", new AsmOperand(statDone, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, statDisable));
        // Acknowledge an already-disabled source without writing STAT again.
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDone, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, statDone));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem))); // restore SVBK
        injected.Add(Expr.MakeAsm("POP_HL"));
        injected.Add(Expr.MakeAsm("POP_DE"));
        injected.Add(Expr.MakeAsm("POP_BC"));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("RETI"));

        int insertAt = 0;
        while (insertAt < rawLines.Count && rawLines[insertAt].MatchTag(Tag.RstMap)) insertAt++;
        rawLines.InsertRange(insertAt, injected);
    }
__scroll_split_reset

Löscht Anzahl und Index der Aufteilungen, deaktiviert ihre Verarbeitung und löscht das LYC-Interruptfreigabebit in STAT.

Syntax

c
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

c
__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.

__scroll_split_reset: DMG
DMG
__scroll_split_reset: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/scroll.h:30

kitaqgb/kitaqgb/CodeGenerator.cs:7897

csharp
if (funcName == "__scroll_split_reset")
            {
                if (args.Length != 0) Error(expr, "__scroll_split_reset() expects 0 arguments");
                EnsureScrollState();
                UseScrollSplitHelpers = true;
                EmitAsm("CALL", new AsmOperand("__kq_scroll_split_reset_core", AddressMode.Absolute));
                return;
            }

void AppendScrollHelpersIfUsed(List<Expr> rawLines)
    {
        bool needScrollHelpers = UseScrollSplitHelpers;
        if (!needScrollHelpers)
        {
            foreach (var e in rawLines)
            {
                string m;
                AsmOperand o;
                if (!e.Match(Tag.Asm, out m, out o) || o == null || !o.Base.HasValue) continue;
                string target = o.Base.Value;
                if (target == "__kq_scroll_split_reset_core" ||
                    target == "__kq_scroll_split_push_core" ||
                    target == "__kq_scroll_split_commit_core")
                {
                    needScrollHelpers = true;
                    break;
                }
            }
        }

        if (!needScrollHelpers) return;

        EnsureScrollState();

        var injected = new List<Expr>();
        injected.Add(Expr.Make(Tag.Comment, "[KITAQGB] injected scroll split helpers"));

        // Reset the split count/index and disable STAT coincidence interrupts; existing table bytes are left in place.
        string resetDone = "__kq_scroll_split_reset_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_reset_core"));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, resetDone));
        injected.Add(Expr.MakeAsm("RET"));

        // Append staged LY, SCX, SCY, WX, WY and masked flags; silently ignore entries beyond the eight-slot capacity.
        string pushHaveRoom = "__kq_scroll_split_push_have_room";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_push_core"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("CP_IMM", new AsmOperand(ScrollSplitMaxEntries, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(pushHaveRoom, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("RET"));
        injected.Add(Expr.Make(Tag.Label, pushHaveRoom));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_L_A"));
        injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpLyVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpScxVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpScyVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpWxVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpWyVar));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitTmpFlagsVar));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagMask, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDI_HL_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("INC_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("RET"));

        // Schedule entry A by programming LYC and STAT bit 6, or disable that bit when A is outside the current count.
        string schedDisable = "__kq_scroll_schedule_next_disable";
        string schedDisabled = "__kq_scroll_schedule_next_disabled";
        string schedHaveTarget = "__kq_scroll_schedule_next_have_target";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_schedule_next"));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("CP_B"));
        injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(schedDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(schedDisable, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, schedHaveTarget));
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_L_A"));
        injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x45, AddressMode.HighMem))); // LYC
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("RET"));
        injected.Add(Expr.Make(Tag.Label, schedDisable));
        // Do not rewrite a disabled STAT source: on DMG even writing zero
        // can raise a fresh request and repeatedly re-enter the STAT handler.
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(schedDisabled, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, schedDisabled));
        injected.Add(Expr.MakeAsm("RET"));

        // Apply the fields selected by an entry's flags. Show follows hide when both are set; palette color zero reuses SCX/SCY bytes.
        string applySkipBg = "__kq_scroll_apply_entry_skip_bg";
        string applySkipWin = "__kq_scroll_apply_entry_skip_win";
        string applySkipHide = "__kq_scroll_apply_entry_skip_hide";
        string applySkipBgColor0 = "__kq_scroll_apply_entry_skip_bg_color0";
        string applyWaitPalette = "__kq_scroll_apply_entry_wait_palette";
        string applyDone = "__kq_scroll_apply_entry_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_apply_entry"));
        injected.Add(Expr.MakeAsm("INC_HL")); // scx
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // scy
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_C_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // wx
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_D_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // wy
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("INC_HL")); // flags
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagMask, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_H_A"));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagUseBg, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipBg, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem))); // SCX
        injected.Add(Expr.MakeAsm("LD_A_C"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem))); // SCY
        injected.Add(Expr.Make(Tag.Label, applySkipBg));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagUseWin, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipWin, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_D"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem))); // WX
        injected.Add(Expr.MakeAsm("LD_A_E"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem))); // WY
        injected.Add(Expr.Make(Tag.Label, applySkipWin));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagWinHide, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipHide, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xDF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, applySkipHide));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagWinShow, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applySkipBgColor0, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x20, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, applySkipBgColor0));
        injected.Add(Expr.MakeAsm("LD_A_H"));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(ScrollSplitFlagBgColor0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(applyDone, AddressMode.Absolute)));
        // CGB palette data writes are blocked during pixel transfer. Wait through
        // OAM search as well, so both color bytes fit in the following HBlank.
        // The LY-zero entry runs in VBlank and does not wait here.
        injected.Add(Expr.Make(Tag.Label, applyWaitPalette));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x02, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(applyWaitPalette, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(0x80, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x68, AddressMode.HighMem))); // BCPS: palette 0, color 0, auto increment
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x69, AddressMode.HighMem))); // BCPD low
        injected.Add(Expr.MakeAsm("LD_A_C"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x69, AddressMode.HighMem))); // BCPD high
        injected.Add(Expr.Make(Tag.Label, applyDone));
        injected.Add(Expr.MakeAsm("RET"));

        // Capture base window visibility and arm a nonempty table at index zero, enabling VBlank/STAT interrupts and issuing EI.
        string commitDisable = "__kq_scroll_split_commit_disable";
        injected.Add(Expr.Make(Tag.Function, "__kq_scroll_split_commit_core"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x20, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollWinVisibleVar));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(commitDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", new AsmOperand(0xFFFF, AddressMode.Absolute))); // IE
        injected.Add(Expr.MakeAsm("OR_IMM", new AsmOperand(0x03, AddressMode.Immediate))); // VBlank | STAT
        injected.Add(Expr.MakeAsm("LD_MEM_A", new AsmOperand(0xFFFF, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("EI"));
        injected.Add(Expr.MakeAsm("RET"));
        injected.Add(Expr.Make(Tag.Label, commitDisable));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("RET"));

        // Save registers/SVBK, select WRAM bank 1, restore base scroll state and restart the split sequence for the frame.
        // A first entry at LY zero is applied immediately before scheduling the following entry.
        string vbDisable = "__kq_vblank_disable";
        string vbFirstNonZero = "__kq_vblank_first_nonzero";
        string vbDone = "__kq_vblank_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_vblank_vector"));
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("PUSH_BC"));
        injected.Add(Expr.MakeAsm("PUSH_DE"));
        injected.Add(Expr.MakeAsm("PUSH_HL"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x70, AddressMode.HighMem))); // SVBK
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        // Set the existing fixed-address frame-ready flag used by the VBlank path.
        injected.Add(Expr.MakeAsm("LD_MEM_A", new AsmOperand(0xC29C, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollBgXCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x43, AddressMode.HighMem))); // SCX
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollBgYCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x42, AddressMode.HighMem))); // SCY
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinXCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem))); // WX
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinYCurVar));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem))); // WY
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem))); // LCDC
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xDF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollWinVisibleVar));
        injected.Add(Expr.MakeAsm("OR_B"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(vbDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(vbDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("LD_A_MEM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(vbFirstNonZero, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_HL_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_apply_entry", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR", new AsmOperand(vbDone, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, vbFirstNonZero));
        injected.Add(Expr.MakeAsm("XOR_A"));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR", new AsmOperand(vbDone, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, vbDisable));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, vbDone));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem))); // restore SVBK
        injected.Add(Expr.MakeAsm("POP_HL"));
        injected.Add(Expr.MakeAsm("POP_DE"));
        injected.Add(Expr.MakeAsm("POP_BC"));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("RETI"));

        // Save registers/SVBK, apply the current entry and advance its index, or disable further coincidence interrupts at the end.
        string statDisable = "__kq_stat_disable";
        string statDone = "__kq_stat_done";
        injected.Add(Expr.Make(Tag.Function, "__kq_stat_vector"));
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("PUSH_BC"));
        injected.Add(Expr.MakeAsm("PUSH_DE"));
        injected.Add(Expr.MakeAsm("PUSH_HL"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x70, AddressMode.HighMem))); // SVBK
        injected.Add(Expr.MakeAsm("PUSH_AF"));
        injected.Add(Expr.MakeAsm("LD_A_IMM", new AsmOperand(1, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitEnabledVar));
        injected.Add(Expr.MakeAsm("OR_A"));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitCountVar));
        injected.Add(Expr.MakeAsm("CP_B"));
        injected.Add(Expr.MakeAsm("JR_C", new AsmOperand(statDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDisable, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_B"));
        injected.Add(Expr.MakeAsm("LD_E_A"));
        injected.Add(Expr.MakeAsm("LD_D_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LD_L_A"));
        injected.Add(Expr.MakeAsm("LD_H_IMM", new AsmOperand(0, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        injected.Add(Expr.MakeAsm("ADD_HL_HL"));
        injected.Add(Expr.MakeAsm("LD_DE_IMM", new AsmOperand(ScrollSplitTableAddr, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("ADD_HL_DE"));
        // DMG STAT writes can request an interrupt unrelated to LYC. Consume
        // this entry only on its scheduled line; leave the index intact otherwise.
        injected.Add(Expr.MakeAsm("LD_A_HL"));
        injected.Add(Expr.MakeAsm("LD_B_A"));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x44, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("CP_B"));
        injected.Add(Expr.MakeAsm("JR_NZ", new AsmOperand(statDone, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_apply_entry", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LD_A_MEM", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("INC_A"));
        injected.Add(Expr.MakeAsm("LD_MEM_A", ScrollSplitIndexVar));
        injected.Add(Expr.MakeAsm("CALL", new AsmOperand("__kq_scroll_schedule_next", AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("JR", new AsmOperand(statDone, AddressMode.Absolute)));
        injected.Add(Expr.Make(Tag.Label, statDisable));
        // Acknowledge an already-disabled source without writing STAT again.
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0x40, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("JR_Z", new AsmOperand(statDone, AddressMode.Absolute)));
        injected.Add(Expr.MakeAsm("LDH_A_MEM", new AsmOperand(0x41, AddressMode.HighMem))); // STAT
        injected.Add(Expr.MakeAsm("AND_IMM", new AsmOperand(0xBF, AddressMode.Immediate)));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x41, AddressMode.HighMem)));
        injected.Add(Expr.Make(Tag.Label, statDone));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("LDH_MEM_A", new AsmOperand(0x70, AddressMode.HighMem))); // restore SVBK
        injected.Add(Expr.MakeAsm("POP_HL"));
        injected.Add(Expr.MakeAsm("POP_DE"));
        injected.Add(Expr.MakeAsm("POP_BC"));
        injected.Add(Expr.MakeAsm("POP_AF"));
        injected.Add(Expr.MakeAsm("RETI"));

        int insertAt = 0;
        while (insertAt < rawLines.Count && rawLines[insertAt].MatchTag(Tag.RstMap)) insertAt++;
        rawLines.InsertRange(insertAt, injected);
    }
__scroll_win_add

Addiert vorzeichenbehaftete Verschiebungen zu den nachgeführten Fensterkoordinaten und wendet sie sofort an.

Syntax

c
void __scroll_win_add(s8 dx, s8 dy);

Parameter

dx

Vorzeichenbehaftete horizontale Pixelverschiebung, −128 bis 127.

dy

Vorzeichenbehaftete 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

c
__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.

__scroll_win_add: DMG
DMG
__scroll_win_add: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

Anwendungsbeispiel

c
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.

__scroll_win_add: DMG
DMG
__scroll_win_add: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/scroll.h:38

kitaqgb/kitaqgb/CodeGenerator.cs:7866

csharp
if (funcName == "__scroll_win_add")
            {
                if (args.Length != 2) Error(expr, "__scroll_win_add(dx, dy) expects 2 arguments");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitPushAAsWord();
                CompileIntoA(args[1]);
                EmitAsm("LD_C_A");
                EmitAsm("POP_HL");
                EmitAsm("LD_B_L");

                EmitLoadA(ScrollWinXCurVar);
                EmitAsm("ADD_B");
                EmitStoreA(ScrollWinXCurVar);
                EmitStoreA(ScrollWinXNextVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem)); // WX

                EmitLoadA(ScrollWinYCurVar);
                EmitAsm("ADD_C");
                EmitStoreA(ScrollWinYCurVar);
                EmitStoreA(ScrollWinYNextVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem)); // WY

                EmitLoadA(ScrollDirtyVar);
                EmitAsm("AND_IMM", new AsmOperand(0xFF ^ ScrollDirtyWinMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__scroll_win_hide

Löscht das Fensterbit in LCDC und den nachgeführten Sichtbarkeitszustand des Fensters.

Syntax

c
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

c
__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.

__scroll_win_hide: DMG
DMG
__scroll_win_hide: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

Anwendungsbeispiel

c
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.

__scroll_win_hide: DMG
DMG
__scroll_win_hide: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/scroll.h:36

kitaqgb/kitaqgb/CodeGenerator.cs:7821

csharp
if (funcName == "__scroll_win_hide")
            {
                if (args.Length != 0) Error(expr, "__scroll_win_hide() expects 0 arguments");
                EnsureScrollState();

                EmitAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem)); // LCDC
                EmitAsm("AND_IMM", new AsmOperand(0xDF, AddressMode.Immediate));
                EmitAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem));
                EmitAsm("XOR_A");
                EmitStoreA(ScrollWinVisibleVar);
                return;
            }
__scroll_win_set

Setzt WX und WY des Fensters sofort.

Syntax

c
void __scroll_win_set(u8 wx, u8 wy);

Parameter

wx

Roher WX-Byte-Wert, 0–255: Addieren Sie 7 zur gewünschten Bildschirm-X-Koordinate; für den linken Rand verwenden Sie 7.

wy

Oberkante 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

c
__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.

__scroll_win_set: DMG
DMG
__scroll_win_set: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

Anwendungsbeispiel

c
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.

__scroll_win_set: DMG
DMG
__scroll_win_set: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/scroll.h:16

kitaqgb/kitaqgb/CodeGenerator.cs:7589

csharp
if (funcName == "__scroll_win_set")
            {
                if (args.Length != 2) Error(expr, "__scroll_win_set(wx, wy) expects 2 arguments");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitPushAAsWord();
                CompileIntoA(args[1]);
                EmitAsm("LD_B_A");
                EmitAsm("POP_HL");

                EmitAsm("LD_A_L");
                EmitStoreA(ScrollWinXCurVar);
                EmitStoreA(ScrollWinXNextVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem)); // WX

                EmitAsm("LD_A_B");
                EmitStoreA(ScrollWinYCurVar);
                EmitStoreA(ScrollWinYNextVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem)); // WY

                EmitLoadA(ScrollDirtyVar);
                EmitAsm("AND_IMM", new AsmOperand(0xFF ^ ScrollDirtyWinMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__scroll_win_set_buffered

Merkt beide Fensterkoordinaten zur späteren Übertragung vor.

Syntax

c
void __scroll_win_set_buffered(u8 wx, u8 wy);

Parameter

wx

Roher WX-Byte-Wert, 0–255: Addieren Sie 7 zur gewünschten Bildschirm-X-Koordinate; für den linken Rand verwenden Sie 7.

wy

Oberkante 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

c
__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.

__scroll_win_set_buffered: DMG
DMG
__scroll_win_set_buffered: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

Anwendungsbeispiel

c
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.

__scroll_win_set_buffered: DMG
DMG
__scroll_win_set_buffered: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/scroll.h:25

kitaqgb/kitaqgb/CodeGenerator.cs:7721

csharp
if (funcName == "__scroll_win_set_buffered")
            {
                if (args.Length != 2) Error(expr, "__scroll_win_set_buffered(wx, wy) expects 2 arguments");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitPushAAsWord();
                CompileIntoA(args[1]);
                EmitAsm("LD_B_A");
                EmitAsm("POP_HL");

                EmitAsm("LD_A_L");
                EmitStoreA(ScrollWinXNextVar);
                EmitAsm("LD_A_B");
                EmitStoreA(ScrollWinYNextVar);
                EmitLoadA(ScrollDirtyVar);
                EmitAsm("OR_IMM", new AsmOperand(ScrollDirtyWinMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__scroll_win_show

Aktiviert das Fensterbit in LCDC und den nachgeführten Sichtbarkeitszustand des Fensters.

Syntax

c
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

c
__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.

__scroll_win_show: DMG
DMG
__scroll_win_show: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

Anwendungsbeispiel

c
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.

__scroll_win_show: DMG
DMG
__scroll_win_show: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/scroll.h:35

kitaqgb/kitaqgb/CodeGenerator.cs:7807

csharp
if (funcName == "__scroll_win_show")
            {
                if (args.Length != 0) Error(expr, "__scroll_win_show() expects 0 arguments");
                EnsureScrollState();

                EmitAsm("LDH_A_MEM", new AsmOperand(0x40, AddressMode.HighMem)); // LCDC
                EmitAsm("OR_IMM", new AsmOperand(0x20, AddressMode.Immediate));
                EmitAsm("LDH_MEM_A", new AsmOperand(0x40, AddressMode.HighMem));
                EmitAsm("LD_A_IMM", new AsmOperand(0x20, AddressMode.Immediate));
                EmitStoreA(ScrollWinVisibleVar);
                return;
            }
__scroll_win_x_get

Liest den nachgeführten aktuellen WX-Wert des Fensters.

Syntax

c
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

c
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.

__scroll_win_x_get: DMG
DMG
__scroll_win_x_get: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

Anwendungsbeispiel

c
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.

__scroll_win_x_get: DMG
DMG
__scroll_win_x_get: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/scroll.h:19

kitaqgb/kitaqgb/CodeGenerator.cs:7653

csharp
if (funcName == "__scroll_win_x_get")
            {
                if (args.Length != 0) Error(expr, "__scroll_win_x_get() expects 0 arguments");
                EnsureScrollState();
                EmitLoadA(ScrollWinXCurVar);
                return;
            }
__scroll_win_x_set

Setzt sofort nur die rohe WX-Koordinate des Fensters.

Syntax

c
void __scroll_win_x_set(u8 wx);

Parameter

wx

Roher 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

c
__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.

__scroll_win_x_set: DMG
DMG
__scroll_win_x_set: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

Anwendungsbeispiel

c
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.

__scroll_win_x_set: DMG
DMG
__scroll_win_x_set: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/scroll.h:17

kitaqgb/kitaqgb/CodeGenerator.cs:7617

csharp
if (funcName == "__scroll_win_x_set")
            {
                if (args.Length != 1) Error(expr, "__scroll_win_x_set(wx) expects 1 argument");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitStoreA(ScrollWinXCurVar);
                EmitStoreA(ScrollWinXNextVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x4B, AddressMode.HighMem)); // WX
                EmitLoadA(ScrollWinYCurVar);
                EmitStoreA(ScrollWinYNextVar);
                EmitLoadA(ScrollDirtyVar);
                EmitAsm("AND_IMM", new AsmOperand(0xFF ^ ScrollDirtyWinMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__scroll_win_x_set_buffered

Merkt nur Fenster-WX vor.

Syntax

c
void __scroll_win_x_set_buffered(u8 wx);

Parameter

wx

Roher 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

c
__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.

__scroll_win_x_set_buffered: DMG
DMG
__scroll_win_x_set_buffered: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

Anwendungsbeispiel

c
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.

__scroll_win_x_set_buffered: DMG
DMG
__scroll_win_x_set_buffered: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/scroll.h:26

kitaqgb/kitaqgb/CodeGenerator.cs:7743

csharp
if (funcName == "__scroll_win_x_set_buffered")
            {
                if (args.Length != 1) Error(expr, "__scroll_win_x_set_buffered(wx) expects 1 argument");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitStoreA(ScrollWinXNextVar);
                EmitLoadA(ScrollDirtyVar);
                EmitAsm("OR_IMM", new AsmOperand(ScrollDirtyWinMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__scroll_win_y_get

Liest den nachgeführten aktuellen WY-Wert des Fensters.

Syntax

c
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

c
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.

__scroll_win_y_get: DMG
DMG
__scroll_win_y_get: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

Anwendungsbeispiel

c
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.

__scroll_win_y_get: DMG
DMG
__scroll_win_y_get: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/scroll.h:20

kitaqgb/kitaqgb/CodeGenerator.cs:7662

csharp
if (funcName == "__scroll_win_y_get")
            {
                if (args.Length != 0) Error(expr, "__scroll_win_y_get() expects 0 arguments");
                EnsureScrollState();
                EmitLoadA(ScrollWinYCurVar);
                return;
            }
__scroll_win_y_set

Setzt sofort nur die WY-Koordinate des Fensters.

Syntax

c
void __scroll_win_y_set(u8 wy);

Parameter

wy

Oberkante 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

c
__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.

__scroll_win_y_set: DMG
DMG
__scroll_win_y_set: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

Anwendungsbeispiel

c
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.

__scroll_win_y_set: DMG
DMG
__scroll_win_y_set: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/scroll.h:18

kitaqgb/kitaqgb/CodeGenerator.cs:7635

csharp
if (funcName == "__scroll_win_y_set")
            {
                if (args.Length != 1) Error(expr, "__scroll_win_y_set(wy) expects 1 argument");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitStoreA(ScrollWinYCurVar);
                EmitStoreA(ScrollWinYNextVar);
                EmitAsm("LDH_MEM_A", new AsmOperand(0x4A, AddressMode.HighMem)); // WY
                EmitLoadA(ScrollWinXCurVar);
                EmitStoreA(ScrollWinXNextVar);
                EmitLoadA(ScrollDirtyVar);
                EmitAsm("AND_IMM", new AsmOperand(0xFF ^ ScrollDirtyWinMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }
__scroll_win_y_set_buffered

Merkt nur Fenster-WY vor.

Syntax

c
void __scroll_win_y_set_buffered(u8 wy);

Parameter

wy

Oberkante 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

c
__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.

__scroll_win_y_set_buffered: DMG
DMG
__scroll_win_y_set_buffered: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

Anwendungsbeispiel

c
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.

__scroll_win_y_set_buffered: DMG
DMG
__scroll_win_y_set_buffered: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

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

kitaqgb/lib/scroll.h:27

kitaqgb/kitaqgb/CodeGenerator.cs:7757

csharp
if (funcName == "__scroll_win_y_set_buffered")
            {
                if (args.Length != 1) Error(expr, "__scroll_win_y_set_buffered(wy) expects 1 argument");
                EnsureScrollState();

                CompileIntoA(args[0]);
                EmitStoreA(ScrollWinYNextVar);
                EmitLoadA(ScrollDirtyVar);
                EmitAsm("OR_IMM", new AsmOperand(ScrollDirtyWinMask, AddressMode.Immediate));
                EmitStoreA(ScrollDirtyVar);
                return;
            }

sprite.h — 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

c
void __oam_dma(u16 src_ptr);

Parameter

src_ptr

Quelladresse in Byte. Nur das obere Byte wird verwendet: 0xC480 wählt 0xC400–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

c
// 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.

__oam_dma: DMG
DMG
__oam_dma: CGB
CGB

Vollständiges Beispielprogramm

Build-Befehl

powershell
New-Item -ItemType Directory -Force .\out | Out-Null
.\kitaqgb\kitaqgb.exe .\kitaq-docs\samples\api-examples\gb\oam_dma.c -I .\kitaqgb\lib -I .\kitaq-docs\samples -o .\out\oam_dma.gb --cgb=cgb --profile=dev --rst-disable --stack-bank=fixed --no-cache --no-disasm
Implementierung und Quelltext

kitaqgb/lib/sprite.h:46

csharp
// Copy one 160-byte, page-aligned OAM source and return after DMA completes.
            if (funcName == "__oam_dma")
            {
                if (args.Length != 1) Error(expr, "__oam_dma(src_ptr) expects 1 argument");

                CompileIntoHL(args[0]); // Evaluate once; hardware uses only the high byte.
                // Mask interrupt sources without changing IME, including calls made inside an ISR.
                EmitAsm("LD_A_MEM", new AsmOperand(0xFFFF, AddressMode.Absolute));
                EmitAsm("PUSH_AF");
                EmitAsm("XOR_A");
                EmitAsm("LD_MEM_A", new AsmOperand(0xFFFF, AddressMode.Absolute));
                // LDH [FF46],A; LD B,40; DEC B; JR NZ,-3; RET.
                // Initialize on every call, so no startup copy or readiness flag is needed.
                byte[] dmaStub = { 0xE0, 0x46, 0x06, 0x28, 0x05, 0x20, 0xFD, 0xC9 };
                for (int i = 0; i < dmaStub.Length; i++)
                {
                    EmitAsm("LD_A_IMM", new AsmOperand(dmaStub[i], AddressMode.Immediate));
                    EmitAsm("LDH_MEM_A", new AsmOperand((OamDmaStubAddr + i) & 0xFF, AddressMode.HighMem));
                }
                EmitAsm("LD_A_H");
                // CALL pushes before DMA starts; RET reads the stack only after the wait.
                EmitAsm("CALL", new AsmOperand(OamDmaStubAddr, AddressMode.Absolute));
                EmitAsm("POP_AF");
                EmitAsm("LD_MEM_A", new AsmOperand(0xFFFF, AddressMode.Absolute));
                return;
            }

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
help
KITAQGB native Rust compiler

Commands:
  disasm ROM -o dis.s
  header ROM -o patched.gb [--title=NAME --cgb=dmg|cgb|cgb_only ...]
  assemble-ir INPUT.assembly.json -o output.gb [header options]
  tokenize SOURCE.c ... -I INCLUDE_DIR -o tokens.json
  parse SOURCE.c ... -I INCLUDE_DIR -o ast.json
  lower SOURCE.c ... -I INCLUDE_DIR -o lowered.json
  emit-ir SOURCE.c ... -I INCLUDE_DIR -o raw.assembly.json
  compile SOURCE.c ... -I INCLUDE_DIR -o output.gb [-O0|-O1]
  optimize-ir INPUT.assembly.json -o optimized.json [-O0|-O1]
  strip-ir INPUT.assembly.json -o stripped.json [--strip-keep=LABELS]

C compilation, aggregates, banked calls and hardware intrinsics are implemented and covered by differential tests. Utility commands: kqhelp, symfind, src2asm, romdiff, template, snippet, conventions, fixhint, irsum, attrviz, recipe, test.

Development and recovery:
  SOURCE.c ... --watch [--watch-max-builds=N]
  devserver SOURCE.c ... [--once --poll-ms=N --debounce-ms=N --max-builds=N]
  recipe [--history=FILE --out=FILE --script=FILE --last=N --stdout]
  SOURCE.c ... --minimize[=FILE] [--minimize-out=FILE --minimize-work=DIR --minimize-quick --minimize-trace=final|all]
  SOURCE.c ... --minimize-on-fail --repro-pack[=DIR]
  SOURCE.c ... --debug-output --disasm-changed[=GIT_BASE]
Watch loops stop with Ctrl+C; the Rust API also accepts a CancellationToken. See PORT_STATUS.md for executed verification and compatibility limits.

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
usage: kitaqgb kqhelp <KQxxxx>
usage
usage: kitaqgb symfind <pattern> [--map=<.map>] [--dbg=<.dbg>] [--regex] [--ignore-case] [--out=<file>|-]
usage
usage: kitaqgb src2asm <source.c:line> [--disasm=<dis.s>] [--funcsizes=<.funcsizes.txt>] [--context=N] [--out=<file>|-]
usage
usage: kitaqgb romdiff <old.gb> <new.gb> [--old-func=<.funcsizes.txt>] [--new-func=<.funcsizes.txt>] [--old-map=<.map>] [--new-map=<.map>] [--out=<file>|-]
usage
usage: kitaqgb template [out.c] [--out=<file.c>] [--cgb] [--overwrite] [--no-build-script] [--no-readme]
usage
usage: kitaqgb conventions [--out=<file>|--stdout]
usage
usage: kitaqgb snippet list | get <id> [--out=<file>|-] | emit <category|all> [--out=<file>|-]
usage
usage: kitaqgb fixhint [diag.json|stderr.log] [--in=<file>] [--out=<file>|-] [--max=N]
usage
usage: kitaqgb irsum [--ir=<syntax_tree_lowered.txt>] [--asm=<assembly_code.txt>] [--top=N] [--out=<file>|-]
usage
usage: kitaqgb devserver [--poll-ms=N] [--debounce-ms=N] [--once] [--max-builds=N] [--deps=<file>] <compile args...>
usage
usage: kitaqgb recipe [--history=<file>] [--last=N] [--out=<file>] [--script=<file>] [--stdout]

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-out

Anhang: 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.

opcodeBefehlFormatSyntaxbeispiel
00NOPIMPNOP
F3DIIMPDI
FBEIIMPEI
76HALTIMPHALT
10STOPIMM8STOP #1
C9RETIMPRET
C0RET_NZIMPRET_NZ
C8RET_ZIMPRET_Z
D0RET_NCIMPRET_NC
D8RET_CIMPRET_C
D9RETIIMPRETI
C3JPABSJP 0xC000
E9JP_HLIMPJP_HL
18JRRELJR +target
20JR_NZRELJR_NZ +target
28JR_ZRELJR_Z +target
30JR_NCRELJR_NC +target
38JR_CRELJR_C +target
CDCALLABSCALL 0xC000
C4CALL_NZABSCALL_NZ 0xC000
CCCALL_ZABSCALL_Z 0xC000
D4CALL_NCABSCALL_NC 0xC000
DCCALL_CABSCALL_C 0xC000
C2JP_NZABSJP_NZ 0xC000
CAJP_ZABSJP_Z 0xC000
D2JP_NCABSJP_NC 0xC000
DAJP_CABSJP_C 0xC000
C7RST_00IMPRST_00
CFRST_08IMPRST_08
D7RST_10IMPRST_10
DFRST_18IMPRST_18
E7RST_20IMPRST_20
EFRST_28IMPRST_28
F7RST_30IMPRST_30
FFRST_38IMPRST_38
3ELD_A_IMMIMM8LD_A_IMM #1
06LD_B_IMMIMM8LD_B_IMM #1
0ELD_C_IMMIMM8LD_C_IMM #1
16LD_D_IMMIMM8LD_D_IMM #1
1ELD_E_IMMIMM8LD_E_IMM #1
26LD_H_IMMIMM8LD_H_IMM #1
2ELD_L_IMMIMM8LD_L_IMM #1
7FLD_A_AIMPLD_A_A
78LD_A_BIMPLD_A_B
79LD_A_CIMPLD_A_C
7ALD_A_DIMPLD_A_D
7BLD_A_EIMPLD_A_E
7CLD_A_HIMPLD_A_H
7DLD_A_LIMPLD_A_L
47LD_B_AIMPLD_B_A
40LD_B_BIMPLD_B_B
41LD_B_CIMPLD_B_C
42LD_B_DIMPLD_B_D
43LD_B_EIMPLD_B_E
44LD_B_HIMPLD_B_H
45LD_B_LIMPLD_B_L
4FLD_C_AIMPLD_C_A
48LD_C_BIMPLD_C_B
49LD_C_CIMPLD_C_C
4ALD_C_DIMPLD_C_D
4BLD_C_EIMPLD_C_E
4CLD_C_HIMPLD_C_H
4DLD_C_LIMPLD_C_L
57LD_D_AIMPLD_D_A
50LD_D_BIMPLD_D_B
51LD_D_CIMPLD_D_C
52LD_D_DIMPLD_D_D
53LD_D_EIMPLD_D_E
54LD_D_HIMPLD_D_H
55LD_D_LIMPLD_D_L
5FLD_E_AIMPLD_E_A
58LD_E_BIMPLD_E_B
59LD_E_CIMPLD_E_C
5ALD_E_DIMPLD_E_D
5BLD_E_EIMPLD_E_E
5CLD_E_HIMPLD_E_H
5DLD_E_LIMPLD_E_L
67LD_H_AIMPLD_H_A
60LD_H_BIMPLD_H_B
61LD_H_CIMPLD_H_C
62LD_H_DIMPLD_H_D
63LD_H_EIMPLD_H_E
64LD_H_HIMPLD_H_H
65LD_H_LIMPLD_H_L
6FLD_L_AIMPLD_L_A
68LD_L_BIMPLD_L_B
69LD_L_CIMPLD_L_C
6ALD_L_DIMPLD_L_D
6BLD_L_EIMPLD_L_E
6CLD_L_HIMPLD_L_H
6DLD_L_LIMPLD_L_L
EALD_MEM_AABSLD_MEM_A 0xC000
FALD_A_MEMABSLD_A_MEM 0xC000
E0LDH_MEM_ALDHLDH_MEM_A 0x40
F0LDH_A_MEMLDHLDH_A_MEM 0x40
E2LD_C_MEM_AIMPLD_C_MEM_A
F2LD_A_MEM_CIMPLD_A_MEM_C
77LD_HL_AIMPLD_HL_A
7ELD_A_HLIMPLD_A_HL
46LD_B_HLIMPLD_B_HL
4ELD_C_HLIMPLD_C_HL
56LD_D_HLIMPLD_D_HL
5ELD_E_HLIMPLD_E_HL
66LD_H_HLIMPLD_H_HL
6ELD_L_HLIMPLD_L_HL
70LD_HL_BIMPLD_HL_B
71LD_HL_CIMPLD_HL_C
72LD_HL_DIMPLD_HL_D
73LD_HL_EIMPLD_HL_E
74LD_HL_HIMPLD_HL_H
75LD_HL_LIMPLD_HL_L
36LD_HL_REF_IMMIMM8LD_HL_REF_IMM #1
12LD_DE_AIMPLD_DE_A
1ALD_A_DEIMPLD_A_DE
02LD_BC_AIMPLD_BC_A
0ALD_A_BCIMPLD_A_BC
22LDI_HL_AIMPLDI_HL_A
2ALDI_A_HLIMPLDI_A_HL
32LDD_HL_AIMPLDD_HL_A
3ALDD_A_HLIMPLDD_A_HL
01LD_BC_IMMIMM16LD_BC_IMM #0xC000
11LD_DE_IMMIMM16LD_DE_IMM #0xC000
21LD_HL_IMMIMM16LD_HL_IMM #0xC000
31LD_SP_IMMIMM16LD_SP_IMM #0xC000
F9LD_SP_HLIMPLD_SP_HL
F8LD_HL_SP_IMMRELLD_HL_SP_IMM +target
E8ADD_SP_IMMRELADD_SP_IMM +target
08LD_MEM_SPIMM16LD_MEM_SP #0xC000
C5PUSH_BCIMPPUSH_BC
C1POP_BCIMPPOP_BC
D5PUSH_DEIMPPUSH_DE
D1POP_DEIMPPOP_DE
E5PUSH_HLIMPPUSH_HL
E1POP_HLIMPPOP_HL
F5PUSH_AFIMPPUSH_AF
F1POP_AFIMPPOP_AF
3CINC_AIMPINC_A
04INC_BIMPINC_B
0CINC_CIMPINC_C
14INC_DIMPINC_D
1CINC_EIMPINC_E
24INC_HIMPINC_H
2CINC_LIMPINC_L
34INC_HL_REFIMPINC_HL_REF
3DDEC_AIMPDEC_A
05DEC_BIMPDEC_B
0DDEC_CIMPDEC_C
15DEC_DIMPDEC_D
1DDEC_EIMPDEC_E
25DEC_HIMPDEC_H
2DDEC_LIMPDEC_L
35DEC_HL_REFIMPDEC_HL_REF
87ADD_AIMPADD_A
80ADD_BIMPADD_B
81ADD_CIMPADD_C
82ADD_DIMPADD_D
83ADD_EIMPADD_E
84ADD_HIMPADD_H
85ADD_LIMPADD_L
86ADD_HL_REFIMPADD_HL_REF
C6ADD_A_IMMIMM8ADD_A_IMM #1
8FADC_AIMPADC_A
88ADC_BIMPADC_B
89ADC_CIMPADC_C
8AADC_DIMPADC_D
8BADC_EIMPADC_E
8CADC_HIMPADC_H
8DADC_LIMPADC_L
8EADC_HL_REFIMPADC_HL_REF
CEADC_IMMIMM8ADC_IMM #1
97SUB_AIMPSUB_A
90SUB_BIMPSUB_B
91SUB_CIMPSUB_C
92SUB_DIMPSUB_D
93SUB_EIMPSUB_E
94SUB_HIMPSUB_H
95SUB_LIMPSUB_L
96SUB_HL_REFIMPSUB_HL_REF
D6SUB_IMMIMM8SUB_IMM #1
9FSBC_AIMPSBC_A
98SBC_BIMPSBC_B
99SBC_CIMPSBC_C
9ASBC_DIMPSBC_D
9BSBC_EIMPSBC_E
9CSBC_HIMPSBC_H
9DSBC_LIMPSBC_L
9ESBC_HL_REFIMPSBC_HL_REF
DESBC_IMMIMM8SBC_IMM #1
A7AND_AIMPAND_A
A0AND_BIMPAND_B
A1AND_CIMPAND_C
A2AND_DIMPAND_D
A3AND_EIMPAND_E
A4AND_HIMPAND_H
A5AND_LIMPAND_L
A6AND_HL_REFIMPAND_HL_REF
E6AND_IMMIMM8AND_IMM #1
B7OR_AIMPOR_A
B0OR_BIMPOR_B
B1OR_CIMPOR_C
B2OR_DIMPOR_D
B3OR_EIMPOR_E
B4OR_HIMPOR_H
B5OR_LIMPOR_L
B6OR_HL_REFIMPOR_HL_REF
F6OR_IMMIMM8OR_IMM #1
AFXOR_AIMPXOR_A
A8XOR_BIMPXOR_B
A9XOR_CIMPXOR_C
AAXOR_DIMPXOR_D
ABXOR_EIMPXOR_E
ACXOR_HIMPXOR_H
ADXOR_LIMPXOR_L
AEXOR_HL_REFIMPXOR_HL_REF
EEXOR_IMMIMM8XOR_IMM #1
BFCP_AIMPCP_A
B8CP_BIMPCP_B
B9CP_CIMPCP_C
BACP_DIMPCP_D
BBCP_EIMPCP_E
BCCP_HIMPCP_H
BDCP_LIMPCP_L
BECP_HL_REFIMPCP_HL_REF
FECP_IMMIMM8CP_IMM #1
03INC_BCIMPINC_BC
0BDEC_BCIMPDEC_BC
13INC_DEIMPINC_DE
1BDEC_DEIMPDEC_DE
23INC_HLIMPINC_HL
2BDEC_HLIMPDEC_HL
33INC_SPIMPINC_SP
3BDEC_SPIMPDEC_SP
09ADD_HL_BCIMPADD_HL_BC
19ADD_HL_DEIMPADD_HL_DE
29ADD_HL_HLIMPADD_HL_HL
39ADD_HL_SPIMPADD_HL_SP
07RLCAIMPRLCA
17RLAIMPRLA
0FRRCAIMPRRCA
1FRRAIMPRRA
CBPREFIX_CBIMPPREFIX_CB
2FCPLIMPCPL
3FCCFIMPCCF
37SCFIMPSCF
27DAAIMPDAA