// scroll_asm.cpp // #include #include #include #ifndef SCROLL_FASTCALL # if defined(_MSC_VER) # define SCROLL_FASTCALL __fastcall # elif (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(_M_IX86)) # define SCROLL_FASTCALL __attribute__((fastcall)) # else # define SCROLL_FASTCALL # endif #endif #ifndef SCROLL_EXTERN_C # if defined(__cplusplus) # define SCROLL_EXTERN_C extern "C" # else # define SCROLL_EXTERN_C # endif #endif /* * The original MASM module imported/exported C-linkage COFF symbols * (_nLVal, _pDungeonCels, @DrawMTileClipTop@4, ...). Keep that linkage * even if this .c file is accidentally compiled as C++. */ #ifndef SCROLL_PORT_EXTERN_C_GLOBALS # define SCROLL_PORT_EXTERN_C_GLOBALS 1 #endif #ifndef SCROLL_BYTE_DEFINED typedef uint8_t BYTE; #define SCROLL_BYTE_DEFINED 1 #endif #ifndef SCROLL_DWORD_DEFINED typedef uint32_t DWORD; #define SCROLL_DWORD_DEFINED 1 #endif #define WTYPE_NONE 0 #define WTYPE_LEFT 1 #define WTYPE_RIGHT 2 #define WTYPE_ULC 3 #define WTYPE_LRC 4 #define PART_TRANS_NONE 0 #define PART_TRANS_LEFT 1 #define PART_TRANS_RIGHT 2 #define NBUFFW32 800 #define NBUFFW64 832 #ifndef SCROLL_UNUSED # if defined(__GNUC__) || defined(__clang__) # define SCROLL_UNUSED __attribute__((unused)) # else # define SCROLL_UNUSED # endif #endif /* * Externals from the original engine modules. * * scroll.asm used these as C-linkage imports, so these declarations must also * use C linkage when this file is compiled as C++. Otherwise MSVC emits * unresolved symbols such as ?pSpeedCels@@3PAEA instead of the original * _pSpeedCels import. * * Leave SCROLL_DEFINE_GLOBALS undefined for the normal Diablo build, where * scrollrt.cpp, lighting.cpp, and gendung.cpp provide the storage. Define it * in exactly one translation unit only for a standalone harness that does not * link those modules. */ #if defined(__cplusplus) && SCROLL_PORT_EXTERN_C_GLOBALS extern "C" { #endif #ifndef SCROLL_DEFINE_GLOBALS extern int32_t nLVal; extern BYTE* glClipY; extern int32_t gnPieceNum; extern int32_t nTrans; extern DWORD gdwPNum; extern BYTE gbPartialTrans; extern BYTE lightmax; extern BYTE* pLightTbl; extern DWORD microoffset[]; extern BYTE* pDungeonCels; extern BYTE* pSpeedCels; extern BYTE nWTypeTable[]; extern BYTE gbLevelTileEnhanceActive; extern BYTE gbLevelTileContrastTable[]; extern BYTE gbLevelTileLumaTable[]; extern BYTE gbLevelTileSharpenLightTable[]; extern BYTE gbLevelTileAoTable[]; extern BYTE gbTownWarmFlickerActive; extern BYTE gbTownWarmFlickerTable[][256]; extern int32_t gnTownTileX; extern int32_t gnTownTileY; extern int32_t gnTownFlickerFrame; #else int32_t nLVal; BYTE* glClipY; int32_t gnPieceNum; int32_t nTrans; DWORD gdwPNum; BYTE gbPartialTrans; BYTE lightmax; BYTE* pLightTbl; DWORD microoffset[4096 * 16]; BYTE* pDungeonCels; BYTE* pSpeedCels; BYTE nWTypeTable[4096]; BYTE gbLevelTileEnhanceActive; BYTE gbLevelTileContrastTable[256]; BYTE gbLevelTileLumaTable[256]; BYTE gbLevelTileSharpenLightTable[256]; BYTE gbLevelTileAoTable[256]; BYTE gbTownWarmFlickerActive; BYTE gbTownWarmFlickerTable[4][256]; int32_t gnTownTileX; int32_t gnTownTileY; int32_t gnTownFlickerFrame; #endif #if defined(__cplusplus) && SCROLL_PORT_EXTERN_C_GLOBALS } #endif #if defined(__cplusplus) extern "C" { #endif void SCROLL_FASTCALL DrawMTileClipTop(BYTE* pDecodeTo); void SCROLL_FASTCALL DrawMTileClipBottom(BYTE* pDecodeTo); #if defined(__cplusplus) } #endif /* Private data from scroll.asm. */ static DWORD sgLineVal; static DWORD sgCM; static BYTE sgWT; static DWORD* sgT; static const DWORD* sgMask; static DWORD sgTimeLow; static DWORD sgTimeHigh; static DWORD sgLoopTime; static DWORD sgUnrollTime; static const DWORD sgRightMask[32] = { 0xEAAAAAAAu, 0xF5555555u, 0xFEAAAAAAu, 0xFF555555u, 0xFFEAAAAAu, 0xFFF55555u, 0xFFFEAAAAu, 0xFFFF5555u, 0xFFFFEAAAu, 0xFFFFF555u, 0xFFFFFEAAu, 0xFFFFFF55u, 0xFFFFFFEAu, 0xFFFFFFF5u, 0xFFFFFFFEu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, }; static const DWORD sgLeftMask[32] = { 0xAAAAAAABu, 0x5555555Fu, 0xAAAAAABFu, 0x555555FFu, 0xAAAAABFFu, 0x55555FFFu, 0xAAAABFFFu, 0x5555FFFFu, 0xAAABFFFFu, 0x555FFFFFu, 0xAABFFFFFu, 0x55FFFFFFu, 0xABFFFFFFu, 0x5FFFFFFFu, 0xBFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, }; static const DWORD sgFullMask[32] = { 0xAAAAAAAAu, 0x55555555u, 0xAAAAAAAAu, 0x55555555u, 0xAAAAAAAAu, 0x55555555u, 0xAAAAAAAAu, 0x55555555u, 0xAAAAAAAAu, 0x55555555u, 0xAAAAAAAAu, 0x55555555u, 0xAAAAAAAAu, 0x55555555u, 0xAAAAAAAAu, 0x55555555u, 0xAAAAAAAAu, 0x55555555u, 0xAAAAAAAAu, 0x55555555u, 0xAAAAAAAAu, 0x55555555u, 0xAAAAAAAAu, 0x55555555u, 0xAAAAAAAAu, 0x55555555u, 0xAAAAAAAAu, 0x55555555u, 0xAAAAAAAAu, 0x55555555u, 0xAAAAAAAAu, 0x55555555u, }; static const DWORD sgDivBy3MulBy4[48] = { 0, 0, 0, 4, 4, 4, 8, 8, 8,12,12,12,16,16,16, 20,20,20,24,24,24,28,28,28,32,32,32,36,36,36, 40,40,40,44,44,44,48,48,48,52,52,52,56,56,56, 60,60,60 }; static const DWORD TotalDataPerLineBottom[17] = { 0, 4, 8, 16, 24, 36, 48, 64, 80, 100,120,144,168,196,224,256,288 }; static const DWORD TotalDataPerLineTop[17] = { 0, 32, 60, 88,112,136,156,176,192, 208,220,232,240,248,252,256,288 }; static SCROLL_UNUSED uint16_t LoadLE16(const BYTE* p) { return (uint16_t)((uint16_t)p[0] | ((uint16_t)p[1] << 8)); } static uint32_t LoadLE32(const BYTE* p) { return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24); } static SCROLL_UNUSED void StoreLE16(BYTE* p, uint16_t v) { p[0] = (BYTE)(v & 0xFFu); p[1] = (BYTE)((v >> 8) & 0xFFu); } static void StoreLE32(BYTE* p, uint32_t v) { p[0] = (BYTE)(v & 0xFFu); p[1] = (BYTE)((v >> 8) & 0xFFu); p[2] = (BYTE)((v >> 16) & 0xFFu); p[3] = (BYTE)((v >> 24) & 0xFFu); } static uintptr_t ClipAddr(void) { return (uintptr_t)glClipY; } static int PtrBelowClip(const BYTE* p) { return (uintptr_t)p < ClipAddr(); } static int BeginRowTop(BYTE* dst) { return !PtrBelowClip(dst); } static int BeginRowBottomDraw(BYTE* dst) { return PtrBelowClip(dst); } typedef enum PixelMode { PIXELS_LIGHT, PIXELS_PRETRANS, PIXELS_BLACK } PixelMode; typedef enum TransMode { TRANS_OPAQUE, TRANS_DITHER, TRANS_HALF_MASK } TransMode; typedef enum ClipMode { CLIP_TOP_MODE, CLIP_BOTTOM_MODE } ClipMode; typedef enum PadMode { PAD_NONE, PAD_PRE, PAD_POST } PadMode; typedef struct RenderSetup { const BYTE* src; const BYTE* lightTbl; PixelMode pixelMode; int shape; } RenderSetup; typedef struct RenderCtx { const BYTE* src; BYTE* dst; const BYTE* lightTbl; PixelMode pixelMode; TransMode transMode; ClipMode clipMode; const DWORD* maskPtr; DWORD maskWord; int linePhase; } RenderCtx; static const BYTE* GetDungeonCelSource(DWORD pnum) { DWORD cel = pnum & 0x0FFFu; DWORD offs = LoadLE32(pDungeonCels + cel * 4u); return pDungeonCels + offs; } static const BYTE* GetSpeedCelSource(DWORD pnum) { DWORD cel = pnum & 0x0FFFu; DWORD idx = (cel << 4) + (DWORD)nLVal; return pSpeedCels + microoffset[idx]; } static void ResolveSpeedCelToNormalPNum(void) { DWORD pnum = gdwPNum; DWORD cel = pnum & 0x0FFFu; gdwPNum = (pnum & 0xF000u) + microoffset[cel << 4]; } static int ShapeFromNormalType(DWORD type) { return (type <= 4u) ? (int)type : 5; } static int ShapeFromPretransType(DWORD type) { switch (type) { case 8: return 0; case 9: return 1; case 10: return 2; case 11: return 3; case 12: return 4; default: return 5; } } static int ShapeFromBlackType(DWORD type) { return (type <= 4u) ? (int)type : 5; } static RenderSetup GetRenderSetup(void) { RenderSetup setup; DWORD pnum = gdwPNum; BYTE lval = (BYTE)nLVal; setup.src = NULL; setup.lightTbl = NULL; setup.pixelMode = PIXELS_PRETRANS; setup.shape = 0; sgT = microoffset; if (lval != 0) { if (lval == lightmax) { if ((pnum & 0x8000u) != 0) { ResolveSpeedCelToNormalPNum(); pnum = gdwPNum; } setup.src = GetDungeonCelSource(pnum); setup.pixelMode = PIXELS_BLACK; setup.shape = ShapeFromBlackType((pnum >> 12) & 0x07u); return setup; } if ((pnum & 0x8000u) != 0) { setup.src = GetSpeedCelSource(pnum); setup.pixelMode = PIXELS_PRETRANS; setup.shape = ShapeFromPretransType((pnum >> 12) & 0x0Fu); return setup; } setup.src = GetDungeonCelSource(pnum); setup.lightTbl = pLightTbl + ((DWORD)nLVal << 8); setup.pixelMode = PIXELS_LIGHT; setup.shape = ShapeFromNormalType((pnum >> 12) & 0x0Fu); return setup; } /* No light: resolve speed CELs back to the unlighted dungeon CEL. */ if ((pnum & 0x8000u) != 0) { ResolveSpeedCelToNormalPNum(); pnum = gdwPNum; } setup.src = GetDungeonCelSource(pnum); setup.pixelMode = PIXELS_PRETRANS; setup.shape = ShapeFromPretransType(((pnum >> 12) & 0x07u) + 8u); return setup; } static int PixelSelected(const RenderCtx* ctx, int x) { if (ctx->transMode == TRANS_DITHER) { return ((x & 1) != ctx->linePhase); } if (ctx->transMode == TRANS_HALF_MASK) { return (ctx->maskWord & (0x80000000u >> (unsigned)x)) != 0; } return 1; } static int InTownLightRect(int x, int y, int cx, int cy, int rx, int ry) { return x >= cx - rx && x <= cx + rx && y >= cy - ry && y <= cy + ry; } static int TownWarmLightPhase(void) { int x, y; if (!gbTownWarmFlickerActive) { return -1; } x = gnTownTileX; y = gnTownTileY; if (x < 0 || y < 0) { return -1; } if (InTownLightRect(x, y, 55, 60, 11, 6)) /* tavern main building */ return gnTownFlickerFrame & 3; if (InTownLightRect(x, y, 55, 68, 6, 2)) /* tavern outside spill */ return (gnTownFlickerFrame / 2 + 1) & 3; if (InTownLightRect(x, y, 62, 63, 9, 7)) /* smith */ return (gnTownFlickerFrame / 2 + 2) & 3; if (InTownLightRect(x, y, 55, 79, 9, 7)) /* healer */ return ((gnTownFlickerFrame * 3) / 2 + 1) & 3; if (InTownLightRect(x, y, 80, 20, 10, 8)) /* witch */ return (gnTownFlickerFrame / 3 + 3) & 3; if (InTownLightRect(x, y, 62, 16, 9, 7)) /* farmhouse */ return ((gnTownFlickerFrame * 2) / 3 + 1) & 3; if (InTownLightRect(x, y, 24, 32, 8, 6)) /* wounded townsman house */ return ((gnTownFlickerFrame * 3) / 4 + 2) & 3; if (InTownLightRect(x, y, 75, 68, 9, 7)) /* player house */ return ((gnTownFlickerFrame * 4) / 3 + 3) & 3; if (InTownLightRect(x, y, 71, 84, 8, 6)) /* Farnham's house */ return ((gnTownFlickerFrame * 5) / 4 + 1) & 3; if (InTownLightRect(x, y, 43, 66, 8, 6)) /* Gillian's house */ return ((gnTownFlickerFrame * 2) / 5 + 2) & 3; if (InTownLightRect(x, y, 49, 21, 9, 7)) /* town portal house */ return ((gnTownFlickerFrame * 5) / 3 + 3) & 3; return -1; } static BYTE TownWarmFlickerPixel(BYTE pixel) { int phase; phase = TownWarmLightPhase(); if (phase < 0) { return pixel; } return gbTownWarmFlickerTable[phase][pixel]; } static BYTE LevelTilePixel(const BYTE* src, const BYTE* lightTbl, int i, int count) { BYTE pixel; int left, right, curr, nearAvg, darkest; pixel = gbLevelTileContrastTable[src[i]]; if (lightTbl != NULL) { pixel = lightTbl[pixel]; } if (i <= 0 || i + 1 >= count) { return TownWarmFlickerPixel(pixel); } left = gbLevelTileLumaTable[src[i - 1]]; right = gbLevelTileLumaTable[src[i + 1]]; curr = gbLevelTileLumaTable[src[i]]; nearAvg = (left + right) >> 1; darkest = (left < right) ? left : right; if (nearAvg - curr > 12) { return TownWarmFlickerPixel(gbLevelTileAoTable[pixel]); } if (curr - darkest > 20 && curr < 220) { return TownWarmFlickerPixel(gbLevelTileAoTable[pixel]); } if (curr - nearAvg > 22) { return TownWarmFlickerPixel(gbLevelTileSharpenLightTable[pixel]); } return TownWarmFlickerPixel(pixel); } static void DrawSpan(RenderCtx* ctx, int count, int xStart, PadMode pad, int draw) { int i; const BYTE* src; BYTE* dst; if (count <= 0) { return; } if (pad == PAD_PRE) { ctx->src += (count & 2); } src = ctx->src; dst = ctx->dst; if (draw) { for (i = 0; i < count; i++) { if (PixelSelected(ctx, xStart + i)) { switch (ctx->pixelMode) { case PIXELS_LIGHT: if (gbLevelTileEnhanceActive) { dst[i] = LevelTilePixel(src, ctx->lightTbl, i, count); } else { dst[i] = ctx->lightTbl[src[i]]; } break; case PIXELS_PRETRANS: if (gbLevelTileEnhanceActive) { dst[i] = LevelTilePixel(src, NULL, i, count); } else { dst[i] = src[i]; } break; case PIXELS_BLACK: dst[i] = 0; break; } } } } ctx->src = src + count; ctx->dst = dst + count; if (pad == PAD_POST) { ctx->src += ((uintptr_t)ctx->src & 2u); } } static void FinishRow(RenderCtx* ctx) { if (ctx->transMode == TRANS_DITHER) { ctx->linePhase ^= 1; } else if (ctx->transMode == TRANS_HALF_MASK && ctx->maskPtr != NULL) { ctx->maskPtr--; } } static int StartRow(RenderCtx* ctx, int* draw) { if (ctx->clipMode == CLIP_TOP_MODE) { if (!BeginRowTop(ctx->dst)) { return 0; } *draw = 1; } else { *draw = BeginRowBottomDraw(ctx->dst); } if (ctx->transMode == TRANS_HALF_MASK && ctx->maskPtr != NULL) { ctx->maskWord = *ctx->maskPtr; } else { ctx->maskWord = 0xFFFFFFFFu; } return 1; } static int DrawFullRow(RenderCtx* ctx) { int draw; if (!StartRow(ctx, &draw)) { return 0; } DrawSpan(ctx, 32, 0, PAD_NONE, draw); ctx->dst -= NBUFFW32; FinishRow(ctx); return 1; } static int DrawLeftTriangleRow(RenderCtx* ctx, int leftSkip) { int draw; if (!StartRow(ctx, &draw)) { return 0; } ctx->dst += leftSkip; DrawSpan(ctx, 32 - leftSkip, leftSkip, PAD_PRE, draw); ctx->dst -= NBUFFW32; FinishRow(ctx); return 1; } static int DrawRightTriangleRow(RenderCtx* ctx, int rightSkip) { int draw; if (!StartRow(ctx, &draw)) { return 0; } DrawSpan(ctx, 32 - rightSkip, 0, PAD_POST, draw); ctx->dst -= NBUFFW32; ctx->dst += rightSkip; FinishRow(ctx); return 1; } static int DrawSolidRows(RenderCtx* ctx, int rows) { int row; for (row = 0; row < rows; row++) { if (!DrawFullRow(ctx)) { return 0; } } return 1; } static int DrawLeftBottomHalf(RenderCtx* ctx) { int skip; for (skip = 30; skip >= 0; skip -= 2) { if (!DrawLeftTriangleRow(ctx, skip)) { return 0; } } return 1; } static int DrawLeftTopHalf(RenderCtx* ctx) { int skip; for (skip = 2; skip != 32; skip += 2) { if (!DrawLeftTriangleRow(ctx, skip)) { return 0; } } return 1; } static int DrawRightBottomHalf(RenderCtx* ctx) { int skip; for (skip = 30; skip >= 0; skip -= 2) { if (!DrawRightTriangleRow(ctx, skip)) { return 0; } } return 1; } static int DrawRightTopHalf(RenderCtx* ctx) { int skip; for (skip = 2; skip != 32; skip += 2) { if (!DrawRightTriangleRow(ctx, skip)) { return 0; } } return 1; } static int DrawRleRows(RenderCtx* ctx) { int row; for (row = 0; row < 32; row++) { int remaining = 32; int x = 0; if (ctx->transMode == TRANS_HALF_MASK && ctx->maskPtr != NULL) { ctx->maskWord = *ctx->maskPtr; } else { ctx->maskWord = 0xFFFFFFFFu; } while (remaining != 0) { BYTE raw = *ctx->src++; if ((raw & 0x80u) != 0) { int jump = (int)((BYTE)(0u - raw)); ctx->dst += jump; x += jump; remaining -= jump; } else { int count = (int)raw; int draw; remaining -= count; if (ctx->clipMode == CLIP_TOP_MODE) { if (!BeginRowTop(ctx->dst)) { return 0; } draw = 1; } else { draw = BeginRowBottomDraw(ctx->dst); } DrawSpan(ctx, count, x, PAD_NONE, draw); x += count; } } ctx->dst -= NBUFFW32; FinishRow(ctx); } return 1; } static void RenderShape(BYTE* dst, const RenderSetup* setup, TransMode transMode, ClipMode clipMode, const DWORD* maskStart) { RenderCtx ctx; ctx.src = setup->src; ctx.dst = dst; ctx.lightTbl = setup->lightTbl; ctx.pixelMode = setup->pixelMode; ctx.transMode = transMode; ctx.clipMode = clipMode; ctx.maskPtr = maskStart; ctx.maskWord = 0xFFFFFFFFu; ctx.linePhase = 0; switch (setup->shape) { case 0: /* Solid 32x32 block. */ (void)DrawSolidRows(&ctx, 32); break; case 1: /* 32x32 block with transparent holes. */ (void)DrawRleRows(&ctx); break; case 2: /* Left triangle / diamond. */ if (transMode == TRANS_HALF_MASK) { ctx.transMode = TRANS_OPAQUE; ctx.maskPtr = NULL; } if (!DrawLeftBottomHalf(&ctx)) { break; } (void)DrawLeftTopHalf(&ctx); break; case 3: /* Right triangle / diamond. */ if (transMode == TRANS_HALF_MASK) { ctx.transMode = TRANS_OPAQUE; ctx.maskPtr = NULL; } if (!DrawRightBottomHalf(&ctx)) { break; } (void)DrawRightTopHalf(&ctx); break; case 4: /* Left triangle to wall. */ if (transMode == TRANS_HALF_MASK) { ctx.transMode = TRANS_OPAQUE; ctx.maskPtr = NULL; } if (!DrawLeftBottomHalf(&ctx)) { break; } if (transMode == TRANS_HALF_MASK) { ctx.transMode = TRANS_HALF_MASK; ctx.maskPtr = maskStart - 16; } else { ctx.transMode = transMode; } (void)DrawSolidRows(&ctx, 16); break; case 5: /* Right triangle to wall. */ default: if (transMode == TRANS_HALF_MASK) { ctx.transMode = TRANS_OPAQUE; ctx.maskPtr = NULL; } if (!DrawRightBottomHalf(&ctx)) { break; } if (transMode == TRANS_HALF_MASK) { ctx.transMode = TRANS_HALF_MASK; ctx.maskPtr = maskStart - 16; } else { ctx.transMode = transMode; } (void)DrawSolidRows(&ctx, 16); break; } } static void DrawMTileDitherClipTop(BYTE* pDecodeTo) { RenderSetup setup = GetRenderSetup(); RenderShape(pDecodeTo, &setup, TRANS_DITHER, CLIP_TOP_MODE, NULL); } static void DrawMTileHalfDitherClipTop(BYTE* pDecodeTo, const DWORD* mask) { RenderSetup setup = GetRenderSetup(); RenderShape(pDecodeTo, &setup, TRANS_HALF_MASK, CLIP_TOP_MODE, mask); } static void DrawMTileDitherClipBottom(BYTE* pDecodeTo) { RenderSetup setup = GetRenderSetup(); RenderShape(pDecodeTo, &setup, TRANS_DITHER, CLIP_BOTTOM_MODE, NULL); } static void DrawMTileHalfDitherClipBottom(BYTE* pDecodeTo, const DWORD* mask) { RenderSetup setup = GetRenderSetup(); RenderShape(pDecodeTo, &setup, TRANS_HALF_MASK, CLIP_BOTTOM_MODE, mask); } static void DrawMTileNoTransClipTop(BYTE* pDecodeTo) { RenderSetup setup = GetRenderSetup(); RenderShape(pDecodeTo, &setup, TRANS_OPAQUE, CLIP_TOP_MODE, NULL); } static void DrawMTileNoTransClipBottom(BYTE* pDecodeTo) { RenderSetup setup = GetRenderSetup(); RenderShape(pDecodeTo, &setup, TRANS_OPAQUE, CLIP_BOTTOM_MODE, NULL); } SCROLL_EXTERN_C void SCROLL_FASTCALL DrawMTileClipTop(BYTE* pDecodeTo) { BYTE partial; BYTE wt; if (nTrans != 0) { partial = gbPartialTrans; if (partial == PART_TRANS_NONE) { DrawMTileDitherClipTop(pDecodeTo); return; } if (partial == PART_TRANS_LEFT) { wt = nWTypeTable[gnPieceNum]; if (wt == WTYPE_LEFT || wt == WTYPE_ULC) { DrawMTileHalfDitherClipTop(pDecodeTo, &sgLeftMask[31]); return; } /* The original assembly jumps to the PART_TRANS_RIGHT test here; * its following WTYPE_LRC check is unreachable, so it is preserved * as unreachable behavior by falling through to no-trans rendering. */ } if (partial == PART_TRANS_RIGHT) { wt = nWTypeTable[gnPieceNum]; if (wt == WTYPE_RIGHT || wt == WTYPE_ULC) { DrawMTileHalfDitherClipTop(pDecodeTo, &sgRightMask[31]); return; } /* Same unreachable WTYPE_LRC path as in scroll.asm. */ } } DrawMTileNoTransClipTop(pDecodeTo); } SCROLL_EXTERN_C void SCROLL_FASTCALL DrawMTileClipBottom(BYTE* pDecodeTo) { BYTE partial; BYTE wt; if (nTrans != 0) { partial = gbPartialTrans; if (partial == PART_TRANS_NONE) { DrawMTileDitherClipBottom(pDecodeTo); return; } if (partial == PART_TRANS_LEFT) { wt = nWTypeTable[gnPieceNum]; if (wt == WTYPE_LEFT || wt == WTYPE_ULC) { DrawMTileHalfDitherClipBottom(pDecodeTo, &sgLeftMask[31]); return; } /* Preserve the assembly's unreachable WTYPE_LRC check. */ } if (partial == PART_TRANS_RIGHT) { wt = nWTypeTable[gnPieceNum]; if (wt == WTYPE_RIGHT || wt == WTYPE_ULC) { DrawMTileHalfDitherClipBottom(pDecodeTo, &sgRightMask[31]); return; } /* Preserve the assembly's unreachable WTYPE_LRC check. */ } } DrawMTileNoTransClipBottom(pDecodeTo); } void DrawBlankMTile(BYTE* pDecodeTo) { BYTE* dst = pDecodeTo; int edx = 30; int ebx = 1; uint32_t zero = 0; for (;;) { int ecx; dst += edx; for (ecx = ebx; ecx != 0; ecx--) { StoreLE32(dst, zero); dst += 4; } dst += edx; dst -= NBUFFW64; if (edx == 0) { break; } edx -= 2; ebx++; } edx = 2; ebx = 15; while (edx != 32) { int ecx; dst += edx; for (ecx = ebx; ecx != 0; ecx--) { StoreLE32(dst, zero); dst += 4; } dst += edx; dst -= NBUFFW64; ebx--; edx += 2; } } /* Keep private symbols referenced so aggressive compilers do not drop the * data tables when this file is compiled into projects that inspect them while * debugging or compare against the original assembly data segment. */ static SCROLL_UNUSED void ScrollPortKeepPrivateDataReferenced(void) { volatile DWORD sink = 0; sink ^= sgLineVal; sink ^= sgCM; sink ^= sgWT; sink ^= (DWORD)(uintptr_t)sgT; sink ^= (DWORD)(uintptr_t)sgMask; sink ^= sgTimeLow ^ sgTimeHigh ^ sgLoopTime ^ sgUnrollTime; sink ^= sgRightMask[0] ^ sgLeftMask[0] ^ sgFullMask[0]; sink ^= sgDivBy3MulBy4[0] ^ TotalDataPerLineBottom[0] ^ TotalDataPerLineTop[0]; (void)sink; }