ASM → C validation progress

May 8, 2026 · View on GitHub

Tracks equivalence testing between original x86 ASM implementations and their C/C++ ports in LIB386/. Each row represents a function (or data table) that exists in both .ASM and .CPP form.

Legend: [ ] untested · [x] tested · [~] partial


3D/ — 3D math & projection (23 pairs)

StatusASM FileCPP FileFunction(s)DescriptionNotes
[x]3D/COPYMATF.ASM3D/COPYMATF.CPPCopyMatrixFCopy 3×3 rotation matrix (float)tests/3D/test_copymatf.cpp now covers identity-like, mixed-sign, finite edge-value, zero-matrix, and 200-round deterministic random matrix cases, comparing the full TYPE_MAT byte-for-byte and verifying the source matrix remains unchanged in both ASM and CPP. Important test-domain note: CopyMatrixF is validated on real float-matrix inputs, not arbitrary raw 48-byte patterns, because the ASM implementation copies via fld/fstp qword pairs rather than raw integer moves.
[x]3D/GETANG2D.ASM3D/GETANG2D.CPPGetAngleVector2D2D angle from (x, z) via tangent table binary searchtests/3D/test_getang2d.cpp now covers axis cases, all quadrants, exact and near-45-degree swap boundaries, small signed crossover pairs, large-magnitude inputs, and 200 deterministic LCG stress rounds against TanTab-driven ASM output.
[x]3D/GETANG3D.ASM3D/GETANG3D.CPPGetAngleVector3D3D angle from (x, y, z); writes X0, Y0tests/3D/test_getang3d.cpp now covers axis and diagonal vectors, vertical and near-vertical cases, sign/quadrant combinations, wrap-sensitive XZ-length cases, near-boundary cases, and 200 deterministic LCG stress rounds while comparing both the return value and written globals X0/Y0.
[x]3D/IMATSTDF.ASM3D/IMATSTDF.CPPInitMatrixStdFInit rotation matrix from Euler angles (α, β, γ)tests/3D/test_imatstdf.cpp now covers axis rotations, diagonal/mixed rotations, wraparound and negative angles, plus 200 deterministic LCG stress rounds, comparing the full TYPE_MAT byte-for-byte against ASM output.
[x]3D/IMATTRAF.ASM3D/IMATTRAF.CPPInitMatrixTransFSet matrix translation (TX, TY, TZ)tests/3D/test_imattraf.cpp now covers fixed translations, 200 deterministic LCG stress rounds, exact float-slot bit patterns for TX/TY/TZ, and preservation of non-translation fields in pre-filled matrices.
[x]3D/LIROT3DF.ASM3D/LIROT3DF.CPPLongInverseRotatePointFInverse-rotate 3D point; writes X0, Y0, Z0tests/3D/test_lirot3df.cpp now covers multiple fixed matrices, mixed-sign vectors, 200 deterministic LCG stress rounds, and verifies both written globals and that the input matrix remains unchanged in ASM and CPP.
[x]3D/LITLISTF.ASM3D/LITLISTF.CPPLightListCompute per-vertex lighting from normalstests/3D/test_litlistf.cpp now covers fixed and deterministic random light vectors, multiple fixed matrices, directional/backlit dot-product cases, factor-light extremes, variable list sizes up to 8 points, zero-count early return, full output-buffer equivalence, and X0/Y0/Z0 side effects. Fixed CPP parity bug: after inverse-rotating the light vector, LightList must store the scaled float bit patterns back into X0/Y0/Z0 to match ASM scratch-state behavior.
[x]3D/LPROJ3DF.ASM3D/LPROJ3DF.CPPLongProjectPoint3DScalar 3D perspective projection; writes Xp, Yptests/3D/test_lproj3df.cpp now covers multiple projection/camera states, exact clip-plane boundary behavior, clip rejection, camera-centred projection, sentinel Xp/Yp outputs on failure, and 200 deterministic LCG stress rounds. Batch list projection remains tracked separately under PRLI3DF.
[x]3D/LPROJISO.ASM3D/LPROJISO.CPPLongProjectPointIsoIsometric projection; writes Xp, Yptests/3D/test_lprojiso.cpp now covers multiple camera/centre states, arithmetic-shift boundary cases, camera-cancellation cases, return value plus Xp/Yp, and 200 deterministic LCG stress rounds.
[x]3D/LROT2DF.ASM3D/LROT2DF.CPPLongRotate, Rotate2D rotation by angle; writes X0, Z0tests/3D/test_lrot2df.cpp now covers zero, quarter-turn, wraparound, and negative-angle cases plus 200 deterministic LCG stress rounds while comparing X0/Z0 exactly.
[x]3D/LROT3DF.ASM3D/LROT3DF.CPPLongRotatePointF3D matrix × point; writes X0, Y0, Z0tests/3D/test_lrot3df.cpp now covers multiple fixed matrices, mixed-sign and large vectors, 200 deterministic LCG stress rounds, and verifies both written globals and that the input matrix remains unchanged in ASM and CPP.
[x]3D/MULMATF.ASM3D/MULMATF.CPPMulMatrixF3×3 matrix multiplication (float)tests/3D/test_mulmatf.cpp now uses strict full-TYPE_MAT byte-for-byte equivalence, covers identity/fixed/random matrix pairs plus non-zero input TX/TY/TZ cases, and verifies that both source matrices remain unchanged. Fixed CPP parity bug: destination TX/TY/TZ must be zeroed to match ASM.
[x]3D/PRLI3DF.ASM3D/PRLI3DF.CPPProjectList3DFBatch 3D projection with screen boundstests/3D/test_prli3df.cpp now covers multi-point batches, exact near-clip boundary handling, all-clipped and mixed visible/clipped batches, overflow sentinels, variable origins/projection states, zero-count early return, screen-bounds globals, and scratch globals Xp/Yp/X0/Y0/Z0. Fixed CPP parity bug: Xp/Yp must retain the raw projected offsets Xe/Ye, while centered coordinates are used only for bounds checks and stored output points.
[x]3D/PRLIISO.ASM3D/PRLIISO.CPPProjectListIsoBatch isometric projectiontests/3D/test_prliiso.cpp now covers multi-point batches, variable centres/origins, screen-bounds globals, explicit sort-key overwrite cases with non-zero Grp bytes, overflow sentinels, and in-place source mutation. Fixed CPP parity bugs: the projection math must use 32-bit intermediates like ASM, and the per-vertex sort value must be written back as a full 32-bit store over the source Z/Grp pair. Note: n == 0 is not a valid input domain for the ASM implementation and is intentionally not tested.
[x]3D/PROJ.ASM3D/PROJ.CPPSetProjection, SetIsoProjectionConfigure projection globals & func pointerstests/3D/test_proj.cpp now covers both setup functions with fixed and deterministic random cases, comparing XCentre, YCentre, NearClip, LFactorX, LFactorY, TypeProj, and exact FRatioX/FRatioY bit patterns.
[x]3D/REGLE3.ASM3D/REGLE3.CPPRegleTrois, BoundRegleTroisLinear interpolation (rule of three) ± boundstests/3D/test_regle3.cpp now covers exact clamp and extrapolation boundaries (step=-1/0/N-1/N/N+1), zero and negative NbStep, negative and out-of-range Step, large signed values, and 200 deterministic LCG stress rounds for both unclamped and clamped interpolation paths.
[x]3D/ROTMATU.ASM3D/ROTMATU.CPPRotateMatrixURotate matrix by Euler anglestests/3D/test_rotmatu.cpp now uses strict full-TYPE_MAT byte-for-byte equivalence, covers multiple source matrices including non-zero input TX/TY/TZ, wraparound/negative Euler angles, and verifies that the source matrix remains unchanged in ASM and CPP.
[x]3D/ROTRALIF.ASM3D/ROTRALIF.CPPRotTransListFBatch rotate + translate vertex listtests/3D/test_rotralif.cpp now uses deterministic fixed and random cases, covers identity, translation-only, asymmetric-scale, and near-singular matrices, verifies list lengths up to 16 points, and checks exact destination bytes while ensuring the source matrix and source vertex list remain unchanged in ASM and CPP.
[x]3D/ROTVECT.ASM3D/ROTVECT.CPPRotateVectorRotate vector by Euler angles; writes X0, Y0, Z0tests/3D/test_rotvect.cpp now uses deterministic fixed and random cases, covers negative and wraparound Euler angles plus large signed norms, and compares X0/Y0/Z0 exactly after each call.
[x]3D/SINTAB.ASM3D/SINTAB.CPPSinTab[], CosTab[]Pre-computed 16-bit sine/cosine (4096 entries)tests/3D/test_sintab.cpp compares all 4096 SinTab and CosTab entries exactly against the ASM tables.
[x]3D/SINTABF.ASM3D/SINTABF.CPPSinTabF[], CosTabF[]Pre-computed float sine/cosine (4096 entries)tests/3D/test_sintabf.cpp now compares the full SinTabF and CosTabF tables byte-for-byte against the ASM data.
[x]3D/SQRROOT.ASM3D/SQRROOT.CPPSqr, QSqrInteger square root (32-bit and 64-bit)tests/3D/test_sqrroot.cpp now includes a concrete failing QSqr reproducer plus deterministic 32-bit and 64-bit stress coverage. Fixed CPP parity bug: replaced sqrtl()-based QSqr with a direct portable port of the ASM bitwise algorithm, and Sqr now uses the same exact integer method.
[x]3D/TANTAB.ASM3D/TANTAB.CPPTanTab[]Pre-computed tangent table (512 entries)tests/3D/test_tantab.cpp compares all 513 tangent-table entries exactly against the ASM data.

SOURCES/ — core source helpers (6 pairs)

  • PLASMA.ASM -> PLASMA.CPP

    • Added tests/test_plasma.cpp covering Do_Plasma with fixed interleave/active-point cases, safe edge cases (Interleave=1/3, NbActivePoints=2), and deterministic random stress, comparing TabVirgule, the touched texture region, and exported globals (Nb_Pts_Inter, Nb_Pts_Control) strictly. Fixed CPP interpolation to match the ASM's zero-extended 16-bit control loads and add/adc/sar rounding. Note: NbActivePoints=1 is excluded because the ASM reads past the lone control point, and Interleave=0 is excluded because the ASM fill loop decrements from zero and walks off the buffer.
  • GRILLE_A.ASM -> GRILLE.CPP

    • Added tests/test_grille.cpp covering GetAdrBlock, Map2Screen, DecompColonne, WorldCodeBrick, GetBlockBrick, GetWorldColBrickVisible, and WorldColBrickFull with fixed in-memory cube/block fixtures plus deterministic random stress, comparing return values and touched globals (XMap, YMap, ZMap, XScreen, YScreen) byte-for-byte/strictly. Fixed CPP world-coordinate conversion to use the ASM's raw shift semantics and matched WorldColBrickFull's upward scan. Note: the WorldColBrickFull negative-y ASM path is excluded because that proc jumps to a shared pop ebx return path without a matching push and crashes before returning.
  • FUNC.ASM -> FUNC.CPP

    • Added tests/test_func.cpp covering SearchBoundColRGB with fixed exact/subrange/upper-bound cases, edge cases for single-entry and coulmin > coulmax, 64-entry random stress, plus 100-round dedicated 256-entry full-palette stress that exercises wider coulmin/coulmax spans and deeper search ranges while verifying the palette remains unchanged byte-for-byte.
  • FLOW_A.ASM -> FLOW_A.CPP

    • Added tests/test_flow_a.cpp covering BoxFlow, ShadeBoxBlk, and CopyBlockShade with fixed clipped/unclipped cases, custom clip-window boundary cases, repeated-application stability sequences, plus deterministic random stress, comparing the full destination buffers byte-for-byte.
  • FIRE.ASM -> FIRE.CPP

    • Added tests/test_fire.cpp covering Do_Fire with zero, all-0xFF, checkerboard, sequential, vertical-stripe, and edge-impulse inputs plus 300 deterministic random rounds, comparing both 32x36 work buffers, the 32x256 texture output, and the unchanged color table byte-for-byte after two consecutive calls from the same initial seed/state.
  • COMPRESS.ASM -> active CPP port lives in LZSS.CPP (COMPRESS.CPP is a commented legacy stub)

    • Added tests/test_compress.cpp covering AddString and DeleteString with fixed tree-shape cases, 300-round add stress, 150-round delete stress, and 80 deterministic mixed interleaved AddString/DeleteString sequences, comparing return values and full tree/window/global state after each operation.

3DEXT/ — scene extension helpers (2 pairs)

StatusASM FileCPP FileFunction(s)DescriptionNotes
[x]3DEXT/BOXZBUF.ASM3DEXT/BOXZBUF.CPPZBufBoxOverWrite2Re-apply terrain occlusion over object bounding boxASM ≡ CPP: return value plus post-call Log, Screen, and PtrZBuffer contents verified for visible, hidden, mixed, single-pixel, negative-depth, and 30 random rounds.
[x]3DEXT/LINERAIN.ASM3DEXT/LINERAIN.CPPLineRainRain line rendering helperASM ≡ CPP: return flags, screen bbox globals, full Log buffer, and no-Z-write PtrZBuffer contents verified for horizontal, diagonal fog, vertical occluded, plain single-pixel, dedicated background-intersection single-pixel, fully-clipped, and 300 random rounds.

ANIM/ — object animation (10 pairs)

StatusASM FileCPP FileFunction(s)DescriptionNotes
[x]ANIM/ANIM.ASMANIM/ANIM.CPPObjectInitAnimInitialize animation for 3D objecttests/ANIM/test_anim.cpp now verifies exact first-init state, same-anim promotion of pending NextBody/NextTexture into Body/Texture, and 100 deterministic random full-T_OBJ_3D ASM-vs-CPP comparisons. Fixed LIB386/ANIM/ANIM.CPP to mirror the ASM's up-front NextBody/NextTexture promotion before the same-anim early return.
[x]ANIM/BODY.ASMANIM/BODY.CPPObjectInitBodyInitialize body (visual model) for objecttests/ANIM/test_body.cpp now uses strict full-T_OBJ_3D ASM-vs-CPP memory comparisons over fixed and deterministic random initial states, covering first-body assignment, existing-body preservation, and status-bit updates.
[x]ANIM/CLEAR.ASMANIM/CLEAR.CPPObjectClearZero/sentinel-fill object structtests/ANIM/test_clear.cpp now uses strict full-T_OBJ_3D ASM-vs-CPP memory comparisons over multiple fill patterns and deterministic random initial states.
[x]ANIM/FRAME.ASMANIM/FRAME.CPPObjectSetFrameSet current animation frametests/ANIM/test_frame.cpp now checks all 3 frames, explicit reset of interpolation/timer state, and full T_OBJ_3D ASM-vs-CPP equivalence for non-zero preseeded state on frame 0, frame 1, and frame 2. Fixed LIB386/ANIM/FRAME.CPP to match the ASM's loop-back branch by sourcing NextTimer and Master from frame 0 when the requested frame is the last frame.
[x]ANIM/INTANIM.ASMANIM/INTANIM.CPPObjectSetInterAnimSet interpolated animation stateStrengthened tests/ANIM/test_intanim.cpp with exact CPP expectations for midpoint, no-change, frame-transition, and deterministic timer-sweep interpolation states (including exact CurrentFrame values and fixed-point interpolator outputs). ASM-vs-CPP parity now includes full T_OBJ_3D comparison for the no-change early-return path, while midpoint parity remains scoped to interpolator/status/CurrentFrame and frame-transition/full-state parity remains limited by the documented INTERDEP divergence.
[x]ANIM/INTERDEP.ASMANIM/INTERDEP.CPPObjectSetInterDepSet inter-frame dependenciestests/ANIM/test_interdep.cpp now covers midpoint, frame-advance, loop, and rotation paths via Watcom-to-C shims, with exact CPP assertions for midpoint interpolator value and frame/loop transition state, plus explicit no-rotation-state preservation checks for Alpha/Beta/Gamma and their last-step accumulators.
[x]ANIM/INTFRAME.ASMANIM/INTFRAME.CPPObjectSetInterFrameSet interpolated frame between keyframesASM ≡ CPP: exact rotate + translate interpolation now checked at 0%, 25%, 50%, 75%, and the 0xFFFF fixed-point endpoint, with deterministic 100-round ASM-vs-CPP stress across interpolator values and full T_OBJ_3D byte comparison.
[x]ANIM/LIBINIT.ASMANIM/LIBINIT.CPPInitObjects, ClearObjectsInitialize/clear animation library statetests/ANIM/test_libinit.cpp now compares the exported animation-library globals exactly between ASM and CPP for provided-buffer initialization, overwrite/reinit behavior, already-initialized null-buffer no-op behavior, and ClearObjects when no malloc buffer is active.
[x]ANIM/STOFRAME.ASMANIM/STOFRAME.CPPObjectStoreFrameStore animation frame stateASM ≡ CPP: fixed CPP to copy NbGroups*2-2 dwords (was NbGroups*2-1), and tests/ANIM/test_stoframe.cpp now verifies both the stored buffer bytes and the full post-call T_OBJ_3D state.
[x]ANIM/TEXTURE.ASMANIM/TEXTURE.CPPObjectInitTextureInitialize texture for objecttests/ANIM/test_texture.cpp now uses strict full-T_OBJ_3D ASM-vs-CPP memory comparisons over fixed and deterministic random initial states, covering first-texture assignment, existing-texture preservation, and next-texture overwrite behavior.

SVGA/ — screen drawing & sprites (15 pairs)

StatusASM FileCPP FileFunction(s)DescriptionNotes
[x]SVGA/AFFSTR.ASMSVGA/AFFSTR.CPPAffStringDisplay string at screen positionASM ≡ CPP: fixed single-glyph exact coverage (including the A glyph's 28-pixel footprint), pixel offset, line stride, TextPaper support, Font8x8 patches (chars 0xC6, 0xE4), and 100 deterministic random string/position/ink/paper rounds
[x]SVGA/BLITBOXF.ASMSVGA/BLITBOXF.CPPBlitBoxFFast blit rectangular region with transparencyImplemented the missing C++ port in LIB386/SVGA/BLITBOXF.CPP and added strict full-buffer ASM-vs-CPP tests in tests/SVGA/test_blitboxf.cpp, covering fixed cases plus 100 deterministic random source/destination contents. The CPP port must match the ASM’s x87 fld/fstp transfer semantics for each 8-byte chunk, not a raw byte memcpy.
[x]SVGA/BOX.ASMSVGA/BOX.CPPBoxDraw rectangle outline with clippingtests/SVGA/test_box.cpp now uses strict whole-framebuffer ASM-vs-CPP comparisons over fixed and deterministic random rectangles and clip windows.
[x]SVGA/CALCMASK.ASMSVGA/CALCMASK.CPPCalcGraphMskCalculate graphical maskASM ≡ CPP: synthetic brick exact-size/byte coverage plus 300-round random stress test
[x]SVGA/CLRBOXF.ASMSVGA/CLRBOXF.CPPClearBoxF, SetClearColorClear rectangle; set clear colortests/SVGA/test_clrboxf.cpp now checks exact ClearColor state and strict whole-framebuffer parity over fixed and deterministic random valid boxes.
[x]SVGA/COPYMASK.ASMSVGA/COPYMASK.CPPCopyMaskCopy masked regionASM ≡ CPP: synthetic bank + 100 deterministic random positions
[x]SVGA/CPYBLOCI.ASMSVGA/CPYBLOCI.CPPCopyBlockIncrustCopy block with incrustationtests/SVGA/test_cpybloci.cpp now performs strict whole-framebuffer ASM-vs-CPP comparison with source preservation checks across fixed and deterministic random clipping and transparency cases.
[x]SVGA/CPYBLOCK.ASMSVGA/CPYBLOCK.CPPCopyBlockFast memory block copytests/SVGA/test_cpyblock.cpp now performs strict whole-framebuffer ASM-vs-CPP comparison with source preservation checks across fixed and deterministic random clipping cases. Fixed the C++ left/top source clipping path in LIB386/SVGA/CPYBLOCK.CPP to preserve the destination offset delta like the ASM.
[x]SVGA/FONT.ASMSVGA/FONT.CPPSizeFont, CarFont, FontFont metrics and character renderingASM ≡ CPP: SizeFont tested with synthetic bank + 100 deterministic random strings
[x]SVGA/GRAPH.ASMSVGA/GRAPH.CPPAffGraph, GetBoxGraphGraphics drawing and box calculationtests/SVGA/test_graph.cpp now covers GetBoxGraph basic, positive-hotspot, negative-hotspot, and explicit 0x80-range signed-hotspot boundary cases, plus AffGraph framebuffer and ScreenX/Y bounds equivalence for unclipped solid/pattern draws, signed-hotspot interior placement, clip-window and screen-edge clipping, off-screen no-op cases, and 100 deterministic random banks.
[x]SVGA/MASK.ASMSVGA/MASK.CPPAffMaskDisplay mask/sprite with transparencyASM ≡ CPP: full-framebuffer + ScreenX/Y bounds equivalence over fixed solid/pattern/sparse banks, explicit high-bit hotspot-byte cases (including 0x80, 0x81, 0x90, 0xC0, 0xC8, and 0xFB..0xFF), screen-edge and clip-window clipping, off-screen no-op cases, and 100 deterministic random rounds via Watcom ABI wrapper. The fixed cases verify that AffMask keeps the ASM's raw-byte hotspot behavior, unlike GetBoxGraph which sign-extends its hotspot fields.
[x]SVGA/RESBLOCK.ASMSVGA/RESBLOCK.CPPRestoreBlockRestore saved screen regionASM ≡ CPP: roundtrip + full-screen + 100 deterministic random regions
[x]SVGA/SAVBLOCK.ASMSVGA/SAVBLOCK.CPPSaveBlockSave screen region to buffertests/SVGA/test_savblock.cpp now performs strict saved-buffer ASM-vs-CPP comparison with screen preservation checks across fixed and deterministic random rectangles.
[x]SVGA/SCALEBOX.ASMSVGA/SCALEBOX.CPPScaleBoxScale rectangular regionASM ≡ CPP: same-size exact-copy coverage (including the fixed 98-pixel 10x10 case), upscale, downscale, 1-pixel strip/single-pixel edge cases, bottom-right edge placement + 100 deterministic random sizes/positions
[x]SVGA/SCALESPI.ASMSVGA/SCALESPI.CPPScaleSpriteScale sprite with transparencyASM fix: added uses esi edi ebx (callee-saved regs were clobbered, causing crash). CPP fixes: ScreenXMax/ScreenYMax now match the ASM's exclusive upper-edge semantics, and hotspot bytes now use signed 8-bit semantics like the ASM movsx path. tests/SVGA/test_scalespi.cpp now replaces the placeholder/min-max-only checks with strict 1:1 framebuffer and ScreenX/Y bounds equivalence for minimal, hotspot, signed-negative-hotspot, clipped-edge, fully clipped, and 100 deterministic random cases across the broader visible/off-screen domain.
[x]SVGA/SCALESPT.ASMSVGA/SCALESPT.CPPScaleSpriteTranspScale sprite with transparency blend tableASM ≡ CPP: fixed synthetic bank + clipping/fallback edge cases + 100 random rounds comparing full framebuffer and ScreenXMin/Max, ScreenYMin/Max. ASM fix: added uses esi edi ebx (callee-saved regs were clobbered, causing crash). CPP fix: rewritten to match ASM fixed-point scaling, fallback-factor handling, and sentinel exit bounds.

SYSTEM/ — system utilities (4 pairs)

StatusASM FileCPP FileFunction(s)DescriptionNotes
[x]SYSTEM/CPU.ASMSYSTEM/CPU.CPPCPU globalsCPU detection and feature flagstests/SYSTEM/test_cpu.cpp now performs strict byte-for-byte ASM-vs-CPP comparison for the manufacturer string, signature, feature flags, and all exported TLB/cache globals. Fixed LIB386/SYSTEM/CPU.CPP to match the ASM data-segment initializers, including the default ProcessorSignature = 0x400.
[x]SYSTEM/FASTCPYF.ASMSYSTEM/FASTCPYF.CPPFastCopyFast memory copytests/SYSTEM/test_fastcpyf.cpp now performs strict whole-buffer ASM-vs-CPP comparison with source preservation checks across fixed and deterministic random sizes and alignments. Fixed LIB386/SYSTEM/FASTCPYF.CPP to emulate the ASM’s x87 qword transfer sequence instead of raw memcpy.
[x]SYSTEM/LZ.ASMSYSTEM/LZ.CPPExpandLZLZ decompressionExisting tests/SYSTEM/test_lz.cpp already provides ASM-vs-CPP coverage over literals, bounded output, and 400 deterministic randomized roundtrip cases for MinBloc 2 and 3.
[x]SYSTEM/MOUSEDAT.ASMSYSTEM/MOUSEDAT.CPPMouse dataMouse driver data structurestests/SYSTEM/test_mousedat.cpp now uses explicit fixed-byte sanity checks plus strict 541-byte ASM-vs-CPP comparison for BinGphMouse, replacing the old placeholder link/nonzero assertions.
StatusASM FileCPP FileFunction(s)DescriptionNotes
[x]MENU/SORT.ASMMENU/SORT.CPPMySortCompFuncComparison function for qsort (strcmp-based)Added the missing C++ implementation in LIB386/MENU/SORT.CPP and strict ASM-vs-CPP comparison coverage in tests/MENU/test_sort.cpp over fixed and deterministic random string pairs. tests/MENU/CMakeLists.txt now compiles SORT.CPP directly into the test target so the real CPP implementation is exercised.

pol_work/ — polygon rendering (14 pairs)

StatusASM FileCPP FileFunction(s)DescriptionNotes
[x]pol_work/POLY.ASMpol_work/POLY.CPPINV64, SetFog, SetCLUT, Switch_Fillers, Fill_Poly, Fill_PolyClip, Triangle_ReadNextEdge, SetScreenPitch, Calc_TextureZ*SlopeFPUCore polygon rendering pipelineASM equiv — 29 tests (13 CPP + 4 ASM-vs-CPP: INV64 300 rounds, SetFog 300 rounds, SetCLUT 300 rounds, Switch_Fillers 300 rounds; + Fill_PolyClip 300 rounds POLY_SOLID, Fill_Poly 300 rounds POLY_SOLID, Fill_Sphere 300 rounds, Line_A 300 rounds; + Fill_PolyClip POLY_TEXTURE_Z_FOG game-trace quad + 300 random rounds (nv=3 triangles + nv=4 quads, both framebuffer and Z-buffer byte-for-byte); + 6 TextureZ slope calculator unit tests: Calc_TextureZXSlopeFPU 300 rounds, Calc_TextureZGouraudXSlopeFPU 300 rounds, Calc_TextureZXSlopeZBufFPU 300 rounds, Calc_TextureZLeftSlopeFPU 300 rounds, Calc_TextureZGouraudLeftSlopeFPU 300 rounds, Calc_TextureZLeftSlopeZBufFPU 300 rounds — CPP regression tests against reference formula). The remaining weak CPP smoke cases were tightened: test_fill_poly_random_types() now asserts repeatable full-framebuffer output plus bounds invariants over deterministic rounds, representative SetFog(100,1000) and SetFog(0,65535) fixtures now assert exact Fill_ZBuffer_Factor/Fill_ScaledFogNear/Fill_Fog_Factor outputs, while invalid-domain helpers are explicit (INV64(1) and SetFog(0,0) stay CPP-only because the original ASM overflows/traps there). POLY_JMP.ASM + POLYTZF.ASM linked as ASM deps. ASM slope calculator symbols (all Calc_/Jmp_/Draw_Triangle/Read_Next_Right) renamed to prevent CPP shadowing. Fixed: Fill_Type made PUBLIC for test sharing; Jmp_XSlopeZBufFPU[24]/Jmp_LeftSlopeZBufFPU[24] corrected to use combined Calc_TextureZ*SlopeZBufFPU functions; ZBuf XSlope sign (MZB-MZC) corrected; test fix: save/restore pts[] before ASM path since CPP Fill_PolyClip modifies array in-place during quad decomposition. FIXED: EnterClip perspective-correct UV division / 256 changed to >> 8 (arithmetic right shift) to match ASM's shr eax,8; shl edx,24; or eax,edx bit extraction. C's / truncates toward zero while ASM's shift is floor-division; for negative products (MapU×W) the results differ by 1, causing clipped vertex MapU mismatch (e.g. UC=43116 CPP vs 43117 ASM). Proven by polyrec_0002 DC#3226 exact slope comparison test.
[x]pol_work/POLY_JMP.ASMpol_work/POLY_JMP.CPPAll 6 jump tables × 26 entries (Fill_N/Fog/ZBuf/FogZBuf/NZW/FogNZW_Table_Jumps)Polygon type dispatcherASM-vs-CPP equivalence: globals comparison (Fill_Filler, Fill_ClipFlag, Fill_Color, Fill_Trame_Parity, IsPolygonHidden) for all entries. 50 random rounds per entry. Fixed 3 CPP bugs: Jmp_SolidFogNZW missing Fill_Color, Jmp_TrameFogZBuf/NZW wrong palette index.
[x]pol_work/POLYCLIP.ASMpol_work/POLYCLIP.CPPClipperZZ-plane polygon clippingASM-vs-CPP equivalence (8 tests, 300 random rounds). The CPP-only all-behind front-end sanity case in tests/pol_work/test_polyclip.cpp now asserts the exact deterministic return value 0 instead of allowing either 0 or (U32)-1. Fixed CPP FPU rounding (fesetround FE_TOWARDZERO) to match ASM chop mode.
[x]pol_work/POLYDISC.ASMpol_work/POLYDISC.CPPFill_Sphere, Sph_Line_*Circle/disc polygon fillASM equiv — strengthened tests/pol_work/test_polydisc.cpp to replace placeholder no-crash coverage with strict framebuffer equivalence for zero-radius, transparent, fog, and a 100-case deterministic mixed-type CPP sweep, in addition to the existing centre/clipped cases and the 300-round ASM-vs-CPP random loop. The remaining basic front-end count checks now assert exact deterministic footprints for radius-15 (770 pixels), radius-1 (6 pixels), and the partially clipped edge case (846 pixels). Watcom register wrapper. Also tested via test_poly (Fill_Sphere 300 random rounds).
[x]pol_work/POLYFLAT.ASMpol_work/POLYFLAT.CPPFiller_Flat, Filler_Trame, Filler_Transparent, ... (10 fillers)Solid-color polygon fillASM equiv — strengthened tests/pol_work/test_polyflat.cpp so the Fill_Poly solid-path degenerate and random coverage no longer uses placeholder assertions: collinear triangles now assert an unchanged framebuffer, 100 deterministic random solid triangles now assert repeatable full-framebuffer output plus color/bounds invariants, and the basic Fill_Poly(POLY_SOLID) / Fill_Poly(POLY_TRAME) area sanity cases now assert exact deterministic 820-pixel and 745-pixel footprints respectively. Direct ASM-vs-CPP filler coverage remains in place for all 10 fillers. FIXED: Trame pair-loop used wrong carry check (diffX & 1 vs shifted-out bit) and wrong loop structure; Trame ZBuf/NZW also had incorrect Z-advance carry detection (zDec >> 31 vs overflow); TrameNZW was missing PtrZBuffer2/PtrLog2 init; Transparent blend used & 0xF0 mask but ASM uses full byte.
[x]pol_work/POLYGOUR.ASMpol_work/POLYGOUR.CPPFiller_Gouraud, Filler_Dither, ... (18 fillers)Gouraud + dither shadingASM equiv — strengthened tests/pol_work/test_polygour.cpp so the Fill_Poly clipped and random Gouraud/Dither paths no longer use placeholder assertions: they now assert repeatable full-framebuffer output and clipped bounding-box behavior over a 100-case deterministic sweep, the basic Fill_Poly(POLY_GOURAUD) / Fill_Poly(POLY_DITHER) triangle sanity cases now assert the exact deterministic 3640-pixel footprint, and the uniform-light Gouraud center sample now asserts the exact deterministic pixel value 11, while the existing ASM-vs-CPP filler coverage remains in place for all 18 fillers. FIXED: ROL8 needed shift &= 7 to match x86 rol al,cl; GouraudTableZBuf/GouraudNZW/DitherNZW/GouraudTableNZW used simplified loop that corrupted per-scanline gouraud accumulator and wrong Z-buffer comparison — rewritten to match ASM register pattern.
[x]pol_work/POLYGTEX.ASMpol_work/POLYGTEX.CPPFiller_TextureGouraud, Filler_TextureDither, Filler_TextureGouraudChromaKey, Filler_TextureDitherChromaKey, Filler_TextureGouraudZBuf, Filler_TextureGouraudChromaKeyZBuf, Filler_TextureGouraudNZW, Filler_TextureGouraudChromaKeyNZW (all 8 fillers)Texture mapping with Gouraud shadingASM equiv — strengthened tests/pol_work/test_polygtex.cpp so the Fill_Poly clipped and random textured Gouraud/Dither paths no longer use placeholder assertions: they now assert repeatable full-framebuffer output and clipped bounding-box behavior over deterministic rounds, and the basic Fill_Poly(POLY_TEXTURE_GOURAUD) / Fill_Poly(POLY_TEXTURE_DITHER) triangle sanity cases now assert the exact deterministic 3640-pixel and 2840-pixel footprints respectively, while the existing ASM-vs-CPP filler coverage remains in place for all 8 textured fillers. FIXED: Filler_TextureDitherChromaKey rewritten — was missing dither pattern, CLUT gouraud row, local UV copies, Fill_CurOffLine save. Filler_TextureGouraudChromaKeyNZW rewritten — was missing carry chain, correct z-test, CLUT gouraud row, local UV copies.
[x]pol_work/POLYLINE.ASMpol_work/POLYLINE.CPPLine, Line_A, Line_ZBuffer, Line_ZBuffer_NZWPolygon edge drawingASM equiv for Line() — 5 CPP + 4 ASM tests. Line_A also tested via test_poly (300 random rounds, non-zbuffer mode). The clipped CPP sanity case in tests/pol_work/test_polyline.cpp now asserts the exact visible clipped span for an off-screen-left horizontal line instead of relying on a no-crash comment. FIXED: snapshot bisect polyrec_0013 DC#4333 exposed a real ASM bug in the Line_ZBuffer_NZW vertical path. @@Do_Y now writes the fogged Color instead of the low byte of YPtrLog, and test_polyline contains an exact regression that asserts both the pixel value and ASM-vs-CPP framebuffer equivalence for the fogged vertical line case.
[x]pol_work/POLYTEXT.ASMpol_work/POLYTEXT.CPPFiller_Texture, Filler_TextureFlat, ... (18 fillers)Texture-mapped polygon fillASM equiv — 4 CPP + 41 ASM filler tests (all 18 variants: static + 300-round random stress). The basic Fill_Poly(POLY_TEXTURE) front-end triangle sanity case in tests/pol_work/test_polytext.cpp now asserts the exact deterministic 3640-pixel footprint for both the solid reference fill and the textured pass. All pass byte-for-byte. FIXED: S32→S64 multiply overflow in sub-pixel UV, inverted Z-buffer comparison in NZW/ChromaKeyZBuf variants, sub-pixel UV applied to outer accumulators instead of local copies, carry-propagating inner loops for 6 broken ZBuf/NZW functions.
[x]pol_work/POLYTEXZ.ASMpol_work/POLYTEXZ.CPPFiller_TextureZ, Fill_Init_Perspective, ... (18 fillers)Perspective-correct texture fillASM equiv — strengthened tests/pol_work/test_polytexz.cpp so the remaining narrow/wide Filler_TextureZ smoke cases now perform strict fixed-case ASM-vs-CPP framebuffer comparisons instead of placeholder assertions, in addition to the existing static + random byte-for-byte coverage across all 18 TextureZ fillers. FIXED: replaced double math with inline x87 asm for perspective; fixed sub-pixel correction (32×32→64 imul bit extraction), inner pixel loops (mapV_mapU register packing), Z-buffer dual-loop structure (@@LoopX/@@NoDisp), and NZW offset-based addressing across all 18 variants (Flat, ChromaKey, Fog, ZBuf, NZW combinations).
[x]pol_work/POLYTZF.ASMpol_work/POLYTZF.CPPFiller_TextureZFogSmooth, Filler_TextureZFogSmoothZBuf, Filler_TextureZFogSmoothNZWTextureZ + smooth fog variantsASM equiv — strengthened tests/pol_work/test_polytzf.cpp so the Fill_Poly basic and random POLY_TEXTURE_Z_FOG paths no longer use placeholder assertions: they now assert repeatable full-framebuffer output plus clipped bounding-box behavior, including a 100-case deterministic random sweep, while the existing filler-level ASM-vs-CPP coverage remains in place for all 3 smooth-fog variants. FIXED: rewrote all 3 CPP fillers from affine to perspective-correct with inline x87 asm, fog CLUT, ZBuf perspective correction, Z-buffer rotated comparison (ror8 + add/adc). Fixed sub-pixel correction accumulation bug. ZBuf/NZW tests verify both framebuffer and Z-buffer byte-for-byte. FIXED: W stack traversal order changed from LIFO to FIFO to match ASM hardware-stack pop order. FIXED: V texture coordinate extraction corrected from (uvCur >> 16) & 0xFF to (uvCur >> 24) & 0xFF to match ASM's shr eax,16; mov al,bh pattern that extracts V[15:8]. All tests pass including full pipeline tests (Fill_PolyClip POLY_TEXTURE_Z_FOG nv=3+nv=4 300 random rounds).
[x]pol_work/POLYTZG.ASMpol_work/POLYTZG.CPPFiller_TextureZGouraud, ... (6 fillers)TextureZ + Gouraud variantsASM equiv — all 6 fillers tested (12 tests: 6 static + 6 random×300). Fixed CPP: wrong Z-buffer globals, NZW missing perspective correction, zbuf row offset, Z comparison/write shift, sub-pixel factor, UV addressing (>>8 not >>16). POLYTEXZ.ASM linked as ASM dep for perspective functions.
[x]pol_work/TESTVUEF.ASMpol_work/TESTVUEF.CPPTestVuePolyBackface culling / polygon visibilityASM equiv — 5 CPP + 5 ASM tests. Uses STRIP_C_ADAPT + inline asm wrapper.

OBJECT/ — 3D object display (1 pair, multiple functions)

StatusASM FileCPP FileFunction(s)DescriptionNotes
[x]OBJECT/AFF_OBJ.ASMOBJECT/AFF_OBJ.CPPObjectDisplay, BodyDisplay, BodyDisplay_AlphaBeta, TestVisibleI, TestVisibleF, QuickSort, QuickSortInvFull 3D object rendering pipelinetests/OBJECT/test_aff_obj.cpp now passes with the original simple render fixture, explicit alias coverage for transparent/trame/dither/gouraud-table/dither-table triangles and quads, line and sphere entity coverage, textured solid-triangle, textured flat-triangle, textured gouraud-triangle, textured dither-triangle, textured chroma-key solid/flat/gouraud/dither triangles, textured solid-quad, textured flat-quad, textured gouraud-quad, textured dither-quad, textured chroma-key solid/flat/gouraud/dither quads, textured perspective-correct flat-triangle, textured perspective-correct solid-triangle, textured perspective-correct gouraud-triangle, textured perspective-correct dither-triangle, textured perspective-correct chroma-key solid/flat/gouraud/dither triangles, textured perspective-correct flat-quad, textured perspective-correct solid-quad, textured perspective-correct gouraud-quad, textured perspective-correct dither-quad, textured perspective-correct chroma-key solid/flat/gouraud/dither quads, and environment-mapped solid-triangle, solid-triangle with non-zero scale, gouraud-triangle, gouraud-triangle with non-zero scale, flat-triangle, flat-triangle with non-zero scale, flat-quad, flat-quad with non-zero scale, solid-quad, solid-quad with non-zero scale, gouraud-quad, and gouraud-quad with non-zero scale fixtures. Coverage now includes visible and clipped end-to-end cases for BodyDisplay, BodyDisplay_AlphaBeta, and ObjectDisplay, with the three base visible fixtures asserting the exact deterministic NonZeroPixels == 233 count, the dedicated transparent alias case asserting NonZeroPixels == 233, the dedicated trame alias case asserting NonZeroPixels == 111, the dedicated Line case asserting NonZeroPixels == 32, the dedicated Sphere and Sphere_Transp cases each asserting NonZeroPixels == 136, the dedicated dither, gouraud-table, and dither-table alias cases each asserting NonZeroPixels == 233, the dedicated textured flat, textured solid, and textured gouraud triangle cases each asserting NonZeroPixels == 514, the dedicated textured Z flat, textured Z solid, and textured Z gouraud triangle cases each asserting NonZeroPixels == 514, the dedicated textured quad flat, textured quad solid, and textured quad gouraud cases each asserting NonZeroPixels == 1058, the dedicated textured quad Z flat, textured quad Z solid, and textured quad Z gouraud cases each asserting NonZeroPixels == 1058, the dedicated env flat, env solid, env gouraud, env flat scaled, env solid scaled, and env gouraud scaled triangle cases each asserting NonZeroPixels == 514, the dedicated env quad flat, env quad flat scaled, env quad solid, env quad solid scaled, env quad gouraud, and env quad gouraud scaled cases each asserting NonZeroPixels == 1058, and the dedicated multigroup visible and multigroup translate cases each asserting NonZeroPixels == 1255, multigroup ObjectDisplay cases that exercise a second group through both rotation and TYPE_TRANSLATE paths with mixed flat, solid, gouraud, triangle, quad, line, and sphere dispatch, non-textured ObjectDisplay cases that explicitly drive the transparent, trame, dither, gouraud-table, and dither-table jump-table aliases for both triangles and quads, textured ObjectDisplay cases that drive the solid, flat, gouraud, dither, chroma-key solid/flat/gouraud/dither, quad-solid, quad-flat, quad-gouraud, quad-dither, quad chroma-key solid/flat/gouraud/dither, POLY_TEXTURE_Z_FLAT, POLY_TEXTURE_Z_SOLID, POLY_TEXTURE_Z_GOURAUD, POLY_TEXTURE_Z_DITHER, POLY_TEXTURE_Z chroma-key solid/flat/gouraud/dither, and POLY_TEXTURE_Z quad-flat, quad-solid, quad-gouraud, quad-dither, and quad chroma-key solid/flat/gouraud/dither paths with real UVs, a texture table, and texture filler setup, env-mapped ObjectDisplay cases that exercise the ASM env solid, env solid with non-zero scale, env gouraud, env gouraud with non-zero scale, env flat, env flat with non-zero scale, env quad flat, env quad flat with non-zero scale, env quad solid, env quad solid with non-zero scale, env quad gouraud, and env quad gouraud with non-zero scale branches end-to-end. The render fixtures compare return values, screen bounds, Obj_ListRotatedPoints, Obj_ListProjectedPoints, ListLights, ListSort, TabMat, ListFillPoly, PosXWr/Y/Z, and the rendered framebuffer. Root fixes required for parity were: InitMatrixTransF storing translation slots with float representation, ObjectDisplay placing face-light data at ListLights + NbPoints rather than ListLights + NbPoints * 2, restoring Triangle_TextureZ_Flat/Quad_TextureZ_Flat to populate perspective Pt_W values instead of zeroing them, the AFF_OBJ env handlers rotating per-vertex normals through the exact RotatePointNoMMX callback path while preserving the ASM-visible X0/Y0/Z0 side effect, matching the ASM env UV packer by preserving the full 32-bit u product before OR-ing in the packed high-word v component, fixing Line to compute Z-buffer values only in perspective mode, fixing Sphere/Sphere_Transp to match the ASM radius and Z-buffer formulas, and linking renamed ASM POLYLINE.ASM/POLYDISC.ASM deps into the test target so the ASM AFF_OBJ path keeps using the original register-ABI Line_A/Fill_Sphere handlers while the C++ path continues using the C++ implementations. The alias tests also require initializing the Gouraud CLUT for plain dither/table fillers even when no texture map is involved. Fully arbitrary unordered inputs are still invalid for the original ASM quicksort helpers because some pivot orders recurse on the same range, so unordered sort stress remains restricted to the ASM-valid domain.

Known discrepancies

1. AffString — Glyph rendering direction and pixel offset (FIXED)

Status: Fixed. CPP now matches ASM byte-for-byte. Test upgraded to [x].

Fixes applied to SVGA/AFFSTR.CPP:

  1. Pixel pointer offset: ptr now starts at ScreenPtr - SizeChar (writes backwards from the offset pointer), matching the ASM's [ebp+ecx] where ecx goes from -SizeChar to -1.
  2. Line stride: replaced hardcoded 640 with ModeDesiredX.
  3. TextPaper support: non-ink pixels now write TextPaper color when TextPaper != 0xFF (0xFF = transparent), matching the ASM's cmp ah, 0FFh.
  4. Font8x8 patches: chars 0xC6 and 0xE4 updated to match ASM's French accented character replacements (à and ò).

2. ObjectStoreFrame — Off-by-one in stored dword count (FIXED)

Status: Fixed. CPP changed from NbGroups * 2 - 1 to NbGroups * 2 - 2 to match ASM. Test upgraded to [x] with strict byte-exact comparison.

The ASM comment ; 2 DWORDs per group, no group 0 clarifies the intent: copy NbGroups - 1 complete groups (each group = 2 dwords). The formula NbGroups * 2 - 2 = (NbGroups - 1) * 2 is correct. The CPP had NbGroups * 2 - 1 which overcounted by 1 dword. The consumer (ObjectSetInterFrame) reads exactly NbGroups - 1 groups, so the extra dword was never read — but the buffer pointer advanced 4 bytes further per store, which could cause wrap-around differences over time.


3. ObjectSetInterDep / ObjectSetInterAnim — Function pointer ABI mismatch (FIXED)

Status: Fixed via Watcom-to-C ABI shim functions. Tests upgraded to [x].

InitMatrixStd and RotatePoint are global function-pointer variables (defined in 3D/IMATSTDF.CPP and 3D/LROT3DF.CPP). The ASM call [InitMatrixStd] correctly reads the pointer and jumps to the target. The problem was that the GCC-compiled target functions use C stack calling convention, while the ASM caller passes params in Watcom registers (edi/eax/ebx/ecx).

Fix: __attribute__((naked)) shim functions in the test that:

  1. Accept Watcom register params (no GCC prologue to clobber them)
  2. Push them onto the stack in C convention order
  3. Call the CPP implementation (c_InitMatrixStdF / c_LongRotatePointF)
  4. Clean up the stack and ret

The shims are installed via install_shims() only before ASM calls. CPP tests use the default function pointers (which point to CPP functions with C convention).


4. ScaleSprite — Callee-saved register clobbering (FIXED)

Status: Fixed in ASM. All 14 tests pass (including ASM-vs-CPP equivalence).

Root cause: ScaleSprite proc uses ebp only saved/restored ebp, but the function body clobbers esi, edi, and ebx — all callee-saved registers in the x86 cdecl calling convention. When called from GCC-compiled C code, the caller's register state was corrupted on return, causing a segfault.

Fix: Changed proc uses ebp to proc uses ebp esi edi ebx in SVGA/SCALESPI.ASM. This generates proper push/pop sequences in the prologue/epilogue to preserve all clobbered callee-saved registers.

CPP fix (prior): ScreenXMax/ScreenYMax changed from exclusive to inclusive values (end_x - 1 instead of end_x), matching the ASM output.

Note: ASM treats Hot_X/Hot_Y as signed bytes (via shl/sar sign extension), while CPP reads them as unsigned U8. For values 0–127 the results are identical; for 128–255 they differ. All current test data uses small positive values so this is not tested.