Representation packing: scope and bounds
=======================================
All changes are confined to slim-work/port1/engine8086/src/engine. Original
port1 engine sources and released geodes remain unchanged. Histories keep256
entries, caches keep all original entries and widths, gc_int remains32-bit,
evaluation/alpha-beta/move ordering and search depth are unchanged.

Changed source files (20):
attack.cpp attack.h pawn.cpp pawn.h board.h board.cpp eval.cpp hash.cpp
move_gen.cpp move_check.cpp move_evasion.cpp see.cpp vector.cpp vector.h
piece.cpp piece.h square.cpp square.h value.cpp value.h

The board-list type board_sq_t is unsigned byte, distinct from arithmetic
sq_t, which remains gc_int. All board-list pointer consumers were updated.
move_check's local gc_int pin[9] retains a separate gc_int pointer; it is not
interpreted as a byte list.

Bounds and exact unpadded storage savings
-----------------------------------------
Attack-private tables: count uint8[4][239], signed delta sint8[4][239][3],
10240->3824 bytes, save6416. Original add_attack explicitly stores at most
2 deltas; slot[count] contains a zero terminator, so3 slots retain every read.
Every legal pair of mailbox squares has delta -119..119, index0..238; all4096
square pairs are exhaustively checked. Original unused rows239..255 are not
queried. Target deltas are signed -119..119, so unsigned byte would be wrong.

Attack public tables: DeltaIncLine/All signed bytes (-17..17 and -33..33),
DeltaMask/IncMask unsigned bytes (0..255), save2256 bytes. PieceCode -1..3
becomes signed byte, save768 bytes. Public attack macros cast packed values
back to gc_int, retaining32-bit arithmetic at callers.

Pawn rank-mask tables (8x16) and four256-entry bit tables: unsigned bytes,
4608->1152, save3456 bytes. Masks/reverse are0..255, count0..8, first/last
ranks4..11. Mask initialization uses unchanged gc_int temporaries/1L shifts.
Mask consumers use bitwise operations bounded0..255; there is no packed-mask
multiplication or left shift with a potentially overflowing16-bit result.
BIT/FIRST/LAST/COUNT accessor macros explicitly restore gc_int result type.

Distance: -1..7 signed bytes, save717 bytes. SquareFrom64: unsigned68..187,
save192 bytes; SquareTo64: signed -1..63, save768 bytes. PieceTo12: signed
-1..11; PieceOrder: signed -1..5, save1536 bytes combined. Their lookup macros
cast to gc_int so caller expression promotion remains as before.

ValuePiece: signed16 storage -1..10000, save512 bytes. VALUE_PIECE explicitly
casts to gc_int BEFORE any caller arithmetic; scores, products, and search
windows therefore remain32-bit. No direct consumer bypasses this macro
except initialization and the exhaustive test.

Total unpadded lookup-table saving:16621 bytes. Resource paragraph placement
and the compiler's -zt512 far-data threshold can change actual geode layout;
these are not final linked fixed-RAM/file-size figures.

Board storage: native Watcom -0/-ml/-zp1 confirms4748->2705 bytes, save2043
per object. square[256] is unsigned piece/edge byte (0..255); pos[256] is
signed -1 sentinel or index0..15. Piece list has17 unsigned-byte slots/color:
at most16 nonpawn men including king plus terminator. Pawn list has9 slots:
at most8 pawns plus terminator. Existing board_init_list rejects larger
counts BEFORE writes and requires at most16 total men/color. Promotions do
not create additional men and remove a pawn before inserting a nonpawn;
undo restores the prior bounded list. No accepted-position capacity is cut.

piece_size/pawn_size are0..16/0..8 bytes; piece_nb0..32; number[12] uses one
byte/color-piece counter, max16; pawn_file[2][16] uses8-bit rank masks. turn
is0/1 (ColourNone2 also fits), flags0..15, ep_square/cap_sq zero or valid
mailbox square. ply_nb/sp and both evaluation totals remain32-bit. All three
64-bit hashes and stack[256] are unchanged. Game/Previous allocations round
to4 bytes:4748->2708, save2040 per allocated board, not2043.

16-bit promotion audit
----------------------
Watcom promotes byte operands to16-bit int. Public packed lookup macros
restore gc_int explicitly, most importantly signed16 ValuePiece. Packed
board fields are stored/compared or copied to gc_int locals/parameters.
The only direct combined count sums are at most32; pawn_file combinations
are AND/OR/XOR at most255. No direct packed-board multiplication/shift was
found. Material scoring first assigns every count into gc_int locals; hash
indices include gc_int piece_12*16, and sort history lookup uses the explicit
gc_int PIECE_TO_12 accessor. Thus no previously32-bit product becomes a
potentially overflowing16-bit product.

Tests and limits
-----------------
run_representation_tables.py builds original and revised table generators
with AddressSanitizer/UndefinedBehaviorSanitizer. Every initialized table
value and all4096 legal-square-pair indices are emitted canonically:9989
records with identical SHA2567f7939848fdd18b42f8d1461cd5d5d49e41991dc9ba1f4c0814ced9830f7a55a.
All stored-range/terminator assertions pass. LeakSanitizer is disabled because
the sandbox cannot enumerate /proc threads; ASan bounds and UBSan stay active.

run_native_packing_probe.py compiles actual16-bit Watcom layout probes for
both sources. native-packing-layout.json confirms board2705, history2048
bytes, gc_int4 bytes, signed sint8 and unsigned uint8=255. Host integer widths
are not used to infer the target's promotions or structure layout.

Whole-engine paired search/perft/rules and native16-bit execution are separate
integration tests owned by the coordinated comparison/runtime work. These
representation tests alone do not establish full native behavior or timings.
