Fix collision offsets, map transitions, and update project docs

- 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 <noreply@anthropic.com>
This commit is contained in:
Happyarch
2026-06-19 02:09:01 -04:00
parent d5b1ca7dcd
commit ecf1f03bd2
16 changed files with 783 additions and 90 deletions

View File

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

141
TODO.md
View File

@@ -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)

View File

@@ -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.*

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -35,6 +35,74 @@ GB_TILEMAP1 equ 0x9C00
; WRAM bank 0 — $C000–$CFFF (4 KB)
; Always available. General-purpose work RAM.
GB_WRAM0 equ 0xC000
W_NUM_BAG_ITEMS equ 0xD31C
W_NUM_BOX_ITEMS equ 0xD539
W_CURRENT_MENU_ITEM equ 0xCC26
W_MAX_MENU_ITEM equ 0xCC28
W_BAG_SAVED_MENU_ITEM equ 0xCC2C
W_LIST_SCROLL_OFFSET equ 0xCC36
W_CUR_ITEM equ 0xCF90
W_WHICH_POKEMON equ 0xCF91
W_ITEM_QUANTITY equ 0xCF95
W_MAX_ITEM_QUANTITY equ 0xCF96
W_LOADED_MON_SPECIES equ 0xCF97
W_MON_DATA_LOCATION equ 0xCC49
W_CUR_PARTY_SPECIES equ 0xCF90
W_CUR_ENEMY_LEVEL equ 0xD126
W_TEXT_BOX_ID equ 0xD124
W_PARTY_MON1_MOVES equ 0xD172
W_PARTY_MON4_MOVES equ 0xD1F6
W_POKEDEX_OWNED equ 0xD2F6
W_POKEDEX_SEEN equ 0xD309
W_CUR_MAP equ 0xD35D
W_Y_COORD equ 0xD360
W_X_COORD equ 0xD361
W_PLAYER_MONEY equ 0xD346
W_OBTAINED_BADGES equ 0xD355
W_OBTAINED_HIDDEN_ITEMS_FLAGS equ 0xD6EF
W_TOWN_VISITED_FLAG equ 0xD70A
W_RIVAL_STARTER equ 0xD714
W_EVENT_FLAGS equ 0xD746
W_SURFING_MINIGAME_PIKACHU_HP equ 0xC5D6
W_SURFING_MINIGAME_RADNESS_MTR equ 0xC5D9
W_SURFING_MINIGAME_RADNESS_SCR equ 0xC5DA
W_SURFING_MINIGAME_TOTAL_SCORE equ 0xC5DC
W_SURFING_MINIGAME_TRICK_FLAGS equ 0xC62F
W_SLOT_MACHINE_SEVEN_AND_BAR_MODE_CHANCE equ 0xCC5B
W_SLOT_MACHINE_WHEEL1_OFFSET equ 0xCD3E
W_SLOT_MACHINE_WHEEL2_OFFSET equ 0xCD3F
W_SLOT_MACHINE_WHEEL3_OFFSET equ 0xCD40
W_SLOT_MACHINE_WHEEL1_BOTTOM_TILE equ 0xCD41
W_SLOT_MACHINE_WHEEL1_MIDDLE_TILE equ 0xCD42
W_SLOT_MACHINE_WHEEL1_TOP_TILE equ 0xCD43
W_SLOT_MACHINE_WHEEL2_BOTTOM_TILE equ 0xCD44
W_SLOT_MACHINE_WHEEL2_MIDDLE_TILE equ 0xCD45
W_SLOT_MACHINE_WHEEL2_TOP_TILE equ 0xCD46
W_SLOT_MACHINE_WHEEL3_BOTTOM_TILE equ 0xCD47
W_SLOT_MACHINE_WHEEL3_MIDDLE_TILE equ 0xCD48
W_SLOT_MACHINE_WHEEL3_TOP_TILE equ 0xCD49
W_SLOT_MACHINE_FLAGS equ 0xCD4C
W_SLOT_MACHINE_WHEEL1_SLIP_COUNTER equ 0xCD4D
W_SLOT_MACHINE_WHEEL2_SLIP_COUNTER equ 0xCD4E
W_SLOT_MACHINE_ALLOW_MATCHES_COUNTER equ 0xD095
W_LOADED_MON_EXP equ 0xCFA5
W_CUR_SPECIES equ 0xD0B4
W_MON_H_GROWTH_RATE equ 0xD0CA
W_REMOVE_MON_FROM_BOX equ 0xCF94
W_MOVE_MON_TYPE equ 0xCF94
W_PARTY_COUNT equ 0xD162
W_PARTY_MONS equ 0xD16A
W_PARTY_MON_OT equ 0xD272
W_PARTY_MON_NICKS equ 0xD2B4
W_PARTY_MON_NICKS_END equ 0xD2F6
W_BOX_COUNT equ 0xDA7F
W_BOX_MONS equ 0xDA95
W_BOX_MON1_MOVES equ 0xDA9D
W_BOX_MON_OT equ 0xDD29
W_BOX_MON_NICKS equ 0xDE05
W_BOX_MON_NICKS_END equ 0xDEE1
W_SAVED_LIST_SCROLL_OFFSET equ 0xD07D
W_LIST_COUNT equ 0xD129
; Sprite state data — 16 slots × $10 bytes each, two parallel arrays.
; wSpriteStateData1 ($C100) and wSpriteStateData2 ($C200) — sym-verified.
@@ -158,6 +226,23 @@ H_SPRITE_SCREEN_X equ 0xFF91 ; hSpriteScreenX
H_SPRITE_SCREEN_Y equ 0xFF92 ; hSpriteScreenY
H_TILE_PLAYER_STANDING_ON equ 0xFF93 ; hTilePlayerStandingOn
H_SPRITE_PRIORITY equ 0xFF94 ; hSpritePriority (BG-priority bit for current sprite)
H_PRODUCT equ 0xFF95 ; hProduct (4 bytes)
H_ITEM_PRICE equ 0xFF8B
H_MONEY equ 0xFF9F
H_EXPERIENCE equ 0xFF96 ; (3 bytes, overlaps hProduct+1)
H_MULTIPLICAND equ 0xFF96 ; hMultiplicand (3 bytes, overlaps hProduct+1)
H_MULTIPLIER equ 0xFF99 ; hMultiplier (1 byte)
H_DIVIDEND equ 0xFF95 ; hDividend (4 bytes, overlaps hProduct)
H_DIVISOR equ 0xFF99 ; hDivisor (1 byte, overlaps hMultiplier)
H_QUOTIENT equ 0xFF95 ; hQuotient (4 bytes, overlaps hProduct)
H_REMAINDER equ 0xFF99 ; hRemainder (1 byte, overlaps hMultiplier)
H_DIVIDE_BUFFER equ 0xFF9A
H_MONEY equ 0xFF9F
H_DIVIDE_BCD_DIVISOR equ 0xFFA2
H_DIVIDE_BCD_QUOTIENT equ 0xFFA2
H_DIVIDE_BCD_BUFFER equ 0xFFA5
H_RANDOM_ADD equ 0xFFD3
H_RANDOM_SUB equ 0xFFD4
H_CURRENT_SPRITE_OFFSET equ 0xFFDA ; hCurrentSpriteOffset (_UpdateSprites loop index)
H_PIKACHU_SPRITE_VRAM_OFFSET equ 0xFFFC ; hPikachuSpriteVRAMOffset (added to tile id $80+; 0 for now)
@@ -495,6 +580,11 @@ SCREEN_H equ 144 ; logical pixels
SCREEN_TILES_W equ 40 ; viewport width in tiles (= RENDER_W / TILE_WIDTH)
SCREEN_TILES_H equ 25 ; viewport height in tiles (= RENDER_H / TILE_HEIGHT)
; Player standing tile in wTileMap — derived from render_sprites +36/+96 shift and
; the Pallet Town view pointer (bg_scy=32, bg_scx=32 → sprite feet at bg_surface Y=143).
PLAYER_STANDING_ROW equ 17 ; wTileMap row containing player's feet
PLAYER_STANDING_COL equ 24 ; wTileMap col containing player's feet
; Native render dimensions (back buffer and VGA output)
RENDER_W equ 320 ; back buffer / VGA width in pixels
RENDER_H equ 200 ; back buffer / VGA height in pixels

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

View File

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

View File

@@ -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
; ---------------------------------------------------------------------------

View File

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