ref: 7d1bc4ab95db2a1199150eb91cc0d7b1a5dd1128
dir: /scale.c/
#define OP_RETF 0xcb
/*
=============================================================================
GLOBALS
=============================================================================
*/
t_compscale _seg *scaledirectory[MAXSCALEHEIGHT+1];
s32int fullscalefarcall[MAXSCALEHEIGHT+1];
s16int maxscale,maxscaleshl2;
/*
=============================================================================
LOCALS
=============================================================================
*/
t_compscale _seg *work;
u16int BuildCompScale (s16int height, uchar **finalspot);
s16int stepbytwo;
//===========================================================================
/*
==============
=
= BadScale
=
==============
*/
void far BadScale (void)
{
Quit ("BadScale called!");
}
void SetupScaling (s16int maxscaleheight)
{
s16int i,x,y;
u8int far *dest;
The dynamically compiled scaling routines are now a Bad Thing.
On uncached machines (the original target) they are the fastest
possible way to scale walls, but on modern processors you just
wind up thrashing the code cash and wrecking performance.
A simple looping texture mapper would be faster on 486+ machines.
maxscaleheight/=2; // one scaler every two pixels
maxscale = maxscaleheight-1;
maxscaleshl2 = maxscale<<2;
//
// free up old scalers
//
for (i=1;i<MAXSCALEHEIGHT;i++)
{
if (scaledirectory[i])
MM_FreePtr (&(uchar *)scaledirectory[i]);
if (i>=stepbytwo)
i += 2;
}
memset (scaledirectory,0,sizeof(scaledirectory));
//
// build the compiled scalers
//
stepbytwo = vw.dy/2; // save space by double stepping
MM_GetPtr (&(uchar *)work,20000);
for (i=1;i<=maxscaleheight;i++)
{
BuildCompScale (i*2,&(uchar *)scaledirectory[i]);
if (i>=stepbytwo)
i+= 2;
}
MM_FreePtr (&(uchar *)work);
//
// compact memory and lock down scalers
//
for (i=1;i<=maxscaleheight;i++)
{
MM_SetLock (&(uchar *)scaledirectory[i],true);
fullscalefarcall[i] = (u16int)scaledirectory[i];
fullscalefarcall[i] <<=16;
fullscalefarcall[i] += scaledirectory[i]->codeofs[0];
if (i>=stepbytwo)
{
scaledirectory[i+1] = scaledirectory[i];
fullscalefarcall[i+1] = fullscalefarcall[i];
scaledirectory[i+2] = scaledirectory[i];
fullscalefarcall[i+2] = fullscalefarcall[i];
i+=2;
}
}
scaledirectory[0] = scaledirectory[1];
fullscalefarcall[0] = fullscalefarcall[1];
//
// check for oversize wall drawing
//
for (i=maxscaleheight;i<MAXSCALEHEIGHT;i++)
fullscalefarcall[i] = (uintptr)BadScale;
}
//===========================================================================
/*
========================
=
= BuildCompScale
=
= Builds a compiled scaler object that will scale a 64 tall object to
= the given height (centered vertically on the screen)
=
= height should be even
=
= Call with
= ---------
= DS:SI Source for scale
= ES:DI Dest for scale
=
= Calling the compiled scaler only destroys AL
=
========================
*/
u16int BuildCompScale (s16int height, uchar **finalspot)
{
a simple looping texture mapper would be better, simpler, faster
u8int far *code;
s16int i;
s32int fix,step;
u16int src,totalscaled,totalsize;
s16int startpix,endpix,toppix;
step = ((s32int)height<<16) / 64;
code = &work->code[0];
toppix = (vw.dy-height)/2;
fix = 0;
for (src=0;src<=64;src++)
{
startpix = fix>>16;
fix += step;
endpix = fix>>16;
if (endpix>startpix)
work->width[src] = endpix-startpix;
else
work->width[src] = 0;
//
// mark the start of the code
//
work->codeofs[src] = FP_OFF(code);
//
// compile some code if the source pixel generates any screen pixels
//
startpix+=toppix;
endpix+=toppix;
if (startpix == endpix || endpix < 0 || startpix >= vw.dy || src == 64)
continue;
//
// mov al,[si+src]
//
*code++ = 0x8a;
*code++ = 0x44;
*code++ = src;
for (;startpix<endpix;startpix++)
{
if (startpix >= vw.dy)
break; // off the bottom of the view area
if (startpix < 0)
continue; // not into the view area
//
// mov [es:di+heightofs],al
//
*code++ = 0x26;
*code++ = 0x88;
*code++ = 0x85;
*((u16int far *)code)++ = startpix*SCREENBWIDE;
}
}
//
// retf
//
*code++ = 0xcb;
totalsize = FP_OFF(code);
MM_GetPtr (finalspot,totalsize);
_fmemcpy ((u8int _seg *)(*finalspot),(u8int _seg *)work,totalsize);
return totalsize;
}
/*
=======================
=
= ScaleLine
=
= linescale should have the high word set to the segment of the scaler
=
=======================
*/
extern s16int slinex,slinewidth;
extern u16int far *linecmds;
extern s32int linescale;
extern u16int maskword;
u8int mask1,mask2,mask3;
void near ScaleLine (void)
{
asm mov cx,WORD PTR [linescale+2]
asm mov es,cx // segment of scaler
asm mov bp,WORD PTR [linecmds]
asm mov dx,SC_INDEX+1 // to set SC_MAPMASK
asm mov bx,[slinex]
asm mov di,bx
asm shr di,2 // X in bytes
asm add di,[bufferofs]
asm and bx,3
asm shl bx,3
asm add bx,[slinewidth] // bx = (pixel*8+pixwidth)
asm mov al,BYTE [mapmasks3-1+bx] // -1 because pixwidth of 1 is first
asm mov ds,WORD PTR [linecmds+2]
asm or al,al
asm jz notthreebyte // scale across three bytes
asm jmp threebyte
notthreebyte:
asm mov al,BYTE PTR ss:[mapmasks2-1+bx] // -1 because pixwidth of 1 is first
asm or al,al
asm jnz twobyte // scale across two bytes
//
// one byte scaling
//
asm mov al,BYTE PTR ss:[mapmasks1-1+bx] // -1 because pixwidth of 1 is first
asm out dx,al // set map mask register
scalesingle:
asm mov bx,[ds:bp] // table location of rtl to patch
asm or bx,bx
asm jz linedone // 0 signals end of segment list
asm mov bx,[es:bx]
asm mov dl,[es:bx] // save old value
asm mov BYTE PTR es:[bx],OP_RETF // patch a RETF in
asm mov si,[ds:bp+4] // table location of entry spot
asm mov ax,[es:si]
asm mov WORD PTR ss:[linescale],ax // call here to start scaling
asm mov si,[ds:bp+2] // corrected top of shape for this segment
asm add bp,6 // next segment list
asm mov ax,SCREENSEG
asm mov es,ax
asm call ss:[linescale] // scale the segment of pixels
asm mov es,cx // segment of scaler
asm mov BYTE PTR es:[bx],dl // unpatch the RETF
asm jmp scalesingle // do the next segment
//
// done
//
linedone:
asm mov ax,ss
asm mov ds,ax
return;
//
// two byte scaling
//
twobyte:
asm mov ss:[mask2],al
asm mov al,BYTE PTR ss:[mapmasks1-1+bx] // -1 because pixwidth of 1 is first
asm mov ss:[mask1],al
scaledouble:
asm mov bx,[ds:bp] // table location of rtl to patch
asm or bx,bx
asm jz linedone // 0 signals end of segment list
asm mov bx,[es:bx]
asm mov cl,[es:bx] // save old value
asm mov BYTE PTR es:[bx],OP_RETF // patch a RETF in
asm mov si,[ds:bp+4] // table location of entry spot
asm mov ax,[es:si]
asm mov WORD PTR ss:[linescale],ax // call here to start scaling
asm mov si,[ds:bp+2] // corrected top of shape for this segment
asm add bp,6 // next segment list
asm mov ax,SCREENSEG
asm mov es,ax
asm mov al,ss:[mask1]
asm out dx,al // set map mask register
asm call ss:[linescale] // scale the segment of pixels
asm inc di
asm mov al,ss:[mask2]
asm out dx,al // set map mask register
asm call ss:[linescale] // scale the segment of pixels
asm dec di
asm mov es,WORD PTR ss:[linescale+2] // segment of scaler
asm mov BYTE PTR es:[bx],cl // unpatch the RETF
asm jmp scaledouble // do the next segment
//
// three byte scaling
//
threebyte:
asm mov ss:[mask3],al
asm mov al,BYTE PTR ss:[mapmasks2-1+bx] // -1 because pixwidth of 1 is first
asm mov ss:[mask2],al
asm mov al,BYTE PTR ss:[mapmasks1-1+bx] // -1 because pixwidth of 1 is first
asm mov ss:[mask1],al
scaletriple:
asm mov bx,[ds:bp] // table location of rtl to patch
asm or bx,bx
asm jz linedone // 0 signals end of segment list
asm mov bx,[es:bx]
asm mov cl,[es:bx] // save old value
asm mov BYTE PTR es:[bx],OP_RETF // patch a RETF in
asm mov si,[ds:bp+4] // table location of entry spot
asm mov ax,[es:si]
asm mov WORD PTR ss:[linescale],ax // call here to start scaling
asm mov si,[ds:bp+2] // corrected top of shape for this segment
asm add bp,6 // next segment list
asm mov ax,SCREENSEG
asm mov es,ax
asm mov al,ss:[mask1]
asm out dx,al // set map mask register
asm call ss:[linescale] // scale the segment of pixels
asm inc di
asm mov al,ss:[mask2]
asm out dx,al // set map mask register
asm call ss:[linescale] // scale the segment of pixels
asm inc di
asm mov al,ss:[mask3]
asm out dx,al // set map mask register
asm call ss:[linescale] // scale the segment of pixels
asm dec di
asm dec di
asm mov es,WORD PTR ss:[linescale+2] // segment of scaler
asm mov BYTE PTR es:[bx],cl // unpatch the RETF
asm jmp scaletriple // do the next segment
}
/*
=======================
=
= scalevis
=
= Draws a compiled shape at [scale] pixels high
=
= each vertical line of the shape has a pointer to segment data:
= end of segment pixel*2 (0 terminates line) used to patch rtl in scaler
= top of virtual line with segment in proper place
= start of segment pixel*2, used to jsl into compiled scaler
= <repeat>
=
= Setup for call
= --------------
= GC_MODE read mode 1, write mode 2
= GC_COLORDONTCARE set to 0, so all reads from video memory return 0xff
= GC_INDEX pointing at GC_BITMASK
=
=======================
*/
static s32int longtemp;
void scalevis (s16int xcenter, s16int shapenum, u16int height)
{
t_compshape _seg *shape;
t_compscale _seg *comptable;
u16int scale,srcx,stopx,tempx; /* /!\ scale shadow */
s16int t;
u16int far *cmdptr;
int leftvis,rightvis;
shape = PM_GetSpritePage (shapenum);
scale = height>>3; // low three bits are fractional
if (!scale || scale>maxscale)
return; // too close or far away
comptable = scaledirectory[scale];
*(((u16int *)&linescale)+1)=(u16int)comptable; // seg of far call
*(((u16int *)&linecmds)+1)=(u16int)shape; // seg of shape
//
// scale to the left (from pixel 31 to shape->leftpix)
//
srcx = 32;
slinex = xcenter;
stopx = shape->leftpix;
cmdptr = &shape->dataofs[31-stopx];
while ( --srcx >=stopx && slinex>0)
{
(u16int)linecmds = *cmdptr--;
if ( !(slinewidth = comptable->width[srcx]) )
continue;
if (slinewidth == 1)
{
slinex--;
if (slinex<vw.dx)
{
if (wallheight[slinex] >= height)
continue; // obscured by closer wall
ScaleLine ();
}
continue;
}
//
// handle multi pixel lines
//
if (slinex>vw.dx)
{
slinex -= slinewidth;
slinewidth = vw.dx-slinex;
if (slinewidth<1)
continue; // still off the right side
}
else
{
if (slinewidth>slinex)
slinewidth = slinex;
slinex -= slinewidth;
}
leftvis = (wallheight[slinex] < height);
rightvis = (wallheight[slinex+slinewidth-1] < height);
if (leftvis)
{
if (rightvis)
ScaleLine ();
else
{
while (wallheight[slinex+slinewidth-1] >= height)
slinewidth--;
ScaleLine ();
}
}
else
{
if (!rightvis)
continue; // totally obscured
while (wallheight[slinex] >= height)
{
slinex++;
slinewidth--;
}
ScaleLine ();
break; // the rest of the shape is gone
}
}
//
// scale to the right
//
slinex = xcenter;
stopx = shape->rightpix;
if (shape->leftpix<31)
{
srcx = 31;
cmdptr = &shape->dataofs[32-shape->leftpix];
}
else
{
srcx = shape->leftpix-1;
cmdptr = &shape->dataofs[0];
}
slinewidth = 0;
while ( ++srcx <= stopx && (slinex+=slinewidth)<vw.dx)
{
(u16int)linecmds = *cmdptr++;
if ( !(slinewidth = comptable->width[srcx]) )
continue;
if (slinewidth == 1)
{
if (slinex>=0 && wallheight[slinex] < height)
{
ScaleLine ();
}
continue;
}
//
// handle multi pixel lines
//
if (slinex<0)
{
if (slinewidth <= -slinex)
continue; // still off the left edge
slinewidth += slinex;
slinex = 0;
}
else
{
if (slinex + slinewidth > vw.dx)
slinewidth = vw.dx-slinex;
}
leftvis = (wallheight[slinex] < height);
rightvis = (wallheight[slinex+slinewidth-1] < height);
if (leftvis)
{
if (rightvis)
{
ScaleLine ();
}
else
{
while (wallheight[slinex+slinewidth-1] >= height)
slinewidth--;
ScaleLine ();
break; // the rest of the shape is gone
}
}
else
{
if (rightvis)
{
while (wallheight[slinex] >= height)
{
slinex++;
slinewidth--;
}
ScaleLine ();
}
else
continue; // totally obscured
}
}
}
/*
=======================
=
= scalespr
=
= NO CLIPPING, height in pixels
=
= Draws a compiled shape at [scale] pixels high
=
= each vertical line of the shape has a pointer to segment data:
= end of segment pixel*2 (0 terminates line) used to patch rtl in scaler
= top of virtual line with segment in proper place
= start of segment pixel*2, used to jsl into compiled scaler
= <repeat>
=
= Setup for call
= --------------
= GC_MODE read mode 1, write mode 2
= GC_COLORDONTCARE set to 0, so all reads from video memory return 0xff
= GC_INDEX pointing at GC_BITMASK
=
=======================
*/
void scalespr (s16int xcenter, s16int shapenum, u16int height)
{
t_compshape _seg *shape;
t_compscale _seg *comptable;
u16int scale,srcx,stopx,tempx; /* /!\ scale shadow */
s16int t;
u16int far *cmdptr;
int leftvis,rightvis;
shape = PM_GetSpritePage (shapenum);
scale = height>>1;
comptable = scaledirectory[scale];
*(((u16int *)&linescale)+1)=(u16int)comptable; // seg of far call
*(((u16int *)&linecmds)+1)=(u16int)shape; // seg of shape
//
// scale to the left (from pixel 31 to shape->leftpix)
//
srcx = 32;
slinex = xcenter;
stopx = shape->leftpix;
cmdptr = &shape->dataofs[31-stopx];
while ( --srcx >=stopx )
{
(u16int)linecmds = *cmdptr--;
if ( !(slinewidth = comptable->width[srcx]) )
continue;
slinex -= slinewidth;
ScaleLine ();
}
//
// scale to the right
//
slinex = xcenter;
stopx = shape->rightpix;
if (shape->leftpix<31)
{
srcx = 31;
cmdptr = &shape->dataofs[32-shape->leftpix];
}
else
{
srcx = shape->leftpix-1;
cmdptr = &shape->dataofs[0];
}
slinewidth = 0;
while ( ++srcx <= stopx )
{
(u16int)linecmds = *cmdptr++;
if ( !(slinewidth = comptable->width[srcx]) )
continue;
ScaleLine ();
slinex+=slinewidth;
}
}
//
// bit mask tables for drawing scaled strips up to eight pixels wide
//
// down here so the STUPID inline assembler doesn't get confused!
//
u8int mapmasks1[4][8] = {
{1 ,3 ,7 ,15,15,15,15,15},
{2 ,6 ,14,14,14,14,14,14},
{4 ,12,12,12,12,12,12,12},
{8 ,8 ,8 ,8 ,8 ,8 ,8 ,8} };
u8int mapmasks2[4][8] = {
{0 ,0 ,0 ,0 ,1 ,3 ,7 ,15},
{0 ,0 ,0 ,1 ,3 ,7 ,15,15},
{0 ,0 ,1 ,3 ,7 ,15,15,15},
{0 ,1 ,3 ,7 ,15,15,15,15} };
u8int mapmasks3[4][8] = {
{0 ,0 ,0 ,0 ,0 ,0 ,0 ,0},
{0 ,0 ,0 ,0 ,0 ,0 ,0 ,1},
{0 ,0 ,0 ,0 ,0 ,0 ,1 ,3},
{0 ,0 ,0 ,0 ,0 ,1 ,3 ,7} };
u16int wordmasks[8][8] = {
{0x0080,0x00c0,0x00e0,0x00f0,0x00f8,0x00fc,0x00fe,0x00ff},
{0x0040,0x0060,0x0070,0x0078,0x007c,0x007e,0x007f,0x807f},
{0x0020,0x0030,0x0038,0x003c,0x003e,0x003f,0x803f,0xc03f},
{0x0010,0x0018,0x001c,0x001e,0x001f,0x801f,0xc01f,0xe01f},
{0x0008,0x000c,0x000e,0x000f,0x800f,0xc00f,0xe00f,0xf00f},
{0x0004,0x0006,0x0007,0x8007,0xc007,0xe007,0xf007,0xf807},
{0x0002,0x0003,0x8003,0xc003,0xe003,0xf003,0xf803,0xfc03},
{0x0001,0x8001,0xc001,0xe001,0xf001,0xf801,0xfc01,0xfe01} };
s16int slinex,slinewidth;
u16int far *linecmds;
s32int linescale;
u16int maskword;