---
title: 'TH04 Rückentwicklung: DOS-Bullet-Muster - REA'
url: https://rea.tools/de/showcase/th04/
---

[Startseite](https://rea.tools/de/) / [Präsentieren](https://rea.tools/de/showcase/) Modell: TH04

Fallstudie \* Touhou 4: Lotus Land Geschichte

# Rekonstruktion des TH04 aufzählungsmuster.

Ein Kugelring beginnt mit einer kurzen Winkelberechnung. Verwenden Sie REA, um die ursprüngliche DOS-Funktion zu überprüfen, und folgen Sie dann den Anweisungen in lesbares C ++.

PC-98 · 16-Bit-DOS · C++ und Assemblierung

[Sehen Sie sich die Rekonstruktion des TH04 an](https://github.com/N0zoM1z0/th04)

Von der Originalanleitung zur geprüften Quelle

1. 01 \* Original-Spiel

   **WICHTIGSTEN.EXE**

   Die ausführbare Datei PC-98 und ihr Aufzählungswinkel-Helfer.
2. 02 \* Inspektion mit REA

   **Anleitungen + Referenzen**

   Überprüfen Sie den geladenen DOS-Code und lesen Sie die Winkelberechnung aus der ausführbaren Datei.
3. 03 \* Lesen Sie die C ++

   **Leertaste → Zielen → Drehen**

   Verbinden Sie jede Operation mit der gepflegten Rekonstruktion.
4. 04 \* Überprüfen Sie die Quelle

   **2.139 übereinstimmende Bytes**

   Aufgezeichnete Compilerprüfungen decken den Code zur Aufzählungszeichengenerierung ab, der diesen Helfer enthält.

REA gibt den Originalcode und seine Analysebeweise zurück. Das Projekt TH04 liefert die gepflegten Quell- und Compilerprüfungen.

## Wie ein Ring auf den Spieler zielt

Ellys Angriff fordert einen gezielten Ring mit einer anfänglichen Anzahl von 16 an. Ein Helfer berechnet die Richtung jeder Kugel. Derselbe Ring kann in eine feste Richtung zeigen oder sich dem Spieler zuwenden.

Ihr Codierungsagent

Verwenden Sie REA, um herauszufinden, wie TH04 einen Kugelring erstellt und auf den Spieler richtet. Erklären Sie die Berechnung und zeigen Sie den Code.

Eine Beispiel-Eingabeaufforderung, bei der die ursprüngliche ausführbare Datei und der Rekonstruktions-Checkout lokal verfügbar sind.

TH04 speichert eine volle Drehung im Uhrzeigersinn in 256 Winkeleinheiten. Bei 16 Aufzählungszeichen addiert jeder Index 16 Einheiten: `0, 16, 32, …, 240`. Das Hinzufügen der Richtung des Spielers dreht diese Positionen zusammen.

![Zwei Diagramme eines 16-Kugeln-Rings. Der feste Ring beginnt bei Winkel Null. Wenn Sie eine Spielerrichtung von 40 hinzufügen, wird jede Richtung um den gleichen Betrag gedreht. Der hervorgehobene erste Punkt zeigt auf den Spieler. Beide halten einen Abstand von 16 Winkeleinheiten oder 22,5 Grad ein.](https://rea.tools/assets/localized/de/figures/th04-bullet-ring.svg)

Eine Illustration der wiederhergestellten Winkelmathematik für Zählung 16, Spielerrichtung 40 und Schablonendrehung 0. [Offene Figur](https://rea.tools/assets/localized/de/figures/th04-bullet-ring.svg)

### Die Zündvorlage in der wiederhergestellten Quelle

Ellys Ringangriff \* Quellenauszug

```
bullet_template.angle = 0;
bullet_template.group = BG_RING_AIMED;
bullet_template.count = 16;
bullet_template_tune();
```

Diese Linien wählen das Muster vor der Schwierigkeits- und Leistungsabstimmung aus. [Lesen Sie die Quelle des Angriffs](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/src/main/boss/elly_phase_ring16.cpp#L26).

## Was REA zurückgibt

Wir prüfen `bullet_velocity_and_angle_set()`, die Funktion, die die Geschwindigkeit und den Winkel jedes Geschosses festlegt. REA gibt seine Anweisungen, Pseudocode und Referenzen zurück. Drei Teile dieses Ergebnisses erklären das Muster.

1. #### Überprüfen Sie das DOS-Image, bevor Sie den Code lesen

   Vertreter → REA

   ```
   open_binary
   {"path": "MAIN.EXE"}

   inspect_native_load_image
   {}
   ```

   REA → Agent \* ausgewählte Felder

   ```
   status: "verified"
   format: "dos-mz"
   load_segment: 4096
   header_bytes: 6144
   module_bytes: 150114
   ```

   Die Prüfung bindet den geladenen Code und die Umzugsdatensätze an die gelieferte ausführbare Datei. Der obige Pfad ist für die Anzeige verkürzt.
2. #### Lesen Sie die Multiplikation und Division des Rings

   Vertreter → REA · `analyze_function`

   ```
   {"procedure": "0x2cfc8"}
   ```

   REA → Agent \* Anweisungsauszug

   ```
   0x2d0eb: MOV AX, SI
   0x2d0ed: SHL AX, 0x8
   0x2d0f0: MOV DL, byte ptr [0x53af]
   0x2d0f9: IDIV BX
   ```

   `SI` enthält den Aufzählungszeichenindex. Die Verschiebung multipliziert es mit 256, dann verwendet die Division die gelesene Zählung `DS:53AF`. Die gepflegte Quelle benennt dieses Feld `bullet_template.count`. Die vollständige Sequenz erscheint unten.
3. #### Finde heraus, wo sich das Muster dem Spieler zuwendet

   Gleiches Funktionsergebnis \* Zielauszug

   ```
   0x2d190: SUB AX, word ptr [0x53a6]
   0x2d198: SUB AX, word ptr [0x53a4]
   0x2d19d: CALLF 0x1000:1da8
   0x2d1a2: ADD word ptr [BP + -0x2], AX
   ```

   Entsprechendes gepflegtes C ++

   ```
   group_angle_offset += iatan2(
     player_pos.cur.y - bullet_template.origin.y,
     player_pos.cur.x - bullet_template.origin.x
   );
   ```

   Die Anweisungsoperanden legen zwei Koordinatendifferenzen und einen Richtungshelfer offen. Sein Rückgabewert wird zum relativen Ringwinkel addiert. Quellennamen machen diese Adressen lesbar; Mit REA kann der Agent die ursprünglichen Vorgänge dahinter überprüfen.

## Von Assembly zu lesbarem C ++

Wählen Sie einen Schritt aus, um die ursprünglichen Anweisungen mit der Winkelberechnung abzugleichen. Diese Zusammenfassung folgt dem Zielringpfad; Die vollständige Quelle behandelt auch Spreads, Stapel und Zufallsmuster.

REA \* originale 16-Bit-Anleitung

```
0x2cfcf: MOV SI, word ptr [BP + 0x4]
0x2d0eb: MOV AX, SI
0x2d0ed: SHL AX, 0x8
0x2d0f0: MOV DL, byte ptr [0x53af]
0x2d0f4: MOV DH, 0x0
0x2d0f6: PUSH DX
0x2d0f7: CWD
0x2d0f8: POP BX
0x2d0f9: IDIV BX
0x2d0fb: MOV word ptr [BP + -0x2], AX
0x2d18d: MOV AX, [0x4650]
0x2d190: SUB AX, word ptr [0x53a6]
0x2d194: PUSH AX
0x2d195: MOV AX, [0x464e]
0x2d198: SUB AX, word ptr [0x53a4]
0x2d19c: PUSH AX
0x2d19d: CALLF 0x1000:1da8
0x2d1a2: ADD word ptr [BP + -0x2], AX
0x2d1b9: MOV AL, byte ptr [BP + -0x2]
0x2d1bc: ADD AL, byte ptr [0x53ad]
0x2d1c0: MOV [0xbcc8], AL
```

Lesbare C ++ \* zielgerichtete Ring-Zusammenfassung

```
unsigned char aimed_ring_angle(
    int index, int count,
    int player_direction,
    unsigned char rotation)
{
    int angle = index;
    angle = (angle * 256) / count;
    angle += player_direction;
    angle += rotation;
    return (unsigned char)angle;
}
```

**01 \* Lesen Sie den Aufzählungszeichenindex.** Das Stapelargument wird geladen in `SI`. In einem 16-Punkte-Ring verläuft dieser Mitgliederindex von 0 bis 15.

**02 \* Platziere die Richtungen um eine volle Umdrehung.** `SHL AX,8` multipliziert mit 256. Die Zählung ist Null-erweitert zu einem Wort, dann `IDIV` dividiert das Produkt durch diese Anzahl. Für Zählung 16 rückt jeder Index 16 Winkeleinheiten vor.

**03 \* Fügen Sie die Spielerrichtung hinzu.** Die beiden Subtraktionen bilden Spieler-minus-Ursprungskoordinaten. Der Richtungshelfer gibt einen Winkel in zurück `AX`; das Hinzufügen dreht den ganzen Ring in Richtung des Spielers.

**04 \* Fügen Sie die Drehung des Musters hinzu.** `ADD AL,[0x53ad]` fügt den Schablonenwinkel hinzu. Ellys anfängliche Vorlage setzt es auf Null; Andere Muster können denselben Helfer mit einer Drehung verwenden.

**05 \* Halten Sie den endgültigen Winkel in einem Byte.** Der Laden schreibt `AL` zum Spawnwinkelfeld. Werte werden nach 255 umgebrochen, genau wie bei der Umwandlung ohne Vorzeichen. Die umgebende Quelle wandelt diesen Winkel und diese Geschwindigkeit ebenfalls in Geschwindigkeit um.

Die Baugruppe wählt den Zielringzweig, die Zielberechnung und den Endspeicher aus. Das obige C ++ ist eine lesbare Zusammenfassung. Die folgenden Compiler-Prüfungen gelten für die gepflegte Quelle im verknüpften Projekt.

Lesen Sie die gepflegte Ring- und Zielquelle

Ringmakro \* TH04 Zweig, Auszug

```
angle_offset = (i * BULLET_ANGLE_FULL_TURN) / count;
if (i >= (count - 1)) {
    group_complete = true;
}
goto aim_or_no_aim;
```

Winkel und Geschwindigkeit \* Quellenauszug

```
aim:
    group_angle_offset += iatan2(
        (player_pos.cur.y - bullet_template.origin.y),
        (player_pos.cur.x - bullet_template.origin.x)
    );

no_aim:
    vector2_near(
        bullet_template.velocity,
        (group_angle_offset + bullet_template.angle),
        speed
    );
    bullet_spawn_angle = (group_angle_offset + bullet_template.angle);
```

[Ringmakro](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/src/main/bullet/add_impl.hpp#L4) · [Ziel- und Geschwindigkeitsquelle](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/src/main/bullet/add.cpp#L422) · [Winkeldarstellung](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/src/main/bullet/types.hpp#L6)

## Überprüfen der gepflegten Quelle

Die aufgezeichneten Überprüfungen des TH04-Projekts kompilieren den Code zur Bullet-Generierung, einschließlich dieses Helpers, mit seiner angehefteten Turbo C ++ 4.0J-Toolchain.

### 2.139 übereinstimmende Bytes

Der kompilierte Code zur Aufzählungszeichengenerierung entspricht einem 2.139 Byte großen Abschnitt der ursprünglichen ausführbaren Datei.

### Zwei saubere Builds

Zwei separate Builds erzeugen dieselben übereinstimmenden Bytes. Die aufgezeichneten Eingabe-Hashes stimmen mit der hier gezeigten gepflegten Quelle überein.

Aufgenommen am 3. Oktober 2026. [Lesen Sie die Compiler-Prüfungen](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/docs/reconstruction/product/TH04_SEMANTIC_BULLET_GENERATION_V1236.md#validation).

Zielidentität und Analyseumfang

|  |  |
| --- | --- |
| Ursprüngliches Ziel | `MAIN.EXE`, 156.258 bytes, ZWEI MZ |
| Hilfseintrag | `0x2cfc8` im geladenen Bild von REA |
| Versatz der ursprünglichen Datei | `0x1e7c8`, zurückgegeben von `address_to_file_offset` |
| Inspektion aufgezeichneter Codes | REA 4.1.0 · 7. Oktober 2026 |

Compiler-Prüfungen decken den gesamten Abschnitt zum Hinzufügen von Code mit Aufzählungszeichen ab, einschließlich dieses Hilfsprogramms. Sie vergleichen auch den Kartenbeitrag und die überlappende Verlagerung.

REA hat das statische DOS-Ladebild am Ladesegment überprüft `0x1000`. Seine Adressen beschreiben dieses Analysebild. Das Funktionsergebnis meldet 503 Körperbytes in zwei Bereichen über einen Zeitraum von 517 Byte; Die oben ausgewählten Anweisungen liegen in diesen gemeldeten Bereichen.

Die Quellnamen und Compilerprüfungen stammen vom Prüfpunkt TH04 `0d72e980`. Frühere Rekonstruktionsbeobachtungen verwendeten die projekteigenen Analyseskripte; Diese Seite fügt eine neue REA-Inspektion desselben ursprünglichen Ziels hinzu. Die Identität des gelieferten Spiels wird durch sein Repository-Manifest festgelegt, wobei die unabhängige Herkunft des unberührten Dumps noch offen ist.

Die Abbildung veranschaulicht die Winkelberechnung. Steuerung des vollständigen Spiels, Timing und Verhalten der PC-98-Hardware haben separate Laufzeitprüfungen im Rekonstruktionsprojekt.

## Die vollständige TH04-Rekonstruktion

Das Projekt erstellt die vier DOS-Produkte des Spiels aus gepflegtem C ++ und Assembly. Über die Aufzählungszeichenerzeugung hinaus umfasst die Quelle Stufen, Bosse, Rendering, Eingabe, Menüs, Endungen und Partiturbehandlung.

[Erkunden Sie das TH04-Repository](https://github.com/N0zoM1z0/th04)

- [Quelle zur Erzeugung von Kugeln](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/src/main/bullet/add.cpp#L292)Der komplette Helfer, andere Mustertypen und Template-Tuning.
- [DOS-Anleitung zum Erstellen und Ausführen](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/docs/DOS_BUILD.md)Erstellen Sie die Quelle und testen Sie sie im PC-98-Emulator-Workflow des Projekts.
- [Aktueller Projektstand](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/docs/RE_HANDOFF.md)DOS-Verifizierung und den separaten nativen x64-Port.
