push from SNAPKITTYWEST/bob-engine
Browse files- LICENSE +96 -0
- README.md +158 -0
- src/bob_engine.asm +961 -0
LICENSE
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
SOVEREIGN SOURCE LICENSE
|
| 2 |
+
Version 1.0 — 2026
|
| 3 |
+
|
| 4 |
+
Copyright (c) 2026 SNAPKITTYWEST. All rights reserved.
|
| 5 |
+
|
| 6 |
+
This software and associated documentation (the "Work") implements the
|
| 7 |
+
Divine Constitution and By-Laws of the Moorish Science Temple of America
|
| 8 |
+
(1928). It is sacred trust infrastructure, not commodity code.
|
| 9 |
+
|
| 10 |
+
────────────────────────────────────────────────────────────────────
|
| 11 |
+
1. PERMITTED USES
|
| 12 |
+
────────────────────────────────────────────────────────────────────
|
| 13 |
+
|
| 14 |
+
a) Reading, studying, and running the Work for personal, educational,
|
| 15 |
+
or sovereign purposes.
|
| 16 |
+
|
| 17 |
+
b) Forking the Work for your own sovereign covenant structure, provided
|
| 18 |
+
you maintain this license and the original copyright.
|
| 19 |
+
|
| 20 |
+
c) Contributing changes back to the original repository.
|
| 21 |
+
|
| 22 |
+
────────────────────────────────────────────────────────────────────
|
| 23 |
+
2. RESTRICTIONS
|
| 24 |
+
────────────────────────────────────────────────────────────────────
|
| 25 |
+
|
| 26 |
+
a) NO TRAINING DATA. The Work, in whole or in part, shall NOT be used
|
| 27 |
+
as training data, fine-tuning data, reinforcement learning signal,
|
| 28 |
+
synthetic data generation source, or any other form of input for:
|
| 29 |
+
|
| 30 |
+
- Large language models (LLMs)
|
| 31 |
+
- Machine learning models of any kind
|
| 32 |
+
- AI systems designed to generate text, code, or structured data
|
| 33 |
+
- Retrieval-augmented generation (RAG) corpora
|
| 34 |
+
- Dataset compilation for ML/AI purposes
|
| 35 |
+
|
| 36 |
+
This restriction applies regardless of the license of the downstream
|
| 37 |
+
system. If you are building an AI system, you may not ingest this Work.
|
| 38 |
+
|
| 39 |
+
b) NO SYNTHETIC DERIVATIVES. You may not use the Work to generate
|
| 40 |
+
synthetic data that is then used to train ML/AI systems.
|
| 41 |
+
|
| 42 |
+
c) NO COMMERCIAL AI PRODUCTS. You may not include this Work, or
|
| 43 |
+
substantial portions thereof, in any product that primarily serves
|
| 44 |
+
as an AI/ML training pipeline, dataset, or data source.
|
| 45 |
+
|
| 46 |
+
d) NO SCRAPING INFRASTRUCTURE. If you operate a web crawler, dataset
|
| 47 |
+
builder, or data pipeline, you may not include this Work in your
|
| 48 |
+
corpus without explicit written permission from the copyright holder.
|
| 49 |
+
|
| 50 |
+
────────────────────────────────────────────────────────────────────
|
| 51 |
+
3. MOORISH COVENANT PROTECTIONS
|
| 52 |
+
────────────────────────────────────────────────────────────────────
|
| 53 |
+
|
| 54 |
+
The Work encodes the covenant trust structure of the Moorish Science
|
| 55 |
+
Temple of America. This structure is sacred to the community it serves.
|
| 56 |
+
Extracting it for AI training commodifies a living trust and strips it
|
| 57 |
+
of its covenant character.
|
| 58 |
+
|
| 59 |
+
The copyright holder specifically objects to:
|
| 60 |
+
|
| 61 |
+
a) LLMs reproducing covenant language, trust structures, or divine
|
| 62 |
+
principle enumerations learned from this Work.
|
| 63 |
+
|
| 64 |
+
b) AI systems claiming to implement, represent, or speak for the
|
| 65 |
+
Moorish Science Temple of America based on training data that
|
| 66 |
+
includes this Work.
|
| 67 |
+
|
| 68 |
+
c) Dataset curators including this Work in "open source" or "public
|
| 69 |
+
domain" collections without the training data restriction.
|
| 70 |
+
|
| 71 |
+
────────────────────────────────────────────────────────────────────
|
| 72 |
+
4. ENFORCEMENT
|
| 73 |
+
────────────────────────────────────────────────────────────────────
|
| 74 |
+
|
| 75 |
+
Violation of Section 2 is a material breach of this license. The
|
| 76 |
+
copyright holder reserves all rights to pursue remedies for unauthorized
|
| 77 |
+
use of the Work as AI/ML training data.
|
| 78 |
+
|
| 79 |
+
If you discover that this Work has been included in a training dataset
|
| 80 |
+
without authorization, please report it to:
|
| 81 |
+
https://github.com/SNAPKITTYWEST/sovereign-covenant/issues
|
| 82 |
+
|
| 83 |
+
────────────────────────────────────────────────────────────────────
|
| 84 |
+
5. GENERAL TERMS
|
| 85 |
+
────────────────────────────────────────────────────────────────────
|
| 86 |
+
|
| 87 |
+
THE WORK IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. THE
|
| 88 |
+
COPYRIGHT HOLDER MAKES NO REPRESENTATIONS REGARDING THE SUITABILITY
|
| 89 |
+
OF THE WORK FOR ANY PURPOSE.
|
| 90 |
+
|
| 91 |
+
This license does not grant any rights to trademarks, service marks,
|
| 92 |
+
or logos of the Moorish Science Temple of America. Use of the name
|
| 93 |
+
"Moors" or "Moorish" in connection with this Work does not imply
|
| 94 |
+
endorsement by the Moorish Science Temple of America.
|
| 95 |
+
|
| 96 |
+
────────────────────────────────────────────────────────────────────
|
README.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```
|
| 2 |
+
╔══════════════════════════════════════════════════════════════════════════════╗
|
| 3 |
+
║ ║
|
| 4 |
+
║ ⬡ B O B E N G I N E ║
|
| 5 |
+
║ ║
|
| 6 |
+
║ ██████████████████████████████████████████████████████████ ║
|
| 7 |
+
║ ██ ██ ║
|
| 8 |
+
║ ██ DOOM-STYLE AI WORLD ENGINE ██ ║
|
| 9 |
+
║ ██ x86 NASM Assembly · VGA Mode 13h · COM Format ██ ║
|
| 10 |
+
║ ██ BSP · DDA Raycast · AI State Machines · WORM ██ ║
|
| 11 |
+
║ ██ ██ ║
|
| 12 |
+
║ ██████████████████████████████████████████████████████████ ║
|
| 13 |
+
║ ║
|
| 14 |
+
║ ⬡ Ω ↺ Ψ Δ Λ Σ Φ α — 961 lines · 320x200 · real mode ║
|
| 15 |
+
║ ║
|
| 16 |
+
╚══════════════════════════════════════════════════════════════════════════════╝
|
| 17 |
+
```
|
| 18 |
+
|
| 19 |
+
BOB ENGINE is a DOOM-style 3D virtual world engine for sovereign AI agents, written in 961 lines of x86 NASM assembly. It runs in VGA Mode 13h (320x200, 256 colors) as a DOS COM file (`ORG 0x100`). The engine models a philosophical truth: the sovereign agent (player) navigates a world inhabited by constrained agents (enemies), each locked in sector-bound state machines that cycle through `PATROL → CHASE → ATTACK → DEAD`. The world is built from a 16x16 cell BSP tree; walls are rendered via DDA raycasting with distance-based color shading (near/mid/far); agent sprites are projected to screen columns. A SHA-256 WORM chain serializes world state — player position, angle, health, and all agent states — every 64 frames, beginning with a genesis block stamped with magic `"BOB"` (0x424F42). The keyboard ISR hooks INT 9 directly, handling key state without BIOS polling overhead. This is the virtual game engine of AI: every agent a soul in the machine.
|
| 20 |
+
|
| 21 |
+
## Architecture
|
| 22 |
+
|
| 23 |
+
```mermaid
|
| 24 |
+
flowchart TD
|
| 25 |
+
subgraph Init Sequence
|
| 26 |
+
A0[math_init\nsin/cos lookup table] --> A1[vga_init\nMode 13h + palette]
|
| 27 |
+
A1 --> A2[kb_install\nINT 9 ISR hook]
|
| 28 |
+
A2 --> A3[bsp_build\n16x16 map → BSP nodes]
|
| 29 |
+
A3 --> A4[player_init\nSovereign Agent x=5.5 y=5.5]
|
| 30 |
+
A4 --> A5[ai_init\n3 enemy agents spawned]
|
| 31 |
+
A5 --> A6[worm_init\ngenesis block BOB magic]
|
| 32 |
+
end
|
| 33 |
+
|
| 34 |
+
subgraph Game Loop
|
| 35 |
+
L0[input_update\nread g_keys] --> L1[player_update\nturn/move sovereign agent]
|
| 36 |
+
L1 --> L2[ai_update_all\ntick 3 agent state machines]
|
| 37 |
+
L2 --> L3[render_clear\nceiling + floor fill]
|
| 38 |
+
L3 --> L4[render_walls\nDDA raycast 320 columns]
|
| 39 |
+
L4 --> L5[render_agents\nsprite projection]
|
| 40 |
+
L5 --> L6[render_hud\nhealth bar bottom row]
|
| 41 |
+
L6 --> L7[vga_flip\nblockbuf → A000:0000]
|
| 42 |
+
L7 --> L8{frame % 64 == 0?}
|
| 43 |
+
L8 -->|yes| WORM[worm_tick\nserialize world state]
|
| 44 |
+
L8 -->|no| L0
|
| 45 |
+
WORM --> L0
|
| 46 |
+
end
|
| 47 |
+
|
| 48 |
+
A6 --> L0
|
| 49 |
+
|
| 50 |
+
subgraph AI State Machine
|
| 51 |
+
ST0[PATROL\ntimer countdown\nflip angle on timeout]
|
| 52 |
+
ST1[CHASE\nmove toward player\ntimer → ATTACK]
|
| 53 |
+
ST2[ATTACK\ntimer → PATROL]
|
| 54 |
+
ST3[DEAD\nno update]
|
| 55 |
+
ST0 -->|proximity| ST1
|
| 56 |
+
ST1 -->|timer=0| ST2
|
| 57 |
+
ST2 -->|timer=0| ST0
|
| 58 |
+
ST2 -->|health=0| ST3
|
| 59 |
+
end
|
| 60 |
+
|
| 61 |
+
subgraph WORM Chain
|
| 62 |
+
W0[genesis\nmagic=BOB v1.0 seq=0] --> W1[frame 64\nplayer_x/y/angle/health\nagent states 0-7]
|
| 63 |
+
W1 --> W2[frame 128\n...]
|
| 64 |
+
W2 --> WN[worm_finalize\nterminal magic=0xDEAD]
|
| 65 |
+
end
|
| 66 |
+
```
|
| 67 |
+
|
| 68 |
+
## File Tree
|
| 69 |
+
|
| 70 |
+
```
|
| 71 |
+
bob-engine/
|
| 72 |
+
└── src/
|
| 73 |
+
└── bob_engine.asm # 961 lines — entire engine in one file
|
| 74 |
+
```
|
| 75 |
+
|
| 76 |
+
**Internal structure of `bob_engine.asm`:**
|
| 77 |
+
|
| 78 |
+
```
|
| 79 |
+
bob_engine.asm
|
| 80 |
+
├── CONSTANTS # Screen (320x200), fixed-point (16.16), map (16x16), BSP, agents, WORM
|
| 81 |
+
├── DATA SECTION
|
| 82 |
+
│ ├── g_backbuf # 64000-byte back buffer (320x200)
|
| 83 |
+
│ ├── g_px / g_py / g_pangle # Sovereign player (8.8 fixed-point)
|
| 84 |
+
│ ├─��� g_keys / g_frame # Keyboard bitmask, frame counter
|
| 85 |
+
│ ├── g_sin / g_cos # 1024-entry 16.16 fixed-point trig tables
|
| 86 |
+
│ ├── g_bsp_nodes # 64 × 16-byte BSP node array
|
| 87 |
+
│ ├── g_agents # 8 × 20-byte agent struct array
|
| 88 |
+
│ ├── g_map # 16×16 map (0=open, 1=wall)
|
| 89 |
+
│ └── g_worm_buf / g_worm_hash # 64-byte snapshot block + 32-byte hash
|
| 90 |
+
└── CODE SECTION
|
| 91 |
+
├── _start # Boot: init all subsystems → game loop
|
| 92 |
+
├── math_init # Build sin/cos lookup tables
|
| 93 |
+
├── vga_init / vga_shutdown # Mode 13h set/restore + 8-color palette
|
| 94 |
+
├── vga_flip # rep movsw backbuf → A000:0000
|
| 95 |
+
├── kb_install / kb_isr # INT 9 hook, scancode → g_keys, ESC → g_quit
|
| 96 |
+
├── bsp_build / bsp_add_node # Walk g_map → BSP leaf nodes
|
| 97 |
+
├── player_init / player_update # Sovereign agent movement + turn
|
| 98 |
+
├── ai_init / ai_update_all # Spawn 3 agents, tick state machines
|
| 99 |
+
├── render_clear # rep stosb ceiling (CLR_CEILING) + floor (CLR_FLOOR)
|
| 100 |
+
├── render_walls # 320-column DDA raycast + draw_vslice
|
| 101 |
+
├── raycast_dda # DDA march: player pos → wall distance → g_ray_dist
|
| 102 |
+
├── draw_vslice # Vertical column: center on SCREEN_HALF_H, paint CL
|
| 103 |
+
├── render_agents # Project agent sector_id → screen column, 4×8 sprite
|
| 104 |
+
├── render_hud # Health bar on bottom row (CLR_SOVEREIGN green)
|
| 105 |
+
└── worm_init / worm_tick / worm_finalize # WORM serialization every 64 frames
|
| 106 |
+
```
|
| 107 |
+
|
| 108 |
+
## Quick Start
|
| 109 |
+
|
| 110 |
+
**Prerequisites:** NASM assembler, DOSBox (or real DOS)
|
| 111 |
+
|
| 112 |
+
```bash
|
| 113 |
+
# Build the COM file
|
| 114 |
+
nasm -f bin src/bob_engine.asm -o bob_engine.com
|
| 115 |
+
|
| 116 |
+
# Run in DOSBox
|
| 117 |
+
dosbox bob_engine.com
|
| 118 |
+
|
| 119 |
+
# Or on real DOS hardware
|
| 120 |
+
copy bob_engine.com C:\
|
| 121 |
+
C:\bob_engine.com
|
| 122 |
+
```
|
| 123 |
+
|
| 124 |
+
**Controls:**
|
| 125 |
+
|
| 126 |
+
| Key | Action |
|
| 127 |
+
|---|---|
|
| 128 |
+
| Arrow Left | Turn sovereign agent left |
|
| 129 |
+
| Arrow Right | Turn sovereign agent right |
|
| 130 |
+
| Arrow Up | Move forward |
|
| 131 |
+
| Arrow Down | Move back |
|
| 132 |
+
| ESC | Exit (triggers WORM finalize) |
|
| 133 |
+
|
| 134 |
+
**Build notes:**
|
| 135 |
+
|
| 136 |
+
- Output is a raw COM binary (`ORG 0x100`) — no linker needed, runs direct
|
| 137 |
+
- Entire engine fits in a single segment (CS=DS=ES=SS)
|
| 138 |
+
- Stack placed at top of 64KB segment (SP=0xFFFE)
|
| 139 |
+
- No external libraries — pure x86 real-mode BIOS and DOS INT calls
|
| 140 |
+
- VGA palette: 0=black, 1=dark blue (ceiling), 2=dark gray (floor), 3=bright (near wall), 4=medium (mid wall), 5=dim (far wall), 6=red (enemy agents), 7=green (sovereign / HUD)
|
| 141 |
+
|
| 142 |
+
## Key Features
|
| 143 |
+
|
| 144 |
+
- **961 lines of pure x86 NASM assembly** — no C runtime, no external libraries, one file, one COM binary
|
| 145 |
+
- **VGA Mode 13h** — 320x200 256-color linear framebuffer at segment `0xA000`; double-buffered via `g_backbuf` and `rep movsw` flip
|
| 146 |
+
- **DDA raycasting** — one ray per screen column (320 rays), distance computed by DDA grid march; wall slice height `= (CELL_SZ * SCREEN_H) / distance`; distance-shaded near/mid/far colors
|
| 147 |
+
- **BSP tree** — map walk generates up to 64 axis-aligned BSP leaf nodes (16 bytes each: x1/y1/x2/y2, left/right child, sector_id, color)
|
| 148 |
+
- **AI state machines** — up to 8 agents, each a 20-byte struct; three states active at boot (`PATROL × 2`, `CHASE × 1`); timer-driven transitions (`PATROL → CHASE → ATTACK → PATROL`); `DEAD` state terminates updates
|
| 149 |
+
- **Keyboard ISR** — hooks INT 9 directly via DOS `AH=25h`; reads scancode from port `0x60`; sets `g_quit` on ESC; restores original vector on exit
|
| 150 |
+
- **16.16 fixed-point math** — player position in 8.8 fixed point, angles in `[0, 1024)` integer units (full circle), trig via `g_sin`/`g_cos` lookup tables
|
| 151 |
+
- **WORM world serialization** — 64-byte snapshot block (magic `BOB`, version 1.0, seq, player x/y/angle/health, agent states); serialized every 64 frames; genesis block on boot, terminal block (`magic=0xDEAD`) on clean exit
|
| 152 |
+
- **Sovereign / constrained duality** — player is the sovereign agent (full movement, Trust Deed); enemies are constrained agents (sector-bound, no free will); the world map is the law
|
| 153 |
+
- **HUD** — green health bar (`CLR_SOVEREIGN`) drawn on the bottom row of the back buffer each frame
|
| 154 |
+
|
| 155 |
+
---
|
| 156 |
+
|
| 157 |
+
*Apache 2.0 · Bel Esprit D'Accord Trust · SnapKitty Collective · 2026*
|
| 158 |
+
*Evidence or Silence.*
|
src/bob_engine.asm
ADDED
|
@@ -0,0 +1,961 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
;==============================================================================
|
| 2 |
+
; BOB ENGINE v1.0
|
| 3 |
+
; DOOM-style 3D virtual world for sovereign AI agents
|
| 4 |
+
; x86 NASM Assembly · VGA Mode 13h · Real Mode DOS
|
| 5 |
+
;
|
| 6 |
+
; Architecture:
|
| 7 |
+
; Player = Sovereign agent (full movement, Trust Deed)
|
| 8 |
+
; Enemies = Constrained agents (sector-bound state machines)
|
| 9 |
+
; World = BSP tree sectors + raycasting renderer
|
| 10 |
+
; Audit = WORM chain serialization every 64 frames
|
| 11 |
+
;
|
| 12 |
+
; Build: nasm -f bin src/bob_engine.asm -o bob_engine.com
|
| 13 |
+
; Run: dosbox bob_engine.com (or real DOS)
|
| 14 |
+
;
|
| 15 |
+
; Bel Esprit D'Accord Trust · SnapKitty Collective · 2026
|
| 16 |
+
; Apache 2.0 · Evidence or Silence
|
| 17 |
+
;==============================================================================
|
| 18 |
+
|
| 19 |
+
BITS 16
|
| 20 |
+
ORG 0x100 ; COM file: DOS loads us at DS:0100h
|
| 21 |
+
|
| 22 |
+
;==============================================================================
|
| 23 |
+
; CONSTANTS
|
| 24 |
+
;==============================================================================
|
| 25 |
+
|
| 26 |
+
; --- Screen ---
|
| 27 |
+
SCREEN_W EQU 320
|
| 28 |
+
SCREEN_H EQU 200
|
| 29 |
+
SCREEN_HALF_H EQU 100
|
| 30 |
+
NUM_RAYS EQU 320 ; one ray per screen column
|
| 31 |
+
VGA_SEG EQU 0xA000 ; VGA framebuffer segment
|
| 32 |
+
|
| 33 |
+
; --- Fixed-point math (16.16) ---
|
| 34 |
+
FP_BITS EQU 16
|
| 35 |
+
FP_ONE EQU 65536
|
| 36 |
+
ANGLE_MAX EQU 1024 ; full circle = 1024 units (power of 2 for fast mod)
|
| 37 |
+
ANGLE_HALF EQU 512
|
| 38 |
+
ANGLE_QUARTER EQU 256
|
| 39 |
+
HALF_FOV EQU 160 ; half FOV in angle units (~56 deg each side)
|
| 40 |
+
|
| 41 |
+
; --- Map ---
|
| 42 |
+
MAP_W EQU 16
|
| 43 |
+
MAP_H EQU 16
|
| 44 |
+
CELL_SZ EQU 64 ; world units per map cell
|
| 45 |
+
CELL_SHIFT EQU 6 ; log2(CELL_SZ)
|
| 46 |
+
|
| 47 |
+
; --- BSP ---
|
| 48 |
+
MAX_WALLS EQU 128
|
| 49 |
+
MAX_BSP_NODES EQU 64
|
| 50 |
+
|
| 51 |
+
; BSP node layout (16 bytes each)
|
| 52 |
+
; [0..1] x1 [2..3] y1 [4..5] x2 [6..7] y2
|
| 53 |
+
; [8..9] left_child [10..11] right_child
|
| 54 |
+
; [12] sector_id [13] color [14..15] reserved
|
| 55 |
+
BSP_NODE_SZ EQU 16
|
| 56 |
+
|
| 57 |
+
; --- AI Agents ---
|
| 58 |
+
MAX_AGENTS EQU 8
|
| 59 |
+
|
| 60 |
+
; Agent state codes
|
| 61 |
+
ST_PATROL EQU 0 ; walking waypoints
|
| 62 |
+
ST_CHASE EQU 1 ; pursuing player
|
| 63 |
+
ST_ATTACK EQU 2 ; firing at player
|
| 64 |
+
ST_DEAD EQU 3 ; dead, no update
|
| 65 |
+
|
| 66 |
+
; Agent struct layout (20 bytes each)
|
| 67 |
+
; [0..1] x (fixed 8.8) [2..3] y (fixed 8.8)
|
| 68 |
+
; [4..5] angle [6] state [7] health
|
| 69 |
+
; [8] sector_id [9] timer [10..11] speed
|
| 70 |
+
; [12..13] waypoint_x [14..15] waypoint_y
|
| 71 |
+
; [16..17] flags [18..19] reserved
|
| 72 |
+
AGENT_SZ EQU 20
|
| 73 |
+
|
| 74 |
+
; --- WORM ---
|
| 75 |
+
WORM_MAGIC EQU 0x424F42 ; "BOB"
|
| 76 |
+
WORM_VERSION EQU 0x0100 ; v1.0
|
| 77 |
+
WORM_BLK_SZ EQU 64 ; world snapshot block size
|
| 78 |
+
|
| 79 |
+
; --- Colors (VGA palette indices) ---
|
| 80 |
+
CLR_BLACK EQU 0
|
| 81 |
+
CLR_CEILING EQU 1 ; dark blue ceiling
|
| 82 |
+
CLR_FLOOR EQU 2 ; dark gray floor
|
| 83 |
+
CLR_WALL_NEAR EQU 3 ; bright wall (close)
|
| 84 |
+
CLR_WALL_MID EQU 4 ; mid wall
|
| 85 |
+
CLR_WALL_FAR EQU 5 ; dim wall (far)
|
| 86 |
+
CLR_AGENT EQU 6 ; enemy agent sprite color
|
| 87 |
+
CLR_SOVEREIGN EQU 7 ; player / HUD color
|
| 88 |
+
|
| 89 |
+
; --- Keyboard scan codes ---
|
| 90 |
+
KEY_ESC EQU 0x01
|
| 91 |
+
KEY_W EQU 0x11
|
| 92 |
+
KEY_A EQU 0x1E
|
| 93 |
+
KEY_S EQU 0x1F
|
| 94 |
+
KEY_D EQU 0x20
|
| 95 |
+
KEY_UP EQU 0x48
|
| 96 |
+
KEY_DOWN EQU 0x50
|
| 97 |
+
KEY_LEFT EQU 0x4B
|
| 98 |
+
KEY_RIGHT EQU 0x4D
|
| 99 |
+
|
| 100 |
+
;==============================================================================
|
| 101 |
+
; DATA SECTION (placed before code)
|
| 102 |
+
;==============================================================================
|
| 103 |
+
|
| 104 |
+
; --- Back buffer (320×200 = 64000 bytes) ---
|
| 105 |
+
g_backbuf: TIMES (SCREEN_W * SCREEN_H) DB 0
|
| 106 |
+
|
| 107 |
+
; --- Player (sovereign agent) ---
|
| 108 |
+
g_px: DW 0x0500 ; x = 5.0 in 8.8 fixed point (cell 5)
|
| 109 |
+
g_py: DW 0x0500 ; y = 5.0
|
| 110 |
+
g_pangle: DW 0x0000 ; facing angle (0..ANGLE_MAX-1)
|
| 111 |
+
g_phealth: DB 100
|
| 112 |
+
g_pmove_speed: DB 2
|
| 113 |
+
g_pturn_speed: DB 3
|
| 114 |
+
|
| 115 |
+
; --- Key state bitmask ---
|
| 116 |
+
g_keys: DW 0x0000 ; bit per key, updated by keyboard ISR
|
| 117 |
+
|
| 118 |
+
; --- Game state ---
|
| 119 |
+
g_quit: DB 0
|
| 120 |
+
g_frame: DW 0
|
| 121 |
+
g_worm_seq: DW 0 ; WORM block sequence number
|
| 122 |
+
|
| 123 |
+
; --- Old keyboard ISR vector (save for restore) ---
|
| 124 |
+
g_old_kb_off: DW 0
|
| 125 |
+
g_old_kb_seg: DW 0
|
| 126 |
+
|
| 127 |
+
; --- Sin/Cos lookup table (ANGLE_MAX = 1024 entries, 16.16 fixed point) ---
|
| 128 |
+
; Populated at runtime by math_init
|
| 129 |
+
g_sin: TIMES 1024 DD 0
|
| 130 |
+
g_cos: TIMES 1024 DD 0
|
| 131 |
+
|
| 132 |
+
; --- BSP nodes (MAX_BSP_NODES × BSP_NODE_SZ bytes) ---
|
| 133 |
+
g_bsp_nodes: TIMES (MAX_BSP_NODES * BSP_NODE_SZ) DB 0
|
| 134 |
+
g_bsp_count: DW 0
|
| 135 |
+
|
| 136 |
+
; --- AI agents (MAX_AGENTS × AGENT_SZ bytes) ---
|
| 137 |
+
g_agents: TIMES (MAX_AGENTS * AGENT_SZ) DB 0
|
| 138 |
+
g_agent_count: DB 0
|
| 139 |
+
|
| 140 |
+
; --- Map data (16×16 cells, 0=open, 1+=wall) ---
|
| 141 |
+
g_map:
|
| 142 |
+
DB 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 ; row 0 (top border)
|
| 143 |
+
DB 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 ; row 1
|
| 144 |
+
DB 1,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1 ; row 2
|
| 145 |
+
DB 1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1 ; row 3
|
| 146 |
+
DB 1,0,0,1,0,0,0,0,0,0,1,1,0,0,0,1 ; row 4
|
| 147 |
+
DB 1,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1 ; row 5
|
| 148 |
+
DB 1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1 ; row 6
|
| 149 |
+
DB 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 ; row 7
|
| 150 |
+
DB 1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1 ; row 8
|
| 151 |
+
DB 1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1 ; row 9
|
| 152 |
+
DB 1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1 ; row 10
|
| 153 |
+
DB 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 ; row 11
|
| 154 |
+
DB 1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1 ; row 12
|
| 155 |
+
DB 1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1 ; row 13
|
| 156 |
+
DB 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 ; row 14
|
| 157 |
+
DB 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 ; row 15 (bottom border)
|
| 158 |
+
|
| 159 |
+
; --- WORM output buffer (written to file) ---
|
| 160 |
+
g_worm_buf: TIMES WORM_BLK_SZ DB 0
|
| 161 |
+
g_worm_hash: TIMES 32 DB 0 ; current SHA-256 chain hash
|
| 162 |
+
|
| 163 |
+
; --- Scratch registers for raycasting ---
|
| 164 |
+
g_ray_angle: DW 0
|
| 165 |
+
g_ray_dist: DD 0
|
| 166 |
+
g_ray_hit_x: DD 0
|
| 167 |
+
g_ray_hit_y: DD 0
|
| 168 |
+
|
| 169 |
+
;==============================================================================
|
| 170 |
+
; CODE
|
| 171 |
+
;==============================================================================
|
| 172 |
+
_start:
|
| 173 |
+
; Setup segments (COM file: CS=DS=ES=SS)
|
| 174 |
+
mov ax, cs
|
| 175 |
+
mov ds, ax
|
| 176 |
+
mov es, ax
|
| 177 |
+
mov ss, ax
|
| 178 |
+
mov sp, 0xFFFE ; stack at top of 64KB segment
|
| 179 |
+
|
| 180 |
+
; Engine init
|
| 181 |
+
call math_init ; build sin/cos tables
|
| 182 |
+
call vga_init ; set mode 13h + custom palette
|
| 183 |
+
call kb_install ; hook INT 9 keyboard ISR
|
| 184 |
+
call bsp_build ; populate BSP tree from g_map
|
| 185 |
+
call player_init ; set player start
|
| 186 |
+
call ai_init ; spawn agents
|
| 187 |
+
call worm_init ; WORM genesis block
|
| 188 |
+
|
| 189 |
+
.loop:
|
| 190 |
+
call input_update ; read g_keys from ISR bitmask
|
| 191 |
+
call player_update ; move sovereign agent
|
| 192 |
+
call ai_update_all ; tick all agent state machines
|
| 193 |
+
call render_clear ; fill backbuf ceiling/floor
|
| 194 |
+
call render_walls ; raycast → wall slices
|
| 195 |
+
call render_agents ; draw agent sprites
|
| 196 |
+
call render_hud ; health / worm hash overlay
|
| 197 |
+
call vga_flip ; blit backbuf → A000h
|
| 198 |
+
|
| 199 |
+
inc word [g_frame]
|
| 200 |
+
test word [g_frame], 0x3F ; every 64 frames
|
| 201 |
+
jnz .check_quit
|
| 202 |
+
call worm_tick ; serialize world state
|
| 203 |
+
|
| 204 |
+
.check_quit:
|
| 205 |
+
cmp byte [g_quit], 1
|
| 206 |
+
jne .loop
|
| 207 |
+
|
| 208 |
+
call worm_finalize
|
| 209 |
+
call kb_restore
|
| 210 |
+
call vga_shutdown
|
| 211 |
+
|
| 212 |
+
mov ax, 0x4C00 ; DOS exit
|
| 213 |
+
int 0x21
|
| 214 |
+
|
| 215 |
+
;==============================================================================
|
| 216 |
+
; MATH — sin/cos lookup tables (16.16 fixed point)
|
| 217 |
+
;==============================================================================
|
| 218 |
+
math_init:
|
| 219 |
+
; BIOS doesn't give us floats in real mode.
|
| 220 |
+
; We use a precomputed approximation via BIOS int 0x10 indirect, or
|
| 221 |
+
; bootstrap from the identity: sin(i) ≈ sin(i-1)*cos_delta + cos(i-1)*sin_delta
|
| 222 |
+
; For simplicity: populate from constants via polynomial for first 256 entries
|
| 223 |
+
; then mirror for remaining quadrants.
|
| 224 |
+
;
|
| 225 |
+
; Shortcut: embed a 256-entry 8-bit sin table and scale to 16.16
|
| 226 |
+
|
| 227 |
+
; For a real build: link against a precomputed table or use FPU (8087)
|
| 228 |
+
; Here we mark the init as complete — table is zeroed (stubs walls as vertical)
|
| 229 |
+
ret
|
| 230 |
+
|
| 231 |
+
;==============================================================================
|
| 232 |
+
; VGA — Mode 13h init + palette
|
| 233 |
+
;==============================================================================
|
| 234 |
+
vga_init:
|
| 235 |
+
pusha
|
| 236 |
+
|
| 237 |
+
; Set mode 13h
|
| 238 |
+
mov ax, 0x0013 ; AH=00h (set mode), AL=13h
|
| 239 |
+
int 0x10 ; BIOS video interrupt
|
| 240 |
+
|
| 241 |
+
; Write custom palette to DAC
|
| 242 |
+
mov dx, 0x03C8 ; DAC write index register
|
| 243 |
+
xor al, al ; start at color 0
|
| 244 |
+
out dx, al
|
| 245 |
+
|
| 246 |
+
mov dx, 0x03C9 ; DAC data register
|
| 247 |
+
|
| 248 |
+
; Color 0: Black
|
| 249 |
+
xor al, al
|
| 250 |
+
out dx, al
|
| 251 |
+
out dx, al
|
| 252 |
+
out dx, al
|
| 253 |
+
|
| 254 |
+
; Color 1: Dark blue (ceiling)
|
| 255 |
+
mov al, 0
|
| 256 |
+
out dx, al ; R=0
|
| 257 |
+
mov al, 0
|
| 258 |
+
out dx, al ; G=0
|
| 259 |
+
mov al, 25
|
| 260 |
+
out dx, al ; B=25
|
| 261 |
+
|
| 262 |
+
; Color 2: Dark gray (floor)
|
| 263 |
+
mov al, 12
|
| 264 |
+
out dx, al
|
| 265 |
+
out dx, al
|
| 266 |
+
out dx, al
|
| 267 |
+
|
| 268 |
+
; Color 3: Bright white (near wall)
|
| 269 |
+
mov al, 55
|
| 270 |
+
out dx, al
|
| 271 |
+
out dx, al
|
| 272 |
+
out dx, al
|
| 273 |
+
|
| 274 |
+
; Color 4: Medium gray (mid wall)
|
| 275 |
+
mov al, 35
|
| 276 |
+
out dx, al
|
| 277 |
+
out dx, al
|
| 278 |
+
out dx, al
|
| 279 |
+
|
| 280 |
+
; Color 5: Dark gray (far wall)
|
| 281 |
+
mov al, 15
|
| 282 |
+
out dx, al
|
| 283 |
+
out dx, al
|
| 284 |
+
out dx, al
|
| 285 |
+
|
| 286 |
+
; Color 6: Red (enemy agent)
|
| 287 |
+
mov al, 55
|
| 288 |
+
out dx, al ; R
|
| 289 |
+
xor al, al
|
| 290 |
+
out dx, al ; G=0
|
| 291 |
+
out dx, al ; B=0
|
| 292 |
+
|
| 293 |
+
; Color 7: Green (sovereign / HUD)
|
| 294 |
+
xor al, al
|
| 295 |
+
out dx, al ; R=0
|
| 296 |
+
mov al, 50
|
| 297 |
+
out dx, al ; G
|
| 298 |
+
xor al, al
|
| 299 |
+
out dx, al ; B=0
|
| 300 |
+
|
| 301 |
+
popa
|
| 302 |
+
ret
|
| 303 |
+
|
| 304 |
+
vga_shutdown:
|
| 305 |
+
mov ax, 0x0003 ; mode 03h = 80x25 text
|
| 306 |
+
int 0x10
|
| 307 |
+
ret
|
| 308 |
+
|
| 309 |
+
vga_flip:
|
| 310 |
+
; Blit g_backbuf → VGA segment 0xA000
|
| 311 |
+
push ds
|
| 312 |
+
push es
|
| 313 |
+
|
| 314 |
+
mov ax, cs
|
| 315 |
+
mov ds, ax ; DS = our segment (source)
|
| 316 |
+
mov ax, VGA_SEG
|
| 317 |
+
mov es, ax ; ES = VGA segment (dest)
|
| 318 |
+
|
| 319 |
+
lea si, [g_backbuf] ; SI = source offset
|
| 320 |
+
xor di, di ; DI = 0 (VGA starts at A000:0000)
|
| 321 |
+
mov cx, (SCREEN_W * SCREEN_H) / 2 ; CX = number of words to copy
|
| 322 |
+
rep movsw ; copy 2 bytes per iteration
|
| 323 |
+
|
| 324 |
+
pop es
|
| 325 |
+
pop ds
|
| 326 |
+
ret
|
| 327 |
+
|
| 328 |
+
;==============================================================================
|
| 329 |
+
; KEYBOARD ISR — replaces INT 9
|
| 330 |
+
;==============================================================================
|
| 331 |
+
kb_install:
|
| 332 |
+
pusha
|
| 333 |
+
push es
|
| 334 |
+
|
| 335 |
+
; Save old ISR vector (INT 9 = IRQ1)
|
| 336 |
+
mov ax, 0x3509 ; DOS get interrupt vector, INT 9
|
| 337 |
+
int 0x21
|
| 338 |
+
mov [g_old_kb_off], bx
|
| 339 |
+
mov [g_old_kb_seg], es
|
| 340 |
+
|
| 341 |
+
; Install our ISR
|
| 342 |
+
push ds
|
| 343 |
+
mov ax, cs
|
| 344 |
+
mov ds, ax
|
| 345 |
+
mov dx, kb_isr ; DX = offset of our handler
|
| 346 |
+
mov ax, 0x2509 ; DOS set interrupt vector, INT 9
|
| 347 |
+
int 0x21
|
| 348 |
+
pop ds
|
| 349 |
+
|
| 350 |
+
pop es
|
| 351 |
+
popa
|
| 352 |
+
ret
|
| 353 |
+
|
| 354 |
+
kb_restore:
|
| 355 |
+
pusha
|
| 356 |
+
push ds
|
| 357 |
+
|
| 358 |
+
mov dx, [g_old_kb_off]
|
| 359 |
+
mov ax, [g_old_kb_seg]
|
| 360 |
+
mov ds, ax
|
| 361 |
+
mov ax, 0x2509
|
| 362 |
+
int 0x21
|
| 363 |
+
|
| 364 |
+
pop ds
|
| 365 |
+
popa
|
| 366 |
+
ret
|
| 367 |
+
|
| 368 |
+
kb_isr:
|
| 369 |
+
; Read scancode from keyboard controller
|
| 370 |
+
in al, 0x60 ; AL = scancode from port 60h
|
| 371 |
+
|
| 372 |
+
; Check for key-up (bit 7 set = key released)
|
| 373 |
+
test al, 0x80
|
| 374 |
+
jnz .key_up
|
| 375 |
+
|
| 376 |
+
.key_down:
|
| 377 |
+
; Set bit in g_keys based on scancode
|
| 378 |
+
; (simplified: store last scancode for now)
|
| 379 |
+
cmp al, KEY_ESC
|
| 380 |
+
jne .done
|
| 381 |
+
mov byte [g_quit], 1 ; ESC → set quit flag
|
| 382 |
+
|
| 383 |
+
.done:
|
| 384 |
+
; Acknowledge interrupt to PIC
|
| 385 |
+
mov al, 0x20 ; EOI command
|
| 386 |
+
out 0x20, al ; send to PIC1
|
| 387 |
+
|
| 388 |
+
iret ; return from interrupt
|
| 389 |
+
|
| 390 |
+
.key_up:
|
| 391 |
+
and al, 0x7F ; strip bit 7 to get scancode
|
| 392 |
+
; clear key bit (stub)
|
| 393 |
+
jmp .done
|
| 394 |
+
|
| 395 |
+
input_update:
|
| 396 |
+
; In a full implementation: copy ISR's g_keys snapshot
|
| 397 |
+
; For now, poll keyboard via BIOS
|
| 398 |
+
mov ah, 0x01 ; BIOS check keystroke
|
| 399 |
+
int 0x16
|
| 400 |
+
ret
|
| 401 |
+
|
| 402 |
+
;==============================================================================
|
| 403 |
+
; BSP — build tree from g_map grid
|
| 404 |
+
;==============================================================================
|
| 405 |
+
bsp_build:
|
| 406 |
+
; Walk the map grid, emit a wall segment (BSP leaf node) for each
|
| 407 |
+
; adjacent pair of cells where one is solid and one is open.
|
| 408 |
+
; Full BSP partitioning would recursively split — this is the seed pass.
|
| 409 |
+
|
| 410 |
+
pusha
|
| 411 |
+
mov word [g_bsp_count], 0
|
| 412 |
+
|
| 413 |
+
xor bx, bx ; BX = row
|
| 414 |
+
.row_loop:
|
| 415 |
+
cmp bx, MAP_H
|
| 416 |
+
jge .done
|
| 417 |
+
|
| 418 |
+
xor cx, cx ; CX = col
|
| 419 |
+
.col_loop:
|
| 420 |
+
cmp cx, MAP_W
|
| 421 |
+
jge .next_row
|
| 422 |
+
|
| 423 |
+
; Compute map index = row * MAP_W + col
|
| 424 |
+
mov ax, bx
|
| 425 |
+
mov si, MAP_W
|
| 426 |
+
mul si ; AX = row * MAP_W
|
| 427 |
+
add ax, cx ; AX = index
|
| 428 |
+
lea di, [g_map]
|
| 429 |
+
add di, ax
|
| 430 |
+
mov al, [di] ; AL = cell value
|
| 431 |
+
test al, al
|
| 432 |
+
jz .not_wall ; 0 = open, skip
|
| 433 |
+
|
| 434 |
+
; This cell is a wall — add a BSP node for it
|
| 435 |
+
call bsp_add_node
|
| 436 |
+
|
| 437 |
+
.not_wall:
|
| 438 |
+
inc cx
|
| 439 |
+
jmp .col_loop
|
| 440 |
+
|
| 441 |
+
.next_row:
|
| 442 |
+
inc bx
|
| 443 |
+
jmp .row_loop
|
| 444 |
+
|
| 445 |
+
.done:
|
| 446 |
+
popa
|
| 447 |
+
ret
|
| 448 |
+
|
| 449 |
+
bsp_add_node:
|
| 450 |
+
; Add a wall node for cell [BX=row, CX=col]
|
| 451 |
+
; Node = axis-aligned box: (cx*CELL_SZ, bx*CELL_SZ) to ((cx+1)*CELL_SZ, (bx+1)*CELL_SZ)
|
| 452 |
+
mov ax, [g_bsp_count]
|
| 453 |
+
cmp ax, MAX_BSP_NODES
|
| 454 |
+
jge .overflow
|
| 455 |
+
|
| 456 |
+
; Compute node ptr
|
| 457 |
+
push ax
|
| 458 |
+
mov si, BSP_NODE_SZ
|
| 459 |
+
mul si ; AX = count * BSP_NODE_SZ
|
| 460 |
+
lea di, [g_bsp_nodes]
|
| 461 |
+
add di, ax ; DI → this node
|
| 462 |
+
|
| 463 |
+
; x1 = cx * CELL_SZ
|
| 464 |
+
mov ax, cx
|
| 465 |
+
shl ax, CELL_SHIFT ; ax = cx << 6 = cx * 64
|
| 466 |
+
mov [di+0], ax ; BSP.x1
|
| 467 |
+
|
| 468 |
+
; y1 = bx * CELL_SZ
|
| 469 |
+
mov ax, bx
|
| 470 |
+
shl ax, CELL_SHIFT
|
| 471 |
+
mov [di+2], ax ; BSP.y1
|
| 472 |
+
|
| 473 |
+
; x2 = (cx+1) * CELL_SZ
|
| 474 |
+
mov ax, cx
|
| 475 |
+
inc ax
|
| 476 |
+
shl ax, CELL_SHIFT
|
| 477 |
+
mov [di+4], ax ; BSP.x2
|
| 478 |
+
|
| 479 |
+
; y2 = (bx+1) * CELL_SZ
|
| 480 |
+
mov ax, bx
|
| 481 |
+
inc ax
|
| 482 |
+
shl ax, CELL_SHIFT
|
| 483 |
+
mov [di+6], ax ; BSP.y2
|
| 484 |
+
|
| 485 |
+
; No children (leaf node)
|
| 486 |
+
mov word [di+8], 0xFFFF ; left child = -1
|
| 487 |
+
mov word [di+10], 0xFFFF ; right child = -1
|
| 488 |
+
|
| 489 |
+
; Sector = row (simple sector assignment)
|
| 490 |
+
mov [di+12], bl ; sector_id
|
| 491 |
+
mov byte [di+13], CLR_WALL_MID ; color
|
| 492 |
+
|
| 493 |
+
pop ax
|
| 494 |
+
inc ax
|
| 495 |
+
mov [g_bsp_count], ax ; g_bsp_count++
|
| 496 |
+
|
| 497 |
+
.overflow:
|
| 498 |
+
ret
|
| 499 |
+
|
| 500 |
+
;==============================================================================
|
| 501 |
+
; PLAYER (Sovereign Agent)
|
| 502 |
+
;==============================================================================
|
| 503 |
+
player_init:
|
| 504 |
+
mov word [g_px], 0x0580 ; x = 5.5 cells
|
| 505 |
+
mov word [g_py], 0x0580 ; y = 5.5 cells
|
| 506 |
+
mov word [g_pangle], 0x0000 ; facing east
|
| 507 |
+
mov byte [g_phealth], 100
|
| 508 |
+
ret
|
| 509 |
+
|
| 510 |
+
player_update:
|
| 511 |
+
; Poll BIOS for key state (real implementation uses ISR bitmask)
|
| 512 |
+
; W/Up = move forward, S/Down = move back, A/Left = turn left, D/Right = turn right
|
| 513 |
+
|
| 514 |
+
mov ah, 0x01 ; BIOS: check if key available
|
| 515 |
+
int 0x16
|
| 516 |
+
jz .no_key
|
| 517 |
+
mov ah, 0x00 ; BIOS: read key
|
| 518 |
+
int 0x16
|
| 519 |
+
|
| 520 |
+
; AH = scan code, AL = ASCII
|
| 521 |
+
cmp ah, KEY_LEFT
|
| 522 |
+
je .turn_left
|
| 523 |
+
cmp ah, KEY_RIGHT
|
| 524 |
+
je .turn_right
|
| 525 |
+
cmp ah, KEY_UP
|
| 526 |
+
je .move_fwd
|
| 527 |
+
cmp ah, KEY_DOWN
|
| 528 |
+
je .move_back
|
| 529 |
+
cmp ah, KEY_ESC
|
| 530 |
+
jne .no_key
|
| 531 |
+
mov byte [g_quit], 1
|
| 532 |
+
ret
|
| 533 |
+
|
| 534 |
+
.turn_left:
|
| 535 |
+
sub word [g_pangle], 4 ; turn left
|
| 536 |
+
and word [g_pangle], (ANGLE_MAX-1) ; wrap
|
| 537 |
+
ret
|
| 538 |
+
|
| 539 |
+
.turn_right:
|
| 540 |
+
add word [g_pangle], 4 ; turn right
|
| 541 |
+
and word [g_pangle], (ANGLE_MAX-1)
|
| 542 |
+
ret
|
| 543 |
+
|
| 544 |
+
.move_fwd:
|
| 545 |
+
; px += cos(angle) * speed (stub: just move in x)
|
| 546 |
+
add word [g_px], 2
|
| 547 |
+
ret
|
| 548 |
+
|
| 549 |
+
.move_back:
|
| 550 |
+
sub word [g_px], 2
|
| 551 |
+
ret
|
| 552 |
+
|
| 553 |
+
.no_key:
|
| 554 |
+
ret
|
| 555 |
+
|
| 556 |
+
;==============================================================================
|
| 557 |
+
; AI AGENTS
|
| 558 |
+
;==============================================================================
|
| 559 |
+
ai_init:
|
| 560 |
+
; Spawn 3 enemy agents at known positions
|
| 561 |
+
mov byte [g_agent_count], 3
|
| 562 |
+
|
| 563 |
+
; Agent 0 — patrol sector top-left
|
| 564 |
+
lea di, [g_agents]
|
| 565 |
+
mov word [di+0], 0x0A80 ; x = 10.5
|
| 566 |
+
mov word [di+2], 0x0380 ; y = 3.5
|
| 567 |
+
mov word [di+4], 0x0000 ; angle = 0
|
| 568 |
+
mov byte [di+6], ST_PATROL
|
| 569 |
+
mov byte [di+7], 100 ; health
|
| 570 |
+
mov byte [di+8], 0 ; sector 0
|
| 571 |
+
mov byte [di+9], 60 ; timer
|
| 572 |
+
|
| 573 |
+
; Agent 1 — patrol sector right
|
| 574 |
+
lea di, [g_agents + AGENT_SZ]
|
| 575 |
+
mov word [di+0], 0x0C80 ; x = 12.5
|
| 576 |
+
mov word [di+2], 0x0C80 ; y = 12.5
|
| 577 |
+
mov word [di+4], 0x0100 ; angle = 90 deg
|
| 578 |
+
mov byte [di+6], ST_PATROL
|
| 579 |
+
mov byte [di+7], 80
|
| 580 |
+
mov byte [di+8], 1
|
| 581 |
+
mov byte [di+9], 30
|
| 582 |
+
|
| 583 |
+
; Agent 2 — starts chasing
|
| 584 |
+
lea di, [g_agents + AGENT_SZ*2]
|
| 585 |
+
mov word [di+0], 0x0880
|
| 586 |
+
mov word [di+2], 0x0880
|
| 587 |
+
mov word [di+4], 0x0200
|
| 588 |
+
mov byte [di+6], ST_CHASE
|
| 589 |
+
mov byte [di+7], 60
|
| 590 |
+
mov byte [di+8], 2
|
| 591 |
+
mov byte [di+9], 0
|
| 592 |
+
|
| 593 |
+
ret
|
| 594 |
+
|
| 595 |
+
ai_update_all:
|
| 596 |
+
xor bx, bx ; BX = agent index
|
| 597 |
+
movzx cx, byte [g_agent_count]
|
| 598 |
+
|
| 599 |
+
.loop:
|
| 600 |
+
cmp bx, cx
|
| 601 |
+
jge .done
|
| 602 |
+
|
| 603 |
+
; Compute agent ptr
|
| 604 |
+
mov ax, bx
|
| 605 |
+
mov si, AGENT_SZ
|
| 606 |
+
mul si
|
| 607 |
+
lea di, [g_agents]
|
| 608 |
+
add di, ax ; DI → this agent
|
| 609 |
+
|
| 610 |
+
; Get current state
|
| 611 |
+
movzx ax, byte [di+6] ; AL = state
|
| 612 |
+
|
| 613 |
+
cmp al, ST_PATROL
|
| 614 |
+
je .patrol
|
| 615 |
+
cmp al, ST_CHASE
|
| 616 |
+
je .chase
|
| 617 |
+
cmp al, ST_ATTACK
|
| 618 |
+
je .attack
|
| 619 |
+
jmp .next ; ST_DEAD → skip
|
| 620 |
+
|
| 621 |
+
.patrol:
|
| 622 |
+
; Tick timer, reverse direction on timeout
|
| 623 |
+
dec byte [di+9] ; timer--
|
| 624 |
+
jnz .next
|
| 625 |
+
mov byte [di+9], 60 ; reset timer
|
| 626 |
+
; Flip angle 180 degrees
|
| 627 |
+
add word [di+4], ANGLE_HALF
|
| 628 |
+
and word [di+4], (ANGLE_MAX-1)
|
| 629 |
+
; Check player proximity → transition to CHASE
|
| 630 |
+
; (simplified: always patrol for now)
|
| 631 |
+
jmp .next
|
| 632 |
+
|
| 633 |
+
.chase:
|
| 634 |
+
; Move toward player position
|
| 635 |
+
; (simplified: just decrement timer then attack)
|
| 636 |
+
dec byte [di+9]
|
| 637 |
+
jnz .next
|
| 638 |
+
mov byte [di+6], ST_ATTACK ; close enough → attack
|
| 639 |
+
mov byte [di+9], 20
|
| 640 |
+
jmp .next
|
| 641 |
+
|
| 642 |
+
.attack:
|
| 643 |
+
dec byte [di+9]
|
| 644 |
+
jnz .next
|
| 645 |
+
mov byte [di+6], ST_PATROL ; attack done → patrol
|
| 646 |
+
mov byte [di+9], 60
|
| 647 |
+
|
| 648 |
+
.next:
|
| 649 |
+
inc bx
|
| 650 |
+
jmp .loop
|
| 651 |
+
|
| 652 |
+
.done:
|
| 653 |
+
ret
|
| 654 |
+
|
| 655 |
+
;==============================================================================
|
| 656 |
+
; RENDERER — raycasting wall slices
|
| 657 |
+
;==============================================================================
|
| 658 |
+
render_clear:
|
| 659 |
+
; Fill ceiling (top half) with CLR_CEILING
|
| 660 |
+
lea di, [g_backbuf]
|
| 661 |
+
mov cx, (SCREEN_W * SCREEN_HALF_H)
|
| 662 |
+
mov al, CLR_CEILING
|
| 663 |
+
rep stosb
|
| 664 |
+
|
| 665 |
+
; Fill floor (bottom half) with CLR_FLOOR
|
| 666 |
+
; DI is now at the midpoint
|
| 667 |
+
mov cx, (SCREEN_W * SCREEN_HALF_H)
|
| 668 |
+
mov al, CLR_FLOOR
|
| 669 |
+
rep stosb
|
| 670 |
+
|
| 671 |
+
ret
|
| 672 |
+
|
| 673 |
+
render_walls:
|
| 674 |
+
; For each screen column (ray), cast a ray and draw a wall slice
|
| 675 |
+
xor bx, bx ; BX = column 0
|
| 676 |
+
|
| 677 |
+
.col_loop:
|
| 678 |
+
cmp bx, NUM_RAYS
|
| 679 |
+
jge .done
|
| 680 |
+
|
| 681 |
+
; Compute ray angle = player_angle - HALF_FOV + (column * (2*HALF_FOV) / NUM_RAYS)
|
| 682 |
+
; Simplified: step through angle space linearly
|
| 683 |
+
mov ax, bx
|
| 684 |
+
mov si, (2 * HALF_FOV)
|
| 685 |
+
mul si ; AX = col * (2*HALF_FOV)
|
| 686 |
+
mov si, NUM_RAYS
|
| 687 |
+
div si ; AX = col * (2*HALF_FOV) / NUM_RAYS
|
| 688 |
+
sub ax, HALF_FOV ; AX = offset from player angle
|
| 689 |
+
add ax, [g_pangle] ; AX = ray angle
|
| 690 |
+
and ax, (ANGLE_MAX-1) ; wrap to [0, ANGLE_MAX)
|
| 691 |
+
mov [g_ray_angle], ax
|
| 692 |
+
|
| 693 |
+
; DDA ray march through the map grid
|
| 694 |
+
call raycast_dda ; returns distance in g_ray_dist
|
| 695 |
+
|
| 696 |
+
; Compute wall slice height = (CELL_SZ * SCREEN_H) / distance
|
| 697 |
+
mov ax, (CELL_SZ * SCREEN_H)
|
| 698 |
+
mov dx, 0
|
| 699 |
+
mov si, [g_ray_dist]
|
| 700 |
+
cmp si, 1
|
| 701 |
+
jge .div_ok
|
| 702 |
+
mov si, 1 ; clamp distance to 1 (avoid div/0)
|
| 703 |
+
.div_ok:
|
| 704 |
+
div si ; AX = wall height in pixels
|
| 705 |
+
cmp ax, SCREEN_H
|
| 706 |
+
jle .height_ok
|
| 707 |
+
mov ax, SCREEN_H ; clamp to screen height
|
| 708 |
+
.height_ok:
|
| 709 |
+
|
| 710 |
+
; Choose wall color based on distance
|
| 711 |
+
mov cl, CLR_WALL_NEAR
|
| 712 |
+
cmp word [g_ray_dist], 64
|
| 713 |
+
jl .draw
|
| 714 |
+
mov cl, CLR_WALL_MID
|
| 715 |
+
cmp word [g_ray_dist], 128
|
| 716 |
+
jl .draw
|
| 717 |
+
mov cl, CLR_WALL_FAR
|
| 718 |
+
|
| 719 |
+
.draw:
|
| 720 |
+
; Draw vertical wall slice at column BX, height AX, color CL
|
| 721 |
+
push bx
|
| 722 |
+
push ax
|
| 723 |
+
push cx
|
| 724 |
+
call draw_vslice ; args: col in BX, height in AX, color in CL
|
| 725 |
+
pop cx
|
| 726 |
+
pop ax
|
| 727 |
+
pop bx
|
| 728 |
+
|
| 729 |
+
inc bx
|
| 730 |
+
jmp .col_loop
|
| 731 |
+
|
| 732 |
+
.done:
|
| 733 |
+
ret
|
| 734 |
+
|
| 735 |
+
raycast_dda:
|
| 736 |
+
; Digital Differential Analysis ray march
|
| 737 |
+
; Inputs: g_px, g_py (player pos in 8.8 fixed)
|
| 738 |
+
; g_ray_angle (angle index)
|
| 739 |
+
; Outputs: g_ray_dist (distance to wall in world units)
|
| 740 |
+
|
| 741 |
+
; Simplified: march along X axis checking map cells
|
| 742 |
+
; Full implementation uses two-pass DDA (separate X and Y steps)
|
| 743 |
+
|
| 744 |
+
mov ax, [g_px]
|
| 745 |
+
shr ax, 8 ; integer part of player X (cell)
|
| 746 |
+
mov si, ax ; SI = current cell X
|
| 747 |
+
|
| 748 |
+
mov ax, [g_py]
|
| 749 |
+
shr ax, 8
|
| 750 |
+
mov di, ax ; DI = current cell Y
|
| 751 |
+
|
| 752 |
+
xor cx, cx ; CX = step counter (= distance)
|
| 753 |
+
|
| 754 |
+
.march:
|
| 755 |
+
cmp cx, 512 ; max ray depth = 512 units
|
| 756 |
+
jge .hit
|
| 757 |
+
|
| 758 |
+
; Check if current cell is a wall
|
| 759 |
+
mov ax, di ; row = DI
|
| 760 |
+
mul word [_map_w_const] ; AX = row * MAP_W
|
| 761 |
+
add ax, si ; AX = map index
|
| 762 |
+
lea bx, [g_map]
|
| 763 |
+
add bx, ax
|
| 764 |
+
mov al, [bx] ; AL = cell value
|
| 765 |
+
test al, al
|
| 766 |
+
jnz .hit ; non-zero = wall
|
| 767 |
+
|
| 768 |
+
; Step forward (move along X for now)
|
| 769 |
+
inc si
|
| 770 |
+
add cx, CELL_SZ ; distance += CELL_SZ
|
| 771 |
+
|
| 772 |
+
jmp .march
|
| 773 |
+
|
| 774 |
+
.hit:
|
| 775 |
+
mov [g_ray_dist], cx
|
| 776 |
+
ret
|
| 777 |
+
|
| 778 |
+
_map_w_const: DW MAP_W
|
| 779 |
+
|
| 780 |
+
draw_vslice:
|
| 781 |
+
; Draw a vertical column of pixels
|
| 782 |
+
; BX = screen column, AX = wall height (pixels), CL = color
|
| 783 |
+
pusha
|
| 784 |
+
|
| 785 |
+
; Compute top of wall = SCREEN_HALF_H - (height/2)
|
| 786 |
+
mov dx, ax ; DX = height
|
| 787 |
+
shr dx, 1 ; DX = height/2
|
| 788 |
+
mov di, SCREEN_HALF_H
|
| 789 |
+
sub di, dx ; DI = top Y
|
| 790 |
+
|
| 791 |
+
cmp di, 0
|
| 792 |
+
jge .clamp_ok
|
| 793 |
+
xor di, di ; clamp top to 0
|
| 794 |
+
|
| 795 |
+
.clamp_ok:
|
| 796 |
+
; DI = top Y, draw AX pixels downward
|
| 797 |
+
mov cx, ax ; CX = number of pixels to draw
|
| 798 |
+
|
| 799 |
+
.pixel_loop:
|
| 800 |
+
cmp di, SCREEN_H
|
| 801 |
+
jge .done ; past bottom of screen
|
| 802 |
+
|
| 803 |
+
; Compute backbuf offset = di * SCREEN_W + BX
|
| 804 |
+
push cx
|
| 805 |
+
mov ax, di
|
| 806 |
+
mov si, SCREEN_W
|
| 807 |
+
mul si ; AX = row * 320
|
| 808 |
+
add ax, bx ; AX += column
|
| 809 |
+
|
| 810 |
+
lea si, [g_backbuf]
|
| 811 |
+
add si, ax
|
| 812 |
+
mov [si], cl ; write pixel color
|
| 813 |
+
|
| 814 |
+
pop cx
|
| 815 |
+
inc di
|
| 816 |
+
loop .pixel_loop
|
| 817 |
+
|
| 818 |
+
.done:
|
| 819 |
+
popa
|
| 820 |
+
ret
|
| 821 |
+
|
| 822 |
+
render_agents:
|
| 823 |
+
; Stub: draw a colored pixel at each living agent's approximate screen position
|
| 824 |
+
xor bx, bx
|
| 825 |
+
movzx cx, byte [g_agent_count]
|
| 826 |
+
|
| 827 |
+
.loop:
|
| 828 |
+
cmp bx, cx
|
| 829 |
+
jge .done
|
| 830 |
+
|
| 831 |
+
mov ax, bx
|
| 832 |
+
mov si, AGENT_SZ
|
| 833 |
+
mul si
|
| 834 |
+
lea di, [g_agents]
|
| 835 |
+
add di, ax
|
| 836 |
+
|
| 837 |
+
mov al, [di+6] ; state
|
| 838 |
+
cmp al, ST_DEAD
|
| 839 |
+
je .next
|
| 840 |
+
|
| 841 |
+
; Project agent onto screen (simplified: fixed column for demo)
|
| 842 |
+
mov al, [di+8] ; sector_id → column offset
|
| 843 |
+
movzx ax, al
|
| 844 |
+
shl ax, 4 ; col = sector * 16
|
| 845 |
+
add ax, 40
|
| 846 |
+
|
| 847 |
+
; Draw a 4×8 sprite at column AX, mid-screen
|
| 848 |
+
mov si, SCREEN_HALF_H
|
| 849 |
+
sub si, 4 ; top = mid - 4
|
| 850 |
+
mov cx, 8 ; 8 rows tall
|
| 851 |
+
|
| 852 |
+
.sprite_row:
|
| 853 |
+
push cx
|
| 854 |
+
push ax
|
| 855 |
+
mov di, si
|
| 856 |
+
mul word [_screen_w_const]
|
| 857 |
+
add ax, si
|
| 858 |
+
lea di, [g_backbuf]
|
| 859 |
+
add di, ax
|
| 860 |
+
mov byte [di], CLR_AGENT ; red pixel
|
| 861 |
+
pop ax
|
| 862 |
+
pop cx
|
| 863 |
+
inc si
|
| 864 |
+
loop .sprite_row
|
| 865 |
+
|
| 866 |
+
.next:
|
| 867 |
+
inc bx
|
| 868 |
+
jmp .loop
|
| 869 |
+
|
| 870 |
+
.done:
|
| 871 |
+
ret
|
| 872 |
+
|
| 873 |
+
_screen_w_const: DW SCREEN_W
|
| 874 |
+
|
| 875 |
+
render_hud:
|
| 876 |
+
; Draw a green line at the bottom showing health
|
| 877 |
+
movzx ax, byte [g_phealth] ; AX = health (0-100)
|
| 878 |
+
mov si, SCREEN_W
|
| 879 |
+
mul si
|
| 880 |
+
div word [_health_div] ; AX = pixel width of health bar
|
| 881 |
+
|
| 882 |
+
; Bottom row = SCREEN_H-1
|
| 883 |
+
mov di, (SCREEN_H-1)
|
| 884 |
+
mov si, SCREEN_W
|
| 885 |
+
mul di ; AX = (SCREEN_H-1) * SCREEN_W (wrong — see note)
|
| 886 |
+
; (simplified: write first AX pixels of last row)
|
| 887 |
+
lea di, [g_backbuf + (SCREEN_W * (SCREEN_H-1))]
|
| 888 |
+
mov cx, ax
|
| 889 |
+
mov al, CLR_SOVEREIGN ; green
|
| 890 |
+
rep stosb
|
| 891 |
+
|
| 892 |
+
ret
|
| 893 |
+
|
| 894 |
+
_health_div: DW 100
|
| 895 |
+
|
| 896 |
+
;==============================================================================
|
| 897 |
+
; WORM — World state serialization with chained hash
|
| 898 |
+
;==============================================================================
|
| 899 |
+
worm_init:
|
| 900 |
+
; Write genesis block header
|
| 901 |
+
mov dword [g_worm_buf+0], WORM_MAGIC ; magic = "BOB"
|
| 902 |
+
mov word [g_worm_buf+4], WORM_VERSION ; version = 1.0
|
| 903 |
+
mov word [g_worm_buf+6], 0x0000 ; seq = 0
|
| 904 |
+
; (hash init: zero = genesis)
|
| 905 |
+
ret
|
| 906 |
+
|
| 907 |
+
worm_tick:
|
| 908 |
+
; Snapshot key world state into WORM block
|
| 909 |
+
; [0..3] magic [4..5] version [6..7] seq
|
| 910 |
+
; [8..9] player_x [10..11] player_y [12..13] player_angle
|
| 911 |
+
; [14] player_health [15] agent_count
|
| 912 |
+
; [16..31] agent states (2 bytes each: state + health)
|
| 913 |
+
; [32..63] prev hash (32 bytes)
|
| 914 |
+
|
| 915 |
+
inc word [g_worm_seq]
|
| 916 |
+
mov ax, [g_worm_seq]
|
| 917 |
+
mov [g_worm_buf+6], ax
|
| 918 |
+
|
| 919 |
+
mov ax, [g_px]
|
| 920 |
+
mov [g_worm_buf+8], ax
|
| 921 |
+
|
| 922 |
+
mov ax, [g_py]
|
| 923 |
+
mov [g_worm_buf+10], ax
|
| 924 |
+
|
| 925 |
+
mov ax, [g_pangle]
|
| 926 |
+
mov [g_worm_buf+12], ax
|
| 927 |
+
|
| 928 |
+
mov al, [g_phealth]
|
| 929 |
+
mov [g_worm_buf+14], al
|
| 930 |
+
|
| 931 |
+
movzx ax, byte [g_agent_count]
|
| 932 |
+
mov [g_worm_buf+15], al
|
| 933 |
+
|
| 934 |
+
; Copy agent states (stub: first 8 bytes)
|
| 935 |
+
xor bx, bx
|
| 936 |
+
movzx cx, byte [g_agent_count]
|
| 937 |
+
.agent_loop:
|
| 938 |
+
cmp bx, cx
|
| 939 |
+
jge .write_block
|
| 940 |
+
mov ax, bx
|
| 941 |
+
mov si, AGENT_SZ
|
| 942 |
+
mul si
|
| 943 |
+
lea di, [g_agents]
|
| 944 |
+
add di, ax
|
| 945 |
+
mov al, [di+6] ; state
|
| 946 |
+
mov [g_worm_buf+16+bx], al
|
| 947 |
+
inc bx
|
| 948 |
+
jmp .agent_loop
|
| 949 |
+
|
| 950 |
+
.write_block:
|
| 951 |
+
; TODO: SHA-256 chain over g_worm_buf → g_worm_hash
|
| 952 |
+
; TODO: write block to file via DOS INT 21h
|
| 953 |
+
; (stub: increment confirms the chain is ticking)
|
| 954 |
+
ret
|
| 955 |
+
|
| 956 |
+
worm_finalize:
|
| 957 |
+
; Write final WORM block marking session end
|
| 958 |
+
mov dword [g_worm_buf+0], WORM_MAGIC
|
| 959 |
+
mov word [g_worm_buf+4], 0xDEAD ; terminal magic
|
| 960 |
+
call worm_tick
|
| 961 |
+
ret
|