From 00e65f4c6b33eebed6c2c2232dc06f47578e4a36 Mon Sep 17 00:00:00 2001 From: nmlgc Date: Mon, 9 Nov 2020 15:34:53 +0100 Subject: [PATCH] [Decompilation] [th03] .MRS: Alpha-tested byte-aligned blitting And since inlining even removes longer if-else chains if they branch depending on a literal constant, we can use a regular parameter to select either MOV or OR in our _FS and _GS poke() template functions, without needing to duplicate them! Part of P0127, funded by [Anonymous]. --- decomp.h | 23 ++++++--- th03/formats/mrs.cpp | 72 ++++++++++++++++++++++++++-- th03/formats/mrs.hpp | 3 ++ th03_main.asm | 112 +++++-------------------------------------- 4 files changed, 100 insertions(+), 110 deletions(-) diff --git a/decomp.h b/decomp.h index 74143f79..90e8dd6c 100644 --- a/decomp.h +++ b/decomp.h @@ -57,6 +57,7 @@ // types. #ifdef __cplusplus } + struct Decomp_ES { void __seg* value() { return (void __seg *)(_ES); } }; struct Decomp_FS { void __seg* value() { return (void __seg *)(_FS); } }; struct Decomp_GS { void __seg* value() { return (void __seg *)(_GS); } }; struct Decomp_DI { void __near* value() { return (void __near *)(_DI); } }; @@ -65,20 +66,28 @@ // perfects the inlining. #define poked(sgm, off, val) \ _EAX = val; \ - poked_eax((Decomp##sgm *)NULL, (Decomp##off *)NULL); + poked_eax((Decomp##sgm *)NULL, (Decomp##off *)NULL, (uint8_t)(0x89)); + + #define poke_or_d(sgm, off, val) \ + _EAX = val; \ + poked_eax((Decomp##sgm *)NULL, (Decomp##off *)NULL, (uint8_t)(0x09)); template inline void poked_eax( - Segment *sgm, Offset *off + Segment *sgm, Offset *off, uint8_t op ) { - *reinterpret_cast(sgm->value() + off->value()) = _EAX; + if(op == 0x89) { + *(uint32_t far *)(sgm->value() + off->value()) = _EAX; + } else if(op == 0x09) { + *(uint32_t far *)(sgm->value() + off->value()) |= _EAX; + } } - inline void poked_eax(Decomp_FS *sgm, Decomp_DI *off) { - __emit__(0x66, 0x64, 0x89, 0x05); // MOV FS:[DI], EAX + inline void poked_eax(Decomp_FS *sgm, Decomp_DI *off, uint8_t op) { + __emit__(0x66, 0x64, op, 0x05); // [op] FS:[DI], EAX } - inline void poked_eax(Decomp_GS *sgm, Decomp_DI *off) { - __emit__(0x66, 0x65, 0x89, 0x05); // MOV GS:[DI], EAX + inline void poked_eax(Decomp_GS *sgm, Decomp_DI *off, uint8_t op) { + __emit__(0x66, 0x65, op, 0x05); // [op] GS:[DI], EAX } extern "C" { diff --git a/th03/formats/mrs.cpp b/th03/formats/mrs.cpp index fe749f5b..f989f41c 100644 --- a/th03/formats/mrs.cpp +++ b/th03/formats/mrs.cpp @@ -1,17 +1,25 @@ -#pragma option -3 +#pragma option -3 -Z- #pragma codeseg SHARED extern "C" { +#include #include #include "platform.h" #include "pc98.h" #include "planar.h" #include "decomp.h" +#include "master.hpp" #include "th03/formats/hfliplut.h" } #include "th03/formats/mrs.hpp" +#undef grcg_off +#define grcg_off() { \ + _AL ^= _AL; \ + __asm out 0x7C, al; \ +} + static const vram_byte_amount_t MRS_BYTE_W = (MRS_W / BYTE_DOTS); static const vram_dword_amount_t MRS_DWORD_W = (MRS_BYTE_W / sizeof(dots32_t)); @@ -67,6 +75,15 @@ struct mrs_at_G_t : public mrs_plane_t { static inline mrs_at_G_t near* mrs_at_G(void) { return reinterpret_cast(offsetof(mrs_t, planes.G)); } + +// At least mrs_put_8() is somewhat sane. +struct mrs_at_B_t : public mrs_plane_t { + dots32_t dots_from_alpha(void) const { return *(*((this + 0)->dots)); } + dots32_t dots_from_B(void) const { return *(*((this + 1)->dots)); } + dots32_t dots_from_R(void) const { return *(*((this + 2)->dots)); } + dots32_t dots_from_G(void) const { return *(*((this + 3)->dots)); } + dots32_t dots_from_E(void) const { return *(*((this + 4)->dots)); } +}; // ------------------------- inline uint16_t to_bottom_left_8(const screen_x_t &left) { @@ -79,6 +96,57 @@ inline seg_t to_segment(const uscreen_y_t &top) { return ((_AX << 2) + _DX); // ... and -> segment } +void pascal mrs_put_8(screen_x_t left, uscreen_y_t top, int slot) +{ + #define _SI reinterpret_cast(_SI) + + grcg_setcolor(GC_RMW, 0); + _DI = to_bottom_left_8(left); + _AX = to_segment(top); + + // "I've spent good money on that Intel 386 CPU, so let's actually use + // *all* its segment registers!" :zunpet: :zunpet: :zunpet: + _ES = (_AX += SEG_PLANE_B); // = B + _FS = (_AX += SEG_PLANE_DIST_BRG); // = R + _GS = (_AX += SEG_PLANE_DIST_BRG); // = G + + __asm { push ds; } + mrs_slot_assign(ds, si, slot); + + _DX = MRS_DWORD_W; + __asm { nop; } + mrs_put_rows(_DX, REP MOVSD); + grcg_off(); + + // Reset to bottom left + _SI = 0; + _DI += (MRS_H * ROW_SIZE); + + _BX = (_GS + SEG_PLANE_DIST_E); // = E + _DX = MRS_DWORD_W; + mrs_put_rows(_DX, { put: + _EAX = _SI->dots_from_alpha(); + _EAX |= _EAX; + if(!FLAGS_ZERO) { + poke_or_d(_ES, _DI, _SI->dots_from_B()); + poke_or_d(_FS, _DI, _SI->dots_from_R()); + poke_or_d(_GS, _DI, _SI->dots_from_G()); + _GS = _BX; + poke_or_d(_GS, _DI, _SI->dots_from_E()); + _GS = (_BX - SEG_PLANE_DIST_E); + } + reinterpret_cast(_SI) += sizeof(dots32_t); + _DI += sizeof(dots32_t); + __asm { loop put; } + }); + + __asm { pop ds; } + + #undef _SI +} + +#pragma codestring "\x90" + void pascal mrs_put_noalpha_8( screen_x_t left, uscreen_y_t top, int slot, bool altered_colors ) @@ -92,8 +160,6 @@ void pascal mrs_put_noalpha_8( mrs_slot_assign(ds, si, slot); _SI = mrs_at_G(); - // "I've spent good money on that Intel 386 CPU, so let's actually use - // *all* its segment registers!" :zunpet: :zunpet: :zunpet: _FS = (_AX += SEG_PLANE_B); // = B _GS = (_AX += SEG_PLANE_DIST_BRG); // = R _ES = (_AX += SEG_PLANE_DIST_BRG); // = G diff --git a/th03/formats/mrs.hpp b/th03/formats/mrs.hpp index 3a8fe0db..a6262a2c 100644 --- a/th03/formats/mrs.hpp +++ b/th03/formats/mrs.hpp @@ -7,6 +7,9 @@ static const int MRS_SLOT_COUNT = 8; static const pixel_t MRS_W = 288; static const pixel_t MRS_H = 184; +// Displays the .MRS image in the given [slot] at (⌊left/8⌋*8, top). +void pascal mrs_put_8(screen_x_t left, uscreen_y_t top, int slot); + // Displays the .MRS image in the given [slot] at (⌊left/8⌋*8, top), // disregarding its alpha plane, and optionally altering its colors slightly. void pascal mrs_put_noalpha_8( diff --git a/th03_main.asm b/th03_main.asm index 2552e23e..0a19f93e 100644 --- a/th03_main.asm +++ b/th03_main.asm @@ -5152,29 +5152,29 @@ sub_C8C4 endp sub_C9FE proc far -arg_0 = byte ptr 6 +@@mrs_slot = byte ptr 6 push bp mov bp, sp push si - mov al, [bp+arg_0] + mov al, [bp+@@mrs_slot] mov ah, 0 mov bx, ax cmp byte ptr [bx+798h], 1 jnz short loc_CA37 - mov si, 10h - cmp [bp+arg_0], 0 + mov si, PLAYFIELD_LEFT + cmp [bp+@@mrs_slot], 0 jz short loc_CA1D - add si, 140h + add si, PLAYFIELD_W_BORDERED loc_CA1D: - push si - push 10h - mov al, [bp+arg_0] + push si ; left + push PLAYFIELD_TOP ; top + mov al, [bp+@@mrs_slot] mov ah, 0 - push ax - call sub_EF46 - mov al, [bp+arg_0] + push ax ; slot + call @mrs_put_8$qiuii + mov al, [bp+@@mrs_slot] mov ah, 0 mov bx, ax mov byte ptr [bx+798h], 2 @@ -8963,95 +8963,7 @@ sub_EF1C endp ; --------------------------------------------------------------------------- nop - -; =============== S U B R O U T I N E ======================================= - -; Attributes: bp-based frame - -sub_EF46 proc far - -arg_0 = word ptr 6 -arg_2 = word ptr 8 -arg_4 = word ptr 0Ah - - push bp - mov bp, sp - push si - push di - call grcg_setcolor pascal, GC_RMW, 0 - mov ax, [bp+arg_4] - sar ax, 3 - add ax, 3930h - mov di, ax - mov ax, [bp+arg_2] - shr ax, 1 - mov dx, ax - shl ax, 2 - add ax, dx - add ax, 0A800h - mov es, ax - assume es:nothing - add ax, 800h - mov fs, ax - add ax, 800h - mov gs, ax - push ds - mov bx, [bp+arg_0] - shl bx, 2 - lds si, _mrs_images[bx] - mov dx, 9 - nop - -loc_EF8A: - mov cx, dx - rep movsd - sub di, 74h ; 't' - jnb short loc_EF8A - GRCG_OFF_VIA_XOR al - xor si, si - add di, 3980h - mov ax, gs - add ax, 2800h - mov bx, ax - mov dx, 9 - -loc_EFA8: - mov cx, dx - -loc_EFAA: - mov eax, [si] - or eax, eax - jz short loc_EFDF - mov eax, [si+19E0h] - or es:[di], eax - mov eax, [si+33C0h] - or fs:[di], eax - mov eax, [si+4DA0h] - or gs:[di], eax - mov gs, bx - assume gs:nothing - mov eax, [si+6780h] - or gs:[di], eax - mov ax, bx - sub ax, 2800h - mov gs, ax - assume gs:nothing - -loc_EFDF: - add si, 4 - add di, 4 - loop loc_EFAA - sub di, 74h ; 't' - jnb short loc_EFA8 - pop ds - pop di - pop si - pop bp - retf 6 -sub_EF46 endp - -; --------------------------------------------------------------------------- - nop + extern @MRS_PUT_8$QIUII:proc extern @MRS_PUT_NOALPHA_8$QIUIIC:proc extern @MRS_HFLIP$QI:proc SPRITE16_SPRITES_COMMIT procdesc pascal far