[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].
This commit is contained in:
nmlgc 2020-11-09 15:34:53 +01:00
parent 8b0165738a
commit 00e65f4c6b
4 changed files with 100 additions and 110 deletions

View File

@ -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 <class Segment, class Offset> inline void poked_eax(
Segment *sgm, Offset *off
Segment *sgm, Offset *off, uint8_t op
) {
*reinterpret_cast<uint32_t far *>(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" {

View File

@ -1,17 +1,25 @@
#pragma option -3
#pragma option -3 -Z-
#pragma codeseg SHARED
extern "C" {
#include <dos.h>
#include <stddef.h>
#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<mrs_at_G_t near *>(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<mrs_at_B_t near *>(_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<uint16_t>(_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

View File

@ -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(

View File

@ -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