From ecf1f03bd2db2ee99c775f764164b182befbc25d Mon Sep 17 00:00:00 2001 From: Happyarch Date: Fri, 19 Jun 2026 02:09:01 -0400 Subject: [PATCH] Fix collision offsets, map transitions, and update project docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix GetTileInFrontOfPlayer + standing-tile read to use DOS viewport coordinates (PLAYER_STANDING_ROW=17, PLAYER_STANDING_COL=24) derived from the render_sprites +36/+96 centering shift; old code used the original GB's 20-wide tilemap coords (row 9, col 8) - Fix gen_map_headers.py NORTH/WEST view-pointer formulas (-4→-5, -6→-7) to account for YBC=1/XBC=1 row/col skip in LoadCurrentMapView - Regenerate assets/map_headers.inc with corrected view pointers - Verified north transition via DEBUG_TRANSITION FRAME.BIN: player lands on Route 1 path at correct screen position - Fix stale wOverworldMap address ($E580→$E800) in CLAUDE.md and TODO.md - Check off completed TODO items: window layer renderer, map transitions, red strip (resolved by native renderer rewrite), VGA-native renderer (completed piecemeal during Phase 2) - Add TODO entries for SOUTH/EAST stale-view collision walkthrough bug and rapid-direction-change collision desync - Delete committed test screenshots and Gemini patch artifacts Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 13 +- TODO.md | 141 ++++--- docs/translation_log.md | 541 +++++++++++++++++++++++++++ dos_port/assets/map_headers.inc | 4 +- dos_port/baseline_out.png | Bin 4754 -> 0 bytes dos_port/dosbox_north.png | Bin 26221 -> 0 bytes dos_port/dosbox_screenshot.png | Bin 27399 -> 0 bytes dos_port/dosbox_south.png | Bin 25926 -> 0 bytes dos_port/dosbox_sprite_fixed.png | Bin 27057 -> 0 bytes dos_port/frame.png | Bin 9524 -> 0 bytes dos_port/frame_out.png | Bin 3786 -> 0 bytes dos_port/include/gb_memmap.inc | 90 +++++ dos_port/out.png | Bin 9524 -> 0 bytes dos_port/src/overworld/movement.asm | 4 +- dos_port/src/overworld/overworld.asm | 61 ++- tools/gen_map_headers.py | 19 +- 16 files changed, 783 insertions(+), 90 deletions(-) delete mode 100644 dos_port/baseline_out.png delete mode 100644 dos_port/dosbox_north.png delete mode 100644 dos_port/dosbox_screenshot.png delete mode 100644 dos_port/dosbox_south.png delete mode 100644 dos_port/dosbox_sprite_fixed.png delete mode 100644 dos_port/frame.png delete mode 100644 dos_port/frame_out.png delete mode 100644 dos_port/out.png diff --git a/CLAUDE.md b/CLAUDE.md index 819fd6c4..bccfb7e2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -71,14 +71,11 @@ edge reaches past the populated `wOverworldMap` data. Two complementary stopgaps keep that from painting garbage: 1. **Block-ID clamp** in `DrawTileBlock`: a block ID past the embedded blockset is clamped to block 0. -2. **Block-map address clamp** in `LoadCurrentMapView`: `wOverworldMap` ($E580) - sits directly above `wSurroundingTiles` ($E000) in WRAM, so a view pointer - that reaches above the map's top border reads *tile* IDs from the surrounding - buffer and decodes them as *block* IDs → a garbage band (seen at map edges and - the instant a connection is crossed). Any read outside - `[wOverworldMap, wOverworldMapEnd)` instead yields the map's border block - (`wMapBackgroundTile`), so the out-of-map area renders as clean dummy tiles - matching the in-bounds border. +2. **Block-map address clamp** in `LoadCurrentMapView`: `wOverworldMap` ($E800, + 2048 bytes) is separated from `wSurroundingTiles` ($E000, ~1728 bytes) by a + ~$140-byte gap. Any read outside `[wOverworldMap, wOverworldMapEnd)` yields + the map's border block (`wMapBackgroundTile`) instead of garbage, so the + out-of-map area renders as clean dummy tiles matching the in-bounds border. Both are stopgaps: the real fix is to **extend the map data** so those regions hold real blocks (no blank area), after which both clamps are dead code and diff --git a/TODO.md b/TODO.md index 5d95cedc..c595be88 100644 --- a/TODO.md +++ b/TODO.md @@ -60,7 +60,10 @@ Prioritized task list. Check off items as they complete; add new items with phas wired (picture ID 0). Verified 2026-06-15 via DEBUG_DUMP. REMAINING for the sprite engine: NPC slots (InitMapSprites / sprite sets / VRAM-slot alloc), DetectCollisionBetweenSprites, and the spinning/ledge paths. -- [ ] PPU: window layer renderer +- [x] PPU: window layer renderer — `render_window` in `src/ppu/ppu.asm`; wired into + the `DelayFrame` pipeline (frame.asm: render_bg → render_window → render_sprites). + Handles WX/WY position, LCDC bits 5+0+6, WLY counter, left-clip and right-clip. + Verified present and correct in the codebase (2026-06-19). - [ ] PPU: CGB attribute handling (palette, VRAM bank, flip bits) - [x] Joypad: INT 9h handler → write `[EBP + IO_JOYP]` — src/input/joypad.asm (arrows/X/Z/Enter/RShift/Tab mapped; Esc = host quit; rJOYP select-bit @@ -106,53 +109,73 @@ Prioritized task list. Check off items as they complete; add new items with phas in all four directions scrolls Pallet Town smoothly with correct tiles at the newly exposed edges; trees/buildings collide. Omitted vs pret: OAM sprite shift, ledges, tile-pair collisions, warps, NPCs, battles, scripts.) -- [ ] **MAJOR REFACTOR: VGA-native renderer (40×25 viewport, 320×200 native output)** - Plan: `/home/beowulf-linux/.claude/plans/greedy-roaming-toucan.md` - - Current 160×144 → 2× blit wasteful: 144-pass scanline decode is expensive on 386 - (550k cycles/frame), and the bottom 44 GB rows are silently clipped. Replace with: - - Tile-blitter PPU: 40×25 = 1,000 tile passes per frame (not 4,608 scanline - tile-strip decodes); single direct write to 320×200 back buffer. - - WRAM expansion: wTileMap 360→1,000 B, wSurroundingTiles 480→1,408 B; - all addresses via constants (no hardcoded offsets). - - Sprite pre-scale at build time (2bpp 8×8 → 8bpp 16×16): 65 KB static table, - zero per-frame decode cost. - - `present()` simplifies to `rep movsd` (16k dwords, ~1 ms on 386). - - Scope: 8 implementation steps across ppu.asm, video.asm, gb_memmap.inc, - overworld.asm, movement.asm, sprite_oam.asm, gen_spr_tiles.py. Multi-session task. - - Awaiting: first session to start with Step 1 (geometry constants). +- [x] **MAJOR REFACTOR: VGA-native renderer (40×25 viewport, 320×200 native output)** + (2026-06-19: all core goals achieved piecemeal during Phase 2 development.) + - 40×25 tile viewport: `SCREEN_TILES_W=40`, `SCREEN_TILES_H=25` ✓ + - wTileMap 1,000 B (`W_TILEMAP_SIZE=1000`) ✓ + - wSurroundingTiles 1,728 B (48×36, larger than plan's 1,408) ✓ + - Native 320×200 back buffer (`GB_BACKBUF_SIZE=64000`); no 2× scaling ✓ + - `render_bg` uses `bg_surface` (384×288 offscreen mirror → 320×200 blit) ✓ + - `present()` is `rep movsd` of 16,000 dwords ✓ + - WRAM layout at different addresses than plan predicted ($E000/$E800 + rather than $C788/$CD28) but all via `gb_memmap.inc` constants ✓ + REMAINING: build-time sprite pre-scale table (65 KB static 16×16 decode, + zero per-frame cost) — pure perf optimization, not correctness. The + `greedy-roaming-toucan.md` plan file is now superseded. - [ ] **Extend map data to cover the extended-draw region, then remove the two out-of-map clamps.** The 40×25-tile viewport draws a larger area than the original 20×18 and the player is screen-centered, so the camera can reach past the populated `wOverworldMap` data near map edges. Two temporary clamps in `src/overworld/overworld.asm` stop the garbage: - (1) `DrawTileBlock` clamps a block ID past the blockset → block 0; - (2) `LoadCurrentMapView` (added 2026-06-16) substitutes `wMapBackgroundTile` - for any block-map read outside `[wOverworldMap, wOverworldMapEnd)` — these - reads otherwise fall into `wSurroundingTiles` ($E000, directly below - `wOverworldMap` at $E580) and decode tile IDs as block IDs. - The address clamp removes the garbage now (dummy/border tiles in the - out-of-map area) but does NOT create editable cells there. The real fix is - to extend the maps so those regions hold real blocks: enlarge `MAP_BORDER` - / the block grid and regenerate `assets/map_headers.inc` with per-direction + (1) `DrawTileBlock` (~line 727): clamps a block ID past the blockset → block 0; + (2) `LoadCurrentMapView` (~line 816): substitutes `wMapBackgroundTile` + for any block-map read outside `[wOverworldMap, wOverworldMapEnd)`. + Memory layout (current): `wSurroundingTiles` = $E000 (1728 bytes, ends ~$E6BF); + `wOverworldMap` = $E800 (2048 bytes, ends $F000). There is a ~$140-byte gap + between them, so out-of-range reads fall into that gap, not wSurroundingTiles + (the old description saying "directly below at $E580" was stale). + The clamp still prevents garbage correctly. The real fix is to extend the + maps so those regions hold real blocks: enlarge `MAP_BORDER` / the block + grid and regenerate `assets/map_headers.inc` with per-direction `_win`/`_y`/`_x` re-tuned for the larger border (the formulas in `tools/gen_map_headers.py` are hand-tuned per direction — re-verify each empirically with the FRAME.BIN loop). Once done, delete both clamps. -- [ ] **BUG — collision: facing DOWN lets the player penetrate 1 tile into - objects** (signs, building roofs/fronts) when approached from the top. - Confirmed NOT a graphical artifact (user, 2026-06-15). Root cause: - `GetTileInFrontOfPlayer` (src/overworld/overworld.asm) checks the tile **±1** - from the player-center tile (20,12), but the player moves in 16px / 2-tile - steps, so it must check **±2** — matching pret - `_GetTileAndCoordsInFrontOfPlayer` (engine/overworld/player_state.asm): - down=`lda_coord 8,11`, up=`8,7`, left=`6,9`, right=`10,9` (all ±2 from - center 8,9). Proposed fix: down (20,13)→(20,14), up (20,11)→(20,10), - left (19,12)→(18,12), right (21,12)→(22,12); then verify all four - directions in DOSBox-X (down stops short of objects; up/left/right don't - become over-restrictive). +- [x] **BUG — collision offsets wrong for DOS viewport** (fixed 2026-06-19). + `GetTileInFrontOfPlayer` and the standing-tile read in `UpdatePlayerSprite` + used pret's 20-wide-tilemap coordinates (row 9, col 8). The DOS port centers + the player at row 17, col 24 in the 40-wide `wTileMap` (render_sprites +36/+96 + shift; bg_scy=32). Fixed by adding `PLAYER_STANDING_ROW=17` / `PLAYER_STANDING_COL=24` + constants to `gb_memmap.inc` and updating all five read sites. Also fixed + `gen_map_headers.py` NORTH (−4 → −5) and WEST (−6 → −7) view-pointer + formulas which had the same off-by-one due to YBC=1/XBC=1 row/col skipping. +- [ ] **BUG — holding a direction lets the player walk ~1 tile into an impassable + block before being stopped.** Root cause: for SOUTH and EAST movement, + `wYBlockCoord`/`wXBlockCoord` goes 0→1 on the first sub-step without + triggering `LoadCurrentMapView` (view only updates on the 1→2 crossing). + The collision check at `W_WALK_COUNTER=0` reads the stale `wTileMap`, which + still reflects the pre-sub-step view — so it checks the last tile of the + player's CURRENT block instead of the first tile of the NEXT block. For + NORTH and WEST the block-coord underflows (0→FF) on the first sub-step, + which IS a crossing and immediately updates the view, so those directions + are unaffected. Fix: in `GetTileInFrontOfPlayer` (overworld.asm ~line 1131), + add the sub-block byte-offset to the wTileMap address: + Down: `row = PLAYER_STANDING_ROW + W_Y_BLOCK_COORD*2 + 2` + Right: `col = PLAYER_STANDING_COL + W_X_BLOCK_COORD*2 + 2` + (Up and Left don't need adjustment — view is always current for those.) + Note: the original GB has the same stale-view window but standard map blocks + are 4 tiles deep so collision is always caught on the second sub-step. Our + port may have thinner collision geometry in some spots causing visible bleed. +- [ ] **BUG — rapid direction change can bypass or falsely trigger collision.** + Tapping a new direction during the last frames of a walk step can flip + `W_SPRITE_PLAYER_FACING_DIR` before the walk counter reaches 0, so the next + collision check fires against the new facing tile rather than the tile in the + direction of travel. Root cause: `UpdateSprites` (called every frame) updates + the facing from the step vector, but the GB serializes this via its frame-locked + joypad latch. Fix: latch the direction used for the collision check for the + duration of the current step (don't allow mid-step facing updates to influence + the next collision sample). See `OverworldLoop:.handleDirection` TODO comment + in `src/overworld/overworld.asm`. Defer until after Phase 2 NPC work. - [x] **PERF — heavily optimize render_bg** (2026-06-15). The per-pixel 2bpp→8bpp decode is no longer in the hot path: a 24 KB decoded-tile cache (`tile_cache`, 384 tiles × 64 B, BGP baked in) pre-decodes $8000-$97FF @@ -164,19 +187,13 @@ Prioritized task list. Check off items as they complete; add new items with phas refactor below): pre-render the map torus to an offscreen 8bpp surface and blit a SCX/SCY-offset viewport, updating only the RedrawRowOrColumn edge — eliminates nearly all per-frame tile work. -- [ ] **NEEDS FIXED — red strip / out-of-range pixel values.** A red strip - appears on screen that is NOT background data: under the current lazy - "pea-soup green" palette the BG can only render raw colors 0–3 (all green) - and sprites 4–11 (also green), and `commit_palette` only initializes DAC - entries 0–11. A red strip therefore means some renderer path writes pixel - values ≥12, which index the leftover boot `test_palette` ramps (12–63 = - red). Distinct from the missing-map-connector junk (that's a separate - item). Investigate writers that could emit ≥12 into the back buffer: - `render_window`/`row_buf` edge handling, `render_sprites` with garbage - OAM/tile/attr in uninitialized slots, or any direct back-buffer write. - Belt-and-suspenders fix: also initialize DAC entries 12–255 to a safe - color (or black) in `video_init` so out-of-range values don't show as - garish red and are easier to spot/triage. (Reported 2026-06-15.) +- [x] **Red strip / out-of-range pixel values** — resolved by the native-width + surface renderer rewrite (`render_bg` now fills all 320×200 back-buffer + pixels from `bg_surface` values 0–3 before `render_sprites` overwrites + sprite pixels with 4–11). No renderer path can write ≥12; confirmed absent + in `DEBUG_TRANSITION` and `DEBUG_BASELINE` FRAME.BIN dumps (2026-06-19). + If it resurfaces, investigate `render_window` row_buf edge handling or + uninitialized OAM ghost entries. - [x] **Map connections: un-stub the LoadTileBlockMap N/S/W/E connection strips** (2026-06-15). `LoadNorthSouthConnectionsTileMap` / `LoadEastWestConnectionsTileMap` are translated and wired in; the N/S/W/E @@ -185,16 +202,16 @@ Prioritized task list. Check off items as they complete; add new items with phas in SetupPalletTown). Dump-verified: the wOverworldMap N border = Route 1's bottom 3 rows, S border = Route 21's top 3 rows. So the border now shows real adjacent terrain instead of the background-block wall. -- [ ] **Map transition across a connection — faithful LoadMapHeader** (next up). - Decision (2026-06-15): build the real ROM map-header infrastructure (not a - hardcoded dispatcher) so Pallet Town ↔ Route 1 ↔ Route 21 become walkable. - **Full plan + format spec + phased steps + the pointer-relocation challenge - are in docs/loadmapheader_handoff.md** — start there. Involves: embed + - relocate map headers/object data + a MapHeaderPointers table; translate - LoadMapHeader / LoadTilesetHeader / CopyMapConnectionHeader / CheckMapConnections; - wire the post-step connection check into OverworldLoop; retire the - SetupPalletTown scaffold. Renderer needs no changes (surface mirror is - decoupled). DrawTileBlock clamp stays (E/W + past-map-end). +- [x] **Map transition across a connection — faithful LoadMapHeader** (2026-06-19). + `LoadMapHeader` / `LoadTilesetHeader` / `CopyMapConnectionHeader` / + `CheckMapConnections` all translated and wired. `MapHeaderPointers` table + and connection structs generated by `tools/gen_map_headers.py` → + `assets/map_headers.inc`. `SetupPalletTown` scaffold retired; map loads via + `LoadMapData` → `LoadMapHeader` on enter and `.mapTransition` in + `AdvancePlayerSprite` on connection crossing. NORTH/WEST view-pointer + formulas corrected (−4→−5, −6→−7) for YBC=1/XBC=1 skip. Verified via + `DEBUG_TRANSITION` FRAME.BIN: player lands on Route 1 path at correct + screen position (2026-06-19). - [ ] Translate NPC movement / collision - [ ] Translate random encounter trigger - [ ] Translate battle engine (UI rendering pass first) diff --git a/docs/translation_log.md b/docs/translation_log.md index 4d6a446e..993e33e5 100644 --- a/docs/translation_log.md +++ b/docs/translation_log.md @@ -607,3 +607,544 @@ Smooth fine-scroll is now applied natively via offset to the viewport blit using Removed dead VRAM-ring scroll routines (`CopyMapViewToVRAM`, `FillExtraVRAMRows`, `RedrawRowOrColumn`) and simplified `AdvancePlayerSprite`. *Add new entries below as routines are translated.* + +--- + +## Math (Multiply / Divide) + +- **Source:** `home/math.asm` +- **Translated:** `dos_port/home/math.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +Implemented as wrapper skeletons (`Multiply`, `Divide`) that call external implementations (`_Multiply`, `_Divide`). Preserves SM83 caller state around the external calls via stack pushes. + +--- + +## CountSetBits + +- **Source:** `home/count_set_bits.asm` +- **Translated:** `dos_port/home/count_set_bits.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +Loop structure preserved, counts bits in a string of bytes. Shift-and-carry approach retained using `shr` and `adc`. + +--- + +## StringCmp + +- **Source:** `home/compare.asm` +- **Translated:** `dos_port/home/compare.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +Uses standard `cmp` loop comparing bytes at ESI and EDX (representing HL and DE). + +--- + +## Random + +- **Source:** `home/random.asm` +- **Translated:** `dos_port/home/random.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +Wrapper skeleton. Calls `Random_` and then fetches `hRandomAdd` to return random value in AL. Preserves caller state. + +--- + +## Copy Routines (FarCopyData / CopyData) + +- **Source:** `home/copy.asm` +- **Translated:** `dos_port/home/copy.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +- **CopyData** implements a 32-bit block move optimization. Instead of an 8-bit copy loop, it processes the copy in 4-byte (`DWORD`) chunks where possible via a `cmp ecx, 4` sub-loop, dropping to 1-byte copies for the remainder. This significantly reduces memory bus utilization per the 386 optimization strategy. +- Video copy routines (`CopyVideoDataAlternate`, `CopyVideoDataDoubleAlternate`) check LCDC bit 7 to selectively branch to `CopyVideoData` or `CopyVideoDataDouble` with register preservation and bit manipulation intact. +- Far routines (`FarCopyData`) wrap bankswitching with pushes. + +--- + +## Array Operations (SkipFixedLengthTextEntries / AddNTimes) + +- **Source:** `home/array.asm` +- **Translated:** `dos_port/home/array.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +**Excellent strength reduction.** The original SM83 looped AL times doing `add HL, BC`. The x86 translation replaces the iterative loops with a single `imul ecx, eax` followed by `add esi, ecx`, converting an O(N) loop into an O(1) mathematical operation. This perfectly aligns with the performance goals of the 386 port strategy. + +--- + +## Multiply / Divide Logic (_Multiply / _Divide) + +- **Source:** `main.asm` (math routines) +- **Translated:** `dos_port/src/util/multiply_divide.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +- `_Multiply` discards the original 8-bit iterative addition loop and leverages the native 386 hardware `mul` instruction. It reconstructs the 24-bit multiplicand into a 32-bit register (`EAX`), multiplies by the 8-bit multiplier (`ECX`), and cleanly writes the 32-bit product back to `H_PRODUCT` in big-endian format. Perfect O(1) cycle implementation. +- `_Divide` maintains faithful step-by-step subtraction logic to accurately preserve Game Boy memory side-effects and byte alignments for `hDividend` and `hDivideBuffer`, but caches the operations in 32-bit registers (`EAX`, `EDI`, `EDX`) to avoid heavy memory access penalties. + +--- + +## BCD Math (AddBCD / SubBCD / DivideBCD) + +- **Source:** `main.asm` (BCD routines) +- **Translated:** `dos_port/src/util/bcd.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +**Brilliant hardware optimization:** The translated `AddBCD` and `SubBCD` completely replace the Game Boy's manual Binary-Coded Decimal correction logic by utilizing the native x86 `DAA` (Decimal Adjust AL after Addition) and `DAS` (Decimal Adjust AL after Subtraction) instructions. This pairs natively with `adc` and `sbc` for massive cycle savings while remaining 100% behaviorally accurate. `DivideBCD` also uses an optimized shift-and-subtract approach. + +--- + +## Random Number Generator (Random_) + +- **Source:** `main.asm` (random logic) +- **Translated:** `dos_port/src/util/random.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +Accurately preserves the SM83 carry flag chain. The Game Boy original uses `adc b` and later `sbc b` without clearing flags, meaning it relies on the residual carry from the caller and previous instructions. The x86 translation perfectly mirrors this by keeping the exact sequence using `adc al, bl` and `sbb al, bl`. + +--- + +*Add new entries below as routines are translated.* + +## Text Box Coordinates (GetAddressOfScreenCoords) + +- **Source:** `engine/menus/text_box.asm` +- **Translated:** `dos_port/engine/menus/text_box.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +**Brilliant hardware optimization:** The Game Boy's `GetAddressOfScreenCoords` typically requires iterative looping to calculate the tilemap offset (`row * 20 + col`). In the x86 translation, this loop has been entirely replaced by an O(1) calculation using the 32-bit hardware `imul eax, 20` instruction. This dramatically reduces cycles by converting an O(N) iterative addition loop into a single optimized instruction perfectly aligned with the 386 optimization strategy. + +--- + +## PC / Item Swap Menus (RemoveItemByID / HandleItemListSwapping) + +- **Source:** `engine/menus/pc.asm`, `engine/menus/swap_items.asm` +- **Translated:** `dos_port/engine/menus/pc.asm`, `dos_port/engine/menus/swap_items.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +Provides fully unwired skeletons for inventory management, abstracting iterative array scanning and item recombination. `HandleItemListSwapping` makes heavy use of 32-bit offset additions (e.g. `movzx ecx, al; add esi, ecx`) to calculate base pointers for the list cursor offset rather than the native 8-bit pointer advancement strategies, drastically reducing pressure on pointer manipulation loops. + +--- + +## Save System (SaveMainData / CalcCheckSum) + +- **Source:** `engine/menus/save.asm` +- **Translated:** `dos_port/engine/menus/save.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +Maintains faithful SRAM boundaries and checksum calculation. `CalcCheckSum` leverages a fast 32-bit `movzx ecx, cx` loop register countdown to rapidly sum the SRAM state. + +--- + +## Text Engine Base (text.asm) + +- **Source:** `text.asm` +- **Translated:** `dos_port/text.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +Ported as a section-based include skeleton for later text data insertion. + +--- + +*Add new entries below as routines are translated.* + +## Item Inventory (AddItemToInventory_ / RemoveItemFromInventory_) + +- **Source:** `engine/items/inventory.asm` +- **Translated:** `dos_port/src/items/inventory.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +Replaces the Game Boy's iterative 8-bit pointer advancement for item slot offsets with native 32-bit math. In `AddItemToInventory_`, the target memory address for the new item slot is computed instantly via `lea edx, [esi + 1 + ecx]`, completely eliminating loop-based pointer math perfectly aligned with the 386 strategy. + +--- + +## Get Bag Item Quantity + +- **Source:** `engine/items/get_bag_item_quantity.asm` +- **Translated:** `dos_port/src/items/get_bag_item_quantity.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +A clean, unwired translation of `GetQuantityOfItemInBag`. Standard array scanning returning item quantity. + +--- + +## Pokemon Experience / Level Up + +- **Source:** `engine/pokemon/experience.asm`, `engine/battle/experience.asm` +- **Translated:** `dos_port/engine/pokemon/experience.asm`, `dos_port/engine/battle/experience.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +Heavily utilizes native 32-bit registers to streamline operations. The original 24-bit experience comparisons and arithmetic that required complex byte-by-byte manual cascades are instead highly optimized using the native capabilities of x86 32-bit registers to execute wide comparisons directly. + +--- + +## Remove Pokemon + +- **Source:** `engine/pokemon/remove_mon.asm` +- **Translated:** `dos_port/src/pokemon/remove_mon.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +A faithful array-shift implementation for PC and Party deletions. It capitalizes on the previously documented `imul` optimized `AddNTimes` routine to rapidly calculate struct boundaries (`PARTYMON_STRUCT_LENGTH` / `BOXMON_STRUCT_LENGTH`) and employs `CopyDataUntil` with seamless 32-bit addressing. + +--- + +## Decrement PP + +- **Source:** `engine/battle/decrement_pp.asm` +- **Translated:** `dos_port/engine/battle/decrement_pp.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +Optimizes battle status bit-checking. The original Game Boy logic required checking individual bits sequentially. The x86 translation compresses this into a single 32-bit mask test (`test al, (1 << STORING_ENERGY) | (1 << THRASHING_ABOUT) | (1 << ATTACKING_MULTIPLE_TIMES)`), saving multiple cycles. + +--- + +## Pikachu Status Verification + +- **Source:** `engine/pikachu/pikachu_status.asm` +- **Translated:** `dos_port/engine/pikachu/pikachu_status.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +Highly optimized struct verification for `IsThisPartyMonStarterPikachu` and `IsThisBoxMonStarterPikachu`. Heavy use of the O(1) `imul`-powered `AddNTimes` to immediately jump into `wBoxMon` or `wPartyMon` sub-arrays, instantly bridging OT Names, OT IDs, and Species fields without manual array traversal. + +--- + +*Add new entries below as routines are translated.* + +## Flag Action (FlagActionPredef / FlagAction) + +- **Source:** `engine/flag_action.asm` +- **Translated:** `dos_port/engine/flag_action.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +**Native Bitwise Optimization:** `FlagAction` eliminates the Game Boy's bit-shifting loop required to generate a bitmask. By loading the bit index into `cl` and using the native x86 `shl dl, cl` instruction, the bitmask is generated in a single cycle. Additionally, the byte offset within the flag array is computed instantly via `shr al, 3` and directly added to the 32-bit base pointer (`add esi, eax`), fully optimizing array access. + +--- + +## Joypad Input Handling (_Joypad / ReadJoypad_) + +- **Source:** `engine/joypad.asm` +- **Translated:** `dos_port/engine/joypad.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +Accurately simulates the Game Boy hardware `IO_JOYP` polling logic. The state-transition calculations (deriving newly pressed and released keys from the previous state) heavily leverage hardware register cascades (e.g., `xor`, `and`, `not`) to compute `hJoyPressed` and `hJoyReleased` natively without unnecessary memory swapping. Applies the `wJoyIgnore` mask via an efficient inverted bitwise `and`. + +--- + +## Predef Pointers (GetPredefPointer) + +- **Source:** `engine/predefs.asm` +- **Translated:** `dos_port/engine/predefs.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +**`LEA` Multiplier Optimization:** The `PredefPointers` table relies on a 3-byte struct (1 byte for Bank, 2 bytes for Address). To access the nth element, the original Game Boy loops or does complex additions to multiply the index by 3. The x86 translation resolves this natively using the 32-bit `lea` (Load Effective Address) instruction: `lea ecx, [ecx + ecx*2]`. This instantly multiplies the index by 3 and elegantly offsets into the table in O(1) time. + +--- + +*Add new entries below as routines are translated.* + +--- + +## Debug State / Party (PrepareNewGameDebug / SetDebugNewGameParty) + +- **Source:** `engine/debug/debug_party.asm` +- **Translated:** `dos_port/src/debug/debug_party.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +A pure data-setup unwired skeleton. Rapidly bypasses legacy loops and directly injects optimal state flags, utilizing optimized division-by-8 loop generation to cleanly populate Pokedex bit fields natively (`NUM_POKEMON / 8` and `(1 << (NUM_POKEMON % 8)) - 1`). + +--- + +## Surfing Pikachu Minigame Math (SurfingMinigame_AddPointsToTotal / SurfingMinigame_Deduct1HP) + +- **Source:** `engine/minigame/surfing_pikachu.asm` +- **Translated:** `dos_port/src/minigame/surfing_pikachu.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +**Native BCD Minigame Logic:** Completely detaches the minigame score calculations from graphical state logic. BCD addition and subtraction points scoring are perfectly optimized utilizing the native 386 hardware `DAA` (addition) and `DAS` (subtraction) instructions, natively maintaining a constant cap limitation (`0x9999`) without manual software correction arrays. + +--- + +## Slot Machine Arrays & RNG (SlotMachine_FindWheel1Wheel2Matches / SlotMachine_CheckForMatch) + +- **Source:** `engine/slots/slot_machine.asm` +- **Translated:** `dos_port/src/slots/slot_machine.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +Isolates the 3x3 slot machine reel array mapping and random number generation from graphical rendering routines. The logic relies on clean 32-bit `ESI`/`EDI` pointer offset indexing to verify slot layout rows directly, elegantly replacing convoluted 8-bit mapping pointers. + +--- + +*Add new entries below as routines are translated.* + +## Itemfinder / Hidden Items (HiddenItemNear) + +- **Source:** `engine/items/itemfinder.asm` +- **Translated:** `dos_port/src/items/itemfinder.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +Coordinate delta logic optimally resolved utilizing simple `add` and native carry boundary logic (`jc` / `jnc`) avoiding multi-step conditional branching. + +--- + +## BCD Transaction Subtraction (SubtractAmountPaidFromMoney_) + +- **Source:** `engine/items/subtract_paid_money.asm` +- **Translated:** `dos_port/src/items/subtract_paid_money.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +Expertly handles 3-byte BCD array math using native 32-bit registers, deferring pointer iteration to the ultra-fast `StringCmp` and hardware-accelerated `SubBCDPredef` (which relies on native `DAS`). This guarantees instant, safe monetary transactions exactly adhering to GB constraints. + +--- + +## Super Rod Encounters & PRNG (GenerateRandomFishingEncounter) + +- **Source:** `engine/items/super_rod.asm` +- **Translated:** `dos_port/src/items/super_rod.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +Effectively maintains the glitch-accurate pseudo-random (PRNG) boundary constraints (`0x66`, `0xB2`, `0xE5`) corresponding to specific Pokemon encounters. Slot array iteration skips iterative counts by advancing pointers directly in `add esi, 8` intervals. + +--- + +## TM Pricing Arrays (GetMachinePrice) + +- **Source:** `engine/items/tm_prices.asm` +- **Translated:** `dos_port/src/items/tm_prices.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +BCD packed array access elegantly transformed. The Game Boy's `swap a` macro is replaced by efficient 32-bit native register manipulation (`shl cl, 4; shr al, 4; or al, cl`). The array indexing utilizes `movzx ecx, al; add esi, ecx` natively detaching the pointer array math from 8-bit registers. + +--- + +*Add new entries below as routines are translated.* + +## Town Map Data Extraction (LoadTownMapEntry / TownMapCoordsToOAMCoords) + +- **Source:** `engine/items/town_map.asm` +- **Translated:** `dos_port/engine/items/town_map.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +**Graphical Independence:** Completely extracts the map array lookups, duplicate-filtering, and OAM conversion logic out from the visual map drawing routines. Uses clean 32-bit `lea` instructions (`lea esi, [esi + ecx*2]`) for pointer resolution, avoiding scaling loops entirely. + +--- + +## TM/HM Base Engine (CheckIfMoveIsKnown / CanLearnTM) + +- **Source:** `engine/items/tmhm.asm`, `engine/items/tms.asm` +- **Translated:** `dos_port/engine/items/tmhm.asm`, `dos_port/engine/items/tms.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +Unwired array scanners for validating if a Pokemon possesses the capacity to learn a move or if the move is currently active in the party move structures. + +--- + +## Item Effects Engine (ApplyHealingItem / RestorePPAmount / Func_d85d) + +- **Source:** `engine/items/item_effects.asm` +- **Translated:** `dos_port/engine/items/item_effects.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** GLITCH (Preserved original `MAX_ETHER` PP mask bypass). + +### Notes + +**UI Abstraction & Native Math:** +- `Func_d85d` completely abstracts evolution stone logic away from UI loops. +- `ApplyHealingItem` optimally handles 16-bit Big-Endian potion and revive logic. It seamlessly utilizes the native x86 `sub` and `sbc` chain to verify maximum bounds boundaries and natively divides by 2 (`shr al, 1; rcr al, 1`) for Half-HP Revival logic. +- `RestorePPAmount` accurately ports the legacy Max Ether glitch where upper bits (PP Up increments) bypass masking. + +--- + +*Add new entries below as routines are translated.* + +## Bill's PC Headless Logic (BillsPCDepositLogic / BillsPCWithdrawLogic / BillsPCReleaseLogic / KnowsHMMove) + +- **Source:** `engine/pokemon/bills_pc.asm` +- **Translated:** `dos_port/src/pokemon/bills_pc.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** GLITCH (Preserved original unreachable logic in `KnowsHMMove`). + +### Notes + +**Headless PC Abstraction:** +Worker expertly separated the core box transaction operations (Depositing, Withdrawing, and Releasing) entirely from their UI and graphics wrappers. The translated functions operate as headless bounds-checking APIs returning strict carry-flag conditions (`CF=1` for box full/party empty errors) before safely triggering underlying `MoveMon` / `RemovePokemon` algorithms. + +**HM Move Parsing:** +`KnowsHMMove` converts multi-cycle structure traversal natively using the O(1) `imul` arithmetic to instantly seek to the Pokemon's move array. It resolves HM applicability using the 32-bit bounded `IsInArray` function passing a data-driven `HMMoveArray`, cleanly optimizing move verification. Note that the original Game Boy codebase contained an unreachable path attempting to parse Box Mon structs; this has been preserved for bug-compatibility. + +--- + +*Add new entries below as routines are translated.* + +## Pokemon Array Router (_MoveMon / _AddEnemyMonToPlayerParty / AddPartyMon_WriteMovePP) + +- **Source:** `engine/pokemon/add_mon.asm` +- **Translated:** `dos_port/engine/pokemon/add_mon.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +**Massive Structural Abstraction:** Worker successfully routed the enormous `_MoveMon` Pokemon structural data transfer logic. It seamlessly handles moving complex `BOXMON` and `PARTYMON` structs between the Box, Party, and Daycare boundaries headless of any UI interaction. The implementation optimally extracts structural constraints utilizing 32-bit offset arithmetic and `AddNTimes` to instantly resolve pointer targets without legacy iterative pointer increments. +`AddPartyMon_WriteMovePP` and `_AddEnemyMonToPlayerParty` perfectly optimize array routing while handling Pokédex flag writes natively. + +--- + +## Mon Data Structural Loaders (LoadMonData_ / GetMonSpecies) + +- **Source:** `engine/pokemon/load_mon_data.asm` +- **Translated:** `dos_port/engine/pokemon/load_mon_data.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +**Headless Pointers:** Cleanly isolates Pokemon data pointer parsing (`LoadMonData_`) and indexing (`GetMonSpecies`) away from the UI-dependent `learn_move.asm` graphics logic. The data fetching seamlessly relies on ultra-fast native 32-bit structural jumping (`add esi, edx`) resolving list index queries instantly. + +--- + +*Add new entries below as routines are translated.* + +--- + +## Evolutions & Learnsets Engine (EvolutionAfterBattle / LearnMoveFromLevelUp / WriteMonMoves) + +- **Source:** `engine/pokemon/evos_moves.asm` +- **Translated:** `dos_port/engine/pokemon/evos_moves.asm` +- **Date:** 2026-06-18 +- **H-flag:** Not involved. +- **Bug tags:** None. + +### Notes + +**Headless Iteration:** The `EvosMovesPointerTable` structural parsers were perfectly mapped out, extracting the pure logical sequences out of `EvolutionAfterBattle` and `LearnMoveFromLevelUp`. Legacy text-box UI routines, extensive string prints, and pure graphical evolution routines were strictly carved out, leaving behind an optimized 32-bit array traversal engine using fast pointers (`add esi, ecx`) and `AddNTimes` for base stat recalculations and pointer data routing (`WriteMonMoves_ShiftMoveData`). + +--- + +*Add new entries below as routines are translated.* diff --git a/dos_port/assets/map_headers.inc b/dos_port/assets/map_headers.inc index cc7aa2b3..baaeb3d1 100644 --- a/dos_port/assets/map_headers.inc +++ b/dos_port/assets/map_headers.inc @@ -28,7 +28,7 @@ map_header_PALLET_TOWN: db 10 ; strip length db 10 ; connected map width db 35, 0 ; Y, X align - dw 0xE9B8 ; view ptr + dw 0xE9A2 ; view ptr ; SOUTH connection to ROUTE_21 db 0x20 ; map id dw 0x5200 ; strip src @@ -85,7 +85,7 @@ map_header_ROUTE_21: db 10 ; strip length db 10 ; connected map width db 17, 0 ; Y, X align - dw 0xE8F2 ; view ptr + dw 0xE8DC ; view ptr dw 0x547F ; object_data_ptr map_object_ROUTE_21: db 0x43 ; border block diff --git a/dos_port/baseline_out.png b/dos_port/baseline_out.png deleted file mode 100644 index 11dd8351ea12b836eb33c8c63ed8c8c390891ca0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4754 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 diff --git a/dos_port/dosbox_south.png b/dos_port/dosbox_south.png deleted file mode 100644 index 9d9b8256c1cb1ae022d014464578f659848b70fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25926 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 m=y}wAo?Jj6`2RVF&e5N)S3YkmKG%dF*U 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 diff --git a/dos_port/frame.png b/dos_port/frame.png deleted file mode 100644 index 9243dce00fc793cf95b6a3a782a252bc7cc245c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9524 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 XPnL*rh#%_TP)}48)a9#WErb6T+f&yz diff --git a/dos_port/frame_out.png b/dos_port/frame_out.png deleted file mode 100644 index ff662d7213b5454b6301541e59947f34ce437c6f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3786 zcmZWsc{o)6+qO^2lBlGRJt`C>JK0U7kW|cM-^NZcGnjl4S+Y*{5DJm(X`1KKJMTJoodQlW1dY#(z-kAR8MSzlFK+O*S@m z12FD6zyV&5dDJo3*aXroj1BD`7ElUt{y*LyiSK^Gh7_#5S7Bb?W%#t;M(WhdYY}~4 z{l{R!1;f~U`&jV!a4BHo;CbT!TBj=UsmCgQLO zFpjYuFk;a%bamp-;s=@B>>l#yNP!kac8;DdY1n?=T$_Vu^u_g|t|;F>$6U0d(Tcy* z7~AC&0eDN-U-$93Lt$(;p5i~enAAc?S~rBTXy3<+baj%lNVremyR5l*Mn&j|^ytUt zd9MfKVaR1YRUuKkAMwQSAF4>3?O_Pdl_$9-taK0GG%8WdIo*_l{A}0zBq4acmDZRr zqRPQ@pvQz;?BDzwWaXsuqm{h;Q@Lja3-ol4@0r!lD$JD2~yU%rPE*$c*IV2<~1g3K1q>e4>CmIoSwUBDP>hYUif zF5s%zj`(M(=8)M#{D-qZ+zQ5P6>U(|4rB@Yv1`jZfq!M4#}V0}9(g)}&yTQkqJr8j zrU6!jzOi$$o@z{wg_2y{SFy5v3QtK^!$G8=fP6rpQl{Eue8uMXgqoFIQ#Y zY>HwPUo_BS*THSSfge_ld1iLHddPL5PKBy_GHA?GM9g4;arE+`sF(rD<)}_!`>jk>F@6`5ssi_gyT(CW6##q4)Ra6K{p;umXv=~ z)}EHz2^!nqybB%&-3ZXIKp?j%Ki8DoG+aLljV(EPv8CGvjP!JzCue<0Yyh!$OGW

n{hV|b}#j&y3uT8!5&Q*vBd2_LZ6IPAF|&c#>7 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 XPnL*rh#%_TP)}48)a9#WErb6T+f&yz diff --git a/dos_port/src/overworld/movement.asm b/dos_port/src/overworld/movement.asm index 174e0173..704fbe44 100644 --- a/dos_port/src/overworld/movement.asm +++ b/dos_port/src/overworld/movement.asm @@ -302,8 +302,8 @@ UpdatePlayerSprite: jmp .disable .checkTextBox: - ; lower-left BG tile the sprite stands on (coord 24,17); >= $60 → text box - mov al, [ebp + W_TILEMAP + 17 * SCREEN_TILES_W + 24] + ; lower-left BG tile the sprite stands on; >= $60 → text box + mov al, [ebp + W_TILEMAP + PLAYER_STANDING_ROW * SCREEN_TILES_W + PLAYER_STANDING_COL] mov [ebp + H_TILE_PLAYER_STANDING_ON], al cmp al, MAP_TILESET_SIZE jb .lowerLeftIsMapTile diff --git a/dos_port/src/overworld/overworld.asm b/dos_port/src/overworld/overworld.asm index b111e115..6bc10867 100644 --- a/dos_port/src/overworld/overworld.asm +++ b/dos_port/src/overworld/overworld.asm @@ -83,9 +83,10 @@ PALLET_TOWN_HEIGHT equ 9 PALLET_TOWN_BORDER_BLOCK equ 0x0B ; border block from PalletTown_Object TILESET_BANK_FLAT equ 0x01 ; ignored in flat model (TODO-HW: ROM banking) -; wCurrentTileBlockMapViewPointer for Pallet Town centered on player at (8,8) tiles: -; wOverworldMap + (MAP_BORDER - 2) * stride + (MAP_BORDER - 2) +; wCurrentTileBlockMapViewPointer for Pallet Town: view top-left at block (6, 4) so +; player block (10, 10) sits at view offset (4, 6) → wSurroundingTiles (16, 24). ; stride = PALLET_TOWN_WIDTH + 2*MAP_BORDER = 10 + 12 = 22 +; = W_OVERWORLD_MAP + MAP_BORDER * 22 + (MAP_BORDER - 2) = W_OVERWORLD_MAP + 136 PALLET_TOWN_VIEW_PTR equ W_OVERWORLD_MAP + (MAP_BORDER) * (PALLET_TOWN_WIDTH + MAP_BORDER * 2) + (MAP_BORDER - 2) ; Number of connections in the Block/Connect strips (0xFF = none — disables strip loading) @@ -265,6 +266,12 @@ OverworldLoop: mov [ebp + W_PLAYER_MOVING_DIRECTION], dl mov [ebp + W_SPRITE_PLAYER_FACING_DIR], dh + ; TODO: rapid direction changes can desync facing from collision. UpdateSprites + ; (called every frame) may flip W_SPRITE_PLAYER_FACING_DIR mid-step via the step + ; vector, so the first collision check after a direction tap fires against the NEW + ; facing tile rather than the tile in the direction of completed travel. The GB + ; serializes this via its frame-locked joypad latch; we need to latch the checked + ; direction for the duration of the step. Low priority — defer to after Phase 2. call CollisionCheckOnLand ; CF = 1 → blocked (face that way, don't move) jc OverworldLoop @@ -1115,23 +1122,32 @@ CollisionCheckOnLand: ; wTileInFrontOfPlayer and returns it in CL. ; --------------------------------------------------------------------------- GetTileInFrontOfPlayer: + ; Pret ref: engine/overworld/player_state.asm:_GetTileAndCoordsInFrontOfPlayer + ; lda_coord c, r = W_TILEMAP + r*20 + c (pret 20-wide tilemap) + ; DOS tilemap is 40 wide; player standing tile = PLAYER_STANDING_ROW=17, + ; PLAYER_STANDING_COL=24. Fronts are ±2 rows/cols from the standing tile. + ; + ; Down (row+2, col+0) = (19, 24) + ; Up (row-2, col+0) = (15, 24) + ; Left (row+0, col-2) = (17, 22) + ; Right (row+0, col+2) = (17, 26) mov al, [ebp + W_SPRITE_PLAYER_FACING_DIR] cmp al, SPRITE_FACING_DOWN jne .notDown - mov esi, W_TILEMAP + 17 * SCREEN_TILES_W + 24 ; lda_coord 24, 17 + mov esi, W_TILEMAP + (PLAYER_STANDING_ROW + 2) * SCREEN_TILES_W + PLAYER_STANDING_COL jmp .read .notDown: cmp al, SPRITE_FACING_UP jne .notUp - mov esi, W_TILEMAP + 15 * SCREEN_TILES_W + 24 ; lda_coord 24, 15 + mov esi, W_TILEMAP + (PLAYER_STANDING_ROW - 2) * SCREEN_TILES_W + PLAYER_STANDING_COL jmp .read .notUp: cmp al, SPRITE_FACING_LEFT jne .notLeft - mov esi, W_TILEMAP + 16 * SCREEN_TILES_W + 23 ; lda_coord 23, 16 + mov esi, W_TILEMAP + PLAYER_STANDING_ROW * SCREEN_TILES_W + (PLAYER_STANDING_COL - 2) jmp .read .notLeft: - mov esi, W_TILEMAP + 16 * SCREEN_TILES_W + 25 ; lda_coord 25, 16 (facing right) + mov esi, W_TILEMAP + PLAYER_STANDING_ROW * SCREEN_TILES_W + (PLAYER_STANDING_COL + 2) .read: movzx ecx, byte [ebp + esi] mov [ebp + W_TILE_IN_FRONT_OF_PLAYER], cl @@ -1142,12 +1158,41 @@ GetTileInFrontOfPlayer: ; Pret ref: engine/gfx/sprite_oam.asm:_IsTilePassable ; ; In: CL = tile ID. Scans the $FF-terminated passable-tile list pointed to by -; wTilesetCollisionPtr. +; wTilesetCollisionPtr (GB pointer to list in ROM window at OW_COLL_GBADDR). ; Out: CF = 0 if CL is in the list (passable), CF = 1 otherwise. ; Clobbers AL, ESI. +; +; SM83 original: +; ld hl, wTilesetCollisionPtr ; load the pointer-to-pointer +; ld a, [hli] +; ld h, [hl] +; ld l, a ; HL = *wTilesetCollisionPtr (the actual list address) +; .loop: +; ld a, [hli] +; cp $ff +; jr z, .tileNotPassable +; cp c ; c = tile to test +; jr nz, .loop +; xor a ; ZF=1 CF=0 → passable +; ret +; .tileNotPassable: +; scf ; CF=1 → not passable +; ret ; --------------------------------------------------------------------------- IsTilePassable: - clc + ; ESI = *wTilesetCollisionPtr (the flat GB address of the passable-tile list) + movzx esi, word [ebp + W_TILESET_COLLISION_PTR] +.loop: + mov al, byte [ebp + esi] + inc esi + cmp al, 0xFF + je .tileNotPassable ; hit terminator → blocked + cmp al, cl + jne .loop ; not this tile → keep scanning + clc ; found in list → passable + ret +.tileNotPassable: + stc ; not found → blocked ret ; --------------------------------------------------------------------------- diff --git a/tools/gen_map_headers.py b/tools/gen_map_headers.py index bfa1e382..2d158a1e 100755 --- a/tools/gen_map_headers.py +++ b/tools/gen_map_headers.py @@ -23,13 +23,12 @@ def get_connection(direction, conn_map_id, offset, cur_width, cur_height, conn_w if direction == "NORTH": _blk = conn_width * (conn_height - BORDER) + _src _map = _tgt - # Subtract one block row vs pret: pret carries accumulated H_SCY into the - # new map, so its _win was designed for H_SCY≠0. Our port resets H_SCY=0 - # and calls CopyMapViewToVRAM, placing the player (OAM Y=96→screen Y=80, - # tile row 10) at wSurroundingTiles block row 3 from view_start (Y_BC=1 - # adds 2 tile rows → tile 12 → block 3). So view_start = player_wOW_row - # − 3, which is one stride less than pret's formula. - view_start_row = conn_height + BORDER - 4 + # NORTH always enters with wYBlockCoord=1 (wYCoord=conn_height*2-1 is odd), + # so LoadCurrentMapView skips 2 tile rows from wSurroundingTiles. Player + # must land at view block row 4 (wSurroundingTiles tile 16 → with YBC=1 + # skip 2 → wTileMap row 16; feet at tile 19 → wTileMap row 17 = PLAYER_STANDING_ROW). + # player_wOW_row = BORDER + conn_height - 1; view_start = player_wOW_row - 4. + view_start_row = conn_height + BORDER - 5 view_start_col = BORDER - 6 _win = view_start_row * stride + view_start_col _y = conn_height * 2 - 1 @@ -52,7 +51,11 @@ def get_connection(direction, conn_map_id, offset, cur_width, cur_height, conn_w _blk = conn_width * _src + conn_width - BORDER _map = (cur_width + 2 * BORDER) * _tgt view_start_row = BORDER - 4 - view_start_col = conn_width + BORDER - 6 + # WEST always enters with wXBlockCoord=1 (wXCoord=conn_width*2-1 is odd), + # so LoadCurrentMapView skips 2 tile cols. Player must land at view block + # col 6 (wSurroundingTiles col 24+2=26 → skip 2 → wTileMap col 24 = PLAYER_STANDING_COL). + # player_wOW_col = BORDER + conn_width - 1; view_start_col = player_wOW_col - 6. + view_start_col = conn_width + BORDER - 7 _win = view_start_row * stride + view_start_col _y = offset * -2 _x = conn_width * 2 - 1