mirror of
https://github.com/dborth/vbagx.git
synced 2024-11-22 18:49:18 +01:00
add USB/SD hot-swap, video changes, remove un-needed VBA file
This commit is contained in:
parent
1d17d3f185
commit
a68f67903d
@ -28,75 +28,84 @@
|
|||||||
FILE * fatfile;
|
FILE * fatfile;
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* fat_is_mounted
|
* MountFAT
|
||||||
* to check whether FAT media are detected.
|
* Attempts to mount the FAT device specified
|
||||||
|
* Sets libfat to use the device by default
|
||||||
|
* Enables read-ahead cache for SD/USB
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
bool MountFAT(PARTITION_INTERFACE part)
|
||||||
|
{
|
||||||
|
bool mounted = fatMountNormalInterface(part, 8);
|
||||||
|
|
||||||
bool FatIsMounted(PARTITION_INTERFACE partition) {
|
if(mounted)
|
||||||
char prefix[] = "fatX:/";
|
{
|
||||||
prefix[3] = partition + '0';
|
fatSetDefaultInterface(part);
|
||||||
DIR_ITER *dir = diropen(prefix);
|
#ifdef HW_RVL
|
||||||
if (dir) {
|
if(part == PI_INTERNAL_SD || part == PI_USBSTORAGE)
|
||||||
dirclose(dir);
|
fatEnableReadAhead (part, 6, 64);
|
||||||
return true;
|
#endif
|
||||||
}
|
}
|
||||||
return false;
|
return mounted;
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* changeFATInterface
|
* UnmountFAT
|
||||||
* Checks if the device (method) specified is available, and
|
* Unmounts the FAT device specified
|
||||||
* sets libfat to use the device
|
***************************************************************************/
|
||||||
|
void UnmountFAT(PARTITION_INTERFACE part)
|
||||||
|
{
|
||||||
|
if(!fatUnmount(part))
|
||||||
|
fatUnsafeUnmount(part);
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* UnmountAllFAT
|
||||||
|
* Unmounts all FAT devices
|
||||||
|
***************************************************************************/
|
||||||
|
void UnmountAllFAT()
|
||||||
|
{
|
||||||
|
#ifdef HW_RVL
|
||||||
|
UnmountFAT(PI_INTERNAL_SD);
|
||||||
|
UnmountFAT(PI_USBSTORAGE);
|
||||||
|
#endif
|
||||||
|
UnmountFAT(PI_SDGECKO_A);
|
||||||
|
UnmountFAT(PI_SDGECKO_B);
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* ChangeFATInterface
|
||||||
|
* Unmounts all devices and attempts to mount/configure the device specified
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
bool ChangeFATInterface(int method, bool silent)
|
bool ChangeFATInterface(int method, bool silent)
|
||||||
{
|
{
|
||||||
bool devFound = false;
|
bool mounted = false;
|
||||||
|
|
||||||
|
// unmount all FAT devices
|
||||||
|
UnmountAllFAT();
|
||||||
|
|
||||||
if(method == METHOD_SD)
|
if(method == METHOD_SD)
|
||||||
{
|
{
|
||||||
// check which SD device is loaded
|
|
||||||
|
|
||||||
#ifdef HW_RVL
|
#ifdef HW_RVL
|
||||||
if (FatIsMounted(PI_INTERNAL_SD))
|
mounted = MountFAT(PI_INTERNAL_SD); // try Wii internal SD
|
||||||
{
|
|
||||||
devFound = true;
|
|
||||||
fatSetDefaultInterface(PI_INTERNAL_SD);
|
|
||||||
fatEnableReadAhead (PI_INTERNAL_SD, 6, 64);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!devFound && FatIsMounted(PI_SDGECKO_A))
|
if(!mounted) // internal SD not found
|
||||||
{
|
mounted = MountFAT(PI_SDGECKO_A); // try SD Gecko on slot A
|
||||||
devFound = true;
|
if(!mounted) // internal SD and SD Gecko (on slot A) not found
|
||||||
}
|
mounted = MountFAT(PI_SDGECKO_B); // try SD Gecko on slot B
|
||||||
if(!devFound && FatIsMounted(PI_SDGECKO_B))
|
if(!mounted && !silent) // no SD device found
|
||||||
{
|
WaitPrompt ((char *)"SD card not found!");
|
||||||
devFound = true;
|
|
||||||
}
|
|
||||||
if(!devFound)
|
|
||||||
{
|
|
||||||
if(!silent)
|
|
||||||
WaitPrompt ((char *)"SD card not found!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if(method == METHOD_USB)
|
else if(method == METHOD_USB)
|
||||||
{
|
{
|
||||||
#ifdef HW_RVL
|
#ifdef HW_RVL
|
||||||
if(FatIsMounted(PI_USBSTORAGE))
|
mounted = MountFAT(PI_USBSTORAGE);
|
||||||
{
|
if(!mounted && !silent)
|
||||||
devFound = true;
|
WaitPrompt ((char *)"USB drive not found!");
|
||||||
fatSetDefaultInterface(PI_USBSTORAGE);
|
|
||||||
fatEnableReadAhead (PI_USBSTORAGE, 6, 64);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(!silent)
|
|
||||||
WaitPrompt ((char *)"USB flash drive not found!");
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return devFound;
|
return mounted;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
|
@ -373,7 +373,7 @@ int FileSelector (int method)
|
|||||||
|
|
||||||
if (!maxfiles)
|
if (!maxfiles)
|
||||||
{
|
{
|
||||||
WaitPrompt ((char*) "Error reading directory !");
|
WaitPrompt ((char*) "Error reading directory!");
|
||||||
haverom = 1; // quit menu
|
haverom = 1; // quit menu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -384,6 +384,11 @@ int FileSelector (int method)
|
|||||||
}
|
}
|
||||||
else // this is a file
|
else // this is a file
|
||||||
{
|
{
|
||||||
|
// better do another unmount/remount, just in case
|
||||||
|
if(method == METHOD_SD || method == METHOD_USB)
|
||||||
|
if(!ChangeFATInterface(method, NOTSILENT))
|
||||||
|
return 0;
|
||||||
|
|
||||||
// 7z file - let's open it up to select a file inside
|
// 7z file - let's open it up to select a file inside
|
||||||
if(IsSz())
|
if(IsSz())
|
||||||
{
|
{
|
||||||
@ -391,7 +396,7 @@ int FileSelector (int method)
|
|||||||
if(!MakeROMPath(szpath, method))
|
if(!MakeROMPath(szpath, method))
|
||||||
{
|
{
|
||||||
WaitPrompt((char*) "Maximum filepath length reached!");
|
WaitPrompt((char*) "Maximum filepath length reached!");
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
int szfiles = SzParse(szpath, method);
|
int szfiles = SzParse(szpath, method);
|
||||||
if(szfiles)
|
if(szfiles)
|
||||||
|
@ -184,10 +184,14 @@ static void draw_init(void)
|
|||||||
|
|
||||||
GX_SetTexCoordGen (GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
|
GX_SetTexCoordGen (GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
|
||||||
|
|
||||||
GX_InvalidateTexAll();
|
GX_SetTevOp (GX_TEVSTAGE0, GX_REPLACE);
|
||||||
|
GX_SetTevOrder (GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLORNULL);
|
||||||
|
|
||||||
memset (&view, 0, sizeof (Mtx));
|
memset (&view, 0, sizeof (Mtx));
|
||||||
guLookAt(view, &cam.pos, &cam.up, &cam.view);
|
guLookAt(view, &cam.pos, &cam.up, &cam.view);
|
||||||
GX_LoadPosMtxImm (view, GX_PNMTX0);
|
GX_LoadPosMtxImm (view, GX_PNMTX0);
|
||||||
|
|
||||||
|
GX_InvVtxCache (); // update vertex cache
|
||||||
}
|
}
|
||||||
|
|
||||||
static void draw_vert(u8 pos, u8 c, f32 s, f32 t)
|
static void draw_vert(u8 pos, u8 c, f32 s, f32 t)
|
||||||
@ -251,9 +255,6 @@ void GX_Start()
|
|||||||
guPerspective(p, 60, 1.33F, 10.0F, 1000.0F);
|
guPerspective(p, 60, 1.33F, 10.0F, 1000.0F);
|
||||||
GX_LoadProjectionMtx(p, GX_PERSPECTIVE);
|
GX_LoadProjectionMtx(p, GX_PERSPECTIVE);
|
||||||
|
|
||||||
GX_SetTevOp(GX_TEVSTAGE0, GX_DECAL);
|
|
||||||
GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
|
|
||||||
|
|
||||||
GX_CopyDisp (xfb[whichfb], GX_TRUE); // reset xfb
|
GX_CopyDisp (xfb[whichfb], GX_TRUE); // reset xfb
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,6 +328,9 @@ void InitialiseVideo ()
|
|||||||
xfb[0] = (u32 *) MEM_K0_TO_K1 (SYS_AllocateFramebuffer (vmode));
|
xfb[0] = (u32 *) MEM_K0_TO_K1 (SYS_AllocateFramebuffer (vmode));
|
||||||
xfb[1] = (u32 *) MEM_K0_TO_K1 (SYS_AllocateFramebuffer (vmode));
|
xfb[1] = (u32 *) MEM_K0_TO_K1 (SYS_AllocateFramebuffer (vmode));
|
||||||
|
|
||||||
|
// A console is always useful while debugging
|
||||||
|
console_init (xfb[0], 20, 64, vmode->fbWidth, vmode->xfbHeight, vmode->fbWidth * 2);
|
||||||
|
|
||||||
// Clear framebuffers etc.
|
// Clear framebuffers etc.
|
||||||
VIDEO_ClearFrameBuffer (vmode, xfb[0], COLOR_BLACK);
|
VIDEO_ClearFrameBuffer (vmode, xfb[0], COLOR_BLACK);
|
||||||
VIDEO_ClearFrameBuffer (vmode, xfb[1], COLOR_BLACK);
|
VIDEO_ClearFrameBuffer (vmode, xfb[1], COLOR_BLACK);
|
||||||
@ -370,21 +374,15 @@ void UpdateScaling()
|
|||||||
yscale *= GCSettings.ZoomLevel;
|
yscale *= GCSettings.ZoomLevel;
|
||||||
|
|
||||||
// Set new aspect (now with crap AR hack!)
|
// Set new aspect (now with crap AR hack!)
|
||||||
square[0] = square[9] = (-xscale - 7);
|
square[0] = square[9] = (-xscale - 7);
|
||||||
square[3] = square[6] = (xscale + 7);
|
square[3] = square[6] = ( xscale + 7);
|
||||||
square[1] = square[4] = (yscale + 7);
|
square[1] = square[4] = ( yscale + 7);
|
||||||
square[7] = square[10] = (-yscale - 7);
|
square[7] = square[10] = (-yscale - 7);
|
||||||
|
|
||||||
GX_InvVtxCache (); // update vertex cache
|
draw_init ();
|
||||||
|
|
||||||
GX_InitTexObj (&texobj, texturemem, vwidth, vheight, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE); // initialize the texture obj we are going to use
|
if(updateScaling)
|
||||||
|
updateScaling--;
|
||||||
if (GCSettings.render == 1)
|
|
||||||
GX_InitTexObjLOD(&texobj,GX_NEAR,GX_NEAR_MIP_NEAR,2.5,9.0,0.0,GX_FALSE,GX_FALSE,GX_ANISO_1); // original/unfiltered video mode: force texture filtering OFF
|
|
||||||
|
|
||||||
GX_LoadTexObj (&texobj, GX_TEXMAP0); // load texture object so its ready to use
|
|
||||||
|
|
||||||
updateScaling--;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
@ -400,6 +398,7 @@ ResetVideo_Emu ()
|
|||||||
|
|
||||||
rmode = vmode; // same mode as menu
|
rmode = vmode; // same mode as menu
|
||||||
|
|
||||||
|
// reconfigure VI
|
||||||
VIDEO_Configure (rmode);
|
VIDEO_Configure (rmode);
|
||||||
VIDEO_ClearFrameBuffer (vmode, xfb[whichfb], COLOR_BLACK);
|
VIDEO_ClearFrameBuffer (vmode, xfb[whichfb], COLOR_BLACK);
|
||||||
VIDEO_Flush();
|
VIDEO_Flush();
|
||||||
@ -410,21 +409,27 @@ ResetVideo_Emu ()
|
|||||||
while (VIDEO_GetNextField())
|
while (VIDEO_GetNextField())
|
||||||
VIDEO_WaitVSync();
|
VIDEO_WaitVSync();
|
||||||
|
|
||||||
|
// reconfigure GX
|
||||||
GX_SetViewport (0, 0, rmode->fbWidth, rmode->efbHeight, 0, 1);
|
GX_SetViewport (0, 0, rmode->fbWidth, rmode->efbHeight, 0, 1);
|
||||||
GX_SetDispCopyYScale ((f32) rmode->xfbHeight / (f32) rmode->efbHeight);
|
GX_SetDispCopyYScale ((f32) rmode->xfbHeight / (f32) rmode->efbHeight);
|
||||||
GX_SetScissor (0, 0, rmode->fbWidth, rmode->efbHeight);
|
GX_SetScissor (0, 0, rmode->fbWidth, rmode->efbHeight);
|
||||||
|
|
||||||
GX_SetDispCopySrc (0, 0, rmode->fbWidth, rmode->efbHeight);
|
GX_SetDispCopySrc (0, 0, rmode->fbWidth, rmode->efbHeight);
|
||||||
GX_SetDispCopyDst (rmode->fbWidth, rmode->xfbHeight);
|
GX_SetDispCopyDst (rmode->fbWidth, rmode->xfbHeight);
|
||||||
GX_SetCopyFilter (rmode->aa, rmode->sample_pattern, (GCSettings.render == 0) ? GX_TRUE : GX_FALSE, rmode->vfilter); // AA on only for filtered mode
|
GX_SetCopyFilter (rmode->aa, rmode->sample_pattern, (GCSettings.render == 1) ? GX_TRUE : GX_FALSE, rmode->vfilter); // deflickering filter only for filtered mode
|
||||||
|
|
||||||
GX_SetFieldMode (rmode->field_rendering, ((rmode->viHeight == 2 * rmode->xfbHeight) ? GX_ENABLE : GX_DISABLE));
|
GX_SetFieldMode (rmode->field_rendering, ((rmode->viHeight == 2 * rmode->xfbHeight) ? GX_ENABLE : GX_DISABLE));
|
||||||
GX_SetPixelFmt (GX_PF_RGB8_Z24, GX_ZC_LINEAR);
|
GX_SetPixelFmt (GX_PF_RGB8_Z24, GX_ZC_LINEAR);
|
||||||
|
|
||||||
guPerspective(p, 60, 1.33F, 10.0F, 1000.0F);
|
guPerspective(p, 60, 1.33F, 10.0F, 1000.0F);
|
||||||
GX_LoadProjectionMtx(p, GX_PERSPECTIVE);
|
GX_LoadProjectionMtx(p, GX_PERSPECTIVE);
|
||||||
|
|
||||||
updateScaling = 20;
|
// reinitialize texture
|
||||||
|
GX_InvalidateTexAll ();
|
||||||
|
GX_InitTexObj (&texobj, texturemem, vwidth, vheight, GX_TF_RGB565, GX_CLAMP, GX_CLAMP, GX_FALSE); // initialize the texture obj we are going to use
|
||||||
|
if (!(GCSettings.render&1))
|
||||||
|
GX_InitTexObjLOD(&texobj,GX_NEAR,GX_NEAR_MIP_NEAR,2.5,9.0,0.0,GX_FALSE,GX_FALSE,GX_ANISO_1); // original/unfiltered video mode: force texture filtering OFF
|
||||||
|
|
||||||
|
// set aspect ratio
|
||||||
|
updateScaling = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
@ -511,6 +516,9 @@ void GX_Render(int width, int height, u8 * buffer, int pitch)
|
|||||||
if(updateScaling)
|
if(updateScaling)
|
||||||
UpdateScaling();
|
UpdateScaling();
|
||||||
|
|
||||||
|
// clear texture objects
|
||||||
|
GX_InvalidateTexAll();
|
||||||
|
|
||||||
for (h = 0; h < vheight; h += 4)
|
for (h = 0; h < vheight; h += 4)
|
||||||
{
|
{
|
||||||
for (w = 0; w < (vwidth >> 2); w++)
|
for (w = 0; w < (vwidth >> 2); w++)
|
||||||
@ -539,13 +547,15 @@ void GX_Render(int width, int height, u8 * buffer, int pitch)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// load texture into GX
|
||||||
DCFlushRange(texturemem, texturesize);
|
DCFlushRange(texturemem, texturesize);
|
||||||
GX_InvalidateTexAll ();
|
GX_LoadTexObj (&texobj, GX_TEXMAP0);
|
||||||
|
|
||||||
|
// render textured quad
|
||||||
draw_square(view);
|
draw_square(view);
|
||||||
|
|
||||||
GX_DrawDone();
|
GX_DrawDone();
|
||||||
|
|
||||||
|
// EFB is ready to be copied into XFB
|
||||||
VIDEO_SetNextFramebuffer(xfb[whichfb]);
|
VIDEO_SetNextFramebuffer(xfb[whichfb]);
|
||||||
VIDEO_Flush();
|
VIDEO_Flush();
|
||||||
copynow = GX_TRUE;
|
copynow = GX_TRUE;
|
||||||
@ -570,12 +580,12 @@ zoom (float speed)
|
|||||||
else if (GCSettings.ZoomLevel > 2.0)
|
else if (GCSettings.ZoomLevel > 2.0)
|
||||||
GCSettings.ZoomLevel = 2.0;
|
GCSettings.ZoomLevel = 2.0;
|
||||||
|
|
||||||
updateScaling = 60; // update video
|
updateScaling = 5; // update video
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
zoom_reset ()
|
zoom_reset ()
|
||||||
{
|
{
|
||||||
GCSettings.ZoomLevel = 1.0;
|
GCSettings.ZoomLevel = 1.0;
|
||||||
updateScaling = 60; // update video
|
updateScaling = 5; // update video
|
||||||
}
|
}
|
||||||
|
@ -1,581 +0,0 @@
|
|||||||
// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
|
|
||||||
// Copyright (C) 1999-2003 Forgotten
|
|
||||||
// Copyright (C) 2004 Forgotten and the VBA development team
|
|
||||||
|
|
||||||
// This program is free software; you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation; either version 2, or(at your option)
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program; if not, write to the Free Software Foundation,
|
|
||||||
// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
#include "System.h"
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#ifdef MMX
|
|
||||||
extern "C" bool cpu_mmx;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Thanks to Kawaks' Mr. K for the code
|
|
||||||
|
|
||||||
Incorporated into vba by Anthony Di Franco
|
|
||||||
*/
|
|
||||||
|
|
||||||
static u8 *frm1 = NULL;
|
|
||||||
static u8 *frm2 = NULL;
|
|
||||||
static u8 *frm3 = NULL;
|
|
||||||
|
|
||||||
extern int RGB_LOW_BITS_MASK;
|
|
||||||
extern u32 qRGB_COLOR_MASK[2];
|
|
||||||
|
|
||||||
static void Init()
|
|
||||||
{
|
|
||||||
frm1 = (u8 *)calloc(322*242,4);
|
|
||||||
// 1 frame ago
|
|
||||||
frm2 = (u8 *)calloc(322*242,4);
|
|
||||||
// 2 frames ago
|
|
||||||
frm3 = (u8 *)calloc(322*242,4);
|
|
||||||
// 3 frames ago
|
|
||||||
}
|
|
||||||
|
|
||||||
void InterframeCleanup()
|
|
||||||
{
|
|
||||||
if(frm1)
|
|
||||||
free(frm1);
|
|
||||||
if(frm2)
|
|
||||||
free(frm2);
|
|
||||||
if(frm3)
|
|
||||||
free(frm3);
|
|
||||||
frm1 = frm2 = frm3 = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef MMX
|
|
||||||
static void SmartIB_MMX(u8 *srcPtr, u32 srcPitch, int width, int height)
|
|
||||||
{
|
|
||||||
u16 *src0 = (u16 *)srcPtr;
|
|
||||||
u16 *src1 = (u16 *)frm1;
|
|
||||||
u16 *src2 = (u16 *)frm2;
|
|
||||||
u16 *src3 = (u16 *)frm3;
|
|
||||||
|
|
||||||
int count = width >> 2;
|
|
||||||
|
|
||||||
for(int i = 0; i < height; i++) {
|
|
||||||
#ifdef __GNUC__
|
|
||||||
asm volatile (
|
|
||||||
"push %4\n"
|
|
||||||
"movq 0(%5), %%mm7\n" // colorMask
|
|
||||||
"0:\n"
|
|
||||||
"movq 0(%0), %%mm0\n" // src0
|
|
||||||
"movq 0(%1), %%mm1\n" // src1
|
|
||||||
"movq 0(%2), %%mm2\n" // src2
|
|
||||||
"movq 0(%3), %%mm3\n" // src3
|
|
||||||
"movq %%mm0, 0(%3)\n" // src3 = src0
|
|
||||||
"movq %%mm0, %%mm4\n"
|
|
||||||
"movq %%mm1, %%mm5\n"
|
|
||||||
"pcmpeqw %%mm2, %%mm5\n" // src1 == src2 (A)
|
|
||||||
"pcmpeqw %%mm3, %%mm4\n" // src3 == src0 (B)
|
|
||||||
"por %%mm5, %%mm4\n" // A | B
|
|
||||||
"movq %%mm2, %%mm5\n"
|
|
||||||
"pcmpeqw %%mm0, %%mm5\n" // src0 == src2 (C)
|
|
||||||
"pcmpeqw %%mm1, %%mm3\n" // src1 == src3 (D)
|
|
||||||
"por %%mm3, %%mm5\n" // C|D
|
|
||||||
"pandn %%mm5, %%mm4\n" // (!(A|B))&(C|D)
|
|
||||||
"movq %%mm0, %%mm2\n"
|
|
||||||
"pand %%mm7, %%mm2\n" // color & colorMask
|
|
||||||
"pand %%mm7, %%mm1\n" // src1 & colorMask
|
|
||||||
"psrlw $1, %%mm2\n" // (color & colorMask) >> 1 (E)
|
|
||||||
"psrlw $1, %%mm1\n" // (src & colorMask) >> 1 (F)
|
|
||||||
"paddw %%mm2, %%mm1\n" // E+F
|
|
||||||
"pand %%mm4, %%mm1\n" // (E+F) & res
|
|
||||||
"pandn %%mm0, %%mm4\n" // color& !res
|
|
||||||
|
|
||||||
"por %%mm1, %%mm4\n"
|
|
||||||
"movq %%mm4, 0(%0)\n" // src0 = res
|
|
||||||
|
|
||||||
"addl $8, %0\n"
|
|
||||||
"addl $8, %1\n"
|
|
||||||
"addl $8, %2\n"
|
|
||||||
"addl $8, %3\n"
|
|
||||||
|
|
||||||
"decl %4\n"
|
|
||||||
"jnz 0b\n"
|
|
||||||
"pop %4\n"
|
|
||||||
"emms\n"
|
|
||||||
: "+r" (src0), "+r" (src1), "+r" (src2), "+r" (src3)
|
|
||||||
: "r" (count), "r" (qRGB_COLOR_MASK)
|
|
||||||
);
|
|
||||||
#else
|
|
||||||
__asm {
|
|
||||||
movq mm7, qword ptr [qRGB_COLOR_MASK];
|
|
||||||
mov eax, src0;
|
|
||||||
mov ebx, src1;
|
|
||||||
mov ecx, src2;
|
|
||||||
mov edx, src3;
|
|
||||||
mov edi, count;
|
|
||||||
label0:
|
|
||||||
movq mm0, qword ptr [eax]; // src0
|
|
||||||
movq mm1, qword ptr [ebx]; // src1
|
|
||||||
movq mm2, qword ptr [ecx]; // src2
|
|
||||||
movq mm3, qword ptr [edx]; // src3
|
|
||||||
movq qword ptr [edx], mm0; // src3 = src0
|
|
||||||
movq mm4, mm0;
|
|
||||||
movq mm5, mm1;
|
|
||||||
pcmpeqw mm5, mm2; // src1 == src2 (A)
|
|
||||||
pcmpeqw mm4, mm3; // src3 == src0 (B)
|
|
||||||
por mm4, mm5; // A | B
|
|
||||||
movq mm5, mm2;
|
|
||||||
pcmpeqw mm5, mm0; // src0 == src2 (C)
|
|
||||||
pcmpeqw mm3, mm1; // src1 == src3 (D)
|
|
||||||
por mm5, mm3; // C|D
|
|
||||||
pandn mm4, mm5; // (!(A|B))&(C|D)
|
|
||||||
movq mm2, mm0;
|
|
||||||
pand mm2, mm7; // color & colorMask
|
|
||||||
pand mm1, mm7; // src1 & colorMask
|
|
||||||
psrlw mm2, 1; // (color & colorMask) >> 1 (E)
|
|
||||||
psrlw mm1, 1; // (src & colorMask) >> 1 (F)
|
|
||||||
paddw mm1, mm2; // E+F
|
|
||||||
pand mm1, mm4; // (E+F) & res
|
|
||||||
pandn mm4, mm0; // color & !res
|
|
||||||
|
|
||||||
por mm4, mm1;
|
|
||||||
movq qword ptr [eax], mm4; // src0 = res
|
|
||||||
|
|
||||||
add eax, 8;
|
|
||||||
add ebx, 8;
|
|
||||||
add ecx, 8;
|
|
||||||
add edx, 8;
|
|
||||||
|
|
||||||
dec edi;
|
|
||||||
jnz label0;
|
|
||||||
mov src0, eax;
|
|
||||||
mov src1, ebx;
|
|
||||||
mov src2, ecx;
|
|
||||||
mov src3, edx;
|
|
||||||
emms;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
src0+=2;
|
|
||||||
src1+=2;
|
|
||||||
src2+=2;
|
|
||||||
src3+=2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Swap buffers around */
|
|
||||||
u8 *temp = frm1;
|
|
||||||
frm1 = frm3;
|
|
||||||
frm3 = frm2;
|
|
||||||
frm2 = temp;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void SmartIB(u8 *srcPtr, u32 srcPitch, int width, int height)
|
|
||||||
{
|
|
||||||
if(frm1 == NULL) {
|
|
||||||
Init();
|
|
||||||
}
|
|
||||||
#ifdef MMX
|
|
||||||
if(cpu_mmx) {
|
|
||||||
SmartIB_MMX(srcPtr, srcPitch, width, height);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
u16 colorMask = ~RGB_LOW_BITS_MASK;
|
|
||||||
|
|
||||||
u16 *src0 = (u16 *)srcPtr;
|
|
||||||
u16 *src1 = (u16 *)frm1;
|
|
||||||
u16 *src2 = (u16 *)frm2;
|
|
||||||
u16 *src3 = (u16 *)frm3;
|
|
||||||
|
|
||||||
int sPitch = srcPitch >> 1;
|
|
||||||
|
|
||||||
int pos = 0;
|
|
||||||
for (int j = 0; j < height; j++)
|
|
||||||
for (int i = 0; i < sPitch; i++) {
|
|
||||||
u16 color = src0[pos];
|
|
||||||
src0[pos] =
|
|
||||||
(src1[pos] != src2[pos]) &&
|
|
||||||
(src3[pos] != color) &&
|
|
||||||
((color == src2[pos]) || (src1[pos] == src3[pos]))
|
|
||||||
? (((color & colorMask) >> 1) + ((src1[pos] & colorMask) >> 1)) :
|
|
||||||
color;
|
|
||||||
src3[pos] = color; /* oldest buffer now holds newest frame */
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Swap buffers around */
|
|
||||||
u8 *temp = frm1;
|
|
||||||
frm1 = frm3;
|
|
||||||
frm3 = frm2;
|
|
||||||
frm2 = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef MMX
|
|
||||||
static void SmartIB32_MMX(u8 *srcPtr, u32 srcPitch, int width, int height)
|
|
||||||
{
|
|
||||||
u32 *src0 = (u32 *)srcPtr;
|
|
||||||
u32 *src1 = (u32 *)frm1;
|
|
||||||
u32 *src2 = (u32 *)frm2;
|
|
||||||
u32 *src3 = (u32 *)frm3;
|
|
||||||
|
|
||||||
int count = width >> 1;
|
|
||||||
|
|
||||||
for(int i = 0; i < height; i++) {
|
|
||||||
#ifdef __GNUC__
|
|
||||||
asm volatile (
|
|
||||||
"push %4\n"
|
|
||||||
"movq 0(%5), %%mm7\n" // colorMask
|
|
||||||
"0:\n"
|
|
||||||
"movq 0(%0), %%mm0\n" // src0
|
|
||||||
"movq 0(%1), %%mm1\n" // src1
|
|
||||||
"movq 0(%2), %%mm2\n" // src2
|
|
||||||
"movq 0(%3), %%mm3\n" // src3
|
|
||||||
"movq %%mm0, 0(%3)\n" // src3 = src0
|
|
||||||
"movq %%mm0, %%mm4\n"
|
|
||||||
"movq %%mm1, %%mm5\n"
|
|
||||||
"pcmpeqd %%mm2, %%mm5\n" // src1 == src2 (A)
|
|
||||||
"pcmpeqd %%mm3, %%mm4\n" // src3 == src0 (B)
|
|
||||||
"por %%mm5, %%mm4\n" // A | B
|
|
||||||
"movq %%mm2, %%mm5\n"
|
|
||||||
"pcmpeqd %%mm0, %%mm5\n" // src0 == src2 (C)
|
|
||||||
"pcmpeqd %%mm1, %%mm3\n" // src1 == src3 (D)
|
|
||||||
"por %%mm3, %%mm5\n" // C|D
|
|
||||||
"pandn %%mm5, %%mm4\n" // (!(A|B))&(C|D)
|
|
||||||
"movq %%mm0, %%mm2\n"
|
|
||||||
"pand %%mm7, %%mm2\n" // color & colorMask
|
|
||||||
"pand %%mm7, %%mm1\n" // src1 & colorMask
|
|
||||||
"psrld $1, %%mm2\n" // (color & colorMask) >> 1 (E)
|
|
||||||
"psrld $1, %%mm1\n" // (src & colorMask) >> 1 (F)
|
|
||||||
"paddd %%mm2, %%mm1\n" // E+F
|
|
||||||
"pand %%mm4, %%mm1\n" // (E+F) & res
|
|
||||||
"pandn %%mm0, %%mm4\n" // color& !res
|
|
||||||
|
|
||||||
"por %%mm1, %%mm4\n"
|
|
||||||
"movq %%mm4, 0(%0)\n" // src0 = res
|
|
||||||
|
|
||||||
"addl $8, %0\n"
|
|
||||||
"addl $8, %1\n"
|
|
||||||
"addl $8, %2\n"
|
|
||||||
"addl $8, %3\n"
|
|
||||||
|
|
||||||
"decl %4\n"
|
|
||||||
"jnz 0b\n"
|
|
||||||
"pop %4\n"
|
|
||||||
"emms\n"
|
|
||||||
: "+r" (src0), "+r" (src1), "+r" (src2), "+r" (src3)
|
|
||||||
: "r" (count), "r" (qRGB_COLOR_MASK)
|
|
||||||
);
|
|
||||||
#else
|
|
||||||
__asm {
|
|
||||||
movq mm7, qword ptr [qRGB_COLOR_MASK];
|
|
||||||
mov eax, src0;
|
|
||||||
mov ebx, src1;
|
|
||||||
mov ecx, src2;
|
|
||||||
mov edx, src3;
|
|
||||||
mov edi, count;
|
|
||||||
label0:
|
|
||||||
movq mm0, qword ptr [eax]; // src0
|
|
||||||
movq mm1, qword ptr [ebx]; // src1
|
|
||||||
movq mm2, qword ptr [ecx]; // src2
|
|
||||||
movq mm3, qword ptr [edx]; // src3
|
|
||||||
movq qword ptr [edx], mm0; // src3 = src0
|
|
||||||
movq mm4, mm0;
|
|
||||||
movq mm5, mm1;
|
|
||||||
pcmpeqd mm5, mm2; // src1 == src2 (A)
|
|
||||||
pcmpeqd mm4, mm3; // src3 == src0 (B)
|
|
||||||
por mm4, mm5; // A | B
|
|
||||||
movq mm5, mm2;
|
|
||||||
pcmpeqd mm5, mm0; // src0 == src2 (C)
|
|
||||||
pcmpeqd mm3, mm1; // src1 == src3 (D)
|
|
||||||
por mm5, mm3; // C|D
|
|
||||||
pandn mm4, mm5; // (!(A|B))&(C|D)
|
|
||||||
movq mm2, mm0;
|
|
||||||
pand mm2, mm7; // color & colorMask
|
|
||||||
pand mm1, mm7; // src1 & colorMask
|
|
||||||
psrld mm2, 1; // (color & colorMask) >> 1 (E)
|
|
||||||
psrld mm1, 1; // (src & colorMask) >> 1 (F)
|
|
||||||
paddd mm1, mm2; // E+F
|
|
||||||
pand mm1, mm4; // (E+F) & res
|
|
||||||
pandn mm4, mm0; // color & !res
|
|
||||||
|
|
||||||
por mm4, mm1;
|
|
||||||
movq qword ptr [eax], mm4; // src0 = res
|
|
||||||
|
|
||||||
add eax, 8;
|
|
||||||
add ebx, 8;
|
|
||||||
add ecx, 8;
|
|
||||||
add edx, 8;
|
|
||||||
|
|
||||||
dec edi;
|
|
||||||
jnz label0;
|
|
||||||
mov src0, eax;
|
|
||||||
mov src1, ebx;
|
|
||||||
mov src2, ecx;
|
|
||||||
mov src3, edx;
|
|
||||||
emms;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
src0++;
|
|
||||||
src1++;
|
|
||||||
src2++;
|
|
||||||
src3++;
|
|
||||||
}
|
|
||||||
/* Swap buffers around */
|
|
||||||
u8 *temp = frm1;
|
|
||||||
frm1 = frm3;
|
|
||||||
frm3 = frm2;
|
|
||||||
frm2 = temp;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void SmartIB32(u8 *srcPtr, u32 srcPitch, int width, int height)
|
|
||||||
{
|
|
||||||
if(frm1 == NULL) {
|
|
||||||
Init();
|
|
||||||
}
|
|
||||||
#ifdef MMX
|
|
||||||
if(cpu_mmx) {
|
|
||||||
SmartIB32_MMX(srcPtr, srcPitch, width, height);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
u32 *src0 = (u32 *)srcPtr;
|
|
||||||
u32 *src1 = (u32 *)frm1;
|
|
||||||
u32 *src2 = (u32 *)frm2;
|
|
||||||
u32 *src3 = (u32 *)frm3;
|
|
||||||
|
|
||||||
u32 colorMask = 0xfefefe;
|
|
||||||
|
|
||||||
int sPitch = srcPitch >> 2;
|
|
||||||
int pos = 0;
|
|
||||||
|
|
||||||
for (int j = 0; j < height; j++)
|
|
||||||
for (int i = 0; i < sPitch; i++) {
|
|
||||||
u32 color = src0[pos];
|
|
||||||
src0[pos] =
|
|
||||||
(src1[pos] != src2[pos]) &&
|
|
||||||
(src3[pos] != color) &&
|
|
||||||
((color == src2[pos]) || (src1[pos] == src3[pos]))
|
|
||||||
? (((color & colorMask) >> 1) + ((src1[pos] & colorMask) >> 1)) :
|
|
||||||
color;
|
|
||||||
src3[pos] = color; /* oldest buffer now holds newest frame */
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Swap buffers around */
|
|
||||||
u8 *temp = frm1;
|
|
||||||
frm1 = frm3;
|
|
||||||
frm3 = frm2;
|
|
||||||
frm2 = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef MMX
|
|
||||||
static void MotionBlurIB_MMX(u8 *srcPtr, u32 srcPitch, int width, int height)
|
|
||||||
{
|
|
||||||
u16 *src0 = (u16 *)srcPtr;
|
|
||||||
u16 *src1 = (u16 *)frm1;
|
|
||||||
|
|
||||||
int count = width >> 2;
|
|
||||||
|
|
||||||
for(int i = 0; i < height; i++) {
|
|
||||||
#ifdef __GNUC__
|
|
||||||
asm volatile (
|
|
||||||
"push %2\n"
|
|
||||||
"movq 0(%3), %%mm7\n" // colorMask
|
|
||||||
"0:\n"
|
|
||||||
"movq 0(%0), %%mm0\n" // src0
|
|
||||||
"movq 0(%1), %%mm1\n" // src1
|
|
||||||
"movq %%mm0, 0(%1)\n" // src1 = src0
|
|
||||||
"pand %%mm7, %%mm0\n" // color & colorMask
|
|
||||||
"pand %%mm7, %%mm1\n" // src1 & colorMask
|
|
||||||
"psrlw $1, %%mm0\n" // (color & colorMask) >> 1 (E)
|
|
||||||
"psrlw $1, %%mm1\n" // (src & colorMask) >> 1 (F)
|
|
||||||
"paddw %%mm1, %%mm0\n" // E+F
|
|
||||||
|
|
||||||
"movq %%mm0, 0(%0)\n" // src0 = res
|
|
||||||
|
|
||||||
"addl $8, %0\n"
|
|
||||||
"addl $8, %1\n"
|
|
||||||
|
|
||||||
"decl %2\n"
|
|
||||||
"jnz 0b\n"
|
|
||||||
"pop %2\n"
|
|
||||||
"emms\n"
|
|
||||||
: "+r" (src0), "+r" (src1)
|
|
||||||
: "r" (count), "r" (qRGB_COLOR_MASK)
|
|
||||||
);
|
|
||||||
#else
|
|
||||||
__asm {
|
|
||||||
movq mm7, qword ptr [qRGB_COLOR_MASK];
|
|
||||||
mov eax, src0;
|
|
||||||
mov ebx, src1;
|
|
||||||
mov edi, count;
|
|
||||||
label0:
|
|
||||||
movq mm0, qword ptr [eax]; // src0
|
|
||||||
movq mm1, qword ptr [ebx]; // src1
|
|
||||||
movq qword ptr [ebx], mm0; // src1 = src0
|
|
||||||
pand mm0, mm7; // color & colorMask
|
|
||||||
pand mm1, mm7; // src1 & colorMask
|
|
||||||
psrlw mm0, 1; // (color & colorMask) >> 1 (E)
|
|
||||||
psrlw mm1, 1; // (src & colorMask) >> 1 (F)
|
|
||||||
paddw mm0, mm1; // E+F
|
|
||||||
|
|
||||||
movq qword ptr [eax], mm0; // src0 = res
|
|
||||||
|
|
||||||
add eax, 8;
|
|
||||||
add ebx, 8;
|
|
||||||
|
|
||||||
dec edi;
|
|
||||||
jnz label0;
|
|
||||||
mov src0, eax;
|
|
||||||
mov src1, ebx;
|
|
||||||
emms;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
src0+=2;
|
|
||||||
src1+=2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void MotionBlurIB(u8 *srcPtr, u32 srcPitch, int width, int height)
|
|
||||||
{
|
|
||||||
if(frm1 == NULL) {
|
|
||||||
Init();
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef MMX
|
|
||||||
if(cpu_mmx) {
|
|
||||||
MotionBlurIB_MMX(srcPtr, srcPitch, width, height);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
u16 colorMask = ~RGB_LOW_BITS_MASK;
|
|
||||||
|
|
||||||
u16 *src0 = (u16 *)srcPtr;
|
|
||||||
u16 *src1 = (u16 *)frm1;
|
|
||||||
|
|
||||||
int sPitch = srcPitch >> 1;
|
|
||||||
|
|
||||||
int pos = 0;
|
|
||||||
for (int j = 0; j < height; j++)
|
|
||||||
for (int i = 0; i < sPitch; i++) {
|
|
||||||
u16 color = src0[pos];
|
|
||||||
src0[pos] =
|
|
||||||
(((color & colorMask) >> 1) + ((src1[pos] & colorMask) >> 1));
|
|
||||||
src1[pos] = color;
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef MMX
|
|
||||||
static void MotionBlurIB32_MMX(u8 *srcPtr, u32 srcPitch, int width, int height)
|
|
||||||
{
|
|
||||||
u32 *src0 = (u32 *)srcPtr;
|
|
||||||
u32 *src1 = (u32 *)frm1;
|
|
||||||
|
|
||||||
int count = width >> 1;
|
|
||||||
|
|
||||||
for(int i = 0; i < height; i++) {
|
|
||||||
#ifdef __GNUC__
|
|
||||||
asm volatile (
|
|
||||||
"push %2\n"
|
|
||||||
"movq 0(%3), %%mm7\n" // colorMask
|
|
||||||
"0:\n"
|
|
||||||
"movq 0(%0), %%mm0\n" // src0
|
|
||||||
"movq 0(%1), %%mm1\n" // src1
|
|
||||||
"movq %%mm0, 0(%1)\n" // src1 = src0
|
|
||||||
"pand %%mm7, %%mm0\n" // color & colorMask
|
|
||||||
"pand %%mm7, %%mm1\n" // src1 & colorMask
|
|
||||||
"psrld $1, %%mm0\n" // (color & colorMask) >> 1 (E)
|
|
||||||
"psrld $1, %%mm1\n" // (src & colorMask) >> 1 (F)
|
|
||||||
"paddd %%mm1, %%mm0\n" // E+F
|
|
||||||
|
|
||||||
"movq %%mm0, 0(%0)\n" // src0 = res
|
|
||||||
|
|
||||||
"addl $8, %0\n"
|
|
||||||
"addl $8, %1\n"
|
|
||||||
|
|
||||||
"decl %2\n"
|
|
||||||
"jnz 0b\n"
|
|
||||||
"pop %2\n"
|
|
||||||
"emms\n"
|
|
||||||
: "+r" (src0), "+r" (src1)
|
|
||||||
: "r" (count), "r" (qRGB_COLOR_MASK)
|
|
||||||
);
|
|
||||||
#else
|
|
||||||
__asm {
|
|
||||||
movq mm7, qword ptr [qRGB_COLOR_MASK];
|
|
||||||
mov eax, src0;
|
|
||||||
mov ebx, src1;
|
|
||||||
mov edi, count;
|
|
||||||
label0:
|
|
||||||
movq mm0, qword ptr [eax]; // src0
|
|
||||||
movq mm1, qword ptr [ebx]; // src1
|
|
||||||
movq qword ptr [ebx], mm0; // src1 = src0
|
|
||||||
pand mm0, mm7; // color & colorMask
|
|
||||||
pand mm1, mm7; // src1 & colorMask
|
|
||||||
psrld mm0, 1; // (color & colorMask) >> 1 (E)
|
|
||||||
psrld mm1, 1; // (src & colorMask) >> 1 (F)
|
|
||||||
paddd mm0, mm1; // E+F
|
|
||||||
|
|
||||||
movq qword ptr [eax], mm0; // src0 = res
|
|
||||||
|
|
||||||
add eax, 8;
|
|
||||||
add ebx, 8;
|
|
||||||
|
|
||||||
dec edi;
|
|
||||||
jnz label0;
|
|
||||||
mov src0, eax;
|
|
||||||
mov src1, ebx;
|
|
||||||
emms;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
src0++;
|
|
||||||
src1++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void MotionBlurIB32(u8 *srcPtr, u32 srcPitch, int width, int height)
|
|
||||||
{
|
|
||||||
if(frm1 == NULL) {
|
|
||||||
Init();
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef MMX
|
|
||||||
if(cpu_mmx) {
|
|
||||||
MotionBlurIB32_MMX(srcPtr, srcPitch, width, height);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
u32 *src0 = (u32 *)srcPtr;
|
|
||||||
u32 *src1 = (u32 *)frm1;
|
|
||||||
|
|
||||||
u32 colorMask = 0xfefefe;
|
|
||||||
|
|
||||||
int sPitch = srcPitch >> 2;
|
|
||||||
int pos = 0;
|
|
||||||
|
|
||||||
for (int j = 0; j < height; j++)
|
|
||||||
for (int i = 0; i < sPitch; i++) {
|
|
||||||
u32 color = src0[pos];
|
|
||||||
src0[pos] = (((color & colorMask) >> 1) +
|
|
||||||
((src1[pos] & colorMask) >> 1));
|
|
||||||
src1[pos] = color;
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user