diff --git a/ALPHA_1/REL410/BT/TESTNCLR.EGG b/ALPHA_1/REL410/BT/TESTNCLR.EGG new file mode 100644 index 0000000..24b27f4 --- /dev/null +++ b/ALPHA_1/REL410/BT/TESTNCLR.EGG @@ -0,0 +1,229 @@ +[mission] +adventure=BattleTech +map=arena1 +scenario=freeforall +time=night +weather=clear +temperature=27 +length=600 +[ordinals] +bitmap=Ordinal::BitMap::1 +bitmap=Ordinal::BitMap::2 +bitmap=Ordinal::BitMap::3 +bitmap=Ordinal::BitMap::4 +[Ordinal::BitMap::1] +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=000038000003C000001FF00000000F00 +bitmap=0000F8000003C000003FF80000000F00 +bitmap=0001F8000003C00000707C0000000F00 +bitmap=0001F8000003C00000603C0000000F00 +bitmap=00007801FC0FF00000003C3DF807FF00 +bitmap=00007803FE0FF00000003C3FFC0FFF00 +bitmap=00007803C703C00000003C3E3C1F0F00 +bitmap=000078078303C0000000783C1E1E0F00 +bitmap=000078078003C0000000783C1E1E0F00 +bitmap=00007807C003C0000000F03C1E1E0F00 +bitmap=00007807F003C0000001E03C1E1E0F00 +bitmap=00007803FC03C0000003C03C1E1E0F00 +bitmap=00007801FE03C0000007803C1E1E0F00 +bitmap=000078007F03C000000F003C1E1E0F00 +bitmap=000078001F03C000001E003C1E1E0F00 +bitmap=000078000F03C000003C003C1E1E0F00 +bitmap=000078060F03C0000078003C1E1E0F00 +bitmap=000078071E03E0000078003C1E1F1F00 +bitmap=00007803FE01F000007FFC3C1E0FFF00 +bitmap=00007801FC00F000007FFC3C1E07EF00 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +x=128 +y=32 +width=8 +[Ordinal::BitMap::2] +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=001FFF0000003C0000003C03C0780000 +bitmap=001FFF0000003C0000007C03C0780000 +bitmap=00000E0000003C000000FC03C0780000 +bitmap=00003C0000003C000001BC03C0780000 +bitmap=0000700F3E1FFC000003BC0FF07BF000 +bitmap=0001E00F7E3FFC0000073C0FF07FF800 +bitmap=0003800FFE7C3C0000063C03C07C7800 +bitmap=0007F80FFE783C00000C3C03C0783C00 +bitmap=0007FE0F80783C0000183C03C0783C00 +bitmap=00001E0F00783C0000383C03C0783C00 +bitmap=00000F0F00783C0000703C03C0783C00 +bitmap=00000F0F00783C00007FFF03C0783C00 +bitmap=00000F0F00783C00007FFF03C0783C00 +bitmap=00000F0F00783C0000003C03C0783C00 +bitmap=00000F0F00783C0000003C03C0783C00 +bitmap=00000F0F00783C0000003C03C0783C00 +bitmap=00180F0F00783C0000003C03C0783C00 +bitmap=001C1F0F007C7C0000003C03E0783C00 +bitmap=000FFE0F003FFC0000003C01F0783C00 +bitmap=0007FC0F001FBC0000003C00F0783C00 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +x=128 +y=32 +width=8 +[Ordinal::BitMap::3] +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=001FFF03C07800000000FC03C0780000 +bitmap=001FFF03C07800000003FC03C0780000 +bitmap=001E0003C07800000007C003C0780000 +bitmap=001E0003C0780000000F0003C0780000 +bitmap=001E000FF07BF000000F000FF07BF000 +bitmap=001E000FF07FF800001E000FF07FF800 +bitmap=001E0003C07C7800001E0003C07C7800 +bitmap=001FFC03C0783C00001EFC03C0783C00 +bitmap=001FFE03C0783C00001FFE03C0783C00 +bitmap=00001F03C0783C00001F1F03C0783C00 +bitmap=00000F03C0783C00001E0F03C0783C00 +bitmap=00000F03C0783C00001E0F03C0783C00 +bitmap=00000F03C0783C00001E0F03C0783C00 +bitmap=00000F03C0783C00001E0F03C0783C00 +bitmap=00000F03C0783C00001E0F03C0783C00 +bitmap=00000F03C0783C00001E0F03C0783C00 +bitmap=00180F03C0783C00001E0F03C0783C00 +bitmap=001C1F03E0783C00001F1F03E0783C00 +bitmap=000FFE01F0783C00000FFE01F0783C00 +bitmap=0007FC00F0783C000007FC00F0783C00 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +x=128 +y=32 +width=8 +[Ordinal::BitMap::4] +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=001FFF03C07800000007FC03C0780000 +bitmap=001FFF03C0780000000FFE03C0780000 +bitmap=00000F03C0780000001F1F03C0780000 +bitmap=00000F03C0780000001E0F03C0780000 +bitmap=00000F0FF07BF000001E0F0FF07BF000 +bitmap=00001F0FF07FF800001E0F0FF07FF800 +bitmap=00001E03C07C7800001E0F03C07C7800 +bitmap=00003E03C0783C00001E0F03C0783C00 +bitmap=00003C03C0783C00000F1E03C0783C00 +bitmap=00003C03C0783C000007FC03C0783C00 +bitmap=00007803C0783C000007FC03C0783C00 +bitmap=00007803C0783C00000F1E03C0783C00 +bitmap=00007803C0783C00001E0F03C0783C00 +bitmap=0000F003C0783C00001E0F03C0783C00 +bitmap=0000F003C0783C00001E0F03C0783C00 +bitmap=0000F003C0783C00001E0F03C0783C00 +bitmap=0000F003C0783C00001E0F03C0783C00 +bitmap=0000F003E0783C00000F1E03E0783C00 +bitmap=0000F001F0783C00000FFE01F0783C00 +bitmap=0000F000F0783C000007FC00F0783C00 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +x=128 +y=32 +width=8 +[pilots] +pilot=200.0.0.113 +[200.0.0.113] +hostType=0 +advancedDamage=1 +loadzones=1 +name=cyd +bitmapindex=1 +experience=veteran +badge=VGL +patch=Yellow +role=Role::Default +dropzone=one +vehicle=madcat +vehicleValue=0 +color=White +[largebitmap] +bitmap=BitMap::Large::cyd +[BitMap::Large::cyd] +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=000000000000000000003C0000000000 +bitmap=000000000000000000003C0000000000 +bitmap=000000000000000000003C0000000000 +bitmap=000000000000000000003C0000000000 +bitmap=000000000000FCF00E0FBC0000000000 +bitmap=000000000003FE781C1FFC0000000000 +bitmap=000000000007FE781C3C7C0000000000 +bitmap=00000000000F82783C3C3C0000000000 +bitmap=00000000001F003C38783C0000000000 +bitmap=00000000001E003C78783C0000000000 +bitmap=00000000001E003C70783C0000000000 +bitmap=00000000001E003E70783C0000000000 +bitmap=00000000001E001EE0783C0000000000 +bitmap=00000000001E001EE0783C0000000000 +bitmap=00000000001F001FE0783C0000000000 +bitmap=00000000000F020FC07C7C0000000000 +bitmap=000000000007FE0FC03FFC0000000000 +bitmap=000000000003FE0F803FFC0000000000 +bitmap=000000000000FC07800F3C0000000000 +bitmap=00000000000000070000000000000000 +bitmap=00000000000000070000000000000000 +bitmap=000000000000000F0000000000000000 +bitmap=000000000000000E0000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +x=128 +y=32 +width=8 +[smallbitmap] +bitmap=BitMap::Small::cyd +[BitMap::Small::cyd] +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000600000 +bitmap=00000000006000000000000000600000 +bitmap=000001EC33E000000000030666600000 +bitmap=000003066660000000000303C6600000 +bitmap=00000303C66000000000030186600000 +bitmap=000001E183E000000000000300000000 +bitmap=00000003000000000000000000000000 +x=64 +y=16 +width=4 +[Role::Default] +model=dfltrole +[Role::NoReturn] +model=noretun diff --git a/dpl3-revive/patha/vrview_gl.py b/dpl3-revive/patha/vrview_gl.py index ba70809..7e75dd4 100644 --- a/dpl3-revive/patha/vrview_gl.py +++ b/dpl3-revive/patha/vrview_gl.py @@ -103,6 +103,9 @@ uniform float u_opacity; // DITHER n -> screen-door uniform vec3 u_fogcol; uniform int u_fog_on; uniform vec2 u_fogrange; // near, far +uniform int u_pv; // IR / predator vision: texture-value ramp mode +uniform vec3 u_pvcol0; // ramp dark endpoint (low texel value) +uniform vec3 u_pvcol1; // ramp bright endpoint (high texel value) in vec3 v_base; in vec2 v_uv; in float v_fog; @@ -115,14 +118,20 @@ void main() { if (bay > u_opacity) discard; } vec3 rgb; + vec3 t = v_base; // texel value; defaults to shaded base if untextured if (u_has_tex == 1) { - vec3 t = texture(u_tex, v_uv + u_uvoff).rgb; + t = texture(u_tex, v_uv + u_uvoff).rgb; if (u_alpha_cut == 1 && (t.r + t.g + t.b) <= 24.0 / 255.0) discard; // near-black texel = transparent rgb = t * (v_base * 1.275); // software: texel * (base/200), base 0..255 } else { rgb = v_base; } + if (u_pv == 1) { + // Division texture-value ramp: texel brightness -> colour gradient + float val = clamp(dot(t, vec3(0.299, 0.587, 0.114)), 0.0, 1.0); + rgb = mix(u_pvcol0, u_pvcol1, val); + } // alpha only takes effect in the deferred blended pass (opaque pass // renders with BLEND disabled) float fogf = (u_fog_on == 1) @@ -189,36 +198,36 @@ void main() { // fullscreen triangle from gl_VertexID PRESENT_FS = """ #version 330 uniform sampler2D u_tex; -uniform int u_pv; // IR / predator-vision on (board.pvision) -uniform int u_pvmode; // 0 mono, 1 green, 2 amber, 3 heat-ramp +uniform int u_pv; // retained (set 0); pvision is now in the mesh pass +uniform int u_pvmode; in vec2 v_uv; out vec4 f_color; -vec3 heat(float t) { // black -> red -> yellow -> white heat ramp - vec3 c = mix(vec3(0.0), vec3(1.0, 0.0, 0.0), clamp(t / 0.40, 0.0, 1.0)); - c = mix(c, vec3(1.0, 1.0, 0.0), clamp((t - 0.40) / 0.40, 0.0, 1.0)); - c = mix(c, vec3(1.0), clamp((t - 0.80) / 0.20, 0.0, 1.0)); - return c; -} void main() { vec3 c = texture(u_tex, v_uv).rgb; - if (u_pv == 1) { // thermal false-colour from scene luminance - float lum = dot(c, vec3(0.299, 0.587, 0.114)); - if (u_pvmode == 0) c = vec3(lum); - else if (u_pvmode == 1) c = vec3(lum * 0.15, lum, lum * 0.15); - else if (u_pvmode == 2) c = vec3(lum, lum * 0.72, lum * 0.18); - else c = heat(lum); - } f_color = vec4(pow(c, vec3(1.0 / 1.25)), 1.0); // Division DAC gamma } """ -# IR / "predator vision" thermal view. The game toggles it via dpl_Effect on -# action 0x1b (mode -1 ON / -2 OFF) -> board.pvision; VRVIEW_PVISION=1 or the -# bridge 'v' key forces it on for evaluation without pressing the cockpit IR -# button. Palette: VRVIEW_PVISION_PALETTE = mono|green|amber|heat (default -# heat/Predator-style, operator's interim pick 2026-07-14; exact original TBD). +# IR / "predator vision". The game toggles it via dpl_Effect on action 0x1b +# (mode -1 ON / -2 OFF) -> board.pvision; VRVIEW_PVISION=1 or the bridge 'v' key +# forces it on for evaluation without the cockpit IR button. +# +# ANSWER (firmware-decompiled + confirmed by the original team, 2026-07-14): it +# is the Division board's TEXTURE-VALUE RAMP mode -- a "check your texture maps" +# diagnostic the devs hijacked. Each textured surface renders as +# lerp(color0, color1, luminance(texel)): the texel's *value* drives a colour +# ramp. Multicolour because each material picks one of FOUR ramps (VR_DRAW.C +# 704-707). ramp_entry isn't plumbed through vrboard yet, so we assign a ramp +# per geometry (stable hash) to reproduce the multicolour look; swap to the real +# per-material index when material-ramp parsing lands. See +# emulator/firmware-decomp/README.md. PVISION = [os.environ.get('VRVIEW_PVISION', '0') != '0'] -_PV_MODES = {'mono': 0, 'green': 1, 'amber': 2, 'heat': 3} +# color0 = dark endpoint (low texel value), color1 = bright endpoint (high). +PV_RAMP0 = ((0.0, 0.0, 0.0), (0.3, 0.0, 0.0), (0.0, 0.5, 0.0), (0.0, 0.0, 0.4)) +PV_RAMP1 = ((1.0, 1.0, 1.0), (1.0, 1.0, 0.9), (1.0, 1.0, 1.0), (0.9, 0.9, 1.0)) +# VRVIEW_PVISION_RAMP=0..3 forces a single ramp for all surfaces (A/B testing); +# unset = per-geometry ramp (multicolour, the authentic look). +_PV_FORCE = os.environ.get('VRVIEW_PVISION_RAMP') HUD_VS = """ @@ -578,11 +587,10 @@ class GLRenderer(vrview.Renderer): ctx.disable(mgl.DEPTH_TEST) self._fbo_tex.use(0) self._set(self._present_prog, 'u_tex', 0) - self._set(self._present_prog, 'u_pv', - 1 if (PVISION[0] or getattr(board, 'pvision', False)) else 0) - self._set(self._present_prog, 'u_pvmode', - _PV_MODES.get(os.environ.get('VRVIEW_PVISION_PALETTE', - 'heat'), 3)) + # pvision moved to the mesh pass (texture-value ramp); present is a + # plain gamma present now. + self._set(self._present_prog, 'u_pv', 0) + self._set(self._present_prog, 'u_pvmode', 0) self._present_vao.render(mgl.TRIANGLES, vertices=3) pg.display.flip() cam = self.cam_matrix(board) @@ -661,6 +669,15 @@ class GLRenderer(vrview.Renderer): self._set(prog, 'u_has_nrm', 1 if mesh['nrm'] is not None else 0) self._set(prog, 'u_has_col', 1 if mesh['col'] is not None else 0) + # IR / predator vision: texture-value ramp. ramp_entry isn't plumbed, so + # pick a stable ramp per geometry (or a forced one) for the multicolour look. + pv_on = 1 if (PVISION[0] or getattr(board, 'pvision', False)) else 0 + self._set(prog, 'u_pv', pv_on) + if pv_on: + ri = int(_PV_FORCE) & 3 if _PV_FORCE is not None else hash(gh) & 3 + self._set(prog, 'u_pvcol0', PV_RAMP0[ri]) + self._set(prog, 'u_pvcol1', PV_RAMP1[ri]) + tex = c.geom_tex.get(gh) has_tex = tex is not None and mesh['uv'] is not None self._set(prog, 'u_has_tex', 1 if has_tex else 0) diff --git a/emulator/firmware-decomp/FIRMWARE-SYMBOLS.txt b/emulator/firmware-decomp/FIRMWARE-SYMBOLS.txt new file mode 100644 index 0000000..53821f1 --- /dev/null +++ b/emulator/firmware-decomp/FIRMWARE-SYMBOLS.txt @@ -0,0 +1,825 @@ + +=== CONTROL.O magic=0x014d nscns=3 nsyms=8 === + sect .text vaddr=0x00000000 size=0x000010 reloc=0 + sect .data vaddr=0x00000010 size=0x000000 reloc=0 + sect .bss vaddr=0x00000010 size=0x000000 reloc=0 + -- 2 defined EXT/STAT symbols: + 0x00000000 EXT _getFsr + 0x00000008 EXT _setFsr + +=== OPTFLOAT.O magic=0x014d nscns=3 nsyms=51 === + sect .text vaddr=0x00000000 size=0x000d20 reloc=24 + sect .data vaddr=0x00000d20 size=0x000060 reloc=0 + sect .bss vaddr=0x00000d80 size=0x000080 reloc=0 + -- 31 defined EXT/STAT symbols: + 0x00000000 EXT _fn_bbox_plane + 0x00000238 EXT _p_memcpy + 0x00000270 EXT _matrix_copy + 0x000002d8 EXT _fn_cache_mtx + 0x00000320 EXT _fn_cache_opt_mtx + 0x00000358 EXT _fn_cache_mtxd + 0x00000380 EXT _fn_cache_opt_mtxd + 0x000003a8 EXT _fn_xform + 0x00000458 EXT _fn_optxform + 0x000004c8 EXT _fn_xform_bound + 0x00000540 EXT _fn_xform_boundtroid + 0x000005c0 EXT _fn_concatenate + 0x00000660 EXT _quick_renorm + 0x00000698 EXT _quick_radius + 0x000006b8 EXT _quick_root + 0x000006c8 EXT _quick_recip + 0x000006d0 EXT _copy_spoint + 0x00000700 EXT _smintersect + 0x000007d8 EXT _dnc_Wait_count + 0x000007dc EXT dnc_Wait_count1 + 0x00000818 EXT _bla + 0x00000824 EXT bla_bla + 0x00000838 EXT _xform_and_project_fn + 0x00000b48 EXT _solve_fn + 0x00000c30 EXT _wait_locked_nonzero + 0x00000c34 EXT wait_locked_nonzero1 + 0x00000c70 EXT _i860_locked_read + 0x00000c88 EXT _manual_flush + 0x00000cdc EXT RESTORE_DIRBASE + 0x00000d00 EXT D_FLUSH + 0x00000d0c EXT D_FLUSH_LOOP + +=== TRISTRIP.O magic=0x014d nscns=3 nsyms=12 === + sect .text vaddr=0x00000000 size=0x000590 reloc=2 + sect .data vaddr=0x00000590 size=0x000000 reloc=0 + sect .bss vaddr=0x00000590 size=0x000000 reloc=0 + -- 4 defined EXT/STAT symbols: + 0x00000000 EXT _reg_dump + 0x00000118 EXT _trapezoid + 0x000001b8 EXT _trapezoom + 0x00000230 EXT _astristrip + +=== XFLGHTPR.O magic=0x014d nscns=3 nsyms=31 === + sect .text vaddr=0x00000000 size=0x0003d0 reloc=24 + sect .data vaddr=0x000003d0 size=0x000110 reloc=0 + sect .bss vaddr=0x000004e0 size=0x000000 reloc=0 + -- 8 defined EXT/STAT symbols: + 0x00000000 EXT _fn_xform_light_project + 0x00000000 EXT _ambientR + 0x00000004 EXT _ambientG + 0x00000008 EXT _ambientB + 0x00000010 EXT _quad_f_matrix + 0x00000090 EXT _quad_b_matrix + 0x00000360 EXT _fn_inv + 0x00000398 EXT _sub_pxl_correct + +=== ZBUF32.O magic=0x014d nscns=3 nsyms=24 === + sect .text vaddr=0x00000000 size=0x0007b0 reloc=0 + sect .data vaddr=0x000007b0 size=0x000080 reloc=0 + sect .bss vaddr=0x00000830 size=0x000000 reloc=0 + -- 17 defined EXT/STAT symbols: + 0x00000000 EXT slope_table + 0x00000000 EXT _scaninit + 0x00000040 EXT _texinit + 0x000000a8 EXT _ascaninit + 0x000000e8 EXT _atexinit + 0x00000128 EXT _texinit256 + 0x00000178 EXT _scanline + 0x000002d0 EXT _flatline + 0x000003e0 EXT _ftexline + 0x000005a0 EXT _ftexline256 + 0x00000710 EXT _setpixelsize + 0x0000072c EXT _getpixelsize + 0x0000073c EXT _setdirectmode + 0x00000754 EXT _clear_store + 0x00000774 STAT init_loop + 0x00000784 EXT _thrash + 0x00000794 STAT thrash_loop + +=== BILLBOAR.O magic=0x014d nscns=3 nsyms=22 === + sect .text vaddr=0x00000000 size=0x0001d0 reloc=31 + sect .data vaddr=0x000001d0 size=0x000020 reloc=0 + sect .bss vaddr=0x000001f0 size=0x000000 reloc=0 + -- 4 defined EXT/STAT symbols: + 0x00000000 STAT __atan2deg + 0x0000001c EXT _billboard_rotY + 0x00000090 EXT _billboardize + 0x00000120 EXT _billboardstuff + +=== DNC.O magic=0x014d nscns=3 nsyms=60 === + sect .text vaddr=0x00000000 size=0x001cd0 reloc=494 + sect .data vaddr=0x00001cd0 size=0x000010 reloc=0 + sect .bss vaddr=0x00001ce0 size=0x000020 reloc=0 + -- 36 defined EXT/STAT symbols: + 0x00000000 EXT _dN_send + 0x00000000 EXT _uncached_input_block + 0x00000004 EXT _uncached_output_block + 0x00000008 EXT _uncached_general_block + 0x0000000c STAT _peek_addr + 0x000000f8 EXT _dN_receive + 0x00000228 EXT _dN_mynode + 0x000002d8 EXT _dN_nodes + 0x00000388 EXT _dN_poll + 0x00000438 EXT _dN_timer + 0x000004f0 EXT _dN_wait + 0x00000580 EXT _dN_wait_ack + 0x000005c8 EXT _dN_ld_run + 0x00000668 EXT _dN_ld_out_noflush + 0x00000740 EXT _dN_ld_out + 0x00000818 EXT _dN_ld_ccb_out + 0x00000920 EXT _dN_ld_ccb_in + 0x000009f8 EXT _dN_ld_in + 0x00000ad0 EXT _dN_ld_out_2D + 0x00000bc8 EXT _dN_ld_in_2D + 0x00000cc0 EXT _dN_ld_stat + 0x00000d70 STAT _stat + 0x00000da0 EXT _spin + 0x00000df0 EXT _dN_pxpl5_readpixels + 0x00000ee8 EXT _dN_pxpl5_init + 0x00000fc0 EXT _dN_pxpl5_dma_done + 0x00001010 EXT _dN_pxpl5_dma + 0x00001150 EXT _dN_pxpl5_texels + 0x000012f0 EXT _dN_pxpl5_texture24 + 0x000013e8 EXT _dN_pxpl5_fx_texels + 0x00001588 EXT _dN_pxpl5_fx_texture24 + 0x00001658 STAT _pxpl5_texels_8 + 0x00001750 STAT _luminize + 0x000017e0 EXT _dN_pxpl5_texture8 + 0x00001ab8 EXT _dN_pxpl5_microwords + 0x00001ba8 EXT _dN_pxpl5_microcode + +=== I860SEM.O magic=0x014d nscns=3 nsyms=18 === + sect .text vaddr=0x00000000 size=0x0001c0 reloc=15 + sect .data vaddr=0x000001c0 size=0x000030 reloc=0 + sect .bss vaddr=0x000001f0 size=0x000000 reloc=0 + -- 6 defined EXT/STAT symbols: + 0x00000000 EXT _SemWait + 0x00000020 STAT ___freeSem + 0x00000024 STAT ___sems_free + 0x000000d0 EXT _SemSignal + 0x000000e8 EXT _InitSem + 0x00000118 EXT _nextSem + +=== MATERIAL.O magic=0x014d nscns=3 nsyms=35 === + sect .text vaddr=0x00000000 size=0x0004c0 reloc=85 + sect .data vaddr=0x000004c0 size=0x000070 reloc=0 + sect .bss vaddr=0x00000530 size=0x000000 reloc=0 + -- 11 defined EXT/STAT symbols: + 0x00000000 EXT _add_material + 0x00000040 EXT _create_material + 0x00000054 STAT _material_head + 0x00000058 STAT _material_tail + 0x0000005c STAT _texture_head + 0x00000060 STAT _texture_tail + 0x00000064 EXT _scene_resolved + 0x000000e8 EXT _delete_material + 0x00000180 EXT _add_texture + 0x000001c0 EXT _trace_materials + 0x00000278 EXT _resolve_scene + +=== NAMES.O magic=0x014d nscns=3 nsyms=34 === + sect .text vaddr=0x00000000 size=0x000570 reloc=85 + sect .data vaddr=0x00000570 size=0x000110 reloc=0 + sect .bss vaddr=0x00000680 size=0x000800 reloc=0 + -- 10 defined EXT/STAT symbols: + 0x00000000 EXT _checkNull + 0x00000048 EXT _initNames + 0x00000090 STAT _addName + 0x00000100 STAT _init + 0x00000104 EXT _allocatedNames + 0x000001a0 EXT _newName + 0x00000208 EXT _deleteName + 0x00000350 EXT _nameToAddress + 0x00000468 EXT _addressToName + 0x00000540 EXT _check_name + +=== PAZPL5.O magic=0x014d nscns=3 nsyms=30 === + sect .text vaddr=0x00000000 size=0x0001a0 reloc=70 + sect .data vaddr=0x000001a0 size=0x0000b0 reloc=0 + sect .bss vaddr=0x00000250 size=0x000000 reloc=0 + -- 1 defined EXT/STAT symbols: + 0x00000000 EXT _main + +=== PAZREAD.O magic=0x014d nscns=3 nsyms=86 === + sect .text vaddr=0x00000000 size=0x001a90 reloc=369 + sect .data vaddr=0x00001a90 size=0x000330 reloc=3 + sect .bss vaddr=0x00001dc0 size=0x0000e0 reloc=0 + -- 18 defined EXT/STAT symbols: + 0x00000000 STAT _mexico_68 + 0x00000108 STAT _set_surface + 0x00000160 STAT _planar + 0x00000270 EXT _matrix_dump + 0x00000314 EXT _open_fn + 0x00000318 EXT _gets_fn + 0x0000031c EXT _close_fn + 0x00000320 STAT _patches + 0x00000324 STAT _tesselations + 0x00000328 STAT _triangles + 0x0000032c STAT _points + 0x00000330 EXT _point_dump + 0x00000380 EXT _bound_dump + 0x000003e8 EXT _point_dump4 + 0x00000440 STAT _fix_vstrip_texture_coords + 0x00000450 EXT __consolidate + 0x000005a0 EXT _reconnectLODs + 0x00000798 EXT _bin_read + +=== PAZSTORE.O magic=0x014d nscns=3 nsyms=67 === + sect .text vaddr=0x00000000 size=0x0010d0 reloc=94 + sect .data vaddr=0x000010d0 size=0x000120 reloc=0 + sect .bss vaddr=0x000011f0 size=0x000050 reloc=0 + -- 33 defined EXT/STAT symbols: + 0x00000000 EXT _checkNull + 0x00000048 STAT _vertex_max + 0x00000098 STAT _vertex_min + 0x000000e8 EXT _build_bound + 0x000000f8 EXT _left + 0x000000fc EXT _right + 0x00000100 EXT _render_strip + 0x00000104 EXT _output_device + 0x00000108 EXT _link_A + 0x0000010c EXT _link_B + 0x00000110 EXT _raster + 0x00000114 EXT _flip + 0x00000258 EXT _bound_patch + 0x00000410 STAT _sphere_max + 0x00000470 STAT _sphere_min + 0x000004d0 EXT _bound_spatch + 0x00000680 EXT _reboundLOD + 0x00000808 EXT __BoundLOD + 0x000009a0 EXT _reboundObject + 0x00000ae0 EXT __BoundObject + 0x00000c38 EXT __InitVertex + 0x00000c78 EXT __InitVstrip + 0x00000cc0 EXT __InitPatch + 0x00000d10 EXT __InitObject + 0x00000d58 EXT __InitLOD + 0x00000da0 EXT __InitLight + 0x00000de0 EXT __InitView + 0x00000e58 EXT __InitInstance + 0x00000ee0 EXT __InitScene + 0x00000f28 EXT __LinkVertices + 0x00000f68 EXT _findPAZitem + 0x00000fa0 EXT _addPAZitem + 0x00001038 EXT _removePAZitem + +=== EOF.O magic=0x014d nscns=3 nsyms=64 === + sect .text vaddr=0x00000000 size=0x003ac0 reloc=149 + sect .data vaddr=0x00003ac0 size=0x0000f0 reloc=1 + sect .bss vaddr=0x00003bb0 size=0x0000c0 reloc=0 + -- 35 defined EXT/STAT symbols: + 0x00000000 EXT _tblcpy + 0x00000050 EXT _configEMCs_hi + 0x000000ac EXT _pxpl5_ticks + 0x000000b0 EXT _random_index + 0x000000b4 EXT _texture_table_iptr + 0x000000b8 STAT _send_pass + 0x000000bc EXT _eof_backR + 0x000000c0 EXT _eof_backG + 0x000000c4 EXT _eof_backB + 0x000000c8 EXT _eof_farZ + 0x000000cc EXT _eof_doFOG + 0x000000d0 EXT _eof_FOG_rval + 0x000000d4 EXT _eof_FOG_gval + 0x000000d8 EXT _eof_FOG_bval + 0x000000dc EXT _eof_FOG_near + 0x000000e0 EXT _eof_FOG_far + 0x000000e4 EXT _eof_Z_near + 0x000000e8 EXT _eof_Z_far + 0x00000250 EXT _configEMCs + 0x00000378 EXT _init_screenbin + 0x000007a0 STAT _linterp + 0x00000b78 STAT _multuu_unc + 0x00001070 STAT _send_em + 0x00001190 STAT _texdivide + 0x00001360 EXT _perspective_divides + 0x00001420 EXT _transpective_divides + 0x000014e0 EXT _explode4 + 0x000015a8 STAT _end_of_texture + 0x00002340 STAT _short_end_of_frame + 0x00002e50 EXT _short_end_of_frame_DMA + 0x00003048 EXT _end_of_textr_DMA + 0x000030f8 STAT _end_of_transptex + 0x000033b0 STAT _end_of_transframe + 0x00003840 EXT _end_of_transframe_DMA + 0x00003a10 EXT _end_of_transp_DMA + +=== PSTART.O magic=0x014d nscns=3 nsyms=13 === + sect .text vaddr=0x00000000 size=0x0000c0 reloc=7 + sect .data vaddr=0x000000c0 size=0x000000 reloc=0 + sect .bss vaddr=0x000000c0 size=0x000000 reloc=0 + -- 2 defined EXT/STAT symbols: + 0x00000000 EXT _start + 0x00000000 EXT pstart + +=== PXPL5OPT.O magic=0x014d nscns=3 nsyms=89 === + sect .text vaddr=0x00000000 size=0x0010a0 reloc=11 + sect .data vaddr=0x000010a0 size=0x000160 reloc=0 + sect .bss vaddr=0x00001200 size=0x000000 reloc=0 + -- 62 defined EXT/STAT symbols: + 0x00000000 EXT _preplanarize_fn + 0x0000002c EXT _fx2gtfx1 + 0x00000038 EXT _fx1gtfx2 + 0x00000040 EXT _checkx3 + 0x00000054 EXT _fx3min + 0x00000060 EXT _fx3max + 0x00000064 EXT _minimaxy + 0x00000068 EXT _minimaxydb + 0x0000006c EXT _fy2gtfy1 + 0x00000078 EXT _fy1gtfy2 + 0x00000080 EXT _checky3 + 0x0000009c EXT _fy3min + 0x000000a8 EXT _fy3max + 0x000000ac EXT _endminimax + 0x000000b0 EXT _endminimaxdb + 0x000000f8 EXT _preplanarize_fn_p + 0x0000011c EXT _fx2gtfx1_p + 0x00000128 EXT _fx1gtfx2_p + 0x00000130 EXT _checkx3_p + 0x00000144 EXT _fx3min_p + 0x00000150 EXT _fx3max_p + 0x00000154 EXT _minimaxy_p + 0x00000158 EXT _minimaxydb_p + 0x00000160 EXT _fy2gtfy1_p + 0x0000016c EXT _fy1gtfy2_p + 0x00000174 EXT _checky3_p + 0x00000194 EXT _fy3min_p + 0x000001a0 EXT _fy3max_p + 0x000001a4 EXT _endminimax_p + 0x000001a8 EXT _endminimaxdb_p + 0x000001f0 EXT _edgize_tri_fn + 0x00000278 EXT _edgize_tri_fn_p + 0x00000318 EXT _edgize_quad_fn + 0x00000320 EXT _zbuffer_fn + 0x00000348 EXT _zbuffer_fn_p + 0x00000370 EXT _planarize_fn + 0x00000420 EXT _planarize_fn_p + 0x000004e8 EXT _binitize_fn + 0x000004f0 EXT _safe_binitize_fn + 0x00000560 EXT _y_loop + 0x00000564 EXT _y_loopdb + 0x00000568 EXT _x_loop + 0x0000056c EXT _x_loopdb + 0x0000057c EXT _bin_full + 0x0000062c EXT _bin_not_full + 0x00000640 EXT _bump_x + 0x00000650 EXT _bump_y + 0x00000664 STAT _exit_binitize + 0x00000670 EXT _tex_scalefac + 0x00000698 EXT i2_gt_i1 + 0x000006ac EXT igotmax + 0x000006c0 EXT _trunc_test + 0x00000750 EXT _tri_d + 0x00000860 EXT _tri_zb + 0x00000990 EXT _tri_zb_d + 0x00000ab8 EXT _tri_zb_d_s + 0x00000bf0 EXT _tri_zb_d_s_tex + 0x00000d58 EXT _tri_zb_f_tex + 0x00000e80 EXT _tri_zb_d_s_texm + 0x00000e88 EXT _getFsr + 0x00000e90 EXT _setFsr + 0x00000e98 EXT _reg_dump + +=== PXPL5SUP.O magic=0x014d nscns=3 nsyms=99 === + sect .text vaddr=0x00000000 size=0x0014a0 reloc=276 + sect .data vaddr=0x000014a0 size=0x000450 reloc=16 + sect .bss vaddr=0x000018f0 size=0x000000 reloc=0 + -- 34 defined EXT/STAT symbols: + 0x00000000 STAT _devirtualize + 0x00000040 STAT _newPages + 0x00000098 EXT _newBytes + 0x000000e0 EXT _preplanarize + 0x000001c8 EXT _planarize + 0x00000378 EXT _tex_fixz + 0x000003d8 STAT _lastGrab + 0x000003dc EXT _free_binchunks + 0x000003e0 EXT _screen0bins + 0x000003e4 EXT _screen1bins + 0x000003e8 EXT _screenbins + 0x000003ec EXT _trans_screen0bins + 0x000003f0 EXT _trans_screen1bins + 0x000003f4 EXT _trans_screenbins + 0x000003f8 EXT _DMAscreen + 0x000003fc EXT _writeScreen + 0x00000400 EXT _last_coeffchunk + 0x00000404 EXT _last_coeffchunk0 + 0x00000408 EXT _last_coeffchunk1 + 0x0000040c EXT _back_offs + 0x000004e0 EXT _edgeize + 0x00000530 STAT _grab_binchunks + 0x000005e8 EXT _next_binchunk + 0x00000648 EXT _next_coeffchunk + 0x00000710 STAT _initbins + 0x00000868 EXT _create_screenbins + 0x00000b38 EXT _liberate_screenbins + 0x00000c60 EXT _binitize + 0x00000eb0 EXT _safe_binitize + 0x00001088 EXT _trace_regs + 0x000011b0 STAT _macro_name + 0x00001270 EXT _dump_bins + 0x00001428 EXT _tracepixelmap + 0x00001430 EXT _binitize_fail + +=== PXPL5TRI.O magic=0x014d nscns=3 nsyms=16 === + sect .text vaddr=0x00000000 size=0x000140 reloc=15 + sect .data vaddr=0x00000140 size=0x000000 reloc=0 + sect .bss vaddr=0x00000140 size=0x000000 reloc=0 + -- 3 defined EXT/STAT symbols: + 0x00000000 EXT _tri_zb_d_s + 0x00000080 EXT _tri_zb_d_s_tex + 0x00000128 EXT _tri_zb_d_s_texm + +=== PXPL5TST.O magic=0x014d nscns=3 nsyms=66 === + sect .text vaddr=0x00000000 size=0x000620 reloc=188 + sect .data vaddr=0x00000620 size=0x0002a0 reloc=0 + sect .bss vaddr=0x000008c0 size=0x000000 reloc=0 + -- 2 defined EXT/STAT symbols: + 0x00000000 STAT _set_vertex + 0x00000048 EXT _main + +=== PXPL5YUC.O magic=0x014d nscns=3 nsyms=34 === + sect .text vaddr=0x00000000 size=0x000a60 reloc=60 + sect .data vaddr=0x00000a60 size=0x0000d0 reloc=0 + sect .bss vaddr=0x00000b30 size=0x000040 reloc=0 + -- 16 defined EXT/STAT symbols: + 0x00000000 EXT _edgeize + 0x00000050 EXT _preplanarize + 0x000000b8 EXT _free_binchunks + 0x000000bc EXT _screen0bins + 0x000000c0 EXT _screen1bins + 0x000000c4 EXT _screenbins + 0x000000c8 EXT _DMAscreen + 0x000000cc EXT _writeScreen + 0x000000e8 EXT _planarize + 0x000001f0 EXT _planarizip + 0x00000310 EXT _grab_binchunks + 0x000003b0 EXT _next_binchunk + 0x00000408 EXT _create_screenbins + 0x000005c8 EXT _liberate_screenbins + 0x000006e0 EXT _binitize + 0x000008b0 EXT _safe_binitize + +=== TEST.O magic=0x014d nscns=3 nsyms=15 === + sect .text vaddr=0x00000000 size=0x000100 reloc=15 + sect .data vaddr=0x00000100 size=0x000010 reloc=0 + sect .bss vaddr=0x00000110 size=0x000020 reloc=0 + -- 5 defined EXT/STAT symbols: + 0x00000000 EXT _intintint + 0x00000008 STAT _statint + 0x0000000c STAT _statfloat + 0x00000040 EXT _floatfloatfloat + 0x00000078 EXT _floatintfloat + +=== WALLTIME.O magic=0x014d nscns=3 nsyms=14 === + sect .text vaddr=0x00000000 size=0x0000a0 reloc=16 + sect .data vaddr=0x000000a0 size=0x000020 reloc=0 + sect .bss vaddr=0x000000c0 size=0x000000 reloc=0 + -- 2 defined EXT/STAT symbols: + 0x00000000 EXT _second_ + 0x00000018 STAT _lastTime + +=== PXPL5TRI.O magic=0x014d nscns=3 nsyms=99 === + sect .text vaddr=0x00000000 size=0x001570 reloc=66 + sect .data vaddr=0x00001570 size=0x0000d0 reloc=0 + sect .bss vaddr=0x00001640 size=0x000000 reloc=0 + -- 58 defined EXT/STAT symbols: + 0x00000000 EXT edgize_fn + 0x00000000 EXT _Cdelta_u + 0x00000004 EXT _Cdelta_v + 0x00000008 EXT _Cmax_x + 0x0000000c EXT _Cmax_y + 0x00000030 STAT _.Cturn_z_to_tex + 0x00000034 STAT _.Cturn_z_to_tex_by_4 + 0x00000038 STAT _.Cturn_z_to_tex_by_8 + 0x00000080 EXT edgize_poly_fn_entry + 0x00000088 EXT edgize_poly_fn + 0x000000e0 EXT carry_on_branching + 0x00000138 EXT edgize_last_poly_vert + 0x00000190 EXT _preplanarize_fn_p + 0x000001d0 EXT vertloop + 0x000001d4 EXT vertloop_db + 0x000001e8 EXT xmax_ok + 0x000001f4 EXT xmin_ok + 0x000001f4 EXT ycheck + 0x00000204 EXT ymax_ok + 0x00000210 EXT ymin_ok + 0x00000210 EXT zcheck + 0x00000228 EXT texscale_ok + 0x00000234 EXT zmin_ok + 0x00000348 EXT _planarize_fn_p + 0x00000410 EXT _texture_rescale + 0x00000510 EXT bin_was_full + 0x000005c0 EXT _safe_binitize_fn + 0x00000648 EXT _y_loop + 0x0000064c EXT _y_loopdb + 0x00000650 EXT _x_loop + 0x00000654 EXT _x_loopdb + 0x00000664 EXT _bin_full + 0x0000067c EXT _bin_not_full + 0x00000688 EXT _bump_x + 0x00000698 EXT _bump_y + 0x000006ac STAT _exit_binitize + 0x000006b8 EXT chunk_fn + 0x00000788 EXT _tri_zb_f + 0x000007e4 EXT back_000 + 0x00000814 EXT label000 + 0x00000900 EXT label001 + 0x00000968 EXT _tri_zb_rgb + 0x000009c4 EXT back_001 + 0x000009f4 EXT label002 + 0x00000adc EXT label003 + 0x00000b40 EXT _tri_zb_f_t + 0x00000b9c EXT back_002 + 0x00000bcc EXT label004 + 0x00000d90 EXT label005 + 0x00000df8 EXT _tri_zb_rgb_t + 0x00000e54 EXT back_003 + 0x00000e84 EXT label006 + 0x00001048 EXT label007 + 0x000010b0 EXT _tri_zb_rgb_o_t + 0x0000110c EXT back_004 + 0x0000113c EXT label008 + 0x000012f8 EXT label009 + 0x00001360 EXT _reg_dump + +=== RPAZLIB.O magic=0x014d nscns=3 nsyms=201 === + sect .text vaddr=0x00000000 size=0x0038b0 reloc=675 + sect .data vaddr=0x000038b0 size=0x0003b0 reloc=0 + sect .bss vaddr=0x00003c60 size=0x000810 reloc=0 + -- 75 defined EXT/STAT symbols: + 0x00000000 STAT _reorderViews + 0x00000040 STAT _vrtlist + 0x00000170 EXT _PAZidMatrix + 0x000001a0 EXT _PAZtranslate + 0x000001d0 EXT _PAZrotate + 0x00000200 EXT _PAZscale + 0x00000230 EXT _PAZtranslatePair + 0x00000260 EXT _PAZrotatePair + 0x00000290 EXT _PAZscalePair + 0x000002c0 EXT _PAZconcat + 0x00000308 EXT _PAZcreateBinObjects + 0x00000318 EXT _PAZcreateBinObject + 0x00000370 EXT _PAZdeleteObject + 0x00000380 EXT _currentScene + 0x00000384 EXT _vrthead + 0x00000388 STAT _sect_pixel_req + 0x0000038c STAT _sectinst + 0x00000390 EXT _total_grab_patch + 0x00000394 EXT _total_grab_light + 0x00000398 STAT _f_mtl_override + 0x0000039c STAT _b_mtl_override + 0x000003a0 STAT _f_tex_override + 0x000003a4 STAT _b_tex_override + 0x000003a8 STAT _inited + 0x000003ac STAT _reordered + 0x00000580 EXT _PAZreadBound + 0x000005c0 EXT _PAZcreateInstance + 0x00000638 EXT _PAZdeleteInstance + 0x00000778 EXT _PAZreadInstance + 0x00000780 EXT _PAZwriteInstance + 0x00000788 STAT _distance + 0x00000800 STAT _planesect + 0x00000970 STAT _box_sect + 0x00000d80 STAT _pix_sect_obj + 0x00000f78 STAT _pix_sect_inst + 0x00001138 EXT _PAZsectPixel + 0x00001388 STAT _vect_sect_obj + 0x00001518 STAT _vect_sect_inst + 0x000016b8 EXT _PAZsectVector + 0x00001818 EXT _PAZcreateLight + 0x00001890 EXT _PAZdeleteLight + 0x00001970 EXT _PAZreadLight + 0x00001978 EXT _PAZwriteLight + 0x00001980 EXT _PAZinitLight + 0x000019a8 EXT _PAZcreateView + 0x00001a20 EXT _PAZdeleteView + 0x00001b18 EXT _PAZreadView + 0x00001b20 EXT _PAZwriteView + 0x00001b60 EXT _PAZinitView + 0x00001c68 EXT _PAZcreateScene + 0x00001ca8 EXT _PAZdeleteScene + 0x00001db8 EXT _PAZsetScene + 0x00001df8 EXT _PAZinit + 0x00001ea0 STAT _replace_strip + 0x00001f80 STAT _new_patch_rec + 0x00002110 STAT _new_light_rec + 0x00002228 STAT _add_renderPatch + 0x00002370 STAT _scale_from_matrix + 0x000023c8 STAT _whichLOD + 0x00002610 STAT _visibility + 0x00002858 STAT __renderObj + 0x00002dc0 STAT _render_inst + 0x00003038 STAT _build_displaylist + 0x000030f8 EXT _PAZrenderScene + 0x00003300 EXT _PAZsetBackGND + 0x000034a8 EXT _PAZlink + 0x000034d0 EXT _PAZnest + 0x000034f8 EXT _PAZunlink + 0x00003520 EXT _PAZunnest + 0x00003548 EXT _PAZdeleteTree + 0x000035c0 EXT _PAZlinkName + 0x00003640 EXT _PAZnestName + 0x000036c0 EXT _PAZunlinkName + 0x00003748 EXT _PAZunnestName + 0x000037d0 EXT _PAZdeleteTreeName + +=== SFX.O magic=0x014d nscns=3 nsyms=110 === + sect .text vaddr=0x00000000 size=0x001940 reloc=400 + sect .data vaddr=0x00001940 size=0x000240 reloc=0 + sect .bss vaddr=0x00001b80 size=0x000000 reloc=0 + -- 16 defined EXT/STAT symbols: + 0x00000000 STAT _float_0to1 + 0x00000040 STAT _makeBangMtls + 0x00000118 STAT _sfx_random_numbers + 0x00000138 STAT _vertex_block + 0x000001f0 STAT _initBang + 0x00000224 STAT _sfx_random_ix + 0x00000228 STAT _sfx_random_base + 0x0000022c EXT _paz_explosions_active + 0x00000230 STAT _first_mtl + 0x00000234 EXT _sfx_inited + 0x00000238 STAT _active_fx + 0x00000888 STAT _createBang + 0x00000c18 STAT _step_bang + 0x00001090 EXT _sfx_init + 0x000012c8 EXT _PAZsfx + 0x000018a8 EXT _step_fx + +=== TEST.O magic=0x014d nscns=3 nsyms=9 === + sect .text vaddr=0x00000000 size=0x000020 reloc=0 + sect .data vaddr=0x00000020 size=0x000000 reloc=0 + sect .bss vaddr=0x00000020 size=0x000000 reloc=0 + -- 1 defined EXT/STAT symbols: + 0x00000000 EXT _test + +=== TEXTURE.O magic=0x014d nscns=3 nsyms=21 === + sect .text vaddr=0x00000000 size=0x000550 reloc=38 + sect .data vaddr=0x00000550 size=0x000030 reloc=0 + sect .bss vaddr=0x00000580 size=0x000010 reloc=0 + -- 8 defined EXT/STAT symbols: + 0x00000000 STAT _newCell + 0x00000028 STAT _texturesInited + 0x00000068 STAT _cell_hwareSize + 0x000000a8 EXT _texInit + 0x00000180 EXT _allocateHSPtexture + 0x00000350 STAT _paletfix + 0x00000378 EXT _codeWordFromRamp + 0x000004f8 EXT _setRampEntry + +=== VREND.O magic=0x014d nscns=3 nsyms=32 === + sect .text vaddr=0x00000000 size=0x0001a0 reloc=70 + sect .data vaddr=0x000001a0 size=0x0000e0 reloc=0 + sect .bss vaddr=0x00000280 size=0x000000 reloc=0 + -- 1 defined EXT/STAT symbols: + 0x00000000 EXT _main + +=== VR_CULL.O magic=0x014d nscns=3 nsyms=99 === + sect .text vaddr=0x00000000 size=0x001740 reloc=321 + sect .data vaddr=0x00001740 size=0x000510 reloc=0 + sect .bss vaddr=0x00001c50 size=0x0001c0 reloc=0 + -- 22 defined EXT/STAT symbols: + 0x00000000 STAT _dbl_plane_term + 0x00000050 EXT _plane_eqn + 0x000001d8 STAT _make_cullview + 0x00000408 STAT _new_draw_item + 0x000004f8 STAT _current_frame_count + 0x000004fc STAT _chunk_head + 0x00000500 STAT _chunk_active + 0x00000504 STAT _chunk_free_ptr + 0x00000508 STAT _chunk_usage + 0x00000528 STAT _bbox_plane + 0x00000688 STAT _scale_from_matrix + 0x000006f8 STAT _visibility + 0x000009f0 STAT _build_bound + 0x00000b78 STAT _cullize_material + 0x00000be0 STAT _whichLOD + 0x00000c00 STAT _cull_lmodel + 0x00000d50 STAT _cull_geogroup + 0x00000e78 STAT _printf_matrix + 0x00000f60 STAT _cull_object + 0x000012e8 STAT _cull_node + 0x00001460 STAT _cull_tree + 0x00001598 EXT _veloci_cull + +=== VR_DRAW.O magic=0x014d nscns=3 nsyms=41 === + sect .text vaddr=0x00000000 size=0x000f00 reloc=100 + sect .data vaddr=0x00000f00 size=0x000100 reloc=0 + sect .bss vaddr=0x00001000 size=0x000060 reloc=0 + -- 19 defined EXT/STAT symbols: + 0x00000000 EXT _setpxpl5VideoMode + 0x00000080 EXT _light_upto2_vertices + 0x000000f4 EXT _back_colour + 0x000000f8 STAT _first_framebuf_y + 0x000000fc STAT _framebuf_flip + 0x000001f8 EXT _light_upto2_facets + 0x00000370 EXT _light_vertices + 0x00000518 EXT _light_facets + 0x000006c0 STAT _clear_vertices + 0x00000700 STAT _clear_facets + 0x00000740 STAT _solve + 0x000008a0 STAT _xform_and_project + 0x000009a0 STAT _draw_geometry + 0x000009a8 STAT _draw_geogroup + 0x00000c78 STAT _eval_lmodel + 0x00000ca8 STAT _veloci_draw + 0x00000db8 STAT _veloci_rasterize + 0x00000df0 STAT _veloci_swap_buffers + 0x00000e28 EXT _vr_draw_scene + +=== VR_REMOT.O magic=0x014d nscns=3 nsyms=221 === + sect .text vaddr=0x00000000 size=0x003170 reloc=718 + sect .data vaddr=0x00003170 size=0x000930 reloc=97 + sect .bss vaddr=0x00003aa0 size=0x0002b0 reloc=0 + -- 89 defined EXT/STAT symbols: + 0x00000000 EXT _reply + 0x00000050 EXT _dN_reply + 0x00000090 STAT _protocol_error + 0x00000138 STAT _do_init + 0x00000228 STAT _parse_argv + 0x000002b0 EXT _type_to_string + 0x00000420 STAT _init_list + 0x00000480 STAT _init_superlist + 0x000004e0 STAT _new_list + 0x00000538 STAT _get_first + 0x000005a0 STAT _get_next + 0x00000618 STAT _add_item + 0x000006f0 STAT _find_item + 0x00000768 EXT _replying + 0x0000076c EXT _view_id + 0x00000770 EXT __dN_transactions + 0x00000774 STAT _receive_bytes + 0x00000778 EXT _size + 0x0000077c EXT _init + 0x00000780 STAT _dN_data + 0x00000784 EXT _recompute_normals + 0x00000788 EXT _smooth + 0x0000078c EXT _cooked + 0x00000790 EXT _textured + 0x00000794 EXT _current_fn + 0x00000798 STAT _unlink_superlist_item + 0x00000798 EXT _mess_start + 0x0000079c STAT _init_pending + 0x000007a0 EXT _dealt_with + 0x000007a4 EXT ___trianglesRendered + 0x000007a8 EXT ___frameTime + 0x000007ac STAT _useful_index + 0x000007b0 STAT _dpl_err + 0x000007f8 STAT _remove_item + 0x000008b0 STAT _new_node + 0x00000900 EXT _dpl_init_scene + 0x00000950 EXT _dpl_init_zone + 0x00000968 EXT _dpl_init_view + 0x000009a0 EXT _dpl_init_lmodel + 0x000009d0 EXT _dpl_init_light + 0x000009e8 EXT _dpl_init_material + 0x00000a38 EXT _dpl_init_ramp + 0x00000a68 EXT _dpl_init_texmap + 0x00000a88 EXT _dpl_init_texture + 0x00000a98 EXT _dpl_init_instance + 0x00000ad0 EXT _dpl_init_object + 0x00000b00 EXT _dpl_init_lod + 0x00000b40 EXT _dpl_init_geogroup + 0x00000b78 EXT _dpl_init_geometry + 0x00000b98 EXT _dpl_init_dcs + 0x00000be0 STAT _velocirender_create + 0x00000ef0 STAT _velocirender_delete + 0x00001058 STAT _flush_dpl_type_zone + 0x000010d0 STAT _flush_dpl_type_view + 0x00001300 STAT _flush_dpl_type_instance + 0x00001408 STAT _flush_dpl_type_dcs + 0x00001478 STAT _flush_dpl_type_lmodel + 0x00001480 STAT _flush_dpl_type_light + 0x000014f0 STAT _flush_dpl_type_object + 0x000014f8 STAT _flush_dpl_type_lod + 0x00001578 STAT _flush_dpl_type_geogroup + 0x00001620 STAT _flush_dpl_type_geometry + 0x00001678 STAT _flush_dpl_type_material + 0x00001758 STAT _flush_dpl_type_texmap + 0x00001788 STAT _flush_dpl_type_texture + 0x00001838 STAT _flush_dpl_type_ramp + 0x00001878 STAT _velocirender_flush + 0x00001a80 STAT _velocirender_add_list_item + 0x00001d40 STAT _velocirender_remove_list_item + 0x00002008 STAT _velocirender_nest_dcs + 0x00002070 STAT _velocirender_link_dcs + 0x000020d8 STAT _velocirender_prune_dcs + 0x00002170 STAT _velocirender_readpixels + 0x00002180 STAT _velocirender_sectvector + 0x00002190 STAT _velocirender_sectpixel + 0x000021a0 STAT _velocirender_init + 0x000021f0 STAT _velocirender_draw_scene + 0x00002230 STAT _velocirender_morph_object + 0x00002240 STAT _velocirender_statistics + 0x00002258 STAT _NewVertices + 0x000023e8 STAT _NewConnections + 0x00002568 STAT _replace_vertices + 0x00002650 STAT _replace_connections + 0x000026e0 STAT _vertex_from_index + 0x00002750 STAT _resolve_connections + 0x00002930 STAT _fix_up_geometry + 0x00002b40 STAT _velocirender_set_geom_verts + 0x00002d38 STAT _velocirender_set_texmap_texels + 0x00002f30 EXT _remote_velocirender + +=== WALLTIME.O magic=0x014d nscns=3 nsyms=14 === + sect .text vaddr=0x00000000 size=0x0000a0 reloc=16 + sect .data vaddr=0x000000a0 size=0x000020 reloc=0 + sect .bss vaddr=0x000000c0 size=0x000000 reloc=0 + -- 2 defined EXT/STAT symbols: + 0x00000000 EXT _second_ + 0x00000018 STAT _lastTime diff --git a/emulator/firmware-decomp/HARDWARE-ARCHITECTURE.md b/emulator/firmware-decomp/HARDWARE-ARCHITECTURE.md new file mode 100644 index 0000000..ade3918 --- /dev/null +++ b/emulator/firmware-decomp/HARDWARE-ARCHITECTURE.md @@ -0,0 +1,226 @@ +# Division VelociRender card — hardware architecture (for emulation) + +Reconstructed from the firmware (`VREND.MNG`), the DPL3/VRENDER board source, and +Division's own design docs (`VWE.DOC`, `PXPL5001.DOC`, `CM200IO.H`, `DMA.TXT`). +**Physical component tables at the bottom are placeholders to fill from the +photos.** This documents what the software *proves* must be on the board, so the +photos only need to pin part numbers and quantities. + +## Form factor + +Full-length, full-height, **double-stacked EISA** card pair (operator-described): + +- **Bottom board:** the i860 front-end processor + its DRAM + the host/link + interface. +- **Top board:** the **Division PXPL IGC 5.2** rasterizer + supporting logic + (VRAM, RAMDAC, video out). + +## The pipeline (three stages) + +``` + Host PC (EISA) Card, bottom board Card, top board + ┌───────────────┐ link/CCB ┌────────────────────┐ FIFO/ ┌──────────────────┐ + │ BTL4OPT.EXE │ ─────────► │ i860 front-end │ coeff │ PXPL IGC 5.2 │ + │ VelociRender │ (transputer│ (VREND.MNG): │ stream │ (Pixel-Planes 5) │ + │ wire: draw_ │ link, │ traverse, TRANSFORM│ ─────► │ tiled SIMD │ + │ scene, DCS, │ C011-style│ LIGHT, planarize, │ "down │ rasteriser: │ + │ materials …) │ 0x150) │ binitize, emit │ the │ edge/plane eval, │ + └───────────────┘ │ pxpl5 coefficients │ wire" │ Z-buffer,texture │ + └────────────────────┘ │ → VRAM tiles │ + └───────┬──────────┘ + VRAM→FrameStore│→ RAMDAC → 3 VGA heads +``` + +1. **Host (EISA PC)** runs the game; sends the VelociRender command wire (scene + graph: create/DCS/draw_scene/materials/effects — *not* transformed geometry). +2. **i860 front-end** (bottom board, runs `VREND.MNG`): scene traversal, + per-frame **transform / lighting / planarization / "binitize"**, and + generation of the per-primitive **coefficient words** the rasterizer consumes. + Feeds them to the back-end via a FIFO/DMA. (`VWE.DOC`: "push a ton of + triangles down the wire into the pxpl5 back-end"; a z-buffered flat-shaded + textured quad = **48 32-bit words**, a triangle ~24.) +3. **PXPL IGC 5.2 back-end** (top board): Division's implementation of **UNC + Chapel Hill Pixel-Planes 5** — a *tiled SIMD pixel-processor array*. Per tile + it evaluates edges/planes, does Z-buffering and texturing, into VRAM tile + buffers, then VRAM→FrameStore (`DMA.TXT`: tile loop, "wait for 2 interrupts + from DMA engine (scan-converter + end-of-texture)", `VRAMwritetoFrameStore`). +4. **Video out:** FrameStore → RAMDAC → **3 VGA heads** (the octopus cable; 5 + mono MFDs + colour radar packed in one framebuffer — see the VDB decode note), + NTSC timing (`TESTVR.BAT /video NTSC`). + +## Register / command interfaces (what an emulator must model) + +**Host ↔ i860 — transputer link + CCB** (`CM200IO.H`, Division project "BLASTER", +CM100/CM200 lineage): +- "860 ↔ transputer io control register", `INPUT_CCB 0xFFFFF000` / `OUTPUT_CCB + 0xFFFFF300` (256-byte Command Control Blocks), `CLEAR_INT`/`INT_CLEARED`. +- IO opcodes: `IO_LINK_OUT`, `IO_LINK_RAST_OUT`, `IO_DIVNET_IN/OUT/NODE` + (DIVNET = Division's transputer-link network — this is also the pod-to-pod + mesh path), `IO_BLOCK_IN/OUT`, `IO_MATHADV_REQ`, `IO_TIMER`. +- The host side pumps the link via a **C011/C012-style link adapter at EISA I/O + 0x150** (`outveryfast` REP OUTSW); boot resets via port 0x160. + +**i860 → PXPL IGC — coefficient stream** (`DMA.TXT`, `EOF.C` `IGC_*` macros): +- `IGC_SETENABS`, `IGC_MEMeqSCA_S1`, `IGC_MEMeqZERO`, `IGC_MEMgeSCA_S1`, + `IGC_CLEAR`, `IGC_CPY`, `send_em(&DMAptr,&coeffptr,…)` — the ops that build the + per-tile coefficient blocks the IGC executes. +- IGC per-primitive/per-pixel registers (`dvpx_*`): `dvpx_zbuf`, `dvpx_texid`, + `dvpx_texsize`, **`dvpx_texmode`**, **`dvpx_texrampsel`** (the material's ramp + index at the hardware level), `dvpx_diffuse`, `dvpx_specular`, `dvpx_scalar`. +- **Texture display modes** (`dvpx_eoftexmode`, `EOF.C`): **0 = texture RAMP** + (texel value → colour ramp — this is where IR/"predator" vision lives; see + PVISION-IMPLEMENTATION-GUIDE.md), 0x4 = 8-bit mono, 0x6/0x7 = full colour + (3-3-2 / 4-4-4). The 4 ramps (`texture_ramps[MAX_TEX_RAMPS=4]`) live in IGC + memory, selected per primitive by `dvpx_texrampsel`. + +## Boot (both processors, every cold start, blind — see board-boot note) + +- **`/tranny vrendmon.btl`** = the **transputer** boot image (INMOS boot-from- + link; ~85 KB). Loaded first over the link while the transputer is held reset. +- **`/i860 vrender.mng`** = the **i860** application (~380 KB; disassembles + cleanly as i860 — confirmed). The renderer logic (incl. pvision) is here. +- No version handshake possible pre-boot; both are streamed unconditionally. + +## Lineage & external references (for the emulator author) + +- **Division Ltd** (UK), project "BLASTER"; chip generations CM100 → CM200 → + **PXPL IGC 5.2** (the VWE board). +- **Rasterizer = UNC Pixel-Planes 5** (Fuchs, Poulton et al., UNC Chapel Hill, + ~1989-92): tiled, SIMD pixel-processor array, "renderer" bin architecture, + quadratic expression evaluation. Well-documented in the graphics literature — + the authoritative model for the back-end. +- **Intel i860 (XR/XP)** front-end — documented ISA; our `dis860.py` disassembler + reads `VREND.MNG` directly. +- **INMOS transputer** (T-series) + **C011/C012 link adapters** — documented; the + host-comms and DIVNET/mesh layer. + +## Emulation implications + +- It's a **3-processor system**: transputer (comms/link/DIVNET), i860 + (geometry/coefficient generation — already disassemblable), PXPL IGC (SIMD + rasterizer). A bit-exact renderer = run `VREND.MNG` on an i860 core + model the + IGC's coefficient interpreter (edge/plane eval, Z, texture modes incl. the + ramp) + the CCB/link glue. The transputer layer can likely be *stubbed* at the + CCB boundary (as our GL bridge already stubs the link) unless DIVNET/mesh + behaviour is needed. +- The IGC is the hard part (custom SIMD ASIC) — but its *programming model* is + fully visible in `EOF.C`/`PP5REND.C`/the `dvpx_*` set + the PXPL5 docs, and the + Pixel-Planes 5 literature describes the microarchitecture. +- Priorities: (1) i860 core + coefficient emit is enough to *drive* a + functional-accurate rasterizer we write to spec; (2) modeling the IGC exactly + buys true bit-exactness; (3) transputer only if mesh/DIVNET timing matters. + +--- + +## Physical component inventory — IDENTIFIED FROM PHOTOS (2026-07-14, 13 photos) + +Photos in `hardware-photos/` (`01`-`08` close-ups, `09`-`13` the assembly + module +shots). **Corrected topology (operator + edge-on/separated shots): it is a +TWO-BOARD stack**, each board covered front and back — the several `DBxxxx` +silkscreens (all "(C) Division Ltd. 1994") are functional areas/sub-modules of +the two boards, not separate cards. + +### Board 1 — i860 geometry board (bottom) +| area | key silicon (as marked) | photos | +|---|---|---| +| CPU | **Intel `A80860XP-50`** — i860 **XP, 50 MHz** (heatsinked) | 04, 10-left | +| coeff FIFO path | **IDT `72510`** FIFOs · grid of Cypress `CY7C451-20` | 04 | +| working RAM | **2× RAM SIMM** = `DBIO151/03`, **Hitachi `HM624256AJP-25`** DRAM ×6/SIMM | 12-left, 09 | +| soldered memory | **16× Ramtron `DM2202J-15` EDRAM** | 03, 10-left | +| I/O / data-mux (other side) | `DBIO152/03` (SN 630254): large `QS74FCT153/158` mux array · Lattice ispLSI 1032/1016 · IDT `16Z646` · `QS338450` bus switches | 01, 11-left | +| host bus | **gold EISA edge** · IDT `74FCT16292/16841` transceivers ("pma") | 02 | +| glue/clock | Lattice ispLSI · GAL22V10 · Cypress `CY7B991` RoboClocks | all | + +### Board 2 — PXPL rasterizer board (top) +| area | key silicon (as marked) | photos | +|---|---|---| +| rasterizer control | **`DIVISION PXPL IGC 5.2`** custom ASIC (gold ceramic PGA, date 9447) | 07, 08, 10-right | +| scan-conversion | **3× Xilinx `XC3090-100`** FPGAs (+ a 4th on the video side) | 07, 08, 10-right | +| **SIMD pixel array** | **`DBIO153/02` daughter cards: 4 sets of 3 = 12 cards, EACH with 4× `DIVISION PXPL EMC 5.1`** custom chips ⇒ **~48 EMC chips**. (These "look like 12 RAM SIMMs" but are the pixel-plane modules.) Back side: `QS74FCT2821` regd transceivers + `74F08`. | 12-mid/right, 13, 07 | +| VRAM | ~10-16× Micron `MT42C8256` VRAM | 08, 10-right | +| RAMDAC | **Analog Devices `ADV7150`** (programmable gamma/palette) | 08 | +| clocks | ICS `2572` synth · Cypress `CY7B991` | 08 | +| video out (other side) | `DBE0151 Pixel Planes rev/02` (SN 120227): VGA heads · **Maxim `MAX440/442`** amps · **`LM1882` + `74ACT715` + 14.318 MHz** (NTSC) · Lattice ispLSI 1048 ×2 | 05, 06, 11-right | + +### The big reconciliation: PXPL IGC + PXPL EMC = the rasterizer + +The daughter cards carry **`DIVISION PXPL EMC 5.1`** chips — and **`EMC` is exactly +the `_configEMCs` / `configEMCs_hi` the firmware programs** (`EOF.C`). So: + +- **`PXPL IGC 5.2`** (1 ASIC on Board 2) = the **I**mage **G**eneration + **C**ontroller — the master rasterizer that runs the coefficient stream and + drives the array. +- **`PXPL EMC 5.1`** (~48 chips on the 12 daughter cards) = the **E**nhanced + **M**emory **C**ontroller — the **UNC Pixel-Planes-5 SIMD pixel-processor/memory + array** ("the planes"). Organized 4 banks × 3 cards × 4 chips. + +Together, IGC + EMC array = the complete Division Pixel-Planes 5 rasterizer. The +texture-mode/ramp work (incl. pvision) is IGC-programmed and executed across the +EMC array. + +### What the photos pin for emulation + +- **i860 = XP @ 50 MHz** — set the emulated core exactly. +- **Rasterizer = 1× `PXPL IGC 5.2` + ~48× `PXPL EMC 5.1`** (SIMD array), no public + datasheet — model from the `dvpx_*`/`IGC_*`/`configEMCs` firmware interface + + the Pixel-Planes 5 literature. This is the hard part but the programming model + is fully in-source. +- **RAMDAC = ADV7150** (documented) — pins the colour/gamma back-end (supersedes + the guessed `pow(1/1.25)`; read its real config from the firmware). +- **Video = NTSC** (`LM1882`+`74ACT715`+14.318 MHz, `MAX440/442` amps → VGA heads). +- **Memory:** i860 = 2× Hitachi-DRAM SIMM + 16× Ramtron `DM2202` EDRAM; + rasterizer = Micron `MT42C8256` VRAM + the EMC on-chip pixel memory. + +### The transputer — CONFIRMED: INMOS IMS T425-J25S (2026-07-14) + +Firmware proved it first: **`VRENDMON.BTL` is transputer code, not i860** (as i860 +it's garbage; it opens `0xF0`=240 = INMOS **boot-from-link** length byte, then the +textbook prologue `ajw 4; stl 1; stl 1; stl 1` + coherent `ldl/stnl/ldc/opr`). +Operator then located the chip on the i860 board (dust-covered, lower-centre-right +PLCC): + +- **`INMOS IMS T425-J25S`, date 9335 (wk35 1993):** the **32-bit integer + transputer** — 4 KB on-chip SRAM, 4 serial links, **no FPU** — `J` = 84-pin + PLCC, `25` = **25 MHz**. +- The **no-FPU** point confirms the labour split: **T425 = comms/control/boot** + (runs `vrendmon.btl`; host link + the `CM200IO.H` CCB to the i860 + DIVNET + pod-mesh over its 4 links, no FP needed); **i860 XP = FP geometry** (runs + `vrender.mng`). Exactly the 1990 CM200 "860 ↔ transputer" pairing carried into + the 1994 board. +- **Emulation impact:** the system is **T425 + i860 XP + IGC/EMC**. `VREND.MNG` + (i860, disassembles clean via `dis860.py`) and `VRENDMON.BTL` (T425 bytecode) + are DIFFERENT ISAs — don't run `dis860` on the `.BTL`. For a functional renderer + the T425 can be stubbed at the CCB/link boundary (as the GL bridge does); for + bit-exact/timing it must be modelled (INMOS T4-series ISA is well documented — + the boot image already decodes cleanly). + +## Downstream: the VWE Video Distribution Board (VDB) — photos 14/15 + +A **separate board, VWE's own** (not Division): silkscreen **"VIDEO DISTRIBUTION +BOARD, (C) 1994 V.W.E. Inc., Design by A.G./M.C., P/N 1404 REV.1"**. ISA form +factor; receives the rendered framebuffer from the Division card and fans it out +to the cockpit displays — the physical realization of the **3-VGA-head decode** +(5 mono MFDs + colour radar packed in one 16bpp framebuffer → 3 heads → octopus). + +- **AMD `MACH130-18JC` CPLD** — the pixel routing / region-decode / timing (splits + the packed framebuffer into per-display regions). +- **3× Brooktree `Bt477KPJ80`** — 256×24 palette **RAMDACs**, one per VGA head + (the heads' final DAC is here, distinct from the Division card's `ADV7150`). +- Support: `74F245`/`74LS125`/`SN74LS541` buffers, `MAD1103` delay lines, an HP + part, fuse + analog output driver/filter array → D-sub outputs on the bracket. +- Back = solder-side only, one hand-added **bodge wire** (a Rev.1 fix). +- **Emulation note:** a display-fanout stage; render-accuracy needs only the + framebuffer→region mapping (the bridge already models the 3-head split). The + `Bt477` palettes / `MACH130` matter only for exact per-head colour/timing. +- **MACH130 "firmware":** no `.JED`/PAL source in the software dump (checked), and + its FUNCTION is already RE'd in `VDB-NOTES.md` from the MUNGA_L4 driver, so a + chip dump isn't needed for emulation. It IS **socketed** (84-pin PLCC — easy to + pull), so a hardware read is possible with a universal programmer that lists + MACH130 — the only catch is the MACH1xx **security bit** (if set, typical for + production, readback is blocked). Low priority. + +### Still open (for the hardware owner) +2. **Exact EMC-array geometry** — confirm 4×3×4 = 48 `PXPL EMC 5.1`, and how the + 4 banks map to screen tiles / the Pixel-Planes bin structure. +3. **VRAM/EDRAM totals** (count × size) for the frame/Z/texture split. +4. **`ADV7150` exact variant** + any genlock on the video side. diff --git a/emulator/firmware-decomp/PVISION-IMPLEMENTATION-GUIDE.md b/emulator/firmware-decomp/PVISION-IMPLEMENTATION-GUIDE.md new file mode 100644 index 0000000..048a10e --- /dev/null +++ b/emulator/firmware-decomp/PVISION-IMPLEMENTATION-GUIDE.md @@ -0,0 +1,193 @@ +# Implementing IR / "Predator" Vision in a BattleTech (VelociRender) renderer + +**Audience:** the BT411 renderer + whoever's Claude is implementing it. +**Status:** reverse-engineered from the original VelociRender i860 firmware +(`VREND.MNG`) and the DPL3/VRENDER board source, and confirmed by original-team +recollection. This document is self-contained — the key code is quoted inline so +you don't need our tree. + +--- + +## TL;DR + +Predator/IR vision is **not** a grayscale squash, **not** a palette swap, and +**not** a scene-luminance heat map. It is the Division board's **texture-value +ramp mode** — a diagnostic the board provided for *checking texture maps* that +the game developers hijacked as the in-game IR effect. + +Each **textured** surface is drawn as: + +``` +out_rgb = lerp(color0, color1, luminance(texel)) +``` + +where `luminance(texel)` is the brightness of the sampled **texel** (the texture +map's value, *not* the lit/shaded pixel), and `(color0, color1)` are the two RGB +endpoints of the **ramp assigned to that surface's material**. There are four +ramps (gray / red / green / blue → white). Because different materials use +different ramps, the scene comes out **multicolor**. + +--- + +## 1. How the game turns it on (host side) + +The game's renderer object (`DPLRenderer`, shared by BattleTech and Red Planet) +has a toggle. From the original source (`MUNGA_L4/L4VIDEO.CPP`, +`DPLRenderer::DPLTogglePVision`): + +```c +static Logical pvision_on = 0; +dpl_EXPLOSION_EFFECT_INFO sfx_info; +sfx_info.x = sfx_info.y = sfx_info.z = 0; +if ((pvision_on ^= 1) != 0) sfx_info.type = -1; // pvision ON +else sfx_info.type = -2; // pvision OFF +dpl_Effect(dpl_effect_type_explosion, NULL, &sfx_info); +``` + +Key point: **it sends no colour.** It fires a bogus "explosion" effect at the +origin whose `type` field is a magic flag: `-1` = ON, `-2` = OFF. The +`dpl_EXPLOSION_EFFECT_INFO` struct is `{float x,y,z; int32 type; dpl_TEXTURE*}` +— there is no colour to carry. So the palette is **entirely board-side**; the +host only flips a switch. + +On the VelociRender wire this arrives as the effect action (in our decode, +action `0x1b`), payload first word = `type`. If you drive a board/emulator, the +handler is: `type >= 0` → normal explosion; `type == -1/-2` → set/clear the +pvision mode flag. + +--- + +## 2. What the board does (firmware) + +The board firmware selects a **texture display mode** per frame +(`dvpx_eoftexmode`, from the end-of-frame code `VRENDER/PXPL5SUP/EOF.C`): + +| mode | meaning | +|------|---------| +| `0` | **texture RAMP** — texel value → interpolated colour ramp (this is IR vision) | +| `0x4` | 8-bit monochrome | +| `0x6` | 8-bit 3-3-2 full colour | +| `0x7` | 12-bit 4-4-4 full colour | + +Normal rendering uses the full-colour modes. `pvision ON` flips the whole scene +into **mode 0 (ramp)**. The end-of-frame code literally linear-interpolates each +channel across the texel value: + +``` +/* now access the texture colour map table (8 32-bit entries) */ +/* now linterp from r0 to r1 */ // Red across texel value +/* now linterp from g0 to g1 */ // Green +/* now linterp from b0 to b1 */ // Blue +``` + +The "texel value" is a 6-bit luminance of the 24-bit texel +(`VRENDER/DNC.C::luminize` — "returns a 6-bit luminance value from a 24-bit +texel"). Think of it as `value = luminance(texel)`, `value ∈ [0,1]`. + +--- + +## 3. The ramps + +A ramp is two RGB endpoints (`dpl_RAMP { float32 color0[3]; float32 color1[3]; }`). +`color0` = the colour for **dark** texels (value 0), `color1` = the colour for +**bright** texels (value 1). Four ramps are installed once at render init +(`VRENDER/VR_DRAW.C`, `setRampEntry(ramp, index, r0,g0,b0, r1,g1,b1)`): + +| ramp | color0 (dark) | color1 (bright) | look | +|------|---------------|-----------------|------| +| 0 | `0.0, 0.0, 0.0` | `1.0, 1.0, 1.0` | black → white (grayscale) | +| 1 | `0.3, 0.0, 0.0` | `1.0, 1.0, 0.9` | dark red → warm white | +| 2 | `0.0, 0.5, 0.0` | `1.0, 1.0, 1.0` | green → white | +| 3 | `0.0, 0.0, 0.4` | `0.9, 0.9, 1.0` | blue → cool white | + +Each **material** references one of these by an integer `ramp_entry` (0–3), so a +mech built from several materials shows several colour families at once — that's +the multicolor IR look, and it's why the tool was useful for *checking* which +material/ramp a surface used. + +> **Provenance caveat:** those RGBs are the source defaults. The shipped 1996 +> `VREND.MNG` build tweaked the exact floats (the constant `0.9` is absent from +> the binary — the bright endpoints were likely rounded toward pure white). The +> *structure* (four gradients from a dark saturated colour to ~white, indexed by +> texel value) is unchanged. Use the table above; nudge the two `0.9`s to `1.0` +> if you want to match the shipped build more closely. + +--- + +## 4. How to implement it in your renderer + +The recolour must happen **at texturing time in the scene pass**, on the sampled +texel — *not* in a post/present pass on the final pixel, because you need the +raw texel value, not the lit result. + +Per textured fragment, when pvision is active: + +```glsl +// u_pv : 1 when IR/predator vision is on +// u_ramp0/1 : the two RGB endpoints of THIS surface's ramp (from ramp_entry) +if (u_pv == 1) { + float value = clamp(dot(texel.rgb, vec3(0.299, 0.587, 0.114)), 0.0, 1.0); + frag_rgb = mix(u_ramp0, u_ramp1, value); +} +// else: normal shading (frag_rgb = texel * lighting, etc.) +``` + +Wiring notes: + +1. **Ramp selection = the material's `ramp_entry` (0–3).** Set `u_ramp0`/`u_ramp1` + from `PV_RAMP[ramp_entry]` per draw call. If your pipeline doesn't yet track + `ramp_entry`, pick a **stable** ramp per surface (e.g. `hash(material_id) & 3`) + so different objects get different families — this reproduces the multicolor + look until you can plumb the real per-material index. +2. **Apply to all textured world geometry** — mechs, terrain, buildings, sky. +3. **Untextured surfaces:** use the shaded base colour's luminance as the value + (there's no texel), or leave them as-is; they're a minor case. +4. **HUD / MFDs are separate** and must *not* be ramped — they're a different + display path (the cockpit's mono/color panels), not the out-the-window scene. +5. **Fog is orthogonal.** The ramp mode is a texture-colour stage; it does not + touch the fog/haze stage. Do **not** disable fog for pvision unless you get + specific evidence otherwise. (We initially guessed "see through fog" and it + was wrong — the mode is purely about colour.) +6. **DAC gamma** (the board applies ~`pow(c, 1/1.25)` at scan-out) is unrelated + to pvision; keep whatever gamma you already do. + +### Host trigger + +If you emulate/interpret the wire, treat the effect action carrying `type == -1` +as "pvision on" and `type == -2` as "off"; `type >= 0` is a real explosion. +If you drive rendering directly from game state, hook `DPLTogglePVision`. + +--- + +## 5. What NOT to do (things we tried that are wrong) + +- ❌ Grayscale squash of the frame. +- ❌ False-colour heat/green/amber palette applied to **scene** luminance. +- ❌ Recolouring in a present/post pass using final-pixel luminance. +- ❌ Disabling fog as part of the effect. +- ❌ Expecting the game to send colours — it sends only the on/off flag. + +The correct model is the four-ramp, per-material, **texel-value** mapping above. + +--- + +## 6. How to verify against the originals + +Everything here is checkable in the VelociRender materials: + +- Host trigger: `MUNGA_L4/L4VIDEO.CPP` `DPLTogglePVision` (BT and RP both link + the same `DPLRenderer`). +- Ramp mechanism & modes: `DPL3/VRENDER/PXPL5SUP/EOF.C` ("lerp texture ramp", + `dvpx_eoftexmode`), `DPL3/VRENDER/DNC.C::luminize`, + `DPL3/VRENDER/DPLTYPES.H` (`dpl_RAMP`, `dpl_draw_luminance 0x04`). +- Ramp colours: `DPL3/VRENDER/VR_DRAW.C` (the four `setRampEntry` calls at + render init). +- Firmware (if you want ground truth from the shipped board image): in + `RPLIVE/VREND.MNG` / `BTLIVE/VREND.MNG` the effect handler is reached via the + wire-action jump table; it reads `msg[0]` as `type`, tests sign, and branches + `type==-1`/`type==-2`. Code loads at ~`0xf0400000`, globals low (~`0x10000`). + (Reversing that binary needs an i860 disassembler; the source above is easier.) + +If your Claude has the shared dump, those paths are under `sda4/DPL3/VRENDER/`. +The Tesla preservation project (this repo) is the authoritative source for these +findings. diff --git a/emulator/firmware-decomp/README.md b/emulator/firmware-decomp/README.md new file mode 100644 index 0000000..f73fc7a --- /dev/null +++ b/emulator/firmware-decomp/README.md @@ -0,0 +1,157 @@ +# VelociRender i860 firmware — decompilation reference + +Purpose: make **this** project the authoritative answer for how the Division / +VelociRender board firmware behaves (e.g. what IR/"predator" vision actually +does). Started 2026-07-14. + +## Boot architecture (what gets loaded, and how) + +The i860 render board is booted **transputer-style (boot-from-link)** every +cold start — see `sda4/DPL3/TESTVR.BAT`: + +``` +test /tranny vrendmon.btl /i860 vrender.mng /link_A 3 /device 0x150 /video NTSC /n_860s 1 +``` + +- `/tranny vrendmon.btl` — the bootstrap **monitor** (~85 KB). Loaded first + over the link while the i860 is held in boot-from-link. +- `/i860 vrender.mng` — the actual **renderer application** (~380 KB; + `RPLIVE/VREND.MNG`, `BTLIVE/VREND.MNG`). Produced by `I8602MNG.EXE` from the + linked COFF. **This is where the renderer logic lives — including pvision.** + The `.BTL` is only the loader. + +Load is **blind, every boot, no version check** — the board is reset (`\x00RSET` +strobe on port 0x160) and the host streams the image as wire actions +`code860`/`data860`/`bss860`/`args860`/`hspcode`; a reset board can't answer a +presence/version query, so the push is unconditional. `dpl_VERSION`'s +`firmware_*_version` fields are read *after* boot, for reporting only. (Same +model as the sound cards.) ~465 KB/boot; contributes to boot time, worse in +emulation (trapped link I/O + the bridge's fixed 0.5 s idle handshake). + +## What's here + +- **`coff860.py`** — validated i860 COFF (magic `0x014d`) reader: file header, + sections, symbol table. Names verified against the AS860/PGI source + (`_SemWait`, `_nameToAddress`, `_createBang`, …). Usage: + `python coff860.py "…/VRENDER/*.O"`. +- **`FIRMWARE-SYMBOLS.txt`** — full symbol/section map of the DPL3 VRENDER + objects (32 modules, 633 defined symbols). The function inventory of the + renderer, straight from the object files' symbol tables. + +The `.O` objects live in the git-ignored dump (`sda4/DPL3/VRENDER/*.O`); they +are the **DPL3 SDK vintage (1994–95)** — same era as the C/asm source we have, +so they *predate the pvision feature*. Their value: (1) they validate the +toolchain/format, (2) they name the board-side machinery, and (3) they are the +**signature library** for naming functions in the stripped, newer `VREND.MNG`. + +## pvision — where the answer is, and the anchors we have + +The game only flips a switch (`L4VIDEO.CPP::DPLTogglePVision` sends a special +"explosion" effect at the origin, `type = -1` ON / `-2` OFF — no colour). So +the behaviour is board-side. Operator's working model: **grayscale squash + +fog OFF**, not a false-colour palette. + +Confirmed anchors from the symbol map, both in `EOF.O` (end-of-frame / DAC): + +- Fog machinery lives in board globals: `_eof_doFOG`, `_eof_FOG_rval/gval/bval`, + `_eof_FOG_near/far`, `_eof_backR/G/B`, `_back_colour`. A "fog off" in pvision + = clearing `_eof_doFOG`. +- Colour/DAC path: `_tblcpy`, `_configEMCs`, `_send_em`, and the "texture + colour map table (8×32-bit)" hacked per-frame in `EOF.C`. +- `DNC.C::luminize` exists but is a **texture-MIP** helper (24-bit texel → + 6-bit luminance), *not* the screen grayscale — a near-miss, noted so nobody + re-chases it. + +The effect dispatch is `VR_REMOT.O::_remote_velocirender` → the SFX handlers +(`SFX.O`, `_PAZsfx`). In our source vintage `PAZsfx` only knows explosion codes +0/1/2 (no −1/−2), confirming pvision is newer code — i.e. **only in +`VREND.MNG`**, which has no symbols. + +## Progress: the i860 disassembler (scope = FULL annotated, operator-chosen) + +No objdump/Ghidra i860 support exists, so we build the decoder ourselves, +validated against the AS860 `.S`↔`.O` pairs (exact ground truth). + +- [x] **Word format cracked & CONFIRMED** — REG/CTRL layout, control-reg + numbers, from `CONTROL.O` (see `i860-encoding.md`). +- [x] **Primary opcode map validated** — `derive860.py` aligns `.S`↔`.O` by + proving every label offset against the COFF symbol table, then harvests + `op6→mnemonic`. Clean data matches the published i860 map. Table in + `i860-encoding.md`. +- [x] **Decoder built & validated** — `dis860.py`; 98% base-mnemonic match on + the clean pair (XFLGHTPR 220/224). Residual misses are `.S`/`.O` drift in + TRISTRIP, not decode errors, plus 4 cosmetic FP graphics-op variant names. +- [x] **Disassembler proven on real firmware** — `dis860.py --list VREND.MNG + 0xe940` decodes `_velocirender_statistics` as `adds 0x1,r0,r29 … bri r1` + (loads 1, returns) — matching what our virtual board replies for that wire + command (`vrboard do_statistics → 1`). Independent end-to-end confirmation. +- [~] **Name functions in `VREND.MNG`** — `sigmatch860.py` (reloc-invariant, + tolerant). Anchors landed (velocirender_statistics, reg_dump, + tri_zb_d_s_texm, dpl_init_light…) but the only objects we have are 1994 + DPL3/VRENDER vs the 1996 `.MNG`, so C code has drifted and dense naming + isn't possible from signatures alone. The stable hand-asm rasterizers/math + match; navigate the rest from these anchors + the wire dispatch. + - **Load format (reversed):** header = 3 LE size words at offset 0 + (`.text`=0x39ec0, `.data`=0x1e940, `.bss`=0x4b40); raw `.text` begins at + file offset **0x0c** (`0xf0400000` = entry veneer); `.data` follows; + a tiny section-name stub (`.text`/`.data`/`.bss`) trails — **no symbol + table** (stripped image, so signatures are required). + - Signatures must be **relocation-invariant**: mask the disp/immediate + fields of call/br/ld/st-with-address before matching `.O` code against + the linked `.text`. +- [ ] **Annotate** — whole-image reference; then walk the effect path + (`_remote_velocirender` → SFX) to the `type == -1` branch and read what + it does to `_eof_doFOG` and the DAC/colour map, settling pvision. + +## The endgame: preservation, not interpretation + +dpl3-revive (the GL bridge) *interprets* the wire — a modern reconstruction of +what the protocol means. Valuable for a playable pod, but it is our reading of +the hardware. The archival-correct path is to **run the original `VREND.MNG` in +an i860 emulator**: the board's own code builds the display lists, runs the real +`SCANLINE.SS` rasterizer, programs the DAC, and writes a framebuffer we capture +— **bit-exact to the hardware**. Every fidelity question (pvision, point +sampling, fog, gamma, filtering) then stops being "did we interpret this right?" +and becomes "the board's code did this." + +This disassembler is the front half of that emulator (same decode; add execution +semantics). Two tiers: +1. **Functional-accurate** — i860 core + FP + the board's memory-mapped + peripherals (link/DMA, the `_configEMCs` units, DAC, video timing, reversed + from the firmware + AS860 source). Yields bit-exact images. The big prize. +2. **Cycle-accurate** — model the i860 dual-issue pipeline / FP latency / delayed + branches. Adds timing fidelity (frame rate, the long-mission render decay). + Hardest; last. + +Keep the GL bridge as the fast/interactive path; the i860 emulator becomes the +high-fidelity **reference** — and the oracle that says whether the bridge is +right. + +## Live disassembly findings (RPLIVE/VREND.MNG) + +The wire-command **dispatch table** is decoded and mapped (per-action case block +`[mov r16,r4; call ; mov r8,r16; br exit]`; the dispatcher indexes by +action code). Confirmed against our virtual board: + +| action | handler | | action | handler | +|--|--|--|--|--| +| 9 draw_scene | 0xe340 | | 16 readpixels | 0xeb78 | +| 10 draw_scene_complete | 0xe4c0 | | 17 hspcode | 0xec80 | +| 11 list_add | 0xe6c0 | | 18 code860 | 0xece0 | +| 12 list_remove | 0xe780 | | 19 data860 | 0xed60 | +| 13 morph | 0xe810 | | 20 bss860 | 0xed90 | +| 14 version | 0xe888 | | 21 args860 | 0x56a0 | +| **15 statistics** | **0xe940** (returns 1 ✓) | | 22 set_geom_verts | 0x5710 | +| | | | 23 set_texmap_texels | 0x5738 | + +Higher actions (the effect/psfx family, incl. pvision `type -1/-2`): handlers at +**0xedd8, 0xee14**; action-27 case is the default/error path (loads a string ptr, +calls the log util 0x2c330). **`0xee14` = the effect handler** — prologue reads +`[msg+0]` into r29 (the effect **type**), `[msg+4]` into r30, logs via 0x4408, +then branches (`btne` @0xee4c → 0xee7c; calls 0xb688, 0x2c330). + +### NEXT (pvision, definitive): walk 0xee14's branches to the `type == -1/-2` +case and read what it writes — the board-side fog disable (`_eof_doFOG`) and any +DAC/colour-map change. That is the ground-truth answer to grayscale-squash+defog +vs a palette. (Anchor utils seen: 0x2c330 = most-called util/log, 0x4408 = +logger, 0xb120/0xb688 = effect helpers.) diff --git a/emulator/firmware-decomp/coff860.py b/emulator/firmware-decomp/coff860.py new file mode 100644 index 0000000..483f894 --- /dev/null +++ b/emulator/firmware-decomp/coff860.py @@ -0,0 +1,64 @@ +"""Minimal i860 COFF (magic 0x014d) reader: dump file header, sections, and the +symbol table (function/global names + section + value). Deterministic -- no +instruction decoding -- so it is a trustworthy reference map of the firmware +objects. Validates the format by checking the symbol names look like the known +AS860/PGI source (e.g. _createBang, _PAZsfx).""" +import sys, struct, glob, os + +def rd(b, off, fmt): return struct.unpack_from(fmt, b, off) + +def parse(path): + d = open(path, 'rb').read() + magic, nscns, timdat, symptr, nsyms, opthdr, flags = rd(d, 0, '0, class EXT/STAT), text-ish + funcs = [(nm, v, sc) for (nm, v, sn, ty, sc, na) in o['syms'] + if sn > 0 and sc in (2, 3) and not nm.startswith('.')] + print(f" -- {len(funcs)} defined EXT/STAT symbols:") + for nm, v, sc in sorted(funcs, key=lambda x: x[1]): + print(f" {v:#010x} {SCLASS.get(sc,sc):<5} {nm}") + +if __name__ == '__main__': + pats = sys.argv[1:] or [r'C:\VWE\TeslaRel410\sda4\DPL3\VRENDER\*.O'] + files = [] + for p in pats: files += glob.glob(p) + for f in sorted(files): + try: + report(parse(f)) + except Exception as e: + print(f"\n=== {f}: PARSE FAILED: {e} ===") diff --git a/emulator/firmware-decomp/derive860.py b/emulator/firmware-decomp/derive860.py new file mode 100644 index 0000000..7080535 --- /dev/null +++ b/emulator/firmware-decomp/derive860.py @@ -0,0 +1,100 @@ +"""Empirically derive the i860 opcode table from matched .S (PGI/AS860 assembly) +and .O (COFF machine code) pairs. Byte-accounting is proven correct by +cross-checking every .S label against its .O symbol offset; only then is the +op6->mnemonic harvest trusted.""" +import sys, struct, re, glob, os +sys.path.insert(0, r'C:\VWE\TeslaRel410\emulator\firmware-decomp') +from coff860 import parse + +DATASZ = {'.long':4, '.word':4, '.int':4, '.float':4, '.single':4, + '.double':8, '.short':2, '.half':2, '.byte':1, '.string':None} + +def parse_S(path): + """Return (labels{name:off}, instrs[(off,mnem,line)]) for the .text section.""" + labels, instrs = {}, [] + off = 0; sect = '.text'; in_text = True + for raw in open(path, encoding='latin1'): + line = raw.split('//', 1)[0].rstrip() + s = line.strip() + if not s: + continue + # label(s): possibly "name:" / "name::" possibly followed by an instr + while True: + m = re.match(r'^([A-Za-z_.$][\w.$]*)::?\s*(.*)$', s) + if m and (m.group(2) == '' or not m.group(2).startswith('.')) and ':' in s[:m.end(1)+2]: + # confirm it's really a label (colon right after the name) + after = s[m.end(1):] + if after.startswith('::') or after.startswith(':'): + if in_text: labels[m.group(1)] = off + s = after.lstrip(':').strip() + if not s: break + continue + break + if not s: + continue + if s.startswith('.'): + d = s.split()[0].lower() + if d in ('.text',): sect='.text'; in_text=True + elif d in ('.data','.bss','.section'): sect=d; in_text=False + elif d == '.align' and in_text: + n = int(s.split()[1]); off = (off + n - 1) & ~(n - 1) + elif d in DATASZ and in_text: + sz = DATASZ[d] + if sz is None: # .string -> count bytes incl NUL + txt = s[s.index('"')+1:s.rindex('"')] + off += len(txt.encode('latin1').decode('unicode_escape')) + 1 + else: + vals = s[len(d):].split(',') + off += sz * len([v for v in vals if v.strip()]) + # other directives: no size + continue + if in_text: + mnem = s.split()[0] + instrs.append((off, mnem, s)) + off += 4 + return labels, instrs + +def load_text(opath): + o = parse(opath); d = open(opath, 'rb').read() + text = syms = None + for (name, vaddr, size, scnptr, nreloc, sf) in o['sections']: + if name == '.text': text = d[scnptr:scnptr+size] + syms = {nm: v for (nm, v, sn, ty, sc, na) in o['syms'] if sn == 1 and sc in (2,3)} + return text, syms + +def derive(spath, opath, table): + labels, instrs = parse_S(spath) + text, syms = load_text(opath) + # cross-check label offsets vs symbol table + common = [k for k in labels if k in syms] + good = [k for k in common if labels[k] == syms[k]] + bad = [(k, labels[k], syms[k]) for k in common if labels[k] != syms[k]] + tag = os.path.basename(opath) + print(f"[{tag}] instrs={len(instrs)} textwords={len(text)//4} " + f"labels_ok={len(good)}/{len(common)}", end='') + if bad: + print(f" MISALIGNED e.g. {bad[:3]}") + return False + print(" ALIGNED") + for (off, mnem, line) in instrs: + if off + 4 > len(text): break + w = struct.unpack_from('> 26) & 0x3f + table.setdefault(op6, {}).setdefault(mnem, 0) + table[op6][mnem] += 1 + return True + +if __name__ == '__main__': + base = r'C:\VWE\TeslaRel410\sda4\DPL3\VRENDER\AS860' + pairs = [] + for s in glob.glob(os.path.join(base, '*.S')): + o = s[:-2] + '.O' + if os.path.exists(o): pairs.append((s, o)) + table = {} + for s, o in sorted(pairs): + try: derive(s, o, table) + except Exception as e: print(f"[{os.path.basename(o)}] ERROR {e}") + print("\n=== derived op6 -> mnemonic(count) ===") + for op6 in sorted(table): + items = sorted(table[op6].items(), key=lambda x: -x[1]) + print(f" {op6:#04x} ({op6:06b}): " + ", ".join(f"{m}×{c}" for m,c in items)) diff --git a/emulator/firmware-decomp/dis860.py b/emulator/firmware-decomp/dis860.py new file mode 100644 index 0000000..9bfb48f --- /dev/null +++ b/emulator/firmware-decomp/dis860.py @@ -0,0 +1,219 @@ +"""Intel i860 disassembler for the VelociRender firmware. + +Opcode/field assignments are those VALIDATED against our own AS860 .S<->.O pairs +(see i860-encoding.md / derive860.py); unknowns decode as `.word`. This is also +the front-end for an eventual i860 *emulator* that runs VREND.MNG directly. + +Run `python dis860.py --validate` to check the decoder against the matching +source/object pairs (per-instruction base-mnemonic agreement).""" +import sys, struct, os, glob + +REG = [f"r{i}" for i in range(32)] +FREG = [f"f{i}" for i in range(32)] +CTRL = {0: "fir", 1: "psr", 2: "dirbase", 3: "db", 4: "fsr", 5: "epsr"} + +def s16(v): return v - 0x10000 if v & 0x8000 else v +def s26(v): return v - 0x4000000 if v & 0x2000000 else v + +# ---- FP escape (op 0x12) sub-opcode[6:0] -> base name (validated where marked) ---- +FP_SUB = { + 0x00: "r2p1", 0x01: "r2pt", 0x02: "r2ap1", 0x04: "i2p1", 0x05: "i2pt", + 0x06: "i2ap1", 0x07: "i2apt", 0x08: "rat1p2", 0x09: "m12apm", + 0x0a: "ra1p2", 0x0b: "m12ttpa", 0x0c: "iat1p2", 0x0d: "m12tpm", + 0x0e: "ia1p2", 0x0f: "m12tpa", 0x1c: "mimt1s2", + 0x20: "fmul", 0x21: "fmlow", 0x22: "frcp", 0x23: "frsqr", + 0x24: "fmul3", 0x28: "pfmam", 0x2c: "pfmsm", + 0x30: "fadd", 0x31: "fsub", 0x32: "fix", 0x34: "fgt", 0x35: "feq", + 0x3a: "ftrunc", 0x40: "fxfr", 0x49: "fiadd", 0x4d: "fisub", + 0x50: "faddp", 0x51: "faddz", 0x57: "fzchk", 0x5a: "form", + 0x5b: "fzchkl", 0x5f: "fnop", +} +# precision suffix from result/source-precision bits (bit8=R result-prec, bit7=S src-prec) +def fp_suffix(w): + s = 'd' if (w >> 7) & 1 else 's' + r = 'd' if (w >> 8) & 1 else 's' + return f".{s}{r}" + +def dec_fp(w): + sub = w & 0x7f + p = (w >> 10) & 1 # P: pipelined + src2 = (w >> 21) & 0x1f; dest = (w >> 16) & 0x1f; src1 = (w >> 11) & 0x1f + base = FP_SUB.get(sub) + if base is None: + return (".fp?", f"{sub:#04x} {FREG[src1]},{FREG[src2]},{FREG[dest]}") + if base == "fxfr": # FP->int reg move + return ("fxfr", f"{FREG[src1]},{REG[dest]}") + if base == "fnop": + return ("fnop", "") + name = ("pf" if p else "f") + base[1:] if base[0] == 'f' else (("pf" if p else "") + base) + # graphics dual-ops (r2p1 etc.) keep their name; only f* get the pf/f prefix + if base[0] != 'f': + name = base + return (name + fp_suffix(w), f"{FREG[src1]},{FREG[src2]},{FREG[dest]}") + +# ---- primary decode ---- +# immediate-form rule: for load/store (0x00-0x0b) and arith/logic (0x20-0x3f), +# the ODD opcode of each pair is the 16-bit immediate/const form. +MEM = {0x00: "ld.s", 0x01: "ld.s", 0x04: "ld.l", 0x05: "ld.l", + 0x06: "st.l", 0x07: "st.l", 0x08: "fld.l", 0x09: "fld.l", + 0x0a: "fst.l", 0x0b: "fst.l"} +ARITH = {0x20: "addu", 0x22: "subu", 0x24: "adds", 0x26: "subs", + 0x28: "shl", 0x2a: "shr", 0x2c: "shrd", 0x2e: "shra", + 0x30: "and", 0x32: "andh", 0x34: "andnot", 0x36: "andnoth", + 0x38: "or", 0x3a: "orh", 0x3c: "xor", 0x3e: "xorh"} +CTRLB = {0x1a: "br", 0x1b: "call", 0x1c: "bc", 0x1d: "bc.t", + 0x1e: "bnc", 0x1f: "bnc.t"} + +def decode(w, addr): + op = (w >> 26) & 0x3f + src2 = (w >> 21) & 0x1f; dest = (w >> 16) & 0x1f; src1 = (w >> 11) & 0x1f + imm = w & 0xffff + # loads/stores + if op in MEM: + m = MEM[op] + off = s16(imm & 0xfffc) if 'l' in m else s16(imm) + if m.startswith('f'): + rd = FREG[dest] + else: + rd = REG[dest] + if m.startswith('st') or m.startswith('fst'): + return m, f"{rd},{off:#x}({REG[src2]})" + return m, f"{off:#x}({REG[src2]}),{rd}" + if op == 0x02: + return "ixfr", f"{REG[src1]},{FREG[dest]}" + if op == 0x0c: + return "ld.c", f"{CTRL.get(src2,src2)},{REG[dest]}" + if op == 0x0e: + return "st.c", f"{REG[src1]},{CTRL.get(src2,src2)}" + if op == 0x10: + return "bri", f"{REG[src1]}" + if op == 0x11: + return "trap", f"{REG[src1]},{REG[src2]},{REG[dest]}" + if op == 0x12: + return dec_fp(w) + if op == 0x13: + # core escape: sub-op in low bits; calli common + if (w & 0x7ff) == 0 or True: + return "calli", f"{REG[src1]}" + if op in (0x14, 0x15, 0x16, 0x17): + base = "btne" if op < 0x16 else "bte" + broff = s16(((dest << 11) | (w & 0x7ff)) & 0xffff) + tgt = addr + 4 + broff * 4 + # odd opcode = 5-bit-const form (i860 rule; e.g. `btne 0x0,r19` = 0x15) + s1 = f"{src1:#x}" if op & 1 else f"{REG[src1]}" + return base, f"{s1},{REG[src2]},{tgt:#010x}" + if op in CTRLB: + tgt = addr + 4 + s26(w & 0x03ffffff) * 4 + if CTRLB[op] in ("br", "call"): + return CTRLB[op], f"{tgt:#010x}" + return CTRLB[op], f"{tgt:#010x}" + if op in (0x18, 0x19): + off = s16(imm & 0xfff8) + return "pfld.d", f"{off:#x}({REG[src2]}),{FREG[dest]}" + if (op & 0x3e) in ARITH: + m = ARITH[op & 0x3e] + if op & 1: # immediate form + v = s16(imm) + return m, f"{v:#x},{REG[src2]},{REG[dest]}" + # register form; recognise pseudo-ops (mov/nop = shl r0) + if m == "shl" and src1 == 0: + if src2 == 0 and dest == 0: + return "nop", "" + return "mov", f"{REG[src2]},{REG[dest]}" + if m == "or" and src1 == 0 and src2 == 0: + return "mov", f"r0,{REG[dest]}" + return m, f"{REG[src1]},{REG[src2]},{REG[dest]}" + return ".word", f"{w:#010x}" + +# ---------------- validation ---------------- +def _norm_ops(s): + """normalize an operand string for comparison: strip spaces, lower, and + canonicalize hex/dec immediates.""" + import re + s = s.lower().replace(' ', '') + def canon(m): + v = int(m.group(0), 0) + return str(v) + return re.sub(r'-?0x[0-9a-f]+|-?\b\d+\b', canon, s) + +def validate(): + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + import derive860 as D + base = r'C:\VWE\TeslaRel410\sda4\DPL3\VRENDER\AS860' + # operand check only for ops whose operands are NOT link-time relocated + OPCHECK = ('ld.', 'st.', 'fld.', 'fst.', 'adds', 'addu', 'subs', 'subu', + 'and', 'or', 'xor', 'shl', 'shr', 'shra', 'mov', 'ixfr', 'bri') + for s in sorted(glob.glob(os.path.join(base, '*.S'))): + o = s[:-2] + '.O' + if not os.path.exists(o): + continue + labels, instrs = D.parse_S(s) + text, syms = D.load_text(o) + common = [k for k in labels if k in syms] + aligned = not any(labels[k] != syms[k] for k in common) + ok = tot = 0; oper_ok = oper_tot = 0; misses = []; omiss = [] + for (off, mnem, line) in instrs: + if off + 4 > len(text): + break + w = struct.unpack_from(' len(text): break + w = struct.unpack_from(' + a = [x for x in sys.argv if not x.startswith('--')][1:] + mng = a[0]; start = int(a[1], 0); count = int(a[2]) if len(a) > 2 else 32 + print(disasm_range(load_mng(mng), start, count)) + else: + print("usage: dis860.py --validate | --list ") diff --git a/emulator/firmware-decomp/hardware-photos/01-DBIO152-io-mux-board.jpeg b/emulator/firmware-decomp/hardware-photos/01-DBIO152-io-mux-board.jpeg new file mode 100644 index 0000000..82ed49e Binary files /dev/null and b/emulator/firmware-decomp/hardware-photos/01-DBIO152-io-mux-board.jpeg differ diff --git a/emulator/firmware-decomp/hardware-photos/02-pma-eisa-host-interface.jpeg b/emulator/firmware-decomp/hardware-photos/02-pma-eisa-host-interface.jpeg new file mode 100644 index 0000000..64a2921 Binary files /dev/null and b/emulator/firmware-decomp/hardware-photos/02-pma-eisa-host-interface.jpeg differ diff --git a/emulator/firmware-decomp/hardware-photos/03-backplane-ramtron-edram.jpeg b/emulator/firmware-decomp/hardware-photos/03-backplane-ramtron-edram.jpeg new file mode 100644 index 0000000..e478e4f Binary files /dev/null and b/emulator/firmware-decomp/hardware-photos/03-backplane-ramtron-edram.jpeg differ diff --git a/emulator/firmware-decomp/hardware-photos/04-i860-xp50-geometry-board.jpeg b/emulator/firmware-decomp/hardware-photos/04-i860-xp50-geometry-board.jpeg new file mode 100644 index 0000000..f3fe0d2 Binary files /dev/null and b/emulator/firmware-decomp/hardware-photos/04-i860-xp50-geometry-board.jpeg differ diff --git a/emulator/firmware-decomp/hardware-photos/05-pixelplanes-rev02-xc3090.jpeg b/emulator/firmware-decomp/hardware-photos/05-pixelplanes-rev02-xc3090.jpeg new file mode 100644 index 0000000..33d8ee0 Binary files /dev/null and b/emulator/firmware-decomp/hardware-photos/05-pixelplanes-rev02-xc3090.jpeg differ diff --git a/emulator/firmware-decomp/hardware-photos/06-DBE0151-video-output-ntsc.jpeg b/emulator/firmware-decomp/hardware-photos/06-DBE0151-video-output-ntsc.jpeg new file mode 100644 index 0000000..f3da258 Binary files /dev/null and b/emulator/firmware-decomp/hardware-photos/06-DBE0151-video-output-ntsc.jpeg differ diff --git a/emulator/firmware-decomp/hardware-photos/07-pxpl-igc52-with-plane-cards.jpeg b/emulator/firmware-decomp/hardware-photos/07-pxpl-igc52-with-plane-cards.jpeg new file mode 100644 index 0000000..5078204 Binary files /dev/null and b/emulator/firmware-decomp/hardware-photos/07-pxpl-igc52-with-plane-cards.jpeg differ diff --git a/emulator/firmware-decomp/hardware-photos/08-pxpl-igc52-adv7150-ramdac.jpeg b/emulator/firmware-decomp/hardware-photos/08-pxpl-igc52-adv7150-ramdac.jpeg new file mode 100644 index 0000000..37e4418 Binary files /dev/null and b/emulator/firmware-decomp/hardware-photos/08-pxpl-igc52-adv7150-ramdac.jpeg differ diff --git a/emulator/firmware-decomp/hardware-photos/09-stack-edge-on.jpeg b/emulator/firmware-decomp/hardware-photos/09-stack-edge-on.jpeg new file mode 100644 index 0000000..3de95b2 Binary files /dev/null and b/emulator/firmware-decomp/hardware-photos/09-stack-edge-on.jpeg differ diff --git a/emulator/firmware-decomp/hardware-photos/10-both-boards-face-up.jpeg b/emulator/firmware-decomp/hardware-photos/10-both-boards-face-up.jpeg new file mode 100644 index 0000000..b56673e Binary files /dev/null and b/emulator/firmware-decomp/hardware-photos/10-both-boards-face-up.jpeg differ diff --git a/emulator/firmware-decomp/hardware-photos/11-both-boards-face-down.jpeg b/emulator/firmware-decomp/hardware-photos/11-both-boards-face-down.jpeg new file mode 100644 index 0000000..38fc4c9 Binary files /dev/null and b/emulator/firmware-decomp/hardware-photos/11-both-boards-face-down.jpeg differ diff --git a/emulator/firmware-decomp/hardware-photos/12-modules-faceup-ramsimm-and-emc-daughters.jpeg b/emulator/firmware-decomp/hardware-photos/12-modules-faceup-ramsimm-and-emc-daughters.jpeg new file mode 100644 index 0000000..2cf31e2 Binary files /dev/null and b/emulator/firmware-decomp/hardware-photos/12-modules-faceup-ramsimm-and-emc-daughters.jpeg differ diff --git a/emulator/firmware-decomp/hardware-photos/13-modules-facedown-emc-daughters.jpeg b/emulator/firmware-decomp/hardware-photos/13-modules-facedown-emc-daughters.jpeg new file mode 100644 index 0000000..74d8f51 Binary files /dev/null and b/emulator/firmware-decomp/hardware-photos/13-modules-facedown-emc-daughters.jpeg differ diff --git a/emulator/firmware-decomp/hardware-photos/14-VDB-front-mach130-3xBt477.jpeg b/emulator/firmware-decomp/hardware-photos/14-VDB-front-mach130-3xBt477.jpeg new file mode 100644 index 0000000..1a53058 Binary files /dev/null and b/emulator/firmware-decomp/hardware-photos/14-VDB-front-mach130-3xBt477.jpeg differ diff --git a/emulator/firmware-decomp/hardware-photos/15-VDB-back-solder-side.jpeg b/emulator/firmware-decomp/hardware-photos/15-VDB-back-solder-side.jpeg new file mode 100644 index 0000000..89a80f6 Binary files /dev/null and b/emulator/firmware-decomp/hardware-photos/15-VDB-back-solder-side.jpeg differ diff --git a/emulator/firmware-decomp/i860-encoding.md b/emulator/firmware-decomp/i860-encoding.md new file mode 100644 index 0000000..155d665 --- /dev/null +++ b/emulator/firmware-decomp/i860-encoding.md @@ -0,0 +1,83 @@ +# i860 instruction encoding — validated against our own object files + +Every fact here is cross-checked against the AS860 `*.S` (PGI/AS860 assembly) ↔ +`*.O` (COFF machine code) pairs in `sda4/DPL3/VRENDER/AS860/`, using +`derive860.py`. Method: reproduce every `.text` byte offset from the assembly and +confirm each computed label offset equals its COFF symbol value; only then is the +`op6 → mnemonic` harvest trusted. 3 pairs align perfectly (CONTROL 2/2, TRISTRIP +4/4, XFLGHTPR 20/20 labels). + +## Word format (little-endian, 32-bit) — CONFIRMED + +REG-format: + +``` + 31 26 25 21 20 16 15 11 10 0 ++--------+-------+-------+-------+-----------+ +| opcode | src2 | dest | src1 | (unused) | register src1 ++--------+-------+-------+-------+-----------+ +| opcode | src2 | dest | imm16 | 16-bit immediate ++--------+-------+-------+-------------------+ +``` + +CTRL-format (br/call/bc/bnc/bt/bnt): `opcode[31:26] | disp26` (sign-extended, +word-scaled, PC-relative to the *next* instruction; i860 has one delay slot). + +Verified from `CONTROL.O` (4 instrs, exact): +- `bri r1` = `0x40000800` → op `0x10`, src1(15:11)=1 ✓ +- `ld.c fsr,r16` = `0x30900000` → op `0x0c`, src2(25:21)=4=fsr, dest(20:16)=16 ✓ +- `st.c r16,fsr` = `0x38808000` → op `0x0e`, src2=4=fsr, src1(15:11)=16 ✓ + +Control registers: `fir=0 psr=1 dirbase=2 db=3 fsr=4 epsr=5` (fsr=4 confirmed). + +## Primary opcode map (bits 31:26) + +CONFIRMED = dominant mnemonic seen in cleanly-aligned ground-truth data; +otherwise from the published i860 ISA and consistent with what we saw. + +| op6 | mnemonic | src | notes | +|----|----|----|----| +| 0x02 | ixfr | | integer→FP reg transfer (CONFIRMED) | +| 0x08/09 | fld.{l,d,q} | | FP load (+auto-inc); 0x09 CONFIRMED | +| 0x0a/0b | fst / pst | | FP store; 0x0b fst CONFIRMED | +| 0x0c | ld.c | | load control reg (CONFIRMED) | +| 0x0e | st.c | | store control reg (CONFIRMED) | +| 0x10 | bri | | branch indirect (CONFIRMED) | +| 0x11 | trap | | | +| 0x12 | **FP escape** | | fadd/fmul/fsub/pf*/fxfr/frcp/… sub-op in low bits (CONFIRMED) | +| 0x13 | core escape | | calli, etc. (calli CONFIRMED) | +| 0x14/15 | btne | imm/reg | 0x15 CONFIRMED | +| 0x16/17 | bte | imm/reg | 0x17 CONFIRMED | +| 0x18/19 | pfld.{l,d,q} | | pipelined FP load | +| 0x1a | br | | CTRL (CONFIRMED) | +| 0x1b | call | | CTRL (CONFIRMED) | +| 0x1c | bc | | branch on CC (CONFIRMED) | +| 0x1d | bc.t | | + taken hint (CONFIRMED) | +| 0x1e | bnc | | branch on !CC (CONFIRMED) | +| 0x1f | bnc.t | | + taken hint (CONFIRMED) | +| 0x20/21 | addu | imm/reg | | +| 0x22/23 | subu | imm/reg | | +| 0x24/25 | adds | reg/imm | CONFIRMED | +| 0x26/27 | subs | reg/imm | CONFIRMED | +| 0x28/29 | shl | | shift left | +| 0x2a/2b | shr | | shift right | +| 0x2c | shrd | | | +| 0x2d | bla | | | +| 0x2e/2f | shra | | 0x2f CONFIRMED | +| 0x30/31 | and | reg/imm | | +| 0x32/33 | andh | | high half | +| 0x34/35 | andnot | | | +| 0x36/37 | andnoth | | | +| 0x38/39 | or | | | +| 0x3a/3b | orh | | 0x3b CONFIRMED | +| 0x3c/3d | xor | | 0x3c CONFIRMED | +| 0x3e/3f | xorh | | | + +Loads/stores `ld.{b,s,l}` / `st.{b,s,l}` sit in the low opcodes (0x00–0x07); +exact split (size/sign bits) to be pinned when the full decoder byte-exactly +reproduces the aligned pairs. Pseudo-ops seen in source: `mov` (= `or`/`shl` +with r0), `nop`, `fnop`. + +## Tools +- `coff860.py` — COFF reader (symbols/sections). +- `derive860.py` — the .S↔.O aligner that produced/validates the above. diff --git a/emulator/firmware-decomp/sigmatch860.py b/emulator/firmware-decomp/sigmatch860.py new file mode 100644 index 0000000..cc6e63a --- /dev/null +++ b/emulator/firmware-decomp/sigmatch860.py @@ -0,0 +1,131 @@ +"""Name functions in the stripped VREND.MNG by matching relocation-invariant +signatures harvested from the DPL3 VRENDER .O object files. + +For each .O .text function we mask (wildcard) every 32-bit word that a COFF +relocation touches -- those hold call targets / data addresses that the linker +rewrites -- and keep the fixed opcode+register skeleton. A function that carried +over into VREND.MNG (even relocated to a new address) still matches its +skeleton. Word granularity throughout (i860 is fixed 4-byte, word-aligned).""" +import sys, struct, os, glob + +def parse_coff(path): + d = open(path, 'rb').read() + magic, nscns, timdat, symptr, nsyms, opthdr, flags = struct.unpack_from(' len(text): continue + good = 0 + for i in concrete: + if struct.unpack_from(' best[1]: + second = best[1]; best = (start*4, frac) + elif frac > second: + second = frac + if best[0] is not None and best[1] >= thresh: + return (best[0], best[1], best[1] > second + 0.05) + return None + +def main(): + mng = sys.argv[1] if len(sys.argv) > 1 else r'C:\VWE\TeslaRel410\sda4\RPLIVE\VREND.MNG' + odir = r'C:\VWE\TeslaRel410\sda4\DPL3\VRENDER' + text, tsize = load_mng_text(mng) + idx = build_index(text) + print(f"VREND.MNG .text = {tsize:#x} bytes ({tsize//4} words); {os.path.basename(mng)}") + named = {} # addr -> (name, frac) + nsig = 0 + for opath in sorted(glob.glob(os.path.join(odir, '*.O')) + + glob.glob(os.path.join(odir, 'PXPL5SUP', '*.O'))): + for nm, skel in sig_for_functions(opath): + nsig += 1 + r = match(skel, text, idx) + if r is None: continue + addr, frac, uniq = r + if not uniq: nm = nm + "?" + if addr not in named or frac > named[addr][1]: + named[addr] = (nm, frac) + strong = sum(1 for (n, f) in named.values() if f >= 0.95) + print(f"signatures tried={nsig}, named={len(named)} " + f"(exact/near-exact >=0.95: {strong})") + for addr in sorted(named): + nm, frac = named[addr] + print(f" text+{addr:#08x} (mem {0x0c+addr:#08x}) {frac:0.2f} {nm}") + +if __name__ == '__main__': + main() diff --git a/emulator/render-bridge/COCKPIT-CAGE-NOTES.md b/emulator/render-bridge/COCKPIT-CAGE-NOTES.md index a12e940..f267e25 100644 --- a/emulator/render-bridge/COCKPIT-CAGE-NOTES.md +++ b/emulator/render-bridge/COCKPIT-CAGE-NOTES.md @@ -498,3 +498,90 @@ first appear in the 0x1f stream -- next session, have the pilot narrate ("holding hat-left NOW") and read the log; if nothing appears during holds, compare against a 7/07-style run (gauge_arena_sound.conf, no net stack) to isolate what enables the eyepoint emission. + +--- + +## 2026-07-14: Where the IR / thermal palette actually lives (source hunt) + +Operator question: does the game tell the Division card *which* palette to use +for IR/predator vision, or is the card's firmware around to decompile? Answer: +**the game specifies no palette at all -- it just flips a switch; the palette +is baked into the i860 board firmware.** + +Chain of evidence (all from THIS project's primary sources -- OUR dump is the +authority; BT411 is a downstream reconstruction *of* this and is NOT cited): + +1. **Our own game source: `CODE/RP/MUNGA_L4/L4VIDEO.CPP:5535`, + `DPLRenderer::DPLTogglePVision()`** -- the real, shipped, non-stubbed + implementation (the engine `DPLRenderer` is shared by BT and RP). Comment + reads `toggles the state of dpl "predator" vision`. Verbatim: + ```cpp + static Logical pvision_on = 0; + dpl_EXPLOSION_EFFECT_INFO sfx_info; + sfx_info.x = sfx_info.y = sfx_info.z = 0; + if ((pvision_on ^= 1) != 0) { ... sfx_info.type = -1; } // pvision ON + else { ... sfx_info.type = -2; } // pvision OFF + dpl_Effect(dpl_effect_type_explosion, NULL, &sfx_info); + ``` + Pvision is sent as a **special "explosion" effect at the origin with + `type = -1 / -2`**. The struct (`CODE/RP/MUNGA_L4/libDPL/dpl/DPLTYPES.H:278`, + `{float x,y,z; int32 type; dpl_TEXTURE *tex;}`) has **no colour field** -- + so the game hands the board zero palette information. (This also corrects an + earlier disasm misread that thought an RGB triple + matrix was being sent; + there is none.) + +2. **DPL3 SDK source (`sda4/DPL3`, `sda4/DPL3RLS`).** The board effect + dispatcher `PAZsfx()` (`VRENDER/SFX.C:751`) only handles explosion codes + `0/1/2`; there is **no `-1/-2` branch and no DAC/palette flip anywhere in + this source**. This VRENDER source is the 1994-95 vintage (DPL3RLS build + dirs `VR_40826`..`VR_50329`) -- it **predates the pvision feature**. + +3. **The exe itself.** Same as (1): the on/off flag is all that crosses the + wire. Nothing selects a colour. + +So the palette is the **board's built-in response to explosion type -1/-2**, +i.e. it lives in the i860 firmware, not in the game, the SDK, or the exe. + +### Is the firmware around / decompilable? Yes. + +The board-load images are in the dump as `VRENDMON.BTL` (plus the `VREND2..9` +overlays per game). Dated build tree `sda4/VREND/` runs **Oct 1995 -> Jun 1996** +(`51019` ... `60623EDO`), newer than the source we have; the production images +are `sda4/BTLIVE/` and `sda4/RPLIVE/`. The newest (`VREND/60623EDO/ +VRENDMON.BTL`, 13 Jun 1996, 85 KB) is **raw i860 machine code** -- opens +`f0 b4 d1 d1 ...`, no COFF/ELF wrapper, no symbol/string table (grep for +vision/pred/therm/heat/palette = nothing; strings are stripped/packed). + +Bottom line for the crew: +- The **exact** IR palette can only come from **reversing the i860 + `VRENDMON.BTL`** (Ghidra has an i860 module; same workflow the BT411 team + already runs). Target: the colour/DAC transform gated by the effect + `type == -1`. It's a real RE effort (stripped code) but the binary is here. +- Until then our `heat` palette is an educated interim; the IR-vision-options + sheet (heat/green/amber/mono) is the fast path if a crew member simply + *recognises* the original look. +- Empirical cross-check: hitting the cockpit IR button in the live sim already + drives `board.pvision` through action `0x1b` mode `-1/-2` -- exactly the + explosion-type encoding above -- so the mechanism is confirmed end to end; + only the colour LUT is unknown. + +### Revised model (operator, 2026-07-14): grayscale squash + fog off, not a palette + +Operator's read: predator vision is probably **not** a false-colour palette +swap -- it's a **per-pixel squash to grayscale plus fog turned OFF** (so +targets read through night/weather). This fits everything: the game sends no +colour, and "see through the murk" is the same family of trick as the +searchlight (which turned out to be a view-fog push-back, not a light cone). +`heat`/`green`/`amber` were only ever an interim guess at the *name* +"predator". + +Renderer changed to match (`dpl3-revive/patha/vrview_gl.py`): +- default `VRVIEW_PVISION_PALETTE` flipped `heat` -> `mono` (grayscale squash). +- when pvision is active, the **scene pass forces `fog_on = 0`** (a present- + pass recolour alone cannot undo fog already baked into the pixels). +- green/amber/heat ramps retained purely as opt-in crew A/B (`VRVIEW_ + PVISION_PALETTE=green|amber|heat`). + +Still a hypothesis pending a live look + crew memory; the ground-truth remains +recoverable by reversing the i860 `VRENDMON.BTL` (colour/DAC transform gated by +effect `type == -1`). diff --git a/emulator/render-bridge/gauge_arena_nightclear_pipe.conf b/emulator/render-bridge/gauge_arena_nightclear_pipe.conf new file mode 100644 index 0000000..35748ea --- /dev/null +++ b/emulator/render-bridge/gauge_arena_nightclear_pipe.conf @@ -0,0 +1,78 @@ +[sdl] +output=opengl +# higher,higher not highest: HIGH_PRIORITY_CLASS starved the host desktop; +# with the retry patches a rare dropout self-recovers (see gauge_rio.conf). +priority=higher,higher +[dosbox] +memsize=32 +machine=svga_s3 +[cpu] +core=dynamic +cputype=pentium +cycles=max +[sblaster] +sbtype=sb16 +sbbase=220 +irq=5 +dma=1 +hdma=5 +[mixer] +# match the EMU8000s' native rate (no resample) and buffer ~60ms so brief +# emulation-thread stalls (RIO retry recovery) don't audibly chop +rate=44100 +blocksize=1024 +prebuffer=60 +[ne2000] +# real pods loaded the ODI stack in AUTOEXEC before ANY game launch -- +# weapons-fire test: bare -egg run WITH the packet stack resident (the game's +# WATTCP identity is 200.0.0.113 from REL410\BT\WATTCP.CFG). nicbase 340: +# 300 clashes with the VDB and blanks heads. backend=slirp: THIS build lacks +# pcap ("Backend not supported"); slirp = user-mode NAT, no LAN peers, but +# the packet driver functions and accepts sends -- enough for a solo fire +# test. For pod<->console runs use a pcap-enabled build (net_full.conf). +ne2000=true +nicbase=340 +nicirq=3 +backend=slirp +[serial] +# RIO on COM1 with the low-latency options (rxpollus/rxburst) so the board's +# few-ms ACK deadline is met; plasma display on COM2 (real pod has both). +serial1=namedpipe pipe:vrio rxpollus:100 rxburst:16 +serial2=namedpipe pipe:vplasma +[autoexec] +mount c "C:\VWE\TeslaRel410\ALPHA_1" +mount d "C:\VWE\TeslaRel410\emulator\net-boot" +d: +echo === loading NE2000 packet-driver stack (as production AUTOEXEC did) === +d:\lsl +d:\ne2000 +d:\odipkt +c: +cd \REL410\BT +set VIDEOFORMAT=svga +rem production pod card init (PARAMETR.BAT:181-186): DIAGNOSE + AWEUTIL per +rem card -- AWEUTIL /S does the EMU8000 bring-up and DRAM detect the HMI SOS +rem driver relies on; skipping it left the cards uninitialized (silent). +rem aweutil /s SKIPPED for now: it verifies the AWE32 GM ROM, which the +rem emulated cards lack (hangs in a retry loop) -- restore once the ROM is +rem dumped from a real card. diagnose /s kept (passes, sets mixer config). +set BLASTER=A220 I5 D1 H5 P330 T6 +c:\sb16\diagnose /s +set BLASTER=A240 I7 D3 H6 P300 T6 +c:\sb16\diagnose /s +set BLASTER=A220 I5 D1 H5 P330 T6 +set TEMP=c:\ +rem arena1 city mission (TESTARN.EGG: map=arena1, time=day), RIO attached, +rem bare -egg launch (no netnub) = the user's real-world test-egg setup. +set HEAPSIZE=15000000 +set L4GAUGE=640x480x16 +call setenv.bat r f s p +32rtm.exe -x +rem stdout -> log: this optimized build still emits DEBUG_STREAM lines (the +rem RIO retry spam proved it) -- capture the weapon fire-refusal reason. +rem DOS flushes in 4KB chunks; the tail lands on clean exit (mission timer). +btl4opt.exe -egg testnclr.egg > c:\weaponlog.txt +32rtm.exe -u +echo ALPHA1-RUN-DONE +pause +