GNU Chess low-memory return-thunk review
=======================================

Reviewed generate_thunks.py and generated thunks.asm. Twenty independent
16-bit Unicorn execution cases pass in test_thunk_abi_independent.py. The
kernel hooks emulate the separately audited native16-bit MemLock/MemUnlock
register contract; they do not claim to execute the real kernel or prove
its resource-loader behavior.

Stack transparency
- Entry saves FLAGS, PUSHA, DS and ES: exactly22 bytes. The original far return
  address is therefore atSS:[BP+22]/[BP+24]. Arguments retain their original
  positions after all saved state is restored.
- The thunk replaces only the original return address. It jumps into the
  target without leaving another frame beneath the original C arguments.
- A target RETF leaves cdecl arguments for its caller; RETF N removes Pascal
  arguments itself. Fixed cleanup runs after either form and jumps to the
  saved original caller without changing the post-return SP.
- Tests cover RETF and RETF N for0/2/4/6/10/18/32 bytes, returned general and
  segment registers, and two arithmetic-FLAGS patterns.

Nested lifetimes
- Each successfully entered target owns one kernel resource lock and one
  context record. Context publication occurs only after MemLock succeeds.
- The fixed cleanup unlocks the completed callee after execution has left its
  movable segment. The caller's outer context remains locked while execution
  resumes inside that caller's movable resource.
- A nested same-resource test confirms lock counts2->1->0 and exact SP at
  inner and outer return. Full real-engine relocation/poison testing belongs
  to the separate engine execution harness.
- Lock failure does not increment context depth. Overflow is checked before
  array access or MemLock; cleanup underflow has a distinct fatal path.

Native1.2 API audit
- MemLock ordinal5 preserves BX/CX/DX/SI/DI/BP/DS/ES on resident and swap-in
  paths. It returns the usable segment inAX and status in carry.
- The supplied1.2 kernel does NOT reload discarded blocks through MemLock.
  Thus the release policy must be0x0a50: preloaded, movable, swapable,
  shared/read-only code; neither HF_DISCARDED nor HF_DISCARDABLE is set.
- MemUnlock ordinal6 decrements the handle's8-bit lock count and preserves
  all16-bit registers and FLAGS. CTX_LIMIT128 stays below lock-count wrap.
- Direct HANDLE relocations in the fixed thunks are valid: resource handles
  remain stable while the actual target segment is obtained afresh per lock.

Required build invariants
- Use the rebuilt8086/16-bit engine. These16-bit thunks do not make a386
  engine safe across the old kernel's16-bit-only context switch.
- Engine/native entry and return DS is its private DGROUP, as required by
  this compiled C ABI. Indirect jump/resume pointers use that DS. This is
  not a general thunk for arbitrary routines returning a different DS.
- Only the process's engine thread may manipulate its private context stack.
  New executable attributes0x9200 clear inherited multi-launch behavior.
- Native UI, compatibility runtime, raw-function-pointer target resource2
  and the thunks stay fixed. The29 movable resources26..54 have no incoming
  raw segment/far-pointer references and no unthunked calls in the candidate.
- Dgroup initialized length0x2c00 includes contexts/scratch, TPD_stackBot at
  offset20 matches0x2c00, and reserved stack0xc000 gives total0xf000<64KB.

Static candidate audit
62 resources;29 movable engine code blocks total83952 initialized bytes;
fixed thunk block3456 bytes;120 unique thunk entries. No raw code pointer or
unthunked reference to movable engine resources was found. Runtime tests must
still establish heap/swap behavior and application responsiveness in Ensemble.
